Clients for the hosted API#
Two small clients drive the hosted API without writing HTTP by hand:
computerworld-cloud for Python (standard library only, Python 3.9 or later) in
clients/python, and computerworld-cloud for JavaScript (one ES
module, no dependencies, Node 18 or later and browsers, with TypeScript declarations) in
clients/js. They have the same shape: a Client holding an API key,
and World, Environment and Swarm handles whose methods are named after the engine's
own World and Environment, so code moves between a local world and a hosted one with
few changes. Python uses snake_case, JavaScript camelCase.
Install#
Neither is published to PyPI or npm; both install from their public repository, ComputaCo/computerworld, MIT-licensed.
# Python
pip install "computerworld-cloud @ git+https://github.com/ComputaCo/computerworld.git#subdirectory=python"
# JavaScript, in a Node project
npm install github:ComputaCo/computerworld
// JavaScript, from a URL (browsers, Deno); pin a tag or commit for anything lasting
import { Client } from 'https://cdn.jsdelivr.net/gh/ComputaCo/computerworld@main/computerworld-cloud.mjs';
The JavaScript client is one file, so copying computerworld-cloud.mjs (and
computerworld-cloud.d.mts for types) into a project works too.
A first run#
Both read COMPUTERWORLD_API_KEY and COMPUTERWORLD_API_BASE when no key or base URL is
passed. The base URL may be given with or without /v1. It defaults to
https://computaco.ai/v1; https://dev.computaco.ai/v1 is the dev tier (Stripe test mode,
its own accounts).
from computerworld_cloud import Client
client = Client()
with client.create_world("agent-desktop", seed=1) as world: # deleted on exit
env = world.environment()
env.click(interaction="shell:launch:editor") # find in the scene, click the centre
env.click(role="textbox", label="Document text")
env.type_text("Hello")
env.render().save("screen.png")
fork = world.fork()
run = fork.run("agent-desktop/legal-engagement", seed=1)
print(run["verification"]["passed"])
fork.delete()
import { Client } from 'computerworld-cloud';
const client = new Client();
const world = await client.createWorld('agent-desktop', { seed: 1 });
try {
const env = await world.environment();
await env.click({ interaction: 'shell:launch:editor' });
await env.click({ role: 'textbox', label: 'Document text' });
await env.typeText('Hello');
const { bytes } = await env.render(); // PNG
const fork = await world.fork();
const run = await fork.run('agent-desktop/legal-engagement', { seed: 1 });
console.log(run.verification.passed);
await fork.delete();
} finally {
await world.delete();
}
Local and hosted, side by side#
| Local engine | Python client | JavaScript client |
|---|---|---|
World(definition, seed) | client.create_world(name | definition= | blueprint=, seed=) | client.createWorld(name, { definition, blueprint, seed }) |
world.environment(config) | world.environment(config) | world.environment(config) |
world.session(id) | world.session(id) | world.session(id) |
snapshot, restore, fork | the same; a snapshot is kept by the API and named by id | the same |
export_snapshot / import_snapshot | export_snapshot() / client.import_world(bundle) | exportSnapshot() / client.importWorld(bundle) |
state_hash, inspect, definition, trajectory, reset | the same | stateHash, inspect, … |
add_computer, remove_computer | the same | addComputer, removeComputer |
instantiate, stage, project, verify | client.instantiate, world.stage/project/verify, world.run | the same, and world.run |
env.step, observe, scene | the same; scene(elements=True) adds the interactive elements | scene({ elements: true }) |
env.render(w, h) → RGBA | env.render(w, h) → PNG bytes with .etag, .state_hash, .tick | { bytes, etag, stateHash, tick } |
Beyond the engine's calls: the account (me, profile, usage, keys), the catalog,
blueprints and their topology edits, advance on the world clock, service state, a
grounded perform, and swarms (create_swarm / createSwarm, rounds, the journal, the
trace and the summary). Environment.connect(token, env_id) builds an agent's handle
from an actor token alone, which can only step, observe, read the scene and render.
Behaviour both share#
- Finding and clicking.
findreturns the frontmost interactive element matching every field given amonglabel,role,interactionandvalue(a string exactly, a regular expression by search), skipping disabled ones; a miss raisesElementNotFoundnaming what is on screen.clicktakes a point, an element, or the same fields and sends apointer.v1 clickat the element's centre in the environment's viewport (1280×800 unless changed). The helpers raiseActionFailedon an unsuccessful outcome;stepitself never does, as in the engine. - Retries. A 429 or 503 is sent again for any method; a 500, 502, 504 or a lost
connection only for GET, PUT and DELETE, so a POST that may have changed a world is not
repeated. The wait is
Retry-Afterwhen sent, otherwise exponential backoff from 0.5 s, jittered and capped at 30 s; four retries by default, and aRetry-Afterlonger than 120 s is raised at once. - Errors carry the HTTP status and the API's
code(not_found,rate_limited,quota_exceeded,invalid_world, …), its message,Retry-Afterand the request's method and path. An answer that is not the API's JSON ishttp_<status>. - User-Agent. Every request from Python, Node, Bun or Deno sends
computerworld-cloud-python/<version>orcomputerworld-cloud-js/<version>, with the caller's own in front when given. Cloudflare refusesurllib's default with403 error code: 1010. A browser sends its own, since a cross-originfetchmay not set one.
Measured#
The live smoke tests (clients/python/tests/test_live.py, clients/js/test/live.test.mjs)
boot agent-desktop, click the editor's launcher, type, render, snapshot, fork, run
legal-engagement in the fork (34 GUI actions, verified) and delete both worlds. Client
side, from the US against computerworld.pages.dev on 2026-10-28, median of three runs
each, in milliseconds:
| Python | JavaScript | |
|---|---|---|
boot agent-desktop | 2,360 | 3,181 |
| scene with elements | 194 | 128 |
| step (click) | 150 | 148 |
| step (type) | 117 | 92 |
| render 1280×800 PNG | 973 | 1,260 |
render, unchanged (304) | 88 | 95 |
| snapshot | 345 | 206 |
| fork | 3,345 | 2,453 |
procedure run legal-engagement | 644 | 526 |
The two clients are within the run-to-run spread of each other: boot, render and fork vary by a second or more between runs of the same client, because each lands on a new Durable Object.
Tests#
cd clients/python && python -m unittest discover -s tests -v # 27 offline tests, a local mock
cd clients/js && npm test # 26 offline tests, a local mock
COMPUTERWORLD_API_KEY=cw_live_… python -m unittest tests/test_live.py -v
COMPUTERWORLD_API_KEY=cw_live_… npm run test:live