agent-browser is the better tool, because it reads and acts on the page itself rather than on pixels.
Try it
boxd machine desktop demo to watch it happen.
How it works
- One action per call. Every call answers with the screen’s size and where the pointer is. Any action can also take a screenshot after itself in the same call, so a look-act-look loop costs one call per step.
- Coordinates are screen pixels from the top left. A screenshot scaled down with
--max-widthor--max-heightis smaller than the screen, so scale coordinates you read off it back up before clicking. The agent adapters do this for you. - The desktop starts on demand. The first action starts it if it isn’t running, which can take about 10 seconds on a machine that has just started. Later actions don’t wait for it.
- Sleeping machines wake. A machine on standby or hibernated is woken by an action. A stopped machine isn’t started for you, so the call fails until you start it.
- Who can use it. Anyone who can run commands on a machine: its owner, any member of the organization when the machine is shared, and API keys with access to it. Actions run as the machine’s user.
From the CLI
The laptop CLI and the CLI inside a machine have the same commands. Inside a machine, leave out the name to act on that machine, or name another machine you can reach.--screenshot FILE and the same image options as screenshot:
- is stdout. Key names are X key names, as xdotool takes them, plus common aliases such as enter, esc, cmd and pageup.
--crop picks the part of the screen. --region is the machine’s region, as on every machine command.From the SDKs
machines.computer has the same actions. See the Python and TypeScript references for all of them.
ComputerAction on the gRPC API.
Hold the screen
By default the screen follows whoever is watching it: a desktop nobody has opened is 1280x800, and while someone has the Desktop view open, the screen is the size of their window. That can change the screen’s size in the middle of an agent’s work, and coordinates it worked out a moment ago then point at the wrong place. An agent should hold the screen while it works:set_screen_size and release_screen_size (setScreenSize and releaseScreenSize), and every result says whether the screen is held.
While the screen is held, someone in the Desktop view sees it scaled to fit their window, with its edge marked. They can still click and type, and nothing they do resizes it. A Held at 1280x800 · Release button at the top right lets them take it back.
A hold survives standby and hibernation. It ends when it is released or the desktop restarts, so a stop or a reboot ends it too.
Claude and OpenAI agents
The SDKs have adapters that carry out a model’s computer-use tool calls on a machine:ClaudeComputer for Claude’s computer use tool and OpenAIComputer for OpenAI’s computer tool. They need neither model’s package, since they take and return plain data, and they work with the official SDKs.
OpenAIComputer works the same way: pass computer.tool in tools, and computer.run(response.output) returns a computer_call_output for each computer_call to send with your next request.
What the adapters do for you:
- Scale. The model sees screenshots scaled down to fit, 1280x800 for Claude and 1600x900 for OpenAI, and its coordinates are scaled back to the screen.
max_widthandmax_height(maxWidthandmaxHeight) change the size. - Hold the screen. The first action holds it at 1280x800 until you call
release.screen_size=(screenSize) picks another size, andNone(null) leaves the screen alone. - Stop at a failure.
runcarries out a turn’s actions in order and stops at the first that fails. The model gets that error, and the actions after it are reported as not run. - Leave safety checks to you. A
computer_callfrom OpenAI with pending safety checks raisesPendingSafetyCheckErrorwithout running anything, unlessacknowledge_safety_checks(acknowledgeSafetyChecks) onrunaccepts them.
AsyncClaudeComputer and AsyncOpenAIComputer take an AsyncBoxd.
A machine in another region
Name the region as you would for any machine command. See Regions.region= ({ region }), and so do the adapters. On screenshot, region is the part of the screen to capture, so name the machine’s region with c.in_region("nyc").screenshot(id) (computer.inRegion("nyc").screenshot(id)).
When an action fails
- A request that doesn’t fit, such as a point off the screen or a key name boxd doesn’t know, is refused before anything happens, with a message saying why. In the SDKs it raises an error with the code
invalid_argument. - Any other failure may have happened after the action ran. The SDKs send a mouse or keyboard action again only when it can’t have reached the machine, so after a failure take a screenshot to see where things stand.
Limits
A full-size PNG of a large screen can pass 30 MB. Ask for less with
--max-width, --max-height or JPEG when you don’t need every pixel.