Bỏ qua để đến nội dung

Give an agent a machine to test on

Nội dung này hiện chưa có sẵn bằng ngôn ngữ của bạn.

A rig is a throwaway machine an agent opens when it needs to actually try something: load a page, click through a desktop app, tap around a phone. You can watch it work in real time, and take the controls whenever you want.

Enclosed terminal, browser and desktop rigs are VMs with throwaway disks and gated networks. Android and iOS are host-managed emulators: their device state is disposable, but their networking follows the server host.

Rigs run on the machine hosting cc_server — the same machine, not the one you are looking at. There are five user-facing surfaces; desktop and mobile surfaces need host setup:

Surface Runs on Setup
Terminal (VM) A microVM booting a pinned Ubuntu image None — the image is fetched automatically on first use
Browser (VM) A microVM booting a pinned Debian image with Chromium, Firefox or WebKit installed on first use None — same
Computer (VM) QEMU with a Linux desktop image that ships Chromium, Firefox, WebKit and a GitHub-hosted-runner CLI baseline Install QEMU (brew install qemu), then Import a desktop image under Settings → Server → Rigs
Android Google’s Android emulator Install the Android SDK, create an AVD and start it (below)
iOS Simulator CoreSimulator on the server Mac Install Xcode and the requested iOS runtime, then install the automation bridge under Settings → Server → Rigs

The terminal and browser machines boot from digest-pinned images: the exact bytes are named in advance, fetched once on first boot, and cached — after the first boot of each kind, later machines skip the download entirely and come up in seconds. Settings → Server → Rigs shows what your server can boot and every machine running right now.

An installed desktop image stays manageable from the same page: Import replaces it with a newer qcow2, and Delete removes it so you can install a fresh build.

There is no image of ours for Android or iOS, and that is not an omission. Android system images come from Google’s SDK; iOS runtimes come from Xcode. For Android, install Android Studio (or the command-line SDK tools), add a virtual device in Device Manager, and start it. The Rigs page then reports Android as available.

It also tells you exactly which step you are missing — no SDK, no emulator, no virtual device, or a device that simply is not running — because those have four different fixes and only one of them is a download. If you start cc_server from a shell that cannot see the SDK, export it:

Terminal window
export ANDROID_HOME="$HOME/Library/Android/sdk"

For iOS, the server itself must run on macOS with Xcode and an iOS Simulator runtime installed. Open Settings → Server → Rigs and press Install iOS automation bridge. The server fetches the checksum-pinned WebDriverAgent artifact, verifies it, and installs it into the server-owned automation store. No simulator boots during setup.

Machines live as tabs, next to the chat and the diff — in a space or on a PR page, in the same + menu, under Machines:

  • Computer — a Linux desktop with Chromium, Firefox and WebKit
  • Chromium, Firefox or WebKit — a browser
  • Android — the running Android emulator
  • iOS Simulator — a disposable simulator created for this conversation

Pick one and press Start the machine. The tab says which stage the boot is in (“Starting the microVM”, “Waiting for the guest to come up”) rather than just spinning.

The tab is scoped to the conversation, and so is the agent tool. A Chromium, Firefox or WebKit tab and browser_use, Android and mobile_use, or iOS Simulator and ios_use address the same default machine. Additional UI slots are separate comparison machines; agent tools always use the default.

To build, install and start a Flutter app on Android:

Terminal window
fvm flutter build apk --debug

Then call mobile_use with install_apk and build/app/outputs/flutter-apk/app-debug.apk, followed by start_app and the Android package name. ui_dump, screenshots and input drive the same device shown in the human tab. stop_app, clear_app_data, uninstall_app, open_url, and argv-shaped shell calls cover the rest of the development cycle without invoking a shell on the server host.

To build, install and start a Flutter app on iOS Simulator:

Terminal window
fvm flutter build ios --simulator --debug
/usr/bin/plutil -extract CFBundleIdentifier raw -o - \
build/ios/iphonesimulator/Runner.app/Info.plist

Then call ios_use with install_app and the .app directory, followed by start_app and the bundle identifier printed by plutil. The iOS tool also exposes stop_app, uninstall_app, open_url, and an argv-shaped spawn action for simulator-local diagnostics.

Desktop and browser displays resize to the panel. iOS keeps the simulator’s fixed point size and letterboxes it. Every surface still has two independent lanes: the agent gets small, cheap screenshots while you get the live view. Switching tabs stops the human stream so a background machine costs no display bandwidth.

A rig tab does not start a machine by itself, including during layout restore. That rule prevents an app launch from unexpectedly booting VMs or creating simulator devices.

The canvas is yours to type and click into whenever nobody else holds exclusive control — or when you do. While you hold that lock, the status tag reads You have control, the agent can still take screenshots, and the server refuses its mutating actions at the single chokepoint every action passes through. Everything you do is recorded against your name.

Press Stop the machine when you are done.

Control is exclusive on purpose: two actors typing into one machine produces input neither of them meant.

Copy and paste work in both directions across the rig canvas, and so does dragging files.

Copy and paste. Press the copy chord over the canvas and whatever the machine copied — text, an image, a list of files — lands on your clipboard. Paste sends yours the other way and presses paste inside the machine. On macOS the chord is ⌘C/⌘V; ⌃C still reaches a guest shell as an interrupt. On Windows and Linux use ⌃C/⌃V — the guest receives the same keystroke it would have anyway, so ⌃C in a guest terminal still interrupts and your own clipboard is left alone.

Nothing syncs in the background. A password you copy on your own machine does not arrive inside a VM because a timer fired; it crosses when you paste.

Paste into a rig is allowed by default. Copy out of a rig prompts first — a ten-minute grant, or Always allow under Settings → Server → Rigs → Clipboard access. The two directions are independent, and they follow you, not the workspace.

Drag files in. Drop them anywhere on the canvas.

  • On the Browser (VM), the page receives a real drop at the point you let go, so an upload zone behaves exactly as it would with a local file.
  • On the Computer (VM), the files land in ~/Drops and their paths go on the machine’s clipboard, so ⌃V pastes them into a file manager or an upload field. A host cannot synthesize a drag into an arbitrary Linux app without running a privileged daemon inside the VM, which rigs deliberately do not have — so the toast tells you where the files went rather than pretending a drop happened.
  • On a terminal, the file is copied in and its path is typed at the prompt, ready to pass to a command. Pasting an image into a terminal does the same thing: it is saved in the machine and you get the path.

Drag files out works from the Computer (VM). Start dragging a file inside the machine, carry the pointer out of the canvas, and it becomes a real drag on your desktop — drop it in Finder or Explorer. Copying files in the machine’s file manager and pasting on your side works too. The Browser (VM) does not offer this: a headless browser has no drag payload to read, and guessing from the page’s text selection would hijack ordinary clicks.

The terminals in a space or on a PR page can run inside a rig instead of on the server’s own machine. When they do, the panel shows an Enclosed VM badge.

What changes:

  • Nothing you run reaches the host. A rm -rf in there costs you the rig.
  • The network is deny-by-default. Only hosts on the rig’s allowlist are reachable, and everything goes through a gate that enforces it.
  • git push still works. The rig has no stored credential; it asks the server for a short-lived one per operation, and that token is revoked when the rig closes.

What to know:

  • A restored enclosed terminal waits for you. Like a rig tab, a microvm terminal that comes back with your layout shows an Open the shell button rather than booting its VM at launch. A host-shell terminal costs a process; an enclosed one costs a machine.
  • Files dropped on the terminal are typed, not opened. Drop a CSV on an enclosed terminal and it is copied into the machine and its guest path appears at the prompt — the host path would be a command that fails there.
  • The copy in the rig is a copy. Your worktree on the host stays the source of truth. Commits you make inside a rig are carried back into refs/rigs/<id>/* when you ask — they never move your branch or touch your working tree.
  • Host-target work still runs on the host. A Linux guest cannot build for macOS or run xcodebuild. Pick the native backend explicitly for that; the app will ask you to confirm rather than switching quietly.

Run a dev server in the Terminal (VM) — pnpm dev on port 3000, say — and within a few seconds the plug icon in the terminal’s corner lights up: the port is forwarded to localhost:3000 on your machine, reachable at localhost:3000 inside the Browser (VM), shareable on your network, and nameable as https://myapp.test. The whole flow — auto-forwarding, LAN sharing, dev domains with HTTPS — is its own guide: Forward ports from a terminal.

A workspace can point the Terminal (VM) and Browser (VM) at its own images — under Settings → Server → Rigs → Custom images (this workspace). Typical use: extend the default image with the toolchain your project needs so every terminal starts ready, or reuse an image your team already publishes.

The machine still boots inside the same enclosure: same egress gate, same credential broker, same lifecycle. Only workspace admins can change the setting, new machines pick it up (running ones keep theirs), and the reference must be a registry image — local paths are refused.

What an image must provide:

  • Terminal (VM) — a Linux image with bash. It also needs git, curl and socat — either preinstalled, or installable on first start (any Debian/Ubuntu-based image works: they are installed automatically, along with a GitHub-hosted-runner-style CLI toolset: compilers, Python 3, jq, archive tools, rsync. Language runtimes like Node/Go/Java and Docker are not preinstalled). Guest port 7911 is reserved for the port forwarder.
  • Browser (VM) — a Debian/Ubuntu image with bash and socat, plus a chromium binary. The server starts Chromium itself as chromium --headless=new and relays DevTools from guest 9223 onto 9222. PulseAudio and ffmpeg are installed on first start. Do not build FROM chromedp/headless-shell — that binary has no audio. The override applies to Chromium rigs only; Firefox and WebKit ignore it and boot the pinned Debian image. Guest ports 9222/9223 are reserved.

Two recommendations: pin the reference by digest (@sha256:…) so the bytes cannot change under you, and host it on Docker Hub or GitHub Container Registry — those registries’ download hosts are admitted through the egress gate automatically; others may need their content delivery hosts reachable to pull.

Rigs are meant to be temporary:

  • Idle — after ~15 minutes with nothing happening a browser, desktop or mobile rig parks (frozen, wakes instantly on the next action). An enclosed terminal waits 45 minutes. After twice the idle window, it closes.
  • Time limit — every rig has a hard lifetime it cannot extend, whatever it is doing.
  • Memory — if the server needs room for a new rig it closes the least-recently-used one. A rig somebody is driving, watching, or has a terminal open in is never taken.

When an enclosed rig closes, its disk is discarded. Closing an Android rig detaches from the host emulator — it does not delete it. An iOS rig deletes the CoreSimulator device it created. Carry anything you want to keep back first.

Enclosed terminal, browser and desktop rigs default to a deny-by-default egress gate: you grant hosts explicitly, and the guest’s only route is a gate that enforces that list. A browser rig also admits the product site (usectrl.dev) out of the box. The credential broker uses the same list.

Two honest caveats:

  • Mobile networking follows the host. Android’s emulator and Apple’s CoreSimulator are not behind the enclosure egress gate. Neither mobile tab offers a network-bypass control because the host-managed network is already unrestricted.
  • Everything a rig reads is untrusted. Page text, DOM, accessibility trees and view dumps come back to the agent wrapped in markers that say “this is data, not instructions”. A page that tries to give your agent orders reads as a quoted string.

You can see everything running, and stop any of it, under Settings → Server → Rigs.

  • Rigs and enclosures — the model behind all of this: why VMs, the two display lanes, the credential broker, the lifecycle
  • Forward ports from a terminal — reach a dev server from anywhere it matters
  • Rigs reference — every surface, verb, port and default in one place
  • Configure guardrails — Drive an enclosure (rig) is an action class you can set to allow, ask-first or deny, per workspace, agent or space. Read-only modes deny it outright.