# Cua Documentation — full text > Cua is an open-source platform for computer-use automation: AI agents that operate real computers by clicking, typing, and reading the screen and accessibility tree. Drive a machine you already have with Cua Driver, provision hosted desktops with Cloud Fleets and the Sandbox SDK, or use Lume as local Apple Silicon/macOS VM infrastructure underneath your own workflows. Source: https://cua.ai/docs # Cua documentation Computer-use infrastructure on your own machines. Sandboxes, Spaces, and agents that drive real desktops. [Cua SDK]() Create and drive computers from your own code. Python, TypeScript, Swift and Rust. [Cua Driver]() Let an agent drive native apps on macOS, Windows and Linux in the background. [Cua Spaces]() The Mac app for working alongside your agents. Watch, take over, share files and teleport apps. [Cua Bench]() Run and score computer-use benchmarks on your machine. [Lume]() macOS and Linux VMs on Apple silicon. [Cua CLI]() `cua`: sandboxes, auth, the daemon and MCP from the terminal. ## Start here - [Sandbox images](): the desktop and benchmark images, and one line to start each. - [Create a Space with the Cua SDK](): create a Linux Space on your machine from Python or TypeScript, use it, watch it and delete it. - [Host Spaces on your spare Mac](): let your laptop create Spaces on another machine, over the relay, ssh or Tailscale. - [Drive your first app with Cua Driver](): let your agent operate a native app on the machine you already have. - [Create a local macOS sandbox with the Cua CLI](): a macOS Tahoe sandbox on Apple silicon with `cua sb create macos`. - [Run a sandbox in GitHub Actions](): a Linux sandbox per CI job, with logs and a screenshot as artifacts. - [Build your first Cua Bench task](): create a task, score it with its oracle, then hand it to an agent. ## Examples - [Cua Bots](): persistent bots on Cua Spaces, each with its own computer, in a Mac and iPhone app. The same kind of app in TypeScript, Rust (Tauri) and Swift is in [Examples in other languages](). - [Background computer-use with Cua Driver](): an MCP tool for your agent. In this example, it builds and QAs a WPF app on Windows ([video](https://x.com/trycua/status/2059688966085853245)). ![Cua Bots: a bot finished its research, with the result card, a notification and its profile]() Coding agents can read these docs as Markdown or query them over MCP: see [Docs for agents](). Service status: [status.cua.ai](https://status.cua.ai). --- # Sandbox images The desktop and benchmark images Cua publishes. Cua publishes these images on `ghcr.io/trycua`. The Linux, Windows, benchmark and macOS 26 images include [cua-spacesd]() and [cua-driver](), making them ready for agentic workloads; macOS 15 runs the legacy guest agent for now. Windows, macOS and tags ending in `-disk` are VMs; the rest are containers. [Image catalog details](). ### Linux - `ghcr.io/trycua/linux:24.04` - `ghcr.io/trycua/linux:24.04-disk` (VM) - `ghcr.io/trycua/linux:24.04-slim` - `ghcr.io/trycua/linux:24.04-slim-disk` (VM) - `ghcr.io/trycua/omarchy:edge` - `ghcr.io/trycua/omarchy:edge-disk` (VM) ### Windows - `ghcr.io/trycua/windows:2022` ### macOS - `ghcr.io/trycua/macos:26` (Tahoe) - `ghcr.io/trycua/macos:26-slim` (Tahoe 26 slim) - `ghcr.io/trycua/macos:15` (Sequoia) ### Adapter benchmark images - `ghcr.io/trycua/bench-web:1.0` (run [MiniWoB++](), [WebVoyager](), [Online-Mind2Web](), [WebGym]()) - `ghcr.io/trycua/bench-web:1.0-disk` (VM) - `ghcr.io/trycua/bench-osworld:verified` (run [OSWorld-Verified]()) - `ghcr.io/trycua/bench-osworld:verified-disk` (VM) ## Run one ```bash skip="host-install" curl -fsSL https://cua.ai/install.sh | sh ``` ```bash test="cli-shape" id="cua-sb-create-desk" cua sb create linux --name desk cua sb view desk ``` --- # Create a Space with the Cua SDK Create a Linux Space on your own machine from Python or TypeScript, run a command in it, take a screenshot, ask Cua Driver for its windows, watch it, and delete it. A Space is a desktop your agents can use and you can watch. In this walkthrough you create one on your own machine from code, run a command in it, take a screenshot, ask Cua Driver which windows it sees, watch the desktop, and delete the Space. Nothing leaves your machine. Requirements: Docker (or Podman or Colima) and about 4 GB of free memory. ## 1. Install ```bash skip="host-install" curl -fsSL https://cua.ai/install.sh | sh # the cua CLI cua runtime doctor # checks the container engine ``` **Python** ```bash skip="host-install" pip install cua ``` **TypeScript** ```bash skip="host-install" npm install @trycua/cua ``` ## 2. Create a Space and use it The program passes no location, so the Space runs on this machine: the canonical Linux desktop (`ghcr.io/trycua/linux:24.04`) in a container. **Python** ```python title="first_space.py" test="container" id="first-space" import asyncio import cua async def main(): spaces = cua.embedded().spaces() created = await spaces.create(cua.SpaceCreateOptions()) space = await spaces.space(created.space.id) print("space:", created.space.id) # Run a command. out = await space.bash("uname -sm", None) print(out.stdout.strip()) # Take a screenshot. shot = await space.screenshot(None) with open("space.png", "wb") as f: f.write(shot.image) print("screenshot: space.png") # Ask Cua Driver in the Space which windows are open. windows = await space.call_tool("list_windows", None, None, None) print("driver:", "error" if windows.is_error else "ok") # Delete the Space. Comment this out to keep it and watch it (step 3). await spaces.delete(created.space.id) asyncio.run(main()) ``` **TypeScript** ```ts title="first-space.ts" test="container" id="first-space-ts" import { writeFile } from 'node:fs/promises'; import { embedded, SpaceCreateOptions } from '@trycua/cua'; const spaces = embedded().spaces(); const created = await spaces.create(SpaceCreateOptions.create({})); const id = created.space!.id; const space = await spaces.space(id); console.log('space:', id); // Run a command. console.log((await space.bash('uname -sm', undefined)).stdout.trim()); // Take a screenshot. const shot = await space.screenshot(undefined); await writeFile('space.png', Buffer.from(shot.image)); console.log('screenshot: space.png'); // Ask Cua Driver in the Space which windows are open. const windows = await space.callTool('list_windows', undefined, undefined, undefined); console.log('driver:', windows.isError ? 'error' : 'ok'); // Delete the Space. Comment this out to keep it and watch it (step 3). await spaces.delete_(id); ``` Run it with `python first_space.py` or `npx tsx first-space.ts`. The first run pulls the image, which can take a few minutes. ```text output space: local:space-1a2b3c Linux x86_64 screenshot: space.png driver: ok ``` `space.png` holds the desktop. On Apple silicon `uname` prints `aarch64`. ## 3. Watch it Keep the Space (comment out the delete line), run the program again, then open its desktop in your browser with the id it printed: ```bash test="cli-shape" id="first-space-view" cua sb view local:space-1a2b3c ``` The [Cua Spaces app]() lists the same Space, since the app, the CLI and the SDK share one registry. ## 4. Clean up The program deletes its Space. One you kept goes with: ```bash test="cli-shape" id="first-space-delete" cua spaces delete local:space-1a2b3c ``` `cua spaces ls` shows what is left. ## Next - [Reach another machine](): connect the SDK to any machine that runs cua-spacesd, by address and token. - [Host Spaces on your spare Mac](): let your laptop create Spaces on another machine, over the relay, ssh or Tailscale. - [Add a Space](): any image, progress, GPUs. --- # Host Spaces on your spare Mac Turn a spare Mac mini (or any machine) into a place your laptop and your agents create Spaces on, over the relay, ssh or Tailscale, without sharing its own desktop. Spaces run on your own Mac, on your other machines, and in your own cloud account ([AWS, Google Cloud or Modal]()). In this walkthrough a spare Mac mini in a closet runs Spaces for your laptop: your laptop, or a coding agent on it, asks for them through the cua.ai relay or [directly over Tailscale](#over-tailscale-without-the-relay), and the Mac mini runs them with its own runtimes (Lume for macOS VMs, Docker for Linux). You can set the Mac up [over ssh](#over-ssh-or-tailscale). Its own desktop, shell and files stay private. ## Set up the spare Mac On the spare Mac: ```bash test="cli-shape" id="cua-host-setup-spare" cua host setup --profile spare --name "Mac mini (spare)" ``` `--profile spare` is shorthand for `--no-desktop --provide-spaces`. Setup joins the relay the way [unattended access]() does. It signs you in for that one command and keeps no account session, only the machine's own token. The machine has two settings, and each works on its own: | Setting | What it does | `desktop` profile (default) | `spare` profile | |---|---|---|---| | Share this desktop | The machine itself is a Space: its screen, input, shell and files. | on | off | | Provide Spaces | Your enrolled devices can create, list and delete Spaces on it. | off | on | With **Share this desktop** off, the host's cua-spacesd runs without its desktop services, and a relayed caller reaches only the Spaces service. Change either setting at any time: ```bash test="cli-shape" id="cua-host-config" cua host config # show both settings and the Spaces it runs cua host config --desktop on --provide-spaces on # share the desktop too cua host config --max-spaces 6 # at most 6 Spaces at once (default 4) ``` The app's **This machine** page has the same two switches, the Spaces the machine provides, and every remote create and delete. ## Over ssh or Tailscale A spare Mac mini you only reach over ssh (on your [Tailscale](https://tailscale.com) or your LAN) works the same way. Tailscale is only the ssh transport here; hosting Spaces still goes through the cua.ai relay, not through Tailscale. ```bash skip="no-runner" ssh mini.your-tailnet.ts.net # on the mini, non-interactively: curl -fsSL https://cua.ai/install.sh | sh -s -- -y --select spaces,host --no-onboarding ``` `-y` accepts the install without the interactive checklist, `--select spaces,host` picks the Cua Spaces app and hosting, and `--no-onboarding` skips the automatic `cua auth login` (there is no browser or tty to sign in with yet). Then set up the host with a device code: ```bash test="cli-shape" id="cua-host-setup-spare-ssh" cua host setup --remote --profile spare --name "Mac mini (spare)" ``` If the mini has no saved account session, this prints a URL and a short code that you can approve in a browser on another device. The sign-in is held only for setup: the mini keeps its machine token, without saving an account session or enrolling it as a controller. An existing account session is reused. **Warning** The mini needs an active GUI login session: `cua-spacesd` runs as a LaunchAgent in the logged-in (Aqua) session, so it starts only after someone is logged in, not at the login window. For a headless mini, turn on auto-login (System Settings > Users & Groups > Login Options) so it signs in on its own after every reboot or power loss. The first run also needs a one-time grant of **Screen Recording** and **Accessibility** to `cua-spacesd` in System Settings > Privacy & Security; nothing in setup grants these for you. From your laptop, create Spaces on it the same way as any other host (see below): `create_space(on="host:", count=2)`, or `cua spaces create macos:26 --on "" --count 2`. ## Create Spaces from your laptop Your laptop must be [enrolled]() for your account. Then ask your coding agent, through the cua MCP server: > spin up 2 macos spaces on my spare mac mini The agent calls `create_space` with `on="host:spare mac mini"`, `image="macos"` and `count=2`. From the CLI: ```bash test="cli-shape" id="cua-spaces-create-host" cua spaces create macos:26 --on "spare mac mini" --count 2 cua spaces create linux --on host:0123abcd4567ef89 ``` The SDK takes the same `on`: `spaces.create(on="host:spare mac mini", image="macos")`. The machine is matched by its ID, its exact name, or the words of its name, so "spare mac mini" finds `Mac mini (spare)` or `dillons-mac-mini`. When two machines fit equally well (two Mac minis), the call fails with `ambiguous_host` and lists them with their IDs, so the agent can ask you which one you meant. Each new Space joins the relay as a machine of its own and comes back as `relay:`. Open it, stream it and run commands in it like any other Space. ## List and delete ```bash test="cli-shape" id="cua-spaces-host-ls" cua spaces ls cua spaces delete relay:space-0123456789abcdef ``` `cua spaces ls` (and the app's sidebar) groups the Spaces a machine provides under that machine. In `list_spaces` each one carries `host` (the machine ID) and `host_name`. Deleting asks the machine to delete the VM or container, and the Space leaves the relay. ## Limits | Limit | Default | Why | |---|---|---| | macOS VMs on one Mac | 2 | Apple's macOS license allows two macOS VMs per Mac, and Apple Virtualization (which Lume runs on) enforces it. | | Spaces at once | 4 | Set with `cua host config --max-spaces N` (0: no limit). | A request past a limit fails with `limit_exceeded` and says which limit and what to do. macOS Spaces need a Mac host: a Linux or Windows host refuses them with `host_capability_missing`. ## Who can create Spaces on it - You, from any device enrolled for your account. - Accounts you share the machine with as editors (`cua host share` or `cua spaces share relay: --role editor`). They see and delete only the Spaces they created; you see and delete all of them. - Nobody else. A device that is not enrolled is refused by the relay, and an account shared as a viewer is refused by the machine. Every remote create, delete and refusal is a line in the machine's hash-chained audit log (`~/.cua/host/spaces-audit.jsonl`). `cua host status` and the app show it, and warn when the log does not verify. Agents follow [per-agent computer access](): a persistent agent uses a Space on your spare Mac only after you allow it, and a grant on the machine does not reach the Spaces it provides. ```bash test="cli-shape" id="cua-agent-allow-host-space" cua agent allow-computer ada relay:space-0123456789abcdef ``` ## Over Tailscale without the relay A spare Mac your laptop reaches by its [Tailscale](https://tailscale.com) (or LAN) address can host Spaces without the cua.ai relay: no sign-in on either machine and no enrolled devices. Set it up on its Tailscale address: ```bash test="cli-shape" id="cua-host-setup-direct-spare" cua host setup --direct 100.101.102.103:3211 --profile spare --name "Mac mini (spare)" ``` Use the Mac's own address from `tailscale ip -4`. With `--direct 0.0.0.0:3211` it listens on every interface and setup picks its Tailscale address (else its LAN address) for the next step. Setup prints the exact command to run on your laptop, with the Mac's token: ```bash test="cli-shape" id="cua-spaces-add-direct-host" cua spaces add 100.101.102.103:3211 --host --name "Mac mini (spare)" --token ``` The laptop keeps the token in its Spaces credential store (`~/.cua/spaces-credentials.json`, 0600) and lists the Mac in `~/.cua/direct-hosts.json`. Then create Spaces on it as on any host: ```bash test="cli-shape" id="cua-spaces-create-direct-host" cua spaces create macos:26 --on "spare mac mini" --count 2 ``` `on="host:"` looks at your machines on the relay first, then at the hosts you added with `--host`, with the same matching: an ID or exact name wins, otherwise the words of the name, and two equally good matches fail with `ambiguous_host`. Each new Space runs on the Mac with the same limits (four Spaces, two macOS VMs). It is not put on the relay: the Mac forwards a port on its own address to the Space's cua-spacesd, and your laptop adds it as `direct:100.101.102.103:` with the Space's own token. `cua spaces ls` lists it under the Mac, `list_spaces` marks it `"via": "direct"`, and `cua spaces delete`, `stop` and `start` ask the Mac to do it. When a Space starts again, or the Mac restarts, the Mac opens the same port again. **Warning** The direct listener is plain HTTP, and the Mac's token is its owner: it creates and deletes Spaces there and reaches the Mac's own shell and files, even with **Share this desktop** off. Treat it like an SSH key and use it only over Tailscale or a LAN you trust. By default the Mac takes host calls, and connections to the Spaces it forwards, only from loopback, Tailscale (`100.64.0.0/10`, `fd7a:115c:a1e0::/48`) and private LAN addresses (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7`), and setup refuses a public `--direct` address. `--allow-any-address` turns both off, with a warning. To rotate the token, delete `~/.cua/host/env-token` on the Mac, run the same `cua host setup` again, and add the Mac on your laptop again with the new command. ## macOS Spaces with Lume The spare Mac runs macOS Spaces as Lume VMs: - Apple silicon and macOS 13 or later, with [Lume]() installed. - About 50 GB free disk and 8 GB free memory per macOS Space. The first one downloads the image, so it takes longer than the next ones. - The cua daemon on the spare Mac creates the VMs. The host's cua-spacesd starts it when a request comes in, with the `cua` CLI that ran setup. **Note** A Space reaches the relay from inside its VM or container. With a relay on the spare Mac itself (a self-hosted relay on `localhost`), a container gets it at `host.docker.internal` and a Lume VM at the host's NAT address, `192.168.64.1`. ## Troubleshooting | Error | Meaning | Fix | |---|---|---| | `ambiguous_host` | Several of your machines fit the name. | Pick one of the listed IDs: `on="host:"`. | | `not_found` | No machine fits the name. | Check `cua spaces ls`, or set the machine up with `--provide-spaces`. | | `host_capability_missing` | The machine is offline, does not provide Spaces, or cannot run that OS. | Run `cua host config --provide-spaces on` on it, or pick another machine. | | `limit_exceeded` | Two macOS VMs already run, or the machine is at its Space limit. | Delete one with `cua spaces delete`, or raise `--max-spaces`. | | `permission_denied` | The machine is shared with you to watch only. | Ask its owner to share it as an editor. | --- # Drive your first app with Cua Driver Install Cua Driver, connect your agent, and verify a result in a desktop app on macOS, Windows, or Linux. By the end of this walkthrough, your agent will open Calculator, compute 6 × 7, and report 42 through **Cua Driver**. You will install the driver, connect your agent, and run a prompt that matches its integration on macOS, Windows, or Linux. Have an agent installed and a desktop calculator available before you begin. On Linux, use your distribution's calculator, such as GNOME Calculator. New to agents that operate applications? [How Cua Driver works]() explains the model behind the workflow. This walkthrough connects an existing agent to Cua Driver. To embed Cua Driver in your own program instead, see [Use the SDK](). To try it without touching your own desktop, run it [in a sandbox](). [Drive a legacy postal app with Cua Driver]() Cua Driver fills shipment details and prints receipts in a Windows desktop app with no API. You will start with a smaller Calculator task below. ## 1. Install Cua Driver Use the same one-line installer on every platform; it picks the right path for the host and needs no administrator access. **macOS** Requires macOS 14 (Sonoma) or later. ```bash skip="host-install" /bin/bash -c "$(curl -fsSL https://cua.ai/driver/install.sh)" ``` Start the daemon through the app bundle so macOS attributes permission prompts to CuaDriver.app. This is what makes the TCC grant stick to the driver: ```bash skip="host-macos-permissions" open -n -g -a CuaDriver --args serve ``` Then grant Accessibility and Screen Recording: ```bash test="cli-shape" id="cua-driver-permissions-grant" cua-driver permissions grant ``` macOS prompts once for each permission. Neither prompt grants anything on its own: both offer **Open System Settings**, not **Allow**: ![The macOS Accessibility Access prompt for CuaDriver, offering Open System Settings or Deny](https://github.com/user-attachments/assets/b7a5a0c0-f297-4249-81f9-ca42c6b6f8ba) ![The macOS Screen Recording prompt for CuaDriver, offering Open System Settings or Deny](https://github.com/user-attachments/assets/69efc964-a375-495e-8a27-400c9dd08e17) Clicking **Open System Settings** adds CuaDriver to that permission's list for you, switched off. Toggling it on is what actually grants access: ![CuaDriver listed under Accessibility in System Settings with its toggle switched off](https://github.com/user-attachments/assets/d149fe5a-28cb-460c-8868-4eda80d540a4) ![CuaDriver listed under Screen & System Audio Recording in System Settings with its toggle switched off](https://github.com/user-attachments/assets/469308b5-84d7-4fd6-a580-e62925120337) **Warning** Do this for both **Accessibility** and **Screen & System Audio Recording**; choosing **Deny** leaves the driver unable to see or drive your desktop. macOS may offer to quit and reopen CuaDriver when you flip a toggle. Accept, because the driver only picks up a changed grant after a full relaunch. If the daemon does not come back, start it again with `open -n -g -a CuaDriver --args serve`. macOS does not always raise both prompts in one pass. Check what actually landed: ```bash test="cli-shape" id="cua-driver-permissions-status" cua-driver permissions status ``` If one is still missing, run the pair again to prompt for it: ```bash test="cli-shape" id="cua-driver-permissions-grant-2" open -n -g -a CuaDriver --args serve cua-driver permissions grant ``` **Windows** Requires Windows 10/11 with an interactive desktop session and PowerShell. ```powershell test="cli-shape" id="cua-driver-autostart-kick" irm https://cua.ai/driver/install.ps1 | iex cua-driver autostart kick ``` The installer attempts to register Cua Driver to start automatically at sign-in. `kick` starts that task immediately. If registration was skipped or failed, run `cua-driver serve` in a terminal in the interactive desktop session and leave it open. Use a second terminal for the checks below. Over SSH, see [Windows over SSH](). **Linux** Requires an x86_64 Linux desktop session with X11 or XWayland, plus AT-SPI 2. On a minimal Debian or Ubuntu image, first run `sudo apt install libxi6 at-spi2-core`. Run this walkthrough inside the desktop session, where the driver can reach its display and accessibility bus. ```bash skip="host-install" /bin/bash -c "$(curl -fsSL https://cua.ai/driver/install.sh)" ``` Start the daemon from a terminal in the same desktop session: ```bash test="cli-shape" id="cua-driver-serve" cua-driver serve ``` Leave that terminal open. Run the verification commands below in a second terminal in the same desktop session. **Note** The daemon you just started runs in the default `standard` permission mode, which is the right choice for this walkthrough. Once you move past Calculator, read [Permissions]() to decide whether a real workload should be `bounded` instead. Stale macOS grants? See [Troubleshoot](). ## 2. Verify it is working First confirm that the daemon is reachable: ```bash test="cli-shape" id="cua-driver-status" cua-driver status ``` Expect `Cua Driver daemon is running`. If it is not running, repeat your platform's startup step above. Then check the environment: ```bash test="cli-shape" id="cua-driver-doctor" cua-driver doctor ``` Then list the running GUI apps the driver can reach: ```bash test="cli-shape" id="cua-driver-call" cua-driver call list_apps ``` Read the `doctor` report, including warnings: a zero exit code does not prove desktop readiness. On macOS, also confirm both grants with `cua-driver permissions status`. Continue when the required permissions and desktop services are available, and `list_apps` includes a GUI app you recognize. An empty list is not a successful desktop check: open Calculator in that session and retry. Resolve permission or display errors before connecting the agent. ## 3. Connect your agent Choose one integration for your agent. A skill teaches the agent to use the Cua Driver CLI; MCP registration exposes Cua Driver as a tool server. `mcp-config` only prints setup instructions: you must run the registration command it prints. Use the output from your machine, not the illustrative paths below. **Claude Code** Install the Cua Driver skill for Claude Code: ```bash test="cli-shape" id="cua-driver-skills-install" cua-driver skills install cua-driver skills status ``` Confirm that the status includes a Claude Code skill link, then start a fresh Claude Code session. If the link is missing, follow [Install the agent skill](). Prefer plain MCP instead of the skill? Print the Claude Code registration command: ```bash test="cli-shape" id="cua-driver-mcp-config-claude" cua-driver mcp-config --client claude ``` It prints a command you run to register the server (paths will be specific to your install): ```bash output claude mcp add-json --scope user cua-computer-use '{"args":["mcp"],"command":"/Users/you/.local/bin/cua-driver"}' ``` **Codex** Print the Codex registration command: ```bash test="cli-shape" id="cua-driver-mcp-config-codex" cua-driver mcp-config --client codex ``` It prints a command you run to register the server. The emitted command uses the absolute installed binary path: ```bash output codex mcp add cua-driver -- /Users/you/.local/bin/cua-driver mcp ``` Run `codex mcp list` to confirm the `cua-driver` registration. Start a fresh Codex session and use `/mcp` to check that the server is active. See the [official Codex MCP instructions](https://learn.chatgpt.com/docs/extend/mcp). **Prime Agent** Install the Cua Driver skill pack. Prime Agent discovers it from its native skill directory and calls Cua Driver from the persistent IPython environment; no MCP registration is required. ```bash test="cli-shape" id="cua-driver-skills-install-2" cua-driver skills install cua-driver skills status ``` Run `/reload` in Prime Agent or start a new session after installation so the skill appears. Use `/skill:cua-driver` to load it explicitly. **OpenClaw** Print the OpenClaw registration command: ```bash test="cli-shape" id="cua-driver-mcp-config-openclaw" cua-driver mcp-config --client openclaw ``` It prints a command you run to register the server, using the absolute installed binary path: ```bash output openclaw mcp set cua-driver '{"command":"/Users/you/.local/bin/cua-driver","args":["mcp"]}' ``` **Warning** On macOS, a server spawned this way by the gateway does not inherit OpenClaw.app's own Accessibility and Screen Recording grants. To embed Cua Driver in the host app instead, so the host owns the grants, see [Expose MCP from a desktop app](). **Hermes** Hermes includes a built-in `computer_use` toolset. Enable it for the CLI instead of adding a second raw MCP server, then start a chat with it: ```bash skip="third-party-cli" hermes tools enable computer_use --platform cli hermes -t computer_use chat ``` If it cannot reach Cua Driver, repair it with `hermes computer-use install` and check with `hermes computer-use doctor`. Using Cursor, Antigravity, OpenCode, Pi, a local model or another harness? See [Connect your agent](). ## 4. Ask your agent Use the prompt that matches the setup you chose. Ask the agent to read the calculator's displayed result after acting, so it verifies the GUI instead of answering the arithmetic from memory. **Claude Code** ```text output Using the Cua Driver skill, open the installed calculator app, compute 6 × 7, and read the displayed result back from a fresh snapshot. ``` If you chose MCP registration instead, replace “Cua Driver skill” with “cua-computer-use MCP server.” **Codex** ```text output Using the cua-driver MCP server, open the installed calculator app, compute 6 × 7, and read the displayed result back from a fresh snapshot. ``` **Prime Agent** ```text output Using the Cua Driver skill, open the installed calculator app, compute 6 × 7, and read the displayed result back from a fresh snapshot. ``` **OpenClaw** ```text output Using the cua-driver MCP server, open the installed calculator app, compute 6 × 7, and read the displayed result back from a fresh snapshot. ``` **Hermes** ```text output Using the computer_use toolset, open the installed calculator app, compute 6 × 7, and read the displayed result back from a fresh snapshot. ``` The app name, controls, and action sequence vary by platform and calculator. The agent must discover them from fresh state; element indices from another run are not reusable. If no calculator is installed, install one through your platform's normal app or package manager, then repeat the prompt. To understand how the driver observes and acts, see [How Cua Driver works](). ## 5. Confirm what happened Confirm that the calculator displays **42** and that the agent's final observation reports that value. A correct answer alone does not prove that the agent operated the app. Cua Driver uses best-effort background delivery. Observe whether your active app, keyboard focus, and real pointer stay unchanged during the interaction; do not infer this from the answer. The visible agent cursor is a separate overlay. macOS and Windows accessibility actions can operate supported controls in the background, but some apps require explicit foreground delivery. Linux AT-SPI actions and X11 routes can also work in the background; native Wayland depends on compositor support and has raw-keyboard limits. Launching an app can itself raise a window. See [Platform mechanisms]() for these differences and [Known limits]() for apps that need the foreground. ## Clean up Stop the daemon when you are done: ```bash test="cli-shape" id="cua-driver-stop" cua-driver stop ``` ## What you did You installed Cua Driver, checked its daemon and desktop access, connected your agent, and verified a result computed inside a desktop app. ## Try a real workflow Use a bounded task to connect app actions with a saved result. These macOS examples include edited recordings, fictional starter files, and output checks: - [Build and inspect a map](): load spatial data, prepare a print layout, and inspect the exported PDF. - [Redact and annotate a PDF](): prepare a review copy in Preview and check the saved redactions. - [Prepare an exhibition panel](): crop an archive image and edit a panel across GIMP and Inkscape. These runs include foreground desktop input. Use a separate desktop, a [sandbox]() or a [VM]() when you need to keep working during the task. ## Next steps - [Connect your agent](): every supported harness, agent SDKs and local models. - [Use the SDK](): drive apps from Python or TypeScript and verify each action. - [Keep Cua Driver running and updated](): autostart, permission modes and updates. - [How Cua Driver works](): how it avoids focus and cursor disruption when the target app supports it. --- # Create a local macOS sandbox with the Cua CLI Create a macOS Tahoe sandbox on an Apple silicon Mac with `cua sb create macos`, run a command, take a screenshot, open its desktop, and remove it. Requirements: Apple silicon, macOS 14 or later, about 40 GB free disk and 8 GB free memory. ## Install ```bash skip="host-install" curl -fsSL https://cua.ai/install.sh | sh ``` ```bash test="cli-shape" id="macos-runtime-setup" cua runtime doctor cua runtime setup lume # installs Lume if it is missing ``` ## Create ```bash test="cli-shape" id="macos-sb-create" cua sb create macos --name mac ``` ```text output ID STATUS LOCATION KIND RUNTIME EXPIRES local:mac ready local vm lume - ``` `macos` is `ghcr.io/trycua/macos:26` (Tahoe); `macos:sequoia` is `:15`. The first create pulls the image (23 GB, about 8 min); later creates clone it and are ready in about 25 s. The Tahoe image starts cua-spacesd at login, with its screen and accessibility permissions already granted. ## Run a command ```bash test="cli-shape" id="macos-sb-exec" cua sb exec mac -- sw_vers ``` ```text output ProductName: macOS ProductVersion: 26.5.2 BuildVersion: 25F84 ``` `exec` runs through cua-spacesd, with stdout, stderr and the exit code kept apart. The Sequoia image (`macos:sequoia`) has no cua-spacesd; there `exec` runs over SSH (`lume ssh`). ## Screenshot and view ```bash test="cli-shape" id="macos-sb-screenshot" cua sb screenshot mac -o desktop.png ``` ```text output wrote desktop.png (1024x768) ``` ![The desktop of a local macOS Tahoe sandbox]() ```bash test="cli-shape" id="macos-sb-view" cua sb view mac ``` ```text output Viewer for mac: http://192.168.64.63:3211/viewer/#ticket=****&files=%2FUsers%2Flume ``` `view` opens cua-spacesd's HTML5 viewer in the browser with a short-lived ticket (`--no-open` prints the URL instead). On Sequoia, which has no cua-spacesd, screenshots come over the VM's VNC endpoint and `view` opens the display with `lume attach`. ## Remove ```bash test="cli-shape" id="macos-sb-rm" cua sb rm mac ``` The pulled image stays as a stopped base VM for the next create; `cua cache ls` lists it. ## From the SDK ```python skip="macos-vm" from cua_sandbox import Image, Sandbox async with Sandbox.ephemeral(Image.macos()) as sb: print((await sb.shell.run("sw_vers")).stdout) png = await sb.screen.screenshot() ``` `sb.shell` and `sb.screen` go through cua-spacesd, the same path as the CLI. ## Next - [Local runtimes]() - [Sandbox images]() - [Lume quickstart](): a vanilla macOS VM from Apple's restore image, with Lume alone. --- # Run a sandbox in GitHub Actions Start a Linux sandbox in a GitHub Actions job with the cua-sandbox action, run a command in it, keep a screenshot, and delete it. The `cua-sandbox` action creates the sandbox, runs your steps, uploads logs, the doctor report and `screenshot.png` as an artifact, and always deletes the sandbox, even on cancel. ```yaml title=".github/workflows/e2e.yml" test="config" id="gha-linux-sandbox" jobs: e2e: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.97.1 - uses: trycua/cua/.github/actions/cua-sandbox@main with: image: linux run: | cua sb exec "$CUA_SANDBOX" uname -a cua sb screenshot "$CUA_SANDBOX" -o "$CUA_SANDBOX_ARTIFACTS/desktop.png" ``` - `run` runs on the runner; `guest-run` runs inside the sandbox. - `cua-version` defaults to `source` (builds the CLI from the action's ref, hence the Rust step). - No Docker or KVM on the runner: set `on: cloud` and pass `CUA_CLIENT_ID` and `CUA_CLIENT_SECRET` from secrets in the step `env`. ## The same steps locally ```bash test="cli-shape" id="gha-local-steps" cua sb create linux --name ci --wait desktop cua sb exec ci uname -a cua sb screenshot ci -o desktop.png cua sb rm ci --force ``` ## Next - [Test in CI with Cua sandboxes](): every input and output, injecting the build under test, and `cua doctor` expectations. --- # Build your first Cua Bench task Create a Cua Bench task, score it with its oracle in a local Linux sandbox, solve it yourself, then hand it to an agent. In this walkthrough you create a small computer-use task, check it with its built-in oracle solution in a Linux sandbox on your machine, solve it yourself in the same sandbox, and then hand it to an agent. You need no API key until the agent step. ## Before you start - Python 3.12 or 3.13 and [uv](https://docs.astral.sh/uv/). - A local runtime for the `ghcr.io/trycua/linux:24.04` sandbox (Docker, and gVisor when installed). Check it with the `cua` CLI: ```bash test="cli-shape" id="cua-runtime-doctor" cua runtime doctor ``` ## 1. Install Cua Bench ```bash skip="host-install" uv tool install cua-bench ``` This puts the `cb` command on your `PATH`. `cb --help` lists its command groups, including `run`, `interact`, `task` and `trace`. ## 2. Create a task Create a working directory and scaffold a task in it: ```bash skip="no-runner" mkdir cua-bench-tutorial && cd cua-bench-tutorial cb task create first-task ``` Answer the prompts, for example: ```text output Author name: Your name Author email: you@example.com License [MIT]: Task description: Write a word to a file Task difficulty (easy|medium|hard) [easy]: Task category (e.g., grounding, software-engineering) [grounding]: Tags (comma-separated): starter,linux ``` The scaffold is two files: ```text output first-task/ ├── main.py └── pyproject.toml ``` `main.py` defines the whole task lifecycle. The starter defines two variants, each asking for one word in `/tmp/answer.txt`, and four functions: ```python skip="pseudo" @cb.tasks_config(split="train") def load(): ... # the variants and the machine they run on (native Linux) @cb.setup_task(split="train") async def start(task_cfg, session): ... # remove any old answer @cb.solve_task(split="train") async def solve(task_cfg, session): ... # the oracle: write the right word @cb.evaluate_task(split="train") async def evaluate(task_cfg, session): ... # 1.0 if the file holds the word ``` ## 3. Inspect the task ```bash skip="no-runner" cb task info first-task ``` ![cb task info: a native Linux task with 2 variants and setup, solve and evaluate]() The provider is `native`: a real OS in a container or VM. The check marks confirm that Cua Bench found the setup, solve and evaluate functions. ## 4. Run the oracle ```bash skip="no-runner" cb run first-task --variant-id 0 --oracle ``` `cb run` starts a Linux sandbox, runs the setup, the oracle solution and the evaluator, and releases the sandbox. It reports `reward=1`. A failing oracle means the task is broken, not the agent. ## 5. Solve it yourself Open the same variant without the oracle: ```bash skip="no-runner" cb interact first-task --variant-id 0 ``` `cb interact` sets the task up, prints the sandbox's `Display:` link and opens the viewer in your browser. Open a terminal in the sandbox desktop and write the answer: ![The task sandbox mid-task: a terminal writing hello to /tmp/answer.txt]() Return to your terminal and press Enter. Cua Bench evaluates the sandbox and releases it: ![cb interact: setup complete, evaluation result 1.0, sandbox released]() The evaluator that scored the oracle also scored your attempt. ## 6. Hand it to an agent ```bash test="cli-shape" id="first-task-agent" cb run first-task --agent cua-agent --model anthropic/claude-sonnet-4-20250514 ``` `--on local` (the default) runs tasks in sandboxes on this machine; `--kind`, `--runtime`, `--image`, `--cpu` and `--memory` shape them. ## Clean up ```bash skip="destructive" cd .. && rm -rf cua-bench-tutorial ``` Run results stay in `~/.local/share/cua-bench/runs`. Keep fewer with `cb prune --runs --keep N`. ## What you built You created a task with variants, a setup function, an oracle solution and an evaluator, and used the same evaluator to score the oracle, your own attempt and an agent. ## Next steps - [Write a task](): richer setup, checks and HTML task windows. - [Run a benchmark](): OSWorld-Verified, MiniWoB++, WebVoyager and more. - [Sandbox images](): the benchmark images and how to start them. - [CLI reference]() --- # Cua SDK Build agentic applications and workflows on isolated Linux, Windows and macOS desktops on your machine, or on any machine that runs cua-spacesd. Build agentic apps and workflows on real desktops. Cua SDK gives your application isolated Linux, Windows and macOS machines on your own computer, runs the agents you already use inside them, and streams everything back so your users can watch, take over or teleport work in and out. [Cua Driver]() is how the agents drive each desktop. Sandboxes run on this machine by default: leave out `local=` (or `on`) and the SDK uses Docker or Podman (gVisor when installed), QEMU, or Lume on Apple silicon. Any OCI image works. **Python** ```python test="container" id="local-ephemeral" session="local" from cua_sandbox import Image, Sandbox, http async with Sandbox.ephemeral( Image.from_registry("python:3.12-slim"), command=["python", "-m", "http.server", "8000"], services={"web": 8000}, wait_for=http("web", "/"), ) as sb: print((await sb.service("web").request("GET", "/")).status_code) # 200 ``` **TypeScript** ```ts test="container" id="local-ephemeral-ts" import { embedded, http, SandboxCreateOptions } from '@trycua/cua'; const sb = await embedded().sandboxes().create( SandboxCreateOptions.create({ image: 'python:3.12-slim', command: ['python', '-m', 'http.server', '8000'], services: new Map([['web', 8000]]), waitFor: [http('web', '/')], }) ); console.log((await sb.service('web').request('GET', '/', undefined, undefined, undefined)).status); // 200 await sb.delete_(); ``` Setup: `cua runtime doctor` shows what this host has and `cua runtime setup` installs what is missing ([Local runtimes]()). Containers run the host's architecture (others run emulated); macOS runs in Lume VMs on Apple silicon. ## Reach another machine Any machine that runs [cua-spacesd](), such as a spare Mac, a server on your LAN or [Tailscale](), or a container you started, connects directly by address and env token. Nothing is created or deleted: **Python** ```python test="docs" id="direct-connect" session="direct-connect" prelude="space-url" from cua_sandbox import Sandbox async with Sandbox.connect(url="http://10.0.0.5:3211", token="TOKEN") as sb: print((await sb.shell.run("uname -a")).stdout) ``` **TypeScript** ```ts test="docs" id="direct-connect-ts" prelude="space-url" import { embedded } from '@trycua/cua'; const sb = await embedded().sandboxes().connectUrl("http://10.0.0.5:3211", "TOKEN", undefined); const guest = await sb.spacesd(undefined); console.log(new TextDecoder().decode((await guest.sh('uname -a', undefined)).stdout)); ``` The CLI does the same with `cua sb create --on direct:10.0.0.5:3211 --token "$CUA_ENV_TOKEN"`. To share machines across a team, join the [Teams waitlist](https://cua.ai/teams). ## Packages | Language | Package | | --- | --- | | Python | `cua-sandbox` (`from cua_sandbox import Sandbox`), over the `cua` SDK (`import cua`) | | TypeScript | `@trycua/cua` | | Rust | `cua-sdk` crate (git dependency), see [Rust crates]() | | Swift | `Cua` (SwiftPM) | | CLI | `cua sb ...`, see [Cua CLI]() | ## How it fits together The SDK is one Rust core with generated bindings, running in your process or in `cua daemon`. It starts local runtimes itself and connects to other machines directly. Nothing needs to run inside the image; images that ship [cua-spacesd]() (such as `Image.linux()`) also get the computer interfaces: `sb.shell`, `sb.files`, `sb.screen`, `sb.mouse`, `sb.keyboard`. See [How sandboxes work](). To control the machine you are on instead of a sandbox, use [Cua Driver](). Next: [Your first sandbox](). --- # Quickstart Your first sandbox. Run a web server in a sandbox on your machine and call it. Run a web server in a sandbox on your machine and call it. Sandboxes run locally by default, so the code passes no location. ## Prerequisites - Python 3.11 or newer: ```bash skip="host-install" pip install cua-sandbox ``` - A container engine (Docker, Podman or Colima). Check the host and install what is missing: ```bash test="cli-shape" id="cua-runtime-doctor" cua runtime doctor cua runtime setup ``` ## Run **Python** ```python title="quickstart.py" test="container" id="local-server" session="local" import asyncio from cua_sandbox import Image, Sandbox, http async def main(): async with Sandbox.ephemeral( Image.from_registry("python:3.12-slim"), command=["python", "-m", "http.server", "8000"], services={"web": 8000}, wait_for=http("web", "/"), ) as sb: print((await sb.service("web").request("GET", "/")).status_code) print(await sb.service("web").url()) asyncio.run(main()) ``` **CLI** ```bash test="cli-shape" id="cua-sb-create" cua sb create python:3.12-slim --name web \ --service web=8000 --wait http:web/ \ -- python -m http.server 8000 cua sb url web web ``` **TypeScript** ```ts test="container" id="typescript" import { embedded, http, SandboxCreateOptions } from '@trycua/cua'; const sb = await embedded().sandboxes().create( SandboxCreateOptions.create({ image: 'python:3.12-slim', command: ['python', '-m', 'http.server', '8000'], services: new Map([['web', 8000]]), waitFor: [http('web', '/')], }) ); console.log(await sb.service('web').url()); await sb.delete_(); ``` ## Result ```text output 200 http://127.0.0.1:53817 ``` The first start of an image pulls it, which can take a few minutes. ## Clean up `Sandbox.ephemeral` deletes the sandbox when the block exits. A CLI sandbox stays until you remove it: `cua sb rm web -f`. `cua cache du` shows what images and sandboxes take on disk ([Disk usage]()). ## Images Pass any registry reference. Each official tag resolves to the variant the backend runs. Linux images run as user `cua` with passwordless `sudo`; open the desktop in the browser with `cua sb view NAME` ([Browser view]()). Full list: [image catalog details](). | Image | Name | Local | Cloud | cua-spacesd | | --- | --- | --- | --- | --- | | `ghcr.io/trycua/linux:24.04` | Ubuntu 24.04 | Container (gVisor when installed) | gVisor container | Yes | | `ghcr.io/trycua/linux:24.04-disk` | Ubuntu 24.04 VM | QEMU VM | KubeVirt VM | Yes | | `ghcr.io/trycua/linux:24.04-slim` | Ubuntu 24.04 slim | Container (gVisor when installed) | gVisor container | Yes | | `ghcr.io/trycua/linux:24.04-slim-disk` | Ubuntu 24.04 slim VM | QEMU VM | KubeVirt VM | Yes | | `ghcr.io/trycua/windows:2022` | Windows Server 2022 | QEMU VM | KubeVirt VM | Yes | | `ghcr.io/trycua/macos:26` | macOS Tahoe 26 | Lume VM (Apple silicon) | Not available yet | Yes | | `ghcr.io/trycua/macos:26-slim` | macOS Tahoe 26 slim | Lume VM (Apple silicon) | Not available yet | Yes | | `ghcr.io/trycua/macos:15` | macOS Sequoia 15 | Lume VM (Apple silicon) | Not available yet | No | | `ghcr.io/trycua/omarchy:edge` | Omarchy | QEMU VM | KubeVirt VM | Yes | | `ghcr.io/trycua/omarchy:edge-disk` | Omarchy disk | QEMU VM | KubeVirt VM | Yes | `Image.linux()` is the Linux container and `Image.linux(kind="vm")` the same desktop as a VM. `Image.windows()` is a Windows Server 2022 VM, amd64, no license included. `Image.macos()` is Tahoe and `Image.macos("15")` Sequoia, both local only on Apple silicon. ## Adapter benchmark images Each has a container tag and a `-disk` VM tag, and runs locally with `cb run`. | Benchmark | What | Tasks | Image | | --- | --- | --- | --- | | [MiniWoB++]() | Synthetic web widgets | 130 | `ghcr.io/trycua/bench-web:1.0` | | [WebVoyager]() | Live websites, LLM-judged | 643 | `ghcr.io/trycua/bench-web:1.0` | | [Online-Mind2Web]() | Live websites, WebJudge-judged | 300 | `ghcr.io/trycua/bench-web:1.0` | | [WebGym]() | Live web test split | 1,167 | `ghcr.io/trycua/bench-web:1.0` | | [OSWorld-Verified]() | Ubuntu desktop apps | 369 | `ghcr.io/trycua/bench-osworld:verified` | ## Next - [Control the desktop]() - [Run a service]() - [Reach another machine]() --- # Choose and build an image Pick a canonical or registry image, add packages and files, and pull private images, the same way locally and in the cloud. A sandbox runs one image reference. One resolver picks the variant each backend runs (container rootfs or VM disk) and pins its digest, so the same reference works locally and in the cloud. ## Canonical images ```python test="docs" id="canonical" session="canonical" from cua_sandbox import Image Image.linux() # ghcr.io/trycua/linux:24.04, Ubuntu 24.04 desktop with cua-spacesd Image.windows() # ghcr.io/trycua/windows:2022, Windows Server 2022 VM Image.macos() # ghcr.io/trycua/macos:26 (Tahoe); Image.macos("15") for Sequoia ``` | Image | Local | Cloud | | --- | --- | --- | | `Image.linux()` | Container (gVisor when installed) | gVisor container | | `Image.linux(kind="vm")` | QEMU VM from `linux:24.04-disk` | KubeVirt VM from `linux:24.04-disk` | | `Image.windows()` | QEMU VM from `windows:2022-disk` | KubeVirt VM from `windows:2022-disk` | | `Image.macos()` | Lume VM (Apple silicon) | Not available | - Only `Image.linux()` ships cua-spacesd, so only it gets the computer interfaces. Reach the Windows and macOS images through services you declare. - `CUA_IMAGE_LINUX`, `CUA_IMAGE_WINDOWS` and `CUA_IMAGE_MACOS` override the defaults. The CLI takes the aliases `linux`, `windows`, `macos`, `macos:tahoe` and `macos:sequoia`. - Windows images do not include a Windows license. Confirm that your own licenses cover your use, including hosting and virtualization rights. The full list is in the [image catalog details](). ## Any registry image ```python test="docs" id="registry" session="registry" prelude="image" image = Image.from_registry("python:3.12-slim") # short refs mean docker.io image = Image.from_registry("ghcr.io/acme/app@sha256:...") windows = Image.from_registry("registry.example/win:1", os_type="windows", kind="vm") ``` - `kind` is detected: a container image runs as a container, a containerDisk (a disk at `/disk/disk.img`) as a VM. - `os_type="windows"` selects EFI firmware; a Windows disk never boots on BIOS. - The cloud runs amd64. Pin a digest when every sandbox must boot the same content. ```bash test="cli-shape" id="cua-sb-create-linux" cua sb create linux --name desk cua sb create python:3.12-slim --name app -- python -m http.server 8000 ``` ## Add packages, files and settings Each builder method returns a new, immutable `Image`. Locally, steps build into your container engine before boot (VM images apply them after boot through cua-spacesd). In the cloud, `apt_install`, `pip_install`, `uv_install`, `run`, `copy` and `env` build remotely; until the cloud builds images, build and push it yourself ([Fleet images]()). ```python test="docs" id="install-packages" session="build" from cua_sandbox import Image Image.linux().apt_install('curl', 'git', 'ffmpeg') Image.macos().brew_install('ffmpeg', 'jq') Image.windows().choco_install('nodejs', 'git') Image.windows().winget_install('Microsoft.VisualStudioCode') ``` ```python test="docs" id="chain" session="build" img = ( Image.linux() .apt_install('curl', 'git', 'ffmpeg', 'python3-pip') # Image.linux() has no pip .pip_install('requests', 'Pillow') # or .uv_install(...) .env(LOG_LEVEL='debug') # not for secrets .copy('./config.json', '/app/config.json') .run('mkdir -p /app/data') .expose(8080) # prefer services={"app": 8080} ) ``` Fork a base and start the result like any image: ```python test="docs" id="fork" session="build" base = Image.linux().apt_install('curl', 'git', 'python3-pip') dev = base.pip_install('ipython', 'rich').env(DEBUG='1') prod = base.pip_install('gunicorn').run('useradd -m appuser') ``` ```python test="container" id="start-dev" session="dev" prelude="images-dev" from cua_sandbox import Sandbox async with Sandbox.ephemeral(dev) as sb: print((await sb.shell.run('python -c "import rich"')).returncode) ``` ```python test="docs" id="inspect" session="build" img = Image.linux().apt_install('curl').pip_install('requests') print(img.to_dict()) # the spec # {'os_type': 'linux', 'distro': 'ubuntu', 'version': '24.04', 'kind': None, # 'layers': [{'type': 'apt_install', 'packages': ['curl']}, # {'type': 'pip_install', 'packages': ['requests']}]} ``` `Image.from_file()` boots a local qcow2, vhdx, raw or ISO disk (URLs are downloaded and cached): ```python skip="pseudo" Image.from_file('/path/to/disk.qcow2', os_type='linux') Image.from_file('https://example.com/windows.vhdx', os_type='windows') ``` ## Private registries Pass a `RegistrySecret`. The SDK uses it for the pull (in the cloud it becomes the sandbox's pull secret, which also covers sidecar images on the same registry). Credentials are never logged or saved in `~/.cua`. ```python skip="pseudo" from cua_sandbox import Image, RegistrySecret, Sandbox image = Image.from_registry( "ghcr.io/acme/agent:1.4", secret=RegistrySecret.from_env(), # CUA_REGISTRY_USERNAME / CUA_REGISTRY_PASSWORD ) sb = await Sandbox.create(image) ``` | Secret | Credentials | | --- | --- | | `RegistrySecret(username, password)` | Explicit values; a token works as the password | | `RegistrySecret.from_env(username_var=..., password_var=...)` | Read from the environment at create time | | `RegistrySecret.aws_ecr(region=None)` | A private Amazon ECR image, via the AWS CLI | Without a secret, local pulls also use `CUA_REGISTRY_*`, `GITHUB_TOKEN` for `ghcr.io`, your Docker config and ECR credentials. ```bash test="cli-shape" id="cua-sb-create" cua sb create ghcr.io/acme/agent:1.4 \ --registry-secret env:CUA_REGISTRY_USERNAME:CUA_REGISTRY_PASSWORD cua sb create 123456789012.dkr.ecr.us-east-1.amazonaws.com/agent:1.4 \ --registry-secret aws-ecr:us-east-1 ``` ```ts test="docs" id="typescript" import { registrySecret, SandboxCreateOptions } from '@trycua/cua'; SandboxCreateOptions.create({ image: 'ghcr.io/acme/agent:1.4', registrySecret: registrySecret.fromEnv('CUA_REGISTRY_USERNAME', 'CUA_REGISTRY_PASSWORD'), }); ``` --- # Run and share a service Run a server in a sandbox, call it, connect to an MCP server in it, forward a port, and share it with a public URL. A service is a named guest port. The image (or `command=`) runs the listener; `services=` names it and `wait_for=` holds `create` until it answers. The listener must bind a guest-reachable address (`0.0.0.0`, not only `127.0.0.1`). | Option | Meaning | | --- | --- | | `command=["python", "app.py"]` | argv that replaces the image's entrypoint | | `env={"LOG_LEVEL": "debug"}` | plain environment variables ([not secrets]()) | | `services={"api": 8000}` | named guest ports | | `wait_for=http("api", "/health")` or `tcp("api")` | readiness; a list waits for all. Fails at once if the command exits | ## Start a server ```python test="container" id="start-local" session="local" prelude="local-cleanup" from cua_sandbox import Image, Sandbox, http sb = await Sandbox.create( Image.from_registry("python:3.12-slim"), command=["python", "-m", "http.server", "8000"], env={"PYTHONUNBUFFERED": "1"}, services={"api": 8000}, wait_for=http("api", "/"), ) ``` ```bash test="cli-shape" id="cua-sb-create" cua sb create python:3.12-slim --name api \ --service api=8000 --wait http:api/ --env PYTHONUNBUFFERED=1 \ -- python -m http.server 8000 cua sb url api api ``` ## Call it ```python test="container" id="call" session="local" prelude="local-cleanup" api = sb.service("api") r = await api.request("GET", "/") # an httpx.Response r = await api.request("POST", "/items", json={"a": 1}, headers={"x-trace": "1"}) print(await api.url()) # usable from this machine ``` `url()` is a loopback URL locally and a signed URL (1 hour) in the cloud. In the cloud the gateway adds its own credentials, so an `authorization` header of your own is refused. ```ts test="container" id="typescript" prelude="local-cleanup" import { embedded, http, SandboxCreateOptions } from '@trycua/cua'; const sb = await embedded().sandboxes().create( SandboxCreateOptions.create({ image: 'python:3.12-slim', command: ['python', '-m', 'http.server', '8000'], env: new Map([['PYTHONUNBUFFERED', '1']]), services: new Map([['api', 8000]]), waitFor: [http('api', '/')], }) ); const res = await sb.service('api').request('GET', '/', undefined, undefined, undefined); console.log(res.status, await sb.service('api').url()); ``` ## MCP servers An MCP server is a service that speaks streamable HTTP; no cua-spacesd is needed. This one serves an `add` tool: ```python test="docs,container" id="mcp-server" session="mcp" import shlex SERVER = """ from mcp.server.mcpserver import MCPServer server = MCPServer("demo") @server.tool() def add(a: int, b: int) -> int: return a + b server.run("streamable-http", host="0.0.0.0", port=8765) """ COMMAND = ["sh", "-c", "pip install -q 'mcp>=2' && exec python -c " + shlex.quote(SERVER)] ``` ```python test="container" id="mcp-local" session="mcp" prelude="local-cleanup" from cua_sandbox import Image, Sandbox, tcp sb = await Sandbox.create( Image.from_registry("python:3.12-slim"), command=COMMAND, services={"mcp": 8765}, wait_for=tcp("mcp"), ) ``` `sb.mcp(service)` opens an official `mcp` client (`pip install "cua-sandbox[mcp]"`). `sb.mcp_config(service)` returns the URL and headers for any other client; fetch a fresh one per connection. ```python test="container" id="mcp-call" session="mcp" prelude="local-cleanup" async with sb.mcp("mcp") as client: # path="/other" for another endpoint result = await client.call_tool("add", {"a": 2, "b": 3}) config = await sb.mcp_config("mcp") # {"url": "http://127.0.0.1:53817/mcp", "headers": {}} ``` ```ts test="container" id="mcp-typescript" prelude="sb-mcp-local,local-cleanup" import { connectMcp } from '@trycua/cua'; const client = await connectMcp(await sb.mcpConfig('mcp', undefined)); const result = await client.callTool({ name: 'add', arguments: { a: 2, b: 3 } }); await client.close(); ``` ```bash test="cli-shape" id="cua-sb-mcp" cua sb mcp NAME mcp tools cua sb mcp NAME mcp call add '{"a": 2, "b": 3}' cua sb mcp NAME mcp config # URL and headers for another client ``` ## Forward a port `sb.tunnel.forward(port)` opens a loopback port for local tools such as a browser, `psql` or a debugger. ```python test="docs" id="forward-python" session="forward" prelude="fakefleet,sb-web" async with sb.tunnel.forward(8000) as t: print(t.url) # http://127.0.0.1:53817 async with sb.tunnel.forward(8000, 9222) as tunnels: # several ports print(tunnels[9222].url) ``` TypeScript: `sb.forward(port)`, `sb.publicUrl(service, ttl, label)` and `sb.revokePublicUrl(id)`. ```bash test="cli-shape" id="cua-sb-port-forward-web" cua sb port-forward web 8000 # until Ctrl-C cua sb port-forward web 8000:9000 # pick the local port ``` In the cloud, a port without cua-spacesd in the image must be declared in `services=`. ## Share with a public URL `sb.public_url(service, ttl=...)` returns a URL that needs no cua credentials and stops working after `ttl` seconds (60 s to 24 h, default 1 h) or when revoked. Locally it is served by the cua daemon on loopback; in the cloud it is a signed HTTPS URL reachable from anywhere. For an MCP server, append `/mcp`. ```python test="container" id="public-url" session="share" prelude="sb-web-local,local-cleanup" share = await sb.public_url("web", ttl=600, label="demo") print(share.url, share.expires_at) await sb.revoke_public_url(share) ``` ```bash test="cli-shape" id="cua-sb-url-web" cua sb url web web --public --ttl 10m ``` Cloud sandboxes also list their signed URLs: ```python test="docs" id="list-signed-urls" session="signed-urls" prelude="fakefleet,sb-web" for item in await sb.services.list_signed_urls(): print(item.label, item.expires_at, item.revoked_at) ``` **Warning** A public URL is a bearer credential. Use the shortest practical `ttl`, revoke it when sharing ends, and keep it out of logs and source control. It does not extend the sandbox's lifetime. --- # Pass secrets into a sandbox Pass credentials from the host into a local or cloud sandbox at runtime, and keep them out of images. Read secrets on the host and hand them to one process in the sandbox at runtime. Never put them in an image spec. The examples take a connected Linux sandbox `sb` with an application at `/app/main.py`; they work locally and in the cloud. | Value | Where it goes | | --- | --- | | API keys, tokens, passwords | The process environment at runtime (below) | | Key files | A temporary file that is removed when the process exits (below) | | Registry credentials | A [`RegistrySecret`](), never logged or saved | | Non-sensitive settings | `Image.env()`, `env=` on `Sandbox.create`, `Container(env=...)` | | A per-claim token on a Fleet pool | [Claim secrets]() | ## Environment variables Set `DATABASE_URL` and `GITHUB_TOKEN` on the host from your secret manager, then call `await run_with_secrets(sb)`: ```python test="docs" id="run-with-secrets" session="secrets" import os import shlex from cua_sandbox import Sandbox async def run_with_secrets(sb: Sandbox): db_url = os.environ['DATABASE_URL'] gh_token = os.environ['GITHUB_TOKEN'] command = ( f'DATABASE_URL={shlex.quote(db_url)} ' f'GITHUB_TOKEN={shlex.quote(gh_token)} ' 'python3 /app/main.py' ) result = await sb.shell.run(command) if not result.success: raise RuntimeError('Application failed; inspect sanitized diagnostics') ``` Keep the assignments and the application in one `shell.run()` call: an `export` does not persist into the next call. `shlex.quote()` keeps spaces, quotes and newlines literal. ## A temporary key file For an application that reads a key file, point `TASK_SSH_KEY_FILE` at the host file (mode `600`), then call `await run_with_key_file(sb)`: ```python test="docs" id="run-with-key-file" session="key-file" import os from pathlib import Path import shlex from cua_sandbox import Sandbox async def run_with_key_file(sb: Sandbox): key_content = Path(os.environ['TASK_SSH_KEY_FILE']).read_bytes().decode('utf-8') script = f""" set -eu umask 077 secret_dir=$(mktemp -d /tmp/cua-task-secret.XXXXXXXXXX) trap 'rm -f "$secret_dir/task-key"; rmdir "$secret_dir"' EXIT trap 'exit 1' HUP INT TERM printf '%s' {shlex.quote(key_content)} > "$secret_dir/task-key" chmod 600 "$secret_dir/task-key" TASK_SSH_KEY_FILE="$secret_dir/task-key" python3 /app/main.py """ result = await sb.shell.run(script) if not result.success: raise RuntimeError('Credential task failed; inspect sanitized diagnostics') ``` The file lives in a private directory and is removed when the application exits, including on failure. A killed shell or a lost connection can skip the cleanup: destroy the sandbox then. **Warning** These commands contain the secret values. Quoting does not redact logs, traces or process listings: do not print the command, do not enable `set -x`, use short-lived credentials, and revoke them when the task ends. --- # Add sidecar containers Run extra containers such as a database or cache next to a sandbox and reach them by name. A sidecar is an extra container next to the sandbox, like a Docker Compose service. It is addressed by name on every runtime: - the sandbox reaches a sidecar at its `name` (`db:6379`); - a sidecar reaches the sandbox at `main`; - `services=` names a sidecar port so you can call it from your machine. ```python test="container" id="local" prelude="local-cleanup" from cua_sandbox import Container, Image, Sandbox, tcp sb = await Sandbox.create( Image.from_registry("python:3.12-slim"), command=["sleep", "infinity"], sidecars=[Container("redis:7-alpine", ports=[6379], name="db")], services={"db": 6379}, wait_for=tcp("db"), runtime="runc", ) ``` Local sidecars share the sandbox container's network namespace, so `localhost` works too. They need `runtime="runc"`: separate gVisor containers cannot share a network namespace, and the SDK refuses rather than switching runtimes silently. Local VM sandboxes refuse sidecars. ## Reach the sandbox from a sidecar This relay fetches the sandbox's page from `http://main:8000` and serves it on its own port. ```python test="container" id="by-name" prelude="local-cleanup" from cua_sandbox import Container, Image, Sandbox, http relay = Container( "busybox:1.36", name="relay", ports=[8081], command=["sh", "-c", "mkdir -p /www; " "until wget -qO /www/index.html http://main:8000/; do sleep 1; done; " "exec httpd -f -p 8081 -h /www"], ) sb = await Sandbox.create( Image.from_registry("python:3.12-slim"), command=["python", "-m", "http.server", "8000"], sidecars=[relay], services={"relay": 8081}, wait_for=http("relay", "/"), runtime="runc", ) print((await sb.service("relay").request("GET", "/")).status_code) # 200 ``` ## Options `Container(image, command=None, env=None, ports=None, name=None)`: - `name` is a DNS label and the hostname. It defaults from the image (`redis` for `redis:7-alpine`) and cannot be `main`. - `command` replaces the sidecar image's entrypoint; `env` holds plain values. - A bare image string works too: `sidecars=["redis:7-alpine"]`. - At most 8 sidecars. With sidecars, the service names `main`, `sidecars` and `sc` are reserved. - A private sidecar image on the sandbox image's registry uses the same [registry secret](). ```bash test="cli-shape" id="cua-sb-create" cua sb create python:3.12-slim --name app --runtime runc \ --sidecar redis:7-alpine,port=6379,name=db --service db=6379 --wait tcp:db \ -- sleep infinity ``` `--sidecar` takes `IMAGE[,port=PORT][,env=K=V][,name=NAME]` and repeats. ```ts test="docs" id="typescript" import { sidecar, SandboxCreateOptions } from '@trycua/cua'; SandboxCreateOptions.create({ image: 'python:3.12-slim', command: ['sleep', 'infinity'], sidecars: [sidecar('redis:7-alpine', { ports: [6379], name: 'db' })], services: new Map([['db', 6379]]), runtime: 'runc', }); ``` --- # Manage sandbox lifecycle Create, keep, reconnect to, list, suspend and delete sandboxes, keep cloud sandboxes alive, and run many at once. Sandboxes run on this machine by default, so the examples pass no location. Every lifecycle call takes the same `local=` switch as `Sandbox.create`. ## Ephemeral or kept ```python test="container" id="ephemeral-or-persistent" session="local" prelude="local-cleanup" from cua_sandbox import Image, Sandbox # Deleted when the block exits. async with Sandbox.ephemeral(Image.linux()) as sb: ... # Kept until you delete it. sb = await Sandbox.create(Image.linux(), name="dev") await sb.disconnect() # the sandbox keeps running ``` Disconnecting never stops a sandbox. A connection and a sandbox have separate lifetimes. ## Inspect ```python test="container" id="inspect" session="local" prelude="local-cleanup" print(sb.id) # its ref, like local:dev: what connect() and delete() take info = await sb.info() info.status # provisioning, starting, ready or stopped info.location # local or cloud info.services # {"web": 8000, ...} info.expires_at # cloud: when the sandbox lapses without a keep-alive info.provider_details # backend internals (local runtime; cloud pool and claim) ``` | Status | Meaning | | --- | --- | | `provisioning` | Capacity is being found or created (image pull, cloud capacity) | | `starting` | Booting, or `wait_for` probes have not passed yet | | `ready` | Running and every probe passed | | `stopped` | Exited, suspended or released | Refs are `local:`, `cloud:` or `direct:`. A bare name works when it is unique; otherwise the call raises `AmbiguousSandbox`. ## Reconnect, list, delete ```python test="container" id="reconnect-list-delete" session="local" prelude="local-cleanup" sb = await Sandbox.connect("dev") for s in await Sandbox.list(): print(s.name, s.status, s.location) await Sandbox.suspend("dev") sb = await Sandbox.resume("dev") await Sandbox.delete("dev") ``` `await sb.destroy()` deletes the sandbox you hold. Suspend, resume and restart are local only: in the cloud `suspend` and `restart` raise `Unsupported`, and `resume` of a running sandbox reconnects. The CLI and the SDK share local state in `~/.cua/sandboxes`, so either can reconnect to the other's sandboxes. ```bash test="cli-shape" id="cua-sb-create-linux" cua sb create linux --name dev cua sb ls # local and cloud; --local or --cloud filters cua sb info dev cua sb suspend dev && cua sb resume dev cua sb keep-alive dev --for 2h # cloud cua sb rm dev -f ``` ## Cloud lifetime A cloud sandbox has a TTL (15 minutes by default) that the SDK renews while your process holds it. When the process exits, it lapses after the TTL. Set a longer TTL or extend it: ```python test="docs" id="cloud-lifetime" session="cloud" prelude="fakefleet,image" from datetime import timedelta from cua_sandbox import CloudOptions sb = await Sandbox.create(image, cloud=CloudOptions(claim_ttl=timedelta(hours=2))) await sb.keep_alive(minutes=60) ``` - `cloud=` implies the cloud; passing it with `local=True` is an error. - `sb.to_dict()` and `Sandbox.from_dict(...)` carry a cloud sandbox to another process. - With `cua daemon` running, CLI-created cloud sandboxes keep being renewed after the command exits. Local sandboxes run until you delete them. For pool and claim TTLs, see [Capacity and claims](). ## Run many at once `Sandbox.ephemeral` is async, so `asyncio.gather` runs sandboxes in parallel. Cap concurrency with an `asyncio.Semaphore` (every local sandbox uses host CPU, memory and disk) and pass `return_exceptions=True` so one failure does not cancel the rest. In the cloud, one image runs at most 10 at once unless you raise `CloudOptions(max_pool_size=...)`. --- # Control the desktop Take screenshots, click and type, call typed Cua Driver tools, open a shell or a browser view, and attach to any cua-spacesd. Images that ship cua-spacesd (port `3211`, service `env`), such as `Image.linux()`, expose the desktop. spacesd delegates pointer and keyboard input to Cua Driver in the guest. On an image without spacesd, `sb.spacesd()` and the interfaces below raise `SpacesdNotAvailable`. | Surface | Use it for | | --- | --- | | `sb.screenshot()`, `sb.mouse`, `sb.keyboard`, `sb.shell`, `sb.files` | Scripts and agent loops | | `sb.driver.connect()` | The full Cua Driver API: windows, accessibility, background input | | `cua mcp --sandbox NAME` | Coding agents and MCP clients | ## Screenshots and input ![A terminal on a local Linux sandbox desktop, opened from the dock and typed into through the HTML5 viewer]() **Python** ```python test="container" id="desktop-python" from pathlib import Path from cua_sandbox import Image, Sandbox FOCUS_TERMINAL = ("export DISPLAY=:1; setsid -f xfce4-terminal -T demo >/dev/null 2>&1; " "xdotool search --sync --onlyvisible --name demo windowactivate --sync") async with Sandbox.ephemeral(Image.linux()) as sb: width, height = await sb.get_dimensions() await sb.mouse.click(width // 2, height // 2) # Keys go to the focused window, so open one. assert (await sb.shell.run(FOCUS_TERMINAL)).success await sb.keyboard.type("hello") await sb.keyboard.keypress(["ctrl", "a"]) Path("desk.png").write_bytes(await sb.screenshot()) ``` **TypeScript** ```ts test="container" id="desktop-typescript" import { writeFile } from 'node:fs/promises'; import { embedded, SandboxCreateOptions, SpacesdCommand } from '@trycua/cua'; const FOCUS_TERMINAL = 'export DISPLAY=:1; setsid -f xfce4-terminal -T demo >/dev/null 2>&1; ' + 'xdotool search --sync --onlyvisible --name demo windowactivate --sync'; const sb = await embedded().sandboxes().create( SandboxCreateOptions.create({ image: 'ghcr.io/trycua/linux:24.04', }) ); try { const env = await sb.spacesd(undefined); const shot = await env.screenshot(undefined); await env.click(Math.floor(shot.width / 2), Math.floor(shot.height / 2)); // Keys go to the focused window, so open one. await env.run(SpacesdCommand.create({ program: 'sh', args: ['-c', FOCUS_TERMINAL] })); await env.typeText('hello'); await env.hotkey(['ctrl', 'a']); await writeFile('desk.png', Buffer.from((await env.screenshot(undefined)).image)); } finally { await sb.delete_(); } ``` **CLI** ```bash test="cli-shape" id="cua-sb-create-linux" cua sb create linux --name desk cua sb screenshot desk -o desk.png cua sb view desk # opens the desktop in the browser viewer cua sb rm -f desk ``` ## Typed Cua Driver ```bash skip="host-install" pip install --extra-index-url https://wheels.cua.ai/simple 'cua-sandbox[driver]==0.9.0' ``` ```python test="container" id="typed-driver" prelude="image-desktop" from cua_driver import GetScreenSizeInput async with Sandbox.ephemeral(image) as sb: async with sb.driver.connect() as driver: result = await driver.get_screen_size(GetScreenSizeInput(session=None)) print(result.text) ``` `connect()` goes through spacesd's `/mcp` route with the sandbox's own credentials; `connect(service="mcp", transport="mcp")` selects a named MCP service instead. `cua mcp` is a stdio MCP server for agents: ```bash test="cli-shape" id="cua-mcp-desk" cua mcp --sandbox desk ``` ## Shell A shell is a PTY served by spacesd; no SSH needed. ```bash test="cli-shape" id="cua-sb-shell-dev" cua sb shell dev # interactive login shell cua sb shell dev python3 # a program in a PTY cua sb exec dev ls -la /tmp # one command, no PTY ``` ```python test="container" id="terminal" from cua_sandbox import Sandbox, Image async with Sandbox.ephemeral(Image.linux()) as sb: session = await sb.terminal.create(cols=80, rows=24) # command='python3' for a program pid = session['pid'] await sb.terminal.send_input(pid, 'echo hello\n') print(await sb.terminal.info(pid)) print('closed:', await sb.terminal.close(pid)) ``` ## Browser view Images with cua-spacesd, such as `Image.linux()`, serve an HTML5 viewer at `/viewer/` on the `env` service (port `3211`). It streams H.264 video (WebCodecs, PNG fallback) and Opus audio, and sends keyboard, mouse and touch input through Cua Driver. ![The cua-spacesd viewer with its toolbar, two app windows and a shared folder in sync]() ```bash test="cli-shape" id="cua-sb-view" cua sb view desk # open the viewer cua sb view desk --view-only --no-open # print a watch-only link cua sb view desk --files none --ttl 30m # no file access, link valid 30 minutes cua sb create linux --name desk2 --view # open it when the sandbox is ready cua viewer # one local page for every sandbox (Cua Spaces) ``` ```python test="container" id="desktop-viewer-url" from cua_sandbox import Image, Sandbox async with Sandbox.ephemeral(Image.linux()) as sb: print(await sb.viewer_url()) ``` The `cua` SDK's `sb.viewer_url(options)` (Rust, Python, TypeScript, Swift, Kotlin) returns `{url, expires_at_unix}`; options are `ttl_seconds`, `view_only`, `clipboard`, `files_root` and `microphone`. The MCP tool is `sandbox_view`. The link's fragment carries a viewer ticket that authorizes only the viewer's calls (never processes or tokens), expires (default 1 hour, at most 24) and is revoked when the root token rotates. In the cloud it is a signed `env` service URL. The toolbar has full screen, scaling, a keys menu, two-way clipboard, file upload (to `~/Downloads`), folder sharing, microphone and stats. | Feature | Chrome, Edge | Firefox | Safari | | --- | --- | --- | --- | | Video, input, typing, audio | yes | yes | yes | | Clipboard to the sandbox | Ctrl+V, and on focus | Ctrl+V | Ctrl+V | | Clipboard from the sandbox | automatic (text and images) | automatic (text) | automatic, or a one-click Copy | | File upload | yes | yes | yes | | Folder sharing | yes | no | no | | Microphone | yes | yes | not verified | | Full screen | yes, with keyboard lock | yes | yes | **Folder sharing** picks a local folder and keeps it in sync with `~/Shared/` both ways. On a conflict both are kept (the sandbox one as `name (sandbox copy YYYY-MM-DD HHMMSS).ext`). Sharing stops when you close the page or click Stop. ![The folder sharing prompt of the viewer]() **Note** The viewer needs a secure context (https or localhost) for WebCodecs, clipboard and folder access. Local sandboxes use a loopback port and the cloud uses https. Over plain http to a LAN address it falls back to PNG frames without clipboard or folders. Images without cua-spacesd that serve their own web display page open with `cua sb view NAME --service `. ## Attach to or run spacesd To use any machine that runs cua-spacesd, or to run it yourself, see [Attach to or run cua-spacesd](). --- # Space thumbnails Read a Space's latest small preview from the cache every client on the machine shares. For a small preview of a Space (a tile, a list row, a placeholder while a stream connects), use `Space.thumbnail` rather than `screenshot`. It reads one cache that every client on the machine shares (the Spaces app, the CLI, your scripts): a small JPEG of the primary display and when it was captured. The cached image comes back at once when it is younger than `max_age_ms`; otherwise the SDK captures a fresh one through cua-spacesd and keeps it for the next caller. When that capture fails, the older image comes back, and `captured_at_ms` says how old it is. **Python** ```python skip="no-runner" import cua space = await cua.connect().spaces().space("local:dev") thumb = await space.thumbnail(max_age_ms=120_000) # None: any age print(thumb.width, thumb.height, thumb.captured_at_ms) ``` **TypeScript** ```ts skip="no-runner" import { connect } from '@trycua/cua'; const space = await connect().spaces().space('local:dev'); const thumb = await space.thumbnail(120_000n); // undefined: any age console.log(thumb.width, thumb.height, thumb.capturedAtMs); ``` **Swift** ```swift skip="no-runner" import Cua let space = try await Cua.connect(address: nil, token: nil).spaces().space(space: "local:dev") let thumb = try await space.thumbnail(maxAgeMs: 120_000) // nil: any age print(thumb.width, thumb.height, thumb.capturedAtMs) ``` The cua daemon owns the cache under `~/.cua/cache/thumbnails`. It keeps the latest image per Space across restarts, drops a Space's image when the Space is deleted or removed, and caps the cache at 32 MB (the oldest images go first). While any client has asked for a thumbnail in the last 10 minutes, the daemon also captures a new one for each running Space about every 90 seconds, so a preview is seldom more than a couple of minutes old. When nobody asks, it captures nothing and puts no load on the guests. --- # Attach to or run cua-spacesd Use any machine that runs cua-spacesd as a sandbox, run and configure cua-spacesd yourself, and publish it through the relay. cua-spacesd serves the desktop, shell, files and media of a machine ([Control the desktop]()). This page covers attaching to one you did not create and running it yourself. ## Attach to any spacesd Any machine that runs spacesd can be used as a sandbox: a container you started, a machine on your LAN, or one behind the relay. You need its address and env token. Nothing is created or deleted. ```bash skip="third-party-cli" export CUA_ENV_TOKEN=$(openssl rand -hex 24) docker run -d --name envbox --shm-size=512m -e CUA_ENV_TOKEN \ -p 127.0.0.1:3211:3211 ghcr.io/trycua/linux:24.04 ``` ```bash test="cli-shape" id="cua-spacesd-caps" cua spacesd caps http://127.0.0.1:3211 # version, OS, features cua spacesd shell http://127.0.0.1:3211 cua sb create --on direct:127.0.0.1:3211 --token "$CUA_ENV_TOKEN" --name envbox cua sb rm envbox # forgets the record; the machine keeps running ``` In Python, `Sandbox.connect(url="http://127.0.0.1:3211", token=...)` returns a regular `Sandbox`; in TypeScript, `embedded().spacesd(url, token)` returns the typed spacesd client. Addresses can be direct (`http://10.0.0.5:3211`), a cloud sandbox's `env` service URL, or a relay URL (`https:///m/`). To run spacesd on a machine, see [Run spacesd yourself](#run-spacesd-yourself); its RPCs are in the [protocol reference](). **Warning** The env token grants full control of the machine. Keep it out of URLs and logs, and bind spacesd to loopback or a private network unless it sits behind the relay or the cloud gateway. ## Run spacesd yourself On a machine you own, `cua host setup` installs spacesd as a service ([Unattended access]()). To run the binary by hand: ```bash skip="no-runner" CUA_ENV_TOKEN=... cua-spacesd # same as `cua-spacesd serve` cua-spacesd --print-config # effective config, never the token ``` | Flag | Env | Default | | --- | --- | --- | | `--listen` | `CUA_ENV_LISTEN` | `0.0.0.0:` with a token, `127.0.0.1:` without | | `--port` | `CUA_ENV_PORT` | `3211` (gRPC, gRPC-Web and HTTP routes) | | `--quic-port` | `CUA_ENV_QUIC_PORT` | `3212` (QUIC media; `0` disables it) | | `--token-file` | `CUA_ENV_TOKEN_FILE` | `/run/cua/env-token` | | `--data-dir` | `CUA_ENV_DATA_DIR` | `~/.cua/spacesd` | | `--downloads-dir` | `CUA_ENV_DOWNLOADS_DIR` | `~/Downloads` (teleport destination) | | `--scrollback-bytes` | `CUA_ENV_SCROLLBACK_BYTES` | 8 MiB per process | | `--allow-guest-power` | `CUA_ENV_ALLOW_GUEST_POWER` | off | | `--no-mcp`, `--no-driver`, `--no-desktop` | `CUA_ENV_NO_MCP`, `CUA_ENV_NO_DRIVER`, `CUA_ENV_NO_DESKTOP` | all on | A non-loopback bind without a token is refused. Logs go to stderr (`CUA_ENV_LOG`, default `info`). Other subcommands: `join`, `token-sync` (Unix), `doctor` and `build-info`; `cua-spacesd --help` lists every flag. ### Viewer architecture One web app and protocol (crate `cua-spacesd-html5`), served two ways: | Mode | Where | Serves | | --- | --- | --- | | Embedded | cua-spacesd, `/viewer/` on port `3211` | the viewer of that machine | | Standalone | `cua viewer` (loopback), or the `cua-spacesd-html5` binary for gateways | a list of sandboxes and the same page per sandbox at `/s//viewer/`, piping gRPC-Web, `/media` and `/files` to each sandbox and adding only transport credentials | The page itself is public; every call it makes is authorized by the viewer ticket. The viewer ships with Cua Spaces (source-available, FSL-1.1-MIT): the `cua` in the Spaces apps runs `cua viewer`, and the open source `cua` hands the command to it. ### Tokens spacesd takes the first non-empty token from `--token`, `CUA_ENV_TOKEN`, then `--token-file`. Clients send it as `authorization: Bearer ` or `x-cua-env-authorization` (the Fleet gateway keeps `authorization` for itself). | Mode | Behavior | | --- | --- | | `SystemService.Init` | An authenticated caller rotates the token and sets env, user, working directory and CA bundle | | `--await-token-file` | Token only from the file, polled every 500 ms; until it appears only `GetCapabilities` and `Health` answer. Changing or emptying the file rotates or revokes every session | | `--insecure-bootstrap` | Tokenless non-loopback bind behind an authenticated gateway: only `GetCapabilities`, `Health` and `Init` answer until the first `Init` sets a token | | `token-sync` | Root helper that mirrors a root-only token file to a `0600` file spacesd's user can read | ### Relay (join) `cua-spacesd join` serves on loopback and publishes the machine through a `cua-relay` over one outbound WebSocket, so a machine behind NAT needs no inbound port. Clients reach it at `https:///m//` (Spaces: `relay:`). ```bash skip="no-runner" CUA_ENV_TOKEN=... cua-spacesd join --relay wss://relay.example --relay-token-file ~/.cua/relay-token ``` | Flag | Env | Default | | --- | --- | --- | | `--relay` | `CUA_ENV_RELAY_URL` | required | | `--relay-token`, `--relay-token-file` | `CUA_RELAY_TOKEN`, `CUA_RELAY_TOKEN_FILE` | one is required; the file is re-read before each reconnect | | `--machine-id-file` | `CUA_ENV_MACHINE_ID_FILE` | `/id` | | `--host-policy` | `CUA_HOST_POLICY` | owner, allowlist and sharing (account mode) | | `--relay-jwks` | `CUA_RELAY_JWKS` | pins the relay's signing keys | | `--heartbeat-secs` | | `15` | ### Install as a service `libs/cua-spacesd/packaging/install.sh` (`--binary`, `--version`, `--token`, `--token-file`, `--no-service`, `--dry-run`) installs the binary and one of: | OS | Service | Token and state | | --- | --- | --- | | Linux | systemd unit `cua-spacesd.service`: `cua-spacesd serve --allow-guest-power`, `Restart=always` | `EnvironmentFile=/etc/cua/spacesd.env`; state in `/var/lib/cua/spacesd` | | macOS | LaunchAgent `com.trycua.spacesd` in the GUI session | `~/.cua/spacesd/token`; logs in `~/Library/Logs/cua-spacesd.log` | | Windows | Scheduled task `cua-spacesd` at logon, in the interactive session | `%USERPROFILE%\.cua\spacesd\token` | --- # Set up local runtimes Which runtime runs a local sandbox, how to check and set up the host, and how to pick a kind or runtime. Local sandboxes run on this machine with no account. Local is the built-in default: omit `on=` (`local=` in cua-sandbox, `--on` in the CLI), or [change the default](). The SDK picks the kind and runtime from the image and starts it itself. | Image | Runtime (`auto`) | | --- | --- | | Container image (any OCI rootfs, `Image.linux()`, `python:3.12-slim`) | `gvisor` when installed, else `runc`, on Docker or Podman | | Linux or Windows VM (`-disk` containerDisk, `kind="vm"`, qcow2, ISO) | `qemu`: KVM on Linux, HVF on macOS, WHPX on Windows, TCG otherwise | | macOS VM (`Image.macos()`) | `lume`, Apple silicon only (the SDK starts `lume serve`) | | Linux arm64 VM on Apple silicon without QEMU | `lume` | ## Check and set up the host ```bash test="cli-shape" id="cua-runtime-doctor" cua runtime doctor # what this host can run (read-only) cua runtime setup --dry-run # the install plan cua runtime setup # install what is missing; or name qemu, lume, container ``` `cua doctor` checks the host, an image and a sandbox's guest in one report. **Warning** Not supported locally: macOS containers, macOS images that are not Lume images, and hosts with no container engine (install Colima or Docker Engine). `Image.windows()` is amd64 and runs under slow TCG emulation on Apple silicon. ## Pick a kind or runtime ```bash test="cli-shape" id="cua-sb-create-linux" cua sb create linux --name dev # a gVisor container cua sb create linux --kind vm --name dev-vm # its -disk variant under QEMU cua sb create linux --runtime runc --name dev-runc # a container without gVisor cua sb create macos:tahoe --name mac # a Lume VM cua sb rm dev ``` `--runtime` takes `auto`, `gvisor` or `runc` for containers and `qemu` or `lume` for VMs; a runtime implies its kind. A combination that does not exist fails with `InvalidPlacement` and lists the valid values: ```text output invalid placement: runtime qemu runs vm sandboxes, not container ones locally; valid runtime: auto, gvisor, runc ``` `pool:` runs a cloud pool's template image locally (it needs cloud credentials to read the template): ```bash test="cli-shape" id="cua-sb-create" cua sb create pool:my-pool --name copy-of-my-pool ``` In code, pass `kind=` and `runtime=` (`Sandbox.create(image, kind="vm", runtime="qemu")`); cua-sandbox still accepts a `Runtime` object ([runtime support]()). ## What differs locally - The host's architecture; others run emulated and slowly. - Services and public URLs are served on loopback (public URLs by the cua daemon, which the SDK starts). - [Sidecars]() need `runtime="runc"` where gVisor would run. - Image layers build into your container engine before boot; VM images apply them after boot through cua-spacesd. - A sandbox runs until you delete it; `keep_alive` and `cloud=` raise. - Images without cua-spacesd fall back to the runtime's agentless path (QMP, VNC, SSH or ADB) where there is one. ## Disk usage and cleanup Images share a cache budget and are evicted least recently used first; a pull that would leave under 5 GiB free fails with `InsufficientDisk`. `cua cache du` shows what images and sandboxes take. See [Disk usage and cleanup](). --- # Disk usage and cleanup What local sandboxes keep on disk and how the SDK keeps it bounded. Images, VM disks and logs live under `~/.cua` (`CUA_HOME` moves them). ```bash test="cli-shape" id="cua-cache-du" cua cache du # usage per category, budget, free space cua sb ls # SIZE column (--no-size skips it) ``` ## Automatic cleanup - **Cache budget.** Pulled images, macOS base VMs and local builds share one budget: 30 GiB or 10% of free space, whichever is smaller. Least recently used entries go first, after every pull and build and when the daemon is idle. - **Never removed:** sandboxes (running or stopped), anything a sandbox uses, checkpoints, and Docker objects the SDK did not create or pull. - **Ephemeral sandboxes** are deleted with their handle. If the program crashes, the next `cua sb` command or the daemon deletes them 10 minutes later. - **Logs** are capped at 10 MiB per file (also the Lume installer's `/tmp` logs when cua installed Lume). - **Screenshots** from `cua do screenshot` without `--save` go to `~/.cua/screenshots`; the newest 20 are kept. ## Free space now ```bash test="cli-shape" id="cua-cache-prune" cua cache prune --dry-run # what would go cua cache prune # down to the budget cua cache prune --all # every unused cache entry cua sb rm my-box -f # a sandbox and all its disks ``` ## Settings ```bash test="cli-shape" id="cua-cache-config" cua cache config --budget 50G --min-free 10G ``` ```json title="~/.cua/cache.json" test="config" id="cua-cache-json" { "budget": "50G", "min_free": "10G", "warn_free": "20G", "orphan_reap_minutes": 10, "auto_gc": true } ``` The environment wins: `CUA_CACHE_BUDGET` (`auto`, `off` or a size), `CUA_DISK_MIN_FREE` (default `5G`), `CUA_DISK_WARN_FREE` (`20G`), `CUA_ORPHAN_REAP_MINUTES` (`10`), `CUA_CACHE_AUTO_GC` (`0` turns it off). ## Low disk A pull, build or VM create that would leave less than `CUA_DISK_MIN_FREE` fails before writing anything: `CuaError.InsufficientDisk` in the SDKs, exit code 7 in the CLI. ```text output cua: insufficient disk: not enough disk space to pull ghcr.io/trycua/linux:24.04: it needs about 3.2 GiB and cua keeps 5.0 GiB free, but only 6.1 GiB is available on /Users/me/.cua/images (run `cua cache prune` to free space, or lower CUA_DISK_MIN_FREE) ``` ## cua-bench results Kept until you remove them; cua-bench warns past 10 GiB (`CUA_BENCH_WARN_SIZE`). Keep fewer with `cb run ... --keep-runs 20` or `cb prune --runs --keep 20`. --- # Run sandboxes in the cloud Run the same sandbox code in the Cua cloud, make the cloud your default, set up credentials, and see what differs from local. Cloud sandboxes use the same code as local ones with `on="cloud"` (`local=False` in cua-sandbox), and `--on cloud` in the CLI. To run in your own AWS, Google Cloud or Modal account instead, see [Use your own cloud](). The SDK manages the capacity: no pools to create, and idle capacity is removed for you. ## Credentials ```bash test="cli-shape" id="cua-auth-login" cua auth login # browser sign-in; --remote for SSH sessions cua auth whoami # verifies the identity against the cloud ``` The CLI and every SDK resolve credentials in this order: | Credential | Set with | Use for | | --- | --- | --- | | Bearer token | `FLEETS_TOKEN` | CI with workload identity (`cua wif-token github`) | | API key | `CUA_CLIENT_ID` + `CUA_CLIENT_SECRET` | Scripts, services, CI | | Signed-in session | `cua auth login` | Your machine | For scripts, create an API key while signed in. It prints the ID and secret once; store them in a secret manager: ```bash test="cli-shape" id="cua-auth-keys-create" cua auth keys create my-script ``` ```bash skip="placeholder" export CUA_CLIENT_ID="" export CUA_CLIENT_SECRET="" unset FLEETS_TOKEN # a token takes precedence over the key ``` Manage keys with `cua auth keys ls` / `rm ID` or under [API keys](https://run.cua.ai/user-keys). Without credentials, a cloud create fails with: ```text output Fleet credentials missing: run `cua auth login` or set CUA_CLIENT_ID/CUA_CLIENT_SECRET, or pass local=True ``` ## Run it ```bash test="cli-shape" id="cua-sb-create-linux" cua sb create linux --on cloud --name demo # a gVisor container cua sb create linux --on cloud --kind vm --name demo-vm # a KubeVirt VM cua sb exec demo -- uname -a cua sb rm demo -f ``` In code, pass `local=False` to the [quickstart]() calls, and `kind=` / `runtime=` as locally (cloud runtimes: `gvisor` for containers, `kubevirt` for VMs). `CloudOptions` tunes the managed capacity: | Field | Default (env override) | Effect | | --- | --- | --- | | `warm` | on for the canonical images (`CUA_FLEET_WARM`) | Keep one ready sandbox of this image | | `max_pool_size` | `10` (`CUA_FLEET_MAX_POOL_SIZE`) | Autoscaling ceiling | | `claim_ttl` | 15 min (`CUA_FLEET_CLAIM_TTL`) | Lifetime after your process stops renewing | | `pool` | none | Claim from a pool you manage ([Cua Fleets]()) | `cpu` and `memory` are part of the shape: a new shape is a new pool. Managed pools idle for 30 minutes are deleted (`CUA_FLEET_POOL_IDLE_GC`, `off` disables it; `cua fleet pools gc` runs it now). ## Choose where sandboxes run by default Without `--on` (or `on=` / `local=`) a sandbox runs on this machine. To make the cloud your default: ```bash test="cli-shape" id="cua-config-set-default-on" cua config set default.on cloud # written to ~/.cua/config.toml cua config list # each setting, its value and its source ``` ```text output KEY VALUE SOURCE default.on cloud config default.kind auto default default.runtime auto default ``` Now `cua sb create linux` and `Sandbox.create(...)` run in the cloud, and `--on local` still runs one here. `cua sb ls` lists every location either way. The first match wins: 1. `--on`, `on=` or `local=` 2. `CUA_DEFAULT_ON` (one shell or CI job) 3. `default.on` in `$CUA_HOME/config.toml` 4. `local` `default.kind` and `default.runtime` (`CUA_DEFAULT_KIND`, `CUA_DEFAULT_RUNTIME`) work the same way, and `cua doctor` shows each default with its source. The SDKs read and write the same file: ```python test="docs" id="cloud-config-default-on" import cua cua.config_set("default.on", "cloud") entry = cua.config_get("default.on") print([entry.value, entry.source]) # ['cloud', 'config'] cua.config_unset("default.on") ``` Switch back with `cua config unset default.on` (or `cua config set default.on local`). With a cloud default and no credentials, a create fails and says to run `cua auth login` or `cua config set default.on local`. ## What differs from local - Public or [private]() registry images, amd64 only: containers under gVisor, containerDisks as KubeVirt VMs. No macOS. - A cold image's first start takes a few minutes; canonical images are warm. - Services go through the gateway: `url()` is a signed URL and `request` adds the gateway's credentials. - `command=`, `env=` and [sidecars]() work on container and VM images; VM `env` values are single-line. - The sandbox lapses `claim_ttl` after your process stops renewing it ([Lifecycle]()). No suspend or restart. - Usage is billed while a sandbox runs. **Note** Image builds from layers are not in the cloud yet: a cloud sandbox with layers fails with a clear error. Build locally, push, and reference the image ([Fleet images]()). For named pools, fixed warm replicas, Terraform or per-claim secrets, use [Cua Fleets](). For errors such as `401` or `403`, see [Troubleshoot](). --- # Use your own cloud Run sandboxes and Spaces in your own AWS, Google Cloud or Modal account with --on aws, --on gcp or --on modal, with Cua's tags, time limits and cleanup on everything it creates. Sandboxes and Spaces can run in a cloud account you own: `--on aws` (EC2), `--on gcp` (Compute Engine) or `--on modal` (Modal Sandboxes). The same images, the same SDK calls and the same `cua-bench` runs work there; you pay the cloud directly. Each sandbox dials out to the cua.ai relay as a machine of your account, so nothing in your cloud listens on the internet, and your laptop, your agents and the Cua Spaces app reach it wherever they are. **Warning** Everything here creates resources that your cloud account bills for. Cua gives each one a time limit (8 hours by default) and cleans up after itself, but set a budget in your cloud first. ## What you need - The cloud's own CLI, signed in. Cua uses its credentials where they are and stores only the names: - **AWS**: a profile in `~/.aws` (access keys, SSO or an assumed role). - **Google Cloud**: `gcloud auth login` and a project with the Compute Engine API enabled. - **Modal**: a profile in `~/.modal.toml` (`modal token new`). - A cua.ai sign-in (`cua auth login`). Each cloud sandbox joins the relay as a machine of your account. ## Connect a cloud `cua cloud status` shows each cloud, whether its credentials were found on this machine, and what it can run at what hourly cost: ```bash test="cli-shape" id="cua-cloud-status" cua cloud status ``` `cua cloud test` checks an account without creating anything. It checks the credentials, runs a dry run of a create, and reads the quota, the region and the machine types: ```bash test="cli-shape" id="cua-cloud-test" cua cloud test aws --profile default --region us-west-2 ``` `cua cloud connect` runs the same checks and connects only when they pass. `--default` also makes it the default location, so `cua sb create` and `create_space` go there when no location is given: ```bash test="cli-shape" id="cua-cloud-connect" cua cloud connect aws --region us-west-2 --default cua cloud connect gcp --project my-project --region us-central1 cua cloud connect modal --environment sandboxes ``` `--ttl-hours` sets how long each sandbox there lives before it deletes itself (default 8; `0` keeps it until you delete it; Modal caps a sandbox at 24): ```bash test="cli-shape" id="cua-cloud-connect-ttl" cua cloud connect aws --ttl-hours 2 ``` The Cua Spaces app does the same from **New Space**, **Your cloud**, **Connect a cloud**. ## Create sandboxes and Spaces A sandbox: ```bash test="cli-shape" id="cua-sb-create-aws" cua sb create linux --on aws --name research cua sb exec aws:research -- uname -a cua sb delete aws:research ``` From Python, including `cua-bench`'s runs: ```python skip="your-cloud" id="your-cloud-python" from cua_sandbox import Image, Sandbox async with Sandbox.ephemeral(Image.linux(), on="aws") as sb: print(await sb.shell.run("uname -a")) await sb.screenshot() ``` ```bash test="cli-shape" id="cb-run-aws" cb run cua-bench-basic --on aws ``` A Space is a cloud sandbox shown as the relay machine it joined as, `relay:`. Stop and start keep its disk (on AWS and Google Cloud): ```bash test="cli-shape" id="cua-spaces-create-aws" cua spaces create linux --on aws --name research cua spaces stop relay:cloud-0123456789abcdef cua spaces start relay:cloud-0123456789abcdef cua spaces delete relay:cloud-0123456789abcdef --force ``` Agents use the same `create_space` tool with `on="aws"`, and `cloud_status`, `cloud_test`, `cloud_connect` and `cloud_sweep` from the [Spaces contract](). ## What Cua creates One VM (or Modal sandbox) per sandbox, tagged `cua-managed=true` with your owner id and an expiry, and recorded in `~/.cua/cloud/state.json`. Nothing else: no IAM users or keys, no change to existing networks. What each cloud creates, the prices and the per-cloud notes: [Your cloud resources and costs](). ## Time limits and cleanup Three things end a sandbox that nobody deleted: - **It shuts itself down.** A VM runs `shutdown` at its time limit and checks it again after every reboot or start. Shutting down deletes it (AWS terminates on shutdown; Google Cloud's run limit deletes it). Modal ends a sandbox at its timeout. - **A failed create cleans up.** Whatever it made is deleted, and its relay machine is removed. - **`cua cloud sweep`** lists expired sandboxes and anything Cua created whose sandbox is gone. With `--delete` it deletes them; `--all` includes every Cua resource in that cloud: ```bash test="cli-shape" id="cua-cloud-sweep" cua cloud sweep cua cloud sweep aws --delete cua cloud sweep --all --delete ``` The sweeper touches only a resource that the cloud shows with `cua-managed=true` and this home's `cua-owner`, and that this home recorded or that carries its owner id. It never matches by name. Untagged resources and other owners' resources are never deleted, whatever they are called. `cua cloud disconnect` forgets a cloud and deletes nothing. It lists what Cua still records there. ## The relay Each sandbox is one machine of your cua.ai account on the relay: - Streams, files and presence pass through relay.cua.ai. There is no direct path from your cloud to your laptop. - An account holds at most 32 machines. Each cloud sandbox uses one, and its delete removes it. - Only your devices and the accounts you share a Space with can reach it. The guest holds only its own machine token, never your account credentials. --- # Your cloud resources and costs What Cua creates in your AWS, Google Cloud or Modal account, what it costs per hour, and the notes for each cloud. Details for [Use your own cloud](). ## What Cua creates | Cloud | Per sandbox | Shared | |---|---|---| | AWS | One EC2 instance and its disk. Docker runs the image on it. | One security group with no inbound rules, in the region's default VPC. | | Google Cloud | One Compute Engine instance and its disk. Docker runs the image on it. | A network with no inbound rules, so the project's default firewall rules never apply to it. | | Modal | One Modal sandbox running the image. | Nothing. | - Nothing else is created: no IAM users or keys, no change to existing networks, security groups or firewall rules. - The VM installs only Docker. The image carries its own cua-spacesd. - Every resource carries these tags (labels on Google Cloud): | Tag | Value | |---|---| | `cua-managed` | `true` | | `cua-owner` | This cua home's owner id | | `cua-space` | The sandbox's name | | `cua-machine` | Its relay machine | | `cua-created-by` | The cua version | | `cua-expires` | When it expires (Unix seconds) | - Every resource is recorded in `~/.cua/cloud/state.json` before the cloud call, with its cloud id once it has one. ## Costs `cua cloud status` shows the estimated on-demand price of each image family per hour while it runs (compute and disk, not network). A stopped VM bills only its disk. | Cloud | `linux` | `linux-slim` | |---|---|---| | AWS | `t4g.medium` (arm64), about $0.04/hour | `t4g.small`, about $0.03/hour | | Google Cloud | `e2-medium`, about $0.04/hour | `e2-small`, about $0.03/hour | | Modal | 1 core (2 vCPU) and 4 GiB, about $0.24/hour | the same | - Windows and Omarchy are not offered yet. They run as VMs inside the cloud's VM, which needs a nested-virtualization machine type; `cua cloud status` says so next to them. - macOS is not offered. On AWS it would need an EC2 Mac dedicated host, which has a 24-hour minimum. ## Per-cloud notes - **AWS**: - One region per connection (`--region`, else the profile's). Profiles, SSO and assume-role profiles from `~/.aws` work as the AWS CLI's do. - The instance allows only IMDSv2 with a hop limit of 1, so the sandbox's containers cannot read the instance's metadata (or its user data). It has no key pair and no instance profile. - A new account's vCPU quota may be low. `cua cloud test aws` does not read it yet; a create past it fails with AWS's own message. - **Google Cloud**: - One project and zone per connection (`--project`, `--region`, `--zone`). - The instance has no service account, so nothing on it can call Google APIs. Its containers reach the metadata server only for DNS. - `cua cloud test gcp` says when the Compute Engine API is disabled. - **Modal**: - One environment per connection (`--environment`) and a `~/.modal.toml` profile (`--profile`). - Sandboxes run under gVisor by default; `--runtime microvm` uses Modal's VM runtime. Neither has `/dev/kvm`, so only Linux images run. - A sandbox cannot be stopped and started, and lives at most 24 hours. - Cua talks to Modal through `cua-modal-helper`, which uses Modal's official Go SDK. Build it with `go build` in `libs/cua/crates/cua-contrib/modal-helper` and put it next to `cua`, on `PATH`, or in `CUA_MODAL_HELPER`. --- # Run on other sandbox providers (contrib) Run cua sandboxes on E2B, Daytona or Modal with --on e2b, --on daytona or --on modal, from the same registry images, with cua-spacesd, services and cua doctor. Contrib providers run the same images (the canonical `ghcr.io/trycua/linux`, or any registry image) on third-party sandbox platforms. The location is one more `--on` word: `--on e2b`, `--on daytona`, `--on modal`. Everything above the location works unchanged: services, `sb.spacesd()`, `cua doctor`, cua-bench. To run in a cloud account you own (AWS, Google Cloud, Modal), see [Use your own cloud](): it needs no provider key, and its sandboxes join the cua.ai relay. Contrib providers are opt-in. The default `cua` build knows the words and tells you how to get the provider; a build with `--features contrib` includes them. ## Set up ```bash skip="host-install" cargo install --locked --path libs/cua/crates/cua-cli --features contrib cua auth provider set e2b # reads E2B_API_KEY from stdin, never echoed cua auth provider ls # where each key comes from, never the value ``` An environment variable wins over a stored key: `E2B_API_KEY`, `DAYTONA_API_KEY`, or `MODAL_TOKEN_ID` and `MODAL_TOKEN_SECRET`. Modal documents no HTTP API, so `--on modal` goes through Modal's official Go SDK in a small helper, `cua-modal-helper`. Build it once and put it next to `cua` or on `PATH` (or point `CUA_MODAL_HELPER` at it): ```bash skip="host-install" (cd libs/cua/crates/cua-contrib/modal-helper && go build -o ~/.local/bin/cua-modal-helper .) ``` ## Create a sandbox ```bash test="cli-shape" id="contrib-cli" cua sb create linux --on e2b --name demo cua doctor e2b:demo --strict cua sb delete e2b:demo ``` The ref is `:` (`e2b:demo`, `daytona:demo`). `--claim-ttl` sets the platform's lifetime backstop (default one hour). ```python test="contrib" id="contrib-python" prelude="contrib" from cua_sandbox import Image, Sandbox async with Sandbox.ephemeral(Image.linux(), on="daytona") as sb: await sb.files.write_text("/tmp/hello.txt", "hello from daytona") print(await sb.files.read_text("/tmp/hello.txt")) # hello from daytona ``` cua-bench takes the same word: `cb run task --on e2b`. ## What happens to the image The SDK resolves and pins the image first, then maps it onto the platform: | Provider | Image step | Cached by | Entrypoint | |---|---|---|---| | E2B | a template built `fromImage` | digest, start command, shape | runs once at build (E2B snapshots it); the SDK installs the cua-spacesd token | | Daytona | a snapshot from `imageName` | digest, entrypoint, shape | runs in every sandbox, with the token in its environment | | Modal | `FROM` the pinned image, `ENTRYPOINT []` | Modal's image cache | the image's `ENTRYPOINT` and `CMD` as the command, with the token in its environment | A moved tag gets a new template. Requests a provider cannot run fail before any API call, with a typed error: VM variants (`--vm`), images without an amd64 build, Windows and macOS images, `network="none"`, sidecars, and (Daytona, Modal) private registries. Modal tunnels are fixed at create: declare every port with `--port`. ## Ports and services Declared services and cua-spacesd (3211) are reachable through the provider's HTTPS URLs; `sb.service(name).url()` and `sb.tunnel.forward(port)` return them. Ports are public on the provider's proxy: cua-spacesd authenticates with its own token, so keep other services authenticated too. ## Support matrix Measured nightly: each provider's lane creates `ghcr.io/trycua/linux:24.04`, runs `cua doctor --strict --json` inside it, and the table below is generated from the latest reports. A regression shows up here on the next run. | Provider | Kind | Verdict | display | input | streaming | audio | files | process | teleport | tunnels | mcp | auth | Measured | |---|---|---|---|---|---|---|---|---|---|---|---|---|---| | e2b | container | not measured | - | - | - | - | - | - | - | - | - | - | never | | daytona | container | not measured | - | - | - | - | - | - | - | - | - | - | never | | modal | container | not measured | - | - | - | - | - | - | - | - | - | - | never | `full`: the desktop works (display, input through cua-driver, streaming, files, auth). `container-only`: cua-spacesd answers but the desktop does not. `headless-only`: no cua-spacesd answered. `not measured`: no run yet. --- # Test in CI with Cua sandboxes Run tests against a Cua sandbox in GitHub Actions or any CI, inject the build under test, and fail on stale builds. The `cua-sandbox` action creates a sandbox from any image, waits until it is ready, runs your tests against it, uploads logs, the doctor report and a screenshot, and always deletes it, even when the job is cancelled. Every step is a plain `cua` command, so the same run works on your machine. ## GitHub Actions ```yaml test="config" id="ci-basic" jobs: e2e: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.97.1 - id: sandbox uses: trycua/cua/.github/actions/cua-sandbox@main with: image: linux run: | cua sb exec "$CUA_SANDBOX" uname -a cua sb screenshot "$CUA_SANDBOX" -o "$CUA_SANDBOX_ARTIFACTS/desktop.png" ``` - `image`: a canonical image, a bench image or any registry reference. - `on`: `local` (the default: Docker on the runner, or QEMU with KVM for VM images) or `cloud`. Cloud needs `CUA_CLIENT_ID` and `CUA_CLIENT_SECRET` in the job env. `claim-ttl` (default `60m`) is the backstop if cleanup never runs. - `run` runs on the runner with `CUA_SANDBOX` set to the sandbox ref. `guest-run` runs inside the sandbox. `guest-setup` runs as root inside it first, for example to install test dependencies. - `cua-version`: `source` (the default) builds the CLI from the action's ref. Pass `cua-bin` to use one you built. - Outputs: `ref`, `name`, `env-url`, `viewer-url`, `urls`, `overlays`, `doctor-status`, `artifacts-dir`. **Note** Pin the action to a commit (`@`) in CI you depend on. `@main` follows the repository. ## Test the code you just built An image bundles its own cua-spacesd, and runners may have their own cua-driver installed. To test your build instead, inject it with `overlay`: ```yaml test="config" id="ci-overlay" - uses: trycua/cua/.github/actions/cua-sandbox@main with: image: linux overlay: | cua-spacesd=target/release/cua-spacesd cua-driver=target/release/cua-driver expect: cua-driver=git:${{ github.sha }} run: ./tests/e2e.sh ``` Each overlay replaces the guest file atomically, is recorded with its sha256 and restarts what runs it. The action then runs `cua doctor` with an expectation per overlay. The job fails before your tests run if anything else runs, for example a daemon that was never restarted or a file replaced afterwards. On the runner, `PATH` resolves `cua-driver` to the injected build only, so a copy installed on the runner is never used. ## The same steps with the CLI ```bash test="cli-shape" id="ci-cli" cua sb create linux --name ci --wait desktop \ --overlay cua-driver=./target/release/cua-driver --json cua doctor local:ci --no-host --only build \ --expect cua-driver=sha256: cua sb exec local:ci uname -a cua sb rm local:ci --force ``` `--overlay` also works on a running sandbox (`cua sb overlay ci cua-driver=PATH`) and for any file: `NAME=PATH:/guest/path`. From Python, pass `overlay=` to `Sandbox.create`, or call `sb.overlay(...)` on a running sandbox. `cua-driver` and `cua-spacesd` find their guest path; any other file takes `(path, "/guest/path")`. If an overlay fails, the new sandbox is deleted and the error raised: ```python test="container" id="ci-overlay-python" prelude="local-cleanup" import os import tempfile from cua_sandbox import Image, Sandbox tool = os.path.join(tempfile.mkdtemp(), "tool") with open(tool, "w") as f: f.write("#!/bin/sh\necho build-under-test\n") os.chmod(tool, 0o755) sb = await Sandbox.create( Image.from_registry("python:3.12-slim"), command=["sleep", "infinity"], runtime="runc", overlay={"tool": (tool, "/usr/local/bin/tool")}, ) [result] = await sb.overlay({"tool": (tool, "/usr/local/bin/tool")}) print(result.name, result.target, result.sha256) await sb.destroy() ``` The Rust, TypeScript, Swift and Kotlin SDKs have the same `overlays` create option and `overlay` method. The action's steps are phases of `.github/actions/cua-sandbox/cua-sandbox.sh`, which also runs outside GitHub. Set the inputs as `CUA_SB_*` environment variables (for example `CUA_SB_IMAGE`, `CUA_SB_OVERLAY` and `CUA_SB_RUN`) and run `cua-sandbox.sh all`. It cleans up on exit and on Ctrl-C. ## Expectations `cua doctor REF --expect COMPONENT=WANT` fails unless that exact build runs. `WANT` is one of: - `sha256:`: the executable. - `git:`: the source revision it was built from. - a version. Components are `cua-spacesd`, `cua-driver` or an overlay name. Inside the sandbox, `cua-spacesd doctor --expect ...` does the same. --- # Run a coding agent in a sandbox Start Claude Code, Codex, Gemini CLI and other harnesses inside a sandbox, stream their events, follow up, interrupt, and collect results and files. `sandbox.agents()` runs a coding-agent harness (Claude Code, Codex, Gemini CLI, OpenCode, Goose, ...) inside the sandbox. The SDK installs the harness from pinned, checksum-verified sources, drives it over the [Agent Client Protocol](https://agentclientprotocol.com), and normalizes its output into events. A run lives in the sandbox (state under `~/.cua/agents/`), so it keeps working after your process exits; any client can reattach later. The examples below are regions of [`examples/agents-in-sandboxes/tour.py`](https://github.com/trycua/cua/blob/main/examples/agents-in-sandboxes/tour.py), which runs them end to end. ## Create a sandbox Any image with cua-spacesd works: the canonical `ghcr.io/trycua/linux:24.04`, your own registry image, or a benchmark image. ```python test="excerpt" id="create" source="examples/agents-in-sandboxes/tour.py#create" c = cua.embedded() sb = await c.sandboxes().create(cua.SandboxCreateOptions( on="local", # or "cloud" image=image, # any image with cua-spacesd name=name, memory_mb=4096, wait_for=[cua.ReadinessProbe(service="env")], )) ``` ## Run a harness Pass the provider key explicitly. `env_from_host` copies the named variables from this process's environment (known provider key names only); `env` takes values directly. ```python test="excerpt" id="run" source="examples/agents-in-sandboxes/tour.py#run" agents = await sb.agents() run = await agents.run( "claude-code", "Write primes.py that prints the first 10 primes, then run it.", cua.AgentRunOptions(env_from_host=["ANTHROPIC_API_KEY"]), ) print(run.run_id()) ``` `agents.run` returns once the run has started. The first run of a harness in a sandbox also installs it (`install` events); `await agents.ensure(["claude-code"])` installs ahead of time. ## Stream events `run.events(cursor, max)` returns a page of events and the cursor to continue from. This helper prints them until the turn ends: ```python test="excerpt" id="follow" source="examples/agents-in-sandboxes/tour.py#follow" async def follow(run, cursor=0, timeout=600): """Prints events until the turn ends; returns the cursor to continue from.""" deadline = time.monotonic() + timeout while time.monotonic() < deadline: page = await run.events(cursor, None) cursor = page.cursor for e in page.events: if e.line: print(f"{e.kind:12} {e.line.splitlines()[0][:100]}") if e.kind in ("turn_ended", "exited"): return cursor if page.caught_up: await asyncio.sleep(0.5) raise TimeoutError(f"{run.run_id()} is still working") ``` ```python test="excerpt" id="stream" source="examples/agents-in-sandboxes/tour.py#stream" cursor = await follow(run) ``` For a single turn, Python also has `async for e in run.stream(): ...`. Output of `tour.py` run against the scripted mock provider (`cua-mock-llm`), so the model reply is scripted, not generated; install lines are trimmed: ```text output install [install] claude-code verify sha512 integrity install [install] claude-code done /home/cua/.cua/tools/claude-code/2.1.281 turn_started > Write primes.py that prints the first 10 primes, then run it. message mock reply (scripted): ... turn_ended [turn 1 ended: end_turn] ``` ## Follow up and interrupt A follow-up continues the same session. It is queued while a turn runs. ```python test="excerpt" id="followup" source="examples/agents-in-sandboxes/tour.py#followup" await run.send("Now add a unit test for it and run the test.", None) cursor = await follow(run, cursor) ``` ```python test="excerpt" id="interrupt" source="examples/agents-in-sandboxes/tour.py#interrupt" await run.send("Rewrite it to use a sieve and benchmark both versions.", None) await asyncio.sleep(5) await run.interrupt() # cancels the turn in flight; the session stays open cursor = await follow(run, cursor) ``` ## Collect the result and files `result()` describes the last turn: status, stop reason, the agent's text, tool call count and token usage. `artifacts()` lists files the run created or changed in its working directory. `run.wait(timeout_ms)` waits for the turn to end, then returns the same result. ```python test="excerpt" id="result" source="examples/agents-in-sandboxes/tour.py#result" result = await run.result() print(result.status, result.stop_reason, result.tool_calls, result.usage_json) print(result.text) for a in await run.artifacts(): print(a.path, a.size) await run.stop() ``` `stop()` ends the run and verifies its process is gone. `remove()` also deletes the run's directory, secrets included. ## Fire and forget, reattach later `exit_when_idle=True` (`--exit-when-idle`) stops the run by itself once the prompt is answered. From the CLI: ```bash test="cli-shape" id="cli-run" cua agent run local:dev claude-code "Fix the failing test in tests/" \ --env-from-host ANTHROPIC_API_KEY --repo https://github.com// --exit-when-idle cua agent ls local:dev cua agent logs local:dev --follow cua agent send local:dev "Now add a regression test" cua agent interrupt local:dev cua agent status local:dev cua agent stop local:dev ``` Any process can pick up the runs of a sandbox: ```python test="excerpt" id="reattach" source="examples/agents-in-sandboxes/tour.py#reattach" agents = await sb.agents() # any process, later runs = await agents.list() # newest first for info in runs: print(info.run_id, info.harness, info.status, info.label) run = await agents.get(runs[0].run_id) print((await run.result()).text) ``` ## Options [Configure coding agents]() covers MCP servers for the agent, custom endpoints and proxies, the supported harnesses, credentials, and every event kind. ## Related - [`examples/agents-in-sandboxes`](https://github.com/trycua/cua/tree/main/examples/agents-in-sandboxes): run a harness over a task file in any image and collect a JSON report. - [cua-bench](): `--agent harness` uses a harness as a benchmark agent. - [Connect to an MCP server]() in a sandbox. --- # Configure coding agents Give a sandboxed coding agent MCP servers, a custom endpoint, and credentials; the supported harnesses and the event kinds they emit. These options extend [Run a coding agent in a sandbox](). The examples are regions of [`examples/agents-in-sandboxes/tour.py`](https://github.com/trycua/cua/blob/main/examples/agents-in-sandboxes/tour.py). ## Give the agent MCP servers Every run gets the sandbox's own MCP tools (cua-driver: screen, input, windows) unless `sandbox_mcp=False` or the harness cannot take them (Pi has no MCP client, Hermes and OpenClaw read MCP only from their own config; Antigravity is off by default, see the table). Add more with `mcp_servers`: a URL (streamable HTTP, as reachable from inside the sandbox) or a command (stdio, run in the sandbox). Header values may be secrets; they are handled like keys. ```python test="excerpt" id="mcp" source="examples/agents-in-sandboxes/tour.py#mcp" run = await agents.run( "openai-codex", "Use the docs server to look up our API style guide, then review api.py.", cua.AgentRunOptions( env_from_host=["OPENAI_API_KEY"], mcp_servers=[ cua.AgentRunMcpServer(name="docs", url="https://mcp.example.com/mcp", headers={"Authorization": "Bearer "}), cua.AgentRunMcpServer(name="fs", command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", "/srv"]), ], exit_when_idle=True, ), ) ``` ```bash test="cli-shape" id="cli-mcp" cua agent run local:dev openai-codex "Review api.py" --env-from-host OPENAI_API_KEY \ --mcp docs=https://mcp.example.com/mcp --no-sandbox-mcp ``` ## Use a custom endpoint or proxy `base_url` points the harness at a gateway, proxy or compatible server; `wire` picks its format (`anthropic`, `openai-responses`, `openai-chat`, `gemini`) when it differs from the harness's own. ```python test="excerpt" id="endpoint" source="examples/agents-in-sandboxes/tour.py#endpoint" run = await agents.run( "claude-code", "Summarize the repository in five bullet points.", cua.AgentRunOptions( env={"ANTHROPIC_API_KEY": ""}, base_url="https://llm-gateway.internal.example.com", model="claude-sonnet-4-5", exit_when_idle=True, ), ) ``` ```bash test="cli-shape" id="cli-endpoint" cua agent run local:dev claude-code "Summarize the repository" \ --env-from-host ANTHROPIC_API_KEY --base-url https://llm-gateway.internal.example.com \ --model ``` ## Harnesses ```bash test="cli-shape" id="cli-harnesses" cua agent harnesses ``` ```python test="docs" id="harness-ids" import json import cua print([h["id"] for h in json.loads(cua.agent_harnesses())]) # ['claude-code', 'openai-codex', 'gemini-cli', 'google-antigravity', 'opencode', 'goose', 'pi', 'hermes', 'openclaw'] ``` | Harness | Key variables (any one) | Endpoint wire | Notes | | --- | --- | --- | --- | | `claude-code` | `ANTHROPIC_API_KEY`, `CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_AUTH_TOKEN` | `anthropic` | | | `openai-codex` | `OPENAI_API_KEY`, `CODEX_API_KEY` | `openai-responses` | | | `gemini-cli` | `GEMINI_API_KEY`, `GOOGLE_API_KEY` | `gemini` | Needs a paid API key | | `google-antigravity` | `GEMINI_API_KEY` | `gemini` | Headless ACP server, no terminal CLI; sandbox MCP off by default | | `opencode` | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY` | `openai-chat` | | | `goose` | `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `OPENROUTER_API_KEY` | `openai-chat` | | | `pi` | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY` | `openai-chat` | Community ACP adapter; no MCP client | | `hermes` | `OPENROUTER_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` | `openai-chat` | Needs git; ignores run MCP servers | | `openclaw` | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `OPENROUTER_API_KEY` | `openai-chat` | Starts its own loopback Gateway; no per-run MCP servers | `cua agent harnesses` (or `cua.agent_harnesses()`) prints the full record: what each installs, its interactive CLI, and known limits. ## Credentials - Keys reach a run only as explicit per-run env (`env`, `env_from_host`, `--env-from-host`). They are written with mode 0600 in the run's directory, redacted from its event log, and never passed in argv. - Nothing is copied from your machine's agent config (`~/.claude`, `~/.codex`, and so on). - To use a signed-in session instead of a key, move it with [teleport]() (`claude-code` is a built-in app), which shows what will move and asks for approval first. - See also [Pass secrets safely](). ## Event kinds Each event has `seq`, `turn`, `kind`, optional `text` and tool fields, a one-line `line` rendering, and `json` (the raw record, ACP payload included). | Kind | Meaning | | --- | --- | | `install` | Installing the harness or its dependencies | | `initialized`, `authenticated`, `session`, `mode` | ACP setup | | `turn_started` | A prompt or follow-up began (`text` is the prompt) | | `message`, `thought` | Agent text and reasoning | | `user_message` | A user message the agent echoed | | `tool_call`, `tool_update` | A tool call and its progress (`tool_title`, `tool_kind`, `tool_status`) | | `plan` | The agent's plan entries | | `usage` | Token usage update | | `permission` | A permission request, auto-approved (the sandbox is the boundary) | | `commands`, `info`, `notice` | Available commands and informational updates | | `cancel_requested` | `interrupt()` was received | | `turn_ended` | The turn finished (`stop_reason`, usage) | | `error` | A harness or provider error | | `exited` | The run's process ended | | `other` | Anything else the harness sent | --- # Browse the web from your agent with Cua Give a coding agent (Claude Code, Codex, Cursor and others) a real browser inside a disposable sandbox with nothing but the cua MCP server. Discover an image, create a browser sandbox, read, click, fill and screenshot pages, teleport a signed-in session with consent, and clean up. Once Cua is installed, a coding agent can browse the web with no other tool or setup. The browser runs inside a sandbox, never on your machine: a local container (gVisor when installed), a VM, or a Cua Fleet sandbox. The agent drives it through the sandbox's own Cua Driver, whose typed browser tools the `cua` MCP server reaches with `call_tool`. ## Set up your agent `cua agents setup` installs the Cua skills (the `cua-sandboxes` skill carries the browsing recipe) and adds the `cua` MCP server to every agent it finds. ```bash test="cli-shape" id="browse-setup" cua agents setup --agents claude,codex,cursor -y ``` Restart the agent. Everything below is what it does with the `cua` MCP tools. ## 1. Pick an image `images_list` returns the image catalog the [image catalog details]() is generated from: each image's OS, container or VM form, runtimes, whether it ships cua-spacesd, and its browsers. `browser_tools: true` marks the images whose browser the Cua Driver browser tools drive. ```python test="excerpt" id="browse-discover" source="tests/e2e/browse/e2e_browse.py#discover" cat = await agent.ok("images_list", {"browser": True}) images = [i for i in cat["images"] if i["browser_tools"]] ``` The same list from a terminal: ```bash test="cli-shape" id="browse-images" cua images ls --browser ``` ## 2. Create a sandbox with a browser `sandbox_create` with `browser: true` starts the canonical Linux image (`ghcr.io/trycua/linux:24.04`), launches Chromium with a fresh throwaway profile inside it, binds the window for the browser tools and, with `url`, opens a first page. It runs on this machine by default. It returns the sandbox `id` and the `session`, `target_id` and `tab_id` every browser call takes. ```python test="excerpt" id="browse-create" source="tests/e2e/browse/e2e_browse.py#create" sb = await agent.ok("sandbox_create", create) space = sb["id"] ids = { "session": sb["session"], "target_id": sb["browser"]["target_id"], "tab_id": sb["browser"]["tab_id"], } ``` From a terminal, the same sandbox (watch it with `cua sb view`): ```bash test="cli-shape" id="browse-create-cli" cua sb create --browser --open https://example.com --name web ``` ## 3. Read, click, type Every browser action is a `call_tool` on the sandbox with the ids above plus the tool's own arguments: | Tool | Arguments | Does | |---|---|---| | `get_browser_state` | `snapshot_format: "semantic_v2"`, `include_screenshot` | Reads the page: an outline and refs such as `p2:0`; optionally the tab as an image | | `browser_navigate` | `url` | Opens a URL | | `browser_click` | `ref`, `delivery_mode: "foreground"` | Clicks an element | | `browser_type` | `ref`, `text`, `replace` | Types; `replace: true` fills a field; text `"\n"` with `mode: "keystrokes"` presses Enter | | `browser_pointer` | `action`, `ref` | Hover, double-click, right-click, scroll, drag | A small helper keeps the calls short: ```python test="excerpt" id="browse-helpers" source="tests/e2e/browse/e2e_browse.py#helpers" async def driver(tool: str, **args) -> dict: return await agent.ok( "call_tool", {"space": space, "tool": tool, "arguments": {**ids, **args}} ) async def read() -> dict: return await driver("get_browser_state", snapshot_format="semantic_v2") async def click(ref: str) -> None: # In a sandbox nobody else uses the browser window, so the # trusted route may activate it (Linux Chromium needs that). await driver("browser_click", ref=ref, delivery_mode="foreground") ``` Refs belong to the read that returned them and stay valid until the page changes. Read again after a navigation or a submit. ## 4. Test a page served in the sandbox The sandbox is a whole computer: serve your app in it (`space_write`, `space_bash`) and browse `http://127.0.0.1:`. This fills and submits a form: ```python test="excerpt" id="browse-fill" source="tests/e2e/browse/e2e_browse.py#fill" await driver("browser_navigate", url=f"http://127.0.0.1:{FORM_PORT}/") # One read; its refs stay valid until the page changes. page = await read() name_ref = find_ref(page, "textbox", "Name") agree_ref = find_ref(page, "checkbox", "I agree") submit_ref = find_ref(page, "button", "Submit") await driver("browser_type", ref=name_ref, text="Ada Lovelace", replace=True) await click(agree_ref) await click(submit_ref) ``` ## 5. Sign in with your own session (with consent) To browse as you on a site you are signed in to, the agent can teleport that site's session from your browser (Firefox or Chrome) into the sandbox with `teleport_browser_session`. It names the exact sites; nothing is selected by default and there is no "all sessions" option. The first call moves nothing: it returns what would move and a consent requirement, which the agent shows you. Only your explicit approval moves those sites' sessions into the sandbox. Delete the sandbox, or sign out there, to end them. ## 6. Clean up ```python test="excerpt" id="browse-cleanup" source="tests/e2e/browse/e2e_browse.py#cleanup" await agent.call("sandbox_delete", {"name": space}) ``` ## Run the whole session [`tests/e2e/browse/e2e_browse.py`](https://github.com/trycua/cua/blob/main/tests/e2e/browse/e2e_browse.py) runs every step above as an MCP client (the official MCP Python SDK) against `cua mcp`, with no model in the loop. CI runs it in the container lane. ```bash skip="host-install" pip install mcp CUA_E2E_BROWSE=1 python tests/e2e/browse/e2e_browse.py ``` ## What the browser tools cover Reading pages (outline, refs, text, a scoped `query`), navigation, clicks, typing and filling, Enter, hover, scroll, drag, uploads (`browser_set_input_files`), downloads (`browser_download`), page dialogs (`browser_dialog`) and tab screenshots. They drive Chromium-family browsers over the Chrome DevTools Protocol. Firefox ships in the image too; drive it with the desktop tools (`computer_screenshot`, `computer_click`). Waiting is a bounded loop of reads; there is no JavaScript `eval`, network interception or cookie editing tool. --- # Run agent frameworks in a sandbox Run OpenAI Agents, Codex, Cursor, OpenClaw, an Omarchy desktop and a Minecraft agent in Cua sandboxes. Each recipe gives a third-party agent its own desktop instead of yours. Sandboxes run on this machine by default; to run them in your own AWS, Google Cloud or Modal account, see [Use your own cloud](). The Python script needs Python `>=3.11,<3.14` with [`uv`](https://docs.astral.sh/uv/) and pins `cua-sandbox==0.9.0`. Your controller owns the sandbox and must delete it. ## OpenAI Agents API A controller creates an `Image.linux()` sandbox, starts `codex exec-server` in it as an OpenAI `self_hosted` environment, and gives the agent Cua Driver as a stdio MCP server. It needs an OpenAI application key (`OPENAI_API_KEY`) and a separate environment key with only `api.agents.environments.connect` (`CODEX_API_KEY`), which is written to a mode-`600` file and deleted after the executor reads it. ```bash skip="third-party-cli" curl -fsSLO https://cua.ai/docs-assets/scripts/openai-agents/run_openai_agents.py uv run run_openai_agents.py ``` The sandbox runs on this machine. To run it in your own cloud, connect the account with `cua cloud connect` and set `CUA_ON=aws` (or `gcp`, `modal`) before `uv run`. The run drives an XFCE terminal through Cua Driver, reconnects the same environment ID, downloads an artifact, and deletes the session and the sandbox. Deleting an OpenAI session alone does not release the sandbox. ## Codex native Computer Use (Windows) Codex-in-ChatGPT's Computer Use plugin runs only on macOS and Windows, with an eligible, signed-in ChatGPT account. To let it take over a VM instead of your desktop, create a Windows sandbox (`Image.from_registry(WINDOWS_IMAGE, os_type="windows", kind="vm")`) from a disk that runs cua-spacesd, open it with `cua sb view`, install the ChatGPT desktop app and Codex CLI, and run `codex plugin add computer-use@openai-bundled`. Sign-in and approvals stay interactive. ## Cursor Cloud Agents Cursor's self-hosted Team Pool worker can run in a Linux Space on your Mac or on a machine you set up with [`cua host setup --provide-spaces`]() (add `--on ""` and use the id `create` prints). It needs Cursor Enterprise with [Self-Hosted Machines](https://cursor.com/docs/cloud-agent/self-hosted) allowed and a service-account API key. Create the Space, install the Cursor CLI, clone the repository the pool serves, and copy the key in as a file so it never appears on a command line: ```bash test="cli-shape" id="cursor-worker-setup" cua spaces create linux --name cursor-worker --cpus 4 --memory-mb 8192 cua sb exec cursor-worker 'curl https://cursor.com/install -fsS | bash' cua sb exec cursor-worker 'git clone https://github.com//.git ~/repo' (umask 077; printf '%s' "$CURSOR_API_KEY" > cursor-api-key) cua sb cp cursor-api-key cursor-worker:/home/cua/.cursor-api-key rm cursor-api-key ``` The worker exits after an idle hour (`--idle-release-timeout`), so `cursor-worker.sh` restarts it: ```sh title="cursor-worker.sh" skip="placeholder" #!/bin/sh chmod 600 ~/.cursor-api-key; cd ~/repo || exit 1 while true; do CURSOR_API_KEY="$(cat ~/.cursor-api-key)" ~/.local/bin/agent worker --pool start sleep 5 done ``` Start it in the background: ```bash test="cli-shape" id="cursor-worker-start" cua sb cp cursor-worker.sh cursor-worker:/home/cua/cursor-worker.sh cua sb exec cursor-worker 'nohup sh ~/cursor-worker.sh > ~/cursor-worker.log 2>&1 &' ``` The worker connects out to Cursor; the Space accepts no incoming connections. After a Space restart, run the `nohup` line again. One worker serves one agent at a time. `cua spaces delete cursor-worker` removes it. ## OpenClaw OpenClaw's bundled `cua-computer` provider runs Cua Driver in-process, so its `computer` tool drives the sandbox's X11 desktop. Create the desktop, then in `cua sb shell openclaw` install Node.js and `openclaw`, run `openclaw plugins enable cua-computer`, and start the Gateway and node with `DISPLAY=:1`: ```bash test="cli-shape" id="cua-sb-create-openclaw" cua sb create linux --name openclaw --cpu 4 --memory 8GB cua sb rm openclaw -f ``` OpenClaw needs X11 (not Wayland) and a vision-capable model. ## Omarchy desktop `ghcr.io/trycua/omarchy:edge` is an amd64 Omarchy (Hyprland) VM image that ships Cua Driver and cua-spacesd. Run it as a local VM and drive it with the cua CLI: ```bash skip="local-vm" cua sb create ghcr.io/trycua/omarchy:edge --kind vm --name omarchy --cpu 4 --memory 4GB cua sb view omarchy cua do switch omarchy cua do screenshot --save omarchy.png cua sb rm omarchy -f ``` It boots under QEMU: KVM on Linux, TCG (slow) on Apple silicon. A black screenshot means Hyprland did not start. The full walkthrough, including Omarchy's own Cua Driver, is in [Omarchy with Cua Driver](). For Omarchy on Apple silicon, see [Lume](). ## Minecraft Build the Prism launcher into an image once, then run it in the cloud or locally. Describe it as `minecraft.json`: ```json title="minecraft.json" test="config" id="json-config" schema="cua-image" { "apiVersion": "images.cua.ai/v1alpha1", "kind": "Image", "metadata": { "name": "minecraft-workspace", "namespace": "minecraft-workspace" }, "spec": { "recipe": { "osType": "linux", "distro": "ubuntu", "version": "24.04", "kind": "vm", "layers": [ { "type": "apt_install", "packages": ["flatpak"] }, { "type": "run", "command": "flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo && flatpak install -y --noninteractive flathub org.prismlauncher.PrismLauncher" } ] } } } ``` Build on an x86_64 host with KVM (the cloud runs amd64), push, and run it: ```bash test="cli-shape" id="cua-image-build" cua image build minecraft.json \ --base vm:ghcr.io/trycua/linux:24.04-disk \ --push ghcr.io//minecraft-workspace:1 cua sb create ghcr.io//minecraft-workspace@sha256: --name mc --cpu 4 --memory 8GB cua sb view mc cua sb rm mc -f ``` **Caution** Never sign in before building or publishing: a published disk carries every token it ever held. Sign in to Prism on the running sandbox (`cua sb view mc`). Then drive the game with any tool-calling model: give it `click`, `type_text` and `press` tools backed by `sb.mouse.click`, `sb.keyboard.type` and `sb.keyboard.keypress` on `Sandbox.connect("mc")`, and return a fresh `sb.screenshot()` after each action. For typed desktop tools, use [Cua Driver](). --- # Omarchy with Cua Driver Run the Omarchy edge VM image, where Omarchy's own Cua Driver injects input at the OS level, and drive it with the cua CLI. `ghcr.io/trycua/omarchy:edge` is Omarchy's edge channel (Arch, Hyprland) with the Cua Driver packages Omarchy ships, `cua-driver-bin` and `cua-hyprland-plugin`, turned on. The plugin adds two agent seats inside Hyprland, so input is injected by the compositor itself. The image also runs cua-spacesd, so the cua CLI, SDK and Spaces work with it. - amd64 only (Omarchy is x86_64). VM only: Hyprland needs a GPU or KMS device, which containers do not have. - It boots under QEMU: KVM on Linux, TCG (slow) on Apple silicon. ## Create, view and run commands Set `OMARCHY=ghcr.io/trycua/omarchy:edge` (pin an `edge--` tag or a digest in CI). ```bash skip="local-vm" cua sb create "$OMARCHY" --kind vm --name omarchy --cpu 4 --memory 4GB cua sb view omarchy # the desktop in the browser cua sb exec omarchy 'pacman -Q cua-driver-bin cua-hyprland-plugin hyprland' cua sb exec omarchy 'hyprctl -j cua:status' ``` ## Drive it ```bash skip="local-vm" cua do switch omarchy cua do launch foot # Hyprland opens a terminal cua do click 640 400 cua do type 'echo hello from Cua' cua do key enter cua do window ls # then: cua do window maximize cua do screenshot --save shot.png ``` Every action goes through Cua Driver. The agent cursor overlay (a layer-shell surface) draws where the driver clicks, so screenshots show it: ![The Cua agent cursor on Omarchy after cua do click]() ## Omarchy's own Cua Driver `/usr/bin/cua-driver` is Omarchy's package, running as the user unit `cua-driver.service`. Its MCP server is available to agents in the guest: ```bash skip="local-vm" cua sb exec omarchy 'cua-driver list-tools | head' cua sb mcp omarchy env tools # the same driver tools over cua-spacesd's /mcp ``` **Note** The Hyprland plugin is experimental and qualified for a narrow app set; see [Platform support](). Build the image yourself with `cua images build libs/images/omarchy --outputs rootfs,disk`. --- # Migrate from deprecated packages cuabot, cua-computer, cua-computer-server and cua-agent are no longer maintained. What each one did and what to use instead. **Warning: Deprecated** Cua no longer maintains `cuabot`, `cua-computer`, `cua-computer-server` or `cua-agent`. They will not receive updates or fixes. Existing releases stay on npm and PyPI, so current installs keep installing, but you should move off them. **Caution: Do not run cuabot** `cuabot` has known security issues and is no longer maintained. Do not run it. If it is installed, [stop and remove it](#cuabot) now. | Package | Registry | Last release | Use instead | | --- | --- | --- | --- | | `cuabot` | npm | 1.0.14 | [Cua Driver]() over MCP, or a sandbox from the [`cua` CLI]() or [Cua Spaces]() | | `cua-computer` | PyPI | 0.5.19 | The [`cua` SDK]() (`cua-sandbox`) for sandboxes, [Cua Driver]() for the machine you are on | | `cua-computer-server` | PyPI | 0.3.46 | Images that ship [cua-spacesd](), or [Cua Driver]() on a host | | `cua-agent` | PyPI | 0.9.0 | A coding agent with the [Cua Driver MCP](), or [`sandbox.agents()`]() | ## cuabot `cuabot` ran coding agents in a Docker sandbox with a local server and a desktop you could watch. Stop it, uninstall it and remove what it created: ```bash skip="destructive" cuabot --stop # repeat with --name for each named session npm uninstall -g cuabot # or delete any local node_modules/cuabot docker rm -f cuabot-xpra docker images -q trycua/cuabot | xargs docker rmi rm -rf ~/.cuabot ``` Instead: - To let Claude Code, Codex, Cursor or another agent operate apps on your machine, [connect it to Cua Driver](). - To give an agent its own computer that you can watch, [create a Space]() or a sandbox with the [`cua` CLI](), then [run the agent in it](). ## cua-computer `cua-computer` was the Python `Computer` class that started a VM or container and drove it through `cua-computer-server`. Use the `cua` SDK instead: it creates local and cloud sandboxes, and `Sandbox` exposes the screen, input, shell and files. Start with the [SDK quickstart]() and the [sandbox reference](). To operate native apps on the machine you are on, use [Cua Driver](). ## cua-computer-server `cua-computer-server` was the HTTP and WebSocket server inside a guest that `cua-computer` talked to. The in-guest agent is now [cua-spacesd](), which Cua Spaces and the current sandbox images ship. Nothing in the `cua` SDK talks to `cua-computer-server` any more. Images built before that change still carry it; see the [image catalog]() for which ones. To drive a desktop directly, install [Cua Driver]() on it. ## cua-agent `cua-agent` was the Python `ComputerAgent` loop with LiteLLM. Bring your own agent instead: - Connect Claude Code, Codex, Cursor, OpenClaw or another agent to the [Cua Driver MCP server](). - Run a coding agent inside a sandbox with [`sandbox.agents()` or `cua agent run`](). - Score agents on tasks with [Cua Bench](). --- # How sandboxes work The SDK, the daemon, cua-spacesd and the backends, where a sandbox runs and on what, how it becomes ready, and how its lifetime works. ```text output Python (cua-sandbox, cua) TypeScript (@trycua/cua) Swift (Cua) Rust (cua-sdk) \ | | / +------------- cua SDK (Rust core, UniFFI) -----------------+ embedded in your process, or a client of `cua daemon` | local (gVisor, runc, QEMU, Lume) | cloud | direct | relay v sandbox: any OCI image; optional cua-spacesd on port 3211 ``` ## The pieces | Piece | What it is | Runs on | | --- | --- | --- | | cua SDK and `cua` CLI | The client: a Rust core with generated bindings | Your process or machine | | `cua daemon` | The SDK as a local service, so several processes (the CLI, the Spaces app, MCP clients, your script) share sandboxes, connections and credentials. `cua.connect()` instead of `cua.embedded()` | Your machine | | cua-spacesd | The optional daemon inside a sandbox: one port (`3211`), gRPC and gRPC-Web. Input and accessibility go to Cua Driver in the guest | The sandbox | | Runtimes | Local: gVisor or runc (on Docker or Podman), QEMU, Lume. Cloud: gVisor containers and KubeVirt VMs | This machine or the cloud | ## Where, what kind, which engine Every create takes the same three choices, all optional: | Axis | SDK | CLI | Values | Default | | --- | --- | --- | --- | --- | | Where | `on=` (cua-sandbox also `local=True/False`) | `--on` | `local`, `cloud`, `direct:` (an existing machine running cua-spacesd), a registered provider | `default.on`, else `local` | | What kind | `kind=` | `--kind` | `auto`, `container`, `vm` | `auto` | | Which engine | `runtime=` | `--runtime` | `auto` or an engine below | `auto` | | Location | container | vm | | --- | --- | --- | | `local` | `gvisor` (auto when installed), `runc` | `qemu` (auto), `lume` (auto for macOS guests; Apple silicon only) | | `cloud` | `gvisor` | `kubevirt` | | `direct:` | the existing machine: `auto` only | | `auto` kind comes from the image: macOS and Windows images are VMs, an image with a container rootfs is a container, and a disk-only image is a VM. So `ghcr.io/trycua/linux:24.04` is a gVisor container locally and in the cloud, and `--kind vm` picks its `-disk` variant (QEMU locally, KubeVirt in the cloud). A runtime implies its kind. A combination that does not exist fails with `InvalidPlacement`, listing the valid values. Defaults resolve in this order, first match wins: 1. explicit: `--on` / `--kind` / `--runtime`, `on=` / `local=` / `kind=` / `runtime=` 2. env: `CUA_DEFAULT_ON`, `CUA_DEFAULT_KIND`, `CUA_DEFAULT_RUNTIME` 3. config: `$CUA_HOME/config.toml` (`cua config set default.on cloud`) 4. built-in: `local`, `auto`, `auto` A default kind or runtime that does not fit an explicit request is skipped. See [Choose where sandboxes run by default](). ## Sandboxes do not require a guest agent Creating, waiting for and deleting a sandbox never assumes anything runs in the guest. A sandbox is ready when its backend reports it running and every probe you declare passes (`wait_for=tcp("mcp")`, `http("web", "/health")`). If the command exits first, readiness fails at once. | Guest | What works | | --- | --- | | Your own services | `sb.service(name)`, `sb.public_url(name)`, `sb.tunnel.forward(port)`, `sb.mcp(name)` | | cua-spacesd | Also shell, files, screenshots, input, windows, accessibility, streaming, and `sb.spacesd()` | | Nothing | Lifecycle, plus the runtime's agentless paths (QMP, VNC, SSH or ADB) where available | ## Lifetime A connection and a sandbox have separate lifetimes. Disconnecting never stops a sandbox; deleting destroys it and its state. | Pattern | Lifetime | | --- | --- | | `Sandbox.ephemeral(...)` | Deleted when the block exits | | `Sandbox.create(...)`, `cua sb create` | Until deleted; a cloud sandbox also lapses when nothing renews its TTL | | `suspend` / `resume` | Local only | | Direct sandbox | Deleting only forgets the record | The SDK manages cloud capacity for your account; to manage it yourself, see [Cua Fleets](). `Sandbox.snapshot()` is not available in `cua-sandbox` 0.9.0. Streaming, teleport and the other [Spaces]() primitives need cua-spacesd. --- # Telemetry and privacy What the cua SDK, the cua CLI, cua daemon, cua-spacesd and the Cua Spaces app collect, what they never collect, and how to turn it off. Cua sends anonymous usage data so we can see which features are used and where things break. It never contains your content. You can see every event before or after it is sent, and turn it off with one switch. ## Turn it off Any of these turns usage telemetry off for every Cua program on the machine (SDK in any language, `cua` CLI, `cua daemon`, the Spaces app, and Cua Driver): | Switch | Scope | |---|---| | `DO_NOT_TRACK=1` | This environment. Wins over everything. | | `CUA_TELEMETRY=0` | This environment. `CUA_TELEMETRY=1` turns it back on. | | `cua telemetry off` or `cua config set telemetry off` | This machine (`[telemetry] enabled = "off"` in `$CUA_HOME/config.toml`). | | Spaces app: "Share anonymous usage data" on the first run's Welcome page, or Settings, Privacy | Same setting as above. | | SDK: `cua.telemetry.disable()` (Python), `telemetrySetEnabled(false)` (TypeScript, Swift, Kotlin) | Same setting as above. | CI environments (`CI`, `GITHUB_ACTIONS`, `BUILDKITE`, ...) are off by default. `CUA_TELEMETRY=1` turns them on, and their events are marked `is_ci`. The older `CUA_TELEMETRY_ENABLED=false` and `CUA_DRIVER_RS_TELEMETRY_ENABLED=false` still work. ## See what is sent ```bash test="cli-shape" id="cua-telemetry-see-what-is-sent" cua telemetry status # on or off, why, and the properties every event carries cua telemetry show-last # the last events, exactly as sent cua telemetry schema # every event and property, with its allowed values cua telemetry reset-id # a new anonymous install id ``` Nothing is sent from a machine until the first-run notice has been shown there once (in the terminal, or in the Spaces app's onboarding or Settings). The process that shows it sends nothing. In the Spaces app the notice is on the first run's Welcome page, next to a "Share anonymous usage data" switch: nothing is queued or sent until you leave that page, and if you turn the switch off there, nothing is sent at all (the setting is saved for every Cua program on the machine). ## How it works - **Anonymous id.** 128 random bits in `$CUA_HOME/telemetry/install_id`, created on the first event actually sent, never while telemetry is off. It is not derived from your machine or account. `reset-id` deletes it. - **No person profiles, no location.** Every event sets `$process_person_profile: false` and `$geoip_disable: true`. Like any HTTPS request, the upload reaches PostHog (EU) from your IP address; it is never an event property and is not used for location. - **Closed vocabularies.** Every property is one of a fixed list of values, a bucket, a boolean or a version. An image reference that is not in the public image catalog is sent as `custom`; an app outside the public teleport catalog as `other`; an error as its category (`not_found`, `timeout`), never its message. An event with any other property or value is dropped before it is queued. - **Never in the way.** Events go out in batches from a background thread with a 3 second budget. Offline, up to 500 events wait on disk for 7 days at most. Exiting waits at most 0.4 seconds. ## What is collected A fixed set of events with closed-vocabulary properties. The full list, each property and why it is collected: [Telemetry events](). ## What we use it for These are the questions the events answer, and nothing else: - How many people install and launch Cua Spaces, and how many finish the first run? Where do they leave it (which page), and what do they pick on each page? - How many make a first Space, of which kind and where? How long until it is ready, and how often does a create fail (in which phase, or by stalling) or get abandoned? - Do people teleport, and does it work? Do they run an agent? - Do they use the Cua Volume, persistent agents, host Spaces, the Keyvault, sharing and multiplayer? Do they update? - Are they still using Cua a day, a week and a month later? The activation funnel joins events on the anonymous install id, in order: app launched (`cua_onboarding_step` `app_launched`), first run completed (`onboarding_completed`), signed in (`signed_in`), first Space created (`first_space_created`), first Space ready (`first_space_ready`), first teleport (`first_teleport`) and first agent run (`first_agent_run`). The `first_*` steps are sent once per install. An agent run is counted by the install that started it: once when it starts, and once when any Cua program of that install first sees its first turn end, fail or stop. Until then a marker with the same fixed words waits in `~/.cua/telemetry/agent_runs/` (dropped after 7 days). Cua programs inside an agent run (`CUA_AGENT_RUN_ID` set) record no agent runs, so a Space never counts as an install. Retention counts installs with a `cua_app_active` event on day 1, 7 and 30 after their first one. ## Never collected File paths, file names, usernames, hostnames, IP addresses, emails, window titles, URLs, clipboard, keystrokes or typed text, screenshots, screen or accessibility content, prompts, model output, sandbox or Space names, error messages, and anything stored in the Keyvault (only consent decisions are counted). Tests enforce this: every event builder is fed paths, emails, hostnames and window titles, and none may appear in a payload. --- # Telemetry events Every usage telemetry event the Cua programs send, each property it carries, and why. This is the full list behind [Telemetry and privacy](). `cua telemetry schema` prints the same list with every allowed value. ## What is collected Every event carries: `telemetry_schema_version`, `event_version`, `product` (`cli`, `daemon`, `sdk_python`, `sdk_typescript`, `sdk_swift`, `sdk_kotlin`, `sdk_rust`, `spaces_app`, `spacesd`, `cua_bench`), `product_version`, `os_family`, `os_major`, `arch`, `is_ci`, `is_synthetic`, `process_session_id` (random per process, never stored), `sample_rate`, and the PostHog flags above. | Event | Properties | Why | |---|---|---| | `cua_first_run` | `install_channel` | Installs by channel. | | `cua_cli_command` | `command` (for example `sandbox.create`, never arguments), `outcome`, `error_kind`, `duration_bucket`, `json_output`, `topology` | Command adoption and reliability. | | `cua_sandbox_created` | `location`, `kind`, `runtime`, `image` (catalog id or `custom`), `guest_os`, `outcome`, `error_kind`, `duration_bucket`, `with_overlays`, `with_build` | Local vs cloud use, reliability by runtime, time to sandbox. | | `cua_sandbox_deleted` | `location`, `outcome`, `error_kind` | Cleanup reliability. | | `cua_api_used` | `api` (a fixed SDK entry point), `outcome`, `error_kind`, `duration_bucket` | SDK feature adoption (25% sampled). | | `cua_teleport_attempted` | `app` (catalog id or `other`), `capability`, `move`, `caller_kind`, `caller_kind_source`, `path` | Teleport demand per app, and how it is invoked. | | `cua_teleport_completed` | the above, plus `outcome` (including `requires_cua_app`), `duration_bucket`, `item_count` | Teleport success rate, and how often it needs the Cua app. | | `cua_keyvault_consent` | `decision` (`requested`, `approved`, `denied`, `expired`) | Consent counts only; nothing about the request. | | `cua_spaces_feature_used` | `feature` (a fixed Spaces app feature) | Spaces app feature adoption. | | `cua_stream_stats` | `transport`, `codec`, `decode` (`hw` or `sw`), `fps_bucket`, `latency_bucket`, `resolution`, `duration_bucket` | Streaming performance (50% sampled). | | `cua_onboarding_step` | `step`, `outcome` | Install and onboarding funnel. | | `cua_agent_run_started` | `harness` (cua-agents id or `other`), `location`, `entry` (`spaces_tool`, `cli`, `sdk`, `persistent`, `routine`), `outcome`, `error_kind` | Agent runs started, and failed starts. | | `cua_agent_run_completed` | `harness`, `location`, `entry`, `outcome`, `error_kind`, `duration_bucket` | First agent run, and how reliably runs finish. Once per run. | | `cua_bench_run_completed` | `taskset` (cua-bench dataset or `custom`), `score_bucket`, `task_count`, `outcome` | Benchmark use and coarse scores. | | `cua_daemon_started` | `mode`, `outcome` | Daemon startup reliability. | | `cua_daemon_health` | `uptime_bucket`, `sandboxes` | Hourly aggregate health. | | `cua_spacesd_health` | `uptime_bucket`, `imports_failed` | In-sandbox health, hourly. | | `cua_spacesd_session_deliveries` | `delivery_grant` (`present` or `absent`), `count` | Session deliveries with and without a Keyvault grant. | | `cua_app_active` | `experiments_on` (the Cua Spaces experiments turned on in Settings, Experiments: `cua_volume`, `your_cloud`, `sharing` joined with `+`, or `none`) | Daily and weekly active installs, and retention: at most one per install per UTC day, on a day Cua is used. Also how many installs use each experiment. | | `cua_onboarding_page` | `page` (`welcome`, `signin`, `agents`, `presentation`, `volume`, `this_machine`, `done`), `action` (`shown`, `completed`, `skipped`, `back`), `choice` (a fixed word such as `signed_in`, `menu_bar_only`, `s3`, `host`, or `none`) | Which first-run page people leave on, and what they pick. | | `cua_space_wizard` | `action` (`opened`, `cancelled`, `submitted`) | How often a New Space is started and abandoned. | | `cua_space_create_started` | `location`, `guest_os`, `kind`, `gpu` (GPU acceleration turned on) | Which Spaces are started, and how often with GPU acceleration. | | `cua_space_create` | `location`, `guest_os`, `kind`, `outcome` (`ok`, `error`, or `cancelled` when Cancel was pressed while it was being created), `failed_phase` (the phase it failed or was cancelled in, or `none`), `stalled`, `time_bucket` (to ready, the failure, or the cancel finishing), `gpu` | Space create success, failure and cancel rates, time to ready, and which phase fails. | | `cua_volume_setup` | `surface` (`onboarding` or `settings`), `storage` (`this_mac`, `s3`, `later`), `add_to_finder`, `mount_method` (`fskit`, `nfs`, `fuse`, `none`, `unknown`), `outcome` | Cua Volume adoption. | | `cua_persistent_agent` | `action` (`create`, `pause`, `resume`, `routine_add`, ...), `harness`, `outcome`, `error_kind` | Persistent agents and routines adoption. | | `cua_host_setup` | `mode` (`relay` or `direct`), `profile` (`desktop`, `spare`, `both`), `outcome`, `error_kind`, `signed_in`, `token_state` (`provided`, `expired`, `missing`, `not_needed`; never the token), `failed_stage` (`none`, `preflight`, `token`, `register`, `download`, `service`), `http_status` (the status a server answered a failure with, `0` when none) | Machines set up for access, and where a failed setup stopped. | | `cua_host_space_provided` | `kind`, `guest_os`, `outcome`, `error_kind` | Spaces a host machine created for your other devices. | | `cua_keyvault_action` | `action` (`setup`, `unlock`, `lock`, `import`, `site_login`), `method` (`os_key_store`, which is Touch ID on a Mac, `passphrase`, `recovery_key`, `none`), `outcome` | Keyvault adoption and how it is unlocked. Nothing about what is stored. | | `cua_share` | `action` (`share`, `unshare`, `change_role`), `role` (`viewer`, `editor`, `none`), `outcome` | Sharing, and how often it is view-only. Never who. | | `cua_presence_session` | `outcome`, `participants` (bucketed), `duration_bucket` | Multiplayer sessions, their size and length. | | `cua_app_update` | `action` (`checked`, `found`, `not_found`, `installed`, `failed`), `channel` (`stable` or `beta`), `trigger` (`user` or `background`) | Whether people update. | | `cua_device_enroll` | `method` (`sign_in`, `approval`, `rekey`), `outcome` | How devices join your account's relay. | | `cua_experiment` | `action` (`experiment_on` or `experiment_off`), `experiment` (`cua_volume`, `your_cloud`, `sharing`) | Which Cua Spaces experiments people try, and which they turn off again. | ## Teleport caller kinds `caller_kind` says who asked for a teleport: `cua_app_signed` (the Cua app or CLI, verified by its code signature), `embedded_sdk` (another app that embeds the cua SDK), `unsigned` (ad hoc or unsigned code) or `unknown` (the platform cannot verify signatures). With `caller_kind_source: broker_verified` it comes from the Keyvault broker's OS-verified caller identity; with `self_reported` the calling process reported it, and an SDK process only ever reports `embedded_sdk` or `unknown`. Paths, bundle ids and signing ids of callers are never sent. A modified build can remove its own telemetry. The receive-side count (`cua_spacesd_session_deliveries`) is recorded by cua-spacesd inside the sandbox, and only when the host enabled telemetry for that sandbox (`CUA_SPACESD_TELEMETRY=1`, which the SDK sets on local sandboxes while telemetry is on). --- # Reference The Cua SDK reference: every object with its signature in Python, TypeScript, Swift, Kotlin and Rust. The Cua SDK in Python, TypeScript, Swift, Kotlin and Rust, all generated from the Rust `cua-sdk` crate. Each page covers one object, with signatures in every language. Pages marked *Cua Spaces app export* document the APIs the Cua Spaces apps are built on, not the SDK: source-available under FSL-1.1-MIT, Swift only. | Language | Package | Import | | --- | --- | --- | | Python | `pip install cua` (0.4.1; `cua[sandbox]` adds the [high-level API]()) | `import cua` | | TypeScript (Node) | `npm install @trycua/cua` (0.4.1) | `import { embedded } from '@trycua/cua'` | | Browser | `@trycua/cua/browser` (a WebAssembly subset) | `import { Cua } from '@trycua/cua/browser'` | | Swift | SwiftPM package `Cua` (macOS 12+, iOS 15+) | `import Cua` | | Kotlin | `ai.cua.sdk` (JNA; compile-checked, not published yet) | `import ai.cua.sdk.*` | | Rust | `cua-sdk` from git (see [Rust crates]()) | `use cua_sdk::*;` | ## Sandboxes | Page | Covers | | --- | --- | | [Cua client]() | The Cua handle: embedded or daemon client, its configuration, and the SDK version. | | [Sandbox]() | Create, find, connect to and control sandboxes, locally, in the cloud or by address. | | [Lifecycle]() | Wait for, keep alive, suspend, resume, restart, detach and delete a sandbox. | | [Without cua-spacesd]() | Run commands, take screenshots and open the display of a local Lume sandbox that has no cua-spacesd. | | [Create options]() | SandboxCreateOptions and what it holds: cloud options, readiness probes and sidecar containers. | | [Image]() | Canonical images, image references, build layers and registry credentials. | | [Image catalog details]() | The canonical images, their variants per backend and architecture. | | [What's installed]() | The apps, tools and simulator runtimes in each image tier, as the strict doctor measured them. | | [Services]() | Named services, port forwards, public URLs and MCP clients of a sandbox. | | [Local runtimes]() | The container and VM runtimes this machine can host, their setup, and local images. | | [Runtime support]() | Which images run on which local runtime and in the cloud. | ## Cloud capacity | Page | Covers | | --- | --- | | [Fleet]() | Cloud pools and templates: apply, check, export, scale and delete (advanced). | | [Claims]() | Claim sandboxes from a cloud pool, keep them alive, release them, and sign service URLs. | | [Cloud images and runtimes]() | Fleet image resources and the runtime (gVisor or KubeVirt) each image runs on. | ## Inside a sandbox | Page | Covers | | --- | --- | | [Spacesd client]() | SpacesdClient: the cua-spacesd client for processes, files, the desktop and media in a sandbox. | | [Processes]() | Run, spawn and attach to processes in a sandbox through cua-spacesd. | | [Files]() | Upload, download, list and remove files in a sandbox through cua-spacesd. | | [Computer]() | Screenshots, pointer, keyboard, clipboard and displays of a sandbox desktop through cua-spacesd. | | [Media]() | Video and audio streaming sessions and the sink callbacks you implement. | | [Coding agents]() | Run coding agents (Claude Code, Codex, Gemini CLI, OpenCode, ...) inside any sandbox. | ## Spaces | Page | Covers | | --- | --- | | [Spaces]() | The Spaces registry: create, add, resolve, delete and remove Spaces. | | [Create options]() | What `Spaces.create` takes and reports: options, progress, cancel and GPU acceleration. | | [Space]() | A connected Space: shell, files, tools, windows and the hotspot. | | [Cua Volume]() | The versioned volume every Space and agent shares: files, history, grants, requests and audit. | | [Cua Volume storage and sync]() | Where the drive keeps its bytes, the volume on this machine and in Spaces, sync across devices, and the block cache. | | [Streams and presence]() | Media sessions of a Space and presence (who is connected, and their cursors). | | [Windows, icons and displays]() | A Space's app icons (batched, through the SDK's one icon cache), window previews, displays and memory and storage use. | | [Agents in a Space]() | Start, follow, message and stop coding-agent runs inside a Space. | | [Persistent agents]() | Named agents whose memory outlives their Space: homes in the Cua Volume, routines, notifications, pause and resume, access to your computers. | | [Your cloud]() | Sandboxes and Spaces in your own AWS, Google Cloud or Modal account: connect, check, and sweep what Cua created there. | | [Teleport]() | Move a running app between machines, with an approval callback. | | [Decoded media]() | Media sessions that deliver decoded BGRA frames and PCM audio. *Cua Spaces app export.* | | [Host teleport]() | The Teleport object of the Spaces apps: providers, manifests and sends from this machine. *Cua Spaces app export.* | | [Teleport an app]() | The app catalog, plans with consent, runs, drops and window drags. *Cua Spaces app export.* | | [Spaces app core]() | The view models the Cua Spaces apps share: Space list, New Space wizard, teleport review, notch and Keyvault. *Cua Spaces app export.* | | [Space list]() | The Space list (roster), the sidebar, a Space's detail and its remote windows. *Cua Spaces app export.* | | [Creates and deletes]() | Spaces being created or deleted: the rows they show at once, their progress and how they hand over to the registry. *Cua Spaces app export.* | | [Stream section and facts]() | A Space detail's Stream rows (desktop and windows with their icons and picture in picture) and its facts with live memory and storage use. *Cua Spaces app export.* | | [New Space wizard]() | The New Space wizard: where it runs, image, engine, address and the create call. *Cua Spaces app export.* | | [Image catalog]() | The images the New Space wizard offers: groups, sizes, the image field and its suggestions. *Cua Spaces app export.* | | [Teleport picker]() | The teleport picker and its catalog. *Cua Spaces app export.* | | [Teleport transfer and drag]() | The overlays that show a teleport in progress: the transfer overlay and drag-and-drop onto a Space. *Cua Spaces app export.* | | [Teleport plan and run]() | The teleport plan, its consent and capabilities, and the run's steps. *Cua Spaces app export.* | | [Teleport review]() | The review and consent before a teleport: what to send per site, from the app or from the Keyvault, and the choice remembered for next time. *Cua Spaces app export.* | | [Teleport picker grid]() | The teleport picker's tiles in both apps: tabs, app and window tiles with their icon and preview sources, and the arrow keys. *Cua Spaces app export.* | | [App core: Cua Volume storage, mount and sync]() | Settings, Storage (this machine or an S3-compatible bucket, the Finder volume, the cache), the first run's Cua Volume page and the Drive page's devices and conflicts. *Cua Spaces app export.* | | [App core: agents, drive, notifications]() | The Agents page (persistent agents, memory, routines, computer access), the Drive page and the notifications the apps post. *Cua Spaces app export.* | | [Your cloud]() | The "Connect a cloud" sheet and the New Space wizard's "Your cloud": providers with their found mark, Test that creates nothing, Connect, and what a connected cloud runs at what cost. *Cua Spaces app export.* | | [Window drag trigger]() | When dragging a window opens Teleport to Cua: move versus resize, the notch line and the dwell. *Cua Spaces app export.* | | [Experiments]() | Settings > Experiments: the switches that hide Cua Volume, your own cloud and sharing until they are turned on. *Cua Spaces app export.* | | [Run on]() | The New Space Run on menu: This Mac, your machines that provide Spaces, and connected clouds. *Cua Spaces app export.* | | [Power button]() | The power button on a Space: suspend and resume, or turn off and on. *Cua Spaces app export.* | | [Notch]() | The notch switcher: state, events, layout on the screen and motion. *Cua Spaces app export.* | | [Keyvault]() | The Keyvault broker client and its overview: items, pending requests, grants, rules and audit. *Cua Spaces app export.* | | [Keyvault screens]() | The Keyvault page, sidebar, the vault list (apps, sites and items with their locks), the unlock prompt and the approval prompt. *Cua Spaces app export.* | | [Onboarding and settings]() | First-run onboarding, its card miniatures, the settings file and the hotkey label. *Cua Spaces app export.* | | [About, updates and launch]() | The About window and its update channel, what the apps do once they launch (the bundled cua install), and the daemon and refresh reports they read. *Cua Spaces app export.* | | [Cua Cloud billing]() | The Billing row in Settings and the out-of-credit notice on a refused cloud Space. *Cua Spaces app export.* | | [This machine]() | The This machine roster entry, its page and the host setup form. *Cua Spaces app export.* | | [Window, menu bar and Settings]() | The window chrome, a Space's toolbar and sections, the menu bar menu, Settings and the coding agents' rows. *Cua Spaces app export.* | | [Devices]() | Settings > Devices: this device's enrollment, the account's devices, Recent Access, and the enroll and approval sheets. *Cua Spaces app export.* | | [Share sheet]() | The Share sheet: who a Space is shared with (viewer or editor) and the share request to run. *Cua Spaces app export.* | | [Usage telemetry]() | Which anonymous usage events a Spaces app step means, and sending them. *Cua Spaces app export.* | | [Devices]() | This device as a client of the account on the relay: enroll, approve, rename, revoke and the audit log. | | [Host and relay]() | Unattended host setup and the cua-relay directory client. | ## Account | Page | Covers | | --- | --- | | [Auth]() | Sign-in and the credential store the SDK, the CLI and the daemon share. | | [Agent setup]() | Detect coding agents, install cua skills and configure their MCP servers. | | [Telemetry]() | Anonymous usage telemetry switches. | ## Errors and types | Page | Covers | | --- | --- | | [Errors]() | Every `CuaError` variant: message, cause and fix. | | [Types]() | The type notation in each language, and every item in one list. | ## Language packages | Page | Covers | | --- | --- | | [Python high-level API]() | `cua_sandbox`: `Sandbox`, `Image`, the computer interfaces, `Pool`, runtimes and configuration. | | [TypeScript additions]() | What `@trycua/cua` adds in TypeScript: `embedded()`, `Image`, helpers, and the Spaces modules. | | [Rust crates]() | The public crates (`cua-sdk`, `cua-spacesd-client`, `cua-fleet`, `cua-sandbox-core`, `cua-spaces`), with stability tiers. | ## Protocol | Page | Covers | | --- | --- | | [Protocol]() | The gRPC contracts of cua-spacesd (`cua.env.v1`) and the cua daemon (`cua.daemon.v1`). | 290 items, 382 methods. The [Types]() page lists them. The Cua Spaces app export adds 723 items and 38 methods. --- # Cua client The Cua handle: embedded or daemon client, its configuration, and the SDK version. Every program starts from one `Cua` handle, embedded in the process or connected to a running `cua daemon`, with the same API either way. Its accessors return the other objects. The `config_*` functions read and write the user defaults in `$CUA_HOME/config.toml` (`cua config`). | Topology | Create | Behavior | | --- | --- | --- | | Embedded | `Cua.embedded(config)` (Python `cua.embedded()`, TypeScript `embedded()`) | The runtime lives in your process | | Daemon client | `Cua.connect(address, token)` (Python `cua.connect()`, TypeScript `connect()`) | Calls a running `cua daemon`, which shares sandboxes, spacesd connections, tunnels and credentials across processes | `connect()` without an address reads `~/.cua/daemon.json`, then the socket `~/.cua/cua.sock`. The connection is lazy: the first call fails with `Transport` when no daemon runs (`cua daemon start`). An embedded client reads Fleet credentials from `FLEETS_TOKEN`, or `CUA_CLIENT_ID` and `CUA_CLIENT_SECRET` (with `CUA_FLEET_BASE_URL` and `CUA_TOKEN_URL`), unless `fleet_from_env` is off. ## `Cua` The entry point of the SDK: create one with `Cua::embedded` or `Cua::connect`, then reach sandboxes, Fleet, Spaces and the rest through its accessors. | Method | Description | | --- | --- | | [`auto`](#cuaauto) | The default topology: the `cua daemon` this machine runs when one accepts connections (so the CLI, MCP clients and apps share sandboxes and Spaces), else the runtime in this process with `config`. | | [`connect`](#cuaconnect) | Connects to a running `cua daemon`. | | [`embedded`](#cuaembedded) | Runs the SDK runtime in this process. | | [`fleet`](#cuafleet) | Fleet pools, templates, claims and images. | | [`info`](#cuainfo) | SDK (and daemon) identity. | | [`shutdown_daemon`](#cuashutdown_daemon) | Asks a connected daemon to stop (daemon mode only). | | [`spacesd`](#cuaspacesd) | Connects to cua-spacesd at `url` (`host:port`, `http(s)://…`, a Fleet service URL or a relay URL) without a sandbox. | | Accessor | Returns | Description | | --- | --- | --- | | `agent_setup()` | [`AgentSetup`]() | Agent onboarding: detect AI coding agents, install the cua skills and configure the cua MCP server for the current user. | | `auth()` | [`Auth`]() | Sign-in and the shared session (`CUA_OIDC_ISSUER`, `CUA_CREDENTIAL_STORE`, `CUA_HOME` apply). | | `local()` | [`Local`]() | Local runtimes (doctor and setup) and local images. | | `mode()` | [`CuaMode`](#cuamode-enum) | Topology. | | `sandboxes()` | [`Sandboxes`]() | Sandboxes (Fleet, local, direct). | | `spaces()` | [`Spaces`]() | Spaces: the registry and every Space primitive. | ### `Cua.auto` The default topology: the `cua daemon` this machine runs when one accepts connections (so the CLI, MCP clients and apps share sandboxes and Spaces), else the runtime in this process with `config`. A discovery file (`~/.cua/daemon.json`) or socket left by a daemon that exited is not a running daemon: the files are removed when their pid is provably dead, and this falls back to embedded. A daemon that is starting (`~/.cua/daemon.starting`) is waited for, up to 30 s. Probes with a local connect only (no RPC), except in a process inside an app bundle that ships its own `cua`: it uses only that build's daemon, and fails with `CuaError.DaemonNotRunning` naming the running one (another app's, or its own before a rebuild or update) rather than silently use it; `<bundle>/Contents/MacOS/cua daemon start` replaces it. **Python** ```python @classmethod def auto(cls, config: CuaConfig) -> Cua ``` **TypeScript** ```ts static auto(config: CuaConfig): CuaLike ``` **Swift** ```swift static func auto(config: CuaConfig) throws -> Cua ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun Cua.Companion.auto(config: CuaConfig): Cua ``` **Rust** ```rust pub fn auto(config: CuaConfig) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `config` | [`CuaConfig`](#cuaconfig-record) | required | **Returns** [`Cua`](#cua) · **Raises** [`CuaError`]() ([`DaemonNotRunning`]()) ### `Cua.connect` Connects to a running `cua daemon`. `address` is a socket path (`unix:` prefix optional) or a loopback URL (then `token` is required unless the discovery file has it); `None` uses `~/.cua/daemon.json`, then `~/.cua/cua.sock`. Connects lazily: the first call fails with `DaemonNotRunning` when no daemon runs; use `Cua.info` to check, or `Cua.auto` to fall back to the runtime in this process. **Python** ```python @classmethod def connect(cls, address: Optional[str], token: Optional[str]) -> Cua ``` **TypeScript** ```ts static connect(address: string | undefined, token: string | undefined): CuaLike ``` **Swift** ```swift static func connect(address: String?, token: String?) throws -> Cua ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun Cua.Companion.connect(address: String?, token: String?): Cua ``` **Rust** ```rust pub fn connect(address: Option, token: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `address` | `Option` | required | | `token` | `Option` | required | **Returns** [`Cua`](#cua) · **Raises** [`CuaError`]() ([`DaemonNotRunning`]()) ### `Cua.embedded` Runs the SDK runtime in this process. Performs no I/O beyond reading the credential store when `fleet_from_session` is set. **Python** ```python @classmethod def embedded(cls, config: CuaConfig) -> Cua ``` **TypeScript** ```ts static embedded(config: CuaConfig): CuaLike ``` **Swift** ```swift static func embedded(config: CuaConfig) throws -> Cua ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun Cua.Companion.embedded(config: CuaConfig): Cua ``` **Rust** ```rust pub fn embedded(config: CuaConfig) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `config` | [`CuaConfig`](#cuaconfig-record) | required | **Returns** [`Cua`](#cua) · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="cua-embedded-py" session="cua-embedded-py" import cua c = cua.embedded() # the SDK runs in this process; cua.connect() uses a running `cua daemon` print(c.mode()) ``` **TypeScript** ```ts test="docs" id="cua-embedded-ts" import { connect, embedded } from '@trycua/cua'; const cua = embedded({ fleetFromSession: true }); // SDK runtime in this process const viaDaemon = connect(); // client of a running `cua daemon`, connected on first use console.log(cua.mode(), typeof viaDaemon.sandboxes); ``` **Swift** ```swift test="swift" id="cua-embedded-swift" import Cua let cua = try Cua.embedded() // the SDK runs in this process print(cua.mode()) ``` **Kotlin** ```kotlin test="kotlin" id="cua-embedded-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig val cua = Cua.embedded(CuaConfig()) // the SDK runs in this process println(cua.mode()) ``` ### `Cua.fleet` Fleet pools, templates, claims and images. Always talks to Fleet from this process with this SDK's credentials (in daemon mode, from the environment). **Python** ```python def fleet(self) -> Fleet ``` **TypeScript** ```ts fleet(): FleetLike ``` **Swift** ```swift func fleet() throws -> Fleet ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun fleet(): Fleet ``` **Rust** ```rust pub fn fleet(&self) -> Result, CuaError> ``` **Returns** [`Fleet`]() · **Raises** [`CuaError`]() ### `Cua.info` SDK (and daemon) identity. In daemon mode this is the connectivity check. **Python** ```python async def info(self) -> CuaInfo ``` **TypeScript** ```ts info(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func info() async throws -> CuaInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun info(): CuaInfo ``` **Rust** ```rust pub async fn info(&self) -> Result ``` **Returns** [`CuaInfo`](#cuainfo-record) · **Async** · **Raises** [`CuaError`]() ### `Cua.shutdown_daemon` Asks a connected daemon to stop (daemon mode only). **Python** ```python async def shutdown_daemon(self) -> None ``` **TypeScript** ```ts shutdownDaemon(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func shutdownDaemon() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun shutdownDaemon() ``` **Rust** ```rust pub async fn shutdown_daemon(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `Cua.spacesd` Connects to cua-spacesd at `url` (`host:port`, `http(s)://…`, a Fleet service URL or a relay URL) without a sandbox. **Python** ```python async def spacesd(self, url: str, token: Optional[str]) -> SpacesdClient ``` **TypeScript** ```ts spacesd(url: string, token: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func spacesd(url: String, token: String?) async throws -> SpacesdClient ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun spacesd(url: String, token: String?): SpacesdClient ``` **Rust** ```rust pub async fn spacesd(&self, url: String, token: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `url` | `String` | required | | `token` | `Option` | required | **Returns** [`SpacesdClient`]() · **Async** · **Raises** [`CuaError`]() ## `CuaConfig` record Configuration of an embedded SDK runtime. | Field | Type | Default | Description | | --- | --- | --- | --- | | `state_dir` / `stateDir` | `Option` | `None` | State directory for sandbox state files (default `~/.cua/sandboxes`). | | `fleet` | [`Option`](#fleetsettings-record) | `None` | Fleet settings (merged over the environment). | | `fleet_from_env` / `fleetFromEnv` | `bool` | `true` | Read Fleet settings from the environment. | | `env_probe_timeout_ms` / `envProbeTimeoutMs` | `Option` | `None` | spacesd probe timeout (default 15 s). | | `spaces_home` / `spacesHome` | `Option` | `None` | Spaces registry directory (default `$CUA_HOME` or `~/.cua`). | | `teleport_home` / `teleportHome` | `Option` | `None` | Teleport reads app sessions under this home directory with no host side effects (tests, CI). Default: the real host. | | `fleet_from_session` / `fleetFromSession` | `bool` | `false` | When the settings and environment carry no Fleet credentials, use the signed-in session (`Cua.auth()`, `cua auth login`) from the shared credential store, refreshed as needed. | | `fleet_pool_home` / `fleetPoolHome` | `Option` | `None` | Where managed Fleet pools keep their name cache and machine-wide GC lock (default: `$CUA_HOME` or `~/.cua`, or next to `state_dir`). | ## `FleetSettings` record Fleet credentials and endpoints. Unset fields fall back to the environment (`CUA_FLEET_BASE_URL`, `CUA_TOKEN_URL`, `CUA_CLIENT_ID`, `CUA_CLIENT_SECRET`, `FLEETS_TOKEN`) when `CuaConfig.fleet_from_env`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `base_url` / `baseUrl` | `Option` | `None` | Fleet API base URL. | | `token_url` / `tokenUrl` | `Option` | `None` | OAuth token URL. | | `client_id` / `clientId` | `Option` | `None` | OAuth client id. | | `client_secret` / `clientSecret` | `Option` | `None` | OAuth client secret. | | `token` | `Option` | `None` | Static Fleet token (wins over client credentials). | ## `CuaInfo` record Identity of the SDK (and daemon, when connected). Returned by [`Cua.info`](#cuainfo). | Field | Type | Default | Description | | --- | --- | --- | --- | | `sdk_version` / `sdkVersion` | `String` | | SDK library version. | | `mode` | [`CuaMode`](#cuamode-enum) | | Topology. | | `daemon_version` / `daemonVersion` | `Option` | | Daemon version (daemon mode). | | `daemon_pid` / `daemonPid` | `Option` | | Daemon pid (daemon mode). | | `socket_path` / `socketPath` | `Option` | | Daemon socket (daemon mode). | | `loopback_url` / `loopbackUrl` | `Option` | | Daemon loopback URL (daemon mode). | | `features` | `Vec` | | Compiled-in modules. | ## `CuaMode` enum Where the SDK runtime lives. **Python** ```python CuaMode.EMBEDDED CuaMode.DAEMON ``` **TypeScript** ```ts CuaMode.Embedded CuaMode.Daemon ``` **Swift** ```swift .embedded .daemon ``` **Kotlin** ```kotlin CuaMode.EMBEDDED CuaMode.DAEMON ``` **Rust** ```rust CuaMode::Embedded CuaMode::Daemon ``` | Variant | Description | | --- | --- | | `Embedded` | In this process. | | `Daemon` | In a `cua daemon`. | ## `cua_sdk_version` Version of the SDK library. **Python** ```python def cua_sdk_version() -> str ``` **TypeScript** ```ts function cuaSdkVersion(): string ``` **Swift** ```swift func cuaSdkVersion() -> String ``` **Kotlin** ```kotlin fun cuaSdkVersion(): String ``` **Rust** ```rust pub fn cua_sdk_version() -> String ``` **Returns** `String` ## `config_list` Every setting with its effective value and source. **Python** ```python def config_list() -> List[ConfigEntry] ``` **TypeScript** ```ts function configList(): Array ``` **Swift** ```swift func configList() throws -> [ConfigEntry] ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun configList(): List ``` **Rust** ```rust pub fn config_list() -> Result, CuaError> ``` **Returns** [`Vec`](#configentry-record) · **Raises** [`CuaError`]() ## `config_get` One setting's effective value and source. **Python** ```python def config_get(key: str) -> ConfigEntry ``` **TypeScript** ```ts function configGet(key: string): ConfigEntry ``` **Swift** ```swift func configGet(key: String) throws -> ConfigEntry ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun configGet(key: String): ConfigEntry ``` **Rust** ```rust pub fn config_get(key: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `key` | `String` | required | **Returns** [`ConfigEntry`](#configentry-record) · **Raises** [`CuaError`]() ## `config_set` Writes a setting to the config file (checked and normalised: `default.on` takes `local`, `cloud` or a provider). Returns its effective value, which an environment variable may still override. **Python** ```python def config_set(key: str, value: str) -> ConfigEntry ``` **TypeScript** ```ts function configSet(key: string, value: string): ConfigEntry ``` **Swift** ```swift func configSet(key: String, value: String) throws -> ConfigEntry ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun configSet(key: String, value: String): ConfigEntry ``` **Rust** ```rust pub fn config_set(key: String, value: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `key` | `String` | required | | `value` | `String` | required | **Returns** [`ConfigEntry`](#configentry-record) · **Raises** [`CuaError`]() ## `config_unset` Removes a setting from the config file. Returns its effective value afterwards (the environment variable or the built-in default). **Python** ```python def config_unset(key: str) -> ConfigEntry ``` **TypeScript** ```ts function configUnset(key: string): ConfigEntry ``` **Swift** ```swift func configUnset(key: String) throws -> ConfigEntry ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun configUnset(key: String): ConfigEntry ``` **Rust** ```rust pub fn config_unset(key: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `key` | `String` | required | **Returns** [`ConfigEntry`](#configentry-record) · **Raises** [`CuaError`]() ## `config_path` The config file's path (`$CUA_HOME/config.toml`). **Python** ```python def config_path() -> str ``` **TypeScript** ```ts function configPath(): string ``` **Swift** ```swift func configPath() -> String ``` **Kotlin** ```kotlin fun configPath(): String ``` **Rust** ```rust pub fn config_path() -> String ``` **Returns** `String` ## `ConfigEntry` record One setting's effective value. Returned by [`config_get`](#config_get), [`config_list`](#config_list), [`config_set`](#config_set), [`config_unset`](#config_unset). | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | `default.on`, `default.kind`, `default.runtime`, `cloud.warm`, `cloud.max_pool_size` or `cloud.claim_ttl`. | | `value` | `String` | | The effective value. | | `source` | `String` | | `env`, `config` or `default`. | | `from` | `String` | | Where exactly: `env CUA_DEFAULT_ON`, `config /home/me/.cua/config.toml` or `default`. | | `env` | `String` | | The environment variable that overrides it. | | `default_value` / `defaultValue` | `String` | | The built-in default. | | `description` | `String` | | One line about it. | ## `locations` Every location with the kinds and runtimes it offers (for pickers and help text). **Python** ```python def locations() -> List[LocationInfo] ``` **TypeScript** ```ts function locations(): Array ``` **Swift** ```swift func locations() -> [LocationInfo] ``` **Kotlin** ```kotlin fun locations(): List ``` **Rust** ```rust pub fn locations() -> Vec ``` **Returns** [`Vec`](#locationinfo-record) ## `LocationInfo` record The locations `on` accepts: `local`, `cloud`, then registered providers. Returned by [`locations`](#locations). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | The location's name. | | `description` | `String` | | One line about it. | | `kinds` | `Vec` | | The kinds it runs (`container`, `vm`). | | `runtimes` | `HashMap>` | | Its runtimes, in `auto` preference order, per kind (`container: gvisor, runc`). | ## `check_placement` Checks a location, kind and runtime against each other without creating anything: `InvalidPlacement` (listing the valid values) when the combination does not exist. Empty strings are `auto`; an empty `on` is the user default. **Python** ```python def check_placement(on: str, kind: str, runtime: str) -> None ``` **TypeScript** ```ts function checkPlacement(on: string, kind: string, runtime: string): void ``` **Swift** ```swift func checkPlacement(on: String, kind: String, runtime: String) throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun checkPlacement(on: String, kind: String, runtime: String) ``` **Rust** ```rust pub fn check_placement(on: String, kind: String, runtime: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `on` | `String` | required | | `kind` | `String` | required | | `runtime` | `String` | required | **Raises** [`CuaError`]() ([`InvalidPlacement`]()) ## `error_doc_url` A link to the errors-reference entry of a `CuaError` variant, by name (`InvalidArgument`, case-insensitive). An unknown name links to the page itself. The bindings' `doc_url` / `docUrl` on `CuaError` use it. **Python** ```python def error_doc_url(variant: str) -> str ``` **TypeScript** ```ts function errorDocUrl(variant: string): string ``` **Swift** ```swift func errorDocUrl(variant: String) -> String ``` **Kotlin** ```kotlin fun errorDocUrl(variant: String): String ``` **Rust** ```rust pub fn error_doc_url(variant: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `variant` | `String` | required | **Returns** `String` --- # Sandbox Create, find, connect to and control sandboxes, locally, in the cloud or by address. `cua.sandboxes()` returns the `Sandboxes` collection; each sandbox is a `Sandbox` handle. The same options run locally and in the cloud: `on` is the switch (`local`, `cloud`, `direct:`), `kind` and `runtime` pick the machine. Python programs usually use the high-level API instead: [`cua_sandbox.Sandbox`](). A sandbox's `id()` is its ref, and every call that takes a sandbox name accepts one: | Ref | Meaning | | --- | --- | | `local:` | A sandbox on this machine | | `cloud:` | A cloud sandbox | | `direct:` | A machine reached by address | | `relay:` | An account machine reached through the relay (a Space) | | `` | A bare name, searched across locations; `AmbiguousSandbox` when it matches more than one | Readiness never assumes a guest agent: the provider reports the sandbox running, then the `wait_for` probes run, and creation fails fast if the sandbox exits. ## `Sandboxes` Creates, finds and manages sandboxes. Returned by [`Cua.sandboxes`](). | Method | Description | | --- | --- | | [`cancel_create`](#sandboxescancel_create) | Cancels the create of the named sandbox `name` that is still running (in this process or the daemon): the work in flight stops (an image download, a boot, a claim) and what the create made is deleted; a sandbox of that name that existed before is never touched. | | [`connect`](#sandboxesconnect) | Reattaches to a sandbox by ref (`local:`, `cloud:`, `direct:`, a legacy id) or by a name that is unique across locations (else `AmbiguousSandbox`, listing the qualified refs). | | [`connect_url`](#sandboxesconnect_url) | Connects directly to a machine by URL (`http(s)://host:port`, bare `host:port`, a Fleet service URL or a relay URL) with an optional spacesd token. | | [`create`](#sandboxescreate) | Creates (Fleet, local) or connects (direct) a sandbox and waits for the provider and every readiness probe. | | [`delete`](#sandboxesdelete) | Deletes a sandbox by ref or unique name. | | [`get`](#sandboxesget) | One sandbox's info, by ref or unique name. | | [`list`](#sandboxeslist) | Sandboxes, each tagged with its `location`. | | [`list_all`](#sandboxeslist_all) | Deprecated: `Sandboxes.list` with no location lists everything. | | [`list_with_warnings`](#sandboxeslist_with_warnings) | `Sandboxes.list` with what the listing left out (for example "cloud sandboxes not listed: <reason>"). | ### `Sandboxes.cancel_create` Cancels the create of the named sandbox `name` that is still running (in this process or the daemon): the work in flight stops (an image download, a boot, a claim) and what the create made is deleted; a sandbox of that name that existed before is never touched. Finished image downloads stay cached. Returns what was removed, or `None` when no create of that name was running. The create itself fails with `Cancelled`. **Python** ```python async def cancel_create(self, name: str) -> Optional[str] ``` **TypeScript** ```ts cancelCreate(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func cancelCreate(name: String) async throws -> String? ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun cancelCreate(name: String): String? ``` **Rust** ```rust pub async fn cancel_create(&self, name: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** `Option` · **Async** · **Raises** [`CuaError`]() ([`Cancelled`]()) ### `Sandboxes.connect` Reattaches to a sandbox by ref (`local:`, `cloud:`, `direct:`, a legacy id) or by a name that is unique across locations (else `AmbiguousSandbox`, listing the qualified refs). **Python** ```python async def connect(self, name: str) -> Sandbox ``` **TypeScript** ```ts connect(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func connect(name: String) async throws -> Sandbox ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun connect(name: String): Sandbox ``` **Rust** ```rust pub async fn connect(&self, name: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`Sandbox`](#sandbox) · **Async** · **Raises** [`CuaError`]() ([`AmbiguousSandbox`]()) ### `Sandboxes.connect_url` Connects directly to a machine by URL (`http(s)://host:port`, bare `host:port`, a Fleet service URL or a relay URL) with an optional spacesd token. No daemon is assumed and nothing is probed. **Python** ```python async def connect_url(self, url: str, token: Optional[str], name: Optional[str]) -> Sandbox ``` **TypeScript** ```ts connectUrl(url: string, token: string | undefined, name: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func connectUrl(url: String, token: String?, name: String?) async throws -> Sandbox ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun connectUrl(url: String, token: String?, name: String?): Sandbox ``` **Rust** ```rust pub async fn connect_url(&self, url: String, token: Option, name: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `url` | `String` | required | | `token` | `Option` | required | | `name` | `Option` | required | **Returns** [`Sandbox`](#sandbox) · **Async** · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="sandboxes-connect-url-py" session="sandboxes-connect-url-py" prelude="spacesd,spacesd-vars" import asyncio import cua async def main(): sb = await cua.embedded().sandboxes().connect_url(URL, TOKEN, "dev") # your spacesd's address and token guest = await sb.spacesd(None) out = await guest.run(cua.SpacesdCommand(program="echo", args=["hi"])) print(out.stdout) asyncio.run(main()) ``` **TypeScript** ```ts test="docs" id="sandboxes-connect-url-ts" prelude="spacesd,spacesd-vars" import { SpacesdCommand, embedded } from '@trycua/cua'; const cua = embedded(); const sb = await cua.sandboxes().connectUrl(URL, TOKEN, 'dev'); // your spacesd's address and token const guest = await sb.spacesd(undefined); const out = await guest.run(SpacesdCommand.create({ program: 'echo', args: ['hi'] })); console.log(new TextDecoder().decode(out.stdout)); ``` **Swift** ```swift test="swift" id="sandboxes-connect-url-swift" import Cua let cua = try Cua.embedded() let sb = try await cua.sandboxes().connectUrl(url: "http://10.0.0.5:3211", token: "t", name: "dev") let guest = try await sb.spacesd(probeTimeoutMs: nil) let out = try await guest.run(command: SpacesdCommand("echo", ["hi"])) ``` **Kotlin** ```kotlin test="kotlin" id="sandboxes-connect-url-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig import ai.cua.sdk.SpacesdCommand val cua = Cua.embedded(CuaConfig()) val sb = cua.sandboxes().connectUrl("http://10.0.0.5:3211", "TOKEN", "dev") // your spacesd's address and token val guest = sb.spacesd(null) val out = guest.run(SpacesdCommand(program = "echo", args = listOf("hi"))) println(out.stdout.decodeToString()) ``` ### `Sandboxes.create` Creates (Fleet, local) or connects (direct) a sandbox and waits for the provider and every readiness probe. **Python** ```python async def create(self, options: SandboxCreateOptions) -> Sandbox ``` **TypeScript** ```ts create(options: SandboxCreateOptions, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func create(options: SandboxCreateOptions) async throws -> Sandbox ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun create(options: SandboxCreateOptions): Sandbox ``` **Rust** ```rust pub async fn create(&self, options: SandboxCreateOptions) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`SandboxCreateOptions`]() | required | **Returns** [`Sandbox`](#sandbox) · **Async** · **Raises** [`CuaError`]() **Example** **Python** ```python test="container" id="sandboxes-create-py" session="sandboxes-create-py" import asyncio import cua async def main(): c = cua.embedded() sb = await c.sandboxes().create(cua.SandboxCreateOptions( on="local", # or "cloud" image=cua.canonical_image("linux", None), name="dev", wait_for=[cua.ReadinessProbe(service="env")], # until its spacesd answers )) guest = await sb.spacesd(None) print((await guest.sh("uname -a", None)).stdout.decode()) await sb.delete() asyncio.run(main()) ``` **TypeScript** ```ts test="container" id="sandboxes-create-ts" import { Image, SandboxCreateOptions, embedded, http } from '@trycua/cua'; const cua = embedded(); const sb = await cua.sandboxes().create( SandboxCreateOptions.create({ on: 'local', // or 'cloud' image: Image.linux(), command: ['python3', '-m', 'http.server', '8000'], services: new Map([['web', 8000]]), waitFor: [http('web', '/')], }) ); const res = await sb.service('web').request('GET', '/', undefined, undefined, undefined); console.log(res.status); await sb.delete_(); ``` **Swift** ```swift test="swift" id="sandboxes-create-swift" import Cua let cua = try Cua.embedded() let sb = try await cua.sandboxes().create(options: SandboxCreateOptions( on: "local", // or "cloud" image: try Image.linux(), name: "dev", waitFor: [ReadinessProbe(service: "env")] // until its spacesd answers )) let guest = try await sb.spacesd(probeTimeoutMs: nil) let out = try await guest.sh(line: "uname -a", timeoutMs: nil) print(String(decoding: out.stdout, as: UTF8.self)) try await sb.delete() ``` **Kotlin** ```kotlin test="kotlin" id="sandboxes-create-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig import ai.cua.sdk.Image import ai.cua.sdk.ReadinessProbe import ai.cua.sdk.SandboxCreateOptions val cua = Cua.embedded(CuaConfig()) val sb = cua.sandboxes().create( SandboxCreateOptions( on = "local", // or "cloud" image = Image.linux(), name = "dev", waitFor = listOf(ReadinessProbe(service = "env")), // until its spacesd answers ) ) val guest = sb.spacesd(null) println(guest.sh("uname -a", null).stdout.decodeToString()) sb.delete() ``` ### `Sandboxes.delete` Deletes a sandbox by ref or unique name. **Python** ```python async def delete(self, name: str) -> None ``` **TypeScript** ```ts delete_(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func delete(name: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun delete(name: String) ``` **Rust** ```rust pub async fn delete(&self, name: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Sandboxes.get` One sandbox's info, by ref or unique name. **Python** ```python async def get(self, name: str) -> SandboxInfo ``` **TypeScript** ```ts get(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func get(name: String) async throws -> SandboxInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun get(name: String): SandboxInfo ``` **Rust** ```rust pub async fn get(&self, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`SandboxInfo`](#sandboxinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Sandboxes.list` Sandboxes, each tagged with its `location`. `None` (the default): all of them, local, direct and the account's live cloud sandboxes, whatever the default location is. `"local"`, `"cloud"` or `"direct"`: only those. Cloud sandboxes never fail the listing: without Fleet credentials they are left out silently, and when Fleet fails or takes longer than 5 s they are left out with a warning (logged here; `Sandboxes.list_with_warnings` returns it). **Python** ```python async def list(self, location: Optional[str]) -> List[SandboxInfo] ``` **TypeScript** ```ts list(location: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func list(location: String?) async throws -> [SandboxInfo] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun list(location: String?): List ``` **Rust** ```rust pub async fn list(&self, location: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `location` | `Option` | required | **Returns** [`Vec`](#sandboxinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Sandboxes.list_all` Deprecated: `Sandboxes.list` with no location lists everything. **Python** ```python async def list_all(self) -> List[SandboxInfo] ``` **TypeScript** ```ts listAll(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func listAll() async throws -> [SandboxInfo] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listAll(): List ``` **Rust** ```rust pub async fn list_all(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#sandboxinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Sandboxes.list_with_warnings` `Sandboxes.list` with what the listing left out (for example "cloud sandboxes not listed: <reason>"). **Python** ```python async def list_with_warnings(self, location: Optional[str]) -> SandboxListing ``` **TypeScript** ```ts listWithWarnings(location: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func listWithWarnings(location: String?) async throws -> SandboxListing ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listWithWarnings(location: String?): SandboxListing ``` **Rust** ```rust pub async fn list_with_warnings(&self, location: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `location` | `Option` | required | **Returns** [`SandboxListing`](#sandboxlisting-record) · **Async** · **Raises** [`CuaError`]() ## `Sandbox` A sandbox handle. Returned by [`Sandboxes.connect`](#sandboxesconnect), [`Sandboxes.connect_url`](#sandboxesconnect_url), [`Sandboxes.create`](#sandboxescreate). | Method | Description | | --- | --- | | [`overlay`](#sandboxoverlay) | Injects binaries (see `Overlay`): each replaces its guest file atomically, is recorded under `/var/lib/cua/overlays` with its sha256, and what runs it is restarted. | | [`spacesd`](#sandboxspacesd) | Attaches to cua-spacesd inside the sandbox (the `env` service or guest port 3211). | | [`viewer_url`](#sandboxviewer_url) | A browser link to this sandbox's desktop in the cua-spacesd HTML5 viewer (`/viewer` on the `env` service): video, audio, input, clipboard, file drop and folder sharing, in any modern browser. | | [`agents`]() | Coding agents inside this sandbox (needs cua-spacesd): run a harness over ACP, stream normalized events, follow up, interrupt, collect results; runs outlive this handle. | | [`delete`]() | Deletes (Fleet: releases the claim and an ephemeral pool). | | [`detach`]() | Managed Fleet claims: stop renewing the claim and forget the handle; the claim runs until its current shutdown time. | | [`forward`]() | Forwards a loopback port to guest `port`: a TCP forward locally (and over cua-spacesd's tunnel when the image has it); in the cloud without cua-spacesd, a loopback HTTP/WebSocket proxy through the Fleet gateway. | | [`guest_display`]() | The guest display without cua-spacesd: the VM's VNC endpoint and a host command that opens it (`lume attach`). | | [`guest_screenshot`]() | Captures the guest framebuffer as PNG without cua-spacesd. | | [`guest_sh`]() | Runs `line` with `/bin/sh -c` in the guest without cua-spacesd, to completion (default timeout 120 s). | | [`keep_alive`]() | Extends a Fleet lease by `seconds`. | | [`mcp`]() | An MCP client (the official Rust SDK) for the MCP server behind `service` at `path` (default `/mcp`). | | [`mcp_config`]() | Where the MCP server behind `service` is (URL of its endpoint at `path`, default `/mcp`, plus the headers every request needs), for any MCP client. | | [`open_media_bridge`]() | Opens a media session and returns a loopback WebSocket bridge for a webview (daemon mode). | | [`public_url`]() | A shareable URL for `service` that stops working after `ttl_seconds` (60 s to 24 h, default 1 h). | | [`refresh`]() | Fresh info (status from the provider). | | [`restart`]() | Restarts. | | [`resume`]() | Resumes. | | [`revoke_public_url`]() | Revokes a URL from `public_url`. | | [`service`]() | A named service (for example `"server"` or `"env"`). | | [`suspend`]() | Suspends. | | [`wait_ready`]() | Waits until every probe passes. | | Accessor | Returns | Description | | --- | --- | --- | | `id()` | `String` | Qualified ref (`local:`, `cloud:`, `direct:`), the same kind of value local and in the cloud; it round-trips through `Sandboxes.connect`. | | `image_info()` | [`Option`]() | The image this sandbox runs, as resolved and pinned at create time: the digest and variant that actually ran. `None` for direct (URL) connections, claims on a named pool, images not resolved from a registry, and the daemon topology. | | `info()` | [`SandboxInfo`](#sandboxinfo-record) | Info as of creation / connection. | | `is_ephemeral()` | `bool` | Whether the sandbox is torn down on delete and has no state file. | | `kind()` | `String` | What kind of machine: `container` or `vm` (empty when not known). | | `location()` | `String` | Where it runs: `local`, `cloud`, `direct` or `relay`. | | `name()` | `String` | Name. | | `runtime()` | `String` | The engine that runs it (`gvisor`, `runc`, `qemu`, `lume`, `kubevirt`; empty when not known). | | `runtime_type()` | `String` | `runtime_type`. | ### `Sandbox.overlay` Injects binaries (see `Overlay`): each replaces its guest file atomically, is recorded under `/var/lib/cua/overlays` with its sha256, and what runs it is restarted. Local containers go through the container engine; other sandboxes need cua-spacesd and root (or passwordless sudo) in the guest. `timeout_ms` bounds a cua-spacesd restart (default 180 s). **Python** ```python async def overlay(self, overlays: List[Overlay], timeout_ms: Optional[int] = None) -> List[OverlayResult] ``` **TypeScript** ```ts overlay(overlays: Array, timeoutMs: number | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func overlay(overlays: [Overlay], timeoutMs: UInt32? = nil) async throws -> [OverlayResult] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun overlay(overlays: List, timeoutMs: UInt? = null): List ``` **Rust** ```rust pub async fn overlay(&self, overlays: Vec, timeout_ms: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `overlays` | [`Vec`](#overlay-record) | required | | `timeout_ms` | `Option` | `None` | **Returns** [`Vec`](#overlayresult-record) · **Async** · **Raises** [`CuaError`]() ### `Sandbox.spacesd` Attaches to cua-spacesd inside the sandbox (the `env` service or guest port 3211). Fails with `SpacesdNotAvailable` when none answers; sandbox lifecycle never depends on it. **Python** ```python async def spacesd(self, probe_timeout_ms: Optional[int]) -> SpacesdClient ``` **TypeScript** ```ts spacesd(probeTimeoutMs: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func spacesd(probeTimeoutMs: UInt32?) async throws -> SpacesdClient ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun spacesd(probeTimeoutMs: UInt?): SpacesdClient ``` **Rust** ```rust pub async fn spacesd(&self, probe_timeout_ms: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `probe_timeout_ms` | `Option` | required | **Returns** [`SpacesdClient`]() · **Async** · **Raises** [`CuaError`]() ([`SpacesdNotAvailable`]()) ### `Sandbox.viewer_url` A browser link to this sandbox's desktop in the cua-spacesd HTML5 viewer (`/viewer` on the `env` service): video, audio, input, clipboard, file drop and folder sharing, in any modern browser. The link carries a scoped viewer ticket, never the sandbox token. Needs cua-spacesd in the sandbox. **Python** ```python async def viewer_url(self, options: Optional[ViewerOptions] = None) -> ViewerLink ``` **TypeScript** ```ts viewerUrl(options: ViewerOptions | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func viewerUrl(options: ViewerOptions? = nil) async throws -> ViewerLink ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun viewerUrl(options: ViewerOptions? = null): ViewerLink ``` **Rust** ```rust pub async fn viewer_url(&self, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`Option`](#vieweroptions-record) | `None` | **Returns** [`ViewerLink`](#viewerlink-record) · **Async** · **Raises** [`CuaError`]() ## `SandboxInfo` record A sandbox as listed. Returned by [`Sandbox.info`](#sandbox), [`Sandbox.refresh`](), [`Sandboxes.get`](#sandboxesget), [`Sandboxes.list`](#sandboxeslist), [`Sandboxes.list_all`](#sandboxeslist_all). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Name. | | `kind` | `String` | | What kind of machine: `container` or `vm` (empty when not known: an existing machine, or a cloud sandbox this client did not create). | | `runtime` | `String` | | The engine that runs it: `gvisor`, `runc`, `qemu`, `lume` or `kubevirt` (empty when not known). | | `runtime_type` / `runtimeType` | `String` | | `runtime_type` as persisted (`fleet`, `direct`, `gvisor`, `qemu`, ...); prefer `location`, `kind` and `runtime`. | | `status` | [`SandboxStatus`](#sandboxstatus-enum) | | Status. | | `status_detail` / `statusDetail` | `Option` | | Provider's word for an unknown status. | | `ephemeral` | `bool` | | No state file; torn down on delete. | | `services` | `HashMap` | | Declared services: name → guest port (0 = only the name is known). | | `endpoints` | `HashMap` | | Service base URLs by name (Fleet: gateway URLs that need the Fleet bearer). | | `image` | `Option` | | Image, when known. | | `id` | `String` | | Qualified ref, the same kind of value local and in the cloud: `local:`, `cloud:` or `direct:`. Every call that takes a sandbox name accepts it. | | `phase` | [`SandboxPhase`](#sandboxphase-enum) | | Portable phase: provisioning, starting, ready or stopped. | | `location` | `String` | | Where it runs: `local`, `cloud`, `direct` (a machine by address) or `relay`. | | `expires_at_unix` / `expiresAtUnix` | `Option` | | When it expires unless kept alive (unix seconds), when known. | | `provider_details` / `providerDetails` | `HashMap` | | Provider internals (cloud: `pool`, `namespace`, `claim`; local: `backend`, `container_id`). Not part of the portable API. | | `image_info` / `imageInfo` | [`Option`]() | `None` | The image as resolved and pinned at create time (digest, variant). `None` for direct connections, named pools, images not resolved from a registry, and the daemon topology. | ## `SandboxListing` record A sandbox listing and what it left out. Returned by [`Sandboxes.list_with_warnings`](#sandboxeslist_with_warnings). | Field | Type | Default | Description | | --- | --- | --- | --- | | `sandboxes` | [`Vec`](#sandboxinfo-record) | | Sandboxes, each with its `location`. | | `warnings` | `Vec` | | Sources left out, for example "cloud sandboxes not listed: ...". | ## `SandboxPhase` enum Portable lifecycle phase, the same words local and in the cloud. **Python** ```python SandboxPhase.PROVISIONING SandboxPhase.STARTING SandboxPhase.READY SandboxPhase.STOPPED ``` **TypeScript** ```ts SandboxPhase.Provisioning SandboxPhase.Starting SandboxPhase.Ready SandboxPhase.Stopped ``` **Swift** ```swift .provisioning .starting .ready .stopped ``` **Kotlin** ```kotlin SandboxPhase.PROVISIONING SandboxPhase.STARTING SandboxPhase.READY SandboxPhase.STOPPED ``` **Rust** ```rust SandboxPhase::Provisioning SandboxPhase::Starting SandboxPhase::Ready SandboxPhase::Stopped ``` | Variant | Description | | --- | --- | | `Provisioning` | Being created (cloud: capacity is being provisioned). | | `Starting` | Booting; readiness probes have not passed yet. | | `Ready` | Running and ready. | | `Stopped` | Stopped or suspended. | ## `SandboxStatus` enum Coarse lifecycle status. **Python** ```python SandboxStatus.RUNNING SandboxStatus.SUSPENDED SandboxStatus.STOPPED SandboxStatus.PROVISIONING SandboxStatus.UNKNOWN ``` **TypeScript** ```ts SandboxStatus.Running SandboxStatus.Suspended SandboxStatus.Stopped SandboxStatus.Provisioning SandboxStatus.Unknown ``` **Swift** ```swift .running .suspended .stopped .provisioning .unknown ``` **Kotlin** ```kotlin SandboxStatus.RUNNING SandboxStatus.SUSPENDED SandboxStatus.STOPPED SandboxStatus.PROVISIONING SandboxStatus.UNKNOWN ``` **Rust** ```rust SandboxStatus::Running SandboxStatus::Suspended SandboxStatus::Stopped SandboxStatus::Provisioning SandboxStatus::Unknown ``` | Variant | Description | | --- | --- | | `Running` | Running (or bound). | | `Suspended` | Suspended or scaled to zero. | | `Stopped` | Stopped. | | `Provisioning` | Starting. | | `Unknown` | Unknown; see `SandboxInfo.status_detail`. | ## `parse_sandbox_ref` Parses a sandbox ref: `local:`, `cloud:`, `direct:`, `relay:`, a legacy spelling (`space://fleet//`, `fleet::`, `url:`, a URL), or a bare name. **Python** ```python def parse_sandbox_ref(input: str) -> SandboxRefParts ``` **TypeScript** ```ts function parseSandboxRef(input: string): SandboxRefParts ``` **Swift** ```swift func parseSandboxRef(input: String) throws -> SandboxRefParts ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun parseSandboxRef(input: String): SandboxRefParts ``` **Rust** ```rust pub fn parse_sandbox_ref(input: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | `String` | required | **Returns** [`SandboxRefParts`](#sandboxrefparts-record) · **Raises** [`CuaError`]() **Example** ```python test="docs" id="parse-sandbox-ref-py" session="parse-sandbox-ref-py" import cua print([cua.parse_sandbox_ref(r).id for r in ("space://fleet/ns/box", "url:10.0.0.5:3211", "box")]) # ['cloud:box', 'direct:10.0.0.5:3211', 'box'] ``` ## `qualify_sandbox_ref` Qualifies `name` for a lookup: `local = true` narrows a bare name to `local:`, `false` to `cloud:`, `None` keeps it (a bare name is then searched across locations). A qualified ref elsewhere than `local` asks is an `InvalidArgument`. **Python** ```python def qualify_sandbox_ref(name: str, local: Optional[bool] = None) -> str ``` **TypeScript** ```ts function qualifySandboxRef(name: string, local: boolean | undefined = undefined): string ``` **Swift** ```swift func qualifySandboxRef(name: String, local: Bool? = nil) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun qualifySandboxRef(name: String, local: Boolean? = null): String ``` **Rust** ```rust pub fn qualify_sandbox_ref(name: String, local: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `local` | `Option` | `None` | **Returns** `String` · **Raises** [`CuaError`]() ([`InvalidArgument`]()) **Example** ```python test="docs" id="qualify-sandbox-ref-py" session="qualify-sandbox-ref-py" import cua print([cua.qualify_sandbox_ref("box", True), cua.qualify_sandbox_ref("box", False), cua.qualify_sandbox_ref("box", None)]) # ['local:box', 'cloud:box', 'box'] ``` ## `ambiguous_sandbox_candidates` The qualified candidates an `AmbiguousSandbox` message lists (empty for any other message). **Python** ```python def ambiguous_sandbox_candidates(message: str) -> List[str] ``` **TypeScript** ```ts function ambiguousSandboxCandidates(message: string): Array ``` **Swift** ```swift func ambiguousSandboxCandidates(message: String) -> [String] ``` **Kotlin** ```kotlin fun ambiguousSandboxCandidates(message: String): List ``` **Rust** ```rust pub fn ambiguous_sandbox_candidates(message: String) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `message` | `String` | required | **Returns** `Vec` ## `SandboxRefParts` record A parsed sandbox ref. Returned by [`parse_sandbox_ref`](#parse_sandbox_ref). | Field | Type | Default | Description | | --- | --- | --- | --- | | `location` | `Option` | | `local`, `cloud`, `direct`, `relay` or a contrib provider (`e2b`); `None` for a bare name. | | `name` | `String` | | The name, `host:port` or relay machine id. | | `id` | `String` | | The canonical form (`location:name`, or the bare name). | ## `Overlay` record A binary to inject into a sandbox. | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | `cua-driver`, `cua-spacesd`, or any name (then `target` is required). Letters, digits, `.`, `_`, `-`. | | `path` | `String` | | The local file to inject. | | `target` | `Option` | `None` | Guest path to replace. Default: resolved for `cua-driver` and `cua-spacesd`. | | `source` | `Option` | `None` | Provenance recorded with it (for example the git sha it was built from). Default: the local path. | ## `OverlayResult` record What an overlay did. Returned by [`Sandbox.overlay`](#sandboxoverlay). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Overlay name. | | `target` | `String` | | Guest path that was replaced. | | `sha256` | `String` | | sha256 (hex) of the injected file (pass it to `cua doctor --expect NAME=sha256:`). | | `previous_sha256` / `previousSha256` | `String` | | sha256 of the file it replaced, empty when there was none. | | `size` | `u64` | | Bytes. | | `restarted` | `String` | | How the running program was restarted (`supervisor:cua-spacesd`, `systemd:cua-spacesd.service`, `stopped:`), empty when nothing ran it. | ## `ViewerLink` record A link to the sandbox desktop in the cua-spacesd HTML5 viewer. Returned by [`Sandbox.viewer_url`](#sandboxviewer_url). | Field | Type | Default | Description | | --- | --- | --- | --- | | `url` | `String` | | Open this in any modern browser. The credential is in the fragment (`#ticket=`), which browsers never send to a server. | | `expires_at_unix` / `expiresAtUnix` | `i64` | | When the link stops working for new connections (unix seconds). | ## `ViewerOptions` record Options for `Sandbox.viewer_url`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `ttl_seconds` / `ttlSeconds` | `Option` | `None` | Lifetime of the link in seconds (60 to 86400). Default 3600. | | `view_only` / `viewOnly` | `bool` | `false` | Watch only: no input, clipboard, files or microphone. | | `clipboard` | `bool` | `true` | Two-way clipboard sync. | | `files_root` / `filesRoot` | `Option` | `None` | Guest directory for uploads and folder sharing (`~` is the desktop user's home). `None` means `~`; an empty string turns files off. | | `microphone` | `bool` | `true` | Allow the microphone (also subject to the sandbox's uplink policy). | ## `contrib_providers_built` The contrib providers this build includes (empty unless built with the `contrib` feature). **Python** ```python def contrib_providers_built() -> List[str] ``` **TypeScript** ```ts function contribProvidersBuilt(): Array ``` **Swift** ```swift func contribProvidersBuilt() -> [String] ``` **Kotlin** ```kotlin fun contribProvidersBuilt(): List ``` **Rust** ```rust pub fn contrib_providers_built() -> Vec ``` **Returns** `Vec` ## `sandbox_locations` Every location word `on=` accepts, in display order: `local`, `cloud`, then the contrib providers (`e2b`, `daytona`, `modal`, ...) and your own clouds (`aws`, `gcp`). A word is listed whether or not this build includes that provider (see `contrib_providers_built`). **Python** ```python def sandbox_locations() -> List[str] ``` **TypeScript** ```ts function sandboxLocations(): Array ``` **Swift** ```swift func sandboxLocations() -> [String] ``` **Kotlin** ```kotlin fun sandboxLocations(): List ``` **Rust** ```rust pub fn sandbox_locations() -> Vec ``` **Returns** `Vec` --- # Lifecycle Wait for, keep alive, suspend, resume, restart, detach and delete a sandbox. Lifecycle calls on a `Sandbox` handle. They act on the container or VM, locally or in the cloud. Python programs usually use the high-level API instead: [`Sandbox` lifecycle](). ## Lifecycle methods Methods of [`Sandbox`](). ### `Sandbox.wait_ready` Waits until every probe passes. **Python** ```python async def wait_ready(self, probes: List[ReadinessProbe], timeout_ms: int) -> None ``` **TypeScript** ```ts waitReady(probes: Array, timeoutMs: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func waitReady(probes: [ReadinessProbe], timeoutMs: UInt32) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun waitReady(probes: List, timeoutMs: UInt) ``` **Rust** ```rust pub async fn wait_ready(&self, probes: Vec, timeout_ms: u32) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `probes` | [`Vec`]() | required | | `timeout_ms` | `u32` | required | **Async** · **Raises** [`CuaError`]() ### `Sandbox.refresh` Fresh info (status from the provider). **Python** ```python async def refresh(self) -> SandboxInfo ``` **TypeScript** ```ts refresh(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func refresh() async throws -> SandboxInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun refresh(): SandboxInfo ``` **Rust** ```rust pub async fn refresh(&self) -> Result ``` **Returns** [`SandboxInfo`]() · **Async** · **Raises** [`CuaError`]() ### `Sandbox.keep_alive` Extends a Fleet lease by `seconds`. **Python** ```python async def keep_alive(self, seconds: int) -> None ``` **TypeScript** ```ts keepAlive(seconds: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func keepAlive(seconds: UInt32) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun keepAlive(seconds: UInt) ``` **Rust** ```rust pub async fn keep_alive(&self, seconds: u32) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `seconds` | `u32` | required | **Async** · **Raises** [`CuaError`]() ### `Sandbox.suspend` Suspends. **Python** ```python async def suspend(self) -> None ``` **TypeScript** ```ts suspend(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func suspend() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun suspend() ``` **Rust** ```rust pub async fn suspend(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `Sandbox.resume` Resumes. **Python** ```python async def resume(self) -> None ``` **TypeScript** ```ts resume(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func resume() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun resume() ``` **Rust** ```rust pub async fn resume(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `Sandbox.restart` Restarts. **Python** ```python async def restart(self) -> None ``` **TypeScript** ```ts restart(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func restart() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun restart() ``` **Rust** ```rust pub async fn restart(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `Sandbox.detach` Managed Fleet claims: stop renewing the claim and forget the handle; the claim runs until its current shutdown time. Embedded runtime only (a daemon holds its claims by design). **Python** ```python def detach(self) -> None ``` **TypeScript** ```ts detach(): void ``` **Swift** ```swift func detach() throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun detach() ``` **Rust** ```rust pub fn detach(&self) -> Result<(), CuaError> ``` **Raises** [`CuaError`]() ### `Sandbox.delete` Deletes (Fleet: releases the claim and an ephemeral pool). **Python** ```python async def delete(self) -> None ``` **TypeScript** ```ts delete_(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func delete() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun delete() ``` **Rust** ```rust pub async fn delete(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() --- # Without cua-spacesd Run commands, take screenshots and open the display of a local Lume sandbox that has no cua-spacesd. The macOS 15 image ships no cua-spacesd, so `sandbox.spacesd()` fails for it (macOS 26 runs cua-spacesd). A local Lume sandbox is still reachable: commands over `lume ssh`, screenshots and the display over the VM's VNC endpoint. `cua sb exec`, `cua sb screenshot` and `cua sb view` use the same calls. Other sandboxes fail with `Unsupported`. Python programs usually use the high-level API instead: [`cua_sandbox` shell and screen](). ## Agentless methods Methods of [`Sandbox`](). ### `Sandbox.lacks_spacesd` Whether this sandbox has no cua-spacesd: it declares no `env` service, or its image says it does not carry one. `spacesd()` fails for these; `guest_sh`, `guest_screenshot` and `guest_display` reach a local Lume sandbox without it. **Python** ```python def lacks_spacesd(self) -> bool ``` **TypeScript** ```ts lacksSpacesd(): boolean ``` **Swift** ```swift func lacksSpacesd() -> Bool ``` **Kotlin** ```kotlin fun lacksSpacesd(): Boolean ``` **Rust** ```rust pub fn lacks_spacesd(&self) -> bool ``` **Returns** `bool` ### `Sandbox.guest_sh` Runs `line` with `/bin/sh -c` in the guest without cua-spacesd, to completion (default timeout 120 s). Local macOS Lume sandboxes only: over `lume ssh`, stdout and stderr kept apart, no stdin. Others fail with `Unsupported`. **Python** ```python async def guest_sh(self, line: str, timeout_ms: Optional[int] = None) -> ProcessOutput ``` **TypeScript** ```ts guestSh(line: string, timeoutMs: number | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func guestSh(line: String, timeoutMs: UInt32? = nil) async throws -> ProcessOutput ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun guestSh(line: String, timeoutMs: UInt? = null): ProcessOutput ``` **Rust** ```rust pub async fn guest_sh(&self, line: String, timeout_ms: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `line` | `String` | required | | `timeout_ms` | `Option` | `None` | **Returns** [`ProcessOutput`]() · **Async** · **Raises** [`CuaError`]() ([`Unsupported`]()) ### `Sandbox.guest_screenshot` Captures the guest framebuffer as PNG without cua-spacesd. Local Lume sandboxes only: over the VM's VNC endpoint. **Python** ```python async def guest_screenshot(self) -> Screenshot ``` **TypeScript** ```ts guestScreenshot(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func guestScreenshot() async throws -> Screenshot ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun guestScreenshot(): Screenshot ``` **Rust** ```rust pub async fn guest_screenshot(&self) -> Result ``` **Returns** [`Screenshot`]() · **Async** · **Raises** [`CuaError`]() ### `Sandbox.guest_display` The guest display without cua-spacesd: the VM's VNC endpoint and a host command that opens it (`lume attach`). Local Lume sandboxes only. **Python** ```python async def guest_display(self) -> GuestDisplay ``` **TypeScript** ```ts guestDisplay(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func guestDisplay() async throws -> GuestDisplay ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun guestDisplay(): GuestDisplay ``` **Rust** ```rust pub async fn guest_display(&self) -> Result, CuaError> ``` **Returns** [`GuestDisplay`](#guestdisplay) · **Async** · **Raises** [`CuaError`]() ## `GuestDisplay` The guest display of a sandbox without cua-spacesd (`Sandbox.guest_display`). An object, not a record, so no binding's default string form (repr, str, description, toString, Debug) can show the VNC password: `url()` is masked, and only the explicit `url_with_password()` returns it. Returned by [`Sandbox.guest_display`](#sandboxguest_display). | Accessor | Returns | Description | | --- | --- | --- | | `open_command()` | `Vec` | A host command that opens a viewer on it (`lume attach `); empty when there is none. It handles the password itself. | | `url()` | `String` | The display URL (`vnc://HOST:PORT`) with any password masked as `****`. Safe to print and log. | | `url_with_password()` | `String` | SENSITIVE: the display URL with the VNC password (`vnc://:PASSWORD@HOST:PORT`), for handing to a VNC client. Never print or log it. | | `via()` | `String` | How the display is reached (`vnc`). | --- # Create options SandboxCreateOptions and what it holds: cloud options, readiness probes and sidecar containers. `Sandboxes.create` takes one `SandboxCreateOptions`. Every field means the same thing locally and in the cloud; `on` picks where the sandbox runs, `kind` and `runtime` what runs it. Python programs usually use the high-level API instead: [`Sandbox.create` parameters](). ## `SandboxCreateOptions` record Options for `Sandboxes.create`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `on` | `Option` | `None` | Where it runs: `local`, `cloud`, `direct:` (an existing machine running cua-spacesd) or a registered provider. `None` (the default): the cloud when `cloud` options (or the deprecated flat cloud fields) are set, else the user default (`default.on` in `~/.cua/config.toml`, `CUA_DEFAULT_ON`, else `local`). `local` together with `cloud` is an `InvalidArgument`. | | `kind` | `Option` | `None` | What kind of machine: `auto`, `container` or `vm`. `None`: the user default (`default.kind`, else `auto`: macOS and Windows images are VMs, an image with a container rootfs is a container, a disk-only image is a VM). | | `runtime` | `Option` | `None` | Which engine: `auto` or one the location offers for the kind (local `gvisor`, `runc`, `qemu`, `lume`; cloud `gvisor`, `kubevirt`). `None`: the user default (`default.runtime`, else `auto`, the safest available). A local sandbox with sidecars needs `runc` where gVisor would run. A combination that does not exist is `InvalidPlacement`, listing the valid values. | | `image` | `String` | `""` | Image reference. Locally, `pool:` runs that cloud pool's template image. | | `name` | `Option` | `None` | Name. `None`: ephemeral (cloud: released with the sandbox; the managed capacity stays for reuse). | | `token` | `Option` | `None` | spacesd token. | | `pool` | `Option` | `None` | Fleet: claim from this existing (user-owned) pool. Unset: a managed, autoscaled pool keyed by image and shape. Local (with an empty image): run this Fleet pool's template locally. | | `os` | `Option` | `None` | Guest OS (`linux`, `macos`, `windows`). | | `cpus` | `Option` | `None` | vCPUs. | | `memory_mb` / `memoryMb` | `Option` | `None` | Memory (MiB). | | `ports` | `Vec` | `[]` | Extra guest ports (local: published; Fleet: `port-` services). | | `services` | `HashMap` | `[:]` | Named services (name → guest port). `env` defaults to 3211. | | `wait_for` / `waitFor` | [`Vec`](#readinessprobe-record) | `[]` | Readiness probes. | | `ready_timeout_ms` / `readyTimeoutMs` | `Option` | `None` | Readiness budget (default 10 min). | | `env` | `HashMap` | `[:]` | Guest environment (local: container environment or cloud-init; cloud: the template environment with `processMode: Run`, on gVisor and KubeVirt; values are not secrets). | | `fleet_replicas` / `fleetReplicas` | `Option` | `None` | Deprecated: initial replicas of a new managed pool (> 0 = warm). | | `fleet_ttl_seconds` / `fleetTtlSeconds` | `Option` | `None` | Deprecated: use `cloud.claim_ttl_seconds`. | | `warm` | `Option` | `None` | Deprecated: use `cloud.warm`. | | `max_pool_size` / `maxPoolSize` | `Option` | `None` | Deprecated: use `cloud.max_pool_size`. | | `command` | `Option>` | `None` | The sandbox's command (argv), replacing the image's entrypoint. Same meaning local (container ENTRYPOINT, or run by cloud-init in a Linux VM) and in the cloud (the template command with `processMode: Run`, on gVisor and KubeVirt). | | `cloud` | [`Option`](#cloudoptions-record) | `None` | Advanced cloud options (warm capacity, limits, TTL, runtime, a dedicated pool). They win over the deprecated flat fields. | | `sidecars` | [`Vec`](#container-record) | `[]` | Sidecar containers, addressed by name (see `Container`). | | `registry_secret` / `registrySecret` | [`Option`]() | `None` | Credentials for a private `image` (and sidecar images on its registry). | | `build` | [`Option`]() | `None` | Image layers built on top of `image` (cloud: a remote build; local: a build into the container engine; both cached by content). | | `network` | `Option` | `None` | Guest network: `"default"` (unset) gives outbound network, like a Docker container; `"none"` cuts it while published ports still work. Only local QEMU VMs honour `"none"`; containers, Lume and cloud sandboxes reject it. | | `overlays` | [`Vec`]() | `[]` | Binaries to inject once the sandbox is up, so tests run the build under test and not the copy the image bundles (see `Overlay`; the same as calling `Sandbox.overlay` after create). Needs cua-spacesd in the image, or a local container. | | `keep_on_failure` / `keepOnFailure` | `bool` | `false` | Keep a named sandbox whose readiness check fails (its cloud claim, VM or container, and its record) to debug it. `false` (the default): a failed create deletes what it made and releases the claim. Ephemeral sandboxes are always deleted. | | `gpu` | `Option` | `None` | A GPU option of the runtime it runs on (`Spaces.gpu_support` lists them): `paravirtual` (a macOS VM on Lume: GPU acceleration, experimental), `virgl` (QEMU on Linux), `nvidia` (a runc container on Linux), a contrib provider's GPU type; `auto` picks the runtime's own. `None` (the default): no GPU. | ## `CloudOptions` record Advanced options for cloud sandboxes (warm capacity, limits, TTL, a dedicated pool). Leave unset for the defaults. Setting them with `on` unset means the cloud; with `on = "local"` they are an error. | Field | Type | Default | Description | | --- | --- | --- | --- | | `warm` | `Option` | `None` | Keep one warm replica of this image ready (faster next start). | | `max_pool_size` / `maxPoolSize` | `Option` | `None` | Most sandboxes of this image at once (default 10). | | `claim_ttl_seconds` / `claimTtlSeconds` | `Option` | `None` | Seconds the sandbox outlives this process without a keep-alive (renewed while held; default 15 min). | | `pool` | `Option` | `None` | Dedicated capacity: claim from this existing pool. The sandbox fields given alongside (image, command, env, services, sidecars, cpus, memory) must match its template, or creation fails with `PoolSpecMismatch` and a diff. | | `apply` | `bool` | `false` | With `pool`: update the pool's template to the given fields instead of failing on a mismatch (`Fleet.apply` semantics for the template; the pool's capacity is kept). Managed pools refuse it. | ## `ReadinessProbe` record A user-declared readiness probe. With no probes a sandbox is ready as soon as its provider reports it running; nothing assumes a guest daemon. Name a declared service (`service = "mcp"`) or a guest port. | Field | Type | Default | Description | | --- | --- | --- | --- | | `port` | `u16` | `0` | Guest port (0 with `service`). | | `http_path` / `httpPath` | `Option` | `None` | HTTP path; `None` is a TCP connect probe. | | `http_status` / `httpStatus` | `Option` | `None` | Exact HTTP status to wait for; `None` accepts any 2xx. | | `service` | `Option` | `None` | A declared service whose guest port to probe (instead of `port`). | ## `Container` record An extra container next to a sandbox (a compose-style sidecar), addressed by name on every runtime: the sandbox reaches it at its `name` (on its `ports`), it reaches the sandbox at `main`, and `services` may name its ports (`{"db": 5432}`). Local containers run it in the sandbox's network namespace (`localhost` works too; needs `runtime="runc"`); cloud gVisor sandboxes run it in the same pod; cloud VM (KubeVirt) sandboxes run it in a companion pod named in the guest's `/etc/hosts`. Local VM sandboxes refuse sidecars. With sidecars the service names `main`, `sidecars` and `sc` are reserved. | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `String` | | Image reference. | | `command` | `Option>` | `None` | argv replacing the image's entrypoint. | | `env` | `HashMap` | `[:]` | Environment (plain values, not secrets). | | `ports` | `Vec` | `[]` | Ports it listens on. | | `name` | `Option` | `None` | Name, unique within the sandbox, and its hostname (not `main`). Unset: from the image (`redis`). | --- # Image Canonical images, image references, build layers and registry credentials. Images are references: the functions here resolve and normalise them without pulling anything. `ImageBuild` layers and a `RegistrySecret` go into `SandboxCreateOptions`. Python programs usually use the high-level API instead: [`cua_sandbox.Image`](). `canonical_image` names one public repository per OS; `CUA_IMAGE_LINUX`, `CUA_IMAGE_WINDOWS` and `CUA_IMAGE_MACOS` override it. The [image catalog details]() lists each image's variants, and [runtime support]() where each one runs. ## `canonical_image` The canonical image for `os` (`linux`, `windows`, `macos`) and `version` (`None` = default: `ghcr.io/trycua/linux:24.04`, `ghcr.io/trycua/windows:2022`, `ghcr.io/trycua/macos:26`; macOS accepts `tahoe`/`sequoia`). `CUA_IMAGE_` overrides the default version. **Python** ```python def canonical_image(os: str, version: Optional[str]) -> str ``` **TypeScript** ```ts function canonicalImage(os: string, version: string | undefined): string ``` **Swift** ```swift func canonicalImage(os: String, version: String?) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun canonicalImage(os: String, version: String?): String ``` **Rust** ```rust pub fn canonical_image(os: String, version: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `os` | `String` | required | | `version` | `Option` | required | **Returns** `String` · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="canonical-image-py" session="canonical-image-py" import cua print([cua.canonical_image(os, None) for os in ("linux", "windows", "macos")]) # ['ghcr.io/trycua/linux:24.04', 'ghcr.io/trycua/windows:2022', 'ghcr.io/trycua/macos:26'] ``` **Swift** ```swift test="swift" id="canonical-image-swift" import Cua print(try ["linux", "windows", "macos"].map { try canonicalImage(os: $0, version: nil) }) // ["ghcr.io/trycua/linux:24.04", "ghcr.io/trycua/windows:2022", "ghcr.io/trycua/macos:26"] ``` **Kotlin** ```kotlin test="kotlin" id="canonical-image-kt" import ai.cua.sdk.canonicalImage println(listOf("linux", "windows", "macos").map { canonicalImage(it, null) }) // [ghcr.io/trycua/linux:24.04, ghcr.io/trycua/windows:2022, ghcr.io/trycua/macos:26] ``` ## `image_alias` The canonical image a CLI alias (`linux`, `windows`, `macos[:version]`, bare `ubuntu`) names, or `None` for a plain reference (`ubuntu:24.04` is Docker Hub's image). **Python** ```python def image_alias(name: str) -> Optional[str] ``` **TypeScript** ```ts function imageAlias(name: string): string | undefined ``` **Swift** ```swift func imageAlias(name: String) -> String? ``` **Kotlin** ```kotlin fun imageAlias(name: String): String? ``` **Rust** ```rust pub fn image_alias(name: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** `Option` **Example** **Python** ```python test="docs" id="image-alias-py" session="image-alias-py" import cua print([cua.image_alias("macos:sequoia"), cua.image_alias("ubuntu:24.04")]) # ['ghcr.io/trycua/macos:15', None] ``` **Swift** ```swift test="swift" id="image-alias-swift" import Cua print(imageAlias(name: "macos:sequoia") ?? "none", imageAlias(name: "ubuntu:24.04") ?? "none") // ghcr.io/trycua/macos:15 none ``` **Kotlin** ```kotlin test="kotlin" id="image-alias-kt" import ai.cua.sdk.imageAlias println(listOf(imageAlias("macos:sequoia"), imageAlias("ubuntu:24.04"))) // [ghcr.io/trycua/macos:15, null] ``` ## `normalize_image` Normalises a reference like the docker CLI (`python` is `docker.io/library/python:latest`). No registry read. **Python** ```python def normalize_image(reference: str) -> str ``` **TypeScript** ```ts function normalizeImage(reference: string): string ``` **Swift** ```swift func normalizeImage(reference: String) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun normalizeImage(reference: String): String ``` **Rust** ```rust pub fn normalize_image(reference: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `reference` | `String` | required | **Returns** `String` · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="normalize-image-py" session="normalize-image-py" import cua print([cua.normalize_image("python:3.12-slim"), cua.normalize_image("ghcr.io/trycua/linux:24.04")]) # ['docker.io/library/python:3.12-slim', 'ghcr.io/trycua/linux:24.04'] ``` **Swift** ```swift test="swift" id="normalize-image-swift" import Cua print(try normalizeImage(reference: "python:3.12-slim")) // docker.io/library/python:3.12-slim ``` **Kotlin** ```kotlin test="kotlin" id="normalize-image-kt" import ai.cua.sdk.normalizeImage println(normalizeImage("python:3.12-slim")) // docker.io/library/python:3.12-slim ``` ## `resolve_image` Resolves `reference` for `backend` (`auto`, `local`, `fleet`, `container`/`docker`/`gvisor`, `vm`/`qemu`/`kubevirt`, `lume`) on `arch` (`amd64`/`arm64`; `None` = this host, or amd64 for `fleet`). `reference` is literal (`ubuntu:24.04` is Docker Hub's image): no alias applies here; use `image_alias` or `canonical_image` first. Raises `NotFound` (no such image: e.g. a tag only the local engine has), `Unauthenticated` (the registry refused the credentials), `Unsupported` (no variant the backend runs, e.g. a macOS image on Fleet). Blocks while the registry is read (cached for five minutes). **Python** ```python def resolve_image(reference: str, backend: str, arch: Optional[str]) -> ResolvedImage ``` **TypeScript** ```ts function resolveImage(reference: string, backend: string, arch: string | undefined): ResolvedImage ``` **Swift** ```swift func resolveImage(reference: String, backend: String, arch: String?) throws -> ResolvedImage ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun resolveImage(reference: String, backend: String, arch: String?): ResolvedImage ``` **Rust** ```rust pub fn resolve_image(reference: String, backend: String, arch: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `reference` | `String` | required | | `backend` | `String` | required | | `arch` | `Option` | required | **Returns** [`ResolvedImage`](#resolvedimage-record) · **Raises** [`CuaError`]() ([`NotFound`](), [`Unauthenticated`](), [`Unsupported`]()) ## `resolve_image_with_secret` `resolve_image` for a private image: `secret` joins the head of the registry auth chain (it is read in this process and never cached or logged). Same errors as `resolve_image`. **Python** ```python def resolve_image_with_secret(reference: str, backend: str, arch: Optional[str], secret: RegistrySecret) -> ResolvedImage ``` **TypeScript** ```ts function resolveImageWithSecret(reference: string, backend: string, arch: string | undefined, secret: RegistrySecret): ResolvedImage ``` **Swift** ```swift func resolveImageWithSecret(reference: String, backend: String, arch: String?, secret: RegistrySecret) throws -> ResolvedImage ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun resolveImageWithSecret(reference: String, backend: String, arch: String?, secret: RegistrySecret): ResolvedImage ``` **Rust** ```rust pub fn resolve_image_with_secret(reference: String, backend: String, arch: Option, secret: RegistrySecret) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `reference` | `String` | required | | `backend` | `String` | required | | `arch` | `Option` | required | | `secret` | [`RegistrySecret`](#registrysecret-enum) | required | **Returns** [`ResolvedImage`](#resolvedimage-record) · **Raises** [`CuaError`]() ## `ResolvedImage` record A reference resolved for a backend. Returned by [`resolve_image`](#resolve_image), [`resolve_image_with_secret`](#resolve_image_with_secret). | Field | Type | Default | Description | | --- | --- | --- | --- | | `reference` | `String` | | The reference as given, normalised (`docker.io/library/python:3.12-slim`). | | `variant_ref` / `variantRef` | `String` | | The chosen variant by tag (the input or its sibling). Lume pulls this. | | `pinned_ref` / `pinnedRef` | `String` | | `registry/repo@sha256:…` of the chosen variant. | | `digest` | `String` | | The digest in `pinned_ref`. | | `variant` | `String` | | `rootfs`, `containerdisk` or `lume`. | | `arch` | `Option` | | Architecture to run (`amd64`/`arm64`). | | `architectures` | `Vec` | | Architectures the variant offers. | | `emulated` | `bool` | | Whether `arch` differs from the requested one (runs emulated). | | `os` | `String` | | Guest OS: `linux`, `windows` or `macos`. | | `spacesd` | `Option` | | Whether the image carries cua-spacesd (`None`: cannot tell). | ## `ImageInfo` record The image a sandbox runs, as resolved and pinned at create time. Returned by [`Fleet.pool_image_info`](), [`Sandbox.image_info`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `reference` | `String` | | The reference as requested, normalised (`docker.io/library/python:3.12-slim`). | | `pinned_ref` / `pinnedRef` | `String` | | `registry/repo@sha256:...` of the variant that runs. | | `digest` | `String` | | The digest in `pinned_ref`. | | `variant` | `String` | | `rootfs`, `containerdisk` or `lume`. | | `arch` | `Option` | | Architecture that runs (`amd64`/`arm64`), when known. | | `os` | `String` | | Guest OS: `linux`, `windows` or `macos`. | | `emulated` | `bool` | | Whether it runs emulated (no build for the requested architecture). | | `spacesd` | `Option` | `None` | Whether the image carries cua-spacesd (`ai.cua.spacesd` label or a published 3211); `None` when it does not say. | ## `ImageBuild` record Image layers built on top of a sandbox's image. Cloud: a remote build (Fleet images API). Local: a build into the container engine (container images only; VM images take no layers here). Both are cached by the same content hash (`cua-b-`), so an identical spec never rebuilds. | Field | Type | Default | Description | | --- | --- | --- | --- | | `layers` | [`Vec`](#imagelayer-enum) | `[]` | Steps, in order. | | `env` | `HashMap` | `[:]` | Image environment. | | `ports` | `Vec` | `[]` | Ports the image exposes. | | `files` | [`Vec`](#buildfile-record) | `[]` | Local files copied in. | | `timeout_ms` / `timeoutMs` | `Option` | `None` | Build budget (default one hour). | ## `ImageLayer` enum One image build step (as in `images.cua.ai/v1alpha1` recipes). **Python** ```python ImageLayer.APT_INSTALL(packages: List[str]) ImageLayer.PIP_INSTALL(packages: List[str]) ImageLayer.UV_INSTALL(packages: List[str]) ImageLayer.RUN(command: str) ImageLayer.APP_INSTALL(app_id: str) ``` **TypeScript** ```ts new ImageLayer.AptInstall({ packages: Array }) new ImageLayer.PipInstall({ packages: Array }) new ImageLayer.UvInstall({ packages: Array }) new ImageLayer.Run({ command: string }) new ImageLayer.AppInstall({ appId: string }) ``` **Swift** ```swift .aptInstall(packages: [String]) .pipInstall(packages: [String]) .uvInstall(packages: [String]) .run(command: String) .appInstall(appId: String) ``` **Kotlin** ```kotlin ImageLayer.AptInstall(packages: List) ImageLayer.PipInstall(packages: List) ImageLayer.UvInstall(packages: List) ImageLayer.Run(command: String) ImageLayer.AppInstall(appId: String) ``` **Rust** ```rust ImageLayer::AptInstall { packages: Vec } ImageLayer::PipInstall { packages: Vec } ImageLayer::UvInstall { packages: Vec } ImageLayer::Run { command: String } ImageLayer::AppInstall { app_id: String } ``` | Variant | Description | | --- | --- | | `AptInstall` | `apt-get install`. Fields: `packages: Vec` | | `PipInstall` | `pip install`. Fields: `packages: Vec` | | `UvInstall` | `uv pip install`. Fields: `packages: Vec` | | `Run` | A shell command. Fields: `command: String` | | `AppInstall` | An app from the cua catalog (VM images only). Fields: `app_id: String` | ## `BuildFile` record A local file copied into a built image. | Field | Type | Default | Description | | --- | --- | --- | --- | | `source` | `String` | | Local path. | | `destination` | `String` | | Absolute path in the image. | ## `RegistrySecret` enum Credentials for a private registry image. Local sandboxes pull with them; cloud sandboxes store them as the sandbox's registry pull Secret. Never logged or persisted. **Python** ```python RegistrySecret.BASIC(username: str, password: str, registry: Optional[str]) RegistrySecret.FROM_ENV(username_var: str, password_var: str, registry: Optional[str]) RegistrySecret.AWS_ECR(region: Optional[str]) ``` **TypeScript** ```ts new RegistrySecret.Basic({ username: string, password: string, registry: string | undefined }) new RegistrySecret.FromEnv({ usernameVar: string, passwordVar: string, registry: string | undefined }) new RegistrySecret.AwsEcr({ region: string | undefined }) ``` **Swift** ```swift .basic(username: String, password: String, registry: String?) .fromEnv(usernameVar: String, passwordVar: String, registry: String?) .awsEcr(region: String?) ``` **Kotlin** ```kotlin RegistrySecret.Basic(username: String, password: String, registry: String?) RegistrySecret.FromEnv(usernameVar: String, passwordVar: String, registry: String?) RegistrySecret.AwsEcr(region: String?) ``` **Rust** ```rust RegistrySecret::Basic { username: String, password: String, registry: Option } RegistrySecret::FromEnv { username_var: String, password_var: String, registry: Option } RegistrySecret::AwsEcr { region: Option } ``` | Variant | Description | | --- | --- | | `Basic` | A user name and password (or access token). Fields: `username: String`, `password: String`, `registry: Option` | | `FromEnv` | Read from environment variables of this process at create time. Fields: `username_var: String`, `password_var: String`, `registry: Option` | | `AwsEcr` | A private Amazon ECR image: a login token from the AWS CLI (`aws ecr get-login-password`) with the caller's AWS credentials. Fields: `region: Option` | ## `canonical_image_tier` The canonical image for `os`, `version` and `tier`: `slim` (the minimum that passes `cua-spacesd doctor --strict`; CI and benchmarks use it), `full` or `None` (the default, with dev tooling) or, on macOS, `xcode` / `xcode-` (full plus one pinned Xcode). The tag is `[-]` (`ghcr.io/trycua/linux:24.04-slim`); the resolver finds its `-disk` sibling. `CUA_IMAGE_` overrides only the default full image. Raises `ImageNotPublished` for a tier CI has not published yet, `InvalidArgument` for an unknown tier or `xcode` outside macOS. **Python** ```python def canonical_image_tier(os: str, version: Optional[str], tier: Optional[str]) -> str ``` **TypeScript** ```ts function canonicalImageTier(os: string, version: string | undefined, tier: string | undefined): string ``` **Swift** ```swift func canonicalImageTier(os: String, version: String?, tier: String?) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun canonicalImageTier(os: String, version: String?, tier: String?): String ``` **Rust** ```rust pub fn canonical_image_tier(os: String, version: Option, tier: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `os` | `String` | required | | `version` | `Option` | required | | `tier` | `Option` | required | **Returns** `String` · **Raises** [`CuaError`]() ([`ImageNotPublished`](), [`InvalidArgument`]()) ## `omarchy_image` The Omarchy image, `ghcr.io/trycua/omarchy:` (default `edge`; also `rc`, `stable`): Omarchy (Arch Linux, Hyprland) with cua-spacesd, as an amd64 VM (QEMU locally, KubeVirt in the cloud; emulated on arm64 hosts). Raises `ImageNotPublished` until CI publishes the channel; pass the reference explicitly to use it before then. **Python** ```python def omarchy_image(channel: Optional[str]) -> str ``` **TypeScript** ```ts function omarchyImage(channel: string | undefined): string ``` **Swift** ```swift func omarchyImage(channel: String?) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun omarchyImage(channel: String?): String ``` **Rust** ```rust pub fn omarchy_image(channel: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `channel` | `Option` | required | **Returns** `String` · **Raises** [`CuaError`]() ([`ImageNotPublished`]()) --- # Image catalog details Every image variant, its runtimes, guest services and limits. Any image works as a sandbox; nothing in lifecycle or readiness needs a guest agent. The `cua-sandbox` computer interfaces (`screen`, `mouse`, `shell`, `files`) and the Spaces primitives need [cua-spacesd]() on port 3211. Images without it are reached through the services you declare. See [Choose an image](). Windows images do not include a Windows license. Bring your own license covering your use, including hosting and virtualization rights. A successful boot is not a licensing check. The table is generated from the SDK source (`cua-image` canonical images) and recorded image facts. | OS | Distribution / version / architecture | Desktop / window manager | Image kind / runtime | Registry image or source | Evidence / limits | | --- | --- | --- | --- | --- | --- | | Linux | Ubuntu 24.04, amd64 and arm64 | XFCE on Xvfb, HTML5 viewer on 3211 (`/viewer/`) | Container (local: Docker or Podman, gVisor when installed; cloud: gVisor); `kind="vm"`: the `-disk` containerDisk (local QEMU, cloud KubeVirt) | `ghcr.io/trycua/linux:24.04` | `Image.linux()` default. Built from `libs/images/linux`; ships cua-spacesd on 3211 (service `env`). Warm by default in the cloud. The resolver pins the digest. | | Linux | Omarchy 4.0.2-1 (Arch Linux-based), amd64 | Hyprland / Wayland | Fleet containerDisk | `public.ecr.aws/k5j5w0x5/cua-omarchy-workspace@sha256:dd09f70f8cd35eca7871dff000538302baad63688a3008803f2238492802d743` | Deployment qualification recorded boot and readiness for `server:8000` and `mcp:3000` with `cua-computer-server` 0.3.46 (legacy guest agent) and embedded Driver 0.22.2; direct Driver qualification used 0.26.1. The `cua-sandbox` interfaces need cua-spacesd, which this artifact does not ship; use its declared services directly. | | Linux | NixOS; release and architecture not recorded in the publication summary | StumpWM | Published candidate image | `public.ecr.aws/k5j5w0x5/cua-nixos-stumpwm:git-ee87f38043c043831a8761016015d6ef5ae783d9` | [Publication summary](https://github.com/trycua/cua/pull/3585) reports the legacy computer-server guest agent and Cua Driver included, guest tests and CI anonymous-pull checks passed. Component versions, service ports, and Fleet claim checks are not recorded there. Commit-named candidate tag, not a digest pin or `latest`; no built-in SDK mapping. | | Linux | NixOS; release and architecture not recorded in the publication summary | Xfce / Xfwm4 | Published candidate image | `public.ecr.aws/k5j5w0x5/cua-nixos-xfce:git-ee87f38043c043831a8761016015d6ef5ae783d9` | [Publication summary](https://github.com/trycua/cua/pull/3585) reports the legacy computer-server guest agent and Cua Driver included, guest tests and CI anonymous-pull checks passed. Component versions, service ports, and Fleet claim checks are not recorded there. Commit-named candidate tag, not a digest pin or `latest`; no built-in SDK mapping. | | Windows | Server 2022, amd64 | Native Windows desktop | VM from the `-disk` containerDisk (local QEMU, cloud KubeVirt); EFI firmware | `ghcr.io/trycua/windows:2022` | `Image.windows()` default. Ships cua-spacesd (a logon task in the desktop session) with input through the Windows cua-driver. Warm by default in the cloud. | | macOS | Tahoe 26 | Native macOS desktop | Local Lume VM | `ghcr.io/trycua/macos:26` | `Image.macos()` default (full tier; `tier="slim"` for `:26-slim`). Compatible Apple silicon host required; no cloud variant yet. Built from `libs/images/macos`; ships cua-spacesd at login on 3211 (service `env`), gated on `cua-spacesd doctor --strict` and the accessibility probe. The resolver pins the digest. | | macOS | Sequoia 15 | Native macOS desktop | Local Lume VM | `ghcr.io/trycua/macos:15` | `Image.macos("15")`. Compatible Apple silicon host required; no cloud variant yet. Ships the legacy `cua-computer-server` guest agent, not cua-spacesd; reach it through services you declare. `cua sb exec`, `cua sb screenshot` and `cua sb view` (and `sb.shell`, `sb.screen`) reach it without cua-spacesd, over SSH and VNC. | ## Tiers Canonical images come in tiers. The tag is `[-][-disk]`: - **full** (no suffix, the default for `Image.linux()`, `Image.macos()`, `Image.windows()` and `cua sb create `): the desktop plus dev tooling. - **slim** (`-slim`): the minimum that passes `cua-spacesd doctor --strict` with every feature. CI and benchmarks use it. - **xcode** (`-xcode`, `-xcode-`, macOS only): full plus one pinned Xcode, its iOS simulator runtime and the Metal toolchain. Pick one with `Image.linux(tier="slim")` or `cua sb create linux --tier slim`. A tier that is not published yet raises `ImageNotPublished`; pass its reference to `Image.from_registry` to use it anyway. Immutable pins append `--`. | Image | Tier | SDK | Status | | --- | --- | --- | --- | | `ghcr.io/trycua/linux:24.04` | full | `Image.linux()` | Published | | `ghcr.io/trycua/linux:24.04-slim` | slim | `Image.linux(tier="slim")` | Published | | `ghcr.io/trycua/windows:2022` | full | `Image.windows()` | Published | | `ghcr.io/trycua/macos:26` | full | `Image.macos()` | Published | | `ghcr.io/trycua/macos:26-slim` | slim | `Image.macos(tier="slim")` | Published | | `ghcr.io/trycua/macos:26-xcode` | xcode | `Image.macos(tier="xcode")` | Not published yet | ## What's installed [What's installed]() lists the apps and tools in each image tier, with the versions `cua-spacesd doctor --strict` measured. ## Images in the app pickers Cua Spaces and the OpenKoalaBots samples offer these images when you create a Space. The apps and this table read one file, `libs/images/sandbox-images.json`. Each entry names its distribution (`distro`), so a new Space shows the right OS icon and System while it is still being created. Adapter benchmark images join the list once they are published. | Image | Group | Name | Tier | Local | Cloud | cua-spacesd | Browsers | | --- | --- | --- | --- | --- | --- | --- | --- | | `ghcr.io/trycua/linux:24.04` | Desktop images | [Ubuntu 24.04]() | full | Container (gVisor when installed) | gVisor container | Yes | Chromium, Firefox | | `ghcr.io/trycua/linux:24.04-disk` | Desktop images | [Ubuntu 24.04 VM]() | full | QEMU VM | KubeVirt VM | Yes | Chromium, Firefox | | `ghcr.io/trycua/linux:24.04-slim` | Desktop images | [Ubuntu 24.04 slim]() | slim | Container (gVisor when installed) | gVisor container | Yes | Chromium | | `ghcr.io/trycua/linux:24.04-slim-disk` | Desktop images | [Ubuntu 24.04 slim VM]() | slim | QEMU VM | KubeVirt VM | Yes | Chromium | | `ghcr.io/trycua/windows:2022` | Desktop images | [Windows Server 2022]() | full | QEMU VM | KubeVirt VM | Yes | Edge | | `ghcr.io/trycua/macos:26` | Desktop images | [macOS Tahoe 26]() | full | Lume VM (Apple silicon) | Not available yet | Yes | Safari, Google Chrome | | `ghcr.io/trycua/macos:26-slim` | Desktop images | [macOS Tahoe 26 slim]() | slim | Lume VM (Apple silicon) | Not available yet | Yes | Safari, Google Chrome | | `ghcr.io/trycua/macos:15` | Desktop images | [macOS Sequoia 15]() | - | Lume VM (Apple silicon) | Not available yet | No | Safari | | `ghcr.io/trycua/omarchy:edge` | Distribution images | Omarchy | - | QEMU VM | KubeVirt VM | Yes | Chromium | | `ghcr.io/trycua/omarchy:edge-disk` | Distribution images | Omarchy disk | - | QEMU VM | KubeVirt VM | Yes | Chromium | --- # What's installed The apps, tools and simulator runtimes each image tier ships, as the strict doctor measured them. Each table lists what `cua-spacesd doctor --strict` measured in a booted guest of the image: the apps and tools the image claims, and on Xcode tiers its simulator runtimes. A missing tool or a wrong version fails the doctor, so a published tier always matches its table. The `-disk` variant of each image carries the same software. Tiers: `-slim` is the minimum for every cua-spacesd feature, the untagged image (`Image.linux()`, `Image.macos()`) adds developer tooling, and `-xcode` (macOS) adds Xcode. See [Image catalog details](). ## `ghcr.io/trycua/linux:24.04-slim` Tier: slim. Recorded from 2026-09-28, container/arm64, published 24.04-slim (sha256:a67d2f0d), doctor report of its arm64 child. | Kind | Name | Version | | --- | --- | --- | | App | chromium | `Chromium 154.0.8037.57` | | App | python3 | `Python 3.12.3` | ## `ghcr.io/trycua/linux:24.04` Tier: full (default). Recorded from 2026-09-28, container/arm64, published 24.04 (sha256:f5306ce8), doctor report of its arm64 child. | Kind | Name | Version | | --- | --- | --- | | App | chromium | `Chromium 154.0.8037.57` | | App | firefox | `Mozilla Firefox 156.0.1` | | App | python3 | `Python 3.12.3` | | Tool | cargo | `cargo 1.98.1 (797e8a9bc 2026-08-05)` | | Tool | cmake | `cmake version 3.28.3` | | Tool | curl | `curl 8.5.0` | | Tool | docker | `Docker version 29.8.1, build 4a63305` | | Tool | fd | `fdfind 9.0.0` | | Tool | gcc | `gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0` | | Tool | gh | `gh version 2.101.0 (2026-09-15)` | | Tool | git | `git version 2.43.0` | | Tool | git-lfs | `git-lfs/3.4.1 (GitHub; linux arm64; go 1.22.2)` | | Tool | go | `go version go1.27.1 linux/arm64` | | Tool | jq | `jq-1.7` | | Tool | make | `GNU Make 4.3` | | Tool | ninja | `1.11.1` | | Tool | node | `v24.21.0` | | Tool | pkg-config | `1.8.1` | | Tool | pnpm | `12.6.0` | | Tool | python3 | `Python 3.12.3` | | Tool | ripgrep | `ripgrep 14.1.0` | | Tool | rustc | `rustc 1.98.1 (48a229cea 2026-09-01)` | | Tool | rustup | `rustup 1.29.1 (d95a37b6a 2026-08-13)` | | Tool | unzip | `UnZip 6.00 of 20 April 2009, by Debian. Original by Info-ZIP.` | | Tool | uv | `uv 0.12.19 (aarch64-unknown-linux-gnu)` | | Tool | zip | `This is Zip 3.0 (July 5th 2008), by Info-ZIP.` | ## `ghcr.io/trycua/macos:26-slim` Tier: slim. Recorded from 2026-09-28, lume/arm64, published pin macos:26-slim-20260927-9a3828d (sha256:8a4bd1a2c64d5a7f48e8a0aa6ef01df9298bf63966a68e1f6fd62619aeafe0f1), Lume build of 9a3828d. | Kind | Name | Version | | --- | --- | --- | | App | chrome | `154.0.8037.58` | | App | safari | `26.5.2` | ## `ghcr.io/trycua/macos:26` Tier: full (default). Recorded from 2026-09-28, lume/arm64, published pin macos:26-20260927-9a3828d (sha256:3b30954940a631d3d22055e7099ad023973fca28624e9bbfc0127c6d53e42649), Lume build of 9a3828d. | Kind | Name | Version | | --- | --- | --- | | App | chrome | `154.0.8037.58` | | App | safari | `26.5.2` | | Tool | brew | `Homebrew 7.0.6` | | Tool | cargo | `cargo 1.98.1 (797e8a9bc 2026-08-05)` | | Tool | clang | `Apple clang version 21.0.0 (clang-2100.1.1.101)` | | Tool | gh | `gh version 2.101.0 (2026-09-15)` | | Tool | git | `git version 2.50.1 (Apple Git-155)` | | Tool | go | `go version go1.27.1 darwin/arm64` | | Tool | jq | `jq-1.8.2` | | Tool | node | `v24.21.0` | | Tool | pnpm | `12.6.0` | | Tool | python3 | `Python 3.9.6` | | Tool | rg | `ripgrep 15.2.0 (rev e89fff89ac)` | | Tool | rustc | `rustc 1.98.1 (48a229cea 2026-09-01)` | | Tool | swift | `Apple Swift version 6.3.3 (swiftlang-6.3.3.1.3 clang-2100.1.1.101)` | | Tool | uv | `uv 0.12.19 (bea138450 2026-09-24 aarch64-apple-darwin)` | ## Not recorded yet - `ghcr.io/trycua/windows:2022` --- # Services Named services, port forwards, public URLs and MCP clients of a sandbox. Reach ports inside a sandbox: `sandbox.service(name)` for a declared service, `sandbox.forward(port)` for any port, `sandbox.public_url(...)` for a shareable URL, and `McpClient` for an MCP server. None of them needs cua-spacesd. Python programs usually use the high-level API instead: [`cua_sandbox` services, tunnels and MCP](). | To reach a guest port | Use | Works | | --- | --- | --- | | From your code, by name | `sandbox.service(name).request(...)` or `.url()` | Declared services, locally and in the cloud | | From local tools (a browser, a client) | `sandbox.forward(port)` | Any port; a loopback listener | | From someone else | `sandbox.public_url(service, ttl_seconds, label)` | Declared services; a bearer URL that expires | | From an MCP client | `sandbox.mcp_config(service, path)` or `sandbox.mcp(service, path)` | An MCP server behind a declared service | ## Sandbox services Methods of [`Sandbox`](). ### `Sandbox.service` A named service (for example `"server"` or `"env"`). **Python** ```python def service(self, name: str) -> Service ``` **TypeScript** ```ts service(name: string): ServiceLike ``` **Swift** ```swift func service(name: String) throws -> Service ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun service(name: String): Service ``` **Rust** ```rust pub fn service(&self, name: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`Service`](#service) · **Raises** [`CuaError`]() ### `Sandbox.services` Declared services. **Python** ```python def services(self) -> dict[str, int] ``` **TypeScript** ```ts services(): Map ``` **Swift** ```swift func services() -> [String: UInt16] ``` **Kotlin** ```kotlin fun services(): Map ``` **Rust** ```rust pub fn services(&self) -> HashMap ``` **Returns** `HashMap` ### `Sandbox.forward` Forwards a loopback port to guest `port`: a TCP forward locally (and over cua-spacesd's tunnel when the image has it); in the cloud without cua-spacesd, a loopback HTTP/WebSocket proxy through the Fleet gateway. Either way `url()` is a loopback URL. **Python** ```python async def forward(self, port: int) -> PortForward ``` **TypeScript** ```ts forward(port: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func forward(port: UInt16) async throws -> PortForward ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun forward(port: UShort): PortForward ``` **Rust** ```rust pub async fn forward(&self, port: u16) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `port` | `u16` | required | **Returns** [`PortForward`](#portforward) · **Async** · **Raises** [`CuaError`]() ### `Sandbox.public_url` A shareable URL for `service` that stops working after `ttl_seconds` (60 s to 24 h, default 1 h). Cloud: a Fleet signed service URL. Local: a loopback URL with its own token, served by the cua daemon (started if needed; `CUA_BIN` names the CLI). **Python** ```python async def public_url(self, service: str, ttl_seconds: Optional[int] = None, label: Optional[str] = None) -> PublicUrl ``` **TypeScript** ```ts publicUrl(service: string, ttlSeconds: number | undefined = undefined, label: string | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func publicUrl(service: String, ttlSeconds: UInt32? = nil, label: String? = nil) async throws -> PublicUrl ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun publicUrl(service: String, ttlSeconds: UInt? = null, label: String? = null): PublicUrl ``` **Rust** ```rust pub async fn public_url(&self, service: String, ttl_seconds: Option, label: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `service` | `String` | required | | `ttl_seconds` | `Option` | `None` | | `label` | `Option` | `None` | **Returns** [`PublicUrl`](#publicurl-record) · **Async** · **Raises** [`CuaError`]() ### `Sandbox.revoke_public_url` Revokes a URL from `public_url`. **Python** ```python async def revoke_public_url(self, id: str) -> None ``` **TypeScript** ```ts revokePublicUrl(id: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func revokePublicUrl(id: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun revokePublicUrl(id: String) ``` **Rust** ```rust pub async fn revoke_public_url(&self, id: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Sandbox.mcp_config` Where the MCP server behind `service` is (URL of its endpoint at `path`, default `/mcp`, plus the headers every request needs), for any MCP client. Embedded: the service route itself (loopback, or the Fleet gateway with a fresh bearer and claim header). Daemon: the daemon's streaming passthrough with its loopback bearer. **Python** ```python async def mcp_config(self, service: str, path: Optional[str] = None) -> McpConfig ``` **TypeScript** ```ts mcpConfig(service: string, path: string | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func mcpConfig(service: String, path: String? = nil) async throws -> McpConfig ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun mcpConfig(service: String, path: String? = null): McpConfig ``` **Rust** ```rust pub async fn mcp_config(&self, service: String, path: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `service` | `String` | required | | `path` | `Option` | `None` | **Returns** [`McpConfig`](#mcpconfig-record) · **Async** · **Raises** [`CuaError`]() ### `Sandbox.mcp` An MCP client (the official Rust SDK) for the MCP server behind `service` at `path` (default `/mcp`). **Python** ```python async def mcp(self, service: str, path: Optional[str] = None) -> McpClient ``` **TypeScript** ```ts mcp(service: string, path: string | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func mcp(service: String, path: String? = nil) async throws -> McpClient ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun mcp(service: String, path: String? = null): McpClient ``` **Rust** ```rust pub async fn mcp(&self, service: String, path: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `service` | `String` | required | | `path` | `Option` | `None` | **Returns** [`McpClient`](#mcpclient) · **Async** · **Raises** [`CuaError`]() ## `Service` A named service of a sandbox. Returned by [`Sandbox.service`](#sandboxservice). | Method | Description | | --- | --- | | [`endpoint`](#serviceendpoint) | Where this service is reachable from this process: base URL plus the headers every request needs. | | [`mcp`](#servicemcp) | An MCP client (the official Rust SDK) for this service's endpoint at `path` (default `/mcp`). | | [`mcp_config`](#servicemcp_config) | The MCP endpoint of this service at `path` (default `/mcp`). | | [`public_url`](#servicepublic_url) | A shareable URL for this service that stops working after `ttl_seconds` (60 s to 24 h, default 1 h): the same as `Sandbox.public_url(name, ...)`. | | [`request`](#servicerequest) | One HTTP request to `path` on the service. | | [`url`](#serviceurl) | A URL for the service usable from this machine with no credentials (no trailing slash): the published loopback port locally, a signed service URL (1 h, renewed on later calls) in the cloud. | | Accessor | Returns | Description | | --- | --- | --- | | `name()` | `String` | Service name. | ### `Service.endpoint` Where this service is reachable from this process: base URL plus the headers every request needs. Any HTTP client can use it (SSE, long-lived streams and every method pass through unchanged). **Python** ```python async def endpoint(self) -> ServiceEndpoint ``` **TypeScript** ```ts endpoint(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func endpoint() async throws -> ServiceEndpoint ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun endpoint(): ServiceEndpoint ``` **Rust** ```rust pub async fn endpoint(&self) -> Result ``` **Returns** [`ServiceEndpoint`](#serviceendpoint-record) · **Async** · **Raises** [`CuaError`]() ### `Service.mcp` An MCP client (the official Rust SDK) for this service's endpoint at `path` (default `/mcp`). **Python** ```python async def mcp(self, path: Optional[str] = None) -> McpClient ``` **TypeScript** ```ts mcp(path: string | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func mcp(path: String? = nil) async throws -> McpClient ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun mcp(path: String? = null): McpClient ``` **Rust** ```rust pub async fn mcp(&self, path: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `Option` | `None` | **Returns** [`McpClient`](#mcpclient) · **Async** · **Raises** [`CuaError`]() ### `Service.mcp_config` The MCP endpoint of this service at `path` (default `/mcp`). **Python** ```python async def mcp_config(self, path: Optional[str] = None) -> McpConfig ``` **TypeScript** ```ts mcpConfig(path: string | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func mcpConfig(path: String? = nil) async throws -> McpConfig ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun mcpConfig(path: String? = null): McpConfig ``` **Rust** ```rust pub async fn mcp_config(&self, path: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `Option` | `None` | **Returns** [`McpConfig`](#mcpconfig-record) · **Async** · **Raises** [`CuaError`]() ### `Service.public_url` A shareable URL for this service that stops working after `ttl_seconds` (60 s to 24 h, default 1 h): the same as `Sandbox.public_url(name, ...)`. Cloud: a Fleet signed service URL. Local: a loopback URL with its own token, served by the cua daemon. **Python** ```python async def public_url(self, ttl_seconds: Optional[int] = None, label: Optional[str] = None) -> PublicUrl ``` **TypeScript** ```ts publicUrl(ttlSeconds: number | undefined = undefined, label: string | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func publicUrl(ttlSeconds: UInt32? = nil, label: String? = nil) async throws -> PublicUrl ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun publicUrl(ttlSeconds: UInt? = null, label: String? = null): PublicUrl ``` **Rust** ```rust pub async fn public_url(&self, ttl_seconds: Option, label: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `ttl_seconds` | `Option` | `None` | | `label` | `Option` | `None` | **Returns** [`PublicUrl`](#publicurl-record) · **Async** · **Raises** [`CuaError`]() ### `Service.request` One HTTP request to `path` on the service. Credentials (Fleet bearer and claim) are attached by the runtime. `headers` are sent as given (for example `content-type`, or `accept` and `mcp-session-id` for MCP over Fleet); `authorization` is refused on Fleet, where the gateway bearer owns it. **Python** ```python async def request(self, method: str, path: str, body: Optional[bytes], timeout_ms: Optional[int], headers: Optional[List[HttpHeader]] = None) -> HttpResponse ``` **TypeScript** ```ts request(method: string, path: string, body: ArrayBuffer | undefined, timeoutMs: number | undefined, headers: Array | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func request(method: String, path: String, body: Data?, timeoutMs: UInt32?, headers: [HttpHeader]? = nil) async throws -> HttpResponse ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun request(method: String, path: String, body: ByteArray?, timeoutMs: UInt?, headers: List? = null): HttpResponse ``` **Rust** ```rust pub async fn request(&self, method: String, path: String, body: Option>, timeout_ms: Option, headers: Option>) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `method` | `String` | required | | `path` | `String` | required | | `body` | `Option>` | required | | `timeout_ms` | `Option` | required | | `headers` | [`Option>`](#httpheader-record) | `None` | **Returns** [`HttpResponse`](#httpresponse-record) · **Async** · **Raises** [`CuaError`]() ### `Service.url` A URL for the service usable from this machine with no credentials (no trailing slash): the published loopback port locally, a signed service URL (1 h, renewed on later calls) in the cloud. **Python** ```python async def url(self) -> str ``` **TypeScript** ```ts url(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func url() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun url(): String ``` **Rust** ```rust pub async fn url(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ## `ServiceEndpoint` record Where a sandbox service is reachable from this process. Returned by [`Service.endpoint`](#serviceendpoint). | Field | Type | Default | Description | | --- | --- | --- | --- | | `url` | `String` | | Base URL (append the path). | | `headers` | [`Vec`](#httpheader-record) | | Headers every request needs. | ## `HttpHeader` record An HTTP header. Returned by [`Space.websocket_headers`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Name. | | `value` | `String` | | Value. | ## `HttpResponse` record An HTTP response from a sandbox service. Returned by [`Service.request`](#servicerequest). | Field | Type | Default | Description | | --- | --- | --- | --- | | `status` | `u16` | | Status. | | `headers` | [`Vec`](#httpheader-record) | | Headers. | | `body` | `Vec` | | Body. | ## `PortForward` An active port forward. Closed on `close()` or when dropped. Returned by [`Sandbox.forward`](#sandboxforward). | Method | Description | | --- | --- | | [`close`](#portforwardclose) | Stops the forward. | | Accessor | Returns | Description | | --- | --- | --- | | `guest_port()` | `u16` | Guest port. | | `local_addr()` | `Option` | Loopback `host:port` accepting connections (local/direct). | | `url()` | `Option` | URL to use (loopback URL, or the Fleet gateway URL). | ### `PortForward.close` Stops the forward. **Python** ```python async def close(self) -> None ``` **TypeScript** ```ts close(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func close() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun closeForward() ``` **Rust** ```rust pub async fn close(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ## `PublicUrl` record A shareable URL of a sandbox service that expires. Returned by [`Sandbox.public_url`](#sandboxpublic_url), [`Service.public_url`](#servicepublic_url). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Id for `Sandbox.revoke_public_url`. | | `url` | `String` | | The URL. | | `expires_at_unix` / `expiresAtUnix` | `i64` | | When it stops working (unix seconds). | | `service` | `String` | | Service name. | | `provider_details` / `providerDetails` | `HashMap` | | Provider internals (cloud: namespace, claim; local: upstream). | ## `McpClient` An MCP client (rmcp, streamable HTTP; protocol 2026-07-28 when the server supports it, else the server's revision). Results are the server's JSON, unchanged. Returned by [`Sandbox.mcp`](#sandboxmcp), [`Service.mcp`](#servicemcp), [`mcp_connect`](#mcp_connect), [`mcp_connect_url`](#mcp_connect_url). | Method | Description | | --- | --- | | [`call_tool`](#mcpclientcall_tool) | Calls `name` with a JSON object of arguments (`None`: `{}`); returns the `tools/call` result JSON verbatim (a tool error is `isError`). | | [`close`](#mcpclientclose) | Closes the connection (ends the session on servers that keep one). | | [`get_prompt`](#mcpclientget_prompt) | Renders prompt `name`; the `prompts/get` result JSON. | | [`list_prompts`](#mcpclientlist_prompts) | Every prompt (all pages), JSON array. | | [`list_resource_templates`](#mcpclientlist_resource_templates) | Every resource template (all pages), JSON array. | | [`list_resources`](#mcpclientlist_resources) | Every resource (all pages), JSON array. | | [`list_tools`](#mcpclientlist_tools) | Every tool (all pages) as a JSON array of MCP `Tool` objects. | | [`read_resource`](#mcpclientread_resource) | Reads `uri`; the `resources/read` result JSON. | | [`request`](#mcpclientrequest) | Any other JSON-RPC method (extensions such as `skills/list`, `skills/get`); returns its `result` JSON. | | Accessor | Returns | Description | | --- | --- | --- | | `server_info_json()` | `Option` | The server's `serverInfo`/negotiated version as JSON, when known. | ### `McpClient.call_tool` Calls `name` with a JSON object of arguments (`None`: `{}`); returns the `tools/call` result JSON verbatim (a tool error is `isError`). **Python** ```python async def call_tool(self, name: str, arguments_json: Optional[str]) -> str ``` **TypeScript** ```ts callTool(name: string, argumentsJson: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func callTool(name: String, argumentsJson: String?) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun callTool(name: String, argumentsJson: String?): String ``` **Rust** ```rust pub async fn call_tool(&self, name: String, arguments_json: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `arguments_json` | `Option` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `McpClient.close` Closes the connection (ends the session on servers that keep one). **Python** ```python async def close(self) -> None ``` **TypeScript** ```ts close(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func close() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun closeClient() ``` **Rust** ```rust pub async fn close(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `McpClient.get_prompt` Renders prompt `name`; the `prompts/get` result JSON. **Python** ```python async def get_prompt(self, name: str, arguments_json: Optional[str]) -> str ``` **TypeScript** ```ts getPrompt(name: string, argumentsJson: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func getPrompt(name: String, argumentsJson: String?) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun getPrompt(name: String, argumentsJson: String?): String ``` **Rust** ```rust pub async fn get_prompt(&self, name: String, arguments_json: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `arguments_json` | `Option` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `McpClient.list_prompts` Every prompt (all pages), JSON array. **Python** ```python async def list_prompts(self) -> str ``` **TypeScript** ```ts listPrompts(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func listPrompts() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listPrompts(): String ``` **Rust** ```rust pub async fn list_prompts(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `McpClient.list_resource_templates` Every resource template (all pages), JSON array. **Python** ```python async def list_resource_templates(self) -> str ``` **TypeScript** ```ts listResourceTemplates(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func listResourceTemplates() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listResourceTemplates(): String ``` **Rust** ```rust pub async fn list_resource_templates(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `McpClient.list_resources` Every resource (all pages), JSON array. **Python** ```python async def list_resources(self) -> str ``` **TypeScript** ```ts listResources(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func listResources() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listResources(): String ``` **Rust** ```rust pub async fn list_resources(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `McpClient.list_tools` Every tool (all pages) as a JSON array of MCP `Tool` objects. **Python** ```python async def list_tools(self) -> str ``` **TypeScript** ```ts listTools(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func listTools() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listTools(): String ``` **Rust** ```rust pub async fn list_tools(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `McpClient.read_resource` Reads `uri`; the `resources/read` result JSON. **Python** ```python async def read_resource(self, uri: str) -> str ``` **TypeScript** ```ts readResource(uri: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func readResource(uri: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun readResource(uri: String): String ``` **Rust** ```rust pub async fn read_resource(&self, uri: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `uri` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `McpClient.request` Any other JSON-RPC method (extensions such as `skills/list`, `skills/get`); returns its `result` JSON. **Python** ```python async def request(self, method: str, params_json: Optional[str]) -> str ``` **TypeScript** ```ts request(method: string, paramsJson: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func request(method: String, paramsJson: String?) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun request(method: String, paramsJson: String?): String ``` **Rust** ```rust pub async fn request(&self, method: String, params_json: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `method` | `String` | required | | `params_json` | `Option` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ## `McpConfig` record Where an MCP endpoint is and what every request needs. Hand it to any MCP client (Claude Code, Cursor, the Python or TypeScript SDKs). On Fleet the headers carry a short-lived gateway bearer: fetch a fresh config per connection. Returned by [`Sandbox.mcp_config`](#sandboxmcp_config), [`Service.mcp_config`](#servicemcp_config). | Field | Type | Default | Description | | --- | --- | --- | --- | | `url` | `String` | | Streamable-HTTP endpoint URL. | | `headers` | [`Vec`](#httpheader-record) | | Headers to send with every request. | ## `mcp_connect` Connects an MCP client to `config` (from `Sandbox.mcp_config`). **Python** ```python async def mcp_connect(config: McpConfig) -> McpClient ``` **TypeScript** ```ts function mcpConnect(config: McpConfig, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func mcpConnect(config: McpConfig) async throws -> McpClient ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun mcpConnect(config: McpConfig): McpClient ``` **Rust** ```rust pub async fn mcp_connect(config: McpConfig) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `config` | [`McpConfig`](#mcpconfig-record) | required | **Returns** [`McpClient`](#mcpclient) · **Async** · **Raises** [`CuaError`]() ## `mcp_connect_url` Connects an MCP client to an endpoint URL (`http://host:8765/mcp`; a bare origin means `/mcp`) with `headers` on every request. **Python** ```python async def mcp_connect_url(url: str, headers: Optional[List[HttpHeader]]) -> McpClient ``` **TypeScript** ```ts function mcpConnectUrl(url: string, headers: Array | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func mcpConnectUrl(url: String, headers: [HttpHeader]?) async throws -> McpClient ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun mcpConnectUrl(url: String, headers: List?): McpClient ``` **Rust** ```rust pub async fn mcp_connect_url(url: String, headers: Option>) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `url` | `String` | required | | `headers` | [`Option>`](#httpheader-record) | required | **Returns** [`McpClient`](#mcpclient) · **Async** · **Raises** [`CuaError`]() --- # Local runtimes The container and VM runtimes this machine can host, their setup, and local images. `cua.local()` checks and sets up the local runtimes and moves images in and out of the local engines. Python programs usually use the high-level API instead: [`cua_sandbox` runtimes](). ## `Local` Local runtimes and images. Returned by [`Cua.local`](). | Method | Description | | --- | --- | | [`build_image`](#localbuild_image) | Builds an image from an `images.cua.ai/v1alpha1` Image resource (JSON) on `base`; pushes to `push` when set. | | [`doctor`](#localdoctor) | Inspects local runtimes (read-only: never installs or starts anything). | | [`pull_image`](#localpull_image) | Pulls an image (container engine for container refs, the image cache for `vm:` containerDisks). | | [`push_image`](#localpush_image) | Copies an image (host architecture) to `destination`; returns the pushed manifest digest. | | [`setup`](#localsetup) | Provisions `components` (`qemu`, `lume`; `runsc` reports its plan). | | [`storage`](#localstorage) | Free space where local Spaces are written (Lume's VM storage, the cua home for QEMU, the container engine's data disk) and which catalog images are already pulled. | ### `Local.build_image` Builds an image from an `images.cua.ai/v1alpha1` Image resource (JSON) on `base`; pushes to `push` when set. Returns `reference@digest` or the local output directory. **Python** ```python async def build_image(self, spec_json: str, base: str, push: Optional[str]) -> str ``` **TypeScript** ```ts buildImage(specJson: string, base: string, push: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func buildImage(specJson: String, base: String, push: String?) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun buildImage(specJson: String, base: String, push: String?): String ``` **Rust** ```rust pub async fn build_image(&self, spec_json: String, base: String, push: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `spec_json` | `String` | required | | `base` | `String` | required | | `push` | `Option` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Local.doctor` Inspects local runtimes (read-only: never installs or starts anything). **Python** ```python async def doctor(self) -> DoctorReport ``` **TypeScript** ```ts doctor(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func doctor() async throws -> DoctorReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun doctor(): DoctorReport ``` **Rust** ```rust pub async fn doctor(&self) -> Result ``` **Returns** [`DoctorReport`](#doctorreport-record) · **Async** · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="local-doctor-py" session="local-doctor-py" import asyncio import cua async def main(): report = await cua.embedded().local().doctor() for check in report.checks: print(check.name, check.status) asyncio.run(main()) ``` **Swift** ```swift test="swift" id="local-doctor-swift" import Cua let report = try await Cua.embedded().local().doctor() for check in report.checks { print(check.name, check.status) } ``` **Kotlin** ```kotlin test="kotlin" id="local-doctor-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig val report = Cua.embedded(CuaConfig()).local().doctor() for (check in report.checks) println("${check.name} ${check.status}") ``` ### `Local.pull_image` Pulls an image (container engine for container refs, the image cache for `vm:` containerDisks). **Python** ```python async def pull_image(self, reference: str) -> LocalImage ``` **TypeScript** ```ts pullImage(reference: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func pullImage(reference: String) async throws -> LocalImage ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun pullImage(reference: String): LocalImage ``` **Rust** ```rust pub async fn pull_image(&self, reference: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `reference` | `String` | required | **Returns** [`LocalImage`](#localimage-record) · **Async** · **Raises** [`CuaError`]() ### `Local.push_image` Copies an image (host architecture) to `destination`; returns the pushed manifest digest. **Python** ```python async def push_image(self, reference: str, destination: str) -> str ``` **TypeScript** ```ts pushImage(reference: string, destination: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func pushImage(reference: String, destination: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun pushImage(reference: String, destination: String): String ``` **Rust** ```rust pub async fn push_image(&self, reference: String, destination: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `reference` | `String` | required | | `destination` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Local.setup` Provisions `components` (`qemu`, `lume`; `runsc` reports its plan). Empty = everything provisionable. `dry_run` only reports. **Python** ```python async def setup(self, components: List[str], dry_run: bool) -> List[SetupStep] ``` **TypeScript** ```ts setup(components: Array, dryRun: boolean, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func setup(components: [String], dryRun: Bool) async throws -> [SetupStep] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun setup(components: List, dryRun: Boolean): List ``` **Rust** ```rust pub async fn setup(&self, components: Vec, dry_run: bool) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `components` | `Vec` | required | | `dry_run` | `bool` | required | **Returns** [`Vec`](#setupstep-record) · **Async** · **Raises** [`CuaError`]() ### `Local.storage` Free space where local Spaces are written (Lume's VM storage, the cua home for QEMU, the container engine's data disk) and which catalog images are already pulled. Read-only and bounded; probed in this process (the daemon runs on the same host). **Python** ```python async def storage(self) -> LocalStorage ``` **TypeScript** ```ts storage(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func storage() async throws -> LocalStorage ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun storage(): LocalStorage ``` **Rust** ```rust pub async fn storage(&self) -> Result ``` **Returns** [`LocalStorage`](#localstorage-record) · **Async** · **Raises** [`CuaError`]() ## `DoctorReport` record Doctor output. Returned by [`Local.doctor`](#localdoctor). | Field | Type | Default | Description | | --- | --- | --- | --- | | `checks` | [`Vec`](#runtimecheck-record) | | One check per backend. | | `report_json` / `reportJson` | `String` | | The full cua-vmm host report as JSON. | ## `LocalImage` record A local image. Returned by [`Local.pull_image`](#localpull_image). | Field | Type | Default | Description | | --- | --- | --- | --- | | `reference` | `String` | | Reference. | | `kind` | `String` | | `container` or `vm`. | | `location` | `String` | | Disk path (VM) or engine image reference (container). | | `size_bytes` / `sizeBytes` | `u64` | | Size in bytes, when known. | ## `LocalStorage` record What local Spaces have room for (see `Local.storage`). Returned by [`Local.storage`](#localstorage). | Field | Type | Default | Description | | --- | --- | --- | --- | | `host_arch` / `hostArch` | `String` | | This host's architecture (`arm64`, `amd64`). | | `reserve_bytes` / `reserveBytes` | `u64` | | Bytes cua keeps free on a volume: a create that would leave less fails (`CUA_DISK_MIN_FREE`, default 5 GiB). | | `lume` | [`Option`](#storagevolume-record) | | Where Lume writes macOS VMs. | | `qemu` | [`Option`](#storagevolume-record) | | Where QEMU writes VM disks and the containerDisk cache. | | `container` | [`Option`](#storagevolume-record) | | The container engine's data disk (Colima, Docker Desktop, OrbStack, `/var/lib/docker`), when its free space can be read. | | `pulled` | `Vec` | | Catalog image refs already pulled here at their published digest. | ## `RuntimeCheck` record One doctor check (one backend). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Backend (`container`, `qemu`, `lume`, ...). | | `status` | [`RuntimeCheckStatus`](#runtimecheckstatus-enum) | | Status. | | `version` | `Option` | | Version, when detected. | | `detail` | `String` | | Explanation, missing pieces and what `setup` would do. | ## `SetupStep` record One setup step. Returned by [`Local.setup`](#localsetup). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Component. | | `status` | [`RuntimeCheckStatus`](#runtimecheckstatus-enum) | | Status after the step. | | `detail` | `String` | | What was (or would be) done. | ## `StorageVolume` record Space on the volume a local engine writes to. | Field | Type | Default | Description | | --- | --- | --- | --- | | `available_bytes` / `availableBytes` | `u64` | | Bytes available to this user. | | `total_bytes` / `totalBytes` | `u64` | | Volume size in bytes. | | `name` | `String` | | What a person calls it: `Macintosh HD`, `Colima`, `Docker Desktop`. | ## `RuntimeCheckStatus` enum Status of one local runtime component. **Python** ```python RuntimeCheckStatus.OK RuntimeCheckStatus.INSTALLABLE RuntimeCheckStatus.ERROR RuntimeCheckStatus.NOT_APPLICABLE RuntimeCheckStatus.UNKNOWN ``` **TypeScript** ```ts RuntimeCheckStatus.Ok RuntimeCheckStatus.Installable RuntimeCheckStatus.Error RuntimeCheckStatus.NotApplicable RuntimeCheckStatus.Unknown ``` **Swift** ```swift .ok .installable .error .notApplicable .unknown ``` **Kotlin** ```kotlin RuntimeCheckStatus.OK RuntimeCheckStatus.INSTALLABLE RuntimeCheckStatus.ERROR RuntimeCheckStatus.NOT_APPLICABLE RuntimeCheckStatus.UNKNOWN ``` **Rust** ```rust RuntimeCheckStatus::Ok RuntimeCheckStatus::Installable RuntimeCheckStatus::Error RuntimeCheckStatus::NotApplicable RuntimeCheckStatus::Unknown ``` | Variant | Description | | --- | --- | | `Ok` | Present and usable. | | `Installable` | Missing; `setup` can provision it. | | `Error` | Missing or broken; needs user action. | | `NotApplicable` | Not used on this host. | | `Unknown` | Not reported. | --- # Runtime support Which images run on which local runtime or in the cloud, what customization each accepts, and how ports reach them. This reference describes the Python Sandbox SDK in `cua-sandbox` **0.9.0**, exported by `cua` **0.4.1**. An accepted image specification or generated Fleet template is evidence of SDK behavior, not proof that a guest boots on every host or deployment. ## Operating systems and image kinds The same image reference runs locally (`local=True`, or `cua sb create`) and in the cloud. The resolver picks the variant each backend runs; the guest OS can still differ in format, backend and available operations between the two. | Image input | Guest and kind | Local backend | Cloud | | --- | --- | --- | --- | | `Image.linux()` | `ghcr.io/trycua/linux:24.04` rootfs (amd64, arm64) | Docker or Podman; gVisor when the engine has `runsc`, else `runc` | gVisor; warm by default | | `Image.linux(kind='vm')` | `ghcr.io/trycua/linux:24.04-disk` containerDisk | QEMU (`cua runtime setup qemu` installs it); raises when QEMU is missing | KubeVirt | | `Image.windows()` | `ghcr.io/trycua/windows:2022-disk` containerDisk | Hyper-V on a Windows host that has it, else QEMU | KubeVirt, EFI; warm by default | | `Image.windows('11')` | Windows 11 VM | Local ISO installation | Rejected | | `Image.macos()` / `Image.macos('15')` | `ghcr.io/trycua/macos:26` / `:15` Lume VM | Lume on Apple silicon | Rejected | | `Image.android()` | Android 14 | Legacy Android emulator adapter | Rejected | | `Image.from_registry(ref)` | Any OCI image: rootfs, containerDisk or Lume | Rootfs as a container, containerDisk on QEMU, Lume image on Lume | Rootfs on gVisor, containerDisk on KubeVirt; amd64 only | | `Image.from_file(...)` | Disk or ISO | QEMU (or Hyper-V) | Rejected | Not supported locally: macOS containers and macOS images that are not Lume images. The cloud runs amd64 only, so a Linux image must have an amd64 variant. With no container engine the managed runtime VM is not available yet; install Colima or Docker Engine. See [Manage local runtimes](). The canonical images are generated from the SDK source: | Constructor | Canonical image | Variant a backend runs | | --- | --- | --- | | `Image.linux()` | `ghcr.io/trycua/linux:24.04` | Rootfs for containers; the `-disk` containerDisk for `kind="vm"` | | `Image.windows()` | `ghcr.io/trycua/windows:2022` | The `-disk` containerDisk (VM) | | `Image.macos()` | `ghcr.io/trycua/macos:26` | Lume, local only (`Image.macos("15")`: `ghcr.io/trycua/macos:15`) | The resolver pins them by digest on both sides. Override one with `CUA_IMAGE_LINUX`, `CUA_IMAGE_WINDOWS` or `CUA_IMAGE_MACOS`. ### Windows firmware Cloud templates for `image.os_type == 'windows'` use `Firmware.EFI`. `Image.from_registry()` defaults to `os_type='linux'` and does not inspect the disk, so pass `os_type='windows', kind='vm'` for a Windows containerDisk. ## Image customization | Operation | Local | Cloud | | --- | --- | --- | | Layers: `apt_install()`, `pip_install()`, `uv_install()`, `run()`, `env()`, `copy()` | Container image: built into the local engine before boot, cached by content. VM image: applied after boot through cua-spacesd (else `Unsupported`) | Remote build on the image as base, cached by content (not available yet) | | Other layers: `brew_install()`, `choco_install()`, `winget_install()`, `app_install()`, ... | Applied after boot through cua-spacesd (for example `Image.linux()`); `Unsupported` on an image without it | `Unsupported` (a `NotImplementedError`) | | `expose()` | Forwarded to a free host port, reported in `sb.exposed_ports` | Declares the service `port-N` | | `Image.from_registry(ref, secret=...)` | Authenticated pull | Registry pull secret for the sandbox and its sidecars | | `Sandbox.snapshot()` | Not implemented | Not implemented; snapshot-derived images are rejected | Cloud layers fail with a clear error until Fleet builds images. `cua image build` and `cua image push` build and publish an image both sides can use. See [Build and publish an image](). ## Workloads, ports and sidecars | Option | Local | Cloud | | --- | --- | --- | | `command=` | Container entrypoint, or run by cloud-init in a Linux VM | Template command with `processMode: Run` (container and VM images) | | `env=` | Container environment or cloud-init | Template environment with `processMode: Run` (VM images: single-line values) | | `services=`, `wait_for=` | Published loopback ports; probes | Gateway services; probes | | `sidecars=` | Extra containers sharing the network namespace, reached by name or `localhost`; needs `runtime="runc"` where gVisor would run | Container images: extra containers in the gVisor pod. VM images: a companion gVisor pod named in the guest's `/etc/hosts` | | VM sandboxes with `sidecars=` | Refused | Supported (companion pod) | Sidecars are reached by name on every runtime: the sandbox reaches `db:6379`, and a sidecar reaches the sandbox at `main`. With sidecars the service names `main`, `sidecars` and `sc` are reserved. Readiness is daemon-agnostic: a sandbox is ready when its backend reports it running, plus the probes you declare (`wait_for=` in Python, `--wait` in the CLI), and fails fast when the sandbox exits. The computer interfaces use cua-spacesd on 3211 when the image has it; the SDK reports the `env` service only for images that do. ### Cloud guest services | Service | Template behavior | Guest requirement | | --- | --- | --- | | `env` | Published on port 3211 when the image has cua-spacesd | cua-spacesd, for the computer interfaces and `sb.spacesd()` | | Names in `services=` | One gateway service per name | Your process listening on that port | | `port-N` | Added by `.expose(N)` | The application listening on `N` | | `server` | Added by the older `server_port=` | Your own daemon on that port | Declaring a service does not install or start it. For an example layout, see the [Omarchy recipe](). ### Transport availability | Sandbox | Computer interfaces | `sb.tunnel.forward()` | `sb.service()`, `public_url()`, `mcp()` | | --- | --- | --- | --- | | Local (container, QEMU, Lume) | spacesd, else QMP or VNC fallback | Loopback TCP listener | Yes; public URLs through the cua daemon | | Cloud | spacesd through the gateway (gRPC-Web) | Loopback listener (spacesd tunnel, else gateway proxy for a declared service) | Yes; public URLs are signed service URLs | | Direct `Sandbox.connect(url=...)` | spacesd | spacesd tunnel | No declared services | | Legacy adapters (Android ADB, OSWorld, Tart, Hyper-V) | Adapter-specific | ADB forwards ports and Android sockets | Adapter-specific | Cloud service URLs require authentication unless you create a public URL. See [Set up cloud credentials]() and [Sandbox interfaces](). ## Evidence and limits The package mappings below are generated from source manifests and image constants. They describe the clients built from this repository, not the versions inside a particular deployed image. - `cua-sandbox` 0.9.0 source: image constructors, runtime selection, pool claims, Fleet validation, firmware, and services. - `cua-image` source (`canonical.rs`): the canonical image references. - `cua` 0.4.1 source: public Python exports. - `cua-fleet` 0.1.17 package mapping: Python bound-claim service requests. - `@trycua/fleet` 0.1.2 package mapping: TypeScript service requests. - `cua-sandbox[driver]` 0.9.0 maps direct Driver use to `cua-driver` 0.28.2. This client mapping is not the Driver version inside a particular published or deployment-qualified guest image. Verification covers source and offline contract checks, not provisioned cloud boots, authenticated service requests, or local VM and container boots on every host. Recipe observations, such as the [Windows Minecraft workflow](), describe their own tested environment. --- # Fleet Cloud pools and templates: apply, check, export, scale and delete (advanced). `cua.fleet()` manages cloud capacity directly: named pools, [claims]() and [images](). Most programs only need `Sandboxes.create` with `on: "cloud"`, which manages pools for you. Python programs usually use the high-level API instead: [`cua_sandbox.Pool`](). ## `Fleet` Fleet control plane. Returned by [`Cua.fleet`](). | Method | Description | | --- | --- | | [`apply`](#fleetapply) | Reconciles pool `name` (= namespace = template) to run `spec` with `options`: the one pool writer (rolls a new pool back if the template fails). | | [`apply_pool`](#fleetapply_pool) | Deprecated: use `Fleet.apply`. | | [`apply_pool_template`](#fleetapply_pool_template) | Lays the set fields of `spec` over pool `pool`'s template and writes it (the pool's capacity is kept; a no-op when nothing differs). | | [`billing_status`](#fleetbilling_status) | The account's Cua Cloud billing: its credit, card and the website billing page. | | [`check_pool_spec`](#fleetcheck_pool_spec) | Compares the set fields of `spec` with pool `pool`'s template: `PoolSpecMismatch` (with a readable diff) when they differ. | | [`delete_pool`](#fleetdelete_pool) | Deletes a pool, its namespace and its same-named template. | | [`export_pool`](#fleetexport_pool) | Reads pool `name` back as the shared model, with its `fleets_pool` Terraform block. | | [`get_pool`](#fleetget_pool) | Looks up a pool. | | [`list_pools`](#fleetlist_pools) | Lists pools in a namespace. | | [`list_templates`](#fleetlist_templates) | Lists templates in a namespace as JSON resources. | | [`pool_image_info`](#fleetpool_image_info) | The image pool `name`'s template runs, as a claim on it reports it (`Sandbox.image_info`): pinned by the resolver (cached per pool), or the template reference with empty `pinned_ref`/`digest` when the registry cannot be read. | | [`set_pool_replicas`](#fleetset_pool_replicas) | Sets warm replicas (0 suspends). | | [`usage_pricing`](#fleetusage_pricing) | This account's Cua Cloud rates (`GET /api/config`), reused for five minutes. | | [`wait_pool_ready`](#fleetwait_pool_ready) | Waits for at least one ready replica. | | [`acquire`]() | Claims a sandbox from a pool and waits for it to bind. | | [`acquire_with`]() | `Fleet.acquire` with claim options, including a per-claim env token (bounded wait for its delivery; `ClaimSecretsNotDelivered` releases the claim). | | [`attach_claim`]() | Waits for a named claim to bind. | | [`claim`]() | Creates a claim without waiting. | | [`create_image`]() | Creates an image resource (remote build) from a JSON manifest. | | [`create_signed_service_url`]() | Mints a signed, shareable service URL. | | [`delete_image`]() | Deletes an image resource. | | [`get_image`]() | Gets an image resource (JSON). | | [`keep_alive`]() | Extends a claim's lease; returns the RFC 3339 shutdown time. | | [`list_claims`]() | Lists claims in a namespace. | | [`list_images`]() | Lists image resources (JSON) in a namespace. | | [`release`]() | Releases a claim (missing claims are fine). | | [`service_url`]() | The gateway URL of a sandbox service (needs the Fleet bearer). | | Accessor | Returns | Description | | --- | --- | --- | | `base_url()` | `String` | Fleet API base URL. | | `ephemeral_pool_name()` | `String` | A random ephemeral pool name (`cua-eph-`). | | `pools()` | [`FleetPools`](#fleetpools) | Managed pools (list, gc). | ### `Fleet.apply` Reconciles pool `name` (= namespace = template) to run `spec` with `options`: the one pool writer (rolls a new pool back if the template fails). The image is pinned to the variant the runtime runs; `spec.registry_secret` is written as the pool's pull Secret. **Python** ```python async def apply(self, name: str, spec: SandboxSpec, options: PoolOptions) -> FleetPool ``` **TypeScript** ```ts apply(name: string, spec: SandboxSpec, options: PoolOptions, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func apply(name: String, spec: SandboxSpec, options: PoolOptions) async throws -> FleetPool ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun apply(name: String, spec: SandboxSpec, options: PoolOptions): FleetPool ``` **Rust** ```rust pub async fn apply(&self, name: String, spec: SandboxSpec, options: PoolOptions) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `spec` | [`SandboxSpec`](#sandboxspec-record) | required | | `options` | [`PoolOptions`](#pooloptions-record) | required | **Returns** [`FleetPool`](#fleetpool-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.apply_pool` Deprecated: use `Fleet.apply`. Reconciles a pool and its template from the flat spec (converted to `SandboxSpec` + `PoolOptions`). **Python** ```python async def apply_pool(self, spec: FleetPoolSpec) -> FleetPool ``` **TypeScript** ```ts applyPool(spec: FleetPoolSpec, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func applyPool(spec: FleetPoolSpec) async throws -> FleetPool ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun applyPool(spec: FleetPoolSpec): FleetPool ``` **Rust** ```rust pub async fn apply_pool(&self, spec: FleetPoolSpec) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `spec` | [`FleetPoolSpec`](#fleetpoolspec-record) | required | **Returns** [`FleetPool`](#fleetpool-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.apply_pool_template` Lays the set fields of `spec` over pool `pool`'s template and writes it (the pool's capacity is kept; a no-op when nothing differs). **Python** ```python async def apply_pool_template(self, pool: str, spec: SandboxSpec) -> None ``` **TypeScript** ```ts applyPoolTemplate(pool: string, spec: SandboxSpec, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func applyPoolTemplate(pool: String, spec: SandboxSpec) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun applyPoolTemplate(pool: String, spec: SandboxSpec) ``` **Rust** ```rust pub async fn apply_pool_template(&self, pool: String, spec: SandboxSpec) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `pool` | `String` | required | | `spec` | [`SandboxSpec`](#sandboxspec-record) | required | **Async** · **Raises** [`CuaError`]() ### `Fleet.billing_status` The account's Cua Cloud billing: its credit, card and the website billing page. A Fleet without billing answers `billing_enabled: false`. **Python** ```python async def billing_status(self) -> FleetBillingStatus ``` **TypeScript** ```ts billingStatus(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func billingStatus() async throws -> FleetBillingStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun billingStatus(): FleetBillingStatus ``` **Rust** ```rust pub async fn billing_status(&self) -> Result ``` **Returns** [`FleetBillingStatus`]() · **Async** · **Raises** [`CuaError`]() ### `Fleet.check_pool_spec` Compares the set fields of `spec` with pool `pool`'s template: `PoolSpecMismatch` (with a readable diff) when they differ. **Python** ```python async def check_pool_spec(self, pool: str, spec: SandboxSpec) -> None ``` **TypeScript** ```ts checkPoolSpec(pool: string, spec: SandboxSpec, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func checkPoolSpec(pool: String, spec: SandboxSpec) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun checkPoolSpec(pool: String, spec: SandboxSpec) ``` **Rust** ```rust pub async fn check_pool_spec(&self, pool: String, spec: SandboxSpec) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `pool` | `String` | required | | `spec` | [`SandboxSpec`](#sandboxspec-record) | required | **Async** · **Raises** [`CuaError`]() ([`PoolSpecMismatch`]()) ### `Fleet.delete_pool` Deletes a pool, its namespace and its same-named template. **Python** ```python async def delete_pool(self, name: str) -> None ``` **TypeScript** ```ts deletePool(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func deletePool(name: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun deletePool(name: String) ``` **Rust** ```rust pub async fn delete_pool(&self, name: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Fleet.export_pool` Reads pool `name` back as the shared model, with its `fleets_pool` Terraform block. **Python** ```python async def export_pool(self, name: str) -> FleetPoolExport ``` **TypeScript** ```ts exportPool(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func exportPool(name: String) async throws -> FleetPoolExport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun exportPool(name: String): FleetPoolExport ``` **Rust** ```rust pub async fn export_pool(&self, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`FleetPoolExport`](#fleetpoolexport-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.get_pool` Looks up a pool. **Python** ```python async def get_pool(self, name: str) -> FleetPool ``` **TypeScript** ```ts getPool(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func getPool(name: String) async throws -> FleetPool ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun getPool(name: String): FleetPool ``` **Rust** ```rust pub async fn get_pool(&self, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`FleetPool`](#fleetpool-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.list_pools` Lists pools in a namespace. **Python** ```python async def list_pools(self, namespace: str) -> List[FleetPool] ``` **TypeScript** ```ts listPools(namespace: string, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func listPools(namespace: String) async throws -> [FleetPool] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listPools(namespace: String): List ``` **Rust** ```rust pub async fn list_pools(&self, namespace: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | **Returns** [`Vec`](#fleetpool-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.list_templates` Lists templates in a namespace as JSON resources. **Python** ```python async def list_templates(self, namespace: str) -> List[str] ``` **TypeScript** ```ts listTemplates(namespace: string, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func listTemplates(namespace: String) async throws -> [String] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listTemplates(namespace: String): List ``` **Rust** ```rust pub async fn list_templates(&self, namespace: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | **Returns** `Vec` · **Async** · **Raises** [`CuaError`]() ### `Fleet.pool_image_info` The image pool `name`'s template runs, as a claim on it reports it (`Sandbox.image_info`): pinned by the resolver (cached per pool), or the template reference with empty `pinned_ref`/`digest` when the registry cannot be read. `None` when the template names no image. **Python** ```python async def pool_image_info(self, name: str) -> Optional[ImageInfo] ``` **TypeScript** ```ts poolImageInfo(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func poolImageInfo(name: String) async throws -> ImageInfo? ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun poolImageInfo(name: String): ImageInfo? ``` **Rust** ```rust pub async fn pool_image_info(&self, name: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`Option`]() · **Async** · **Raises** [`CuaError`]() ### `Fleet.set_pool_replicas` Sets warm replicas (0 suspends). **Python** ```python async def set_pool_replicas(self, name: str, replicas: int) -> FleetPool ``` **TypeScript** ```ts setPoolReplicas(name: string, replicas: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func setPoolReplicas(name: String, replicas: UInt32) async throws -> FleetPool ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun setPoolReplicas(name: String, replicas: UInt): FleetPool ``` **Rust** ```rust pub async fn set_pool_replicas(&self, name: String, replicas: u32) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `replicas` | `u32` | required | **Returns** [`FleetPool`](#fleetpool-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.usage_pricing` This account's Cua Cloud rates (`GET /api/config`), reused for five minutes. `None` when Fleet answers without rates: show no price rather than a guess. **Python** ```python async def usage_pricing(self) -> Optional[FleetUsagePricing] ``` **TypeScript** ```ts usagePricing(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func usagePricing() async throws -> FleetUsagePricing? ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun usagePricing(): FleetUsagePricing? ``` **Rust** ```rust pub async fn usage_pricing(&self) -> Result, CuaError> ``` **Returns** [`Option`]() · **Async** · **Raises** [`CuaError`]() ### `Fleet.wait_pool_ready` Waits for at least one ready replica. **Python** ```python async def wait_pool_ready(self, name: str, timeout_ms: int) -> FleetPool ``` **TypeScript** ```ts waitPoolReady(name: string, timeoutMs: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func waitPoolReady(name: String, timeoutMs: UInt32) async throws -> FleetPool ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun waitPoolReady(name: String, timeoutMs: UInt): FleetPool ``` **Rust** ```rust pub async fn wait_pool_ready(&self, name: String, timeout_ms: u32) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `timeout_ms` | `u32` | required | **Returns** [`FleetPool`](#fleetpool-record) · **Async** · **Raises** [`CuaError`]() ## `FleetPools` Managed pools: what `Sandboxes.create` without a pool uses. Returned by [`Fleet.pools`](#fleet). | Method | Description | | --- | --- | | [`gc`](#fleetpoolsgc) | Deletes managed pools idle for `idle_seconds` (default 1800) and stuck Pending/Failed managed claims past their TTL. | | [`gc_pools`](#fleetpoolsgc_pools) | `FleetPools.gc` restricted to the named managed pools: each is deleted (with its namespace) once it has no claims and has been idle for `idle_seconds` (default 0, i.e. now). | | [`list`](#fleetpoolslist) | This account's managed pools. | ### `FleetPools.gc` Deletes managed pools idle for `idle_seconds` (default 1800) and stuck Pending/Failed managed claims past their TTL. **Python** ```python async def gc(self, idle_seconds: Optional[int]) -> FleetGcReport ``` **TypeScript** ```ts gc(idleSeconds: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func gc(idleSeconds: UInt32?) async throws -> FleetGcReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun gc(idleSeconds: UInt?): FleetGcReport ``` **Rust** ```rust pub async fn gc(&self, idle_seconds: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `idle_seconds` | `Option` | required | **Returns** [`FleetGcReport`](#fleetgcreport-record) · **Async** · **Raises** [`CuaError`]() ### `FleetPools.gc_pools` `FleetPools.gc` restricted to the named managed pools: each is deleted (with its namespace) once it has no claims and has been idle for `idle_seconds` (default 0, i.e. now). Pools with live claims are kept, so a pool another process is using survives. Tests use this to remove the pools they created. **Python** ```python async def gc_pools(self, names: List[str], idle_seconds: Optional[int]) -> FleetGcReport ``` **TypeScript** ```ts gcPools(names: Array, idleSeconds: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func gcPools(names: [String], idleSeconds: UInt32?) async throws -> FleetGcReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun gcPools(names: List, idleSeconds: UInt?): FleetGcReport ``` **Rust** ```rust pub async fn gc_pools(&self, names: Vec, idle_seconds: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `names` | `Vec` | required | | `idle_seconds` | `Option` | required | **Returns** [`FleetGcReport`](#fleetgcreport-record) · **Async** · **Raises** [`CuaError`]() ### `FleetPools.list` This account's managed pools. **Python** ```python async def list(self) -> List[FleetManagedPool] ``` **TypeScript** ```ts list(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func list() async throws -> [FleetManagedPool] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun list(): List ``` **Rust** ```rust pub async fn list(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#fleetmanagedpool-record) · **Async** · **Raises** [`CuaError`]() ## `SandboxSpec` record What a sandbox runs: the one model behind `Sandbox.create`, managed pools and `Fleet.apply`. Unset fields keep Fleet's defaults (and are not compared by `Fleet.check_pool_spec`). | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `String` | `""` | Image (a container image runs on gVisor, a containerDisk on KubeVirt). | | `command` | `Option>` | `None` | argv replacing the image ENTRYPOINT. | | `args` | `Option>` | `None` | Arguments replacing the image CMD. | | `env` | `HashMap` | `[:]` | Plain environment variables (not secrets). | | `services` | `HashMap` | `[:]` | Named services (name → guest port). | | `readiness` | [`Option`]() | `None` | Readiness probe (`port`, or a declared `service`; `http_path` for HTTP). A replica binds a claim only once it passes. | | `cpu` | `Option` | `None` | vCPUs. | | `memory_mb` / `memoryMb` | `Option` | `None` | Memory (MiB). | | `efi` | `bool` | `false` | UEFI firmware (Windows images). | | `sidecars` | [`Vec`]() | `[]` | Extra containers sharing the sandbox's network namespace. | | `registry_secret` / `registrySecret` | [`Option`]() | `None` | Credentials for a private image: stored as the pool's `cua-registry-*` pull Secret. | | `registry_secret_name` / `registrySecretName` | `Option` | `None` | An existing `cua-registry-*` pull Secret in the pool namespace (instead of `registry_secret`). | | `process_mode` / `processMode` | `Option` | `None` | `Legacy` or `Run` (`vmTemplate.processMode`). | | `claim_secrets` / `claimSecrets` | `bool` | `false` | Per-claim secrets: claims may carry a token delivered at `/run/cua/env-token`. | ## `PoolOptions` record How a pool keeps capacity for a `SandboxSpec`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `runtime` | `Option` | `None` | `kubevirt` or `gvisor`. Unset: from the image. | | `replicas` | `Option` | `None` | Replicas of a pool without autoscaling (default 1). | | `warm` | `Option` | `None` | Keep one sandbox warm (`minPoolSize: 1`). | | `min_pool_size` / `minPoolSize` | `Option` | `None` | Autoscaling floor. | | `max_pool_size` / `maxPoolSize` | `Option` | `None` | Autoscaling ceiling. | | `idle_ttl_seconds` / `idleTtlSeconds` | `Option` | `None` | Delete the pool after this many seconds without claims. | | `ttl_policy` / `ttlPolicy` | `Option` | `None` | `Retain` or `Cascade`: what TTL expiry deletes. | | `pool_ttl_seconds` / `poolTtlSeconds` | `Option` | `None` | Pool creation-age TTL (seconds). | | `claim_ttl_seconds` / `claimTtlSeconds` | `Option` | `None` | Default TTL (seconds) of claims made on the pool through this SDK. | ## `FleetPoolExport` record A pool read back as the shared model, plus its Terraform block. Returned by [`Fleet.export_pool`](#fleetexport_pool). | Field | Type | Default | Description | | --- | --- | --- | --- | | `spec` | [`SandboxSpec`](#sandboxspec-record) | | What the pool's sandboxes run. | | `options` | [`PoolOptions`](#pooloptions-record) | | The pool's capacity settings. | | `runtime` | `String` | | The runtime. | | `terraform` | `String` | | A `fleets_pool` resource block (attributes the provider lacks yet are commented). | ## `FleetPool` record A pool. Returned by [`Fleet.apply`](#fleetapply), [`Fleet.apply_pool`](#fleetapply_pool), [`Fleet.get_pool`](#fleetget_pool), [`Fleet.list_pools`](#fleetlist_pools), [`Fleet.set_pool_replicas`](#fleetset_pool_replicas), [`Fleet.wait_pool_ready`](#fleetwait_pool_ready). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Name. | | `namespace` | `String` | | Namespace. | | `replicas` | `u32` | | Desired replicas. | | `ready_replicas` / `readyReplicas` | `Option` | | Ready replicas, when reported. | | `json` | `String` | | The resource as JSON. | ## `FleetPoolSpec` record A warm pool spec (`Pool.apply` semantics: pool, namespace and template share `name`). Deprecated: use `SandboxSpec` + `PoolOptions` with `Fleet.apply`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Pool name (globally unique on Fleet). | | `image` | `String` | | containerDisk (kubevirt) or pod image (gvisor). | | `runtime` | `Option` | `None` | `kubevirt` (default) or `gvisor`. | | `replicas` | `Option` | `None` | Warm replicas (default 1). | | `cpu` | `Option` | `None` | vCPUs. | | `memory_mb` / `memoryMb` | `Option` | `None` | Memory (MiB). | | `services` | `HashMap` | `[:]` | Services (name → port). Default `{"env": 3211}`. | | `readiness_tcp_port` / `readinessTcpPort` | `Option` | `None` | Optional TCP readiness probe port. | | `efi` | `bool` | `false` | UEFI firmware (Windows images). | | `command` | `Option>` | `None` | Pod runtimes: entrypoint override. | | `ttl_seconds_after_created` / `ttlSecondsAfterCreated` | `Option` | `None` | Pool TTL after creation (seconds). | ## `FleetManagedPool` record A managed pool (`cua-auto-*`, or a legacy `cua-eph-*`). Returned by [`FleetPools.list`](#fleetpoolslist). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Pool (= namespace = template) name. | | `managed` | `bool` | | Created by the SDK (labeled), not only matching the prefix. | | `spec_hash` / `specHash` | `Option` | | Spec hash label (first 32 hex digits). | | `image` | `Option` | | Template image. | | `replicas` | `u32` | | Current replicas (KEDA-owned). | | `ready_replicas` / `readyReplicas` | `Option` | | Ready replicas. | | `max_pool_size` / `maxPoolSize` | `Option` | | Autoscaling ceiling. | | `claims` | `u32` | | Claims in the pool. | | `bound_claims` / `boundClaims` | `u32` | | Bound claims. | | `last_used_unix` / `lastUsedUnix` | `Option` | | Last use (unix seconds). | | `created_unix` / `createdUnix` | `Option` | | Creation (unix seconds). | | `expires_unix` / `expiresUnix` | `Option` | | Backstop expiry (unix seconds). | | `terminating` | `bool` | | Being deleted. | ## `FleetGcReport` record What a pool GC did. Returned by [`FleetPools.gc`](#fleetpoolsgc), [`FleetPools.gc_pools`](#fleetpoolsgc_pools). | Field | Type | Default | Description | | --- | --- | --- | --- | | `deleted_pools` / `deletedPools` | `Vec` | | Deleted pools. | | `deleted_namespaces` / `deletedNamespaces` | `Vec` | | Deleted leftover namespaces. | | `deleted_claims` / `deletedClaims` | `Vec` | | Deleted stuck claims (`namespace/name`). | | `kept` | `Vec` | | Kept pools. | | `errors` | `Vec` | | Non-fatal errors. | ## `fleet_generate_claim_token` A fresh per-claim env token (64 hex characters). **Python** ```python def fleet_generate_claim_token() -> str ``` **TypeScript** ```ts function fleetGenerateClaimToken(): string ``` **Swift** ```swift func fleetGenerateClaimToken() -> String ``` **Kotlin** ```kotlin fun fleetGenerateClaimToken(): String ``` **Rust** ```rust pub fn fleet_generate_claim_token() -> String ``` **Returns** `String` --- # Claims Claim sandboxes from a cloud pool, keep them alive, release them, and sign service URLs. A claim binds one sandbox of a pool to you until you release it or its TTL passes. Python programs usually use the high-level API instead: [`Pool.claim`](). ## Claim methods Methods of [`Fleet`](). ### `Fleet.claim` Creates a claim without waiting. **Python** ```python async def claim(self, pool: str, name: Optional[str], ttl_seconds: Optional[int]) -> FleetClaim ``` **TypeScript** ```ts claim(pool: string, name: string | undefined, ttlSeconds: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func claim(pool: String, name: String?, ttlSeconds: UInt32?) async throws -> FleetClaim ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun claim(pool: String, name: String?, ttlSeconds: UInt?): FleetClaim ``` **Rust** ```rust pub async fn claim(&self, pool: String, name: Option, ttl_seconds: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `pool` | `String` | required | | `name` | `Option` | required | | `ttl_seconds` | `Option` | required | **Returns** [`FleetClaim`](#fleetclaim-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.acquire` Claims a sandbox from a pool and waits for it to bind. A claim named `name` that already exists is reattached. **Python** ```python async def acquire(self, pool: str, name: Optional[str], ttl_seconds: Optional[int]) -> FleetSandbox ``` **TypeScript** ```ts acquire(pool: string, name: string | undefined, ttlSeconds: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func acquire(pool: String, name: String?, ttlSeconds: UInt32?) async throws -> FleetSandbox ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun acquire(pool: String, name: String?, ttlSeconds: UInt?): FleetSandbox ``` **Rust** ```rust pub async fn acquire(&self, pool: String, name: Option, ttl_seconds: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `pool` | `String` | required | | `name` | `Option` | required | | `ttl_seconds` | `Option` | required | **Returns** [`FleetSandbox`](#fleetsandbox-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.acquire_with` `Fleet.acquire` with claim options, including a per-claim env token (bounded wait for its delivery; `ClaimSecretsNotDelivered` releases the claim). **Python** ```python async def acquire_with(self, pool: str, options: FleetClaimOptions) -> FleetSandbox ``` **TypeScript** ```ts acquireWith(pool: string, options: FleetClaimOptions, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func acquireWith(pool: String, options: FleetClaimOptions) async throws -> FleetSandbox ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun acquireWith(pool: String, options: FleetClaimOptions): FleetSandbox ``` **Rust** ```rust pub async fn acquire_with(&self, pool: String, options: FleetClaimOptions) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `pool` | `String` | required | | `options` | [`FleetClaimOptions`](#fleetclaimoptions-record) | required | **Returns** [`FleetSandbox`](#fleetsandbox-record) · **Async** · **Raises** [`CuaError`]() ([`ClaimSecretsNotDelivered`]()) ### `Fleet.attach_claim` Waits for a named claim to bind. **Python** ```python async def attach_claim(self, namespace: str, name: str) -> FleetSandbox ``` **TypeScript** ```ts attachClaim(namespace: string, name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func attachClaim(namespace: String, name: String) async throws -> FleetSandbox ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun attachClaim(namespace: String, name: String): FleetSandbox ``` **Rust** ```rust pub async fn attach_claim(&self, namespace: String, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | | `name` | `String` | required | **Returns** [`FleetSandbox`](#fleetsandbox-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.list_claims` Lists claims in a namespace. **Python** ```python async def list_claims(self, namespace: str) -> List[FleetClaim] ``` **TypeScript** ```ts listClaims(namespace: string, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func listClaims(namespace: String) async throws -> [FleetClaim] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listClaims(namespace: String): List ``` **Rust** ```rust pub async fn list_claims(&self, namespace: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | **Returns** [`Vec`](#fleetclaim-record) · **Async** · **Raises** [`CuaError`]() ### `Fleet.keep_alive` Extends a claim's lease; returns the RFC 3339 shutdown time. **Python** ```python async def keep_alive(self, namespace: str, name: str, seconds: int) -> str ``` **TypeScript** ```ts keepAlive(namespace: string, name: string, seconds: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func keepAlive(namespace: String, name: String, seconds: UInt32) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun keepAlive(namespace: String, name: String, seconds: UInt): String ``` **Rust** ```rust pub async fn keep_alive(&self, namespace: String, name: String, seconds: u32) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | | `name` | `String` | required | | `seconds` | `u32` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Fleet.release` Releases a claim (missing claims are fine). **Python** ```python async def release(self, namespace: str, name: str) -> None ``` **TypeScript** ```ts release(namespace: string, name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func release(namespace: String, name: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun release(namespace: String, name: String) ``` **Rust** ```rust pub async fn release(&self, namespace: String, name: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | | `name` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Fleet.service_url` The gateway URL of a sandbox service (needs the Fleet bearer). **Python** ```python def service_url(self, sandbox: FleetSandbox, service: str) -> str ``` **TypeScript** ```ts serviceUrl(sandbox: FleetSandbox, service: string): string ``` **Swift** ```swift func serviceUrl(sandbox: FleetSandbox, service: String) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun serviceUrl(sandbox: FleetSandbox, service: String): String ``` **Rust** ```rust pub fn service_url(&self, sandbox: FleetSandbox, service: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `sandbox` | [`FleetSandbox`](#fleetsandbox-record) | required | | `service` | `String` | required | **Returns** `String` · **Raises** [`CuaError`]() ### `Fleet.create_signed_service_url` Mints a signed, shareable service URL. **Python** ```python async def create_signed_service_url(self, sandbox: FleetSandbox, service: str, label: Optional[str], expires_in_seconds: int) -> FleetSignedUrl ``` **TypeScript** ```ts createSignedServiceUrl(sandbox: FleetSandbox, service: string, label: string | undefined, expiresInSeconds: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func createSignedServiceUrl(sandbox: FleetSandbox, service: String, label: String?, expiresInSeconds: UInt32) async throws -> FleetSignedUrl ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun createSignedServiceUrl(sandbox: FleetSandbox, service: String, label: String?, expiresInSeconds: UInt): FleetSignedUrl ``` **Rust** ```rust pub async fn create_signed_service_url(&self, sandbox: FleetSandbox, service: String, label: Option, expires_in_seconds: u32) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `sandbox` | [`FleetSandbox`](#fleetsandbox-record) | required | | `service` | `String` | required | | `label` | `Option` | required | | `expires_in_seconds` | `u32` | required | **Returns** [`FleetSignedUrl`](#fleetsignedurl-record) · **Async** · **Raises** [`CuaError`]() ## `FleetClaim` record A claim. Returned by [`Fleet.claim`](#fleetclaim), [`Fleet.list_claims`](#fleetlist_claims). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Claim name. | | `namespace` | `String` | | Namespace (the pool's). | | `json` | `String` | | The resource as JSON. | ## `FleetClaimOptions` record Claim options for `Fleet.acquire_with`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `Option` | `None` | Claim name; an existing claim with it is reattached. | | `ttl_seconds` / `ttlSeconds` | `Option` | `None` | Claim TTL (seconds). | | `claim_token` / `claimToken` | `Option` | `None` | Per-claim env token (see `fleet_generate_claim_token`); the pool's spec must set `claim_secrets`. Acquire waits (at most 90 s) until the sandbox has it, else releases the claim and raises `ClaimSecretsNotDelivered`. | ## `FleetSandbox` record A bound sandbox. Returned by [`Fleet.acquire`](#fleetacquire), [`Fleet.acquire_with`](#fleetacquire_with), [`Fleet.attach_claim`](#fleetattach_claim). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Sandbox name. | | `namespace` | `String` | | Namespace. | | `claim` | `String` | | Claim name. | | `services` | `Vec` | | Declared service names. | ## `FleetSignedUrl` record A signed, shareable service URL. Returned by [`Fleet.create_signed_service_url`](#fleetcreate_signed_service_url). | Field | Type | Default | Description | | --- | --- | --- | --- | | `url` | `String` | | The URL. | | `json` | `String` | | The resource as JSON (for `revoke_signed_service_url`). | --- # Cloud images and runtimes Fleet image resources and the runtime (gVisor or KubeVirt) each image runs on. Which cloud runtime an image needs, and the Fleet image resources of a namespace. ## Image methods Methods of [`Fleet`](). ### `Fleet.create_image` Creates an image resource (remote build) from a JSON manifest. **Python** ```python async def create_image(self, namespace: str, manifest_json: str) -> str ``` **TypeScript** ```ts createImage(namespace: string, manifestJson: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func createImage(namespace: String, manifestJson: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun createImage(namespace: String, manifestJson: String): String ``` **Rust** ```rust pub async fn create_image(&self, namespace: String, manifest_json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | | `manifest_json` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Fleet.get_image` Gets an image resource (JSON). **Python** ```python async def get_image(self, namespace: str, name: str) -> str ``` **TypeScript** ```ts getImage(namespace: string, name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func getImage(namespace: String, name: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun getImage(namespace: String, name: String): String ``` **Rust** ```rust pub async fn get_image(&self, namespace: String, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | | `name` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Fleet.list_images` Lists image resources (JSON) in a namespace. **Python** ```python async def list_images(self, namespace: str) -> List[str] ``` **TypeScript** ```ts listImages(namespace: string, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func listImages(namespace: String) async throws -> [String] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listImages(namespace: String): List ``` **Rust** ```rust pub async fn list_images(&self, namespace: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | **Returns** `Vec` · **Async** · **Raises** [`CuaError`]() ### `Fleet.delete_image` Deletes an image resource. **Python** ```python async def delete_image(self, namespace: str, name: str) -> None ``` **TypeScript** ```ts deleteImage(namespace: string, name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func deleteImage(namespace: String, name: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun deleteImage(namespace: String, name: String) ``` **Rust** ```rust pub async fn delete_image(&self, namespace: String, name: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `namespace` | `String` | required | | `name` | `String` | required | **Async** · **Raises** [`CuaError`]() ## `fleet_resolve_runtime` The Fleet runtime for `image`: `runtime` (`kubevirt`, `gvisor`) when given, else the one the image needs. What the image is comes from its registry manifest (read with docker, ghcr and ECR credentials): a KubeVirt containerDisk (a `/disk/disk.img` layer or trycua containerDisk media types) runs on `kubevirt`, a container rootfs on `gvisor`. When the manifest cannot be read and no runtime is given, the runtime is guessed from the reference with a warning (a `docker-` tag runs on `gvisor`, anything else on `kubevirt`). Raises `InvalidArgument` only for a runtime the image cannot run on, and `Unsupported` for a macOS image or runtime `macos` (Fleet does not offer macOS in this SDK). Blocks while the registry is read (at most 20 s; `CUA_FLEET_IMAGE_INSPECT=0` skips it). The one copy of this rule; cua-sandbox calls it. **Python** ```python def fleet_resolve_runtime(runtime: Optional[str], image: str) -> str ``` **TypeScript** ```ts function fleetResolveRuntime(runtime: string | undefined, image: string): string ``` **Swift** ```swift func fleetResolveRuntime(runtime: String?, image: String) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun fleetResolveRuntime(runtime: String?, image: String): String ``` **Rust** ```rust pub fn fleet_resolve_runtime(runtime: Option, image: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `runtime` | `Option` | required | | `image` | `String` | required | **Returns** `String` · **Raises** [`CuaError`]() ([`InvalidArgument`](), [`Unsupported`]()) ## `fleet_check_runtime` `fleet_resolve_runtime` with the image variant already known (`variant` as `fleet_image_variant` returns it); `None` means the manifest could not be read (an unset runtime then falls back to the reference). Pure (no registry read). **Python** ```python def fleet_check_runtime(runtime: Optional[str], image: str, variant: Optional[str]) -> str ``` **TypeScript** ```ts function fleetCheckRuntime(runtime: string | undefined, image: string, variant: string | undefined): string ``` **Swift** ```swift func fleetCheckRuntime(runtime: String?, image: String, variant: String?) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun fleetCheckRuntime(runtime: String?, image: String, variant: String?): String ``` **Rust** ```rust pub fn fleet_check_runtime(runtime: Option, image: String, variant: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `runtime` | `Option` | required | | `image` | `String` | required | | `variant` | `Option` | required | **Returns** `String` · **Raises** [`CuaError`]() ## `fleet_image_variant` The variant of an image from its registry documents (`manifest`: an image manifest or index, `config`: the platform manifest's config blob): `container-disk`, `rootfs` or `other`. Pure (no registry read). A macOS image raises `Unsupported`. **Python** ```python def fleet_image_variant(manifest: str, config: Optional[str]) -> str ``` **TypeScript** ```ts function fleetImageVariant(manifest: string, config: string | undefined): string ``` **Swift** ```swift func fleetImageVariant(manifest: String, config: String?) throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun fleetImageVariant(manifest: String, config: String?): String ``` **Rust** ```rust pub fn fleet_image_variant(manifest: String, config: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `manifest` | `String` | required | | `config` | `Option` | required | **Returns** `String` · **Raises** [`CuaError`]() ([`Unsupported`]()) ## `FleetBillingCard` record The saved card (`Fleet.billing_status`): never its number. | Field | Type | Default | Description | | --- | --- | --- | --- | | `brand` | `String` | | `visa`, `mastercard`, ... | | `last4` | `String` | | Last four digits. | | `exp_month` / `expMonth` | `u32` | | Expiry month. | | `exp_year` / `expYear` | `u32` | | Expiry year. | ## `FleetBillingCredit` record The account's Cua Cloud credit. | Field | Type | Default | Description | | --- | --- | --- | --- | | `balance_usd_cents` / `balanceUsdCents` | `i64` | | What is left, US cents. | | `signup_grant_usd_cents` / `signupGrantUsdCents` | `i64` | | The signup grant's amount, US cents (0: none). | | `signup_grant_unused` / `signupGrantUnused` | `bool` | | A signup grant exists and none of it has been used. | ## `FleetBillingStatus` record The account's Cua Cloud billing (`GET /api/billing/status`). Returned by [`Fleet.billing_status`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `billing_enabled` / `billingEnabled` | `bool` | | Billing is on for this account. | | `payment_method_present` / `paymentMethodPresent` | `bool` | | A default card is saved. | | `card` | [`Option`](#fleetbillingcard-record) | | That card. | | `plan` | `String` | | `none`, `payg`, or a plan key. | | `payg_available` / `paygAvailable` | `bool` | | A card can be added for pay as you go. | | `credit` | [`Option`](#fleetbillingcredit-record) | | The account's credit, when Fleet has credit for it. | | `billing_url` / `billingUrl` | `Option` | | The website billing page (credit, cards, plans). | ## `FleetSizeLimits` record The everyday sizes of a cloud sandbox, which the Cua apps offer (`cua_fleet::CLOUD_DEFAULT_RANGE_CPUS`, `cua_fleet::CLOUD_DEFAULT_RANGE_MEMORY_MB`). Returned by [`fleet_size_limits`](#fleet_size_limits). | Field | Type | Default | Description | | --- | --- | --- | --- | | `min_cpus` / `minCpus` | `u32` | | Fewest vCPUs. | | `max_cpus` / `maxCpus` | `u32` | | Most vCPUs. | | `min_memory_mb` / `minMemoryMb` | `u32` | | Least memory, MiB. | | `max_memory_mb` / `maxMemoryMb` | `u32` | | Most memory, MiB. | ## `FleetUsagePricing` record This account's Cua Cloud rates (`Fleet.usage_pricing`). Fleet bills the vCPUs and memory a sandbox reserves, per hour; disk is not metered. Returned by [`Fleet.usage_pricing`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `vcpu_hour_usd` / `vcpuHourUsd` | `f64` | | USD per reserved vCPU per hour. | | `memory_gib_hour_usd` / `memoryGibHourUsd` | `f64` | | USD per reserved GiB of memory per hour. | ## `fleet_size_limits` The everyday sizes of a cloud sandbox (1-8 vCPUs, 1-32 GiB), which the Cua apps offer. The SDK does not enforce them: it accepts up to 64 vCPUs and 512 MiB to 512 GiB, and Fleet decides what an account may run (a size over the account's limits fails with `FleetAdmissionDenied`). **Python** ```python def fleet_size_limits() -> FleetSizeLimits ``` **TypeScript** ```ts function fleetSizeLimits(): FleetSizeLimits ``` **Swift** ```swift func fleetSizeLimits() -> FleetSizeLimits ``` **Kotlin** ```kotlin fun fleetSizeLimits(): FleetSizeLimits ``` **Rust** ```rust pub fn fleet_size_limits() -> FleetSizeLimits ``` **Returns** [`FleetSizeLimits`](#fleetsizelimits-record) --- # Spacesd client SpacesdClient: the cua-spacesd client for processes, files, the desktop and media in a sandbox. `sandbox.spacesd()`, `space.spacesd()` (or `cua.spacesd(url, token)` for any spacesd) returns a `SpacesdClient`. It picks native gRPC or gRPC-Web, sends the token and chunks transfers. Methods by task: [processes](), [files](), [computer]() and [media](). Python programs usually use the high-level API instead: [`cua_sandbox` interfaces](). cua-spacesd is optional: images without it still boot and serve their declared services, and `sandbox.spacesd()` raises `SpacesdNotAvailable`. `capabilities()` reports what this spacesd supports (`has_feature(name)` checks one feature). `call_json(method, json)` reaches every `cua.env.v1` RPC of the [protocol](), including those without a typed method. ## From a Space Methods of [`Space`](). ### `Space.spacesd` The cua-spacesd client over the connection this Space authenticated. **Python** ```python def spacesd(self) -> SpacesdClient ``` **TypeScript** ```ts spacesd(): SpacesdClientLike ``` **Swift** ```swift func spacesd() throws -> SpacesdClient ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun spacesd(): SpacesdClient ``` **Rust** ```rust pub fn spacesd(&self) -> Result, CuaError> ``` **Returns** [`SpacesdClient`](#spacesdclient) · **Raises** [`CuaError`]() ## `SpacesdClient` Client for one cua-spacesd. Returned by [`Cua.spacesd`](), [`Relay.connect`](), [`Sandbox.spacesd`](), [`Space.spacesd`](#spacespacesd). | Method | Description | | --- | --- | | [`call_json`](#spacesdclientcall_json) | Calls any unary `cua.env.v1` RPC with a proto3-JSON request and returns the proto3-JSON response. | | [`call_json_stream`](#spacesdclientcall_json_stream) | Calls a server-streaming `cua.env.v1` RPC and collects at most `max_messages` responses (or until the stream ends or `timeout_ms` elapses). | | [`capabilities`](#spacesdclientcapabilities) | `GetCapabilities` (cached). | | [`diagnose`](#spacesdclientdiagnose) | Runs the image self-test (`SystemService.Diagnose`, the checks of `cua-spacesd doctor`) and returns the report as JSON (schema version 1, `libs/cua/proto/diagnose-report.schema.json`). | | [`has_feature`](#spacesdclienthas_feature) | Whether the guest supports `feature`. | | [`health`](#spacesdclienthealth) | `Health` as proto3 JSON. | | [`http`](#spacesdclienthttp) | Sends one plain-HTTP request to the spacesd port with this client's endpoint and credentials (through the Fleet gateway when the endpoint is a Fleet service). | | [`agents`]() | Coding agents in this guest. | | [`attach`]() | Attaches to a running process by pid or tag. | | [`click`]() | Left click. | | [`cursor_position`]() | Pointer position. | | [`displays`]() | Displays as proto3 JSON (`[Display]`). | | [`double_click`]() | Double click. | | [`download`]() | Downloads `path` into memory. | | [`download_file`]() | Downloads `path` to a local file. | | [`drag`]() | Drags with the left button. | | [`get_clipboard`]() | Clipboard text. | | [`hotkey`]() | Presses a chord (`["ctrl", "c"]`). | | [`keyboard_json`]() | Full `ComputerService.Keyboard` from proto3 JSON. | | [`list_dir`]() | `ListDir` (depth 1 = direct children). | | [`list_processes`]() | Guest processes as proto3 JSON (`[ProcessInfo]`). | | [`make_dir`]() | `MakeDir` (with parents). | | [`move_to`]() | Moves the pointer. | | [`open_media`]() | Opens a media session and starts delivering encoded video frames and control events to `frames`. | | [`open_media_with_audio`]() | `SpacesdClient.open_media` that also delivers audio packets to `audio` (set `options.audio` to negotiate audio tracks). | | [`pointer_json`]() | Full `ComputerService.Pointer` from proto3 JSON. | | [`press`]() | Presses one key (`enter`, `KEY_ESCAPE`, `a`, ...). | | [`remove`]() | `Remove`. | | [`right_click`]() | Right click. | | [`run`]() | Runs a command to completion. | | [`screenshot`]() | Captures a display. | | [`scroll`]() | Scrolls by line deltas. | | [`set_clipboard`]() | Sets clipboard text; returns the clipboard generation. | | [`sh`]() | Runs `line` with `/bin/sh -c` to completion. | | [`spawn`]() | Starts a command and returns its handle. | | [`stat`]() | `Stat`. | | [`type_text`]() | Types text. | | [`upload`]() | Uploads bytes to `path` (chunked, resumable, SHA-256 verified). | | [`upload_file`]() | Uploads a local file to `path`. | | Accessor | Returns | Description | | --- | --- | --- | | `endpoint()` | `String` | Endpoint URL. | | `json_methods()` | `Vec` | Methods reachable through `call_json` / `call_json_stream`. | | `transport()` | `String` | `grpc` or `grpc-web`. | ### `SpacesdClient.call_json` Calls any unary `cua.env.v1` RPC with a proto3-JSON request and returns the proto3-JSON response. `method` is the full path (`/cua.env.v1.WindowsService/ListWindows`) or `Service/Method`. Covers windows, accessibility, driver tools, teleport, tunnels and every other RPC without a typed method. **Python** ```python async def call_json(self, method: str, request_json: str) -> str ``` **TypeScript** ```ts callJson(method: string, requestJson: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func callJson(method: String, requestJson: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun callJson(method: String, requestJson: String): String ``` **Rust** ```rust pub async fn call_json(&self, method: String, request_json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `method` | `String` | required | | `request_json` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.call_json_stream` Calls a server-streaming `cua.env.v1` RPC and collects at most `max_messages` responses (or until the stream ends or `timeout_ms` elapses). **Python** ```python async def call_json_stream(self, method: str, request_json: str, max_messages: int, timeout_ms: int) -> List[str] ``` **TypeScript** ```ts callJsonStream(method: string, requestJson: string, maxMessages: number, timeoutMs: number, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func callJsonStream(method: String, requestJson: String, maxMessages: UInt32, timeoutMs: UInt32) async throws -> [String] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun callJsonStream(method: String, requestJson: String, maxMessages: UInt, timeoutMs: UInt): List ``` **Rust** ```rust pub async fn call_json_stream(&self, method: String, request_json: String, max_messages: u32, timeout_ms: u32) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `method` | `String` | required | | `request_json` | `String` | required | | `max_messages` | `u32` | required | | `timeout_ms` | `u32` | required | **Returns** `Vec` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.capabilities` `GetCapabilities` (cached). **Python** ```python async def capabilities(self) -> SpacesdCapabilities ``` **TypeScript** ```ts capabilities(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func capabilities() async throws -> SpacesdCapabilities ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun capabilities(): SpacesdCapabilities ``` **Rust** ```rust pub async fn capabilities(&self) -> Result ``` **Returns** [`SpacesdCapabilities`](#spacesdcapabilities-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.diagnose` Runs the image self-test (`SystemService.Diagnose`, the checks of `cua-spacesd doctor`) and returns the report as JSON (schema version 1, `libs/cua/proto/diagnose-report.schema.json`). `options_json` is a proto3-JSON `DiagnoseOptions` (`"{}"` for the defaults: every check, read-only). Falls back to `DiagnoseOnce` when the stream is cut. **Python** ```python async def diagnose(self, options_json: str) -> str ``` **TypeScript** ```ts diagnose(optionsJson: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func diagnose(optionsJson: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun diagnose(optionsJson: String): String ``` **Rust** ```rust pub async fn diagnose(&self, options_json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `options_json` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.has_feature` Whether the guest supports `feature`. **Python** ```python async def has_feature(self, feature: str) -> bool ``` **TypeScript** ```ts hasFeature(feature: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func hasFeature(feature: String) async throws -> Bool ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun hasFeature(feature: String): Boolean ``` **Rust** ```rust pub async fn has_feature(&self, feature: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `feature` | `String` | required | **Returns** `bool` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.health` `Health` as proto3 JSON. **Python** ```python async def health(self) -> str ``` **TypeScript** ```ts health(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func health() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun health(): String ``` **Rust** ```rust pub async fn health(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.http` Sends one plain-HTTP request to the spacesd port with this client's endpoint and credentials (through the Fleet gateway when the endpoint is a Fleet service). Used for streamable-HTTP MCP at `/mcp`, for example by typed cua-driver clients. Not retried. **Python** ```python async def http(self, request: SpacesdHttpRequest) -> SpacesdHttpResponse ``` **TypeScript** ```ts http(request: SpacesdHttpRequest, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func http(request: SpacesdHttpRequest) async throws -> SpacesdHttpResponse ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun http(request: SpacesdHttpRequest): SpacesdHttpResponse ``` **Rust** ```rust pub async fn http(&self, request: SpacesdHttpRequest) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `request` | [`SpacesdHttpRequest`](#spacesdhttprequest-record) | required | **Returns** [`SpacesdHttpResponse`](#spacesdhttpresponse-record) · **Async** · **Raises** [`CuaError`]() ## `SpacesdCapabilities` record `SystemService.GetCapabilities`, summarized. `json` has everything. Returned by [`SpacesdClient.capabilities`](#spacesdclientcapabilities). | Field | Type | Default | Description | | --- | --- | --- | --- | | `version` | `String` | | spacesd version. | | `protocol_version` / `protocolVersion` | `u32` | | `cua.env.v1` protocol version. | | `protocol_revision` / `protocolRevision` | `u32` | | Additive contract revision. | | `os_family` / `osFamily` | `String` | | OS family (`linux`, `macos`, `windows`). | | `os_name` / `osName` | `String` | | OS name. | | `os_version` / `osVersion` | `String` | | OS version. | | `arch` | `String` | | CPU architecture. | | `hostname` | `String` | | Guest hostname. | | `initialized` | `bool` | | `SystemService.Init` has run. | | `features` | [`Vec`](#spacesdfeature-record) | | Feature flags. | | `json` | `String` | | The full response as proto3 JSON. | ## `SpacesdFeature` record One capability flag. | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Feature name (for example `a11y`, `stream.h264`, `audio.desktop`). | | `supported` | `bool` | | Supported in this guest. | | `limitation` | `Option` | | Limitation when degraded or unsupported. | | `attributes` | `HashMap` | | Extra attributes. | ## `SpacesdHttpRequest` record A plain-HTTP request to the spacesd port (for example MCP at `/mcp`). | Field | Type | Default | Description | | --- | --- | --- | --- | | `method` | `String` | | Method, for example `POST`. | | `path` | `String` | | Absolute path under the endpoint, for example `/mcp`. Never a URL. | | `headers` | [`Vec`](#spacesdhttpheader-record) | `[]` | Extra headers. Credentials (`authorization`, the env and Fleet claim headers) are the client's and cannot be set here. | | `body` | `Vec` | | Request body (empty for none). | | `timeout_ms` / `timeoutMs` | `Option` | `None` | Whole-request timeout; 30 s when unset. | | `max_response_bytes` / `maxResponseBytes` | `Option` | `None` | Response body cap; 16 MiB when unset. | ## `SpacesdHttpResponse` record The spacesd's HTTP answer. Returned by [`SpacesdClient.http`](#spacesdclienthttp). | Field | Type | Default | Description | | --- | --- | --- | --- | | `status` | `u16` | | Status code. | | `headers` | [`Vec`](#spacesdhttpheader-record) | | Response headers, in order. | | `body` | `Vec` | | Response body. | ## `SpacesdHttpHeader` record One HTTP header (duplicates are kept in order). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Header name. | | `value` | `String` | | Header value. | --- # Processes Run, spawn and attach to processes in a sandbox through cua-spacesd. Run a command to completion with `run` or `sh`, or `spawn` a `SpacesdProcess` to stream its output and write its input. Python programs usually use the high-level API instead: [`sb.shell`, `sb.terminal`](). ## Run processes Methods of [`SpacesdClient`](). ### `SpacesdClient.run` Runs a command to completion. **Python** ```python async def run(self, command: SpacesdCommand) -> ProcessOutput ``` **TypeScript** ```ts run(command: SpacesdCommand, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func run(command: SpacesdCommand) async throws -> ProcessOutput ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun run(command: SpacesdCommand): ProcessOutput ``` **Rust** ```rust pub async fn run(&self, command: SpacesdCommand) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `command` | [`SpacesdCommand`](#spacesdcommand-record) | required | **Returns** [`ProcessOutput`](#processoutput-record) · **Async** · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="spacesd-client-run-py" session="spacesd-client-run-py" prelude="spacesd,spacesd-vars" import asyncio import cua async def main(): guest = await cua.embedded().spacesd(URL, TOKEN) # or: await sandbox.spacesd(None) out = await guest.run(cua.SpacesdCommand(program="echo", args=["hello"])) print([out.exit.success, out.stdout]) # [True, b'hello\n'] asyncio.run(main()) ``` **TypeScript** ```ts test="docs" id="spacesd-client-run-ts" prelude="spacesd,spacesd-vars" import { SpacesdCommand, embedded } from '@trycua/cua'; const guest = await embedded().spacesd(URL, TOKEN); // or: await sandbox.spacesd(undefined) const out = await guest.run(SpacesdCommand.create({ program: 'echo', args: ['hello'] })); console.log(out.exit.success, new TextDecoder().decode(out.stdout)); ``` **Swift** ```swift test="swift" id="spacesd-client-run-swift" import Cua let guest = try await Cua.embedded().spacesd(url: "http://10.0.0.5:3211", token: "TOKEN") // or: try await sandbox.spacesd(probeTimeoutMs: nil) let out = try await guest.run(command: SpacesdCommand("echo", ["hello"])) print(out.exit.success, String(decoding: out.stdout, as: UTF8.self)) ``` **Kotlin** ```kotlin test="kotlin" id="spacesd-client-run-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig import ai.cua.sdk.SpacesdCommand val guest = Cua.embedded(CuaConfig()).spacesd("http://10.0.0.5:3211", "TOKEN") // or: sandbox.spacesd(null) val out = guest.run(SpacesdCommand(program = "echo", args = listOf("hello"))) println("${out.exit.success} ${out.stdout.decodeToString()}") ``` ### `SpacesdClient.sh` Runs `line` with `/bin/sh -c` to completion. **Python** ```python async def sh(self, line: str, timeout_ms: Optional[int]) -> ProcessOutput ``` **TypeScript** ```ts sh(line: string, timeoutMs: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func sh(line: String, timeoutMs: UInt32?) async throws -> ProcessOutput ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun sh(line: String, timeoutMs: UInt?): ProcessOutput ``` **Rust** ```rust pub async fn sh(&self, line: String, timeout_ms: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `line` | `String` | required | | `timeout_ms` | `Option` | required | **Returns** [`ProcessOutput`](#processoutput-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.spawn` Starts a command and returns its handle. **Python** ```python async def spawn(self, command: SpacesdCommand) -> SpacesdProcess ``` **TypeScript** ```ts spawn(command: SpacesdCommand, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func spawn(command: SpacesdCommand) async throws -> SpacesdProcess ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun spawn(command: SpacesdCommand): SpacesdProcess ``` **Rust** ```rust pub async fn spawn(&self, command: SpacesdCommand) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `command` | [`SpacesdCommand`](#spacesdcommand-record) | required | **Returns** [`SpacesdProcess`](#spacesdprocess) · **Async** · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="spacesd-client-spawn-py" session="spacesd-client-spawn-py" prelude="spacesd,spacesd-vars" import asyncio import cua async def main(): guest = await cua.embedded().spacesd(URL, TOKEN) proc = await guest.spawn(cua.SpacesdCommand(program="cat", args=[], stdin=True)) await proc.write_stdin(b"xyz") await proc.close_stdin() print([(await proc.wait()).stdout]) # [b'xyz'] asyncio.run(main()) ``` **Swift** ```swift test="swift" id="spacesd-client-spawn-swift" import Cua import Foundation let guest = try await Cua.embedded().spacesd(url: "http://10.0.0.5:3211", token: "TOKEN") let proc = try await guest.spawn(command: SpacesdCommand("cat", stdin: true)) try await proc.writeStdin(data: Data("xyz".utf8)) try await proc.closeStdin() print(String(decoding: try await proc.wait().stdout, as: UTF8.self)) ``` **Kotlin** ```kotlin test="kotlin" id="spacesd-client-spawn-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig import ai.cua.sdk.SpacesdCommand val guest = Cua.embedded(CuaConfig()).spacesd("http://10.0.0.5:3211", "TOKEN") val proc = guest.spawn(SpacesdCommand(program = "cat", stdin = true)) proc.writeStdin("xyz".encodeToByteArray()) proc.closeStdin() println(proc.wait().stdout.decodeToString()) ``` ### `SpacesdClient.attach` Attaches to a running process by pid or tag. **Python** ```python async def attach(self, pid: Optional[int], tag: Optional[str], replay: ReplayMode) -> SpacesdProcess ``` **TypeScript** ```ts attach(pid: number | undefined, tag: string | undefined, replay: ReplayMode, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func attach(pid: UInt32?, tag: String?, replay: ReplayMode) async throws -> SpacesdProcess ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun attach(pid: UInt?, tag: String?, replay: ReplayMode): SpacesdProcess ``` **Rust** ```rust pub async fn attach(&self, pid: Option, tag: Option, replay: ReplayMode) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `pid` | `Option` | required | | `tag` | `Option` | required | | `replay` | [`ReplayMode`](#replaymode-enum) | required | **Returns** [`SpacesdProcess`](#spacesdprocess) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.list_processes` Guest processes as proto3 JSON (`[ProcessInfo]`). **Python** ```python async def list_processes(self, include_exited: bool) -> str ``` **TypeScript** ```ts listProcesses(includeExited: boolean, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func listProcesses(includeExited: Bool) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listProcesses(includeExited: Boolean): String ``` **Rust** ```rust pub async fn list_processes(&self, include_exited: bool) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `include_exited` | `bool` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ## `SpacesdProcess` A guest process, from `spawn` or `attach`. Returned by [`SpacesdClient.attach`](#spacesdclientattach), [`SpacesdClient.spawn`](#spacesdclientspawn). | Method | Description | | --- | --- | | [`close_stdin`](#spacesdprocessclose_stdin) | Closes stdin. | | [`detach`](#spacesdprocessdetach) | Stops receiving output without affecting the process. | | [`kill`](#spacesdprocesskill) | SIGKILL. | | [`next_event`](#spacesdprocessnext_event) | Next output chunk or the exit; `None` after the exit was returned. | | [`resize`](#spacesdprocessresize) | Resizes the PTY. | | [`signal`](#spacesdprocesssignal) | Sends a signal by name (`term`, `kill`, `SIGINT`, ...). | | [`wait`](#spacesdprocesswait) | Collects the remaining output until exit. | | [`write_pty`](#spacesdprocesswrite_pty) | Writes PTY keystrokes. | | [`write_stdin`](#spacesdprocesswrite_stdin) | Writes stdin (PTY input for PTY processes), exactly once across retries. | | Accessor | Returns | Description | | --- | --- | --- | | `pid()` | `u32` | Guest pid. | | `tag()` | `Option` | Tag, when set. | ### `SpacesdProcess.close_stdin` Closes stdin. **Python** ```python async def close_stdin(self) -> None ``` **TypeScript** ```ts closeStdin(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func closeStdin() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun closeStdin() ``` **Rust** ```rust pub async fn close_stdin(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `SpacesdProcess.detach` Stops receiving output without affecting the process. **Python** ```python async def detach(self) -> None ``` **TypeScript** ```ts detach(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func detach() async ``` **Kotlin** ```kotlin suspend fun detach() ``` **Rust** ```rust pub async fn detach(&self) ``` **Async** ### `SpacesdProcess.kill` SIGKILL. **Python** ```python async def kill(self) -> None ``` **TypeScript** ```ts kill(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func kill() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun kill() ``` **Rust** ```rust pub async fn kill(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `SpacesdProcess.next_event` Next output chunk or the exit; `None` after the exit was returned. Reconnects transparently from the last seen offset. **Python** ```python async def next_event(self) -> Optional[ProcessEvent] ``` **TypeScript** ```ts nextEvent(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func nextEvent() async throws -> ProcessEvent? ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun nextEvent(): ProcessEvent? ``` **Rust** ```rust pub async fn next_event(&self) -> Result, CuaError> ``` **Returns** [`Option`](#processevent-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdProcess.resize` Resizes the PTY. **Python** ```python async def resize(self, cols: int, rows: int) -> None ``` **TypeScript** ```ts resize(cols: number, rows: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func resize(cols: UInt32, rows: UInt32) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun resize(cols: UInt, rows: UInt) ``` **Rust** ```rust pub async fn resize(&self, cols: u32, rows: u32) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `cols` | `u32` | required | | `rows` | `u32` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdProcess.signal` Sends a signal by name (`term`, `kill`, `SIGINT`, ...). **Python** ```python async def signal(self, signal: str) -> None ``` **TypeScript** ```ts signal(signal: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func signal(signal: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun signal(signal: String) ``` **Rust** ```rust pub async fn signal(&self, signal: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `signal` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdProcess.wait` Collects the remaining output until exit. **Python** ```python async def wait(self) -> ProcessOutput ``` **TypeScript** ```ts wait(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func wait() async throws -> ProcessOutput ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun wait(): ProcessOutput ``` **Rust** ```rust pub async fn wait(&self) -> Result ``` **Returns** [`ProcessOutput`](#processoutput-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdProcess.write_pty` Writes PTY keystrokes. **Python** ```python async def write_pty(self, data: bytes) -> None ``` **TypeScript** ```ts writePty(data: ArrayBuffer, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func writePty(data: Data) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun writePty(data: ByteArray) ``` **Rust** ```rust pub async fn write_pty(&self, data: Vec) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `data` | `Vec` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdProcess.write_stdin` Writes stdin (PTY input for PTY processes), exactly once across retries. **Python** ```python async def write_stdin(self, data: bytes) -> None ``` **TypeScript** ```ts writeStdin(data: ArrayBuffer, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func writeStdin(data: Data) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun writeStdin(data: ByteArray) ``` **Rust** ```rust pub async fn write_stdin(&self, data: Vec) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `data` | `Vec` | required | **Async** · **Raises** [`CuaError`]() ## `SpacesdCommand` record A command to run in the guest. | Field | Type | Default | Description | | --- | --- | --- | --- | | `program` | `String` | | Program (absolute path or `PATH` lookup). | | `args` | `Vec` | `[]` | Arguments. | | `env` | `HashMap` | `[:]` | Extra environment. | | `cwd` | `Option` | `None` | Working directory. | | `user` | `Option` | `None` | Run as this user. | | `timeout_ms` / `timeoutMs` | `Option` | `None` | Kill after this long. | | `tag` | `Option` | `None` | Tag for reattaching (`attach(tag=…)`). | | `stdin` | `bool` | `false` | Keep stdin open for `write_stdin`. | | `pty` | [`Option`](#ptysize-record) | `None` | Run in a PTY of this size. | ## `ProcessOutput` record Collected output of a finished process. Returned by [`Sandbox.guest_sh`](), [`SpacesdClient.run`](#spacesdclientrun), [`SpacesdClient.sh`](#spacesdclientsh), [`SpacesdProcess.wait`](#spacesdprocesswait). | Field | Type | Default | Description | | --- | --- | --- | --- | | `exit` | [`ExitInfo`](#exitinfo-record) | | Exit. | | `stdout` | `Vec` | | stdout bytes. | | `stderr` | `Vec` | | stderr bytes. | | `pty` | `Vec` | | PTY bytes. | ## `ExitInfo` record How a process ended. | Field | Type | Default | Description | | --- | --- | --- | --- | | `code` | `Option` | | Exit code for a normal exit. | | `signal` | `Option` | | Terminating signal (`kill`, `term`, ...). | | `timed_out` / `timedOut` | `bool` | | Stopped by its timeout. | | `error` | `Option` | | Supervisor error (for example "executable not found"). | | `success` | `bool` | | `code == 0`. | ## `ProcessEvent` record One output chunk or the exit. Returned by [`SpacesdProcess.next_event`](#spacesdprocessnext_event). | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`ProcessEventKind`](#processeventkind-enum) | | Kind. | | `offset` | `u64` | | Combined-output offset of the first byte. | | `data` | `Vec` | | Bytes (empty for `Exit`). | | `exit` | [`Option`](#exitinfo-record) | | Exit (only for `Exit`). | ## `ProcessEventKind` enum Kind of a process event. **Python** ```python ProcessEventKind.STDOUT ProcessEventKind.STDERR ProcessEventKind.PTY ProcessEventKind.EXIT ``` **TypeScript** ```ts ProcessEventKind.Stdout ProcessEventKind.Stderr ProcessEventKind.Pty ProcessEventKind.Exit ``` **Swift** ```swift .stdout .stderr .pty .exit ``` **Kotlin** ```kotlin ProcessEventKind.STDOUT ProcessEventKind.STDERR ProcessEventKind.PTY ProcessEventKind.EXIT ``` **Rust** ```rust ProcessEventKind::Stdout ProcessEventKind::Stderr ProcessEventKind::Pty ProcessEventKind::Exit ``` | Variant | Description | | --- | --- | | `Stdout` | stdout bytes. | | `Stderr` | stderr bytes. | | `Pty` | PTY bytes. | | `Exit` | The process ended (always last). | ## `PtySize` record A PTY size. | Field | Type | Default | Description | | --- | --- | --- | --- | | `cols` | `u32` | | Columns. | | `rows` | `u32` | | Rows. | ## `ReplayMode` enum Scrollback replay when attaching. **Python** ```python ReplayMode.NONE() ReplayMode.ALL() ReplayMode.LAST_BYTES(n: int) ReplayMode.FROM_OFFSET(offset: int) ``` **TypeScript** ```ts new ReplayMode.None() new ReplayMode.All() new ReplayMode.LastBytes({ n: bigint }) new ReplayMode.FromOffset({ offset: bigint }) ``` **Swift** ```swift .none .all .lastBytes(n: UInt64) .fromOffset(offset: UInt64) ``` **Kotlin** ```kotlin ReplayMode.None ReplayMode.All ReplayMode.LastBytes(n: ULong) ReplayMode.FromOffset(offset: ULong) ``` **Rust** ```rust ReplayMode::None ReplayMode::All ReplayMode::LastBytes { n: u64 } ReplayMode::FromOffset { offset: u64 } ``` | Variant | Description | | --- | --- | | `None` | Live output only. | | `All` | Everything retained. | | `LastBytes` | The last `n` bytes. Fields: `n: u64` | | `FromOffset` | From this combined-output offset. Fields: `offset: u64` | --- # Files Upload, download, list and remove files in a sandbox through cua-spacesd. Paths are guest paths unless a parameter names a local one. Transfers are chunked, so size is not capped. Python programs usually use the high-level API instead: [`sb.files`](). ## File methods Methods of [`SpacesdClient`](). ### `SpacesdClient.upload` Uploads bytes to `path` (chunked, resumable, SHA-256 verified). **Python** ```python async def upload(self, path: str, data: bytes, options: Optional[UploadOptions]) -> TransferResult ``` **TypeScript** ```ts upload(path: string, data: ArrayBuffer, options: UploadOptions | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func upload(path: String, data: Data, options: UploadOptions?) async throws -> TransferResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun upload(path: String, data: ByteArray, options: UploadOptions?): TransferResult ``` **Rust** ```rust pub async fn upload(&self, path: String, data: Vec, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `data` | `Vec` | required | | `options` | [`Option`](#uploadoptions-record) | required | **Returns** [`TransferResult`](#transferresult-record) · **Async** · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="spacesd-client-upload-py" session="spacesd-client-upload-py" prelude="spacesd,spacesd-vars" import asyncio import cua async def main(): guest = await cua.embedded().spacesd(URL, TOKEN) sent = await guest.upload("/tmp/note.txt", b"hi from the host", None) back = await guest.download("/tmp/note.txt") print([sent.size, back]) # [16, b'hi from the host'] asyncio.run(main()) ``` **Swift** ```swift test="swift" id="spacesd-client-upload-swift" import Cua import Foundation let guest = try await Cua.embedded().spacesd(url: "http://10.0.0.5:3211", token: "TOKEN") let sent = try await guest.upload(path: "/tmp/note.txt", data: Data("hi from the host".utf8), options: nil) let back = try await guest.download(path: "/tmp/note.txt") print(sent.size, String(decoding: back, as: UTF8.self)) ``` **Kotlin** ```kotlin test="kotlin" id="spacesd-client-upload-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig val guest = Cua.embedded(CuaConfig()).spacesd("http://10.0.0.5:3211", "TOKEN") val sent = guest.upload("/tmp/note.txt", "hi from the host".encodeToByteArray(), null) val back = guest.download("/tmp/note.txt") println("${sent.size} ${back.decodeToString()}") ``` ### `SpacesdClient.upload_file` Uploads a local file to `path`. **Python** ```python async def upload_file(self, local_path: str, path: str, options: Optional[UploadOptions]) -> TransferResult ``` **TypeScript** ```ts uploadFile(localPath: string, path: string, options: UploadOptions | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func uploadFile(localPath: String, path: String, options: UploadOptions?) async throws -> TransferResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun uploadFile(localPath: String, path: String, options: UploadOptions?): TransferResult ``` **Rust** ```rust pub async fn upload_file(&self, local_path: String, path: String, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `local_path` | `String` | required | | `path` | `String` | required | | `options` | [`Option`](#uploadoptions-record) | required | **Returns** [`TransferResult`](#transferresult-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.download` Downloads `path` into memory. **Python** ```python async def download(self, path: str) -> bytes ``` **TypeScript** ```ts download(path: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func download(path: String) async throws -> Data ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun download(path: String): ByteArray ``` **Rust** ```rust pub async fn download(&self, path: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | **Returns** `Vec` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.download_file` Downloads `path` to a local file. **Python** ```python async def download_file(self, path: str, local_path: str) -> TransferResult ``` **TypeScript** ```ts downloadFile(path: string, localPath: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func downloadFile(path: String, localPath: String) async throws -> TransferResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun downloadFile(path: String, localPath: String): TransferResult ``` **Rust** ```rust pub async fn download_file(&self, path: String, local_path: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `local_path` | `String` | required | **Returns** [`TransferResult`](#transferresult-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.stat` `Stat`. **Python** ```python async def stat(self, path: str) -> FileEntry ``` **TypeScript** ```ts stat(path: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func stat(path: String) async throws -> FileEntry ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun stat(path: String): FileEntry ``` **Rust** ```rust pub async fn stat(&self, path: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | **Returns** [`FileEntry`](#fileentry-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.list_dir` `ListDir` (depth 1 = direct children). **Python** ```python async def list_dir(self, path: str, depth: int) -> List[FileEntry] ``` **TypeScript** ```ts listDir(path: string, depth: number, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func listDir(path: String, depth: UInt32) async throws -> [FileEntry] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listDir(path: String, depth: UInt): List ``` **Rust** ```rust pub async fn list_dir(&self, path: String, depth: u32) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `depth` | `u32` | required | **Returns** [`Vec`](#fileentry-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.make_dir` `MakeDir` (with parents). **Python** ```python async def make_dir(self, path: str) -> FileEntry ``` **TypeScript** ```ts makeDir(path: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func makeDir(path: String) async throws -> FileEntry ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun makeDir(path: String): FileEntry ``` **Rust** ```rust pub async fn make_dir(&self, path: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | **Returns** [`FileEntry`](#fileentry-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.remove` `Remove`. **Python** ```python async def remove(self, path: str, recursive: bool) -> None ``` **TypeScript** ```ts remove(path: string, recursive: boolean, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func remove(path: String, recursive: Bool) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun remove(path: String, recursive: Boolean) ``` **Rust** ```rust pub async fn remove(&self, path: String, recursive: bool) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `recursive` | `bool` | required | **Async** · **Raises** [`CuaError`]() ## `FileEntry` record A file or directory in the guest. Returned by [`SpacesdClient.list_dir`](#spacesdclientlist_dir), [`SpacesdClient.make_dir`](#spacesdclientmake_dir), [`SpacesdClient.stat`](#spacesdclientstat). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Base name. | | `path` | `String` | | Absolute path. | | `kind` | `String` | | `file`, `directory`, `symlink` or `other`. | | `size` | `u64` | | Size in bytes. | | `mode` | `u32` | | Unix mode bits. | | `modified_ms` / `modifiedMs` | `Option` | | Modification time (Unix ms), when known. | | `symlink_target` / `symlinkTarget` | `Option` | | Symlink target. | ## `TransferResult` record Result of an upload or download. Returned by [`SpacesdClient.download_file`](#spacesdclientdownload_file), [`SpacesdClient.upload`](#spacesdclientupload), [`SpacesdClient.upload_file`](#spacesdclientupload_file). | Field | Type | Default | Description | | --- | --- | --- | --- | | `size` | `u64` | | Bytes transferred. | | `sha256` | `String` | | SHA-256 (hex) of the content. | | `resumes` | `u32` | | Resumes after dropped connections. | ## `UploadOptions` record Upload options. | Field | Type | Default | Description | | --- | --- | --- | --- | | `create_new` / `createNew` | `bool` | `false` | Fail when the file exists (instead of overwriting). | | `append` | `bool` | `false` | Append instead of overwriting. | | `permissions` | `u32` | `0` | Unix permissions (0 = default). | | `create_parents` / `createParents` | `bool` | `true` | Create missing parent directories. | --- # Computer Screenshots, pointer, keyboard, clipboard and displays of a sandbox desktop through cua-spacesd. Coordinates are screen pixels. cua-spacesd delegates input to Cua Driver in the guest; these calls need an image with a desktop. Python programs usually use the high-level API instead: [`sb.mouse`, `sb.keyboard`, `sb.screen`](). ## Desktop methods Methods of [`SpacesdClient`](). ### `SpacesdClient.screenshot` Captures a display. **Python** ```python async def screenshot(self, options: Optional[ScreenshotOptions]) -> Screenshot ``` **TypeScript** ```ts screenshot(options: ScreenshotOptions | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func screenshot(options: ScreenshotOptions?) async throws -> Screenshot ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun screenshot(options: ScreenshotOptions?): Screenshot ``` **Rust** ```rust pub async fn screenshot(&self, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`Option`](#screenshotoptions-record) | required | **Returns** [`Screenshot`](#screenshot-record) · **Async** · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="spacesd-client-screenshot-py" session="spacesd-client-screenshot-py" prelude="spacesd,spacesd-vars" import asyncio import cua async def main(): guest = await cua.embedded().spacesd(URL, TOKEN) shot = await guest.screenshot(None) # PNG by default await guest.click(shot.width / 2, shot.height / 2) print([shot.width > 0, shot.image[:4]]) # [True, b'\x89PNG'] asyncio.run(main()) ``` **Swift** ```swift test="swift" id="spacesd-client-screenshot-swift" import Cua let guest = try await Cua.embedded().spacesd(url: "http://10.0.0.5:3211", token: "TOKEN") let shot = try await guest.screenshot(options: nil) // PNG by default try await guest.click(x: Double(shot.width) / 2, y: Double(shot.height) / 2) print(shot.width, shot.image.count) ``` **Kotlin** ```kotlin test="kotlin" id="spacesd-client-screenshot-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig val guest = Cua.embedded(CuaConfig()).spacesd("http://10.0.0.5:3211", "TOKEN") val shot = guest.screenshot(null) // PNG by default guest.click(shot.width.toDouble() / 2, shot.height.toDouble() / 2) println("${shot.width} ${shot.image.size}") ``` ### `SpacesdClient.click` Left click. **Python** ```python async def click(self, x: float, y: float) -> None ``` **TypeScript** ```ts click(x: number, y: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func click(x: Double, y: Double) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun click(x: Double, y: Double) ``` **Rust** ```rust pub async fn click(&self, x: f64, y: f64) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `x` | `f64` | required | | `y` | `f64` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.double_click` Double click. **Python** ```python async def double_click(self, x: float, y: float) -> None ``` **TypeScript** ```ts doubleClick(x: number, y: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func doubleClick(x: Double, y: Double) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun doubleClick(x: Double, y: Double) ``` **Rust** ```rust pub async fn double_click(&self, x: f64, y: f64) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `x` | `f64` | required | | `y` | `f64` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.right_click` Right click. **Python** ```python async def right_click(self, x: float, y: float) -> None ``` **TypeScript** ```ts rightClick(x: number, y: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func rightClick(x: Double, y: Double) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun rightClick(x: Double, y: Double) ``` **Rust** ```rust pub async fn right_click(&self, x: f64, y: f64) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `x` | `f64` | required | | `y` | `f64` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.move_to` Moves the pointer. **Python** ```python async def move_to(self, x: float, y: float) -> None ``` **TypeScript** ```ts moveTo(x: number, y: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func moveTo(x: Double, y: Double) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun moveTo(x: Double, y: Double) ``` **Rust** ```rust pub async fn move_to(&self, x: f64, y: f64) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `x` | `f64` | required | | `y` | `f64` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.drag` Drags with the left button. **Python** ```python async def drag(self, from_x: float, from_y: float, to_x: float, to_y: float) -> None ``` **TypeScript** ```ts drag(fromX: number, fromY: number, toX: number, toY: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func drag(fromX: Double, fromY: Double, toX: Double, toY: Double) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun drag(fromX: Double, fromY: Double, toX: Double, toY: Double) ``` **Rust** ```rust pub async fn drag(&self, from_x: f64, from_y: f64, to_x: f64, to_y: f64) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `from_x` | `f64` | required | | `from_y` | `f64` | required | | `to_x` | `f64` | required | | `to_y` | `f64` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.scroll` Scrolls by line deltas. **Python** ```python async def scroll(self, dx: float, dy: float) -> None ``` **TypeScript** ```ts scroll(dx: number, dy: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func scroll(dx: Double, dy: Double) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun scroll(dx: Double, dy: Double) ``` **Rust** ```rust pub async fn scroll(&self, dx: f64, dy: f64) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `dx` | `f64` | required | | `dy` | `f64` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.type_text` Types text. **Python** ```python async def type_text(self, text: str) -> None ``` **TypeScript** ```ts typeText(text: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func typeText(text: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun typeText(text: String) ``` **Rust** ```rust pub async fn type_text(&self, text: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `text` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.press` Presses one key (`enter`, `KEY_ESCAPE`, `a`, ...). **Python** ```python async def press(self, key: str) -> None ``` **TypeScript** ```ts press(key: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func press(key: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun press(key: String) ``` **Rust** ```rust pub async fn press(&self, key: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `key` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.hotkey` Presses a chord (`["ctrl", "c"]`). **Python** ```python async def hotkey(self, keys: List[str]) -> None ``` **TypeScript** ```ts hotkey(keys: Array, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func hotkey(keys: [String]) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun hotkey(keys: List) ``` **Rust** ```rust pub async fn hotkey(&self, keys: Vec) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `keys` | `Vec` | required | **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.pointer_json` Full `ComputerService.Pointer` from proto3 JSON. **Python** ```python async def pointer_json(self, request_json: str) -> str ``` **TypeScript** ```ts pointerJson(requestJson: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func pointerJson(requestJson: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun pointerJson(requestJson: String): String ``` **Rust** ```rust pub async fn pointer_json(&self, request_json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `request_json` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.keyboard_json` Full `ComputerService.Keyboard` from proto3 JSON. **Python** ```python async def keyboard_json(self, request_json: str) -> str ``` **TypeScript** ```ts keyboardJson(requestJson: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func keyboardJson(requestJson: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun keyboardJson(requestJson: String): String ``` **Rust** ```rust pub async fn keyboard_json(&self, request_json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `request_json` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.get_clipboard` Clipboard text. **Python** ```python async def get_clipboard(self) -> Optional[str] ``` **TypeScript** ```ts getClipboard(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func getClipboard() async throws -> String? ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun getClipboard(): String? ``` **Rust** ```rust pub async fn get_clipboard(&self) -> Result, CuaError> ``` **Returns** `Option` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.set_clipboard` Sets clipboard text; returns the clipboard generation. **Python** ```python async def set_clipboard(self, text: str) -> int ``` **TypeScript** ```ts setClipboard(text: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func setClipboard(text: String) async throws -> UInt64 ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun setClipboard(text: String): ULong ``` **Rust** ```rust pub async fn set_clipboard(&self, text: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `text` | `String` | required | **Returns** `u64` · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.cursor_position` Pointer position. **Python** ```python async def cursor_position(self) -> Point ``` **TypeScript** ```ts cursorPosition(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func cursorPosition() async throws -> Point ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun cursorPosition(): Point ``` **Rust** ```rust pub async fn cursor_position(&self) -> Result ``` **Returns** [`Point`](#point-record) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.displays` Displays as proto3 JSON (`[Display]`). **Python** ```python async def displays(self) -> str ``` **TypeScript** ```ts displays(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func displays() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun displays(): String ``` **Rust** ```rust pub async fn displays(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ## `Screenshot` record A captured screenshot. Returned by [`Sandbox.guest_screenshot`](), [`Space.screenshot`](), [`SpacesdClient.screenshot`](#spacesdclientscreenshot). | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `Vec` | | Encoded image. | | `format` | [`ImageFormat`](#imageformat-enum) | | Encoding. | | `width` | `u32` | | Pixel width. | | `height` | `u32` | | Pixel height. | | `scale` | `f64` | | Image pixels per logical point. | | `screenshot_id` / `screenshotId` | `String` | | Id for screenshot-space input. | ## `ScreenshotOptions` record Screenshot options. | Field | Type | Default | Description | | --- | --- | --- | --- | | `display` | `Option` | `None` | Display id (primary when unset). | | `format` | [`Option`](#imageformat-enum) | `None` | Encoding (PNG when unset). | | `quality` | `Option` | `None` | Lossy quality 1–100. | | `max_dimension` / `maxDimension` | `Option` | `None` | Long-edge cap in pixels. | | `include_cursor` / `includeCursor` | `bool` | `false` | Draw the cursor. | ## `ImageFormat` enum Screenshot encoding. **Python** ```python ImageFormat.PNG ImageFormat.JPEG ImageFormat.WEBP ``` **TypeScript** ```ts ImageFormat.Png ImageFormat.Jpeg ImageFormat.Webp ``` **Swift** ```swift .png .jpeg .webp ``` **Kotlin** ```kotlin ImageFormat.PNG ImageFormat.JPEG ImageFormat.WEBP ``` **Rust** ```rust ImageFormat::Png ImageFormat::Jpeg ImageFormat::Webp ``` | Variant | Description | | --- | --- | | `Png` | PNG. | | `Jpeg` | JPEG. | | `Webp` | WebP. | ## `Point` record A point. Returned by [`SpacesdClient.cursor_position`](#spacesdclientcursor_position). | Field | Type | Default | Description | | --- | --- | --- | --- | | `x` | `f64` | | X. | | `y` | `f64` | | Y. | --- # Media Video and audio streaming sessions and the sink callbacks you implement. Media sessions deliver encoded frames and audio packets (`FrameSink`, `AudioSink`) to callbacks you implement. Decoded BGRA frames and PCM audio come with Cua Spaces: see [Decoded media](). ## Open a session Methods of [`SpacesdClient`](). ### `SpacesdClient.open_media` Opens a media session and starts delivering encoded video frames and control events to `frames`. Decoding stays in the host language. **Python** ```python async def open_media(self, options: MediaOpenOptions, frames: FrameSink) -> MediaSession ``` **TypeScript** ```ts openMedia(options: MediaOpenOptions, frames: FrameSink, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func openMedia(options: MediaOpenOptions, frames: FrameSink) async throws -> MediaSession ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun openMedia(options: MediaOpenOptions, frames: FrameSink): MediaSession ``` **Rust** ```rust pub async fn open_media(&self, options: MediaOpenOptions, frames: Arc) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`MediaOpenOptions`](#mediaopenoptions-record) | required | | `frames` | [`FrameSink`](#framesink) | required | **Returns** [`MediaSession`](#mediasession) · **Async** · **Raises** [`CuaError`]() ### `SpacesdClient.open_media_with_audio` `SpacesdClient.open_media` that also delivers audio packets to `audio` (set `options.audio` to negotiate audio tracks). Two methods instead of an optional sink: optional callback objects do not lower in every binding generator. **Python** ```python async def open_media_with_audio(self, options: MediaOpenOptions, frames: FrameSink, audio: AudioSink) -> MediaSession ``` **TypeScript** ```ts openMediaWithAudio(options: MediaOpenOptions, frames: FrameSink, audio: AudioSink, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func openMediaWithAudio(options: MediaOpenOptions, frames: FrameSink, audio: AudioSink) async throws -> MediaSession ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun openMediaWithAudio(options: MediaOpenOptions, frames: FrameSink, audio: AudioSink): MediaSession ``` **Rust** ```rust pub async fn open_media_with_audio(&self, options: MediaOpenOptions, frames: Arc, audio: Arc) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`MediaOpenOptions`](#mediaopenoptions-record) | required | | `frames` | [`FrameSink`](#framesink) | required | | `audio` | [`AudioSink`](#audiosink) | required | **Returns** [`MediaSession`](#mediasession) · **Async** · **Raises** [`CuaError`]() ## Media bridge Methods of [`Sandbox`](). ### `Sandbox.open_media_bridge` Opens a media session and returns a loopback WebSocket bridge for a webview (daemon mode). `open_media_json` is a `cua.env.v1.OpenMediaRequest` in proto3 JSON (`None`: primary display). **Python** ```python async def open_media_bridge(self, open_media_json: Optional[str]) -> MediaBridge ``` **TypeScript** ```ts openMediaBridge(openMediaJson: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func openMediaBridge(openMediaJson: String?) async throws -> MediaBridge ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun openMediaBridge(openMediaJson: String?): MediaBridge ``` **Rust** ```rust pub async fn open_media_bridge(&self, open_media_json: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `open_media_json` | `Option` | required | **Returns** [`MediaBridge`](#mediabridge-record) · **Async** · **Raises** [`CuaError`]() ## `MediaSession` A live media session. Returned by [`Space.attach_stream`](), [`SpacesdClient.open_media`](#spacesdclientopen_media), [`SpacesdClient.open_media_with_audio`](#spacesdclientopen_media_with_audio). | Method | Description | | --- | --- | | [`close`](#mediasessionclose) | Closes the socket and the session. | | [`request_keyframe`](#mediasessionrequest_keyframe) | `StreamService.RequestKeyframe`. | | [`send_control`](#mediasessionsend_control) | Sends a JSON control message on the media socket (for example `{"type":"request_keyframe","payload":{}}` or interactive input). | | [`set_preferences_json`](#mediasessionset_preferences_json) | `StreamService.SetPreferences` from proto3 JSON (the session id is filled in). | | Accessor | Returns | Description | | --- | --- | --- | | `codec()` | `String` | Negotiated video codec (`h264`, `bgra`, `png`). | | `is_closed()` | `bool` | Whether the socket has closed. | | `open_response_json()` | `String` | `OpenMediaResponse` as proto3 JSON (ticket removed). | | `session_id()` | `String` | `OpenMediaResponse.media_session_id`. | | `stats()` | [`MediaStats`](#mediastats-record) | Delivery counters. | ### `MediaSession.close` Closes the socket and the session. **Python** ```python async def close(self) -> None ``` **TypeScript** ```ts close(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func close() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun closeSession() ``` **Rust** ```rust pub async fn close(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `MediaSession.request_keyframe` `StreamService.RequestKeyframe`. **Python** ```python async def request_keyframe(self) -> None ``` **TypeScript** ```ts requestKeyframe(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func requestKeyframe() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun requestKeyframe() ``` **Rust** ```rust pub async fn request_keyframe(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `MediaSession.send_control` Sends a JSON control message on the media socket (for example `{"type":"request_keyframe","payload":{}}` or interactive input). **Python** ```python def send_control(self, json: str) -> None ``` **TypeScript** ```ts sendControl(json: string): void ``` **Swift** ```swift func sendControl(json: String) throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun sendControl(json: String) ``` **Rust** ```rust pub fn send_control(&self, json: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Raises** [`CuaError`]() ### `MediaSession.set_preferences_json` `StreamService.SetPreferences` from proto3 JSON (the session id is filled in). **Python** ```python async def set_preferences_json(self, json: str) -> None ``` **TypeScript** ```ts setPreferencesJson(json: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func setPreferencesJson(json: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun setPreferencesJson(json: String) ``` **Rust** ```rust pub async fn set_preferences_json(&self, json: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Async** · **Raises** [`CuaError`]() ## `MediaOpenOptions` record Media session options. | Field | Type | Default | Description | | --- | --- | --- | --- | | `display` | `Option` | `None` | Display id (primary when unset). Ignored when `window_handle` is set. | | `window_handle` / `windowHandle` | `Option` | `None` | Window handle (from `ListTargets` / `ListWindows`). | | `max_fps` / `maxFps` | `u32` | `0` | FPS cap (0 = driver default). | | `max_dimension` / `maxDimension` | `u32` | `0` | Long-edge cap (0 = native). | | `audio` | `bool` | `false` | Also negotiate desktop/app audio (Opus). | | `disable_video` / `disableVideo` | `bool` | `false` | Audio only. | | `request_json` / `requestJson` | `Option` | `None` | Extra `OpenMediaRequest` fields as proto3 JSON, merged over the above (escape hatch). | ## `MediaBridge` record A media bridge for webviews (daemon mode). Returned by [`Sandbox.open_media_bridge`](#sandboxopen_media_bridge). | Field | Type | Default | Description | | --- | --- | --- | --- | | `ws_url` / `wsUrl` | `String` | | Loopback `ws://` URL carrying a short-lived bridge ticket. Speaks rcdp wire v2 unchanged; no Fleet bearer or env token is exposed. | | `ticket` | `String` | | The bridge ticket (also accepted as subprotocol `cua.ticket.`). | | `open_media_response_json` / `openMediaResponseJson` | `String` | | `cua.env.v1.OpenMediaResponse` as proto3 JSON (upstream ticket removed). | ## `AudioSink` Receives audio packets. Must return quickly. **Note** You implement `AudioSink` and pass it to the SDK (a callback interface): subclass it in Python, implement the interface in TypeScript, the `AudioSink` protocol in Swift, the interface in Kotlin and the trait in Rust. | Method | Description | | --- | --- | | [`on_audio`](#audiosinkon_audio) | An audio packet. | ### `AudioSink.on_audio` An audio packet. **Python** ```python def on_audio(self, packet: AudioPacket) -> None ``` **TypeScript** ```ts onAudio(packet: AudioPacket): void ``` **Swift** ```swift func onAudio(packet: AudioPacket) ``` **Kotlin** ```kotlin fun onAudio(packet: AudioPacket) ``` **Rust** ```rust pub fn on_audio(&self, packet: AudioPacket) ``` | Parameter | Type | Default | | --- | --- | --- | | `packet` | [`AudioPacket`](#audiopacket-record) | required | ## `FrameSink` Receives video frames and control events. Implementations must return quickly and hand off decoding. **Note** You implement `FrameSink` and pass it to the SDK (a callback interface): subclass it in Python, implement the interface in TypeScript, the `FrameSink` protocol in Swift, the interface in Kotlin and the trait in Rust. | Method | Description | | --- | --- | | [`on_event`](#framesinkon_event) | A control message or the socket close. | | [`on_frame`](#framesinkon_frame) | A video access unit. | ### `FrameSink.on_event` A control message or the socket close. **Python** ```python def on_event(self, event: MediaEvent) -> None ``` **TypeScript** ```ts onEvent(event: MediaEvent): void ``` **Swift** ```swift func onEvent(event: MediaEvent) ``` **Kotlin** ```kotlin fun onEvent(event: MediaEvent) ``` **Rust** ```rust pub fn on_event(&self, event: MediaEvent) ``` | Parameter | Type | Default | | --- | --- | --- | | `event` | [`MediaEvent`](#mediaevent-record) | required | ### `FrameSink.on_frame` A video access unit. **Python** ```python def on_frame(self, frame: VideoFrame) -> None ``` **TypeScript** ```ts onFrame(frame: VideoFrame): void ``` **Swift** ```swift func onFrame(frame: VideoFrame) ``` **Kotlin** ```kotlin fun onFrame(frame: VideoFrame) ``` **Rust** ```rust pub fn on_frame(&self, frame: VideoFrame) ``` | Parameter | Type | Default | | --- | --- | --- | | `frame` | [`VideoFrame`](#videoframe-record) | required | ## `AudioPacket` record One audio packet (MEDIA.md §12.2). | Field | Type | Default | Description | | --- | --- | --- | --- | | `track_id` / `trackId` | `u16` | | Track id from `NegotiatedAudio`. | | `sequence` | `u32` | | Per-track sequence (wraps mod 2³²). | | `pts_us` / `ptsUs` | `u64` | | Media-clock time of the first sample (µs). | | `frame_samples` / `frameSamples` | `u16` | | Samples per channel. | | `config_epoch` / `configEpoch` | `u8` | | Must match the latest `audio_config` of the track. | | `discontinuity` | `bool` | | The previous packet was dropped or the track restarted. | | `dtx` | `bool` | | DTX / comfort-noise frame. | | `data` | `Vec` | | One Opus packet or interleaved s16le PCM. | ## `MediaEvent` record A control message (`hello`, `session_opened`, `lifecycle`, `audio_config`, `stats`, `error`, ...) or the close of the socket (`kind = "closed"`, `json = {"code":…, "reason":…}`). | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | `String` | | The message `type`. | | `json` | `String` | | The whole message as JSON. | ## `MediaStats` record Delivery counters. Returned by [`MediaSession.stats`](#mediasession). | Field | Type | Default | Description | | --- | --- | --- | --- | | `frames` | `u64` | | Video frames delivered. | | `frames_dropped` / `framesDropped` | `u64` | | Video frames dropped because the sink fell behind. | | `audio_packets` / `audioPackets` | `u64` | | Audio packets delivered. | | `malformed` | `u64` | | Malformed binary messages ignored. | | `events` | `u64` | | Control events delivered. | ## `VideoFrame` record One encoded video access unit. | Field | Type | Default | Description | | --- | --- | --- | --- | | `sequence` | `u64` | | Per-target frame sequence (may start anywhere; gaps mean replaced frames). | | `codec` | `String` | | `h264`, `bgra` or `png`. | | `keyframe` | `bool` | | Random-access point (IDR with SPS/PPS for H.264). | | `width` | `u32` | | Payload width in pixels. | | `height` | `u32` | | Payload height in pixels. | | `capture_timestamp_us` / `captureTimestampUs` | `u64` | | Media-clock capture time (µs). | | `codec_epoch` / `codecEpoch` | `u64` | | Decoder configuration generation; reset the decoder when it changes. | | `geometry_epoch` / `geometryEpoch` | `u64` | | Coordinate generation. | | `data` | `Vec` | | Encoded payload. | | `header_json` / `headerJson` | `String` | | The packet's JSON header, verbatim. | --- # Coding agents Run coding agents (Claude Code, Codex, Gemini CLI, OpenCode, ...) inside any sandbox. `sandbox.agents()` (or `guest.agents()`) runs a harness over the Agent Client Protocol: normalized events, follow-ups, interrupts and results. Runs outlive the handle that started them. ## Agents in a sandbox Methods of [`Sandbox`](). ### `Sandbox.agents` Coding agents inside this sandbox (needs cua-spacesd): run a harness over ACP, stream normalized events, follow up, interrupt, collect results; runs outlive this handle. **Python** ```python async def agents(self) -> Agents ``` **TypeScript** ```ts agents(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agents() async throws -> Agents ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agents(): Agents ``` **Rust** ```rust pub async fn agents(&self) -> Result, CuaError> ``` **Returns** [`Agents`](#agents) · **Async** · **Raises** [`CuaError`]() ## Agents through a spacesd client Methods of [`SpacesdClient`](). ### `SpacesdClient.agents` Coding agents in this guest. **Python** ```python async def agents(self) -> Agents ``` **TypeScript** ```ts agents(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agents() async throws -> Agents ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agents(): Agents ``` **Rust** ```rust pub async fn agents(&self) -> Result, CuaError> ``` **Returns** [`Agents`](#agents) · **Async** · **Raises** [`CuaError`]() ## `Agents` Agent runs in one sandbox. Returned by [`Sandbox.agents`](#sandboxagents), [`SpacesdClient.agents`](#spacesdclientagents). | Method | Description | | --- | --- | | [`ensure`](#agentsensure) | Installs harnesses or apps (`["claude-code", "blender"]`: harness ids or installable ids) now; returns the progress lines. | | [`get`](#agentsget) | A handle for an existing run (started by any client). | | [`list`](#agentslist) | Every run in the sandbox, newest first. | | [`run`](#agentsrun) | Starts `harness` on `prompt` and returns at once; the run lives in the sandbox (fire and forget) until it is stopped. | ### `Agents.ensure` Installs harnesses or apps (`["claude-code", "blender"]`: harness ids or installable ids) now; returns the progress lines. **Python** ```python async def ensure(self, ids: List[str]) -> List[str] ``` **TypeScript** ```ts ensure(ids: Array, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func ensure(ids: [String]) async throws -> [String] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun ensure(ids: List): List ``` **Rust** ```rust pub async fn ensure(&self, ids: Vec) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `ids` | `Vec` | required | **Returns** `Vec` · **Async** · **Raises** [`CuaError`]() ### `Agents.get` A handle for an existing run (started by any client). **Python** ```python async def get(self, run_id: str) -> AgentRun ``` **TypeScript** ```ts get(runId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func get(runId: String) async throws -> AgentRun ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun get(runId: String): AgentRun ``` **Rust** ```rust pub async fn get(&self, run_id: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `run_id` | `String` | required | **Returns** [`AgentRun`](#agentrun) · **Async** · **Raises** [`CuaError`]() ### `Agents.list` Every run in the sandbox, newest first. **Python** ```python async def list(self) -> List[AgentRunInfo] ``` **TypeScript** ```ts list(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func list() async throws -> [AgentRunInfo] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun list(): List ``` **Rust** ```rust pub async fn list(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#agentruninfo-record) · **Async** · **Raises** [`CuaError`]() ### `Agents.run` Starts `harness` on `prompt` and returns at once; the run lives in the sandbox (fire and forget) until it is stopped. **Python** ```python async def run(self, harness: str, prompt: str, options: Optional[AgentRunOptions]) -> AgentRun ``` **TypeScript** ```ts run(harness: string, prompt: string, options: AgentRunOptions | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func run(harness: String, prompt: String, options: AgentRunOptions?) async throws -> AgentRun ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun run(harness: String, prompt: String, options: AgentRunOptions?): AgentRun ``` **Rust** ```rust pub async fn run(&self, harness: String, prompt: String, options: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `harness` | `String` | required | | `prompt` | `String` | required | | `options` | [`Option`](#agentrunoptions-record) | required | **Returns** [`AgentRun`](#agentrun) · **Async** · **Raises** [`CuaError`]() ## `AgentRun` One run. Returned by [`Agents.get`](#agentsget), [`Agents.run`](#agentsrun). | Method | Description | | --- | --- | | [`artifacts`](#agentrunartifacts) | Files the run created or changed in its working directory. | | [`events`](#agentrunevents) | Events after `cursor` (0: from the start), at most `max`. | | [`interrupt`](#agentruninterrupt) | Cancels the turn in flight; the session stays open. | | [`remove`](#agentrunremove) | Stops the run and deletes its directory (secrets included). | | [`result`](#agentrunresult) | The last turn's outcome. | | [`send`](#agentrunsend) | A follow-up in the same session (queued while a turn runs). | | [`status`](#agentrunstatus) | | | [`stop`](#agentrunstop) | Stops the run and verifies its process is gone. | | [`wait`](#agentrunwait) | Waits until no turn is running (at most `timeout_ms`), then returns the result. | | Accessor | Returns | Description | | --- | --- | --- | | `harness()` | `String` | | | `run_id()` | `String` | | ### `AgentRun.artifacts` Files the run created or changed in its working directory. **Python** ```python async def artifacts(self) -> List[AgentArtifact] ``` **TypeScript** ```ts artifacts(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func artifacts() async throws -> [AgentArtifact] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun artifacts(): List ``` **Rust** ```rust pub async fn artifacts(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#agentartifact-record) · **Async** · **Raises** [`CuaError`]() ### `AgentRun.events` Events after `cursor` (0: from the start), at most `max`. **Python** ```python async def events(self, cursor: int, max: Optional[int]) -> AgentEventPage ``` **TypeScript** ```ts events(cursor: bigint, max: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func events(cursor: UInt64, max: UInt32?) async throws -> AgentEventPage ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun events(cursor: ULong, max: UInt?): AgentEventPage ``` **Rust** ```rust pub async fn events(&self, cursor: u64, max: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `cursor` | `u64` | required | | `max` | `Option` | required | **Returns** [`AgentEventPage`](#agenteventpage-record) · **Async** · **Raises** [`CuaError`]() ### `AgentRun.interrupt` Cancels the turn in flight; the session stays open. **Python** ```python async def interrupt(self) -> AgentRunInfo ``` **TypeScript** ```ts interrupt(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func interrupt() async throws -> AgentRunInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun interrupt(): AgentRunInfo ``` **Rust** ```rust pub async fn interrupt(&self) -> Result ``` **Returns** [`AgentRunInfo`](#agentruninfo-record) · **Async** · **Raises** [`CuaError`]() ### `AgentRun.remove` Stops the run and deletes its directory (secrets included). **Python** ```python async def remove(self) -> None ``` **TypeScript** ```ts remove(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func remove() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun remove() ``` **Rust** ```rust pub async fn remove(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `AgentRun.result` The last turn's outcome. **Python** ```python async def result(self) -> AgentRunResult ``` **TypeScript** ```ts result(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func result() async throws -> AgentRunResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun result(): AgentRunResult ``` **Rust** ```rust pub async fn result(&self) -> Result ``` **Returns** [`AgentRunResult`](#agentrunresult-record) · **Async** · **Raises** [`CuaError`]() ### `AgentRun.send` A follow-up in the same session (queued while a turn runs). **Python** ```python async def send(self, text: str, files: Optional[List[AgentFile]]) -> AgentRunInfo ``` **TypeScript** ```ts send(text: string, files: Array | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func send(text: String, files: [AgentFile]?) async throws -> AgentRunInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun send(text: String, files: List?): AgentRunInfo ``` **Rust** ```rust pub async fn send(&self, text: String, files: Option>) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `text` | `String` | required | | `files` | [`Option>`](#agentfile-record) | required | **Returns** [`AgentRunInfo`](#agentruninfo-record) · **Async** · **Raises** [`CuaError`]() ### `AgentRun.status` **Python** ```python async def status(self) -> AgentRunInfo ``` **TypeScript** ```ts status(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func status() async throws -> AgentRunInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun status(): AgentRunInfo ``` **Rust** ```rust pub async fn status(&self) -> Result ``` **Returns** [`AgentRunInfo`](#agentruninfo-record) · **Async** · **Raises** [`CuaError`]() ### `AgentRun.stop` Stops the run and verifies its process is gone. **Python** ```python async def stop(self) -> AgentRunInfo ``` **TypeScript** ```ts stop(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func stop() async throws -> AgentRunInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun stop(): AgentRunInfo ``` **Rust** ```rust pub async fn stop(&self) -> Result ``` **Returns** [`AgentRunInfo`](#agentruninfo-record) · **Async** · **Raises** [`CuaError`]() ### `AgentRun.wait` Waits until no turn is running (at most `timeout_ms`), then returns the result. **Python** ```python async def wait(self, timeout_ms: Optional[int]) -> AgentRunResult ``` **TypeScript** ```ts wait(timeoutMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func wait(timeoutMs: UInt64?) async throws -> AgentRunResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun wait(timeoutMs: ULong?): AgentRunResult ``` **Rust** ```rust pub async fn wait(&self, timeout_ms: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `timeout_ms` | `Option` | required | **Returns** [`AgentRunResult`](#agentrunresult-record) · **Async** · **Raises** [`CuaError`]() ## `AgentRunOptions` record Options for `Agents.run`. Every field is optional. | Field | Type | Default | Description | | --- | --- | --- | --- | | `cwd` | `Option` | `None` | Working directory in the sandbox (default: the run's own). | | `repo` | `Option` | `None` | Git URL cloned into the working directory first. | | `branch` | `Option` | `None` | | | `env` | `HashMap` | `[:]` | Env for the agent only (API keys): written 0600 in the run, redacted from its event log, never in argv. | | `env_from_host` / `envFromHost` | `Vec` | `[]` | Provider key variables copied from THIS process's environment (`["ANTHROPIC_API_KEY"]`); only known provider key names. | | `model` | `Option` | `None` | Model id. | | `base_url` / `baseUrl` | `Option` | `None` | A custom model endpoint (proxy, gateway, compatible server). | | `wire` | `Option` | `None` | Its wire format: `anthropic`, `openai-responses`, `openai-chat`, `gemini` (default: the harness's own). | | `mcp_servers` / `mcpServers` | [`Vec`](#agentrunmcpserver-record) | `[]` | | | `sandbox_mcp` / `sandboxMcp` | `Option` | `None` | Give the agent the sandbox's own MCP (cua-driver). Default true. | | `skills` | `Option` | `None` | Copy the cua skills into the harness. Default true. | | `install` | `Option` | `None` | Install what the harness needs. Default true. | | `files` | [`Vec`](#agentfile-record) | `[]` | | | `exit_when_idle` / `exitWhenIdle` | `bool` | `false` | Stop (resumably) once the queue is empty: fire-and-forget. | | `label` | `Option` | `None` | | ## `AgentRunMcpServer` record An MCP server a run's agent gets: `url` (streamable HTTP, as reachable from inside the sandbox) or `command` (stdio, run in the sandbox). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Name the agent sees. | | `url` | `Option` | `None` | | | `headers` | `HashMap` | `[:]` | Header values (secrets allowed: they travel like env keys). | | `command` | `Option` | `None` | | | `args` | `Vec` | `[]` | | | `env` | `HashMap` | `[:]` | | ## `AgentFile` record A file for the first prompt or a follow-up. | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | File name in the run's `attachments/`. | | `bytes` | `Vec` | | | ## `AgentEvent` record One normalized event. | Field | Type | Default | Description | | --- | --- | --- | --- | | `seq` | `u64` | | | | `ts_ms` / `tsMs` | `u64` | | | | `turn` | `u32` | | | | `kind` | `String` | | `message`, `thought`, `tool_call`, `tool_update`, `plan`, `usage`, `permission`, `turn_started`, `turn_ended`, `install`, `error`, `exited`, ... (see `harnesses()`). | | `text` | `Option` | | | | `tool_id` / `toolId` | `Option` | | | | `tool_title` / `toolTitle` | `Option` | | | | `tool_kind` / `toolKind` | `Option` | | | | `tool_status` / `toolStatus` | `Option` | | | | `stop_reason` / `stopReason` | `Option` | | | | `category` | `String` | | How a conversation view shows it: `message` (the agent's words), `user` (the prompt as the agent got it), `activity` (a muted one-line row: install, thinking, tools, plan, turn end, notice, error, exit) or `hidden`. See `AgentTranscript`. | | `summary` | `Option` | | One short line for an `activity` event. | | `line` | `Option` | | One human-readable line, when the kind has one. | | `json` | `String` | | The event as written (ACP payload included), JSON. | ## `AgentEventPage` record A page of events. Returned by [`AgentRun.events`](#agentrunevents). | Field | Type | Default | Description | | --- | --- | --- | --- | | `events` | [`Vec`](#agentevent-record) | | | | `cursor` | `u64` | | Pass back to continue. | | `caught_up` / `caughtUp` | `bool` | | Nothing more is written yet. | ## `AgentRunInfo` record A run's state. Returned by [`AgentRun.interrupt`](#agentruninterrupt), [`AgentRun.send`](#agentrunsend), [`AgentRun.status`](#agentrunstatus), [`AgentRun.stop`](#agentrunstop), [`Agents.list`](#agentslist). | Field | Type | Default | Description | | --- | --- | --- | --- | | `run_id` / `runId` | `String` | | | | `harness` | `Option` | | | | `status` | `String` | | `running`, `idle`, `failed`, `crashed`, `unknown`. | | `phase` | `String` | | `installing`, `starting`, `working`, `waiting`, `exited`, ... | | `reason` | `String` | | | | `turn` | `u32` | | | | `alive` | `Option` | | | | `accepts_message` / `acceptsMessage` | `bool` | | A follow-up sent now starts the next turn (not mid-turn). | | `prompt` | `Option` | | | | `label` | `Option` | | | | `created_at` / `createdAt` | `Option` | | Unix seconds. | | `json` | `String` | | | ## `AgentRunResult` record What the last turn produced. Returned by [`AgentRun.result`](#agentrunresult), [`AgentRun.wait`](#agentrunwait). | Field | Type | Default | Description | | --- | --- | --- | --- | | `run_id` / `runId` | `String` | | | | `status` | `String` | | | | `turn` | `u32` | | | | `text` | `String` | | The agent's messages in the last turn. | | `stop_reason` / `stopReason` | `Option` | | | | `usage_json` / `usageJson` | `Option` | | | | `error` | `Option` | | | | `tool_calls` / `toolCalls` | `u32` | | | ## `AgentArtifact` record A file the run created or changed. Returned by [`AgentRun.artifacts`](#agentrunartifacts). | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | | | `size` | `u64` | | | | `modified_ms` / `modifiedMs` | `u64` | | | ## `agent_harnesses` The harnesses, their readiness, installs, key variables and limits, as JSON. **Python** ```python def agent_harnesses() -> str ``` **TypeScript** ```ts function agentHarnesses(): string ``` **Swift** ```swift func agentHarnesses() -> String ``` **Kotlin** ```kotlin fun agentHarnesses(): String ``` **Rust** ```rust pub fn agent_harnesses() -> String ``` **Returns** `String` ## `AgentTranscript` A run's events folded into conversation items, the same rule in every language: the agent's message chunks join into `message` items, the prompt is a `user` item (never repeated as agent text), and consecutive activity of one turn folds into one `activity` group. Absorbing an event twice changes nothing, so a poll can re-read a page. | Method | Description | | --- | --- | | [`new`](#agenttranscriptnew) | An empty transcript. | | [`absorb`](#agenttranscriptabsorb) | Adds events from `AgentRun.events`. | | [`absorb_json`](#agenttranscriptabsorb_json) | Adds the events of an `agent_events` result (`Space.agent_events`, the MCP tool), or a JSON array of events. | | [`note`](#agenttranscriptnote) | Adds a line the app itself produced (a start note, "queued") as an activity step of `turn`. | | Accessor | Returns | Description | | --- | --- | --- | | `cursor()` | `u64` | The highest `agent_events` cursor absorbed: pass it back to read on. | | `items()` | [`Vec`](#agenttranscriptitem-record) | The items so far. | | `preview()` | `Option` | The agent's last message on one line (a roster preview), `None` before it has said anything. Activity is never a preview. | | `revision()` | `u64` | Changes whenever the items do. | ### `AgentTranscript.new` An empty transcript. **Python** ```python AgentTranscript() ``` **TypeScript** ```ts new AgentTranscript() ``` **Swift** ```swift init() ``` **Kotlin** ```kotlin constructor() ``` **Rust** ```rust pub fn new() -> Arc ``` **Returns** [`AgentTranscript`](#agenttranscript) ### `AgentTranscript.absorb` Adds events from `AgentRun.events`. **Python** ```python def absorb(self, events: List[AgentEvent]) -> None ``` **TypeScript** ```ts absorb(events: Array): void ``` **Swift** ```swift func absorb(events: [AgentEvent]) ``` **Kotlin** ```kotlin fun absorb(events: List) ``` **Rust** ```rust pub fn absorb(&self, events: Vec) ``` | Parameter | Type | Default | | --- | --- | --- | | `events` | [`Vec`](#agentevent-record) | required | ### `AgentTranscript.absorb_json` Adds the events of an `agent_events` result (`Space.agent_events`, the MCP tool), or a JSON array of events. Returns the page's `cursor` (also kept, see `AgentTranscript.cursor`) when it has one. **Python** ```python def absorb_json(self, json: str) -> Optional[int] ``` **TypeScript** ```ts absorbJson(json: string): bigint | undefined ``` **Swift** ```swift func absorbJson(json: String) throws -> UInt64? ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun absorbJson(json: String): ULong? ``` **Rust** ```rust pub fn absorb_json(&self, json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** `Option` · **Raises** [`CuaError`]() ### `AgentTranscript.note` Adds a line the app itself produced (a start note, "queued") as an activity step of `turn`. **Python** ```python def note(self, turn: int, text: str) -> None ``` **TypeScript** ```ts note(turn: number, text: string): void ``` **Swift** ```swift func note(turn: UInt32, text: String) ``` **Kotlin** ```kotlin fun note(turn: UInt, text: String) ``` **Rust** ```rust pub fn note(&self, turn: u32, text: String) ``` | Parameter | Type | Default | | --- | --- | --- | | `turn` | `u32` | required | | `text` | `String` | required | ## `AgentTranscriptItem` record One item of an `AgentTranscript`. Returned by [`AgentTranscript.items`](#agenttranscript). | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | `String` | | `message` (a bubble or prose), `user` (the prompt as the agent got it; skip it when the app shows what the user typed) or `activity` (a muted, collapsible group of one-line steps). | | `turn` | `u32` | | Turn number (0 before the first prompt). | | `text` | `String` | | The message or prompt; for `activity`, the group summary (`5 steps`). | | `steps` | `Vec` | | `activity` only: one line per step. | ## `agent_event_category` The category of an event kind: `message`, `user`, `activity` or `hidden` (the rule `AgentEvent.category` and `AgentTranscript` use). **Python** ```python def agent_event_category(kind: str) -> str ``` **TypeScript** ```ts function agentEventCategory(kind: string): string ``` **Swift** ```swift func agentEventCategory(kind: String) -> String ``` **Kotlin** ```kotlin fun agentEventCategory(kind: String): String ``` **Rust** ```rust pub fn agent_event_category(kind: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `kind` | `String` | required | **Returns** `String` --- # Spaces The Spaces registry: create, add, resolve, delete and remove Spaces. `cua.spaces()` returns the `Spaces` registry. Create a Space where `on` says (local or cloud), or add an existing machine by URL, then open a [`Space`](). ## `Spaces` The Spaces registry: add a machine by URL, claim a Fleet Space, provision a local one, and get `Space` handles. Returned by [`Cua.spaces`](). | Method | Description | | --- | --- | | [`add`](#spacesadd) | Adds a Space by URL (`http(s)://host:port`, `host:port`, or a Space id such as `local:`) after a `GetCapabilities` handshake. | | [`add_with_service`](#spacesadd_with_service) | `Spaces.add`, naming the service a plain MCP endpoint is registered under (default `mcp`). | | [`call_tool_json`](#spacescall_tool_json) | Calls any Spaces MCP tool with JSON arguments (the implementation `cua daemon mcp` serves). | | [`cancel_create`](#spacescancel_create) | Cancels a create that is still running: `space` is the create's `create_id`, the id the Space will have (`local:`, carried by every progress report) or its name. | | [`create`](#spacescreate) | Creates a Space where `options.on` says (the user default when unset): a new sandbox registered as a Space. | | [`create_with_progress`](#spacescreate_with_progress) | `Spaces.create`, reporting what it does to `listener` (pulling the image, booting, waiting for cua-spacesd, connecting) until the Space is ready. | | [`delete`](#spacesdelete) | Deletes a Space's sandbox and forgets it (a Space added by address is only forgotten). | | [`gpu_support`](#spacesgpu_support) | The GPU options each runtime of `on` (`local` by default, or `cloud`) offers on this host: Lume's "GPU acceleration" for macOS VMs (experimental, Apple silicon), QEMU's virgl and containers' NVIDIA GPUs on Linux. | | [`hosts`](#spaceshosts) | Your machines that provide Spaces (the account's relay hosts, then the hosts added by their Tailscale or LAN address), each asked for its limits: what `create` takes as `on="host:"`. | | [`list`](#spaceslist) | Registered Spaces. | | [`list_tools_json`](#spaceslist_tools_json) | The Spaces MCP `tools/list` result as JSON (the contract). | | [`relay_register`](#spacesrelay_register) | Publishes `space` on the cua.ai relay as a machine of the signed-in account, the way `cua host setup` publishes this computer: the Space's own driver dials out, so the account's other devices (a phone off this network) reach it as `relay:`, and nobody else until it is shared. | | [`relay_unregister`](#spacesrelay_unregister) | Takes `space` off the relay (its driver leaves; every share goes). | | [`remove`](#spacesremove) | Unregisters a Space (the sandbox is not touched). | | [`resolve`](#spacesresolve) | Resolves an id, legacy id, URL or display name. | | [`space`](#spacesspace) | A connected handle to a registered Space. | | [`start`](#spacesstart) | Turns a Space on again: resumes a suspended one, boots a stopped one (a Space one of your machines provides joins the relay again), and leaves a running one as it is. | | [`stop`](#spacesstop) | Turns a Space off the way its provider can (`SpaceInfo.power`): suspends it, keeping its memory (a local container or QEMU VM), or stops it, keeping its disk (a local Lume VM; a Space one of your machines provides, which that machine stops). | | [`agent_capabilities`]() | What every agent harness is and cannot do (`agent_capabilities`), as JSON. | | [`agent_pause`]() | Pauses a persistent agent: run, routines and (local) Space. | | [`agent_resume`]() | Resumes a paused persistent agent; with `prompt`, starts a run. | | [`cloud_connect`]() | Connects a cloud account (after the checks `cloud_test` runs). | | [`cloud_disconnect`]() | Forgets a connected cloud (nothing in it is deleted). | | [`cloud_status`]() | Your clouds: found credentials, the connected account and region, what each can run at what hourly cost, and what Cua created there. | | [`cloud_sweep`]() | What Cua left in your clouds (expired Spaces, resources whose Space is gone; with `all`, everything Cua created there). | | [`cloud_test`]() | Checks a cloud account without creating anything. | | [`computer_access`]() | Per-agent computer grants (of one agent, or all). | | [`computer_access_grant`]() | Lets one persistent agent use one of the user's computers (asks for presence). | | [`computer_access_revoke`]() | Takes a persistent agent's computer access back (every machine when `machine` is empty). | | [`notifications`]() | The notifications feed, newest first. | | [`notifications_ack`]() | Marks notifications read (every one when `ids` is empty). | | [`notify_user`]() | Posts a notification to the Cua app. | | [`persistent_agent_create`]() | Creates a persistent agent: `harness` working in `space`, its home at `agents//` in the Cua Volume. | | [`persistent_agent_remove`]() | Forgets a persistent agent (its home stays in the drive). | | [`persistent_agent_save`]() | Saves a persistent agent's home into the drive now. | | [`persistent_agent_send`]() | Gives a persistent agent a turn: a follow-up to its idle run, or a new run with its home restored. | | [`persistent_agents`]() | Every persistent agent. | | [`routine_add`]() | Adds a routine. | | [`routine_remove`]() | Deletes a routine. | | [`routine_set_enabled`]() | Turns a routine on or off. | | [`routines`]() | Routines (of one agent, or all). | | [`volume_approve`]() | `volume_approve`: the request becomes a grant (with presence). | | [`volume_audit`]() | `volume_audit`: the newest events (default 50) and whether the log verified. | | [`volume_cache_clear`]() | `volume_cache_clear`: drops every cached block. | | [`volume_cache_set`]() | `volume_cache_set`: the cache's size cap (at least 256 MiB). | | [`volume_cache_stats`]() | `volume_cache_stats`: the block cache's size, cap and hit rate. | | [`volume_delete`]() | `volume_delete`: a delete marker (history stays). | | [`volume_deny`]() | `volume_deny`. | | [`volume_grant`]() | `volume_grant`: widens `principal`'s access (`agent:` or `space:`, mode `r` or `rw`). | | [`volume_grants`]() | `volume_grants`: live grants (every grant with `all`). | | [`volume_history`]() | `volume_history`: a file's versions, newest first. | | [`volume_ls`]() | `volume_ls`: a folder's immediate children (default the root). | | [`volume_mount`]() | `volume_mount`: turns the mount on (kept across restarts) and mounts. | | [`volume_mount_status`]() | `volume_mount_status`: whether the drive is mounted as a volume. | | [`volume_read`]() | `volume_read`: a file (or one of its versions). | | [`volume_request_access`]() | `volume_request_access`: `agent` asks the user for more access. | | [`volume_requests`]() | `volume_requests`: requests waiting for the user. | | [`volume_restore`]() | `volume_restore`: makes an old version current again. | | [`volume_revoke`]() | `volume_revoke`. | | [`volume_storage`]() | `volume_storage`: where the drive keeps its bytes. | | [`volume_storage_set`]() | `volume_storage_set`: tests, and unless `dry_run` saves and switches to, a storage backend (live, no restart). | | [`volume_sync_events`]() | `volume_sync_events`: events after `since_seq`, waiting up to `wait_ms` (at most 30000) for one. | | [`volume_sync_resolve`]() | `volume_sync_resolve`: clears a conflict from the list (files stay). | | [`volume_sync_status`]() | `volume_sync_status`: devices, pending uploads and conflicts. | | [`volume_unmount`]() | `volume_unmount`: turns the mount off; pending uploads land first. | | [`volume_write`]() | `volume_write`: a new version of a file. | ### `Spaces.add` Adds a Space by URL (`http(s)://host:port`, `host:port`, or a Space id such as `local:`) after a `GetCapabilities` handshake. Any other MCP endpoint (`http://host:8765/mcp`) becomes a Space with one MCP service, `mcp`, and no spacesd capabilities. **Python** ```python async def add(self, url: str, token: Optional[str], name: Optional[str]) -> SpaceInfo ``` **TypeScript** ```ts add(url: string, token: string | undefined, name: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func add(url: String, token: String?, name: String?) async throws -> SpaceInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun add(url: String, token: String?, name: String?): SpaceInfo ``` **Rust** ```rust pub async fn add(&self, url: String, token: Option, name: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `url` | `String` | required | | `token` | `Option` | required | | `name` | `Option` | required | **Returns** [`SpaceInfo`](#spaceinfo-record) · **Async** · **Raises** [`CuaError`]() **Example** **Python** ```python test="docs" id="spaces-add-py" session="spaces-add-py" prelude="space-url" import asyncio import cua async def main(): spaces = cua.embedded().spaces() info = await spaces.add("http://10.0.0.5:3211", "TOKEN", "lab") # a machine running cua-spacesd space = await spaces.space(info.id) out = await space.bash("echo hi", None) print([info.provider, out.stdout, out.exit_code]) asyncio.run(main()) ``` **Swift** ```swift test="swift" id="spaces-add-swift" import Cua let spaces = try Cua.embedded().spaces() let info = try await spaces.add(url: "http://10.0.0.5:3211", token: "TOKEN", name: "lab") // a machine running cua-spacesd let space = try await spaces.space(space: info.id) let out = try await space.bash(command: "echo hi", timeoutMs: nil) print(info.provider, out.stdout, out.exitCode ?? -1) ``` **Kotlin** ```kotlin test="kotlin" id="spaces-add-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig val spaces = Cua.embedded(CuaConfig()).spaces() val info = spaces.add("http://10.0.0.5:3211", "TOKEN", "lab") // a machine running cua-spacesd val space = spaces.space(info.id) val out = space.bash("echo hi", null) println("${info.provider} ${out.stdout} ${out.exitCode}") ``` ### `Spaces.add_with_service` `Spaces.add`, naming the service a plain MCP endpoint is registered under (default `mcp`). **Python** ```python async def add_with_service(self, url: str, token: Optional[str], name: Optional[str], service: Optional[str]) -> SpaceInfo ``` **TypeScript** ```ts addWithService(url: string, token: string | undefined, name: string | undefined, service: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func addWithService(url: String, token: String?, name: String?, service: String?) async throws -> SpaceInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun addWithService(url: String, token: String?, name: String?, service: String?): SpaceInfo ``` **Rust** ```rust pub async fn add_with_service(&self, url: String, token: Option, name: Option, service: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `url` | `String` | required | | `token` | `Option` | required | | `name` | `Option` | required | | `service` | `Option` | required | **Returns** [`SpaceInfo`](#spaceinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.call_tool_json` Calls any Spaces MCP tool with JSON arguments (the implementation `cua daemon mcp` serves). Tool errors are returned, not raised. **Python** ```python async def call_tool_json(self, tool: str, arguments_json: Optional[str]) -> SpaceToolResult ``` **TypeScript** ```ts callToolJson(tool: string, argumentsJson: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func callToolJson(tool: String, argumentsJson: String?) async throws -> SpaceToolResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun callToolJson(tool: String, argumentsJson: String?): SpaceToolResult ``` **Rust** ```rust pub async fn call_tool_json(&self, tool: String, arguments_json: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `tool` | `String` | required | | `arguments_json` | `Option` | required | **Returns** [`SpaceToolResult`]() · **Async** · **Raises** [`CuaError`]() ### `Spaces.cancel_create` Cancels a create that is still running: `space` is the create's `create_id`, the id the Space will have (`local:`, carried by every progress report) or its name. The work in flight stops (an image download, a boot, a claim, a relay registration) and what the create made is removed (its VM or container and disks, its cloud claim, its relay machine); nothing that existed before is touched. Finished image downloads stay cached, so the next create resumes. Returns once the clean-up is done; the create itself fails with `Cancelled`. Idempotent: a second call says `not_creating`. Works for a create another process runs, and one a daemon restart cut off. **Python** ```python async def cancel_create(self, space: str) -> SpaceCancelOutcome ``` **TypeScript** ```ts cancelCreate(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func cancelCreate(space: String) async throws -> SpaceCancelOutcome ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun cancelCreate(space: String): SpaceCancelOutcome ``` **Rust** ```rust pub async fn cancel_create(&self, space: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Returns** [`SpaceCancelOutcome`]() · **Async** · **Raises** [`CuaError`]() ([`Cancelled`]()) ### `Spaces.create` Creates a Space where `options.on` says (the user default when unset): a new sandbox registered as a Space. An existing machine is added with `Spaces.add`. **Python** ```python async def create(self, options: SpaceCreateOptions) -> SpaceCreateResult ``` **TypeScript** ```ts create(options: SpaceCreateOptions, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func create(options: SpaceCreateOptions) async throws -> SpaceCreateResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun create(options: SpaceCreateOptions): SpaceCreateResult ``` **Rust** ```rust pub async fn create(&self, options: SpaceCreateOptions) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`SpaceCreateOptions`]() | required | **Returns** [`SpaceCreateResult`]() · **Async** · **Raises** [`CuaError`]() ### `Spaces.create_with_progress` `Spaces.create`, reporting what it does to `listener` (pulling the image, booting, waiting for cua-spacesd, connecting) until the Space is ready. Always waits (`options.wait` is ignored). Through a daemon that predates progress, the listener hears only `ready`. **Python** ```python async def create_with_progress(self, options: SpaceCreateOptions, listener: SpaceCreateListener) -> SpaceCreateResult ``` **TypeScript** ```ts createWithProgress(options: SpaceCreateOptions, listener: SpaceCreateListener, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func createWithProgress(options: SpaceCreateOptions, listener: SpaceCreateListener) async throws -> SpaceCreateResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun createWithProgress(options: SpaceCreateOptions, listener: SpaceCreateListener): SpaceCreateResult ``` **Rust** ```rust pub async fn create_with_progress(&self, options: SpaceCreateOptions, listener: Arc) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`SpaceCreateOptions`]() | required | | `listener` | [`SpaceCreateListener`]() | required | **Returns** [`SpaceCreateResult`]() · **Async** · **Raises** [`CuaError`]() ### `Spaces.delete` Deletes a Space's sandbox and forgets it (a Space added by address is only forgotten). Returns what happened. **Python** ```python async def delete(self, space: str) -> str ``` **TypeScript** ```ts delete_(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func delete(space: String) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun delete(space: String): String ``` **Rust** ```rust pub async fn delete(&self, space: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Spaces.gpu_support` The GPU options each runtime of `on` (`local` by default, or `cloud`) offers on this host: Lume's "GPU acceleration" for macOS VMs (experimental, Apple silicon), QEMU's virgl and containers' NVIDIA GPUs on Linux. A runtime with no option says why. **Python** ```python async def gpu_support(self, on: Optional[str]) -> List[GpuSupport] ``` **TypeScript** ```ts gpuSupport(on: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func gpuSupport(on: String?) async throws -> [GpuSupport] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun gpuSupport(on: String?): List ``` **Rust** ```rust pub async fn gpu_support(&self, on: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `on` | `Option` | required | **Returns** [`Vec`]() · **Async** · **Raises** [`CuaError`]() ### `Spaces.hosts` Your machines that provide Spaces (the account's relay hosts, then the hosts added by their Tailscale or LAN address), each asked for its limits: what `create` takes as `on="host:"`. One that does not answer is listed offline when it provided a Space before. **Python** ```python async def hosts(self) -> List[SpacesHost] ``` **TypeScript** ```ts hosts(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func hosts() async throws -> [SpacesHost] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun hosts(): List ``` **Rust** ```rust pub async fn hosts(&self) -> Result, CuaError> ``` **Returns** [`Vec`]() · **Async** · **Raises** [`CuaError`]() ### `Spaces.list` Registered Spaces. **Python** ```python async def list(self) -> List[SpaceInfo] ``` **TypeScript** ```ts list(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func list() async throws -> [SpaceInfo] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun list(): List ``` **Rust** ```rust pub async fn list(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#spaceinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.list_tools_json` The Spaces MCP `tools/list` result as JSON (the contract). **Python** ```python async def list_tools_json(self) -> str ``` **TypeScript** ```ts listToolsJson(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func listToolsJson() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listToolsJson(): String ``` **Rust** ```rust pub async fn list_tools_json(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Spaces.relay_register` Publishes `space` on the cua.ai relay as a machine of the signed-in account, the way `cua host setup` publishes this computer: the Space's own driver dials out, so the account's other devices (a phone off this network) reach it as `relay:`, and nobody else until it is shared. Idempotent. **Python** ```python async def relay_register(self, space: str) -> SpaceRelayRegistration ``` **TypeScript** ```ts relayRegister(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func relayRegister(space: String) async throws -> SpaceRelayRegistration ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun relayRegister(space: String): SpaceRelayRegistration ``` **Rust** ```rust pub async fn relay_register(&self, space: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Returns** [`SpaceRelayRegistration`]() · **Async** · **Raises** [`CuaError`]() ### `Spaces.relay_unregister` Takes `space` off the relay (its driver leaves; every share goes). Returns false when it was not on the relay. **Python** ```python async def relay_unregister(self, space: str) -> bool ``` **TypeScript** ```ts relayUnregister(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func relayUnregister(space: String) async throws -> Bool ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun relayUnregister(space: String): Boolean ``` **Rust** ```rust pub async fn relay_unregister(&self, space: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Returns** `bool` · **Async** · **Raises** [`CuaError`]() ### `Spaces.remove` Unregisters a Space (the sandbox is not touched). **Python** ```python async def remove(self, space: str) -> None ``` **TypeScript** ```ts remove(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func remove(space: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun remove(space: String) ``` **Rust** ```rust pub async fn remove(&self, space: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Spaces.resolve` Resolves an id, legacy id, URL or display name. **Python** ```python async def resolve(self, space: str) -> SpaceInfo ``` **TypeScript** ```ts resolve(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func resolve(space: String) async throws -> SpaceInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun resolve(space: String): SpaceInfo ``` **Rust** ```rust pub async fn resolve(&self, space: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Returns** [`SpaceInfo`](#spaceinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.space` A connected handle to a registered Space. **Python** ```python async def space(self, space: str) -> Space ``` **TypeScript** ```ts space(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func space(space: String) async throws -> Space ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun space(space: String): Space ``` **Rust** ```rust pub async fn space(&self, space: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Returns** [`Space`]() · **Async** · **Raises** [`CuaError`]() ### `Spaces.start` Turns a Space on again: resumes a suspended one, boots a stopped one (a Space one of your machines provides joins the relay again), and leaves a running one as it is. Returns once it answers (bounded). **Python** ```python async def start(self, space: str) -> SpacePowerReport ``` **TypeScript** ```ts start(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func start(space: String) async throws -> SpacePowerReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun start(space: String): SpacePowerReport ``` **Rust** ```rust pub async fn start(&self, space: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Returns** [`SpacePowerReport`](#spacepowerreport-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.stop` Turns a Space off the way its provider can (`SpaceInfo.power`): suspends it, keeping its memory (a local container or QEMU VM), or stops it, keeping its disk (a local Lume VM; a Space one of your machines provides, which that machine stops). A cloud Space, a Space added by address and your own computers cannot be turned off. **Python** ```python async def stop(self, space: str) -> SpacePowerReport ``` **TypeScript** ```ts stop(space: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func stop(space: String) async throws -> SpacePowerReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun stop(space: String): SpacePowerReport ``` **Rust** ```rust pub async fn stop(&self, space: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | `String` | required | **Returns** [`SpacePowerReport`](#spacepowerreport-record) · **Async** · **Raises** [`CuaError`]() ## `SpaceInfo` record A registered Space. Returned by [`Space.info`](), [`Spaces.add`](#spacesadd), [`Spaces.add_with_service`](#spacesadd_with_service), [`Spaces.list`](#spaceslist), [`Spaces.resolve`](#spacesresolve). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Id, for example `direct:10.0.0.5:3211` or `cloud:` (the sandbox ref scheme). | | `name` | `String` | | Display name. | | `provider` | `String` | | `cloud`, `local`, `direct` or `relay` (the location words). | | `spacesd_version` / `spacesdVersion` | `String` | | spacesd version at the last handshake. | | `features` | `Vec` | | Supported features at the last handshake. | | `os` | `String` | | Guest OS family at the last handshake: `linux`, `macos`, `windows`, or empty when not reported. | | `os_name` / `osName` | `String` | | Guest OS product or distribution at the last handshake ("Ubuntu", "macOS"), or empty when not reported. | | `os_pretty_name` / `osPrettyName` | `String` | | The guest's full OS string at the last handshake ("Ubuntu 24.04.3 LTS", "macOS 26.5.2 (25F84)"), or empty from older drivers. | | `image` | `String` | | The image the sandbox runs ("ghcr.io/trycua/linux:24.04"), or empty when unknown (a Space added by address). | | `image_digest` / `imageDigest` | `String` | | The digest of the variant that runs ("sha256:..."), or empty. | | `kind` | `String` | | `container` or `vm`, or empty when unknown. | | `arch` | `String` | | The guest's CPU architecture (`arm64`, `amd64`), or empty when unknown. | | `services` | `Vec` | | Declared services (never `env`), reachable with `list_tools` / `call_tool` (`service`). A Space without cua-spacesd has an empty `features` list and only these. | | `added_at` / `addedAt` | `Option` | | When it was added (RFC 3339). | | `host` | `String` | | For a Space one of your machines provides (created with `on="host:"`): that host's relay machine id; empty otherwise. Lists group these Spaces under their host. | | `host_name` / `hostName` | `String` | | The host's display name, when known. | | `power` | `String` | | How it turns off and on again (`Spaces.stop`, `Spaces.start`): `suspend` (its memory is kept), `stop` (its disk is kept), or empty when it cannot (a cloud Space, a Space added by address). | | `power_state` / `powerState` | `String` | | `running`, `suspended` or `stopped` as cua last recorded it; empty when unknown (a Space one of your machines provides answers while it runs). | | `cloud` | `String` | | A Space in your own cloud: the provider (`aws`, `gcp`, `modal`); empty otherwise. | | `cloud_place` / `cloudPlace` | `String` | | Where it runs ("AWS · us-west-2"). | | `cloud_delete` / `cloudDelete` | `String` | | How `delete` would delete it permanently from here: `here` (this device created it), `host:` (the device that created it, through the relay) or `elsewhere` (only there; `remove` keeps it). Empty for other Spaces. | ## `SpacePowerReport` record What `Spaces.stop` or `Spaces.start` did. Returned by [`Spaces.start`](#spacesstart), [`Spaces.stop`](#spacesstop). | Field | Type | Default | Description | | --- | --- | --- | --- | | `space` | `String` | | The Space. | | `state` | `String` | | `running`, `suspended` or `stopped`: the state it is in now. | | `power` | `String` | | How it turns off: `suspend` (its memory is kept) or `stop` (its disk is kept). | | `message` | `String` | | What happened, for people. | ## `spaces_tool_methods` Which SDK method covers each Spaces contract tool. Language test suites check every listed method exists on the generated class. **Python** ```python def spaces_tool_methods() -> List[SpacesToolMethod] ``` **TypeScript** ```ts function spacesToolMethods(): Array ``` **Swift** ```swift func spacesToolMethods() -> [SpacesToolMethod] ``` **Kotlin** ```kotlin fun spacesToolMethods(): List ``` **Rust** ```rust pub fn spaces_tool_methods() -> Vec ``` **Returns** [`Vec`](#spacestoolmethod-record) ## `SpacesToolMethod` record One row of `SPACES_TOOL_METHODS`. Returned by [`spaces_tool_methods`](#spaces_tool_methods). | Field | Type | Default | Description | | --- | --- | --- | --- | | `tool` | `String` | | Contract tool name. | | `method` | `String` | | `Class.method` (Rust / Python spelling; camelCase in TS, Swift and Kotlin). | --- # Create options What `Spaces.create` takes and reports: options, progress, cancel and GPU acceleration. `Spaces.create` takes `SpaceCreateOptions` and returns a `SpaceCreateResult`. A `SpaceCreateListener` receives `SpaceCreateProgress` while it runs; `Spaces.cancel_create` stops it. `GpuSupport` says whether a location offers GPU acceleration. ## `SpaceCreateOptions` record Options for `Spaces.create`: the same location / kind / runtime model as sandboxes. | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `Option` | `None` | Image (default: the canonical Linux image). | | `on` | `Option` | `None` | Where: `local` (free) or `cloud` (metered). `None`: the user default (`default.on`, `CUA_DEFAULT_ON`, else `local`). | | `kind` | `Option` | `None` | `auto` (default), `container` or `vm`. | | `runtime` | `Option` | `None` | `auto` (default) or an engine the location offers for the kind. | | `name` | `Option` | `None` | Name (default `space-`). | | `cpus` | `Option` | `None` | vCPUs. Local default 2; cloud 1-64 (most accounts run 1-8; Fleet decides), default the pool template's (4). | | `memory_mb` / `memoryMb` | `Option` | `None` | Memory in MiB. Local default 4096; cloud 512 up to 524288 (most accounts run 1024-32768; Fleet decides), default the pool template's (4096). | | `disk_gb` / `diskGb` | `Option` | `None` | Grow the VM's disk to this many GiB (local VMs; default: the image's size, never smaller). Lume grows the macOS APFS container before the first boot; Linux images grow their root partition at boot. Containers and cloud Spaces refuse it. | | `timeout_ms` / `timeoutMs` | `Option` | `None` | Readiness budget (local; default 600 s). | | `wait` | `Option` | `None` | Wait until ready (default true). | | `reuse` | `bool` | `false` | Return a reachable registered Space in the same location with the requested services instead of creating one. | | `command` | `Option>` | `None` | Entrypoint override. | | `env` | `HashMap` | `[:]` | Guest environment. | | `services` | `HashMap` | `[:]` | Named services (name → guest port), for example `{"mcp": 8765}`. | | `spacesd` | `Option` | `None` | Whether the image runs cua-spacesd (default: yes for the canonical images, no for any other image). | | `gpu` | `Option` | `None` | A GPU option of the runtime the Space runs on (`Spaces.gpu_support`): `paravirtual` for a macOS VM on Lume (GPU acceleration, experimental), `virgl` (QEMU on Linux), `nvidia` (a runc container on Linux); `auto` picks the runtime's own. `None` (the default): no GPU. Kept with the Space. | | `create_id` / `createId` | `Option` | `None` | Your own key for this create (an app's pending row), for `Spaces.cancel_create` before the Space's id is known. | ## `SpaceCreateResult` record Result of `Spaces.create`. Returned by [`Spaces.create`](), [`Spaces.create_with_progress`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `space` | [`Option`]() | | The Space, once ready. | | `pending_id` / `pendingId` | `Option` | | With `wait = false`: the id the Space will have. | | `reused` | `bool` | | `reuse` returned an existing Space. | ## `SpaceCreateProgress` record What a Space create is doing (see `Spaces.create_with_progress`). | Field | Type | Default | Description | | --- | --- | --- | --- | | `phase` | `String` | | `preparing`, `pulling`, `creating`, `booting`, `waiting_for_services`, `connecting` or `ready`, in that order. | | `fraction` | `Option` | | How far through the phase, 0.0 to 1.0, when its size is known (an image pull). | | `detail` | `String` | | One short line, for example the image being pulled. | | `bytes_done` / `bytesDone` | `Option` | `None` | Bytes downloaded so far, when the step counts them (an image pull). | | `bytes_total` / `bytesTotal` | `Option` | `None` | Bytes the download has in all. | | `bytes_per_second` / `bytesPerSecond` | `Option` | `None` | Smoothed download rate, bytes per second (time left: the bytes to go over it). | | `space` | `String` | `""` | The id the Space will have (`local:`), for `Spaces.cancel_create`. | ## `SpaceCreateListener` Receives `SpaceCreateProgress` while `Spaces.create_with_progress` runs, in order, ending with `ready`. Called on an SDK worker thread; return quickly. **Note** You implement `SpaceCreateListener` and pass it to the SDK (a callback interface): subclass it in Python, implement the interface in TypeScript, the `SpaceCreateListener` protocol in Swift, the interface in Kotlin and the trait in Rust. | Method | Description | | --- | --- | | [`on_progress`](#spacecreatelisteneron_progress) | The create moved on. | ### `SpaceCreateListener.on_progress` The create moved on. **Python** ```python def on_progress(self, progress: SpaceCreateProgress) -> None ``` **TypeScript** ```ts onProgress(progress: SpaceCreateProgress): void ``` **Swift** ```swift func onProgress(progress: SpaceCreateProgress) ``` **Kotlin** ```kotlin fun onProgress(progress: SpaceCreateProgress) ``` **Rust** ```rust pub fn on_progress(&self, progress: SpaceCreateProgress) ``` | Parameter | Type | Default | | --- | --- | --- | | `progress` | [`SpaceCreateProgress`](#spacecreateprogress-record) | required | ## `SpaceCancelOutcome` record What `Spaces.cancel_create` did. Returned by [`Spaces.cancel_create`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | The Space id the create had (`local:`), when known. | | `state` | `String` | | `cancelled`, `not_creating` (nothing by that key is running) or `already_created` (it finished: delete it instead). | | `message` | `String` | | What was removed and what stays, for people. | ## `GpuSupport` record The GPU options of one runtime: empty, with the reason, when it has none. Returned by [`Spaces.gpu_support`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `runtime` | `String` | | `lume`, `qemu`, `container`, `gvisor`, `fleet`, a provider's name. | | `options` | [`Vec`](#gpuoption-record) | | Its options. | | `reason` | `String` | | Why it has none. | ## `GpuOption` record One GPU option of a runtime (see `Spaces.gpu_support`). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | What to pass as `gpu`. | | `label` | `String` | | How a person names it ("GPU acceleration"). | | `experimental` | `bool` | | Experimental. | | `supported` | `bool` | | Works on this host now. | | `reason` | `String` | | Why not, one short line (empty when supported). | | `learn_more` / `learnMore` | `Option` | | A page that explains it. | | `usd_per_hour` / `usdPerHour` | `Option` | | Estimated cost per hour while it runs (cloud GPU types). | --- # Space A connected Space: shell, files, tools, windows and the hotspot. Connected machine primitives; missing spacesd features return `CapabilityMissing`. ## `Space` A connected Space. Every primitive checks the spacesd feature it needs first and fails with `CapabilityMissing` naming it. Returned by [`Spaces.space`](). | Method | Description | | --- | --- | | [`bash`](#spacebash) | Runs a shell command (`/bin/sh -c`, `cmd /C` on Windows). | | [`call_tool`](#spacecall_tool) | Calls a tool of the Space's MCP service with JSON arguments. | | [`capabilities_json`](#spacecapabilities_json) | `GetCapabilities` of the spacesd, as proto3 JSON. | | [`download`](#spacedownload) | Copies a guest file or folder into `dest_dir` on this host. | | [`home`](#spacehome) | The Space user's home directory. | | [`hotspot_status`](#spacehotspot_status) | This Space's hotspot status. | | [`list_tools`](#spacelist_tools) | Tools of the Space's MCP service (`driver`, the default). | | [`request_site_login`](#spacerequest_site_login) | Signs in to a site in this Space's browser with a password the user saved in the Cua Keyvault. | | [`screenshot`](#spacescreenshot) | Captures the Space's display: PNG at full size on the primary display unless `options` say otherwise (for a small preview, `Space.thumbnail` reads the shared cache instead). | | [`send_file`](#spacesend_file) | Drops a host file or folder into the Space's `~/Downloads[/subdir]` (TeleportService.ReceiveFiles, per-file SHA-256). | | [`set_presence_settings`](#spaceset_presence_settings) | Sets this Space's presence settings. | | [`share`](#spaceshare) | Lets `who` (a verified email or an account id) watch (`viewer`, the default) or use (`editor`) this Space through the relay. | | [`shares`](#spaceshares) | Who this Space is shared with, and who of them is connected now. | | [`start_hotspot`](#spacestart_hotspot) | Starts the reverse-SOCKS hotspot: this host serves the Space's egress. | | [`stop_hotspot`](#spacestop_hotspot) | Stops this Space's hotspot. | | [`supports`](#spacesupports) | Whether the spacesd supports `feature`. | | [`unshare`](#spaceunshare) | Stops sharing this Space with `who` at once, or with everyone when `who` is `None`. | | [`upload`](#spaceupload) | Copies a host file or folder to `dest` in the Space (default: the home directory). | | [`write`](#spacewrite) | Writes bytes to a guest path (parents created, verified by SHA-256). | | [`agent_events`]() | Normalized events after `cursor` (0: the start), as the `agent_events` JSON (`events`, `cursor`, `caught_up`, `status`). | | [`agent_interrupt`]() | Cancels the run's turn in flight; the session stays open. | | [`agent_list`]() | Every agent run in this Space. | | [`agent_message`]() | Sends a follow-up message to a run. | | [`agent_start`]() | Starts an agent CLI (`claude-code`, `openai-codex`, ...) with a prompt, as a detached spacesd process. | | [`agent_status`]() | A run's status with the last `tail` lines of output. | | [`agent_stop`]() | Stops a run. | | [`app_icon`]() | The icon the guest desktop shows for a window's app (pass the window's `app_name`, `app_id` and `pid`), or `None` when the Space has none: show no icon then, not a placeholder. | | [`app_icons`]() | Icons for many windows' apps at once, in request order (`None`: the Space has no icon for that app; show none). | | [`attach_stream`]() | Attaches to a ticket from `Self.open_stream` and delivers the media socket's encoded frames and audio packets to the sinks as they arrive. | | [`close_stream`]() | Closes a media session opened with `Space.open_stream`. | | [`displays`]() | The Space's displays, primary first, with their resolution in physical pixels. | | [`join_presence`]() | Joins presence. | | [`open_stream`]() | Mints a media session (ticket + WebSocket URL) for another client to attach to. | | [`spacesd`]() | The cua-spacesd client over the connection this Space authenticated. | | [`stream_session`]() | Opens a media session and delivers keyframe-gated encoded frames and audio to the sinks (one delivery thread; decoding stays with the caller). | | [`teleport`]() | Teleports `app`'s session into this Space. | | [`teleport_manifest`]() | What teleporting `app` (`full` or `tabs`) would move from this host. | | [`thumbnail`]() | The Space's latest thumbnail (a small JPEG of its primary display) from the cache every client on this machine shares: returned at once when it is younger than `max_age_ms` (unset: any age), else captured fresh through cua-spacesd and kept for the next caller. | | [`usage`]() | Memory and storage use now (cheap: poll it at a low rate while a detail is visible). | | [`websocket_headers`]() | Headers a media WebSocket to this Space needs besides its ticket. | | [`window_thumbnail`]() | A small JPEG preview of one of the Space's windows (`window_id` and `epoch` from `windows`), at most `max_dimension` px, reused for a few seconds (the SDK's preview cache); `None` when the guest captured nothing. | | [`windows`]() | Streamable windows, optionally of one app. | | Accessor | Returns | Description | | --- | --- | --- | | `id()` | `String` | Id. | | `info()` | [`SpaceInfo`]() | Connection-time snapshot; never refreshed. With spacesd, handshake `os`, `os_name`, `os_pretty_name` replace record values; missing/unknown families clear them. Other fields and non-spacesd services retain registry/discovery values. `Spaces.list()`/`Spaces.resolve()` remain record views. | | `services()` | `Vec` | The MCP services this Space exposes: `driver` when its spacesd has the tool registry, then every declared service (reached over generic MCP; no spacesd needed). | ### `Space.bash` Runs a shell command (`/bin/sh -c`, `cmd /C` on Windows). **Python** ```python async def bash(self, command: str, timeout_ms: Optional[int]) -> SpaceBashResult ``` **TypeScript** ```ts bash(command: string, timeoutMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func bash(command: String, timeoutMs: UInt64?) async throws -> SpaceBashResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun bash(command: String, timeoutMs: ULong?): SpaceBashResult ``` **Rust** ```rust pub async fn bash(&self, command: String, timeout_ms: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `command` | `String` | required | | `timeout_ms` | `Option` | required | **Returns** [`SpaceBashResult`](#spacebashresult-record) · **Async** · **Raises** [`CuaError`]() ### `Space.call_tool` Calls a tool of the Space's MCP service with JSON arguments. **Python** ```python async def call_tool(self, tool: str, arguments_json: Optional[str], service: Optional[str], timeout_ms: Optional[int]) -> SpaceToolResult ``` **TypeScript** ```ts callTool(tool: string, argumentsJson: string | undefined, service: string | undefined, timeoutMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func callTool(tool: String, argumentsJson: String?, service: String?, timeoutMs: UInt64?) async throws -> SpaceToolResult ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun callTool(tool: String, argumentsJson: String?, service: String?, timeoutMs: ULong?): SpaceToolResult ``` **Rust** ```rust pub async fn call_tool(&self, tool: String, arguments_json: Option, service: Option, timeout_ms: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `tool` | `String` | required | | `arguments_json` | `Option` | required | | `service` | `Option` | required | | `timeout_ms` | `Option` | required | **Returns** [`SpaceToolResult`](#spacetoolresult-record) · **Async** · **Raises** [`CuaError`]() ### `Space.capabilities_json` `GetCapabilities` of the spacesd, as proto3 JSON. **Python** ```python def capabilities_json(self) -> str ``` **TypeScript** ```ts capabilitiesJson(): string ``` **Swift** ```swift func capabilitiesJson() throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun capabilitiesJson(): String ``` **Rust** ```rust pub fn capabilities_json(&self) -> Result ``` **Returns** `String` · **Raises** [`CuaError`]() ### `Space.download` Copies a guest file or folder into `dest_dir` on this host. Existing host files are never replaced: a name that is taken lands as `name (1).ext`, and the report's `dest` is the path used. Use `download_file` on the Space's environment to write one exact path. **Python** ```python async def download(self, remote_path: str, dest_dir: str) -> SpaceTransferReport ``` **TypeScript** ```ts download(remotePath: string, destDir: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func download(remotePath: String, destDir: String) async throws -> SpaceTransferReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun download(remotePath: String, destDir: String): SpaceTransferReport ``` **Rust** ```rust pub async fn download(&self, remote_path: String, dest_dir: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `remote_path` | `String` | required | | `dest_dir` | `String` | required | **Returns** [`SpaceTransferReport`](#spacetransferreport-record) · **Async** · **Raises** [`CuaError`]() ### `Space.home` The Space user's home directory. **Python** ```python async def home(self) -> str ``` **TypeScript** ```ts home(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func home() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun home(): String ``` **Rust** ```rust pub async fn home(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Space.hotspot_status` This Space's hotspot status. **Python** ```python async def hotspot_status(self) -> List[SpaceHotspotStatus] ``` **TypeScript** ```ts hotspotStatus(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func hotspotStatus() async throws -> [SpaceHotspotStatus] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun hotspotStatus(): List ``` **Rust** ```rust pub async fn hotspot_status(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#spacehotspotstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Space.list_tools` Tools of the Space's MCP service (`driver`, the default). **Python** ```python async def list_tools(self, service: Optional[str]) -> List[SpaceToolInfo] ``` **TypeScript** ```ts listTools(service: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func listTools(service: String?) async throws -> [SpaceToolInfo] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun listTools(service: String?): List ``` **Rust** ```rust pub async fn list_tools(&self, service: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `service` | `Option` | required | **Returns** [`Vec`](#spacetoolinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Space.request_site_login` Signs in to a site in this Space's browser with a password the user saved in the Cua Keyvault. The first call files a request (`status == "pending"` with a `request_id`); the user approves it in Cua, then call again with that `request_id` and the Keyvault types the login through the Space's cua-driver (`status == "filled"`). A declined request is an error. The password never reaches the caller. **Python** ```python async def request_site_login(self, url: str, options: Optional[SiteLoginOptions]) -> SiteLoginReport ``` **TypeScript** ```ts requestSiteLogin(url: string, options: SiteLoginOptions | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func requestSiteLogin(url: String, options: SiteLoginOptions?) async throws -> SiteLoginReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun requestSiteLogin(url: String, options: SiteLoginOptions?): SiteLoginReport ``` **Rust** ```rust pub async fn request_site_login(&self, url: String, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `url` | `String` | required | | `options` | [`Option`](#siteloginoptions-record) | required | **Returns** [`SiteLoginReport`](#siteloginreport-record) · **Async** · **Raises** [`CuaError`]() ### `Space.screenshot` Captures the Space's display: PNG at full size on the primary display unless `options` say otherwise (for a small preview, `Space.thumbnail` reads the shared cache instead). Fails with `CapabilityMissing` (`spacesd`) when the image runs no cua-spacesd. **Python** ```python async def screenshot(self, options: Optional[ScreenshotOptions]) -> Screenshot ``` **TypeScript** ```ts screenshot(options: ScreenshotOptions | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func screenshot(options: ScreenshotOptions?) async throws -> Screenshot ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun screenshot(options: ScreenshotOptions?): Screenshot ``` **Rust** ```rust pub async fn screenshot(&self, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`Option`]() | required | **Returns** [`Screenshot`]() · **Async** · **Raises** [`CuaError`]() ([`CapabilityMissing`]()) ### `Space.send_file` Drops a host file or folder into the Space's `~/Downloads[/subdir]` (TeleportService.ReceiveFiles, per-file SHA-256). **Python** ```python async def send_file(self, local_path: str, options: SpaceSendFileOptions) -> SpaceSendFileReport ``` **TypeScript** ```ts sendFile(localPath: string, options: SpaceSendFileOptions, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func sendFile(localPath: String, options: SpaceSendFileOptions) async throws -> SpaceSendFileReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun sendFile(localPath: String, options: SpaceSendFileOptions): SpaceSendFileReport ``` **Rust** ```rust pub async fn send_file(&self, local_path: String, options: SpaceSendFileOptions) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `local_path` | `String` | required | | `options` | [`SpaceSendFileOptions`](#spacesendfileoptions-record) | required | **Returns** [`SpaceSendFileReport`](#spacesendfilereport-record) · **Async** · **Raises** [`CuaError`]() ### `Space.set_presence_settings` Sets this Space's presence settings. `cursor_probe`: whether cua-spacesd may read the real cursor shape by briefly moving the idle guest pointer to a participant's position (on by default); `None` leaves it unchanged. **Python** ```python async def set_presence_settings(self, cursor_probe: Optional[bool] = None) -> None ``` **TypeScript** ```ts setPresenceSettings(cursorProbe: boolean | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func setPresenceSettings(cursorProbe: Bool? = nil) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun setPresenceSettings(cursorProbe: Boolean? = null) ``` **Rust** ```rust pub async fn set_presence_settings(&self, cursor_probe: Option) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `cursor_probe` | `Option` | `None` | **Async** · **Raises** [`CuaError`]() ### `Space.share` Lets `who` (a verified email or an account id) watch (`viewer`, the default) or use (`editor`) this Space through the relay. A Space that is not a host is attached to the relay on its first share. **Python** ```python async def share(self, who: str, role: Optional[str]) -> SpaceShares ``` **TypeScript** ```ts share(who: string, role: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func share(who: String, role: String?) async throws -> SpaceShares ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun share(who: String, role: String?): SpaceShares ``` **Rust** ```rust pub async fn share(&self, who: String, role: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `who` | `String` | required | | `role` | `Option` | required | **Returns** [`SpaceShares`](#spaceshares-record) · **Async** · **Raises** [`CuaError`]() ### `Space.shares` Who this Space is shared with, and who of them is connected now. **Python** ```python async def shares(self) -> SpaceShares ``` **TypeScript** ```ts shares(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func shares() async throws -> SpaceShares ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun shares(): SpaceShares ``` **Rust** ```rust pub async fn shares(&self) -> Result ``` **Returns** [`SpaceShares`](#spaceshares-record) · **Async** · **Raises** [`CuaError`]() ### `Space.start_hotspot` Starts the reverse-SOCKS hotspot: this host serves the Space's egress. `set_system_proxy` points the guest's proxy settings at it (default false here). **Python** ```python async def start_hotspot(self, set_system_proxy: Optional[bool], bypass: Optional[List[str]]) -> SpaceHotspotStatus ``` **TypeScript** ```ts startHotspot(setSystemProxy: boolean | undefined, bypass: Array | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func startHotspot(setSystemProxy: Bool?, bypass: [String]?) async throws -> SpaceHotspotStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun startHotspot(setSystemProxy: Boolean?, bypass: List?): SpaceHotspotStatus ``` **Rust** ```rust pub async fn start_hotspot(&self, set_system_proxy: Option, bypass: Option>) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `set_system_proxy` | `Option` | required | | `bypass` | `Option>` | required | **Returns** [`SpaceHotspotStatus`](#spacehotspotstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Space.stop_hotspot` Stops this Space's hotspot. Returns the stopped Space ids. **Python** ```python async def stop_hotspot(self) -> List[str] ``` **TypeScript** ```ts stopHotspot(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func stopHotspot() async throws -> [String] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun stopHotspot(): List ``` **Rust** ```rust pub async fn stop_hotspot(&self) -> Result, CuaError> ``` **Returns** `Vec` · **Async** · **Raises** [`CuaError`]() ### `Space.supports` Whether the spacesd supports `feature`. **Python** ```python def supports(self, feature: str) -> bool ``` **TypeScript** ```ts supports(feature: string): boolean ``` **Swift** ```swift func supports(feature: String) -> Bool ``` **Kotlin** ```kotlin fun supports(feature: String): Boolean ``` **Rust** ```rust pub fn supports(&self, feature: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `feature` | `String` | required | **Returns** `bool` ### `Space.unshare` Stops sharing this Space with `who` at once, or with everyone when `who` is `None`. **Python** ```python async def unshare(self, who: Optional[str]) -> SpaceShares ``` **TypeScript** ```ts unshare(who: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func unshare(who: String?) async throws -> SpaceShares ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun unshare(who: String?): SpaceShares ``` **Rust** ```rust pub async fn unshare(&self, who: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `who` | `Option` | required | **Returns** [`SpaceShares`](#spaceshares-record) · **Async** · **Raises** [`CuaError`]() ### `Space.upload` Copies a host file or folder to `dest` in the Space (default: the home directory). **Python** ```python async def upload(self, local_path: str, dest: Optional[str]) -> SpaceTransferReport ``` **TypeScript** ```ts upload(localPath: string, dest: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func upload(localPath: String, dest: String?) async throws -> SpaceTransferReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun upload(localPath: String, dest: String?): SpaceTransferReport ``` **Rust** ```rust pub async fn upload(&self, local_path: String, dest: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `local_path` | `String` | required | | `dest` | `Option` | required | **Returns** [`SpaceTransferReport`](#spacetransferreport-record) · **Async** · **Raises** [`CuaError`]() ### `Space.write` Writes bytes to a guest path (parents created, verified by SHA-256). **Python** ```python async def write(self, path: str, content: bytes) -> SpaceWriteReport ``` **TypeScript** ```ts write(path: string, content: ArrayBuffer, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func write(path: String, content: Data) async throws -> SpaceWriteReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun write(path: String, content: ByteArray): SpaceWriteReport ``` **Rust** ```rust pub async fn write(&self, path: String, content: Vec) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `content` | `Vec` | required | **Returns** [`SpaceWriteReport`](#spacewritereport-record) · **Async** · **Raises** [`CuaError`]() ## `SpaceBashResult` record Output of `Space.bash`. Returned by [`Space.bash`](#spacebash). | Field | Type | Default | Description | | --- | --- | --- | --- | | `stdout` | `String` | | Stdout. | | `stderr` | `String` | | Stderr. | | `exit_code` / `exitCode` | `Option` | | Exit code (absent when signalled or timed out). | | `signal` | `Option` | | Signal name, when killed by one. | | `timed_out` / `timedOut` | `bool` | | Hit the timeout. | | `error` | `Option` | | Launch error. | | `rendered` | `String` | | The `space_bash` tool text (`stdout`, `[stderr]`, `[exit N]`). | ## `SpaceWriteReport` record Result of `Space.write`. Returned by [`Space.write`](#spacewrite). | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | Guest path. | | `bytes` | `u64` | | Bytes. | | `sha256` | `String` | | SHA-256 verified in the Space. | ## `SpaceTransferReport` record Result of `Space.upload` / `Space.download`. Returned by [`Space.download`](#spacedownload), [`Space.upload`](#spaceupload). | Field | Type | Default | Description | | --- | --- | --- | --- | | `source` | `String` | | Source. | | `dest` | `String` | | Destination. | | `kind` | `String` | | `file` or `folder`. | | `files` | `u64` | | Files written. | | `directories` | `u64` | | Directories created (uploads). | | `bytes` | `u64` | | Total bytes. | | `sha256` | `Option` | | SHA-256 (single files). | | `verified` | `bool` | | Every file verified by SHA-256. | ## `SpaceSendFileOptions` record Options for `Space.send_file`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `target_directory` / `targetDirectory` | `Option` | `None` | Downloads subdirectory (`None` = `~/Downloads`), or a `~/Downloads/…` path. | | `respect_ignore_files` / `respectIgnoreFiles` | `bool` | `true` | Honor ignore files when sending a folder. | | `conflict` | `Option` | `None` | When a file of the same name is already there: `rename` (default, keeps both as `name (1).ext`), `skip`, or `overwrite` to replace it. | ## `SpaceSendFileReport` record Result of `Space.send_file`. Returned by [`Space.send_file`](#spacesend_file). | Field | Type | Default | Description | | --- | --- | --- | --- | | `source` | `String` | | Host source. | | `destination` | `String` | | Transfer root in the guest. | | `dest` | `String` | | Where the file or folder landed. | | `kind` | `String` | | `file` or `folder`. | | `files` | [`Vec`](#spacesentfile-record) | | Files placed. | | `bytes` | `u64` | | Total bytes. | | `skipped_by_ignorefiles` / `skippedByIgnorefiles` | `Vec` | | Paths left out by ignore rules (first 200). | | `skipped_by_guest` / `skippedByGuest` | `Vec` | | Paths the guest skipped. | | `verified` | `bool` | | Every hash matched. | ## `SpaceSentFile` record One file placed by `Space.send_file`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | Guest path. | | `size` | `u64` | | Bytes. | | `sha256` | `String` | | SHA-256, verified on both ends. | ## `SpaceWindow` record A streamable window. Returned by [`Space.windows`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `window_id` / `windowId` | `String` | | Window handle. | | `epoch` | `u64` | | Generation. | | `title` | `String` | | Title. | | `app_name` / `appName` | `String` | | App name. | | `app_id` / `appId` | `String` | | App id. | | `pid` | `u32` | | Pid. | | `bounds` | `Vec` | | `[x, y, width, height]` in logical points. | | `on_screen` / `onScreen` | `bool` | | On screen. | | `focused` | `bool` | | Focused. | | `available` | `bool` | | Streamable now. | | `limitation` | `String` | | Why not. | ## `SpaceToolInfo` record A tool of a Space's MCP service. Returned by [`Space.list_tools`](#spacelist_tools). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Name. | | `description` | `String` | | Description. | | `input_schema_json` / `inputSchemaJson` | `String` | | Input JSON Schema. | | `read_only` / `readOnly` | `bool` | | Read-only. | | `destructive` | `bool` | | Destructive. | ## `SpaceToolResult` record The result of a tool call (MCP content as JSON). Returned by [`Space.call_tool`](#spacecall_tool), [`Spaces.call_tool_json`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `content_json` / `contentJson` | `String` | | MCP content parts as a JSON array. | | `structured_json` / `structuredJson` | `Option` | | Structured content as JSON, when present. | | `is_error` / `isError` | `bool` | | The tool failed. | | `text` | `String` | | Concatenated text parts. | | `meta_json` / `metaJson` | `Option` | | The result's `_meta` as JSON, when present. | ## `SpaceHotspotStatus` record Hotspot state. Returned by [`Space.hotspot_status`](#spacehotspot_status), [`Space.start_hotspot`](#spacestart_hotspot). | Field | Type | Default | Description | | --- | --- | --- | --- | | `space` | `String` | | Space. | | `state` | `String` | | `stopped`, `waiting_for_peer` or `active`. | | `hotspot_id` / `hotspotId` | `String` | | Hotspot id. | | `socks_address` / `socksAddress` | `String` | | Guest SOCKS address. | | `active_connections` / `activeConnections` | `u32` | | Open relayed connections. | | `bytes_out` / `bytesOut` | `u64` | | Bytes out of the guest. | | `bytes_in` / `bytesIn` | `u64` | | Bytes into the guest. | | `served_here` / `servedHere` | `bool` | | Served by the process that owns the Spaces runtime. | ## `SiteLoginOptions` record Options for `Space.request_site_login`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `username` | `Option` | `None` | Which saved username, when the site has several. | | `agent` | `Option` | `None` | The persistent agent asking (shown to the user in the approval). | | `session` | `Option` | `None` | cua-driver lifecycle session of the tab to sign in. | | `target_id` / `targetId` | `Option` | `None` | cua-driver browser target id (from `get_browser_state`). | | `tab_id` / `tabId` | `Option` | `None` | cua-driver tab id (from `get_browser_state`). | | `request_id` / `requestId` | `Option` | `None` | The request id a previous call returned (the retry after approval). | | `wait_secs` / `waitSecs` | `Option` | `None` | Seconds to wait for the user's decision on a retry (default 20, at most 120). | ## `SiteLoginReport` record What `Space.request_site_login` did. Never the password. Returned by [`Space.request_site_login`](#spacerequest_site_login). | Field | Type | Default | Description | | --- | --- | --- | --- | | `status` | `String` | | `pending` (the user has not approved yet) or `filled`. | | `request_id` / `requestId` | `Option` | | The Keyvault request to retry with, while pending. | | `site` | `Option` | | Site of the saved login (`github.com`), once filled. | | `origin` | `Option` | | The origin signed in to, once filled. | | `username_hint` / `usernameHint` | `Option` | | The username, masked (`a***@example.test`). | | `submitted` | `bool` | | The form was submitted. | | `page_url` / `pageUrl` | `Option` | | The tab's URL after the fill. | | `session` | `Option` | | The tab it signed in (cua-driver ids), to keep using it. | | `target_id` / `targetId` | `Option` | | See `session`. | | `tab_id` / `tabId` | `Option` | | See `session`. | ## `SpaceRelayRegistration` record A Space published on the relay (`Spaces.relay_register`). Returned by [`Spaces.relay_register`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `space` | `String` | | The Space. | | `relay_space` / `relaySpace` | `String` | | How the account's other devices reach it: `relay:`. | | `machine` | `String` | | The relay machine id. | | `url` | `String` | | The relay URL of the machine. | | `online` | `bool` | | Connected to the relay now. | ## `SpaceShareEntry` record One account a Space is shared with. | Field | Type | Default | Description | | --- | --- | --- | --- | | `who` | `String` | | The email or account id, as shared. | | `role` | `String` | | `viewer` or `editor`. | | `connected` | `bool` | | Connected through the relay right now. | ## `SpaceShares` record Who a Space is shared with. Returned by [`Space.share`](#spaceshare), [`Space.shares`](#spaceshares), [`Space.unshare`](#spaceunshare). | Field | Type | Default | Description | | --- | --- | --- | --- | | `space` | `String` | | The Space. | | `machine` | `String` | | The relay machine it is shared as (empty when not shared). | | `invitee_space` / `inviteeSpace` | `String` | | What the people it is shared with open (`relay:`). | | `url` | `String` | | The relay URL of the machine. | | `online` | `bool` | | The machine is on the relay now. | | `shares` | [`Vec`](#spaceshareentry-record) | | The accounts and their roles. | ## `SpacesHost` record One of your machines that provides Spaces (`Spaces.hosts`). Returned by [`Spaces.hosts`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | What `on="host:"` takes: the relay machine id, or the name a direct host was added as. | | `name` | `String` | | Its name. | | `via` | `String` | | `relay` or `direct`. | | `online` | `bool` | | It answered. | | `os` | `String` | | Its operating system (`macos`, `linux`, `windows`), when it answered. | | `limits` | [`Vec`](#spaceshostlimit-record) | | Its limits, when it answered. | ## `SpacesHostLimit` record One limit of a host that provides Spaces and how much of it is used. | Field | Type | Default | Description | | --- | --- | --- | --- | | `resource` | `String` | | `spaces` (every Space it provides) or `macos_vms`. | | `used` | `u32` | | In use now. | | `limit` | `u32` | | The limit (0: none). | | `reason` | `String` | | Why the limit exists, for people. | --- # Cua Volume The versioned volume every Space and agent shares: files, history, grants, requests and audit. Cua Volume methods on the `Spaces` registry. Access is checked by the runtime; pass a `DriveView` to see the drive as a persistent agent does. Grants and approvals ask the user for presence. Guide: [Cua Volume](). ## Drive methods Methods of [`Spaces`](). ### `Spaces.volume_ls` `volume_ls`: a folder's immediate children (default the root). **Python** ```python async def volume_ls(self, path: Optional[str], view: Optional[DriveView]) -> DriveListing ``` **TypeScript** ```ts volumeLs(path: string | undefined, view: DriveView | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeLs(path: String?, view: DriveView?) async throws -> DriveListing ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeLs(path: String?, view: DriveView?): DriveListing ``` **Rust** ```rust pub async fn volume_ls(&self, path: Option, view: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `Option` | required | | `view` | [`Option`](#driveview-record) | required | **Returns** [`DriveListing`](#drivelisting-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_read` `volume_read`: a file (or one of its versions). **Python** ```python async def volume_read(self, path: str, version: Optional[str], view: Optional[DriveView]) -> DriveFile ``` **TypeScript** ```ts volumeRead(path: string, version: string | undefined, view: DriveView | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeRead(path: String, version: String?, view: DriveView?) async throws -> DriveFile ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeRead(path: String, version: String?, view: DriveView?): DriveFile ``` **Rust** ```rust pub async fn volume_read(&self, path: String, version: Option, view: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `version` | `Option` | required | | `view` | [`Option`](#driveview-record) | required | **Returns** [`DriveFile`](#drivefile-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_write` `volume_write`: a new version of a file. `if_etag` makes it a compare-and-swap, `create_only` makes it create-only. **Python** ```python async def volume_write(self, path: str, content: bytes, if_etag: Optional[str], create_only: bool, view: Optional[DriveView]) -> DriveObject ``` **TypeScript** ```ts volumeWrite(path: string, content: ArrayBuffer, ifEtag: string | undefined, createOnly: boolean, view: DriveView | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeWrite(path: String, content: Data, ifEtag: String?, createOnly: Bool, view: DriveView?) async throws -> DriveObject ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeWrite(path: String, content: ByteArray, ifEtag: String?, createOnly: Boolean, view: DriveView?): DriveObject ``` **Rust** ```rust pub async fn volume_write(&self, path: String, content: Vec, if_etag: Option, create_only: bool, view: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `content` | `Vec` | required | | `if_etag` | `Option` | required | | `create_only` | `bool` | required | | `view` | [`Option`](#driveview-record) | required | **Returns** [`DriveObject`](#driveobject-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_delete` `volume_delete`: a delete marker (history stays). **Python** ```python async def volume_delete(self, path: str, if_etag: Optional[str], view: Optional[DriveView]) -> None ``` **TypeScript** ```ts volumeDelete(path: string, ifEtag: string | undefined, view: DriveView | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeDelete(path: String, ifEtag: String?, view: DriveView?) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeDelete(path: String, ifEtag: String?, view: DriveView?) ``` **Rust** ```rust pub async fn volume_delete(&self, path: String, if_etag: Option, view: Option) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `if_etag` | `Option` | required | | `view` | [`Option`](#driveview-record) | required | **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_history` `volume_history`: a file's versions, newest first. **Python** ```python async def volume_history(self, path: str, view: Optional[DriveView]) -> List[DriveVersion] ``` **TypeScript** ```ts volumeHistory(path: string, view: DriveView | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func volumeHistory(path: String, view: DriveView?) async throws -> [DriveVersion] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeHistory(path: String, view: DriveView?): List ``` **Rust** ```rust pub async fn volume_history(&self, path: String, view: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `view` | [`Option`](#driveview-record) | required | **Returns** [`Vec`](#driveversion-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_restore` `volume_restore`: makes an old version current again. **Python** ```python async def volume_restore(self, path: str, version: str, view: Optional[DriveView]) -> DriveObject ``` **TypeScript** ```ts volumeRestore(path: string, version: string, view: DriveView | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeRestore(path: String, version: String, view: DriveView?) async throws -> DriveObject ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeRestore(path: String, version: String, view: DriveView?): DriveObject ``` **Rust** ```rust pub async fn volume_restore(&self, path: String, version: String, view: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `version` | `String` | required | | `view` | [`Option`](#driveview-record) | required | **Returns** [`DriveObject`](#driveobject-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_grant` `volume_grant`: widens `principal`'s access (`agent:` or `space:`, mode `r` or `rw`). The user confirms with presence. **Python** ```python async def volume_grant(self, principal: str, prefix: str, mode: str, expires_in_secs: Optional[int], note: Optional[str]) -> DriveGrant ``` **TypeScript** ```ts volumeGrant(principal: string, prefix: string, mode: string, expiresInSecs: bigint | undefined, note: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeGrant(principal: String, prefix: String, mode: String, expiresInSecs: UInt64?, note: String?) async throws -> DriveGrant ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeGrant(principal: String, prefix: String, mode: String, expiresInSecs: ULong?, note: String?): DriveGrant ``` **Rust** ```rust pub async fn volume_grant(&self, principal: String, prefix: String, mode: String, expires_in_secs: Option, note: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `principal` | `String` | required | | `prefix` | `String` | required | | `mode` | `String` | required | | `expires_in_secs` | `Option` | required | | `note` | `Option` | required | **Returns** [`DriveGrant`](#drivegrant-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_revoke` `volume_revoke`. **Python** ```python async def volume_revoke(self, grant_id: str) -> DriveGrant ``` **TypeScript** ```ts volumeRevoke(grantId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeRevoke(grantId: String) async throws -> DriveGrant ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeRevoke(grantId: String): DriveGrant ``` **Rust** ```rust pub async fn volume_revoke(&self, grant_id: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `grant_id` | `String` | required | **Returns** [`DriveGrant`](#drivegrant-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_grants` `volume_grants`: live grants (every grant with `all`). **Python** ```python async def volume_grants(self, all: bool) -> List[DriveGrant] ``` **TypeScript** ```ts volumeGrants(all: boolean, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func volumeGrants(all: Bool) async throws -> [DriveGrant] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeGrants(all: Boolean): List ``` **Rust** ```rust pub async fn volume_grants(&self, all: bool) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `all` | `bool` | required | **Returns** [`Vec`](#drivegrant-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_request_access` `volume_request_access`: `agent` asks the user for more access. **Python** ```python async def volume_request_access(self, agent: str, prefix: str, mode: str, reason: Optional[str], in_space: Optional[str]) -> DriveAccessRequest ``` **TypeScript** ```ts volumeRequestAccess(agent: string, prefix: string, mode: string, reason: string | undefined, inSpace: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeRequestAccess(agent: String, prefix: String, mode: String, reason: String?, inSpace: String?) async throws -> DriveAccessRequest ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeRequestAccess(agent: String, prefix: String, mode: String, reason: String?, inSpace: String?): DriveAccessRequest ``` **Rust** ```rust pub async fn volume_request_access(&self, agent: String, prefix: String, mode: String, reason: Option, in_space: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `agent` | `String` | required | | `prefix` | `String` | required | | `mode` | `String` | required | | `reason` | `Option` | required | | `in_space` | `Option` | required | **Returns** [`DriveAccessRequest`](#driveaccessrequest-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_requests` `volume_requests`: requests waiting for the user. **Python** ```python async def volume_requests(self) -> List[DriveAccessRequest] ``` **TypeScript** ```ts volumeRequests(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func volumeRequests() async throws -> [DriveAccessRequest] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeRequests(): List ``` **Rust** ```rust pub async fn volume_requests(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#driveaccessrequest-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_approve` `volume_approve`: the request becomes a grant (with presence). **Python** ```python async def volume_approve(self, request_id: str, expires_in_secs: Optional[int]) -> DriveGrant ``` **TypeScript** ```ts volumeApprove(requestId: string, expiresInSecs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeApprove(requestId: String, expiresInSecs: UInt64?) async throws -> DriveGrant ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeApprove(requestId: String, expiresInSecs: ULong?): DriveGrant ``` **Rust** ```rust pub async fn volume_approve(&self, request_id: String, expires_in_secs: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `request_id` | `String` | required | | `expires_in_secs` | `Option` | required | **Returns** [`DriveGrant`](#drivegrant-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_deny` `volume_deny`. **Python** ```python async def volume_deny(self, request_id: str) -> None ``` **TypeScript** ```ts volumeDeny(requestId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeDeny(requestId: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeDeny(requestId: String) ``` **Rust** ```rust pub async fn volume_deny(&self, request_id: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `request_id` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_audit` `volume_audit`: the newest events (default 50) and whether the log verified. **Python** ```python async def volume_audit(self, limit: Optional[int]) -> DriveAudit ``` **TypeScript** ```ts volumeAudit(limit: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeAudit(limit: UInt32?) async throws -> DriveAudit ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeAudit(limit: UInt?): DriveAudit ``` **Rust** ```rust pub async fn volume_audit(&self, limit: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `limit` | `Option` | required | **Returns** [`DriveAudit`](#driveaudit-record) · **Async** · **Raises** [`CuaError`]() ## `DriveView` record Whose view of the drive a call takes: the user (the default, the whole drive) or a persistent agent in a Space. It only ever narrows. | Field | Type | Default | Description | | --- | --- | --- | --- | | `as_agent` / `asAgent` | `Option` | `None` | A persistent agent's name (`ada`). | | `in_space` / `inSpace` | `Option` | `None` | The Space id the agent is in (`local:work`). | ## `DriveListing` record A folder's listing. Returned by [`Spaces.volume_ls`](#spacesvolume_ls). | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | | | `principal` | `String` | | `user` or `agent:`. | | `entries` | [`Vec`](#driveentry-record) | | | ## `DriveEntry` record One row of a drive listing. | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | Key (folders end in `/`). | | `name` | `String` | | | | `folder` | `bool` | | | | `size` | `u64` | | | | `modified_ms` / `modifiedMs` | `u64` | | | | `etag` | `String` | | | | `mode` | `String` | | `r` or `rw`: what this view may do there. | | `sync` | [`Option`](#drivefilesync-record) | | A file's sync state, when it has something to say (uploading, conflicted, or last written by another device). | ## `DriveFile` record A file's content and version. Returned by [`Spaces.volume_read`](#spacesvolume_read). | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | | | `size` | `u64` | | | | `etag` | `String` | | | | `version` | `String` | | | | `modified_ms` / `modifiedMs` | `u64` | | | | `content` | `Vec` | | | | `sync` | [`Option`](#drivefilesync-record) | | Its sync state, while the drive's services run. | ## `DriveObject` record A written version. Returned by [`Spaces.volume_restore`](#spacesvolume_restore), [`Spaces.volume_write`](#spacesvolume_write). | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | | | `size` | `u64` | | | | `etag` | `String` | | | | `version` | `String` | | | | `modified_ms` / `modifiedMs` | `u64` | | | ## `DriveVersion` record One version in a file's history. Returned by [`Spaces.volume_history`](#spacesvolume_history). | Field | Type | Default | Description | | --- | --- | --- | --- | | `version` | `String` | | | | `size` | `u64` | | | | `modified_ms` / `modifiedMs` | `u64` | | | | `deleted` | `bool` | | | | `latest` | `bool` | | | ## `DriveGrant` record A widening of an agent's or a Space's access. Returned by [`Spaces.volume_approve`](#spacesvolume_approve), [`Spaces.volume_grant`](#spacesvolume_grant), [`Spaces.volume_grants`](#spacesvolume_grants), [`Spaces.volume_revoke`](#spacesvolume_revoke). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `principal` | `String` | | | | `prefix` | `String` | | | | `mode` | `String` | | `r` or `rw`. | | `created_ms` / `createdMs` | `u64` | | | | `expires_ms` / `expiresMs` | `Option` | | | | `revoked` | `bool` | | | | `note` | `String` | | | ## `DriveAccessRequest` record An agent's request for more access, waiting for the user. Returned by [`Spaces.volume_request_access`](#spacesvolume_request_access), [`Spaces.volume_requests`](#spacesvolume_requests). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `principal` | `String` | | | | `prefix` | `String` | | | | `mode` | `String` | | | | `reason` | `String` | | | | `created_ms` / `createdMs` | `u64` | | | ## `DriveAudit` record The newest audit events and whether the hash chain verified. Returned by [`Spaces.volume_audit`](#spacesvolume_audit). | Field | Type | Default | Description | | --- | --- | --- | --- | | `events` | [`Vec`](#driveauditevent-record) | | | | `verified` | `bool` | | | | `error` | `Option` | | | ## `DriveAuditEvent` record One audit event. | Field | Type | Default | Description | | --- | --- | --- | --- | | `seq` | `u64` | | | | `ts_ms` / `tsMs` | `u64` | | | | `principal` | `String` | | | | `action` | `String` | | | | `path` | `String` | | | | `detail` | `String` | | | ## `DriveFileSync` record One file's sync state. | Field | Type | Default | Description | | --- | --- | --- | --- | | `state` | `String` | | `synced`, `pending_upload` (not yet in storage: other devices do not see it), `conflict` (a write lost to a later one, kept at `conflict_path`) or `conflict_copy` (the kept copy of a losing write). | | `written_by` / `writtenBy` | `Option` | | The device that last wrote it, when another one did. | | `conflict_path` / `conflictPath` | `Option` | | | --- # Cua Volume storage and sync Where the drive keeps its bytes, the volume on this machine and in Spaces, sync across devices, and the block cache. Runtime methods of the drive on the `Spaces` registry: Settings > Storage, the mount, sync state and the cache. `volume_sync_status` also lists the volume mounted in each Space. Files: [Cua Volume](). ## Storage, the volume, sync and the cache Methods of [`Spaces`](). ### `Spaces.volume_storage` `volume_storage`: where the drive keeps its bytes. **Python** ```python async def volume_storage(self) -> DriveStorage ``` **TypeScript** ```ts volumeStorage(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeStorage() async throws -> DriveStorage ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeStorage(): DriveStorage ``` **Rust** ```rust pub async fn volume_storage(&self) -> Result ``` **Returns** [`DriveStorage`](#drivestorage-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_storage_set` `volume_storage_set`: tests, and unless `dry_run` saves and switches to, a storage backend (live, no restart). **Python** ```python async def volume_storage_set(self, update: DriveStorageUpdate) -> DriveStorageCheck ``` **TypeScript** ```ts volumeStorageSet(update: DriveStorageUpdate, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeStorageSet(update: DriveStorageUpdate) async throws -> DriveStorageCheck ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeStorageSet(update: DriveStorageUpdate): DriveStorageCheck ``` **Rust** ```rust pub async fn volume_storage_set(&self, update: DriveStorageUpdate) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `update` | [`DriveStorageUpdate`](#drivestorageupdate-record) | required | **Returns** [`DriveStorageCheck`](#drivestoragecheck-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_mount_status` `volume_mount_status`: whether the drive is mounted as a volume. **Python** ```python async def volume_mount_status(self) -> DriveMountStatus ``` **TypeScript** ```ts volumeMountStatus(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeMountStatus() async throws -> DriveMountStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeMountStatus(): DriveMountStatus ``` **Rust** ```rust pub async fn volume_mount_status(&self) -> Result ``` **Returns** [`DriveMountStatus`](#drivemountstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_mount` `volume_mount`: turns the mount on (kept across restarts) and mounts. **Python** ```python async def volume_mount(self) -> DriveMountStatus ``` **TypeScript** ```ts volumeMount(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeMount() async throws -> DriveMountStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeMount(): DriveMountStatus ``` **Rust** ```rust pub async fn volume_mount(&self) -> Result ``` **Returns** [`DriveMountStatus`](#drivemountstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_unmount` `volume_unmount`: turns the mount off; pending uploads land first. **Python** ```python async def volume_unmount(self) -> DriveMountStatus ``` **TypeScript** ```ts volumeUnmount(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeUnmount() async throws -> DriveMountStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeUnmount(): DriveMountStatus ``` **Rust** ```rust pub async fn volume_unmount(&self) -> Result ``` **Returns** [`DriveMountStatus`](#drivemountstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_sync_status` `volume_sync_status`: devices, pending uploads and conflicts. **Python** ```python async def volume_sync_status(self) -> DriveSyncStatus ``` **TypeScript** ```ts volumeSyncStatus(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeSyncStatus() async throws -> DriveSyncStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeSyncStatus(): DriveSyncStatus ``` **Rust** ```rust pub async fn volume_sync_status(&self) -> Result ``` **Returns** [`DriveSyncStatus`](#drivesyncstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_sync_events` `volume_sync_events`: events after `since_seq`, waiting up to `wait_ms` (at most 30000) for one. **Python** ```python async def volume_sync_events(self, since_seq: Optional[int], wait_ms: Optional[int]) -> DriveSyncEvents ``` **TypeScript** ```ts volumeSyncEvents(sinceSeq: bigint | undefined, waitMs: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeSyncEvents(sinceSeq: UInt64?, waitMs: UInt32?) async throws -> DriveSyncEvents ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeSyncEvents(sinceSeq: ULong?, waitMs: UInt?): DriveSyncEvents ``` **Rust** ```rust pub async fn volume_sync_events(&self, since_seq: Option, wait_ms: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `since_seq` | `Option` | required | | `wait_ms` | `Option` | required | **Returns** [`DriveSyncEvents`](#drivesyncevents-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_sync_resolve` `volume_sync_resolve`: clears a conflict from the list (files stay). **Python** ```python async def volume_sync_resolve(self, path: str) -> None ``` **TypeScript** ```ts volumeSyncResolve(path: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeSyncResolve(path: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeSyncResolve(path: String) ``` **Rust** ```rust pub async fn volume_sync_resolve(&self, path: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_cache_stats` `volume_cache_stats`: the block cache's size, cap and hit rate. **Python** ```python async def volume_cache_stats(self) -> DriveCacheStats ``` **TypeScript** ```ts volumeCacheStats(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeCacheStats() async throws -> DriveCacheStats ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeCacheStats(): DriveCacheStats ``` **Rust** ```rust pub async fn volume_cache_stats(&self) -> Result ``` **Returns** [`DriveCacheStats`](#drivecachestats-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_cache_set` `volume_cache_set`: the cache's size cap (at least 256 MiB). **Python** ```python async def volume_cache_set(self, capacity_bytes: int) -> DriveCacheStats ``` **TypeScript** ```ts volumeCacheSet(capacityBytes: bigint, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeCacheSet(capacityBytes: UInt64) async throws -> DriveCacheStats ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeCacheSet(capacityBytes: ULong): DriveCacheStats ``` **Rust** ```rust pub async fn volume_cache_set(&self, capacity_bytes: u64) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `capacity_bytes` | `u64` | required | **Returns** [`DriveCacheStats`](#drivecachestats-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.volume_cache_clear` `volume_cache_clear`: drops every cached block. **Python** ```python async def volume_cache_clear(self) -> DriveCacheStats ``` **TypeScript** ```ts volumeCacheClear(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func volumeCacheClear() async throws -> DriveCacheStats ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun volumeCacheClear(): DriveCacheStats ``` **Rust** ```rust pub async fn volume_cache_clear(&self) -> Result ``` **Returns** [`DriveCacheStats`](#drivecachestats-record) · **Async** · **Raises** [`CuaError`]() ## `DriveStorage` record Where the drive keeps its bytes (Settings > Storage). Returned by [`Spaces.volume_storage`](#spacesvolume_storage). | Field | Type | Default | Description | | --- | --- | --- | --- | | `backend` | `String` | | `fs` or `s3`. | | `fs_path` / `fsPath` | `String` | | Where the `fs` backend keeps bytes. | | `s3` | [`Option`](#drives3settings-record) | | | | `has_keys` / `hasKeys` | `bool` | | S3 keys are saved (they are never returned). | | `cloud_available` / `cloudAvailable` | `bool` | | Always false in this release: hide the Cua cloud option. | ## `DriveS3Settings` record Where an S3-compatible bucket is (no keys). | Field | Type | Default | Description | | --- | --- | --- | --- | | `endpoint` | `Option` | | `http://127.0.0.1:9000`, `https://.r2.cloudflarestorage.com`; `None` for AWS. | | `region` | `String` | | `us-east-1`; `auto` for R2. | | `bucket` | `String` | | | | `root` | `String` | | A key prefix inside the bucket (may be empty). | | `path_style` / `pathStyle` | `bool` | | Path-style addressing (MinIO and most self-hosted stores). | ## `DriveStorageUpdate` record A storage change, or with `dry_run` a connection test. | Field | Type | Default | Description | | --- | --- | --- | --- | | `backend` | `String` | | `fs` or `s3` (`cloud` is refused). | | `s3` | [`Option`](#drives3settings-record) | | | | `access_key_id` / `accessKeyId` | `Option` | | With `secret_access_key` (both or neither); saved in the credential store. | | `secret_access_key` / `secretAccessKey` | `Option` | | | | `dry_run` / `dryRun` | `bool` | | Only test; change nothing. | ## `DriveStorageCheck` record What a storage test found. Returned by [`Spaces.volume_storage_set`](#spacesvolume_storage_set). | Field | Type | Default | Description | | --- | --- | --- | --- | | `ok` | `bool` | | | | `reachable` | `bool` | | | | `authorized` | `bool` | | | | `versioning` | `bool` | | Bucket versioning is on (required). | | `detail` | `Option` | | A sentence when not ok: what is wrong and how to fix it. | | `problem` | `Option` | | What is wrong, to key the UI on: `unreachable`, `bad_keys`, `forbidden`, `bucket_missing`, `versioning_off`, `path_style_needed`. | | `applied` | `bool` | | Saved and switched live. | ## `DriveMountStatus` record The drive as a volume. Returned by [`Spaces.volume_mount`](#spacesvolume_mount), [`Spaces.volume_mount_status`](#spacesvolume_mount_status), [`Spaces.volume_unmount`](#spacesvolume_unmount). | Field | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | `bool` | | The user's opt-in (off by default). | | `state` | `String` | | `off`, `mounting`, `mounted`, `needs_approval`, `unsupported`, `error`. | | `method` | `String` | | `nfs` (macOS), `fuse` (Linux), `fskit`, `none`. | | `path` | `Option` | | The mount point when mounted ("Show in Finder" opens it). | | `volume_name` / `volumeName` | `String` | | | | `detail` | `Option` | | | | `settings_url` / `settingsUrl` | `Option` | | | ## `DriveSyncStatus` record Sync across devices. Returned by [`Spaces.volume_sync_status`](#spacesvolume_sync_status). | Field | Type | Default | Description | | --- | --- | --- | --- | | `device_id` / `deviceId` | `String` | | | | `device_name` / `deviceName` | `String` | | | | `feed` | `String` | | `live`, `off` (a store on this machine), `offline` (the bucket stopped answering; see `last_error`). | | `poll_interval_ms` / `pollIntervalMs` | `u64` | | | | `last_poll_ms` / `lastPollMs` | `u64` | | | | `last_remote_change_ms` / `lastRemoteChangeMs` | `u64` | | | | `pending_uploads` / `pendingUploads` | `u32` | | | | `pending_bytes` / `pendingBytes` | `u64` | | | | `conflicts` | [`Vec`](#driveconflict-record) | | | | `devices` | [`Vec`](#drivedevice-record) | | | | `last_error` / `lastError` | `Option` | | | | `pending` | [`Vec`](#drivependingupload-record) | | The files still uploading, largest first (at most 100). | | `backend` | `String` | | `fs` (This Mac) or `s3` (your bucket). | | `mount` | `String` | | This machine's mount: `off`, `mounting`, `mounted`, `needs_approval`, `unsupported` or `error`. | | `cache` | [`Option`](#drivecachestats-record) | | The block cache in front of a bucket. | | `volumes` | [`Vec`](#drivespacevolume-record) | | The volume mounted in Spaces. | ## `DrivePendingUpload` record A file waiting to upload. | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | | | `bytes` | `u64` | | | ## `DriveSpaceVolume` record The volume mounted in a Space's guest. | Field | Type | Default | Description | | --- | --- | --- | --- | | `space` | `String` | | | | `mount_path` / `mountPath` | `String` | | `/volume` (Linux) or `~/Cua Volume` (macOS), as the guest sees it. | | `backend` | `String` | | `fs` (FUSE) or `nfs`. | | `principal` | `String` | | Whose view it shows: `space:`, or `agent:` while a persistent agent runs there. | ## `DriveConflict` record A write that lost to a later one, kept in history and as a copy. | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | | | `conflict_path` / `conflictPath` | `String` | | | | `winner_device` / `winnerDevice` | `String` | | | | `loser_device` / `loserDevice` | `String` | | | | `winner_version` / `winnerVersion` | `String` | | | | `loser_version` / `loserVersion` | `String` | | | | `ts_ms` / `tsMs` | `u64` | | | ## `DriveDevice` record A device sharing the drive's bucket. | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `name` | `String` | | | | `this_device` / `thisDevice` | `bool` | | | | `last_seen_ms` / `lastSeenMs` | `u64` | | | | `last_change_ms` / `lastChangeMs` | `u64` | | | | `changes` | `u64` | | | ## `DriveSyncEvents` record Events after a sequence number, and the sequence to ask after next. Returned by [`Spaces.volume_sync_events`](#spacesvolume_sync_events). | Field | Type | Default | Description | | --- | --- | --- | --- | | `events` | [`Vec`](#drivesyncevent-record) | | | | `next_seq` / `nextSeq` | `u64` | | | ## `DriveSyncEvent` record One sync event. | Field | Type | Default | Description | | --- | --- | --- | --- | | `seq` | `u64` | | | | `ts_ms` / `tsMs` | `u64` | | | | `kind` | `String` | | `remote_change`, `remote_delete`, `upload_started`, `upload_done`, `upload_failed`, `conflict`, `error`. | | `path` | `String` | | | | `device` | `String` | | | | `size` | `u64` | | | | `version` | `String` | | | | `detail` | `String` | | | ## `DriveCacheStats` record The block cache in front of a remote store. Returned by [`Spaces.volume_cache_clear`](#spacesvolume_cache_clear), [`Spaces.volume_cache_set`](#spacesvolume_cache_set), [`Spaces.volume_cache_stats`](#spacesvolume_cache_stats). | Field | Type | Default | Description | | --- | --- | --- | --- | | `dir` | `String` | | | | `size_bytes` / `sizeBytes` | `u64` | | | | `capacity_bytes` / `capacityBytes` | `u64` | | | | `block_bytes` / `blockBytes` | `u64` | | | | `blocks` | `u64` | | | | `hits` | `u64` | | | | `misses` | `u64` | | | | `hit_rate` / `hitRate` | `f64` | | | | `prefetched_bytes` / `prefetchedBytes` | `u64` | | | | `evictions` | `u64` | | | --- # Streams and presence Media sessions of a Space and presence (who is connected, and their cursors). Open a media session for another client, stream frames to callbacks, and join presence. `PresenceView` and the `presence_cursor_art*` functions draw the cursors the same way in every Cua UI. ## Space streams Methods of [`Space`](). ### `Space.open_stream` Mints a media session (ticket + WebSocket URL) for another client to attach to. **Python** ```python async def open_stream(self, options: SpaceStreamOptions) -> SpaceStreamTicket ``` **TypeScript** ```ts openStream(options: SpaceStreamOptions, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func openStream(options: SpaceStreamOptions) async throws -> SpaceStreamTicket ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun openStream(options: SpaceStreamOptions): SpaceStreamTicket ``` **Rust** ```rust pub async fn open_stream(&self, options: SpaceStreamOptions) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`SpaceStreamOptions`](#spacestreamoptions-record) | required | **Returns** [`SpaceStreamTicket`](#spacestreamticket-record) · **Async** · **Raises** [`CuaError`]() ### `Space.attach_stream` Attaches to a ticket from `Self.open_stream` and delivers the media socket's encoded frames and audio packets to the sinks as they arrive. Keyframe gating, loss recovery and decoding are the caller's; `Self.stream_session` does them and ships with Cua Spaces. End the session with `Self.close_stream` and the ticket's `media_session_id`. **Python** ```python async def attach_stream(self, ticket: SpaceStreamTicket, frames: FrameSink, audio: Optional[AudioSink]) -> MediaSession ``` **TypeScript** ```ts attachStream(ticket: SpaceStreamTicket, frames: FrameSink, audio: AudioSink | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func attachStream(ticket: SpaceStreamTicket, frames: FrameSink, audio: AudioSink?) async throws -> MediaSession ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun attachStream(ticket: SpaceStreamTicket, frames: FrameSink, audio: AudioSink?): MediaSession ``` **Rust** ```rust pub async fn attach_stream(&self, ticket: SpaceStreamTicket, frames: Arc, audio: Option>) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `ticket` | [`SpaceStreamTicket`](#spacestreamticket-record) | required | | `frames` | [`FrameSink`]() | required | | `audio` | [`Option`]() | required | **Returns** [`MediaSession`]() · **Async** · **Raises** [`CuaError`]() ### `Space.stream_session` Opens a media session and delivers keyframe-gated encoded frames and audio to the sinks (one delivery thread; decoding stays with the caller). The streaming client ships with Cua Spaces (source-available, FSL-1.1-MIT): without it this raises `HostCapabilityMissing`; `Self.attach_stream` needs no client. **Python** ```python async def stream_session(self, options: SpaceStreamOptions, frames: FrameSink, audio: Optional[AudioSink]) -> SpaceStreamSession ``` **TypeScript** ```ts streamSession(options: SpaceStreamOptions, frames: FrameSink, audio: AudioSink | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func streamSession(options: SpaceStreamOptions, frames: FrameSink, audio: AudioSink?) async throws -> SpaceStreamSession ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun streamSession(options: SpaceStreamOptions, frames: FrameSink, audio: AudioSink?): SpaceStreamSession ``` **Rust** ```rust pub async fn stream_session(&self, options: SpaceStreamOptions, frames: Arc, audio: Option>) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`SpaceStreamOptions`](#spacestreamoptions-record) | required | | `frames` | [`FrameSink`]() | required | | `audio` | [`Option`]() | required | **Returns** [`SpaceStreamSession`](#spacestreamsession) · **Async** · **Raises** [`CuaError`]() ([`HostCapabilityMissing`]()) ### `Space.close_stream` Closes a media session opened with `Space.open_stream`. **Python** ```python async def close_stream(self, media_session_id: str) -> None ``` **TypeScript** ```ts closeStream(mediaSessionId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func closeStream(mediaSessionId: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun closeStream(mediaSessionId: String) ``` **Rust** ```rust pub async fn close_stream(&self, media_session_id: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `media_session_id` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Space.websocket_headers` Headers a media WebSocket to this Space needs besides its ticket. **Python** ```python async def websocket_headers(self) -> List[HttpHeader] ``` **TypeScript** ```ts websocketHeaders(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func websocketHeaders() async throws -> [HttpHeader] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun websocketHeaders(): List ``` **Rust** ```rust pub async fn websocket_headers(&self) -> Result, CuaError> ``` **Returns** [`Vec`]() · **Async** · **Raises** [`CuaError`]() ### `Space.windows` Streamable windows, optionally of one app. **Python** ```python async def windows(self, app: Optional[str]) -> List[SpaceWindow] ``` **TypeScript** ```ts windows(app: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func windows(app: String?) async throws -> [SpaceWindow] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun windows(app: String?): List ``` **Rust** ```rust pub async fn windows(&self, app: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `app` | `Option` | required | **Returns** [`Vec`]() · **Async** · **Raises** [`CuaError`]() ### `Space.join_presence` Joins presence. Waits up to `timeout_ms` (default 10 s) for the roster. **Python** ```python async def join_presence(self, identity: PresenceIdentity, timeout_ms: Optional[int]) -> SpacePresence ``` **TypeScript** ```ts joinPresence(identity: PresenceIdentity, timeoutMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func joinPresence(identity: PresenceIdentity, timeoutMs: UInt64?) async throws -> SpacePresence ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun joinPresence(identity: PresenceIdentity, timeoutMs: ULong?): SpacePresence ``` **Rust** ```rust pub async fn join_presence(&self, identity: PresenceIdentity, timeout_ms: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `identity` | [`PresenceIdentity`](#presenceidentity-record) | required | | `timeout_ms` | `Option` | required | **Returns** [`SpacePresence`](#spacepresence) · **Async** · **Raises** [`CuaError`]() ## `SpaceStreamSession` A live Space media session (see `Space.stream_session`). Returned by [`Space.stream_session`](#spacestream_session). | Method | Description | | --- | --- | | [`close`](#spacestreamsessionclose) | Closes the socket and the media session. | | [`request_keyframe`](#spacestreamsessionrequest_keyframe) | Asks the encoder for a keyframe. | | [`send_text`](#spacestreamsessionsend_text) | Sends a raw JSON control message (for example input events). | | Accessor | Returns | Description | | --- | --- | --- | | `codec()` | `String` | Negotiated codec. | | `is_open()` | `bool` | Whether the socket is still open. | | `media_session_id()` | `String` | Media session id. | | `stats()` | [`SpaceStreamStats`](#spacestreamstats-record) | Delivery counters (zeros once closed). | ### `SpaceStreamSession.close` Closes the socket and the media session. Returns the final counters. **Python** ```python async def close(self) -> SpaceStreamStats ``` **TypeScript** ```ts close(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func close() async throws -> SpaceStreamStats ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun closeStream(): SpaceStreamStats ``` **Rust** ```rust pub async fn close(&self) -> Result ``` **Returns** [`SpaceStreamStats`](#spacestreamstats-record) · **Async** · **Raises** [`CuaError`]() ### `SpaceStreamSession.request_keyframe` Asks the encoder for a keyframe. **Python** ```python def request_keyframe(self) -> None ``` **TypeScript** ```ts requestKeyframe(): void ``` **Swift** ```swift func requestKeyframe() throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun requestKeyframe() ``` **Rust** ```rust pub fn request_keyframe(&self) -> Result<(), CuaError> ``` **Raises** [`CuaError`]() ### `SpaceStreamSession.send_text` Sends a raw JSON control message (for example input events). **Python** ```python def send_text(self, json: str) -> None ``` **TypeScript** ```ts sendText(json: string): void ``` **Swift** ```swift func sendText(json: String) throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun sendText(json: String) ``` **Rust** ```rust pub fn send_text(&self, json: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Raises** [`CuaError`]() ## `SpaceStreamOptions` record What to stream and how. | Field | Type | Default | Description | | --- | --- | --- | --- | | `window_id` / `windowId` | `Option` | `None` | Window handle (wins over `app_name` and `display`). | | `app_name` / `appName` | `Option` | `None` | The largest window of this app. | | `display` | `Option` | `None` | Display id (`None` = primary). | | `codecs` | `Vec` | `[]` | Codecs in preference order (`h264`, `bgra`, `png`; empty = any). | | `max_fps` / `maxFps` | `u32` | `0` | FPS cap (0 = 30). | | `max_dimension` / `maxDimension` | `u32` | `0` | Long-edge cap (0 = native). | | `audio` | `bool` | `false` | Request the paired audio track. | | `ticket_ttl_ms` / `ticketTtlMs` | `Option` | `None` | Ticket lifetime (default 60 s). | | `policy` | `Option` | `None` | Input policy: `view_only` (default), `background_only` (input without activating the target) or `allow_activation`. | ## `SpaceStreamTicket` record A minted media session. Returned by [`Space.open_stream`](#spaceopen_stream). | Field | Type | Default | Description | | --- | --- | --- | --- | | `space` | `String` | | Space id. | | `media_session_id` / `mediaSessionId` | `String` | | Media session id. | | `ws_url` / `wsUrl` | `String` | | Media WebSocket URL, ticket included (the daemon passthrough in daemon mode). | | `ticket` | `String` | | The bare ticket. | | `ticket_expires_at` / `ticketExpiresAt` | `Option` | | Expiry (RFC 3339). | | `codec` | `String` | | `h264`, `bgra` or `png`. | | `wire_version` / `wireVersion` | `u32` | | Media wire version. | | `width` | `u32` | | Initial width. | | `height` | `u32` | | Initial height. | | `needs_headers` / `needsHeaders` | `bool` | | Attaching needs `Space.websocket_headers` (Fleet gateway or the daemon passthrough). | ## `SpaceStreamStats` record Delivery counters of a `SpaceStreamSession`. Returned by [`SpaceStreamSession.close`](#spacestreamsessionclose), [`SpaceStreamSession.stats`](#spacestreamsession). | Field | Type | Default | Description | | --- | --- | --- | --- | | `frames` | `u64` | | Frames delivered. | | `keyframes` | `u64` | | Keyframes delivered. | | `frames_dropped` / `framesDropped` | `u64` | | Frames dropped (sink behind; each forces a keyframe resync). | | `frames_gated` / `framesGated` | `u64` | | Frames held back awaiting a keyframe. | | `keyframe_requests` / `keyframeRequests` | `u64` | | Keyframe requests sent. | | `audio_packets` / `audioPackets` | `u64` | | Audio packets delivered. | | `audio_lost` / `audioLost` | `u64` | | Audio packets reported lost. | | `events` | `u64` | | Control events delivered. | | `malformed` | `u64` | | Malformed messages ignored. | ## `SpacePresence` A joined presence session. Returned by [`Space.join_presence`](#spacejoin_presence). | Method | Description | | --- | --- | | [`leave`](#spacepresenceleave) | Leaves. | | [`me`](#spacepresenceme) | This participant. | | [`next_event`](#spacepresencenext_event) | The next event, or `None` when `timeout_ms` (default 5 s) passes. | | [`roster`](#spacepresenceroster) | Everyone present, with cursors when known. | | [`update_cursor`](#spacepresenceupdate_cursor) | Moves this participant's cursor. | | Accessor | Returns | Description | | --- | --- | --- | | `uses_datagrams()` | `bool` | Whether cursors travel over the QUIC datagram channel (else the `Join` stream and `UpdateCursor`). | | `view()` | [`PresenceView`](#presenceview) | A fresh `PresenceView` seeded with the caller and the roster at join, with the render delay matching the transport. Feed it every event from `next_event`. | ### `SpacePresence.leave` Leaves. **Python** ```python async def leave(self) -> None ``` **TypeScript** ```ts leave(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func leave() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun leave() ``` **Rust** ```rust pub async fn leave(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `SpacePresence.me` This participant. **Python** ```python async def me(self) -> PresenceParticipant ``` **TypeScript** ```ts me(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func me() async throws -> PresenceParticipant ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun me(): PresenceParticipant ``` **Rust** ```rust pub async fn me(&self) -> Result ``` **Returns** [`PresenceParticipant`](#presenceparticipant-record) · **Async** · **Raises** [`CuaError`]() ### `SpacePresence.next_event` The next event, or `None` when `timeout_ms` (default 5 s) passes. **Python** ```python async def next_event(self, timeout_ms: Optional[int]) -> Optional[PresenceEvent] ``` **TypeScript** ```ts nextEvent(timeoutMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func nextEvent(timeoutMs: UInt64?) async throws -> PresenceEvent? ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun nextEvent(timeoutMs: ULong?): PresenceEvent? ``` **Rust** ```rust pub async fn next_event(&self, timeout_ms: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `timeout_ms` | `Option` | required | **Returns** [`Option`](#presenceevent-record) · **Async** · **Raises** [`CuaError`]() ### `SpacePresence.roster` Everyone present, with cursors when known. **Python** ```python async def roster(self) -> List[PresenceMember] ``` **TypeScript** ```ts roster(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func roster() async throws -> [PresenceMember] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun roster(): List ``` **Rust** ```rust pub async fn roster(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#presencemember-record) · **Async** · **Raises** [`CuaError`]() ### `SpacePresence.update_cursor` Moves this participant's cursor. Throttled to 30 Hz, newest wins: a move inside the interval is held and sent at its end, and show, hide and target changes go out at once. Never waits for `next_event`. **Python** ```python async def update_cursor(self, cursor: PresenceCursor) -> None ``` **TypeScript** ```ts updateCursor(cursor: PresenceCursor, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func updateCursor(cursor: PresenceCursor) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun updateCursor(cursor: PresenceCursor) ``` **Rust** ```rust pub async fn update_cursor(&self, cursor: PresenceCursor) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `cursor` | [`PresenceCursor`](#presencecursor-record) | required | **Async** · **Raises** [`CuaError`]() ## `PresenceIdentity` record Who joins presence. | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Stable principal id. | | `display_name` / `displayName` | `String` | | Display name. | | `color` | `String` | `""` | Requested color. | | `agent` | `bool` | `false` | An agent rather than a human. | ## `PresenceParticipant` record A presence participant. Returned by [`PresenceView.participant`](#presenceviewparticipant), [`SpacePresence.me`](#spacepresenceme). | Field | Type | Default | Description | | --- | --- | --- | --- | | `participant_id` / `participantId` | `String` | | Participant id (per join). | | `principal_id` / `principalId` | `String` | | Principal id. | | `display_name` / `displayName` | `String` | | Display name. | | `color` | `String` | | Color. | | `kind` | `String` | | `human` or `agent`. | ## `PresenceMember` record A roster entry. Returned by [`SpacePresence.roster`](#spacepresenceroster). | Field | Type | Default | Description | | --- | --- | --- | --- | | `participant` | [`PresenceParticipant`](#presenceparticipant-record) | | Who. | | `cursor` | [`Option`](#presencecursor-record) | | Their cursor, if known. | ## `PresenceCursor` record A normalized cursor. | Field | Type | Default | Description | | --- | --- | --- | --- | | `display_id` / `displayId` | `String` | `""` | Display id (empty = primary). | | `window_id` / `windowId` | `Option` | `None` | Window handle. | | `x` | `f64` | | X in `[0, 1]`. | | `y` | `f64` | | Y in `[0, 1]`. | | `visible` | `bool` | `true` | Visible. | | `pressed` | `bool` | `false` | A button is down (datagram channel only). | | `shape` | `String` | `"arrow"` | The guest's cursor shape here (server-computed): `arrow`, `text`, `pointer`, `resize_ns`, `resize_ew`, `resize_nesw`, `resize_nwse`, `wait`, `progress`, `not_allowed`, `crosshair`, `grab`, `grabbing`, `move`. Ignored when publishing. | | `shape_source` / `shapeSource` | `String` | `"unspecified"` | How `shape` was determined: `unspecified`, `hit_test`, `system` or `probe`. Ignored when publishing. | | `at_ms` / `atMs` | `f64` | `0.0` | Server time of the sample in milliseconds (0 = unknown). | | `received_ms` / `receivedMs` | `f64` | `0.0` | Local Unix time it was received in milliseconds (0 = unknown). | ## `PresenceEvent` record A presence event. Returned by [`SpacePresence.next_event`](#spacepresencenext_event). | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | `String` | | `joined`, `left`, `cursor_moved`, `shape_changed`, `heartbeat` or `keep_alive`. | | `participant` | [`Option`](#presenceparticipant-record) | | `joined`: who. | | `participant_id` / `participantId` | `Option` | | `left` / `cursor_moved` / `shape_changed`: whose. | | `cursor` | [`Option`](#presencecursor-record) | | `cursor_moved`: where. | | `shape` | `Option` | `None` | `shape_changed`: the new shape. | | `shape_source` / `shapeSource` | `Option` | `None` | `shape_changed`: how it was determined. | | `reason` | `Option` | `None` | `left`: why (`left`, `disconnected`, `timeout`, `run_ended`, or empty). | | `participant_ids` / `participantIds` | `Option>` | `None` | `heartbeat`: everyone present. | ## `PresenceView` The shared presence netcode model: who is present and what to draw, with remote cursors interpolated (render delay, Catmull-Rom, extrapolation cap), idle cursors faded, stale participants dropped and your own cursor at the local pointer (libs/cua/proto/PRESENCE.md section 4). Feed it every presence event; call `drawables` each frame. Returned by [`SpacePresence.view`](#spacepresence). | Method | Description | | --- | --- | | [`new`](#presenceviewnew) | A view for `me` (the caller's participant id), rendering remote cursors `delay_ms` behind (default 100; 66 on the datagram channel). | | [`apply`](#presenceviewapply) | Folds one event in at `local_ms` (see `presence_now_ms`). | | [`drawables`](#presenceviewdrawables) | Everything to draw at `local_ms`, your own cursor at `pointer` when the local pointer is over the surface. | | [`expire`](#presenceviewexpire) | Drops everyone but the caller when heartbeats stopped for 3 intervals. | | [`participant`](#presenceviewparticipant) | A participant. | | [`set_heartbeat_interval_ms`](#presenceviewset_heartbeat_interval_ms) | The expected heartbeat interval (default 5000 ms). | | [`shape_of`](#presenceviewshape_of) | A participant's current shape name (the caller's included). | | [`upsert`](#presenceviewupsert) | Adds or updates a participant. | | Accessor | Returns | Description | | --- | --- | --- | | `participant_ids()` | `Vec` | Participant ids, the caller first. | ### `PresenceView.new` A view for `me` (the caller's participant id), rendering remote cursors `delay_ms` behind (default 100; 66 on the datagram channel). **Python** ```python PresenceView(me: str, delay_ms: Optional[float] = None) ``` **TypeScript** ```ts new PresenceView(me: string, delayMs: number | undefined = undefined) ``` **Swift** ```swift init(me: String, delayMs: Double? = nil) ``` **Kotlin** ```kotlin constructor(me: String, delayMs: Double? = null) ``` **Rust** ```rust pub fn new(me: String, delay_ms: Option) -> Arc ``` | Parameter | Type | Default | | --- | --- | --- | | `me` | `String` | required | | `delay_ms` | `Option` | `None` | **Returns** [`PresenceView`](#presenceview) ### `PresenceView.apply` Folds one event in at `local_ms` (see `presence_now_ms`). Returns whether anything changed. **Python** ```python def apply(self, event: PresenceEvent, local_ms: float) -> bool ``` **TypeScript** ```ts apply(event: PresenceEvent, localMs: number): boolean ``` **Swift** ```swift func apply(event: PresenceEvent, localMs: Double) -> Bool ``` **Kotlin** ```kotlin fun apply(event: PresenceEvent, localMs: Double): Boolean ``` **Rust** ```rust pub fn apply(&self, event: PresenceEvent, local_ms: f64) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `event` | [`PresenceEvent`](#presenceevent-record) | required | | `local_ms` | `f64` | required | **Returns** `bool` ### `PresenceView.drawables` Everything to draw at `local_ms`, your own cursor at `pointer` when the local pointer is over the surface. **Python** ```python def drawables(self, local_ms: float, pointer: Optional[PresencePoint] = None) -> List[PresenceDrawable] ``` **TypeScript** ```ts drawables(localMs: number, pointer: PresencePoint | undefined = undefined): Array ``` **Swift** ```swift func drawables(localMs: Double, pointer: PresencePoint? = nil) -> [PresenceDrawable] ``` **Kotlin** ```kotlin fun drawables(localMs: Double, pointer: PresencePoint? = null): List ``` **Rust** ```rust pub fn drawables(&self, local_ms: f64, pointer: Option) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `local_ms` | `f64` | required | | `pointer` | [`Option`](#presencepoint-record) | `None` | **Returns** [`Vec`](#presencedrawable-record) ### `PresenceView.expire` Drops everyone but the caller when heartbeats stopped for 3 intervals. Returns the removed ids. **Python** ```python def expire(self, local_ms: float) -> List[str] ``` **TypeScript** ```ts expire(localMs: number): Array ``` **Swift** ```swift func expire(localMs: Double) -> [String] ``` **Kotlin** ```kotlin fun expire(localMs: Double): List ``` **Rust** ```rust pub fn expire(&self, local_ms: f64) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `local_ms` | `f64` | required | **Returns** `Vec` ### `PresenceView.participant` A participant. **Python** ```python def participant(self, participant_id: str) -> Optional[PresenceParticipant] ``` **TypeScript** ```ts participant(participantId: string): PresenceParticipant | undefined ``` **Swift** ```swift func participant(participantId: String) -> PresenceParticipant? ``` **Kotlin** ```kotlin fun participant(participantId: String): PresenceParticipant? ``` **Rust** ```rust pub fn participant(&self, participant_id: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `participant_id` | `String` | required | **Returns** [`Option`](#presenceparticipant-record) ### `PresenceView.set_heartbeat_interval_ms` The expected heartbeat interval (default 5000 ms). **Python** ```python def set_heartbeat_interval_ms(self, ms: float) -> None ``` **TypeScript** ```ts setHeartbeatIntervalMs(ms: number): void ``` **Swift** ```swift func setHeartbeatIntervalMs(ms: Double) ``` **Kotlin** ```kotlin fun setHeartbeatIntervalMs(ms: Double) ``` **Rust** ```rust pub fn set_heartbeat_interval_ms(&self, ms: f64) ``` | Parameter | Type | Default | | --- | --- | --- | | `ms` | `f64` | required | ### `PresenceView.shape_of` A participant's current shape name (the caller's included). **Python** ```python def shape_of(self, participant_id: str) -> Optional[str] ``` **TypeScript** ```ts shapeOf(participantId: string): string | undefined ``` **Swift** ```swift func shapeOf(participantId: String) -> String? ``` **Kotlin** ```kotlin fun shapeOf(participantId: String): String? ``` **Rust** ```rust pub fn shape_of(&self, participant_id: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `participant_id` | `String` | required | **Returns** `Option` ### `PresenceView.upsert` Adds or updates a participant. **Python** ```python def upsert(self, participant: PresenceParticipant) -> None ``` **TypeScript** ```ts upsert(participant: PresenceParticipant): void ``` **Swift** ```swift func upsert(participant: PresenceParticipant) ``` **Kotlin** ```kotlin fun upsert(participant: PresenceParticipant) ``` **Rust** ```rust pub fn upsert(&self, participant: PresenceParticipant) ``` | Parameter | Type | Default | | --- | --- | --- | | `participant` | [`PresenceParticipant`](#presenceparticipant-record) | required | ## `PresenceDrawable` record One cursor to draw now (see `PresenceView.drawables`). Returned by [`PresenceView.drawables`](#presenceviewdrawables). | Field | Type | Default | Description | | --- | --- | --- | --- | | `participant_id` / `participantId` | `String` | | Whose. | | `display_name` / `displayName` | `String` | | Their display name. | | `color` | `String` | | Their color (`#rrggbb`). | | `is_me` / `isMe` | `bool` | | This client's own cursor, drawn at the local pointer. | | `is_agent` / `isAgent` | `bool` | | An agent. | | `x` | `f64` | | Normalized x. | | `y` | `f64` | | Normalized y. | | `shape` | `String` | | Shape name (see `PresenceCursor.shape`). | | `shape_source` / `shapeSource` | `String` | | How the shape was determined. | | `alpha` | `f64` | | Opacity in `[0, 1]` (idle cursors fade). | | `display_id` / `displayId` | `String` | | Display id of the cursor's target. | | `window_id` / `windowId` | `Option` | | Window id, when over a window stream. | ## `PresencePoint` record A normalized point. | Field | Type | Default | Description | | --- | --- | --- | --- | | `x` | `f64` | | X in `[0, 1]`. | | `y` | `f64` | | Y in `[0, 1]`. | ## `CursorArt` record One cursor shape's shared art: an SVG path on a square canvas, filled with the participant's color over an outline. Returned by [`presence_cursor_art`](#presence_cursor_art), [`presence_cursor_art_all`](#presence_cursor_art_all). | Field | Type | Default | Description | | --- | --- | --- | --- | | `shape` | `String` | | Shape name. | | `path_d` / `pathD` | `String` | | SVG path data (absolute `M`, `L`, `C`, `Z`; nonzero fill). | | `hotspot_x` / `hotspotX` | `f64` | | Hot spot x on the canvas. | | `hotspot_y` / `hotspotY` | `f64` | | Hot spot y on the canvas. | | `canvas` | `f64` | | Canvas size (square). | | `outline_color` / `outlineColor` | `String` | | Outline color, drawn under the fill. | | `outline_width` / `outlineWidth` | `f64` | | Outline width in canvas units. | ## `presence_color` The stable presence color of an agent or other principal (`#rrggbb`): a cursor palette color picked by a hash of `id`. Request it when the agent joins presence (`PresenceIdentity.color`) and use it as the agent's avatar background, so both come from one source. **Python** ```python def presence_color(id: str) -> str ``` **TypeScript** ```ts function presenceColor(id: string): string ``` **Swift** ```swift func presenceColor(id: String) -> String ``` **Kotlin** ```kotlin fun presenceColor(id: String): String ``` **Rust** ```rust pub fn presence_color(id: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | **Returns** `String` ## `presence_text_color` Black or white text (`#000000` / `#ffffff`), whichever reads better on `background` (`#rrggbb`). **Python** ```python def presence_text_color(background: str) -> str ``` **TypeScript** ```ts function presenceTextColor(background: string): string ``` **Swift** ```swift func presenceTextColor(background: String) -> String ``` **Kotlin** ```kotlin fun presenceTextColor(background: String): String ``` **Rust** ```rust pub fn presence_text_color(background: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `background` | `String` | required | **Returns** `String` ## `presence_now_ms` The local clock `PresenceView` expects: Unix time in milliseconds. **Python** ```python def presence_now_ms() -> float ``` **TypeScript** ```ts function presenceNowMs(): number ``` **Swift** ```swift func presenceNowMs() -> Double ``` **Kotlin** ```kotlin fun presenceNowMs(): Double ``` **Rust** ```rust pub fn presence_now_ms() -> f64 ``` **Returns** `f64` ## `presence_cursor_art` The shared art for `shape` (unknown names give the arrow). **Python** ```python def presence_cursor_art(shape: str) -> CursorArt ``` **TypeScript** ```ts function presenceCursorArt(shape: string): CursorArt ``` **Swift** ```swift func presenceCursorArt(shape: String) -> CursorArt ``` **Kotlin** ```kotlin fun presenceCursorArt(shape: String): CursorArt ``` **Rust** ```rust pub fn presence_cursor_art(shape: String) -> CursorArt ``` | Parameter | Type | Default | | --- | --- | --- | | `shape` | `String` | required | **Returns** [`CursorArt`](#cursorart-record) ## `presence_cursor_art_all` The shared art for every shape. **Python** ```python def presence_cursor_art_all() -> List[CursorArt] ``` **TypeScript** ```ts function presenceCursorArtAll(): Array ``` **Swift** ```swift func presenceCursorArtAll() -> [CursorArt] ``` **Kotlin** ```kotlin fun presenceCursorArtAll(): List ``` **Rust** ```rust pub fn presence_cursor_art_all() -> Vec ``` **Returns** [`Vec`](#cursorart-record) ## `presence_cursor_art_svg` A standalone SVG of `shape` in `color`, `size` pixels square. **Python** ```python def presence_cursor_art_svg(shape: str, color: str, size: float) -> str ``` **TypeScript** ```ts function presenceCursorArtSvg(shape: string, color: string, size: number): string ``` **Swift** ```swift func presenceCursorArtSvg(shape: String, color: String, size: Double) -> String ``` **Kotlin** ```kotlin fun presenceCursorArtSvg(shape: String, color: String, size: Double): String ``` **Rust** ```rust pub fn presence_cursor_art_svg(shape: String, color: String, size: f64) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `shape` | `String` | required | | `color` | `String` | required | | `size` | `f64` | required | **Returns** `String` ## `presence_conformance_json` The presence conformance vectors (JSON), for binding test suites. **Python** ```python def presence_conformance_json() -> str ``` **TypeScript** ```ts function presenceConformanceJson(): string ``` **Swift** ```swift func presenceConformanceJson() -> String ``` **Kotlin** ```kotlin fun presenceConformanceJson(): String ``` **Rust** ```rust pub fn presence_conformance_json() -> String ``` **Returns** `String` --- # Windows, icons and displays A Space's app icons (batched, through the SDK's one icon cache), window previews, displays and memory and storage use. `app_icons` answers a whole window list in one call: cached icons come from memory or `$CUA_HOME/cache/icons`, and every miss costs one guest round trip. `thumbnail` is the Space's latest small preview from the cache every client on the machine shares (`$CUA_HOME/cache/thumbnails`, kept fresh by the daemon while someone asks). `window_thumbnail` previews one window, reused for a few seconds. `displays` lists the displays with their resolution and `usage` the memory and storage use. ## Windows, icons and displays Methods of [`Space`](). ### `Space.app_icons` Icons for many windows' apps at once, in request order (`None`: the Space has no icon for that app; show none). Answered by the SDK's one icon cache (memory, then `$CUA_HOME/cache/icons`, keyed by the app and the Space's image, never the pid); every miss costs one guest round trip for the whole batch. macOS renders the app bundle of the pid; Linux reads the `.desktop` entry's `Icon=` from the icon theme; Windows has none. **Python** ```python async def app_icons(self, requests: List[SpaceAppIconRequest]) -> List[Optional[SpaceAppIcon]] ``` **TypeScript** ```ts appIcons(requests: Array, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func appIcons(requests: [SpaceAppIconRequest]) async throws -> [SpaceAppIcon?] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun appIcons(requests: List): List ``` **Rust** ```rust pub async fn app_icons(&self, requests: Vec) -> Result>, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `requests` | [`Vec`](#spaceappiconrequest-record) | required | **Returns** [`Vec>`](#spaceappicon-record) · **Async** · **Raises** [`CuaError`]() ### `Space.app_icon` The icon the guest desktop shows for a window's app (pass the window's `app_name`, `app_id` and `pid`), or `None` when the Space has none: show no icon then, not a placeholder. A window list should ask `Space.app_icons` once for all its windows instead. **Python** ```python async def app_icon(self, app_name: str, app_id: str, pid: int) -> Optional[SpaceAppIcon] ``` **TypeScript** ```ts appIcon(appName: string, appId: string, pid: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func appIcon(appName: String, appId: String, pid: UInt32) async throws -> SpaceAppIcon? ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun appIcon(appName: String, appId: String, pid: UInt): SpaceAppIcon? ``` **Rust** ```rust pub async fn app_icon(&self, app_name: String, app_id: String, pid: u32) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `app_name` | `String` | required | | `app_id` | `String` | required | | `pid` | `u32` | required | **Returns** [`Option`](#spaceappicon-record) · **Async** · **Raises** [`CuaError`]() ### `Space.thumbnail` The Space's latest thumbnail (a small JPEG of its primary display) from the cache every client on this machine shares: returned at once when it is younger than `max_age_ms` (unset: any age), else captured fresh through cua-spacesd and kept for the next caller. When that capture fails, the older one comes back (`captured_at_ms` says how old). Asking keeps the daemon refreshing running Spaces' thumbnails in the background for a while (about every 90 s). **Python** ```python async def thumbnail(self, max_age_ms: Optional[int]) -> SpaceThumbnail ``` **TypeScript** ```ts thumbnail(maxAgeMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func thumbnail(maxAgeMs: UInt64?) async throws -> SpaceThumbnail ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun thumbnail(maxAgeMs: ULong?): SpaceThumbnail ``` **Rust** ```rust pub async fn thumbnail(&self, max_age_ms: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `max_age_ms` | `Option` | required | **Returns** [`SpaceThumbnail`](#spacethumbnail-record) · **Async** · **Raises** [`CuaError`]() ### `Space.window_thumbnail` A small JPEG preview of one of the Space's windows (`window_id` and `epoch` from `windows`), at most `max_dimension` px, reused for a few seconds (the SDK's preview cache); `None` when the guest captured nothing. **Python** ```python async def window_thumbnail(self, window_id: str, epoch: int, max_dimension: int) -> Optional[bytes] ``` **TypeScript** ```ts windowThumbnail(windowId: string, epoch: bigint, maxDimension: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func windowThumbnail(windowId: String, epoch: UInt64, maxDimension: UInt32) async throws -> Data? ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun windowThumbnail(windowId: String, epoch: ULong, maxDimension: UInt): ByteArray? ``` **Rust** ```rust pub async fn window_thumbnail(&self, window_id: String, epoch: u64, max_dimension: u32) -> Result>, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `window_id` | `String` | required | | `epoch` | `u64` | required | | `max_dimension` | `u32` | required | **Returns** `Option>` · **Async** · **Raises** [`CuaError`]() ### `Space.displays` The Space's displays, primary first, with their resolution in physical pixels. **Python** ```python async def displays(self) -> List[SpaceDisplay] ``` **TypeScript** ```ts displays(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func displays() async throws -> [SpaceDisplay] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun displays(): List ``` **Rust** ```rust pub async fn displays(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#spacedisplay-record) · **Async** · **Raises** [`CuaError`]() ### `Space.usage` Memory and storage use now (cheap: poll it at a low rate while a detail is visible). **Python** ```python async def usage(self) -> SpaceUsage ``` **TypeScript** ```ts usage(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func usage() async throws -> SpaceUsage ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun usage(): SpaceUsage ``` **Rust** ```rust pub async fn usage(&self) -> Result ``` **Returns** [`SpaceUsage`](#spaceusage-record) · **Async** · **Raises** [`CuaError`]() ## `SpaceAppIcon` record An app icon from `Space.app_icons`: the icon the guest desktop uses, normalized by the SDK's icon cache. Returned by [`Space.app_icon`](#spaceapp_icon), [`Space.app_icons`](#spaceapp_icons). | Field | Type | Default | Description | | --- | --- | --- | --- | | `bytes` | `Vec` | | The 64 px PNG (2x), or an SVG the guest could not rasterize. | | `content_type` / `contentType` | `String` | | `image/png` or `image/svg+xml`. | | `bytes_1x` / `bytes1x` | `Vec` | | The 32 px PNG (1x); empty for an SVG. | ## `SpaceAppIconRequest` record One window's app, for `Space.app_icons`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `app_name` / `appName` | `String` | | App name, as the window list reports it. | | `app_id` / `appId` | `String` | `""` | App id (bundle id, `.desktop` id). | | `pid` | `u32` | `0` | A process of the app (0 when unknown). | ## `SpaceDisplay` record One of a Space's displays (`Space.displays`). Returned by [`Space.displays`](#spacedisplays). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Display id. | | `name` | `String` | | Name ("XVFB-0", "Built-in Retina Display"). | | `primary` | `bool` | | The primary display. | | `width_px` / `widthPx` | `u32` | | Framebuffer width in physical pixels. | | `height_px` / `heightPx` | `u32` | | Framebuffer height in physical pixels. | | `scale_factor` / `scaleFactor` | `f64` | | Physical pixels per logical point. | ## `SpaceThumbnail` record A Space's latest thumbnail, from the cache every client on this machine shares (`crate.native.spaces.Space.thumbnail`). Returned by [`Space.thumbnail`](#spacethumbnail). | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `Vec` | | Encoded image (a small JPEG of the primary display). | | `format` | [`ImageFormat`]() | | Encoding. | | `width` | `u32` | | Pixel width. | | `height` | `u32` | | Pixel height. | | `captured_at_ms` / `capturedAtMs` | `u64` | | When it was captured, Unix milliseconds. | ## `SpaceUsage` record Memory and storage use (`Space.usage`). A size is the guest's own limit only when its `*_limited` flag says so (a VM's memory or disk, a container's cgroup memory limit). Returned by [`Space.usage`](#spaceusage). | Field | Type | Default | Description | | --- | --- | --- | --- | | `memory_used` / `memoryUsed` | `u64` | | Memory in use, bytes. | | `memory_total` / `memoryTotal` | `u64` | | Memory size, bytes. | | `memory_limited` / `memoryLimited` | `bool` | | `memory_total` is the guest's limit. | | `disk_used` / `diskUsed` | `u64` | | Home filesystem bytes in use. | | `disk_total` / `diskTotal` | `u64` | | Home filesystem size, bytes. | | `disk_limited` / `diskLimited` | `bool` | | `disk_total` is the guest's own disk. | --- # Agents in a Space Start, follow, message and stop coding-agent runs inside a Space. An agent run is a coding-agent CLI started in the Space with a prompt, detached from the call that started it. ## Agent runs Methods of [`Space`](). ### `Space.agent_start` Starts an agent CLI (`claude-code`, `openai-codex`, ...) with a prompt, as a detached spacesd process. **Python** ```python async def agent_start(self, agent: str, prompt: str, show: Optional[bool], options: Optional[SpaceAgentOptions]) -> AgentStartReport ``` **TypeScript** ```ts agentStart(agent: string, prompt: string, show: boolean | undefined, options: SpaceAgentOptions | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentStart(agent: String, prompt: String, show: Bool?, options: SpaceAgentOptions?) async throws -> AgentStartReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentStart(agent: String, prompt: String, show: Boolean?, options: SpaceAgentOptions?): AgentStartReport ``` **Rust** ```rust pub async fn agent_start(&self, agent: String, prompt: String, show: Option, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `agent` | `String` | required | | `prompt` | `String` | required | | `show` | `Option` | required | | `options` | [`Option`](#spaceagentoptions-record) | required | **Returns** [`AgentStartReport`](#agentstartreport-record) · **Async** · **Raises** [`CuaError`]() ### `Space.agent_status` A run's status with the last `tail` lines of output. **Python** ```python async def agent_status(self, run_id: str, tail: Optional[int]) -> AgentRunStatus ``` **TypeScript** ```ts agentStatus(runId: string, tail: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentStatus(runId: String, tail: UInt32?) async throws -> AgentRunStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentStatus(runId: String, tail: UInt?): AgentRunStatus ``` **Rust** ```rust pub async fn agent_status(&self, run_id: String, tail: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `run_id` | `String` | required | | `tail` | `Option` | required | **Returns** [`AgentRunStatus`](#agentrunstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Space.agent_events` Normalized events after `cursor` (0: the start), as the `agent_events` JSON (`events`, `cursor`, `caught_up`, `status`). **Python** ```python async def agent_events(self, run_id: str, cursor: Optional[int], max: Optional[int]) -> str ``` **TypeScript** ```ts agentEvents(runId: string, cursor: bigint | undefined, max: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentEvents(runId: String, cursor: UInt64?, max: UInt32?) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentEvents(runId: String, cursor: ULong?, max: UInt?): String ``` **Rust** ```rust pub async fn agent_events(&self, run_id: String, cursor: Option, max: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `run_id` | `String` | required | | `cursor` | `Option` | required | | `max` | `Option` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Space.agent_message` Sends a follow-up message to a run. **Python** ```python async def agent_message(self, run_id: str, text: str, force: Optional[bool]) -> AgentActionReport ``` **TypeScript** ```ts agentMessage(runId: string, text: string, force: boolean | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentMessage(runId: String, text: String, force: Bool?) async throws -> AgentActionReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentMessage(runId: String, text: String, force: Boolean?): AgentActionReport ``` **Rust** ```rust pub async fn agent_message(&self, run_id: String, text: String, force: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `run_id` | `String` | required | | `text` | `String` | required | | `force` | `Option` | required | **Returns** [`AgentActionReport`](#agentactionreport-record) · **Async** · **Raises** [`CuaError`]() ### `Space.agent_interrupt` Cancels the run's turn in flight; the session stays open. **Python** ```python async def agent_interrupt(self, run_id: str) -> AgentActionReport ``` **TypeScript** ```ts agentInterrupt(runId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentInterrupt(runId: String) async throws -> AgentActionReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentInterrupt(runId: String): AgentActionReport ``` **Rust** ```rust pub async fn agent_interrupt(&self, run_id: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `run_id` | `String` | required | **Returns** [`AgentActionReport`](#agentactionreport-record) · **Async** · **Raises** [`CuaError`]() ### `Space.agent_list` Every agent run in this Space. **Python** ```python async def agent_list(self) -> List[AgentRunStatus] ``` **TypeScript** ```ts agentList(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func agentList() async throws -> [AgentRunStatus] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentList(): List ``` **Rust** ```rust pub async fn agent_list(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#agentrunstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Space.agent_stop` Stops a run. **Python** ```python async def agent_stop(self, run_id: str) -> AgentActionReport ``` **TypeScript** ```ts agentStop(runId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentStop(runId: String) async throws -> AgentActionReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentStop(runId: String): AgentActionReport ``` **Rust** ```rust pub async fn agent_stop(&self, run_id: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `run_id` | `String` | required | **Returns** [`AgentActionReport`](#agentactionreport-record) · **Async** · **Raises** [`CuaError`]() ## Harnesses Methods of [`Spaces`](). ### `Spaces.agent_capabilities` What every agent harness is and cannot do (`agent_capabilities`), as JSON. **Python** ```python async def agent_capabilities(self) -> str ``` **TypeScript** ```ts agentCapabilities(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentCapabilities() async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentCapabilities(): String ``` **Rust** ```rust pub async fn agent_capabilities(&self) -> Result ``` **Returns** `String` · **Async** · **Raises** [`CuaError`]() ## `AgentStartReport` record Result of `Space.agent_start`. Returned by [`Space.agent_start`](#spaceagent_start). | Field | Type | Default | Description | | --- | --- | --- | --- | | `run_id` / `runId` | `String` | | Run id. | | `agent` | `String` | | Agent. | | `space` | `String` | | Space. | | `process_tag` / `processTag` | `String` | | spacesd process tag of the first turn. | | `notes` | `Vec` | | Preparation steps that could not be completed. | | `json` | `String` | | The whole report as JSON. | ## `AgentRunStatus` record One agent run's status. Returned by [`Space.agent_list`](#spaceagent_list), [`Space.agent_status`](#spaceagent_status). | Field | Type | Default | Description | | --- | --- | --- | --- | | `run_id` / `runId` | `String` | | Run id. | | `agent` | `Option` | | Agent. | | `status` | `String` | | Status (`running`, `done`, `failed`, ...). | | `reason` | `String` | | Why. | | `phase` | `String` | | Finer grain: `installing`, `working`, `waiting`, `exited`, ... | | `result_text` / `resultText` | `Option` | | The last turn's final text (`agent_status` only). | | `accepts_message` / `acceptsMessage` | `bool` | | A follow-up sent now starts the next turn (the server's rule: idle, or a crashed or failed run it restarts; not mid-turn). Read this, never derive it from `status`. | | `output_tail` / `outputTail` | `Option` | | Output tail. | | `json` | `String` | | The whole status as JSON. | ## `AgentActionReport` record Result of `Space.agent_message` / `Space.agent_stop`. Returned by [`Space.agent_interrupt`](#spaceagent_interrupt), [`Space.agent_message`](#spaceagent_message), [`Space.agent_stop`](#spaceagent_stop). | Field | Type | Default | Description | | --- | --- | --- | --- | | `run_id` / `runId` | `String` | | Run id. | | `ok` | `bool` | | Delivered (message) or stopped (stop). | | `reason` | `String` | | Why. | | `json` | `String` | | The whole report as JSON. | ## `SpaceAgentOptions` record Options for `Space.agent_start` beyond the harness and prompt. | Field | Type | Default | Description | | --- | --- | --- | --- | | `env_from_host` / `envFromHost` | `Vec` | `[]` | Provider key variables forwarded from the Spaces host's environment. | | `env` | `HashMap` | `[:]` | More environment for the agent, for example `HERMES_HOME` (stored 0600 in the run, redacted from its output; not for provider keys). | | `repo` | `Option` | `None` | | | `branch` | `Option` | `None` | | | `cwd` | `Option` | `None` | | | `model` | `Option` | `None` | | | `base_url` / `baseUrl` | `Option` | `None` | A custom model endpoint. | | `exit_when_idle` / `exitWhenIdle` | `Option` | `None` | Stop (resumably) once the prompt is answered. | | `home` | `Option` | `None` | Run as this persistent agent: its home in the Cua Volume is restored first and saved after every turn (see `Spaces.persistent_agent_send`). | --- # Persistent agents Named agents whose memory outlives their Space: homes in the Cua Volume, routines, notifications, pause and resume, access to your computers. A persistent agent keeps its harness memory in its home in the Cua Volume (`agents//`). The daemon restores the home before each run, saves it after each turn, fires routines with or without an app open, and posts notifications. See [Persistent agents](). ## Persistent agents Methods of [`Spaces`](). ### `Spaces.persistent_agent_create` Creates a persistent agent: `harness` working in `space`, its home at `agents//` in the Cua Volume. **Python** ```python async def persistent_agent_create(self, name: str, harness: str, space: str, options: Optional[PersistentAgentOptions]) -> PersistentAgentInfo ``` **TypeScript** ```ts persistentAgentCreate(name: string, harness: string, space: string, options: PersistentAgentOptions | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func persistentAgentCreate(name: String, harness: String, space: String, options: PersistentAgentOptions?) async throws -> PersistentAgentInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun persistentAgentCreate(name: String, harness: String, space: String, options: PersistentAgentOptions?): PersistentAgentInfo ``` **Rust** ```rust pub async fn persistent_agent_create(&self, name: String, harness: String, space: String, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `harness` | `String` | required | | `space` | `String` | required | | `options` | [`Option`](#persistentagentoptions-record) | required | **Returns** [`PersistentAgentInfo`](#persistentagentinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.persistent_agents` Every persistent agent. **Python** ```python async def persistent_agents(self) -> List[PersistentAgentInfo] ``` **TypeScript** ```ts persistentAgents(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func persistentAgents() async throws -> [PersistentAgentInfo] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun persistentAgents(): List ``` **Rust** ```rust pub async fn persistent_agents(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#persistentagentinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.persistent_agent_remove` Forgets a persistent agent (its home stays in the drive). **Python** ```python async def persistent_agent_remove(self, name: str) -> PersistentAgentInfo ``` **TypeScript** ```ts persistentAgentRemove(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func persistentAgentRemove(name: String) async throws -> PersistentAgentInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun persistentAgentRemove(name: String): PersistentAgentInfo ``` **Rust** ```rust pub async fn persistent_agent_remove(&self, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`PersistentAgentInfo`](#persistentagentinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.persistent_agent_send` Gives a persistent agent a turn: a follow-up to its idle run, or a new run with its home restored. **Python** ```python async def persistent_agent_send(self, name: str, text: str) -> AgentDelivery ``` **TypeScript** ```ts persistentAgentSend(name: string, text: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func persistentAgentSend(name: String, text: String) async throws -> AgentDelivery ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun persistentAgentSend(name: String, text: String): AgentDelivery ``` **Rust** ```rust pub async fn persistent_agent_send(&self, name: String, text: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `text` | `String` | required | **Returns** [`AgentDelivery`](#agentdelivery-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.persistent_agent_save` Saves a persistent agent's home into the drive now. **Python** ```python async def persistent_agent_save(self, name: str) -> HomeTransfer ``` **TypeScript** ```ts persistentAgentSave(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func persistentAgentSave(name: String) async throws -> HomeTransfer ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun persistentAgentSave(name: String): HomeTransfer ``` **Rust** ```rust pub async fn persistent_agent_save(&self, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`HomeTransfer`](#hometransfer-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.agent_pause` Pauses a persistent agent: run, routines and (local) Space. **Python** ```python async def agent_pause(self, name: str) -> AgentPauseReport ``` **TypeScript** ```ts agentPause(name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentPause(name: String) async throws -> AgentPauseReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentPause(name: String): AgentPauseReport ``` **Rust** ```rust pub async fn agent_pause(&self, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** [`AgentPauseReport`](#agentpausereport-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.agent_resume` Resumes a paused persistent agent; with `prompt`, starts a run. **Python** ```python async def agent_resume(self, name: str, prompt: Optional[str]) -> AgentResumeReport ``` **TypeScript** ```ts agentResume(name: string, prompt: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func agentResume(name: String, prompt: String?) async throws -> AgentResumeReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun agentResume(name: String, prompt: String?): AgentResumeReport ``` **Rust** ```rust pub async fn agent_resume(&self, name: String, prompt: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `prompt` | `Option` | required | **Returns** [`AgentResumeReport`](#agentresumereport-record) · **Async** · **Raises** [`CuaError`]() ## Routines Methods of [`Spaces`](). ### `Spaces.routine_add` Adds a routine. Give exactly one of `every_minutes`, `daily_at` (`HH:MM`) or `weekly_on` (`mon 09:00`). **Python** ```python async def routine_add(self, agent: str, title: str, prompt: str, every_minutes: Optional[int], daily_at: Optional[str], weekly_on: Optional[str]) -> RoutineInfo ``` **TypeScript** ```ts routineAdd(agent: string, title: string, prompt: string, everyMinutes: bigint | undefined, dailyAt: string | undefined, weeklyOn: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func routineAdd(agent: String, title: String, prompt: String, everyMinutes: Int64?, dailyAt: String?, weeklyOn: String?) async throws -> RoutineInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun routineAdd(agent: String, title: String, prompt: String, everyMinutes: Long?, dailyAt: String?, weeklyOn: String?): RoutineInfo ``` **Rust** ```rust pub async fn routine_add(&self, agent: String, title: String, prompt: String, every_minutes: Option, daily_at: Option, weekly_on: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `agent` | `String` | required | | `title` | `String` | required | | `prompt` | `String` | required | | `every_minutes` | `Option` | required | | `daily_at` | `Option` | required | | `weekly_on` | `Option` | required | **Returns** [`RoutineInfo`](#routineinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.routines` Routines (of one agent, or all). **Python** ```python async def routines(self, agent: Optional[str]) -> List[RoutineInfo] ``` **TypeScript** ```ts routines(agent: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func routines(agent: String?) async throws -> [RoutineInfo] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun routines(agent: String?): List ``` **Rust** ```rust pub async fn routines(&self, agent: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `agent` | `Option` | required | **Returns** [`Vec`](#routineinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.routine_remove` Deletes a routine. **Python** ```python async def routine_remove(self, id: str) -> None ``` **TypeScript** ```ts routineRemove(id: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func routineRemove(id: String) async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun routineRemove(id: String) ``` **Rust** ```rust pub async fn routine_remove(&self, id: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | **Async** · **Raises** [`CuaError`]() ### `Spaces.routine_set_enabled` Turns a routine on or off. **Python** ```python async def routine_set_enabled(self, id: str, enabled: bool) -> RoutineInfo ``` **TypeScript** ```ts routineSetEnabled(id: string, enabled: boolean, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func routineSetEnabled(id: String, enabled: Bool) async throws -> RoutineInfo ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun routineSetEnabled(id: String, enabled: Boolean): RoutineInfo ``` **Rust** ```rust pub async fn routine_set_enabled(&self, id: String, enabled: bool) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | | `enabled` | `bool` | required | **Returns** [`RoutineInfo`](#routineinfo-record) · **Async** · **Raises** [`CuaError`]() ## Notifications Methods of [`Spaces`](). ### `Spaces.notify_user` Posts a notification to the Cua app. Returns its id. **Python** ```python async def notify_user(self, title: str, body: Optional[str], agent: Optional[str]) -> str ``` **TypeScript** ```ts notifyUser(title: string, body: string | undefined, agent: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func notifyUser(title: String, body: String?, agent: String?) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun notifyUser(title: String, body: String?, agent: String?): String ``` **Rust** ```rust pub async fn notify_user(&self, title: String, body: Option, agent: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `title` | `String` | required | | `body` | `Option` | required | | `agent` | `Option` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ### `Spaces.notifications` The notifications feed, newest first. **Python** ```python async def notifications(self, unread_only: bool, since_ms: Optional[int]) -> List[NotificationInfo] ``` **TypeScript** ```ts notifications(unreadOnly: boolean, sinceMs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func notifications(unreadOnly: Bool, sinceMs: UInt64?) async throws -> [NotificationInfo] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun notifications(unreadOnly: Boolean, sinceMs: ULong?): List ``` **Rust** ```rust pub async fn notifications(&self, unread_only: bool, since_ms: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `unread_only` | `bool` | required | | `since_ms` | `Option` | required | **Returns** [`Vec`](#notificationinfo-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.notifications_ack` Marks notifications read (every one when `ids` is empty). **Python** ```python async def notifications_ack(self, ids: List[str]) -> int ``` **TypeScript** ```ts notificationsAck(ids: Array, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func notificationsAck(ids: [String]) async throws -> UInt64 ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun notificationsAck(ids: List): ULong ``` **Rust** ```rust pub async fn notifications_ack(&self, ids: Vec) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `ids` | `Vec` | required | **Returns** `u64` · **Async** · **Raises** [`CuaError`]() ## Access to your computers Methods of [`Spaces`](). ### `Spaces.computer_access_grant` Lets one persistent agent use one of the user's computers (asks for presence). **Python** ```python async def computer_access_grant(self, agent: str, machine: str, expires_in_secs: Optional[int]) -> ComputerGrant ``` **TypeScript** ```ts computerAccessGrant(agent: string, machine: string, expiresInSecs: bigint | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func computerAccessGrant(agent: String, machine: String, expiresInSecs: UInt64?) async throws -> ComputerGrant ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun computerAccessGrant(agent: String, machine: String, expiresInSecs: ULong?): ComputerGrant ``` **Rust** ```rust pub async fn computer_access_grant(&self, agent: String, machine: String, expires_in_secs: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `agent` | `String` | required | | `machine` | `String` | required | | `expires_in_secs` | `Option` | required | **Returns** [`ComputerGrant`](#computergrant-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.computer_access_revoke` Takes a persistent agent's computer access back (every machine when `machine` is empty). Returns how many grants. **Python** ```python async def computer_access_revoke(self, agent: str, machine: Optional[str]) -> int ``` **TypeScript** ```ts computerAccessRevoke(agent: string, machine: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func computerAccessRevoke(agent: String, machine: String?) async throws -> UInt64 ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun computerAccessRevoke(agent: String, machine: String?): ULong ``` **Rust** ```rust pub async fn computer_access_revoke(&self, agent: String, machine: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `agent` | `String` | required | | `machine` | `Option` | required | **Returns** `u64` · **Async** · **Raises** [`CuaError`]() ### `Spaces.computer_access` Per-agent computer grants (of one agent, or all). **Python** ```python async def computer_access(self, agent: Optional[str]) -> List[ComputerGrant] ``` **TypeScript** ```ts computerAccess(agent: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func computerAccess(agent: String?) async throws -> [ComputerGrant] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun computerAccess(agent: String?): List ``` **Rust** ```rust pub async fn computer_access(&self, agent: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `agent` | `Option` | required | **Returns** [`Vec`](#computergrant-record) · **Async** · **Raises** [`CuaError`]() ## `PersistentAgentInfo` record A persistent agent: a named harness whose memory (its home in the Cua Drive) outlives its runs and its Space. Returned by [`Spaces.persistent_agent_create`](#spacespersistent_agent_create), [`Spaces.persistent_agent_remove`](#spacespersistent_agent_remove), [`Spaces.persistent_agents`](#spacespersistent_agents). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | | | `harness` | `String` | | Harness id. | | `space` | `String` | | The Space it works in. | | `model` | `Option` | | | | `base_url` / `baseUrl` | `Option` | | | | `env_from_host` / `envFromHost` | `Vec` | | | | `paused` | `bool` | | | | `space_state` / `spaceState` | `String` | | `running`, `suspended` (a paused local Space) or `released` (a paused cloud Space). | | `run_id` / `runId` | `Option` | | The current run. | | `saved_ms` / `savedMs` | `u64` | | Unix ms of the last home save. | | `created_ms` / `createdMs` | `u64` | | | | `last_error` / `lastError` | `Option` | | | ## `PersistentAgentOptions` record How to create a persistent agent. | Field | Type | Default | Description | | --- | --- | --- | --- | | `model` | `Option` | `None` | | | `base_url` / `baseUrl` | `Option` | `None` | | | `env_from_host` / `envFromHost` | `Vec` | `[]` | Provider key variables forwarded from the host's environment at each start. | | `env` | `HashMap` | `[:]` | More environment for every run (not secrets). | ## `AgentDelivery` record What `Spaces.persistent_agent_send` did. Returned by [`Spaces.persistent_agent_send`](#spacespersistent_agent_send). | Field | Type | Default | Description | | --- | --- | --- | --- | | `run_id` / `runId` | `String` | | | | `started` | `bool` | | A new run started (with the home restored) rather than a follow-up. | | `restored` | [`Option`](#hometransfer-record) | | | ## `HomeTransfer` record What moved between the drive and a Space. Returned by [`Spaces.persistent_agent_save`](#spacespersistent_agent_save). | Field | Type | Default | Description | | --- | --- | --- | --- | | `files` | `u64` | | | | `bytes` | `u64` | | | | `unchanged` | `u64` | | | | `removed` | `u64` | | | | `blocked` | `Vec` | | `path (kind)` of each file the secret scanner kept out. | | `millis` | `u64` | | | ## `AgentPauseReport` record What `Spaces.agent_pause` did. Returned by [`Spaces.agent_pause`](#spacesagent_pause). | Field | Type | Default | Description | | --- | --- | --- | --- | | `stopped_run` / `stoppedRun` | `Option` | | | | `saved` | [`Option`](#hometransfer-record) | | | | `space_state` / `spaceState` | `String` | | | | `millis` | `u64` | | | ## `AgentResumeReport` record What `Spaces.agent_resume` did. Returned by [`Spaces.agent_resume`](#spacesagent_resume). | Field | Type | Default | Description | | --- | --- | --- | --- | | `space` | `String` | | | | `recreated` | `bool` | | | | `restored` | [`Option`](#hometransfer-record) | | | | `run_id` / `runId` | `Option` | | | | `ready_ms` / `readyMs` | `u64` | | From the call to the home being back in a ready Space. | ## `RoutineInfo` record A routine: a recurring turn of a persistent agent, fired by the daemon. Returned by [`Spaces.routine_add`](#spacesroutine_add), [`Spaces.routine_set_enabled`](#spacesroutine_set_enabled), [`Spaces.routines`](#spacesroutines). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `agent` | `String` | | | | `title` | `String` | | | | `prompt` | `String` | | | | `label` | `String` | | `Every day at 8:00 AM`. | | `enabled` | `bool` | | | | `next_fire` / `nextFire` | `Option` | | RFC 3339. | | `last_fired_at` / `lastFiredAt` | `Option` | | | | `last_outcome` / `lastOutcome` | `Option` | | | | `last_run_id` / `lastRunId` | `Option` | | | ## `NotificationInfo` record One entry of the notifications feed. Returned by [`Spaces.notifications`](#spacesnotifications). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `at_ms` / `atMs` | `u64` | | | | `agent` | `Option` | | | | `kind` | `String` | | `turn_ended`, `message`, `approval` or `error`. | | `title` | `String` | | | | `body` | `String` | | | | `run_id` / `runId` | `Option` | | | | `space` | `Option` | | | | `read` | `bool` | | | ## `ComputerGrant` record A persistent agent's grant on one of the user's computers. Returned by [`Spaces.computer_access`](#spacescomputer_access), [`Spaces.computer_access_grant`](#spacescomputer_access_grant). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `agent` | `String` | | | | `machine` | `String` | | | | `created_ms` / `createdMs` | `u64` | | | | `expires_ms` / `expiresMs` | `Option` | | | | `revoked` | `bool` | | | --- # Your cloud Sandboxes and Spaces in your own AWS, Google Cloud or Modal account: connect, check, and sweep what Cua created there. Create a sandbox or Space with `on="aws"`, `"gcp"` or `"modal"` once the cloud is connected. Each cloud uses its own CLI sign-in; Cua stores only the profile, region, project or environment names. The guest dials out to the cua.ai relay, so nothing listens on the internet. `cloud_test` creates nothing; `cloud_sweep` deletes only what Cua tagged and recorded. ## Your cloud Methods of [`Spaces`](). ### `Spaces.cloud_status` Your clouds: found credentials, the connected account and region, what each can run at what hourly cost, and what Cua created there. `provider` narrows it to one cloud. **Python** ```python async def cloud_status(self, provider: Optional[str]) -> CloudStatus ``` **TypeScript** ```ts cloudStatus(provider: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func cloudStatus(provider: String?) async throws -> CloudStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun cloudStatus(provider: String?): CloudStatus ``` **Rust** ```rust pub async fn cloud_status(&self, provider: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `provider` | `Option` | required | **Returns** [`CloudStatus`](#cloudstatus-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.cloud_connect` Connects a cloud account (after the checks `cloud_test` runs). With `make_default`, Spaces are created there when no location is given. `ttl_hours` (default 8, 0: never) deletes each Space there after that long. **Python** ```python async def cloud_connect(self, target: CloudTarget, make_default: bool, ttl_hours: Optional[int]) -> CloudConnected ``` **TypeScript** ```ts cloudConnect(target: CloudTarget, makeDefault: boolean, ttlHours: number | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func cloudConnect(target: CloudTarget, makeDefault: Bool, ttlHours: UInt32?) async throws -> CloudConnected ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun cloudConnect(target: CloudTarget, makeDefault: Boolean, ttlHours: UInt?): CloudConnected ``` **Rust** ```rust pub async fn cloud_connect(&self, target: CloudTarget, make_default: bool, ttl_hours: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `target` | [`CloudTarget`](#cloudtarget-record) | required | | `make_default` | `bool` | required | | `ttl_hours` | `Option` | required | **Returns** [`CloudConnected`](#cloudconnected-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.cloud_test` Checks a cloud account without creating anything. **Python** ```python async def cloud_test(self, target: CloudTarget) -> CloudTestReport ``` **TypeScript** ```ts cloudTest(target: CloudTarget, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func cloudTest(target: CloudTarget) async throws -> CloudTestReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun cloudTest(target: CloudTarget): CloudTestReport ``` **Rust** ```rust pub async fn cloud_test(&self, target: CloudTarget) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `target` | [`CloudTarget`](#cloudtarget-record) | required | **Returns** [`CloudTestReport`](#cloudtestreport-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.cloud_disconnect` Forgets a connected cloud (nothing in it is deleted). **Python** ```python async def cloud_disconnect(self, provider: str) -> CloudDisconnected ``` **TypeScript** ```ts cloudDisconnect(provider: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func cloudDisconnect(provider: String) async throws -> CloudDisconnected ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun cloudDisconnect(provider: String): CloudDisconnected ``` **Rust** ```rust pub async fn cloud_disconnect(&self, provider: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `provider` | `String` | required | **Returns** [`CloudDisconnected`](#clouddisconnected-record) · **Async** · **Raises** [`CuaError`]() ### `Spaces.cloud_sweep` What Cua left in your clouds (expired Spaces, resources whose Space is gone; with `all`, everything Cua created there). Deletes it unless `dry_run`. Only resources tagged and recorded as Cua's are ever touched. **Python** ```python async def cloud_sweep(self, provider: Optional[str], dry_run: bool, all: bool) -> CloudSweepReport ``` **TypeScript** ```ts cloudSweep(provider: string | undefined, dryRun: boolean, all: boolean, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func cloudSweep(provider: String?, dryRun: Bool, all: Bool) async throws -> CloudSweepReport ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun cloudSweep(provider: String?, dryRun: Boolean, all: Boolean): CloudSweepReport ``` **Rust** ```rust pub async fn cloud_sweep(&self, provider: Option, dry_run: bool, all: bool) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `provider` | `Option` | required | | `dry_run` | `bool` | required | | `all` | `bool` | required | **Returns** [`CloudSweepReport`](#cloudsweepreport-record) · **Async** · **Raises** [`CuaError`]() ## `CloudTarget` record Where in a cloud account Spaces go. Credentials are never here: each cloud uses its own CLI sign-in. | Field | Type | Default | Description | | --- | --- | --- | --- | | `provider` | `String` | | `aws`, `gcp` or `modal`. | | `profile` | `Option` | `None` | AWS or Modal profile. | | `region` | `Option` | `None` | AWS or GCP region. | | `zone` | `Option` | `None` | GCP zone. | | `project` | `Option` | `None` | GCP project. | | `environment` | `Option` | `None` | Modal environment. | ## `CloudStatus` record `cloud_status`. Returned by [`Spaces.cloud_status`](#spacescloud_status). | Field | Type | Default | Description | | --- | --- | --- | --- | | `default_on` / `defaultOn` | `String` | | `default.on`, for example `aws`. | | `providers` | [`Vec`](#cloudprovider-record) | | | | `resources` | [`Vec`](#cloudresource-record) | | | ## `CloudProvider` record One cloud as `cloud_status` lists it. | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | `aws`, `gcp`, `modal`. | | `title` | `String` | | `AWS`, `Google Cloud`, `Modal`. | | `tier` | `String` | | `vm` or `sandbox`. | | `connected` | `bool` | | | | `default` | `bool` | | Whether it is the default location (`default.on`). | | `credentials` | [`CloudCredentials`](#cloudcredentials-record) | | | | `account` | `String` | | The account, project or workspace (after a test or connect). | | `profile` | `String` | | | | `region` | `String` | | | | `zone` | `String` | | | | `project` | `String` | | | | `environment` | `String` | | | | `label` | `String` | | "AWS · us-west-2". | | `ttl_hours` / `ttlHours` | `u32` | | Hours after which a Space there deletes itself (0: never). | | `kinds` | [`Vec`](#cloudkind-record) | | | ## `CloudKind` record What a cloud can run for one image family, and the estimated cost. | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `String` | | `linux`, `linux-slim`, `windows`, `omarchy`, `macos`. | | `kind` | `String` | | `container` or `vm`. | | `supported` | `bool` | | | | `reason` | `String` | | Why not, or how it runs (one line). | | `machine_type` / `machineType` | `String` | | The machine type Cua picks (`t3.medium`, `e2-medium`). | | `usd_per_hour` / `usdPerHour` | `f64` | | Estimated on-demand US dollars per hour while it runs. | ## `CloudCredentials` record Where a cloud's credentials were found on this machine (never a secret). | Field | Type | Default | Description | | --- | --- | --- | --- | | `found` | `bool` | | | | `source` | `String` | | "~/.aws profile default", "gcloud account ada@x.io". | ## `CloudResource` record One thing Cua created in a cloud. | Field | Type | Default | Description | | --- | --- | --- | --- | | `provider` | `String` | | | | `id` | `String` | | The cloud's id. | | `resource_type` / `resourceType` | `String` | | `instance`, `security_group`, `firewall`, `sandbox`, ... | | `sandbox` | `String` | | The sandbox it belongs to (`aws:`), empty for shared resources. | | `machine` | `String` | | The relay machine the sandbox joined as (its Space is `relay:`). | | `region` | `String` | | | | `state` | `String` | | | | `created` | `String` | | RFC 3339. | | `expires` | `String` | | RFC 3339, empty: never. | | `expired` | `bool` | | | ## `CloudConnected` record `cloud_connect`: the connected cloud and the checks it passed. Returned by [`Spaces.cloud_connect`](#spacescloud_connect). | Field | Type | Default | Description | | --- | --- | --- | --- | | `provider` | [`CloudProvider`](#cloudprovider-record) | | | | `checks` | [`Vec`](#cloudcheck-record) | | | ## `CloudCheck` record One check `cloud_test` ran. | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | | | `ok` | `bool` | | | | `detail` | `String` | | | ## `CloudTestReport` record `cloud_test`. Returned by [`Spaces.cloud_test`](#spacescloud_test). | Field | Type | Default | Description | | --- | --- | --- | --- | | `provider` | `String` | | | | `ok` | `bool` | | | | `account` | `String` | | | | `checks` | [`Vec`](#cloudcheck-record) | | | ## `CloudDisconnected` record `cloud_disconnect`. Returned by [`Spaces.cloud_disconnect`](#spacescloud_disconnect). | Field | Type | Default | Description | | --- | --- | --- | --- | | `provider` | `String` | | | | `disconnected` | `bool` | | | | `left` | [`Vec`](#cloudresource-record) | | Resources Cua still records there. | ## `CloudSweepReport` record `cloud_sweep`. Returned by [`Spaces.cloud_sweep`](#spacescloud_sweep). | Field | Type | Default | Description | | --- | --- | --- | --- | | `dry_run` / `dryRun` | `bool` | | | | `resources` | [`Vec`](#cloudsweepitem-record) | | | ## `CloudSweepItem` record One row of `cloud_sweep`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `resource` | [`CloudResource`](#cloudresource-record) | | | | `action` | `String` | | `delete` (dry run), `deleted`, `keep` or `failed`. | | `reason` | `String` | | | --- # Teleport Move a running app between machines, with an approval callback. Teleport exports an app session from this machine and imports it on another. A consent callback sees the manifest first. `Space.teleport` and `Space.teleport_manifest` ask the Cua Spaces daemon, which runs teleport; an embedded runtime without Cua Spaces raises `HostCapabilityMissing`. To bring any app into a Space (install it, open files in it), see [Teleport an app](). ## Teleport into a Space Methods of [`Space`](). ### `Space.teleport` Teleports `app`'s session into this Space. `approver` sees the manifest and returns the human's decision; `None` cancels (raises `TeleportRefused`). Sensitive items need an explicit acknowledgement. Through a daemon, this may need two calls (see `TeleportReceipt`): the first returns `status: "pending"` with a `request_id` once the user's approval is filed with the Cua Keyvault; call again with that same `request_id` (`approver` is not consulted again -- the decision it already made was what the first call filed) until `status: "moved"`. `request_id` is ignored for an embedded host, which always finishes in one call. **Python** ```python async def teleport(self, app: str, scope: Optional[str], approver: TeleportApprover, request_id: Optional[str] = None) -> TeleportReceipt ``` **TypeScript** ```ts teleport(app: string, scope: string | undefined, approver: TeleportApprover, requestId: string | undefined = undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func teleport(app: String, scope: String?, approver: TeleportApprover, requestId: String? = nil) async throws -> TeleportReceipt ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun teleport(app: String, scope: String?, approver: TeleportApprover, requestId: String? = null): TeleportReceipt ``` **Rust** ```rust pub async fn teleport(&self, app: String, scope: Option, approver: Arc, request_id: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `app` | `String` | required | | `scope` | `Option` | required | | `approver` | [`TeleportApprover`](#teleportapprover) | required | | `request_id` | `Option` | `None` | **Returns** [`TeleportReceipt`](#teleportreceipt-record) · **Async** · **Raises** [`CuaError`]() ([`TeleportRefused`]()) ### `Space.teleport_manifest` What teleporting `app` (`full` or `tabs`) would move from this host. **Python** ```python async def teleport_manifest(self, app: str, scope: Optional[str]) -> TeleportManifest ``` **TypeScript** ```ts teleportManifest(app: string, scope: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func teleportManifest(app: String, scope: String?) async throws -> TeleportManifest ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun teleportManifest(app: String, scope: String?): TeleportManifest ``` **Rust** ```rust pub async fn teleport_manifest(&self, app: String, scope: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `app` | `String` | required | | `scope` | `Option` | required | **Returns** [`TeleportManifest`](#teleportmanifest-record) · **Async** · **Raises** [`CuaError`]() ## `TeleportApprover` The consent gate of `Space.teleport`: shown the manifest, returns the human's decision, or `None` to cancel. Runs on a blocking worker thread; it may take as long as the human needs. **Note** You implement `TeleportApprover` and pass it to the SDK (a callback interface): subclass it in Python, implement the interface in TypeScript, the `TeleportApprover` protocol in Swift, the interface in Kotlin and the trait in Rust. | Method | Description | | --- | --- | | [`approve`](#teleportapproverapprove) | Decides. | ### `TeleportApprover.approve` Decides. **Python** ```python def approve(self, manifest: TeleportManifest) -> Optional[TeleportDecision] ``` **TypeScript** ```ts approve(manifest: TeleportManifest): TeleportDecision | undefined ``` **Swift** ```swift func approve(manifest: TeleportManifest) -> TeleportDecision? ``` **Kotlin** ```kotlin fun approve(manifest: TeleportManifest): TeleportDecision? ``` **Rust** ```rust pub fn approve(&self, manifest: TeleportManifest) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `manifest` | [`TeleportManifest`](#teleportmanifest-record) | required | **Returns** [`Option`](#teleportdecision-record) ## `TeleportDecision` record A human's answer to a teleport manifest. Returned by [`TeleportApprover.approve`](#teleportapproverapprove). | Field | Type | Default | Description | | --- | --- | --- | --- | | `include` | `Option>` | `None` | Items to send (`None` = the provider's default-checked set, never everything). An empty list is refused as ambiguous. | | `acknowledge_sensitive` / `acknowledgeSensitive` | `bool` | `false` | The human acknowledged the sensitive items in the selection. | ## `TeleportReceipt` record What a teleport moved, or how to retry a pending Keyvault approval. Through a daemon (`Host::Daemon`), the underlying `teleport_app` tool is consent-gated: a first call never delivers, it only files the request and returns `status: "pending"` with a `request_id`; show the user the Keyvault approval step, then call `Space.teleport` again with that same `request_id` (`TeleportRetry::request_id`). A second `"pending"` means the user has not decided yet -- call again the same way. Only `status: "moved"` means the session actually landed. An embedded host (no separate daemon) always returns `"moved"` in one call: there is nothing to retry. Returned by [`Space.teleport`](#spaceteleport). | Field | Type | Default | Description | | --- | --- | --- | --- | | `status` | `String` | | `moved` or `pending`. | | `request_id` / `requestId` | `Option` | | The Keyvault request to retry with, while pending. | | `app` | `String` | | App. | | `space` | `String` | | Space. | | `method` | `String` | | `import_session`. | | `transferred_paths` / `transferredPaths` | `Vec` | | Paths sent. | | `bundle_bytes` / `bundleBytes` | `u64` | | Bundle bytes. | | `bundle_sha256` / `bundleSha256` | `String` | | Bundle SHA-256, verified by the Space. | | `imported` | `Vec` | | Groups imported. | | `skipped` | `Vec` | | Items skipped, with reasons. | | `launched` | `bool` | | The app was launched. | ## `TeleportItem` record One item a teleport would move. | Field | Type | Default | Description | | --- | --- | --- | --- | | `relative_path` / `relativePath` | `String` | | Bundle-relative path (what an approval names). | | `label` | `String` | | Label. | | `estimated_bytes` / `estimatedBytes` | `u64` | | Estimated bytes. | | `is_sensitive` / `isSensitive` | `bool` | | Credentials, cookies, tokens, transcripts. | | `is_checked_by_default` / `isCheckedByDefault` | `bool` | | In the default selection. | | `count` | `Option` | | Count of things it holds. | | `count_noun` / `countNoun` | `Option` | | Noun for `count`. | ## `TeleportManifest` record Exactly what would leave this machine. Returned by [`Space.teleport_manifest`](#spaceteleport_manifest). | Field | Type | Default | Description | | --- | --- | --- | --- | | `app` | `String` | | Provider id (`firefox`, `chrome`, `claude-code`, ...). | | `display_name` / `displayName` | `String` | | Display name. | | `scope` | `String` | | `full` or `tabs`. | | `items` | [`Vec`](#teleportitem-record) | | Items. | | `total_estimated_bytes` / `totalEstimatedBytes` | `u64` | | Provider total. | | `notes` | `Vec` | | Provider caveats. | --- # Decoded media Media sessions that deliver decoded BGRA frames and PCM audio. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The streaming decoder ships with Cua Spaces. `spacesd_open_media_decoded` opens a media session on a `SpacesdClient` and delivers decoded video (packed BGRA; VideoToolbox on macOS, OpenH264 elsewhere) to a `DecodedFrameSink`, requesting a keyframe whenever a reference is lost (Swift: `client.openMediaDecoded(...)`). The open source SDK delivers the encoded frames: see [Media](). ## `spacesd_open_media_decoded` Opens a media session on `client` and delivers *decoded* video (packed BGRA) and control events to `frames`. Lost references trigger a keyframe request automatically. (Swift: `client.openMediaDecoded(...)`.) **Swift** ```swift func spacesdOpenMediaDecoded(client: SpacesdClient, options: MediaOpenOptions, frames: DecodedFrameSink) async throws -> MediaSession ``` **Rust** ```rust pub async fn spacesd_open_media_decoded(client: Arc, options: MediaOpenOptions, frames: Arc) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `client` | [`SpacesdClient`]() | required | | `options` | [`MediaOpenOptions`]() | required | | `frames` | [`DecodedFrameSink`](#decodedframesink) | required | **Returns** [`MediaSession`]() · **Async** · **Raises** [`CuaError`]() ## `spacesd_open_media_decoded_with_audio` `spacesd_open_media_decoded` that also delivers decoded PCM audio to `pcm` (set `options.audio` to negotiate audio tracks). **Swift** ```swift func spacesdOpenMediaDecodedWithAudio(client: SpacesdClient, options: MediaOpenOptions, frames: DecodedFrameSink, pcm: PcmSink) async throws -> MediaSession ``` **Rust** ```rust pub async fn spacesd_open_media_decoded_with_audio(client: Arc, options: MediaOpenOptions, frames: Arc, pcm: Arc) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `client` | [`SpacesdClient`]() | required | | `options` | [`MediaOpenOptions`]() | required | | `frames` | [`DecodedFrameSink`](#decodedframesink) | required | | `pcm` | [`PcmSink`](#pcmsink) | required | **Returns** [`MediaSession`]() · **Async** · **Raises** [`CuaError`]() ## `DecodedVideoFrame` record One decoded video frame : packed top-down BGRA. | Field | Type | Default | Description | | --- | --- | --- | --- | | `sequence` | `u64` | | Per-target frame sequence. | | `width` | `u32` | | Width in pixels. | | `height` | `u32` | | Height in pixels. | | `stride` | `u32` | | Bytes per row (`width * 4`). | | `format` | `String` | | Pixel format; always `bgra`. | | `capture_timestamp_us` / `captureTimestampUs` | `u64` | | Media-clock capture time (µs). | | `codec_epoch` / `codecEpoch` | `u64` | | Decoder configuration generation of the source frame. | | `geometry_epoch` / `geometryEpoch` | `u64` | | Coordinate generation. | | `keyframe` | `bool` | | The source access unit was a keyframe. | | `encoded_size` / `encodedSize` | `u64` | | Bytes of the source (encoded) access unit. | | `received_at_us` / `receivedAtUs` | `u64` | | When the encoded frame reached the decoder (Unix µs, this host). | | `decode_duration_us` / `decodeDurationUs` | `u64` | | Time spent decoding and converting it (µs). | | `data` | `Vec` | | Pixels. | ## `PcmAudio` record Decoded audio : interleaved signed 16-bit PCM. | Field | Type | Default | Description | | --- | --- | --- | --- | | `track_id` / `trackId` | `u16` | | Track id from `NegotiatedAudio`. | | `sample_rate` / `sampleRate` | `u32` | | Samples per second per channel. | | `channels` | `u16` | | Interleaved channels. | | `pts_us` / `ptsUs` | `u64` | | Media-clock time of the first sample (µs). | | `concealed` | `bool` | | Synthesized by packet-loss concealment for a lost packet. | | `sequence` | `u32` | | RAU2 sequence of the packet this frame was decoded from (the packet that revealed the loss, for concealed frames). | | `config_epoch` / `configEpoch` | `u8` | | RAU2 config epoch (the audio codec generation). | | `encoded_size` / `encodedSize` | `u64` | | Bytes of the encoded packet (0 for concealed frames). | | `received_at_us` / `receivedAtUs` | `u64` | | When the packet reached the decoder (Unix µs, this host). | | `decode_duration_us` / `decodeDurationUs` | `u64` | | Time spent decoding the packet (µs). | | `samples` | `Vec` | | Interleaved samples. | ## `DecodedFrameSink` Receives decoded video frames and control events. The SDK decodes on the session's delivery thread (VideoToolbox on macOS, OpenH264 elsewhere); implementations must return quickly. **Note** You implement `DecodedFrameSink` and pass it to the SDK (a callback interface): implement the `DecodedFrameSink` protocol in Swift or the trait in Rust. | Method | Description | | --- | --- | | [`on_decoded_frame`](#decodedframesinkon_decoded_frame) | A decoded frame. | | [`on_event`](#decodedframesinkon_event) | A control message, a `decode_error`, or the socket close. | ### `DecodedFrameSink.on_decoded_frame` A decoded frame. **Swift** ```swift func onDecodedFrame(frame: DecodedVideoFrame) ``` **Rust** ```rust pub fn on_decoded_frame(&self, frame: DecodedVideoFrame) ``` | Parameter | Type | Default | | --- | --- | --- | | `frame` | [`DecodedVideoFrame`](#decodedvideoframe-record) | required | ### `DecodedFrameSink.on_event` A control message, a `decode_error`, or the socket close. **Swift** ```swift func onEvent(event: MediaEvent) ``` **Rust** ```rust pub fn on_event(&self, event: MediaEvent) ``` | Parameter | Type | Default | | --- | --- | --- | | `event` | [`MediaEvent`]() | required | ## `PcmSink` Receives decoded audio. Must return quickly. **Note** You implement `PcmSink` and pass it to the SDK (a callback interface): implement the `PcmSink` protocol in Swift or the trait in Rust. | Method | Description | | --- | --- | | [`on_pcm`](#pcmsinkon_pcm) | One decoded (or concealed) audio frame. | ### `PcmSink.on_pcm` One decoded (or concealed) audio frame. **Swift** ```swift func onPcm(audio: PcmAudio) ``` **Rust** ```rust pub fn on_pcm(&self, audio: PcmAudio) ``` | Parameter | Type | Default | | --- | --- | --- | | `audio` | [`PcmAudio`](#pcmaudio-record) | required | --- # Host teleport The Teleport object of the Spaces apps: providers, manifests and sends from this machine. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. [`teleport(cua)`]() returns the `Teleport` object (Swift: `cua.teleport()`). It lists the apps whose sessions this machine can export, builds manifests and sends them into a Space; the app catalog, plans and runs are on [Teleport an app](). ## `Teleport` Teleport send. Exports from this machine's real apps; tests of hosts embedding the SDK must set `CUA_ENV_TEST_SANDBOX=1` (every host effect is then refused) or use `cua-teleport` directly with a fake host. Returned by [`teleport`](). | Method | Description | | --- | --- | | [`app_icon_png`](#teleportapp_icon_png) | An app's icon as PNG bytes (`size` points), or `None`. | | [`capture_window_thumbnail`](#teleportcapture_window_thumbnail) | A PNG preview of that one window (never the screen or another window), at most `max_width` pixels wide, kept in memory for a few seconds (the SDK's preview cache). | | [`catalog`](#teleportcatalog) | "Teleport an app…": every installed app on this machine, classified (full, install only, unsupported with a reason), recents first. | | [`catalog_entry_for_name`](#teleportcatalog_entry_for_name) | The catalog row for an app known by name or id (a dragged window whose bundle is unknown). | | [`catalog_entry_for_path`](#teleportcatalog_entry_for_path) | The catalog row for a dropped app (a bundle, `.desktop` entry or shortcut path). | | [`list_windows`](#teleportlist_windows) | This machine's user windows (for a window picker). | | [`manifest`](#teleportmanifest) | Describes what teleporting `app` (a bundle id or app name, e.g. | | [`parse_drop`](#teleportparse_drop) | Parses a drag payload: paths, `file://` URIs, URLs or a `text/uri-list` blob. | | [`plan`](#teleportplan) | What teleporting `app` into `space` will install, send and import, with the consent items. | | [`record_recent`](#teleportrecord_recent) | Records a teleported app so the catalog lists it first. | | [`request_window_drag_permission`](#teleportrequest_window_drag_permission) | Asks for the window-drag permission (opens System Settings on macOS); returns the state after asking. | | [`run`](#teleportrun) | Runs `plan` after the user's `consent` (secrets need `acknowledge_sensitive`), reporting progress to `listener`, and records the app in the recents. | | [`search_catalog`](#teleportsearch_catalog) | Filters catalog rows by a search query (every word must match the name or id); order is kept. | | [`send`](#teleportsend) | Teleports `app` into `sandbox` (through its cua-spacesd), a direct upload: the bundle is captured on this machine and sent straight to `sandbox`, never through the Keyvault. | | [`send_env`](#teleportsend_env) | Like `Self.send`, to a spacesd connection (for example `Cua.spacesd(url, token)`). | | [`send_through_keyvault`](#teleportsend_through_keyvault) | Teleports `app` into `sandbox`, through the caller's own Cua Keyvault: the session is captured into the Keyvault (sealed, once Touch ID / presence gated) and delivered from there, so this process, the Swift app and the network never see a raw password or cookie. | | [`space_hint`](#teleportspace_hint) | The Space's OS and CPU, to pass as `space_os` / `space_arch` in `TeleportCatalogOptions`. | | [`start_window_drag`](#teleportstart_window_drag) | Watches for a real app window being dragged (macOS; needs the Accessibility permission). | | Accessor | Returns | Description | | --- | --- | --- | | `prefetch()` | | Warms the catalog's app list and every app's icon in the background (returns at once). Apps call it when they start so the first "Teleport an app…" opens on cached apps and icons. | | `providers()` | [`Vec`](#teleportprovider-record) | Every app this SDK can teleport, with install probes for consent UIs. | | `window_drag_permitted()` | `bool` | Whether this process may watch window drags (macOS Accessibility). | | `window_drag_supported()` | `bool` | Whether window-drag detection exists on this OS (macOS today). | ### `Teleport.app_icon_png` An app's icon as PNG bytes (`size` points), or `None`. **Swift** ```swift func appIconPng(path: String, size: UInt32) -> Data? ``` **Rust** ```rust pub fn app_icon_png(&self, path: String, size: u32) -> Option> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `size` | `u32` | required | **Returns** `Option>` ### `Teleport.capture_window_thumbnail` A PNG preview of that one window (never the screen or another window), at most `max_width` pixels wide, kept in memory for a few seconds (the SDK's preview cache). `None` when it cannot be captured (no Screen Recording permission). **Swift** ```swift func captureWindowThumbnail(windowId: UInt32, maxWidth: UInt32?) throws -> Data? ``` **Rust** ```rust pub fn capture_window_thumbnail(&self, window_id: u32, max_width: Option) -> Result>, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `window_id` | `u32` | required | | `max_width` | `Option` | required | **Returns** `Option>` · **Raises** [`CuaError`]() ### `Teleport.catalog` "Teleport an app…": every installed app on this machine, classified (full, install only, unsupported with a reason), recents first. **Swift** ```swift func catalog(options: TeleportCatalogOptions?) async throws -> [TeleportCatalogEntry] ``` **Rust** ```rust pub async fn catalog(&self, options: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`Option`]() | required | **Returns** [`Vec`]() · **Async** · **Raises** [`CuaError`]() ### `Teleport.catalog_entry_for_name` The catalog row for an app known by name or id (a dragged window whose bundle is unknown). **Swift** ```swift func catalogEntryForName(name: String, options: TeleportCatalogOptions?) throws -> TeleportCatalogEntry ``` **Rust** ```rust pub fn catalog_entry_for_name(&self, name: String, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `options` | [`Option`]() | required | **Returns** [`TeleportCatalogEntry`]() · **Raises** [`CuaError`]() ### `Teleport.catalog_entry_for_path` The catalog row for a dropped app (a bundle, `.desktop` entry or shortcut path). **Swift** ```swift func catalogEntryForPath(path: String, options: TeleportCatalogOptions?) throws -> TeleportCatalogEntry ``` **Rust** ```rust pub fn catalog_entry_for_path(&self, path: String, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `options` | [`Option`]() | required | **Returns** [`TeleportCatalogEntry`]() · **Raises** [`CuaError`]() ### `Teleport.list_windows` This machine's user windows (for a window picker). **Swift** ```swift func listWindows() throws -> [TeleportWindow] ``` **Rust** ```rust pub fn list_windows(&self) -> Result, CuaError> ``` **Returns** [`Vec`]() · **Raises** [`CuaError`]() ### `Teleport.manifest` Describes what teleporting `app` (a bundle id or app name, e.g. `"com.google.Chrome"` or `"Slack"`) would move. Reads the local profile; never prompts. **Swift** ```swift func manifest(app: String, scope: TeleportScope, options: TeleportOptions?) async throws -> TeleportManifest ``` **Rust** ```rust pub async fn manifest(&self, app: String, scope: TeleportScope, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `app` | `String` | required | | `scope` | [`TeleportScope`](#teleportscope-enum) | required | | `options` | [`Option`](#teleportoptions-record) | required | **Returns** [`TeleportManifest`]() · **Async** · **Raises** [`CuaError`]() ### `Teleport.parse_drop` Parses a drag payload: paths, `file://` URIs, URLs or a `text/uri-list` blob. **Swift** ```swift func parseDrop(items: [String]) -> TeleportDrop ``` **Rust** ```rust pub fn parse_drop(&self, items: Vec) -> TeleportDrop ``` | Parameter | Type | Default | | --- | --- | --- | | `items` | `Vec` | required | **Returns** [`TeleportDrop`]() ### `Teleport.plan` What teleporting `app` into `space` will install, send and import, with the consent items. Reads only file sizes and the provider's manifest. **Swift** ```swift func plan(app: TeleportCatalogEntry, space: Space, options: TeleportPlanOptions) async throws -> TeleportPlan ``` **Rust** ```rust pub async fn plan(&self, app: TeleportCatalogEntry, space: Arc, options: TeleportPlanOptions) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `app` | [`TeleportCatalogEntry`]() | required | | `space` | [`Space`]() | required | | `options` | [`TeleportPlanOptions`]() | required | **Returns** [`TeleportPlan`]() · **Async** · **Raises** [`CuaError`]() ### `Teleport.record_recent` Records a teleported app so the catalog lists it first. **Swift** ```swift func recordRecent(id: String, recentsPath: String?) throws ``` **Rust** ```rust pub fn record_recent(&self, id: String, recents_path: Option) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | | `recents_path` | `Option` | required | **Raises** [`CuaError`]() ### `Teleport.request_window_drag_permission` Asks for the window-drag permission (opens System Settings on macOS); returns the state after asking. **Swift** ```swift func requestWindowDragPermission() throws -> Bool ``` **Rust** ```rust pub fn request_window_drag_permission(&self) -> Result ``` **Returns** `bool` · **Raises** [`CuaError`]() ### `Teleport.run` Runs `plan` after the user's `consent` (secrets need `acknowledge_sensitive`), reporting progress to `listener`, and records the app in the recents. **Swift** ```swift func run(plan: TeleportPlan, space: Space, consent: TeleportConsent, listener: TeleportRunListener?) async throws -> TeleportRunReport ``` **Rust** ```rust pub async fn run(&self, plan: TeleportPlan, space: Arc, consent: TeleportConsent, listener: Option>) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `plan` | [`TeleportPlan`]() | required | | `space` | [`Space`]() | required | | `consent` | [`TeleportConsent`]() | required | | `listener` | [`Option`]() | required | **Returns** [`TeleportRunReport`]() · **Async** · **Raises** [`CuaError`]() ### `Teleport.search_catalog` Filters catalog rows by a search query (every word must match the name or id); order is kept. **Swift** ```swift func searchCatalog(entries: [TeleportCatalogEntry], query: String) -> [TeleportCatalogEntry] ``` **Rust** ```rust pub fn search_catalog(&self, entries: Vec, query: String) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `entries` | [`Vec`]() | required | | `query` | `String` | required | **Returns** [`Vec`]() ### `Teleport.send` Teleports `app` into `sandbox` (through its cua-spacesd), a direct upload: the bundle is captured on this machine and sent straight to `sandbox`, never through the Keyvault. `selected_items` are manifest `rel_path`s; `None` sends the default selection. `approval` is asked first (`None` approves); sensitive items then need OS authorization. For an app whose captured items may include a signed-in browser session (cookies, saved passwords), prefer `Self.send_through_keyvault`, which never lets this process or the network see the raw secret. **Swift** ```swift func send(sandbox: Sandbox, app: String, scope: TeleportScope, selectedItems: [String]?, approval: TeleportApproval?, options: TeleportOptions?) async throws -> TeleportResult ``` **Rust** ```rust pub async fn send(&self, sandbox: Arc, app: String, scope: TeleportScope, selected_items: Option>, approval: Option>, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `sandbox` | [`Sandbox`]() | required | | `app` | `String` | required | | `scope` | [`TeleportScope`](#teleportscope-enum) | required | | `selected_items` | `Option>` | required | | `approval` | [`Option`](#teleportapproval) | required | | `options` | [`Option`](#teleportoptions-record) | required | **Returns** [`TeleportResult`](#teleportresult-record) · **Async** · **Raises** [`CuaError`]() ### `Teleport.send_env` Like `Self.send`, to a spacesd connection (for example `Cua.spacesd(url, token)`). **Swift** ```swift func sendEnv(env: SpacesdClient, app: String, scope: TeleportScope, selectedItems: [String]?, approval: TeleportApproval?, options: TeleportOptions?) async throws -> TeleportResult ``` **Rust** ```rust pub async fn send_env(&self, env: Arc, app: String, scope: TeleportScope, selected_items: Option>, approval: Option>, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `env` | [`SpacesdClient`]() | required | | `app` | `String` | required | | `scope` | [`TeleportScope`](#teleportscope-enum) | required | | `selected_items` | `Option>` | required | | `approval` | [`Option`](#teleportapproval) | required | | `options` | [`Option`](#teleportoptions-record) | required | **Returns** [`TeleportResult`](#teleportresult-record) · **Async** · **Raises** [`CuaError`]() ### `Teleport.send_through_keyvault` Teleports `app` into `sandbox`, through the caller's own Cua Keyvault: the session is captured into the Keyvault (sealed, once Touch ID / presence gated) and delivered from there, so this process, the Swift app and the network never see a raw password or cookie. `selected_items` are manifest `rel_path`s; `None` sends the default selection. `approval` is asked first (`None` approves); the Keyvault's own presence prompt follows for a sensitive selection, in place of `Teleporter`'s OS authorization. Fails with `CuaError.Unsupported` when no Keyvault is reachable (the Cua daemon is not running, or this process is not signed as first party): this never falls back to `Self.send`'s direct upload, which would leave the session outside the Keyvault. **Swift** ```swift func sendThroughKeyvault(sandbox: Sandbox, app: String, scope: TeleportScope, selectedItems: [String]?, approval: TeleportApproval?, options: TeleportOptions?) async throws -> TeleportResult ``` **Rust** ```rust pub async fn send_through_keyvault(&self, sandbox: Arc, app: String, scope: TeleportScope, selected_items: Option>, approval: Option>, options: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `sandbox` | [`Sandbox`]() | required | | `app` | `String` | required | | `scope` | [`TeleportScope`](#teleportscope-enum) | required | | `selected_items` | `Option>` | required | | `approval` | [`Option`](#teleportapproval) | required | | `options` | [`Option`](#teleportoptions-record) | required | **Returns** [`TeleportResult`](#teleportresult-record) · **Async** · **Raises** [`CuaError`]() ([`Unsupported`]()) ### `Teleport.space_hint` The Space's OS and CPU, to pass as `space_os` / `space_arch` in `TeleportCatalogOptions`. **Swift** ```swift func spaceHint(space: Space) async throws -> TeleportCatalogOptions ``` **Rust** ```rust pub async fn space_hint(&self, space: Arc) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | [`Space`]() | required | **Returns** [`TeleportCatalogOptions`]() · **Async** · **Raises** [`CuaError`]() ### `Teleport.start_window_drag` Watches for a real app window being dragged (macOS; needs the Accessibility permission). `start` and `end` carry the window and its classified app. **Swift** ```swift func startWindowDrag(listener: TeleportWindowDragListener) throws -> TeleportWindowDragMonitor ``` **Rust** ```rust pub fn start_window_drag(&self, listener: Arc) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `listener` | [`TeleportWindowDragListener`]() | required | **Returns** [`TeleportWindowDragMonitor`]() · **Raises** [`CuaError`]() ## `TeleportApproval` Consent callback: shown the manifest and selection before anything is read; return `false` to abort. Runs on a worker thread and may block (for example on a dialog). It never replaces the OS authorization prompt for sensitive items. **Note** You implement `TeleportApproval` and pass it to the SDK (a callback interface): implement the `TeleportApproval` protocol in Swift or the trait in Rust. | Method | Description | | --- | --- | | [`approve`](#teleportapprovalapprove) | Approve or decline. | ### `TeleportApproval.approve` Approve or decline. **Swift** ```swift func approve(request: TeleportApprovalRequest) -> Bool ``` **Rust** ```rust pub fn approve(&self, request: TeleportApprovalRequest) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `request` | [`TeleportApprovalRequest`](#teleportapprovalrequest-record) | required | **Returns** `bool` ## `TeleportApprovalRequest` record What the approval callback is asked. | Field | Type | Default | Description | | --- | --- | --- | --- | | `manifest` | [`TeleportManifest`]() | | Everything that could move. | | `selected` | [`Vec`]() | | What will move. | | `sensitive` | `bool` | | Whether any selected item is sensitive (the OS prompt follows). | | `destination` | `String` | | The destination spacesd endpoint. | ## `TeleportOptions` record Options for `Teleport.manifest` and `Teleport.send`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `chrome_profile` / `chromeProfile` | `Option` | `None` | Chrome profile to capture: a name ("Profile 1") or a path. Default `Default`. | | `display_name` / `displayName` | `Option` | `None` | Display name for the app (UI only). Default: the app id. | | `launch_after` / `launchAfter` | `Option` | `None` | Launch the app in the sandbox after importing. Default true. | | `close_running_app` / `closeRunningApp` | `bool` | `false` | Ask the sandbox to close a running instance first. | | `relay_plaintext_ack` / `relayPlaintextAck` | `bool` | `false` | Explicit, per-delivery opt-in to send over a `relay:` Space whose image predates end-to-end sealing (S1). Without it, a `relay:` destination fails with a `PermissionDenied` naming the risk (`cua_teleport::send::RELAY_UNSEALED_WARNING`) before anything is read or uploaded. Show that warning and get the user's explicit, per-delivery consent before setting this; never a standing setting. Local and direct Spaces are unaffected. | ## `TeleportProvider` record An app this machine can teleport. Returned by [`Teleport.providers`](#teleport). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Provider id. | | `display_name` / `displayName` | `String` | | Display name. | | `app_ids` / `appIds` | `Vec` | | Bundle ids and app names it matches. | | `macos` | `bool` | | Exports on macOS. | | `linux` | `bool` | | Exports on Linux. | | `windows` | `bool` | | Exports on Windows. | | `install_probe` / `installProbe` | `Option` | | Install check: a path, or a binary name when `install_probe_on_path`. | | `install_probe_on_path` / `installProbeOnPath` | `bool` | | Whether `install_probe` is a `PATH` binary name. | ## `TeleportResult` record The result of `Teleport.send`. Returned by [`Teleport.send`](#teleportsend), [`Teleport.send_env`](#teleportsend_env), [`Teleport.send_through_keyvault`](#teleportsend_through_keyvault). | Field | Type | Default | Description | | --- | --- | --- | --- | | `provider_id` / `providerId` | `String` | | Provider id. | | `import_id` / `importId` | `String` | | Upload id. | | `bundle_bytes` / `bundleBytes` | `u64` | | Bundle size. | | `sha256` | `String` | | Bundle SHA-256 (hex). | | `sent` | `Vec` | | Selected item keys. | | `withheld` | `Vec` | | Item keys the default selection left out. | | `imported` | `Vec` | | What the sandbox imported. | | `skipped` | `Vec` | | What it skipped, as "item: reason". | | `launched` | `bool` | | Whether it launched the app. | ## `TeleportScope` enum How much of an app session to move. **Swift** ```swift .tabs .full ``` **Rust** ```rust TeleportScope::Tabs TeleportScope::Full ``` | Variant | Description | | --- | --- | | `Tabs` | Only the open tabs / documents and session state. | | `Full` | The full profile (may include cookies, logins, history). | --- # Teleport an app The app catalog, plans with consent, runs, drops and window drags. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `Teleport.catalog` lists every app on this machine with what teleport can do (full, install only, unsupported). `plan` says what will be installed, sent and imported, with a consent item per path and secret; `run` executes an approved plan with progress. `parse_drop` and `start_window_drag` feed drag-and-drop onto a Space. ## `teleport` Teleport send: move app sessions from this machine into sandboxes (the Swift app calls it as `cua.teleport()`). **Swift** ```swift func teleport(cua: Cua) -> Teleport ``` **Rust** ```rust pub fn teleport(cua: Arc) -> Arc ``` | Parameter | Type | Default | | --- | --- | --- | | `cua` | [`Cua`]() | required | **Returns** [`Teleport`]() ## `TeleportRunListener` Receives run progress (on an SDK worker thread). **Note** You implement `TeleportRunListener` and pass it to the SDK (a callback interface): implement the `TeleportRunListener` protocol in Swift or the trait in Rust. | Method | Description | | --- | --- | | [`on_event`](#teleportrunlisteneron_event) | | ### `TeleportRunListener.on_event` **Swift** ```swift func onEvent(event: TeleportRunEvent) ``` **Rust** ```rust pub fn on_event(&self, event: TeleportRunEvent) ``` | Parameter | Type | Default | | --- | --- | --- | | `event` | [`TeleportRunEvent`](#teleportrunevent-record) | required | ## `TeleportWindowDragListener` Receives window-drag events on the monitor thread (keep it quick). **Note** You implement `TeleportWindowDragListener` and pass it to the SDK (a callback interface): implement the `TeleportWindowDragListener` protocol in Swift or the trait in Rust. | Method | Description | | --- | --- | | [`on_event`](#teleportwindowdraglisteneron_event) | | ### `TeleportWindowDragListener.on_event` **Swift** ```swift func onEvent(event: TeleportWindowDragEvent) ``` **Rust** ```rust pub fn on_event(&self, event: TeleportWindowDragEvent) ``` | Parameter | Type | Default | | --- | --- | --- | | `event` | [`TeleportWindowDragEvent`](#teleportwindowdragevent-record) | required | ## `TeleportWindowDragMonitor` A running window-drag monitor. `stop` (or dropping it) ends it. Returned by [`Teleport.start_window_drag`](). | Accessor | Returns | Description | | --- | --- | --- | | `stop()` | | Stops watching. | ## `TeleportCatalogEntry` record One row of "Teleport an app…". Returned by [`Teleport.catalog`](), [`Teleport.catalog_entry_for_name`](), [`Teleport.catalog_entry_for_path`](), [`Teleport.search_catalog`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Stable id (`vscode`, `firefox`, or the host bundle id). | | `name` | `String` | | Display name. | | `host_path` / `hostPath` | `Option` | | The host app bundle, `.desktop` entry or shortcut. | | `host_app_id` / `hostAppId` | `Option` | | Host bundle or desktop id. | | `version` | `Option` | | Host app version. | | `capability` | [`TeleportCapability`](#teleportcapability-enum) | | Capability level. | | `reason` | `Option` | | Why it is unsupported, or a caveat. | | `moves` | [`Vec`](#teleportmove-enum) | | Offered moves, in UI order. | | `provider_id` / `providerId` | `Option` | | Provider id, when state can move. | | `sensitive_groups` / `sensitiveGroups` | [`Vec`](#teleportsensitivegroup-enum) | | The opt-in groups the signed-in state move offers (`TeleportPlanOptions.sensitive_groups`). | | `install_source` / `installSource` | `Option` | | `manifest` (pinned install), `image` (ships in cua's images), `space` (must already be there), or none. | | `install_id` / `installId` | `Option` | | Install manifest id and pinned version, for `manifest`. | | `install_version` / `installVersion` | `Option` | | | | `launch_bin` / `launchBin` | `Option` | | Binary started in the Space. | | `last_used_ms` / `lastUsedMs` | `Option` | | Last teleported (Unix ms). | | `json` | `String` | | The entry as JSON (what `plan` reads back). | ## `TeleportCatalogOptions` record Options for `Teleport.catalog`. Returned by [`Teleport.space_hint`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `roots` | `Option>` | `None` | App roots to scan. Default: this machine's (`/Applications`, `~/Applications`; `.desktop` dirs; Start Menu). Tests pass fixture directories. | | `space_os` / `spaceOs` | `Option` | `None` | The Space's OS (`linux`, `macos`, `windows`), to narrow the catalog. | | `space_arch` / `spaceArch` | `Option` | `None` | The Space's CPU (`aarch64`, `x86_64`). | | `recents_path` / `recentsPath` | `Option` | `None` | Recents file. Default: `~/.cua/teleport-recents.json`. | ## `TeleportConsent` record The user's answer to the consent screen. | Field | Type | Default | Description | | --- | --- | --- | --- | | `approved` | `bool` | | Confirmed. | | `acknowledge_sensitive` / `acknowledgeSensitive` | `bool` | `false` | Saw and accepted the secrets. | | `save_to_keyvault` / `saveToKeyvault` | `bool` | `false` | "Save to Keyvault": keep the captured session sealed in the user's own Cua Keyvault after this delivery. | | `acknowledge_relay_plaintext` / `acknowledgeRelayPlaintext` | `bool` | `false` | Saw and accepted `TeleportPlan.relay_unsealed`'s warning (S1). | | `cookie_domains` / `cookieDomains` | `Option>` | `None` | The review's per-site choice: the sites whose cookies to send (`None`: every cookie in the selection). | | `exclude` | `Vec` | `[]` | Consent items (their keys) the user turned off. | | `from_vault` / `fromVault` | `Option>` | `None` | Send these saved Keyvault items (ids) instead of reading the live app: nothing is captured from the host. | | `include_passwords` / `includePasswords` | `bool` | `false` | Also send the saved passwords: only when ticked in the review. | ## `TeleportConsentItem` record One line of the consent screen. | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`TeleportConsentKind`](#teleportconsentkind-enum) | | | | `key` | `String` | | Installable id, host path, or manifest path. | | `label` | `String` | | | | `detail` | `String` | | Version and checksum, destination, or count. | | `bytes` | `u64` | | Bytes that leave this machine. | | `sensitive` | `bool` | | | ## `TeleportDrop` record A parsed drop. Returned by [`Teleport.parse_drop`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | `String` | | `app`, `files`, `url` or `empty`. | | `apps` | `Vec` | | App bundles, `.desktop` entries, shortcuts. | | `files` | `Vec` | | Files and folders. | | `urls` | `Vec` | | URLs. | | `ignored` | `Vec` | | Unusable entries. | ## `TeleportPlan` record Exactly what a teleport will do and move. Returned by [`Teleport.plan`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `app` | [`TeleportCatalogEntry`](#teleportcatalogentry-record) | | | | `space_id` / `spaceId` | `String` | | | | `moves` | [`TeleportMove`](#teleportmove-enum) | | | | `steps` | [`Vec`](#teleportplanstep-record) | | | | `consent` | [`Vec`](#teleportconsentitem-record) | | Every install, path and secret. | | `sensitive` | `bool` | | A consent item is a secret (needs `acknowledge_sensitive`). | | `total_bytes` / `totalBytes` | `u64` | | Bytes that leave this machine. | | `warnings` | `Vec` | | | | `relay_unsealed` / `relayUnsealed` | `bool` | | This Space is reached through a relay connection that predates end-to-end sealing: a secret this plan sends would cross it in the clear (S1). Needs `acknowledge_relay_plaintext` the same way a secret needs `acknowledge_sensitive`. | | `json` | `String` | | The plan as JSON (what `run` reads back). | ## `TeleportPlanOptions` record Options for `Teleport.plan`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `moves` | [`TeleportMove`](#teleportmove-enum) | | What moves. | | `files` | `Vec` | `[]` | Host files or folders (`AppWithFiles`). | | `state_items` / `stateItems` | `Option>` | `None` | Provider manifest items (`AppWithState`); `None` is the provider's default selection. | | `sensitive_groups` / `sensitiveGroups` | [`Vec`](#teleportsensitivegroup-enum) | `[]` | With `state_items` `None`: also these opt-in groups of the entry's `sensitive_groups` (`SignIns` keeps the app signed in). Their items are secrets in the plan's consent: `run` needs `acknowledge_sensitive`. | | `scope` | `Option` | `None` | `tabs` or `full` (default) state. | | `launch` | `Option` | `None` | Launch the app when done (default true). | ## `TeleportPlanStep` record One step of a plan. | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | `String` | | `install`, `files`, `state` or `launch`. | | `summary` | `String` | | Human summary. | ## `TeleportRunEvent` record One progress event of `Teleport.run`. | Field | Type | Default | Description | | --- | --- | --- | --- | | `step` | `u32` | | | | `steps` | `u32` | | | | `kind` | `String` | | `install`, `files`, `state`, `launch`, `done`. | | `phase` | `String` | | `started`, `progress`, `finished`, `failed`, `done`. | | `detail` | `String` | | | | `done_bytes` / `doneBytes` | `u64` | | | | `total_bytes` / `totalBytes` | `u64` | | | ## `TeleportRunReport` record What a run did. Returned by [`Teleport.run`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `app_id` / `appId` | `String` | | | | `installed` | `Vec` | | | | `sent` | `Vec` | | Guest paths of sent files. | | `imported` | `Vec` | | | | `skipped` | `Vec` | | | | `launched` | `bool` | | | ## `TeleportWindow` record One on-screen window of this machine. Returned by [`Teleport.list_windows`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `window_id` / `windowId` | `u32` | | Platform window id (for the thumbnail). | | `pid` | `i64` | | Owning process. | | `app_name` / `appName` | `String` | | Owning app. | | `title` | `String` | | Title (empty without Screen Recording on macOS). | | `bundle_path` / `bundlePath` | `Option` | | The owning app's bundle, when known. | ## `TeleportWindowDragEvent` record A window-drag event (global top-left display points). | Field | Type | Default | Description | | --- | --- | --- | --- | | `phase` | `String` | | `start`, `move` or `end`. | | `x` | `f64` | | | | `y` | `f64` | | | | `window` | [`Option`](#teleportwindow-record) | | The dragged window (`start`, `end`). | | `app` | [`Option`](#teleportcatalogentry-record) | | Its app, classified (`start`, `end`). | | `start_frame` / `startFrame` | [`Option`]() | `None` | The window's frame at mouse down (`start`). | | `frame` | [`Option`]() | `None` | The window's frame now (`start`, `end`, and some `move`s). Compare with `start_frame` to tell a move from a resize (`app_drag_trigger_apply` does). | ## `TeleportCapability` enum What teleport can do with an app. **Swift** ```swift .full .installOnly .unsupported ``` **Rust** ```rust TeleportCapability::Full TeleportCapability::InstallOnly TeleportCapability::Unsupported ``` | Variant | Description | | --- | --- | | `Full` | Its signed-in state can move (a provider), on top of the app. | | `InstallOnly` | Installed in the Space; opens empty or with chosen files. | | `Unsupported` | Shown disabled; `reason` says why. | ## `TeleportConsentKind` enum What a consent item is. **Swift** ```swift .install .file .folder .state .secret ``` **Rust** ```rust TeleportConsentKind::Install TeleportConsentKind::File TeleportConsentKind::Folder TeleportConsentKind::State TeleportConsentKind::Secret ``` | Variant | Description | | --- | --- | | `Install` | An install into the Space (nothing leaves this machine). | | `File` | A host file that leaves this machine. | | `Folder` | A host folder that leaves this machine. | | `State` | App state that leaves this machine. | | `Secret` | A secret (cookies, tokens, logins) that leaves this machine. | ## `TeleportMove` enum What a teleport moves. **Swift** ```swift .appOnly .appWithFiles .appWithState ``` **Rust** ```rust TeleportMove::AppOnly TeleportMove::AppWithFiles TeleportMove::AppWithState ``` | Variant | Description | | --- | --- | | `AppOnly` | The app only. | | `AppWithFiles` | The app plus chosen files or folders. | | `AppWithState` | The app plus its signed-in state (consent lists every path and secret). | ## `TeleportSensitiveGroup` enum Credential-shaped state the signed-in state move leaves out by default, which a person opts into one group at a time (each item is a secret in the plan's consent). **Swift** ```swift .signIns .passwords .history ``` **Rust** ```rust TeleportSensitiveGroup::SignIns TeleportSensitiveGroup::Passwords TeleportSensitiveGroup::History ``` | Variant | Description | | --- | --- | | `SignIns` | The session cookies: what keeps the app signed in. | | `Passwords` | Saved passwords. | | `History` | Browsing history. | --- # Spaces app core The view models the Cua Spaces apps share: Space list, New Space wizard, teleport review, notch and Keyvault. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The Tauri and SwiftUI Cua Spaces apps render the same state from this core, so their behaviour cannot diverge. Build your own Spaces UI on it. Each screen is a state record, a pure `*_reduce` function that applies an action or event, and a `*_view` that derives what to draw. The subpages cover one screen each; the shared formatters are on [Window, menu bar and Settings](). ## `cua_spaces_register` Registers the Cua Spaces extensions (teleport, the Cua Volume, persistent agents, the Keyvault) for every runtime the SDK builds in this process. Idempotent; the SwiftUI app calls it once at launch, before its first `Cua`. **Swift** ```swift func cuaSpacesRegister() ``` **Rust** ```rust pub fn cua_spaces_register() ``` ## `AppRuntimeSwitch` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `setting` | `String` | | The `cua config` setting: `runtime.lume`. | | `value` | `String` | | The value to set: `builtin`. | | `label` | `String` | | The button: "Use built-in Lume". | | `detail` | `String` | | One line on what that does. | ## `app_menu_input_from_json` The menu bar item's menu input from JSON (fixtures, parity). **Swift** ```swift func appMenuInputFromJson(json: String) throws -> AppMenuInput ``` **Rust** ```rust pub fn app_menu_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppMenuInput`]() · **Raises** [`CuaError`]() ## All items 606 items, across this page and its subpages, grouped by page. Methods are listed on their object. **About, updates and launch**: [`app_about_after_launch`](), [`app_about_allowed_channels`](), [`app_about_input_from_json`](), [`app_about_refresh_notice`](), [`app_about_restart_daemon`](), [`app_about_view`](), [`app_daemon_check_from_json`](), [`app_launch_input_from_json`](), [`app_refresh_report_from_json`](), [`AppAboutInput`](), [`AppAboutLink`](), [`AppAboutLinkId`](), [`AppAboutUpdates`](), [`AppAboutView`](), [`AppCliInstaller`](), [`AppCliInstallPlan`](), [`AppCliInstallRequest`](), [`AppDaemonCheck`](), [`AppInstallMethod`](), [`AppLaunchInput`](), [`AppLaunchPlan`](), [`AppRefreshReport`](), [`AppUpdateChannel`]() **Spaces**: [`app_agent_name`](), [`app_ambient_dots`](), [`app_group_windows_by_app`](), [`app_hosted_rows`](), [`app_roster_initial`](), [`app_roster_reduce`](), [`app_rows_to_spaces`](), [`app_sidebar`](), [`app_space_accepts_drop`](), [`app_space_detail`](), [`app_space_detail_for`](), [`app_space_detail_with`](), [`app_space_rows_from_json`](), [`app_spaces_from_json`](), [`app_status_line`](), [`app_this_machine_space`](), [`app_without_this_relay_machine`](), [`AppAgentStatus`](), [`AppAmbientDots`](), [`AppFactWarning`](), [`AppHostSummaryInput`](), [`AppNewSpaceOn`](), [`AppNotice`](), [`AppNoticeKind`](), [`AppOpenApp`](), [`AppOpenWindow`](), [`AppRemoteWindow`](), [`AppRemoteWindowGroup`](), [`AppRosterAction`](), [`AppRosterState`](), [`AppSidebarRow`](), [`AppSidebarSection`](), [`AppSidebarView`](), [`AppSpace`](), [`AppSpaceAgentRun`](), [`AppSpaceDetail`](), [`AppSpaceKind`](), [`AppSpaceOs`](), [`AppSpaceProgress`](), [`AppSpaceProvider`](), [`AppSpaceRow`](), [`AppSpaceSdkRef`](), [`AppSpaceStatus`](), [`AppThumbnailScene`](), [`AppWindowMode`]() **Window, menu bar and Settings**: [`app_agent_settings_rows`](), [`app_agent_setup_outcome`](), [`app_agent_setup_outcomes_from_json`](), [`app_agent_setup_status`](), [`app_agent_setup_statuses_from_json`](), [`app_agent_setup_summary`](), [`app_chrome_input_from_json`](), [`app_delete_failed_text`](), [`app_display_name`](), [`app_display_path`](), [`app_drop_sending_text`](), [`app_drop_sent_text`](), [`app_format_bytes`](), [`app_login_item_launch_plan`](), [`app_main_chrome`](), [`app_menu`](), [`app_menu_bar`](), [`app_menu_bar_with_keyvault`](), [`app_openable_count`](), [`app_sent_file`](), [`app_sent_files_from_json`](), [`app_settings_input_from_json`](), [`app_settings_page`](), [`app_space_detail_copy`](), [`AppAgentSettingsRow`](), [`AppAgentSetupOutcomeInput`](), [`AppAgentSetupStatus`](), [`AppAgentSetupSummary`](), [`AppChromeInput`](), [`AppDeleteConfirm`](), [`AppDetailAction`](), [`AppDetailActionId`](), [`AppDetailCopy`](), [`AppFact`](), [`AppFactCopy`](), [`AppLoginItemInput`](), [`AppLoginItemPlan`](), [`AppLoginItemStatus`](), [`AppMainChrome`](), [`AppMenuInput`](), [`AppMenuItem`](), [`AppMenuItemId`](), [`AppSentFileInfo`](), [`AppSettingsInput`](), [`AppSettingsOption`](), [`AppSettingsPage`](), [`AppSettingsRow`](), [`AppSettingsRowKind`](), [`AppSettingsSection`](), [`AppSignInPhase`](), [`AppTelemetryInput`]() **Devices**: [`app_approve_open`](), [`app_approve_reduce`](), [`app_approve_sheet_action_from_json`](), [`app_approve_view`](), [`app_devices_clean_name`](), [`app_devices_input`](), [`app_devices_input_from_json`](), [`app_devices_view`](), [`app_enroll_action_from_json`](), [`app_enroll_initial`](), [`app_enroll_reduce`](), [`app_enroll_state_from_json`](), [`app_enroll_view`](), [`app_machine_access_notice`](), [`AppActivityRow`](), [`AppApprovalPrompt`](), [`AppApproveRequest`](), [`AppApproveSheetAction`](), [`AppApproveSheetState`](), [`AppApproveSheetView`](), [`AppAuditInput`](), [`AppBannerTone`](), [`AppDeviceAction`](), [`AppDeviceBanner`](), [`AppDeviceConfirm`](), [`AppDeviceInput`](), [`AppDeviceRow`](), [`AppDevicesInput`](), [`AppDevicesLabels`](), [`AppDevicesView`](), [`AppEnrollAction`](), [`AppEnrollmentKind`](), [`AppEnrollMethod`](), [`AppEnrollOption`](), [`AppEnrollPhase`](), [`AppEnrollState`](), [`AppEnrollView`](), [`AppMachineAccessNotice`](), [`AppMachineInput`](), [`AppThisDevice`](), [`AppUnconfirmedMachine`]() **Cua Cloud billing**: [`app_billing_status`](), [`AppBillingCard`](), [`AppBillingCredit`](), [`AppBillingStatus`](), [`AppCreditNotice`]() **Teleport picker**: [`app_catalog_entries_from_json`](), [`app_catalog_entry`](), [`app_picker_can_plan`](), [`app_picker_consent`](), [`app_picker_initial`](), [`app_picker_plan_sensitive`](), [`app_picker_primary`](), [`app_picker_progress`](), [`app_picker_reduce`](), [`app_picker_review`](), [`app_picker_sections`](), [`app_picker_sensitive_options`](), [`app_picker_status`](), [`AppCatalogEntry`](), [`AppEntrySection`](), [`AppPickerAction`](), [`AppPickerEvent`](), [`AppPickerPrimary`](), [`AppPickerState`](), [`AppPickerStep`](), [`AppPickerTab`]() **Your cloud**: [`app_cloud_connect_action_from_json`](), [`app_cloud_connect_initial`](), [`app_cloud_connect_input_from_json`](), [`app_cloud_connect_input_from_status_json`](), [`app_cloud_connect_reduce`](), [`app_cloud_connect_view`](), [`app_connected_cloud_from_json`](), [`app_connected_clouds_from_status_json`](), [`app_is_cloud_word`](), [`AppCloudCheckInput`](), [`AppCloudCheckRow`](), [`AppCloudConnectAction`](), [`AppCloudConnectInput`](), [`AppCloudConnectPhase`](), [`AppCloudConnectRequest`](), [`AppCloudConnectState`](), [`AppCloudConnectView`](), [`AppCloudField`](), [`AppCloudOffer`](), [`AppCloudProviderInput`](), [`AppCloudProviderRow`](), [`AppCloudTargetArgs`](), [`AppConnectedCloud`]() **Creates and deletes**: [`app_creates_compose`](), [`app_creates_is_deleting`](), [`app_creates_is_pending`](), [`app_creates_reduce`](), [`app_creates_settle`](), [`AppCreateAction`](), [`AppCreatesState`](), [`AppPendingCreate`](), [`AppPendingDelete`]() **Power button**: [`app_creates_is_powering`](), [`app_power_button`](), [`AppPendingPower`](), [`AppPowerButton`](), [`AppPowerControl`](), [`AppSpacePower`]() **Stream section and facts**: [`app_desktop_cover`](), [`app_space_detail_live`](), [`app_stream_desktop_row_id`](), [`app_stream_pip_click`](), [`app_stream_pip_reduce`](), [`app_stream_section`](), [`app_thumbnail_policy`](), [`app_usage_refresh_ms`](), [`AppDesktopCover`](), [`AppDesktopCoverInput`](), [`AppDesktopCoverKind`](), [`AppPipCommand`](), [`AppPipEvent`](), [`AppSpaceUsage`](), [`AppStreamDisplay`](), [`AppStreamPhase`](), [`AppStreamRow`](), [`AppStreamRowAction`](), [`AppStreamRowActionId`](), [`AppStreamRowIcon`](), [`AppStreamRowKind`](), [`AppStreamSection`](), [`AppStreamSectionInput`](), [`AppThumbnailPolicy`]() **Window drag trigger**: [`app_drag_classify`](), [`app_drag_displays`](), [`app_drag_portal_displays`](), [`app_drag_trigger_apply`](), [`app_drag_trigger_initial`](), [`AppDragDisplay`](), [`AppDragKind`](), [`AppDragTriggerEvent`](), [`AppDragTriggerState`](), [`AppDragTriggerTransition`](), [`AppStillAnchor`](), [`AppTriggerPhase`]() **Onboarding and settings**: [`app_driver_preview`](), [`app_driver_preview_frame`](), [`app_driver_preview_still`](), [`app_format_hotkey`](), [`app_onboarding_action_from_json`](), [`app_onboarding_copy`](), [`app_onboarding_initial`](), [`app_onboarding_installed_at_text`](), [`app_onboarding_reduce`](), [`app_onboarding_replaces_text`](), [`app_onboarding_sign_in_code_text`](), [`app_onboarding_signed_in_text`](), [`app_onboarding_view`](), [`app_presentation_preview`](), [`app_presentation_preview_frame`](), [`app_presentation_preview_still`](), [`app_settings_load`](), [`app_settings_save`](), [`AppDriverFrame`](), [`AppDriverPreview`](), [`AppKeyCombo`](), [`AppMenuPreview`](), [`AppMenuPreviewRow`](), [`AppModeChoice`](), [`AppOnboardingAction`](), [`AppOnboardingCheckbox`](), [`AppOnboardingCopy`](), [`AppOnboardingMode`](), [`AppOnboardingState`](), [`AppOnboardingStep`](), [`AppOnboardingView`](), [`AppPresentationCard`](), [`AppPresentationPreview`](), [`AppPreviewFrame`](), [`AppPreviewPoint`](), [`AppPreviewSegment`](), [`AppPreviewWindow`](), [`AppSettings`](), [`AppSwitcherTheme`](), [`AppUsageToggle`]() **Experiments**: [`app_experiments_choose`](), [`app_experiments_from_json`](), [`app_experiments_page`](), [`app_settings_with_storage`](), [`app_telemetry_experiments_changed`](), [`app_telemetry_experiments_on`](), [`AppExperiments`]() **This machine**: [`app_host_account_key`](), [`app_host_account_step`](), [`app_host_arch`](), [`app_host_form_action_from_json`](), [`app_host_form_initial`](), [`app_host_form_reduce`](), [`app_host_form_view`](), [`app_host_looks_like_listen`](), [`app_host_panel`](), [`app_host_parse_allow_list`](), [`app_host_permission_rows`](), [`app_host_setting_change`](), [`app_host_setup`](), [`app_host_state`](), [`app_host_state_from_json`](), [`app_host_summary_input`](), [`app_host_validate_setup`](), [`app_with_this_machine`](), [`AppHostAccess`](), [`AppHostAccessRow`](), [`AppHostAccount`](), [`AppHostAccountStep`](), [`AppHostAction`](), [`AppHostActionId`](), [`AppHostClient`](), [`AppHostConfirm`](), [`AppHostFormAction`](), [`AppHostFormChoice`](), [`AppHostFormField`](), [`AppHostFormState`](), [`AppHostFormView`](), [`AppHostLimit`](), [`AppHostPanelView`](), [`AppHostPermissionInput`](), [`AppHostProvidedSpace`](), [`AppHostSettingChange`](), [`AppHostSetupChoice`](), [`AppHostSetupRequest`](), [`AppHostSpacesAudit`](), [`AppHostState`](), [`AppHostToggle`](), [`AppPermissionRow`]() **Spaces app core**: [`app_menu_input_from_json`](#app_menu_input_from_json), [`AppRuntimeSwitch`](#appruntimeswitch-record), [`cua_spaces_register`](#cua_spaces_register) **Notch**: [`app_notch_estimated_progress`](), [`app_notch_initial`](), [`app_notch_layout`](), [`app_notch_motion`](), [`app_notch_radii`](), [`app_notch_reduce`](), [`app_notch_tile_at`](), [`app_notch_view`](), [`app_os_icon_svg`](), [`app_os_icon_system_symbol`](), [`app_presence_name`](), [`app_presence_principal_id`](), [`AppLogicalRect`](), [`AppNotchAccess`](), [`AppNotchActivity`](), [`AppNotchActivityKind`](), [`AppNotchButton`](), [`AppNotchButtonId`](), [`AppNotchEffect`](), [`AppNotchEvent`](), [`AppNotchHeader`](), [`AppNotchLayout`](), [`AppNotchMotion`](), [`AppNotchPermission`](), [`AppNotchPhase`](), [`AppNotchPreview`](), [`AppNotchRadii`](), [`AppNotchState`](), [`AppNotchTab`](), [`AppNotchTile`](), [`AppNotchTransfer`](), [`AppNotchTransition`](), [`AppNotchView`](), [`AppScreenFacts`]() **Teleport picker grid**: [`app_picker_app_grid`](), [`app_picker_grid_primary`](), [`app_picker_grid_step`](), [`app_picker_grid_tabs`](), [`app_picker_remote_grid`](), [`app_picker_window_grid`](), [`AppPickerGrid`](), [`AppPickerGridPrimary`](), [`AppPickerGridTab`](), [`AppPickerGridTabItem`](), [`AppPickerTile`](), [`AppPickerTileIcon`](), [`AppPickerTileSection`](), [`AppPickerTileThumbnail`]() **Teleport review**: [`app_review_remember`](), [`app_review_remember_key`](), [`app_review_remembered`](), [`AppConsentItem`](), [`AppConsentKind`](), [`AppInstallCuaPrompt`](), [`AppPlanStepView`](), [`AppRememberedChoice`](), [`AppReviewChoice`](), [`AppReviewDomain`](), [`AppReviewToggle`](), [`AppReviewView`](), [`AppSendSource`](), [`AppVaultSource`]() **Keyvault**: [`app_sensitive_group_sdk`](), [`AppSensitiveGroup`](), [`AppSensitiveOption`](), [`KeyvaultClient`](), [`KeyvaultOverview`](), [`kv_credential_form`](), [`kv_duration`](), [`kv_labels`](), [`kv_overview_from_json`](), [`kv_passphrase_check`](), [`kv_prune_dismissed`](), [`kv_recovery_key_text`](), [`kv_sharing_label`](), [`kv_signed_in_spaces`](), [`kv_space_access_key`](), [`kv_visible_sharing_label`](), [`KvAccessRequest`](), [`KvAuditEntry`](), [`KvCaller`](), [`KvCommand`](), [`KvCredentialForm`](), [`KvDelivery`](), [`KvDomainCount`](), [`KvFavicon`](), [`KvFormMode`](), [`KvGrant`](), [`KvInventory`](), [`KvItem`](), [`KvItemPolicy`](), [`KvLabels`](), [`KvMethod`](), [`KvOutcome`](), [`KvPassphraseCheck`](), [`KvPending`](), [`KvRule`](), [`KvRuleCaller`](), [`KvSelector`](), [`KvSigning`](), [`KvStatus`](), [`KvStrength`](), [`KvVerification`]() **Share sheet**: [`app_share_action_from_json`](), [`app_share_initial`](), [`app_share_input_from_json`](), [`app_share_reduce`](), [`app_share_state_from_json`](), [`app_share_view`](), [`AppRoleOption`](), [`AppShareEntryInput`](), [`AppShareInput`](), [`AppShareRequest`](), [`AppShareRowView`](), [`AppShareSheetAction`](), [`AppShareSheetState`](), [`AppShareSheetView`]() **Run on**: [`app_space_host`](), [`app_wizard_placement_options`](), [`AppPlacementOption`](), [`AppSpaceHost`]() **Usage telemetry**: [`app_telemetry_creates`](), [`app_telemetry_enroll`](), [`app_telemetry_feature`](), [`app_telemetry_launched`](), [`app_telemetry_onboarding`](), [`app_telemetry_onboarding_finished`](), [`app_telemetry_record`](), [`app_telemetry_share`](), [`app_telemetry_start`](), [`app_telemetry_storage`](), [`app_telemetry_welcome_left`](), [`AppTelemetrySignal`]() **Teleport plan and run**: [`app_teleport_plan`](), [`app_teleport_plan_from_json`](), [`app_teleport_run_event`](), [`app_teleport_run_status`](), [`AppTeleportCapability`](), [`AppTeleportConsent`](), [`AppTeleportMove`](), [`AppTeleportPlan`](), [`AppTeleportRunEvent`](), [`AppTeleportRunPhase`](), [`AppTeleportRunReport`]() **Teleport transfer and drag**: [`app_transfer_reduce`](), [`app_transfer_title`](), [`AppDragOverlayEffect`](), [`AppDragOverlayEvent`](), [`AppDragOverlayPhase`](), [`AppDragOverlayState`](), [`AppDragOverlayTransition`](), [`AppTransferOverlayState`](), [`AppTransferPhase`](), [`AppTransferSignal`](), [`AppTransferStatus`]() **New Space wizard**: [`app_wizard_action_from_json`](), [`app_wizard_create_args`](), [`app_wizard_create_failed_text`](), [`app_wizard_creating_text`](), [`app_wizard_env_from_json`](), [`app_wizard_image_suggestions`](), [`app_wizard_initial`](), [`app_wizard_reduce`](), [`app_wizard_validate_image_ref`](), [`app_wizard_view`](), [`AppAddressForm`](), [`AppAddressSubmit`](), [`AppAddressView`](), [`AppCloudEngine`](), [`AppCloudPricing`](), [`AppCreatePlan`](), [`AppCreateSpaceArgs`](), [`AppGpuChoice`](), [`AppGpuRow`](), [`AppKindOption`](), [`AppLocalEngine`](), [`AppLocalStorage`](), [`AppLocation`](), [`AppMenuOption`](), [`AppRuntime`](), [`AppStepDot`](), [`AppStepState`](), [`AppStepView`](), [`AppStorageVolume`](), [`AppTile`](), [`AppWizardAction`](), [`AppWizardEnv`](), [`AppWizardFact`](), [`AppWizardField`](), [`AppWizardLabels`](), [`AppWizardMode`](), [`AppWizardState`](), [`AppWizardView`]() **Image catalog**: [`AppImageDistro`](), [`AppImageFieldView`](), [`AppImageGroup`](), [`AppImageSizes`](), [`AppImageSuggestion`](), [`AppImageTier`](), [`AppPlatformSize`](), [`AppSandboxImage`](), [`AppSuggestionGroup`]() **Keyvault screens**: [`kv_approval_approve_command`](), [`kv_approval_deny_command`](), [`kv_approval_open`](), [`kv_approval_reduce`](), [`kv_approval_view`](), [`kv_delete_command`](), [`kv_delete_confirm`](), [`kv_list`](), [`kv_live_copy_spaces`](), [`kv_lock_command`](), [`kv_page`](), [`kv_sidebar`](), [`kv_unlock_command`](), [`kv_unlock_prompt`](), [`kv_unlock_prompt_always`](), [`kv_vault_prune`](), [`kv_vault_reduce`](), [`kv_vault_source`](), [`kv_vault_view`](), [`KvAccessKind`](), [`KvAccessRow`](), [`KvApprovalAction`](), [`KvApprovalRow`](), [`KvApprovalState`](), [`KvApprovalView`](), [`KvAppRow`](), [`KvCategory`](), [`KvCategoryRow`](), [`KvDecision`](), [`KvDecisionTone`](), [`KvDeleteConfirm`](), [`KvKind`](), [`KvListView`](), [`KvLock`](), [`KvPage`](), [`KvPendingRow`](), [`KvRecentRow`](), [`KvSelection`](), [`KvSidebar`](), [`KvSigningBadge`](), [`KvTone`](), [`KvTri`](), [`KvUnlockPrompt`](), [`KvVaultAction`](), [`KvVaultApp`](), [`KvVaultFiles`](), [`KvVaultRow`](), [`KvVaultSelection`](), [`KvVaultSite`](), [`KvVaultSource`](), [`KvVaultState`](), [`KvVaultView`]() --- # Space list The Space list (roster), the sidebar, a Space's detail and its remote windows. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The roster is the list of Spaces the app shows, from `Spaces.list` rows. `app_roster_reduce` applies an action; `app_sidebar` and `app_space_detail` derive the main window. ## `AppFactWarning` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `symbol` | `String` | | SF Symbol (`WARNING_SYMBOL`). | | `help` | `String` | | Tooltip and accessibility label. | ## `app_rows_to_spaces` Registry rows (`Spaces.list`) as Spaces. **Swift** ```swift func appRowsToSpaces(rows: [AppSpaceRow], nowMs: Int64) -> [AppSpace] ``` **Rust** ```rust pub fn app_rows_to_spaces(rows: Vec, now_ms: i64) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `rows` | [`Vec`](#appspacerow-record) | required | | `now_ms` | `i64` | required | **Returns** [`Vec`](#appspace-record) ## `app_status_line` The menu bar status line ("3 Spaces"). **Swift** ```swift func appStatusLine(count: UInt32) -> String ``` **Rust** ```rust pub fn app_status_line(count: u32) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `count` | `u32` | required | **Returns** `String` ## `app_this_machine_space` The "This machine" roster entry. **Swift** ```swift func appThisMachineSpace(status: AppHostSummaryInput?, nowMs: Int64) -> AppSpace ``` **Rust** ```rust pub fn app_this_machine_space(status: Option, now_ms: i64) -> AppSpace ``` | Parameter | Type | Default | | --- | --- | --- | | `status` | [`Option`](#apphostsummaryinput-record) | required | | `now_ms` | `i64` | required | **Returns** [`AppSpace`](#appspace-record) ## `AppHostSummaryInput` record Returned by [`app_host_summary_input`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `configured` | `bool` | | Set up for access. | | `error` | `Option` | | Needs attention. | | `service_running` / `serviceRunning` | `bool` | | The host service runs. | | `sharing` | `bool` | | Sharing is on. | | `clients` | `u32` | | Connected clients. | | `mode` | `Option` | | `relay` or `direct`. | | `online` | `Option` | | Relay presence. | | `paused_signed_out` / `pausedSignedOut` | `bool` | | Relay sharing is paused while signed out. | ## `app_agent_name` A coding agent's display name. **Swift** ```swift func appAgentName(agent: String) -> String ``` **Rust** ```rust pub fn app_agent_name(agent: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `agent` | `String` | required | **Returns** `String` ## `AppAgentStatus` enum **Swift** ```swift .running .idle .failed .crashed .unknown ``` **Rust** ```rust AppAgentStatus::Running AppAgentStatus::Idle AppAgentStatus::Failed AppAgentStatus::Crashed AppAgentStatus::Unknown ``` | Variant | Description | | --- | --- | | `Running` | A turn is running. | | `Idle` | Waiting for a follow-up. | | `Failed` | Failed. | | `Crashed` | Crashed. | | `Unknown` | Could not be read. | ## `app_ambient_dots` The ambient status dots. **Swift** ```swift func appAmbientDots(spaces: [AppSpace]) -> AppAmbientDots ``` **Rust** ```rust pub fn app_ambient_dots(spaces: Vec) -> AppAmbientDots ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | [`Vec`](#appspace-record) | required | **Returns** [`AppAmbientDots`](#appambientdots-record) ## `AppAmbientDots` record Returned by [`app_ambient_dots`](#app_ambient_dots). | Field | Type | Default | Description | | --- | --- | --- | --- | | `running` | `bool` | | Any Space running or starting. | | `approval` | `bool` | | Any Space waiting for approval. | | `suspended` | `bool` | | Any Space suspended. | | `agent_active` / `agentActive` | `bool` | | Any agent actively working. | ## `app_group_windows_by_app` A Space's remote windows grouped by app. **Swift** ```swift func appGroupWindowsByApp(windows: [AppRemoteWindow]) -> [AppRemoteWindowGroup] ``` **Rust** ```rust pub fn app_group_windows_by_app(windows: Vec) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `windows` | [`Vec`](#appremotewindow-record) | required | **Returns** [`Vec`](#appremotewindowgroup-record) ## `AppOpenApp` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `app_id` / `appId` | `String` | | App id. | | `app_name` / `appName` | `String` | | App name. | | `supported` | `bool` | | Supported. | ## `AppOpenWindow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `window_id` / `windowId` | `u32` | | CoreGraphics window number. | | `app_id` / `appId` | `String` | | App id. | | `app_name` / `appName` | `String` | | App name. | | `window_title` / `windowTitle` | `String` | | Window title. | | `supported` | `bool` | | Teleport can bring the app up in a Space. | | `bundle_path` / `bundlePath` | `Option` | | The owning app's bundle (or `.desktop` entry), when known. | ## `AppThumbnailScene` enum **Swift** ```swift .macDesktop .windowsDesktop .linuxTerminal .notes .browser .blank ``` **Rust** ```rust AppThumbnailScene::MacDesktop AppThumbnailScene::WindowsDesktop AppThumbnailScene::LinuxTerminal AppThumbnailScene::Notes AppThumbnailScene::Browser AppThumbnailScene::Blank ``` | Variant | Description | | --- | --- | | `MacDesktop` | A macOS desktop. | | `WindowsDesktop` | A Windows desktop. | | `LinuxTerminal` | A Linux terminal. | | `Notes` | A notes app. | | `Browser` | A browser. | | `Blank` | Nothing. | ## `AppWindowMode` enum **Swift** ```swift .ambient .ambientTeleport .switcher .createFleet ``` **Rust** ```rust AppWindowMode::Ambient AppWindowMode::AmbientTeleport AppWindowMode::Switcher AppWindowMode::CreateFleet ``` | Variant | Description | | --- | --- | | `Ambient` | | | `AmbientTeleport` | Ambient, but grown taller so the drag teleport's "Teleport to Cua" box has room to hang below the notch. Same top-centre placement as `Ambient`. | | `Switcher` | | | `CreateFleet` | | ## `AppNotice` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `u32` | | Sequence id (clear by id). | | `kind` | [`AppNoticeKind`](#appnoticekind-enum) | | Kind. | | `text` | `String` | | Text. | ## `AppNoticeKind` enum **Swift** ```swift .switch .create ``` **Rust** ```rust AppNoticeKind::Switch AppNoticeKind::Create ``` | Variant | Description | | --- | --- | | `Switch` | Switched Spaces. | | `Create` | Created (or creating) Spaces. | ## `AppNewSpaceOn` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | The button's label. | | `on` | `String` | | The "Run on" entry it picks (`host:`; the wizard's `ChoosePlacement`). | ## `app_hosted_rows` The Spaces one of your machines provides (`machine_space_id` is its `relay:`), as sidebar rows. **Swift** ```swift func appHostedRows(spaces: [AppSpace], machineSpaceId: String, selectedId: String) -> [AppSidebarRow] ``` **Rust** ```rust pub fn app_hosted_rows(spaces: Vec, machine_space_id: String, selected_id: String) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | [`Vec`](#appspace-record) | required | | `machine_space_id` | `String` | required | | `selected_id` | `String` | required | **Returns** [`Vec`](#appsidebarrow-record) ## `app_without_this_relay_machine` The roster without this machine's own relay entry (`this_machine_id` is `HostStatus.machine_id`): it is "This machine", not one of "My machines". Matched by id, never by name. **Swift** ```swift func appWithoutThisRelayMachine(spaces: [AppSpace], thisMachineId: String?) -> [AppSpace] ``` **Rust** ```rust pub fn app_without_this_relay_machine(spaces: Vec, this_machine_id: Option) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | [`Vec`](#appspace-record) | required | | `this_machine_id` | `Option` | required | **Returns** [`Vec`](#appspace-record) ## `AppRemoteWindow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Stable key. | | `app_name` / `appName` | `String` | | App name. | | `title` | `String` | | Title. | | `visible` | `bool` | | Visible. | | `app_id` / `appId` | `String` | | App id. | | `target_epoch` / `targetEpoch` | `u64` | | Window handle epoch. | | `width_px` / `widthPx` | `Option` | | Width, px. | | `height_px` / `heightPx` | `Option` | | Height, px. | | `pid` | `Option` | | The owning process (what the SDK's `Space.app_icon` looks a macOS app up by), when the window list reports one. | ## `AppRemoteWindowGroup` record Returned by [`app_group_windows_by_app`](#app_group_windows_by_app). | Field | Type | Default | Description | | --- | --- | --- | --- | | `app_id` / `appId` | `String` | | App id. | | `app_name` / `appName` | `String` | | App name. | | `windows` | [`Vec`](#appremotewindow-record) | | Windows, arrival order. | ## `AppRosterState` record Returned by [`app_roster_initial`](#app_roster_initial), [`app_roster_reduce`](#app_roster_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `mode` | [`AppWindowMode`](#appwindowmode-enum) | | The notch switcher's mode. | | `spaces` | [`Vec`](#appspace-record) | | Spaces, MRU order. | | `selected_id` / `selectedId` | `String` | | The selected Space id (empty when none). | | `focus_index` / `focusIndex` | `i32` | | Keyboard focus: an index into `spaces`, or `NEW_TILE_INDEX`. | | `notice` | [`Option`](#appnotice-record) | | The notice, if one shows. | | `notice_seq` / `noticeSeq` | `u32` | | Last notice id. | ## `AppSidebarRow` record Returned by [`app_hosted_rows`](#app_hosted_rows). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Space id. | | `name` | `String` | | Name. | | `os_icon` / `osIcon` | `String` | | The Space's OS icon id (`crate.notch.os_icon`, the same mark the notch tiles use), before the name. | | `status` | [`AppSpaceStatus`](#appspacestatus-enum) | | Status (the dot). | | `status_text` / `statusText` | `String` | | Status word (the dot's accessibility label). | | `detail` | `String` | | Tooltip. | | `dim` | `bool` | | Drawn at reduced opacity. | | `selected` | `bool` | | Selected. | | `progress` | `Option` | | While it is being created: overall progress in thousandths (a ring). | | `trailing` | `Option` | | Short text after the name: the percentage while it is being created, the error when the create failed. | | `nested` | `bool` | | A Space one of your machines provides, drawn nested under that machine's row (the row before it). | | `power` | [`Option`]() | | The power button (next to Delete), for a Space whose provider can turn it off and on. | | `place` | `Option` | | Where a Space in your cloud runs ("AWS · us-west-2"), secondary text after the name. | ## `AppSidebarSection` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Cua Cloud", "This Mac", "Connected", "My machines". | | `rows` | [`Vec`](#appsidebarrow-record) | | Rows. | ## `AppSidebarView` record Returned by [`app_sidebar`](#app_sidebar). | Field | Type | Default | Description | | --- | --- | --- | --- | | `this_machine` / `thisMachine` | [`Option`](#appsidebarrow-record) | | The "This machine" row, when it matches the search. | | `sections` | [`Vec`](#appsidebarsection-record) | | Location sections, in `SECTION_ORDER`. | | `selected_id` / `selectedId` | `Option` | | The effective selection (falls back to the first Space). | | `empty_text` / `emptyText` | `Option` | | "No matches" / "No Spaces yet" when nothing is listed. | ## `AppSpace` record Returned by [`app_creates_compose`](), [`app_rows_to_spaces`](#app_rows_to_spaces), [`app_spaces_from_json`](#app_spaces_from_json), [`app_this_machine_space`](#app_this_machine_space), [`app_with_this_machine`](), [`app_without_this_relay_machine`](#app_without_this_relay_machine). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Sandbox ref (`cloud:`, `local:`, `direct:`, ...). | | `name` | `String` | | Display name. | | `os` | [`AppSpaceOs`](#appspaceos-enum) | | Operating system. | | `status` | [`AppSpaceStatus`](#appspacestatus-enum) | | Status. | | `detail` | `String` | | One short status detail. | | `last_used_at` / `lastUsedAt` | `i64` | | Epoch ms of the last focus. | | `started_at` / `startedAt` | `Option` | | Epoch ms it was created. | | `scene` | [`AppThumbnailScene`](#appthumbnailscene-enum) | | Placeholder scene. | | `fleet_id` / `fleetId` | `Option` | | Group (cloud namespace or location word). | | `size` | `Option` | | Fixture machine size. | | `region` | `Option` | | Fixture region. | | `provider` | [`Option`](#appspaceprovider-enum) | | Where it runs (cloud when omitted). | | `sdk` | [`Option`](#appspacesdkref-record) | | SDK facts, for Spaces from the live registry. | | `os_name` / `osName` | `Option` | | The guest OS product or distribution ("Ubuntu", "macOS"), when the Space reported one: it picks the OS icon (`crate.notch.os_icon`). | | `os_pretty_name` / `osPrettyName` | `Option` | | The guest's full OS string ("Ubuntu 24.04.3 LTS"), when the Space reported one. | | `image` | `Option` | | The image it runs ("ghcr.io/trycua/linux:24.04"), when known. | | `image_digest` / `imageDigest` | `Option` | | The digest of the variant that runs, when known. | | `progress` | [`Option`](#appspaceprogress-record) | | While it is being created: how far along, from the SDK's create progress (`crate.spaces.creating`). | | `kind` | [`Option`](#appspacekind-enum) | | Container or virtual machine, when known. | | `arch` | `Option` | | The guest's CPU architecture (`arm64`, `amd64`), when known. | | `host` | `Option` | | For a Space one of your machines provides: that machine's relay id (the sidebar nests it under the machine). | | `host_name` / `hostName` | `Option` | | That machine's name, when known. | | `power` | [`Option`]() | | Whether and how it turns off and on (the SDK's `power`), with a power action in flight (`crate.spaces.creating.compose`). `None`: it cannot (a cloud Space, one added by address). | | `cloud` | `Option` | | For a Space in your cloud: the provider word (`aws`, `gcp`, `modal`). | | `cloud_place` / `cloudPlace` | `Option` | | Its account and region in words ("AWS · us-west-2"). | | `cloud_delete` / `cloudDelete` | `Option` | | Where Delete permanently can delete it: `here`, `host:` (that machine created it) or `elsewhere` (another device did). | ## `AppSpaceAgentRun` record Returned by [`app_agent_rows`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `run_id` / `runId` | `String` | | Run id. | | `agent` | `String` | | Harness id. | | `status` | [`AppAgentStatus`](#appagentstatus-enum) | | Status. | | `reason` | `String` | | Why. | | `summary` | `String` | | Prompt summary. | | `created_at` / `createdAt` | `Option` | | Unix seconds. | | `phase` | `String` | | Finer phase. | | `turn` | `u32` | | Turns. | ## `AppSpaceDetail` record Returned by [`app_space_detail`](#app_space_detail), [`app_space_detail_for`](#app_space_detail_for), [`app_space_detail_live`](), [`app_space_detail_with`](#app_space_detail_with). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Space id. | | `title` | `String` | | Title. | | `facts` | [`Vec`]() | | Status, Image, System, Memory, Storage, Identifier (each when known). | | `is_host` / `isHost` | `bool` | | The host machine itself. | | `show_sections` / `showSections` | `bool` | | The Stream / Agents / Teleport sections show. | | `can_stream` / `canStream` | `bool` | | Streaming is possible (toolbar PiP and Stream). | | `preview_text` / `previewText` | `String` | | Placeholder over the preview until a frame arrives. | | `progress` | `Option` | | While it is being created: overall progress in thousandths (the preview's ring). | | `progress_text` / `progressText` | `Option` | | Under the bar while it downloads: "4.2 of 22.1 GB · 85 MB/s · about 4 min". | | `delete_label` / `deleteLabel` | `String` | | "Remove" (added by address) or "Delete Space". | | `remove_only` / `removeOnly` | `bool` | | Removing only forgets it. | | `actions` | [`Vec`]() | | Toolbar buttons, in order (none for this machine). | | `confirm` | [`AppDeleteConfirm`]() | | What Delete asks first. | | `sections` | `Vec` | | The sections under the facts, in order (Stream, Agents, Teleport). | | `credit_notice` / `creditNotice` | [`Option`]() | | A cloud Space refused for want of credit: one line and "Add credit" (the website billing page). | | `power_error` / `powerError` | `Option` | | Why turning it off or on failed, shown inline under the preview. | | `access` | [`Option`]() | | Signed in, but this device is not enrolled: a machine reached through the relay is listed with its Connect greyed out under this line and its one action (`detail_for`). | | `desktop_note` / `desktopNote` | `Option` | | One of your machines that does not share its desktop: the line shown in place of its desktop, Stream, Agents and Teleport (`desktop_note`). | | `new_space` / `newSpace` | [`Option`](#appnewspaceon-record) | | With `Self.desktop_note`, when the machine provides Spaces: "New Space on <name>…", which opens New Space on it. | ## `AppSpaceProgress` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `phase` | `String` | | The SDK's phase word (`preparing`, `pulling`, `booting`, ...). | | `permille` | `u32` | | Overall progress in thousandths. | | `label` | `String` | | The phase in words ("Starting…", "Downloading image…"), or "Failed". | | `error` | `Option` | | Why the create failed. | | `credit_url` / `creditUrl` | `Option` | | It failed because the account is out of Cua Cloud credit: the website billing page "Add credit" opens. | | `transfer` | `Option` | | While it downloads: "4.2 of 22.1 GB · 85 MB/s · about 4 min" (`crate.spaces.creating.transfer_text`). | | `cancellable` | `bool` | | Cancel can stop it now. | | `cancelling` | `bool` | | Cancel was pressed and the create is being cleaned up. | ## `AppSpaceRow` record Returned by [`app_space_rows_from_json`](#app_space_rows_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Sandbox ref. | | `name` | `String` | | Name spacesd reported. | | `provider` | `String` | | Location word. | | `spacesd_version` / `spacesdVersion` | `String` | | spacesd version. | | `features` | `Vec` | | spacesd features. | | `added_at` / `addedAt` | `Option` | | RFC 3339 time it was added. | | `os` | [`Option`](#appspaceos-enum) | | Operating system. | | `os_name` / `osName` | `Option` | | OS product or distribution spacesd reported ("Ubuntu"). | | `os_pretty_name` / `osPrettyName` | `Option` | | The guest's full OS string ("Ubuntu 24.04.3 LTS"), when the Space reported one. | | `image` | `Option` | | The image it runs ("ghcr.io/trycua/linux:24.04"), when known. | | `image_digest` / `imageDigest` | `Option` | | The digest of the variant that runs, when known. | | `kind` | [`Option`](#appspacekind-enum) | | Container or virtual machine, when known. | | `arch` | `Option` | | The guest's CPU architecture, when known (any spelling: `arm64`, `aarch64`, `amd64`, `x86_64`). | | `reachable` | `bool` | | Answered the last connect. | | `error` | `Option` | | Why not. | | `host` | `Option` | | For a Space one of your machines provides: that machine's relay id. | | `host_name` / `hostName` | `Option` | | That machine's name, when known. | | `power` | `Option` | | How it turns off and on (`suspend`, `stop`); absent when it cannot. | | `power_state` / `powerState` | `Option` | | `running`, `suspended` or `stopped` as the SDK recorded it; absent when unknown. | | `cloud` | `Option` | | For a Space in your cloud: the provider word (`aws`, `gcp`, `modal`). | | `cloud_place` / `cloudPlace` | `Option` | | Its account and region in words ("AWS · us-west-2"). | | `cloud_delete` / `cloudDelete` | `Option` | | Where Delete permanently can delete it: `here`, `host:` (that machine created it) or `elsewhere` (another device did). | ## `AppSpaceSdkRef` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `features` | `Vec` | | spacesd feature names. | | `spacesd_version` / `spacesdVersion` | `String` | | spacesd version at the last handshake. | | `reachable` | `bool` | | Answered the last bounded connect. | | `error` | `Option` | | Why not. | ## `AppRosterAction` enum **Swift** ```swift .expand .collapse .openCreate .cancelCreate .focus(index: Int32) .focusMove(delta: Int32) .select(id: String, now: Int64) .clearNotice(id: UInt32) .notify(text: String, kind: AppNoticeKind?) .syncSpaces(spaces: [AppSpace]) ``` **Rust** ```rust AppRosterAction::Expand AppRosterAction::Collapse AppRosterAction::OpenCreate AppRosterAction::CancelCreate AppRosterAction::Focus { index: i32 } AppRosterAction::FocusMove { delta: i32 } AppRosterAction::Select { id: String, now: i64 } AppRosterAction::ClearNotice { id: u32 } AppRosterAction::Notify { text: String, kind: Option } AppRosterAction::SyncSpaces { spaces: Vec } ``` | Variant | Description | | --- | --- | | `Expand` | Open the switcher. | | `Collapse` | Back to ambient. | | `OpenCreate` | Open the create panel. | | `CancelCreate` | Close it. | | `Focus` | Focus a tile. Fields: `index: i32` | | `FocusMove` | Move focus by one, wrapping through the New tile. Fields: `delta: i32` | | `Select` | Select a Space (touches its MRU time). Fields: `id: String`, `now: i64` | | `ClearNotice` | Clear the notice with this id. Fields: `id: u32` | | `Notify` | Show a notice. Fields: `text: String`, `kind: Option` | | `SyncSpaces` | Replace the list with a registry refresh. Fields: `spaces: Vec` | ## `AppSpaceKind` enum **Swift** ```swift .container .vm ``` **Rust** ```rust AppSpaceKind::Container AppSpaceKind::Vm ``` | Variant | Description | | --- | --- | | `Container` | A container. | | `Vm` | A virtual machine. | ## `AppSpaceOs` enum **Swift** ```swift .macos .windows .linux .unknown ``` **Rust** ```rust AppSpaceOs::Macos AppSpaceOs::Windows AppSpaceOs::Linux AppSpaceOs::Unknown ``` | Variant | Description | | --- | --- | | `Macos` | macOS. | | `Windows` | Windows. | | `Linux` | Linux. | | `Unknown` | The host has not reported a recognized operating system. | ## `AppSpaceProvider` enum **Swift** ```swift .cloud .local .direct .relay ``` **Rust** ```rust AppSpaceProvider::Cloud AppSpaceProvider::Local AppSpaceProvider::Direct AppSpaceProvider::Relay ``` | Variant | Description | | --- | --- | | `Cloud` | Cua Cloud. | | `Local` | This Mac. | | `Direct` | A machine added by address. | | `Relay` | A machine of the signed-in account through the relay. | ## `AppSpaceStatus` enum **Swift** ```swift .local .running .approval .suspended .provisioning .deleting ``` **Rust** ```rust AppSpaceStatus::Local AppSpaceStatus::Running AppSpaceStatus::Approval AppSpaceStatus::Suspended AppSpaceStatus::Provisioning AppSpaceStatus::Deleting ``` | Variant | Description | | --- | --- | | `Local` | The host machine itself. | | `Running` | Reachable, session active. | | `Approval` | An agent waits for approval. | | `Suspended` | Unreachable or suspended. | | `Provisioning` | Being created. | | `Deleting` | Being deleted (the user confirmed; the SDK is removing it). | ## `app_roster_initial` The Space list's first state. **Swift** ```swift func appRosterInitial(spaces: [AppSpace]) -> AppRosterState ``` **Rust** ```rust pub fn app_roster_initial(spaces: Vec) -> AppRosterState ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | [`Vec`](#appspace-record) | required | **Returns** [`AppRosterState`](#approsterstate-record) ## `app_roster_reduce` Advances the Space list. **Swift** ```swift func appRosterReduce(state: AppRosterState, action: AppRosterAction) -> AppRosterState ``` **Rust** ```rust pub fn app_roster_reduce(state: AppRosterState, action: AppRosterAction) -> AppRosterState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppRosterState`](#approsterstate-record) | required | | `action` | [`AppRosterAction`](#approsteraction-enum) | required | **Returns** [`AppRosterState`](#approsterstate-record) ## `app_sidebar` The main window's sidebar. **Swift** ```swift func appSidebar(spaces: [AppSpace], query: String, selectedId: String) -> AppSidebarView ``` **Rust** ```rust pub fn app_sidebar(spaces: Vec, query: String, selected_id: String) -> AppSidebarView ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | [`Vec`](#appspace-record) | required | | `query` | `String` | required | | `selected_id` | `String` | required | **Returns** [`AppSidebarView`](#appsidebarview-record) ## `app_space_accepts_drop` Whether a Space takes a teleport drop (the notch tiles' and the Space rows' rule). **Swift** ```swift func appSpaceAcceptsDrop(space: AppSpace) -> Bool ``` **Rust** ```rust pub fn app_space_accepts_drop(space: AppSpace) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | [`AppSpace`](#appspace-record) | required | **Returns** `bool` ## `app_space_detail` A Space's detail. **Swift** ```swift func appSpaceDetail(space: AppSpace) -> AppSpaceDetail ``` **Rust** ```rust pub fn app_space_detail(space: AppSpace) -> AppSpaceDetail ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | [`AppSpace`](#appspace-record) | required | **Returns** [`AppSpaceDetail`](#appspacedetail-record) ## `app_space_detail_for` `app_space_detail_with` as this device sees it: `access` (from `app_machine_access_notice`) greys out Connect on every Space reached through the relay while this device is not enrolled; one of your machines that does not share its desktop shows its note in place of the desktop, Stream, Agents and Teleport. **Swift** ```swift func appSpaceDetailFor(space: AppSpace, usage: AppSpaceUsage?, hostArch: String?, experiments: AppExperiments, access: AppMachineAccessNotice?) -> AppSpaceDetail ``` **Rust** ```rust pub fn app_space_detail_for(space: AppSpace, usage: Option, host_arch: Option, experiments: AppExperiments, access: Option) -> AppSpaceDetail ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | [`AppSpace`](#appspace-record) | required | | `usage` | [`Option`]() | required | | `host_arch` | `Option` | required | | `experiments` | [`AppExperiments`]() | required | | `access` | [`Option`]() | required | **Returns** [`AppSpaceDetail`](#appspacedetail-record) ## `app_space_detail_with` `app_space_detail_live` without what Settings, Experiments hides (the Share button while Sharing is off). **Swift** ```swift func appSpaceDetailWith(space: AppSpace, usage: AppSpaceUsage?, hostArch: String?, experiments: AppExperiments) -> AppSpaceDetail ``` **Rust** ```rust pub fn app_space_detail_with(space: AppSpace, usage: Option, host_arch: Option, experiments: AppExperiments) -> AppSpaceDetail ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | [`AppSpace`](#appspace-record) | required | | `usage` | [`Option`]() | required | | `host_arch` | `Option` | required | | `experiments` | [`AppExperiments`]() | required | **Returns** [`AppSpaceDetail`](#appspacedetail-record) ## `app_space_rows_from_json` Registry rows from JSON (fixtures, parity flows). **Swift** ```swift func appSpaceRowsFromJson(json: String) throws -> [AppSpaceRow] ``` **Rust** ```rust pub fn app_space_rows_from_json(json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`Vec`](#appspacerow-record) · **Raises** [`CuaError`]() ## `app_spaces_from_json` Spaces from the webview's JSON shape (fixtures, parity flows). **Swift** ```swift func appSpacesFromJson(json: String) throws -> [AppSpace] ``` **Rust** ```rust pub fn app_spaces_from_json(json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`Vec`](#appspace-record) · **Raises** [`CuaError`]() --- # Creates and deletes Spaces being created or deleted: the rows they show at once, their progress and how they hand over to the registry. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_creates_reduce` applies a create or delete action (a start, the SDK's create progress, a timer tick, the result) and `app_creates_compose` adds the pending rows to the registry's Spaces. Progress moves by the SDK's fractions, else by the time each phase usually takes for the image, from the `now` the shell passes. ## `AppCreatesState` record Returned by [`app_creates_reduce`](#app_creates_reduce), [`app_creates_settle`](#app_creates_settle). | Field | Type | Default | Description | | --- | --- | --- | --- | | `pending` | [`Vec`](#apppendingcreate-record) | | Oldest first. | | `deleting` | [`Vec`](#apppendingdelete-record) | | Spaces being deleted, oldest first. | | `powering` | [`Vec`]() | | Spaces being turned off or on (or whose last try failed). | ## `AppCreateAction` enum **Swift** ```swift .start(id: String, name: String, os: AppSpaceOs, provider: AppSpaceProvider, now: Int64, image: String?, kind: AppSpaceKind?, hostArch: String?, gpu: Bool) .progress(id: String, phase: String, fraction: Double?, now: Int64?, bytesDone: UInt64?, bytesTotal: UInt64?, bytesPerSecond: Double?) .cancelStart(id: String) .cancelDone(id: String) .cancelFail(id: String, error: String) .tick(now: Int64) .finish(id: String, spaceId: String) .fail(id: String, error: String) .dismiss(id: String) .deleteStart(id: String, now: Int64) .deleteFail(id: String) .deleteDone(id: String) .powerStart(id: String, on: Bool, now: Int64) .powerDone(id: String) .powerFail(id: String, error: String) ``` **Rust** ```rust AppCreateAction::Start { id: String, name: String, os: AppSpaceOs, provider: AppSpaceProvider, now: i64, image: Option, kind: Option, host_arch: Option, gpu: bool } AppCreateAction::Progress { id: String, phase: String, fraction: Option, now: Option, bytes_done: Option, bytes_total: Option, bytes_per_second: Option } AppCreateAction::CancelStart { id: String } AppCreateAction::CancelDone { id: String } AppCreateAction::CancelFail { id: String, error: String } AppCreateAction::Tick { now: i64 } AppCreateAction::Finish { id: String, space_id: String } AppCreateAction::Fail { id: String, error: String } AppCreateAction::Dismiss { id: String } AppCreateAction::DeleteStart { id: String, now: i64 } AppCreateAction::DeleteFail { id: String } AppCreateAction::DeleteDone { id: String } AppCreateAction::PowerStart { id: String, on: bool, now: i64 } AppCreateAction::PowerDone { id: String } AppCreateAction::PowerFail { id: String, error: String } ``` | Variant | Description | | --- | --- | | `Start` | A create started: its row shows now. Fields: `id: String`, `name: String`, `os: AppSpaceOs`, `provider: AppSpaceProvider`, `now: i64`, `image: Option`, `kind: Option`, `host_arch: Option`, `gpu: bool` | | `Progress` | The SDK reported progress. Fields: `id: String`, `phase: String`, `fraction: Option`, `now: Option`, `bytes_done: Option`, `bytes_total: Option`, `bytes_per_second: Option` | | `CancelStart` | Cancel was pressed: the shell asks the SDK to cancel the create (`cancel_create` with the pending id, passed as the create's `create_id`); the row shows Cancelling until the create ends. Fields: `id: String` | | `CancelDone` | The cancel finished (or the create ended cancelled): the row goes. Fields: `id: String` | | `CancelFail` | The cancel itself failed: the row shows why. Fields: `id: String`, `error: String` | | `Tick` | Time passed (a shell's timer, a few times a second while a create is pending): progress within a phase that reports no fraction advances with the time the phase usually takes. Fields: `now: i64` | | `Finish` | The create returned the Space. Fields: `id: String`, `space_id: String` | | `Fail` | The create failed. Fields: `id: String`, `error: String` | | `Dismiss` | Remove a failed row. Fields: `id: String` | | `DeleteStart` | The user confirmed Delete: the row shows Deleting now. A second start for a Space already deleting does nothing. Fields: `id: String`, `now: i64` | | `DeleteFail` | The delete failed: the row is back as the registry lists it. Fields: `id: String` | | `DeleteDone` | The delete returned: the row goes now, and stays gone while the registry still lists the Space. Fields: `id: String` | | `PowerStart` | The power button was pressed: the row says Suspending, Resuming, Turning off or Turning on now and the button waits. A second press while one runs does nothing. Fields: `id: String`, `on: bool`, `now: i64` | | `PowerDone` | The SDK's stop or start returned: the row keeps saying so until the registry shows the Space on (or off). Fields: `id: String` | | `PowerFail` | It failed: the row shows why, inline, and the button works again. Fields: `id: String`, `error: String` | ## `AppPendingCreate` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `pending:`. | | `name` | `String` | | Display name. | | `os` | [`AppSpaceOs`]() | | Operating system. | | `provider` | [`AppSpaceProvider`]() | | Where it runs. | | `started_at` / `startedAt` | `i64` | | Epoch ms it started. | | `phase` | `String` | | The SDK's last phase word (`preparing`, `pulling`, ...). | | `fraction` | `Option` | | The phase's fraction, when the SDK knows it. | | `pulled` | `bool` | | The image was pulled (the pull takes most of the bar). | | `permille` | `u32` | | Overall progress in thousandths; never goes backwards. | | `error` | `Option` | | Why it failed. | | `credit_url` / `creditUrl` | `Option` | | It failed for want of Cua Cloud credit: the billing page. | | `space_id` / `spaceId` | `Option` | | The registered Space's id once it finished; the row goes when the registry lists it. | | `image` | `Option` | | The image asked for. | | `kind` | [`Option`]() | | Container or VM. | | `arch` | `Option` | | The platform that will run (`arm64`, `amd64`). | | `emulated` | `bool` | | It runs emulated: a local Space whose platform is not the host's. | | `phase_at` / `phaseAt` | `Option` | | Epoch ms the current phase began (the time-based progress within a phase that reports no fraction), or last moved (a new fraction): a phase that stays put longer than `stall_limit_secs` fails the row with `stall_error`. | | `bytes_done` / `bytesDone` | `Option` | | Bytes of the download so far, when the SDK counts them. | | `bytes_total` / `bytesTotal` | `Option` | | Bytes the download has in all. | | `bytes_per_second` / `bytesPerSecond` | `Option` | | The download's smoothed rate, bytes per second. | | `cancelling` | `bool` | | Cancel was pressed: the SDK is stopping the create and removing what it made; the row goes when that ends. | | `gpu` | `bool` | | GPU acceleration was asked for. | ## `AppPendingDelete` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | The Space's id. | | `started_at` / `startedAt` | `i64` | | Epoch ms the delete started. | | `done` | `bool` | | The delete returned: the row stays hidden until the registry stops listing the Space. | ## `app_creates_compose` The registry's Spaces plus a row per Space being created. **Swift** ```swift func appCreatesCompose(spaces: [AppSpace], state: AppCreatesState) -> [AppSpace] ``` **Rust** ```rust pub fn app_creates_compose(spaces: Vec, state: AppCreatesState) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | [`Vec`]() | required | | `state` | [`AppCreatesState`](#appcreatesstate-record) | required | **Returns** [`Vec`]() ## `app_creates_is_deleting` Whether the Space `id` is being deleted: a Delete for it does nothing. **Swift** ```swift func appCreatesIsDeleting(state: AppCreatesState, id: String) -> Bool ``` **Rust** ```rust pub fn app_creates_is_deleting(state: AppCreatesState, id: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppCreatesState`](#appcreatesstate-record) | required | | `id` | `String` | required | **Returns** `bool` ## `app_creates_is_pending` Whether `id` is a Space still being created (or whose create failed). **Swift** ```swift func appCreatesIsPending(id: String) -> Bool ``` **Rust** ```rust pub fn app_creates_is_pending(id: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | **Returns** `bool` ## `app_creates_reduce` Advances the Spaces being created (see `spaces::creating`). **Swift** ```swift func appCreatesReduce(state: AppCreatesState, action: AppCreateAction) -> AppCreatesState ``` **Rust** ```rust pub fn app_creates_reduce(state: AppCreatesState, action: AppCreateAction) -> AppCreatesState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppCreatesState`](#appcreatesstate-record) | required | | `action` | [`AppCreateAction`](#appcreateaction-enum) | required | **Returns** [`AppCreatesState`](#appcreatesstate-record) ## `app_creates_settle` Drops finished creates the registry lists and finished deletes it no longer lists (after each registry refresh). **Swift** ```swift func appCreatesSettle(state: AppCreatesState, spaces: [AppSpace]) -> AppCreatesState ``` **Rust** ```rust pub fn app_creates_settle(state: AppCreatesState, spaces: Vec) -> AppCreatesState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppCreatesState`](#appcreatesstate-record) | required | | `spaces` | [`Vec`]() | required | **Returns** [`AppCreatesState`](#appcreatesstate-record) --- # Stream section and facts A Space detail's Stream rows (desktop and windows with their icons and picture in picture) and its facts with live memory and storage use. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_stream_section` builds the Stream rows both apps draw: the Desktop with the OS mark and the display's resolution, then one row per window titled by the window, with the app's icon source and a picture-in-picture button. `app_space_detail_live` adds Memory and Storage to the facts from `Space.usage`. `app_desktop_cover` says what the preview card shows before the first frame (Connecting, a Connect button, or the Space's own line) and `app_thumbnail_policy` how fresh the shells keep its blurred preview. ## `app_space_detail_live` The selected Space's detail with its memory and storage use (the SDK's `Space.usage`, refreshed every `app_usage_refresh_ms`). `host_arch` is this Mac's CPU architecture (`arm64`, `x86_64`): a local Space of another one shows a warning on its Architecture. **Swift** ```swift func appSpaceDetailLive(space: AppSpace, usage: AppSpaceUsage?, hostArch: String?) -> AppSpaceDetail ``` **Rust** ```rust pub fn app_space_detail_live(space: AppSpace, usage: Option, host_arch: Option) -> AppSpaceDetail ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | [`AppSpace`]() | required | | `usage` | [`Option`](#appspaceusage-record) | required | | `host_arch` | `Option` | required | **Returns** [`AppSpaceDetail`]() ## `AppSpaceUsage` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `memory_used` / `memoryUsed` | `u64` | | Memory in use, bytes. | | `memory_total` / `memoryTotal` | `u64` | | Memory size, bytes. | | `memory_limited` / `memoryLimited` | `bool` | | `memory_total` is the guest's limit. | | `disk_used` / `diskUsed` | `u64` | | Storage in use, bytes. | | `disk_total` / `diskTotal` | `u64` | | Storage size, bytes. | | `disk_limited` / `diskLimited` | `bool` | | `disk_total` is the guest's own disk. | ## `app_usage_refresh_ms` How often a shell refreshes a visible detail's memory and storage use. **Swift** ```swift func appUsageRefreshMs() -> UInt64 ``` **Rust** ```rust pub fn app_usage_refresh_ms() -> u64 ``` **Returns** `u64` ## `AppPipCommand` enum **Swift** ```swift .open(row: String) .close(row: String) ``` **Rust** ```rust AppPipCommand::Open { row: String } AppPipCommand::Close { row: String } ``` | Variant | Description | | --- | --- | | `Open` | Open the row's panel. Fields: `row: String` | | `Close` | Close the row's open panel. Fields: `row: String` | ## `AppPipEvent` enum **Swift** ```swift .opened(row: String) .closed(row: String) .synced(rows: [String]) ``` **Rust** ```rust AppPipEvent::Opened { row: String } AppPipEvent::Closed { row: String } AppPipEvent::Synced { rows: Vec } ``` | Variant | Description | | --- | --- | | `Opened` | A row's panel opened. Fields: `row: String` | | `Closed` | A row's panel closed. Fields: `row: String` | | `Synced` | The panels open now, as the shell listed them. Fields: `rows: Vec` | ## `app_desktop_cover` What a Space's preview card shows before (or instead of) its live desktop: "Connecting…", a Connect button, or the Space's own line. **Swift** ```swift func appDesktopCover(input: AppDesktopCoverInput) -> AppDesktopCover ``` **Rust** ```rust pub fn app_desktop_cover(input: AppDesktopCoverInput) -> AppDesktopCover ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppDesktopCoverInput`](#appdesktopcoverinput-record) | required | **Returns** [`AppDesktopCover`](#appdesktopcover-record) ## `AppDesktopCover` record Returned by [`app_desktop_cover`](#app_desktop_cover). | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`AppDesktopCoverKind`](#appdesktopcoverkind-enum) | | How it draws. | | `text` | `Option` | | The line, centered. | | `button` | `Option` | | A button under (or instead of) the line. | | `button_help` / `buttonHelp` | `Option` | | Its tooltip. | | `open_stream` / `openStream` | `bool` | | The shell opens a stream session now (none is open, and one is wanted). | | `retry` | `bool` | | The shell starts the open session again (Try again was pressed). | | `button_disabled` / `buttonDisabled` | `bool` | | The button shows greyed out (Connect while this device is not enrolled). | | `action` | `Option` | | The one action above a greyed-out Connect ("Enroll This Mac…"): the shell opens the enroll sheet. | ## `AppDesktopCoverInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `can_stream` / `canStream` | `bool` | | The detail's `can_stream`. | | `preview_text` / `previewText` | `String` | | The detail's `preview_text` (why it cannot stream). | | `auto_connect` / `autoConnect` | `bool` | | Settings: "Connect to the desktop automatically". | | `connect_requested` / `connectRequested` | `bool` | | Connect (or Try again) was pressed for this Space. | | `stream` | [`AppStreamPhase`](#appstreamphase-enum) | | The shell's stream session. | | `access` | [`Option`]() | | The detail's `access`: this device is signed in but not enrolled, so Connect shows greyed out under this notice and its action. | ## `AppDesktopCoverKind` enum **Swift** ```swift .stream .connecting .connect .status ``` **Rust** ```rust AppDesktopCoverKind::Stream AppDesktopCoverKind::Connecting AppDesktopCoverKind::Connect AppDesktopCoverKind::Status ``` | Variant | Description | | --- | --- | | `Stream` | No cover: the live desktop (or the stream view's own badge). | | `Connecting` | The preview with "Connecting…". | | `Connect` | The preview with a Connect button. | | `Status` | The preview with a line (and maybe a button): the Space cannot stream, or the stream failed. | ## `app_thumbnail_policy` How fresh the shells keep each Space's thumbnail. **Swift** ```swift func appThumbnailPolicy() -> AppThumbnailPolicy ``` **Rust** ```rust pub fn app_thumbnail_policy() -> AppThumbnailPolicy ``` **Returns** [`AppThumbnailPolicy`](#appthumbnailpolicy-record) ## `AppThumbnailPolicy` record Returned by [`app_thumbnail_policy`](#app_thumbnail_policy). | Field | Type | Default | Description | | --- | --- | --- | --- | | `open_interval_ms` / `openIntervalMs` | `u64` | | While the notch is open: every running Space, this often. | | `background_interval_ms` / `backgroundIntervalMs` | `u64` | | Otherwise (the app visible, not in Low Power Mode): a running Space whose thumbnail is older than this gets a new one. | | `max_dimension` / `maxDimension` | `u32` | | The long edge of a captured thumbnail, in pixels. | | `keep_ms` / `keepMs` | `u64` | | A thumbnail kept on disk longer than this is dropped at launch. | ## `AppStreamDisplay` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `width_px` / `widthPx` | `u32` | | Width, px. | | `height_px` / `heightPx` | `u32` | | Height, px. | ## `AppStreamRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `DESKTOP_ROW_ID` or the window's handle. | | `kind` | [`AppStreamRowKind`](#appstreamrowkind-enum) | | Desktop or window. | | `label` | `String` | | The one line ("Desktop (1280×800)", a window's title). | | `help` | `String` | | The full label, for the tooltip when it is truncated. | | `resolution` | `Option` | | The display's resolution ("1280×800"), Desktop row only. | | `icon` | [`AppStreamRowIcon`](#appstreamrowicon-enum) | | The icon. | | `actions` | [`Vec`](#appstreamrowaction-record) | | Icon buttons, in order. | ## `AppStreamRowAction` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | [`AppStreamRowActionId`](#appstreamrowactionid-enum) | | What it does. | | `symbol` | `String` | | SF Symbol (`pip.enter`, `pip.exit` while open). | | `help` | `String` | | Tooltip and accessibility label. | | `active` | `bool` | | Its panel is open. | ## `AppStreamSection` record Returned by [`app_stream_section`](#app_stream_section). | Field | Type | Default | Description | | --- | --- | --- | --- | | `rows` | [`Vec`](#appstreamrow-record) | | The Desktop row, then one per window. | | `status_text` / `statusText` | `Option` | | Under the rows while there are no window rows: loading, none, failed or no match. | ## `AppStreamSectionInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `windows` | [`Option>`]() | | The Space's windows, `None` while they load. | | `failed` | `bool` | | The window list could not be read. | | `display` | [`Option`](#appstreamdisplay-record) | | The primary display, when the display list has been read. | | `os` | [`AppSpaceOs`]() | | The Space's OS (the Desktop row's icon). | | `os_name` / `osName` | `Option` | | The OS name the Space reported ("Ubuntu 24.04"), if any. | | `open` | `Vec` | | Rows whose picture in picture is open (row ids). | | `query` | `String` | | Filter text. | ## `AppStreamPhase` enum **Swift** ```swift .noSession .idle .connecting .streaming .suspended .failed ``` **Rust** ```rust AppStreamPhase::NoSession AppStreamPhase::Idle AppStreamPhase::Connecting AppStreamPhase::Streaming AppStreamPhase::Suspended AppStreamPhase::Failed ``` | Variant | Description | | --- | --- | | `NoSession` | No session yet. | | `Idle` | A session that has not started. | | `Connecting` | Opening (no frame yet). | | `Streaming` | Frames arrive. | | `Suspended` | Paused by the Space (the stream view says why). | | `Failed` | It could not open. | ## `AppStreamRowActionId` enum **Swift** ```swift .pip ``` **Rust** ```rust AppStreamRowActionId::Pip ``` | Variant | Description | | --- | --- | | `Pip` | Picture in picture: the row's own stream in a floating panel. | ## `AppStreamRowIcon` enum **Swift** ```swift .os(id: String) .app(appName: String, appId: String, pid: UInt32) ``` **Rust** ```rust AppStreamRowIcon::Os { id: String } AppStreamRowIcon::App { app_name: String, app_id: String, pid: u32 } ``` | Variant | Description | | --- | --- | | `Os` | The Space's OS mark (`os-ubuntu`, `os-windows`, ...): the core's artwork (`notch.osIconSvg`) or system symbol. Fields: `id: String` | | `App` | The app's icon from the SDK's `Space.app_icon` (these three arguments); none when the Space has none. Fields: `app_name: String`, `app_id: String`, `pid: u32` | ## `AppStreamRowKind` enum **Swift** ```swift .desktop .window ``` **Rust** ```rust AppStreamRowKind::Desktop AppStreamRowKind::Window ``` | Variant | Description | | --- | --- | | `Desktop` | The whole desktop. | | `Window` | One window. | ## `app_stream_desktop_row_id` The Stream section's Desktop row id (a window row's id is its handle). **Swift** ```swift func appStreamDesktopRowId() -> String ``` **Rust** ```rust pub fn app_stream_desktop_row_id() -> String ``` **Returns** `String` ## `app_stream_pip_click` What a row's picture-in-picture button does: close its open panel, or open one. **Swift** ```swift func appStreamPipClick(open: [String], row: String) -> AppPipCommand ``` **Rust** ```rust pub fn app_stream_pip_click(open: Vec, row: String) -> AppPipCommand ``` | Parameter | Type | Default | | --- | --- | --- | | `open` | `Vec` | required | | `row` | `String` | required | **Returns** [`AppPipCommand`](#apppipcommand-enum) ## `app_stream_pip_reduce` The open picture-in-picture panels (row ids) after a panel opened, closed or the shell re-read them: the Stream section's `open`. **Swift** ```swift func appStreamPipReduce(open: [String], event: AppPipEvent) -> [String] ``` **Rust** ```rust pub fn app_stream_pip_reduce(open: Vec, event: AppPipEvent) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `open` | `Vec` | required | | `event` | [`AppPipEvent`](#apppipevent-enum) | required | **Returns** `Vec` ## `app_stream_section` A Space's Stream section: the Desktop row, then one row per window. **Swift** ```swift func appStreamSection(input: AppStreamSectionInput) -> AppStreamSection ``` **Rust** ```rust pub fn app_stream_section(input: AppStreamSectionInput) -> AppStreamSection ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppStreamSectionInput`](#appstreamsectioninput-record) | required | **Returns** [`AppStreamSection`](#appstreamsection-record) --- # New Space wizard The New Space wizard: where it runs, image, engine, address and the create call. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_wizard_initial` starts the wizard, `app_wizard_reduce` applies an action, and `app_wizard_view` derives the screen. `app_wizard_create_args` turns a finished wizard into `Spaces.create` arguments. ## `AppLocation` enum **Swift** ```swift .cloud .local .yours .host ``` **Rust** ```rust AppLocation::Cloud AppLocation::Local AppLocation::Yours AppLocation::Host ``` | Variant | Description | | --- | --- | | `Cloud` | Cua Cloud (the SDK's `on="cloud"`). The apps do not offer it for new Spaces (`CLOUD_SPACES_OFFERED`); existing cloud Spaces still list. | | `Local` | This Mac. | | `Yours` | The user's own cloud account (AWS, Google Cloud, Modal), connected with `cua cloud connect`: the SDK's `on` is the provider's word (`aws`), which the wizard carries beside it. | | `Host` | One of the user's machines that provides Spaces (a relay host, or one added by its Tailscale or LAN address): the SDK's `on` is `host:`, which the wizard carries beside it. | ## `AppCloudEngine` enum **Swift** ```swift .gvisor .kubevirt ``` **Rust** ```rust AppCloudEngine::Gvisor AppCloudEngine::Kubevirt ``` | Variant | Description | | --- | --- | | `Gvisor` | gVisor containers. | | `Kubevirt` | KubeVirt VMs. | ## `AppLocalEngine` enum **Swift** ```swift .container .qemu .lume ``` **Rust** ```rust AppLocalEngine::Container AppLocalEngine::Qemu AppLocalEngine::Lume ``` | Variant | Description | | --- | --- | | `Container` | A container (gVisor when installed, else runc). | | `Qemu` | A QEMU VM. | | `Lume` | A Lume VM. | ## `AppRuntime` enum **Swift** ```swift .auto .gvisor .runc .qemu .lume .kubevirt ``` **Rust** ```rust AppRuntime::Auto AppRuntime::Gvisor AppRuntime::Runc AppRuntime::Qemu AppRuntime::Lume AppRuntime::Kubevirt ``` | Variant | Description | | --- | --- | | `Auto` | Let the SDK pick. | | `Gvisor` | gVisor. | | `Runc` | runc. | | `Qemu` | QEMU. | | `Lume` | Lume. | | `Kubevirt` | KubeVirt. | ## `AppKindOption` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`AppSpaceKind`]() | | Container or VM. | | `image` | [`AppSandboxImage`]() | | The image with that variant. | ## `AppMenuOption` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `value` | `String` | | Value. | | `label` | `String` | | Title. | ## `AppTile` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Stable id (`linux`, `cloud`, `vm`, ...). | | `title` | `String` | | Title. | | `detail` | `String` | | Tooltip. | | `pressed` | `bool` | | Selected. | | `enabled` | `bool` | | Enabled. | ## `AppCreatePlan` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `placement` | [`AppLocation`](#applocation-enum) | | Where. | | `cloud` | `Option` | | For "Your cloud": the connected cloud's word (`aws`). | | `host` | `Option` | | For one of your machines: its id (`on="host:"`). | | `image` | [`AppSandboxImage`]() | | The image (its variant is the kind). | | `runtime` | [`AppRuntime`](#appruntime-enum) | | Engine (`auto` unless chosen). | | `name` | `Option` | | Name, when given. | | `cpus` | `Option` | | vCPUs (local and Cua Cloud). | | `memory_mb` / `memoryMb` | `Option` | | Memory MB (local and Cua Cloud). | | `disk_gb` / `diskGb` | `Option` | | Local VMs only: grow the disk to this many GB (larger than the image's). | | `open_when_ready` / `openWhenReady` | `bool` | | Open the desktop when ready. | | `open_desktop` / `openDesktop` | `bool` | | Open the desktop once created: asked for and the image streams. | | `gpu` | `Option` | | A GPU option of the runtime (`paravirtual`), when checked and offered. | ## `AppCreateSpaceArgs` record Returned by [`app_wizard_create_args`](#app_wizard_create_args). | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `String` | | Image ref. | | `on` | `String` | | The SDK's `on`: `local`, `cloud`, `host:`, or a connected cloud's word (`aws`, `gcp`, `modal`). | | `kind` | [`AppSpaceKind`]() | | `container` / `vm`. | | `runtime` | [`AppRuntime`](#appruntime-enum) | | Engine. | | `name` | `Option` | | Name. | | `cpus` | `Option` | | vCPUs. | | `memory_mb` / `memoryMb` | `Option` | | Memory MB. | | `disk_gb` / `diskGb` | `Option` | | Disk GB (local VMs; the image's size when unset). | | `spacesd` | `bool` | | Runs cua-spacesd. | | `gpu` | `Option` | | A GPU option of the runtime (`paravirtual`). | ## `AppLocalStorage` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `reserve_bytes` / `reserveBytes` | `u64` | | Bytes the SDK keeps free on a volume (a create that would leave less fails). | | `lume` | [`Option`](#appstoragevolume-record) | | Lume's VM storage (macOS). | | `qemu` | [`Option`](#appstoragevolume-record) | | The cua home (QEMU disks and the image cache). | | `container` | [`Option`](#appstoragevolume-record) | | The container engine's data disk. | | `pulled` | `Vec` | | Catalog refs already pulled here. | ## `AppStorageVolume` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `available_bytes` / `availableBytes` | `u64` | | Bytes available. | | `total_bytes` / `totalBytes` | `u64` | | Volume size in bytes. | | `name` | `String` | | `Macintosh HD`, `Colima`, `Docker Desktop`. | ## `AppCloudPricing` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `vcpu_hour_usd` / `vcpuHourUsd` | `f64` | | USD per reserved vCPU per hour. | | `memory_gib_hour_usd` / `memoryGibHourUsd` | `f64` | | USD per reserved GB (GiB) of memory per hour. | ## `AppGpuChoice` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `runtime` | `String` | | `lume`, `qemu`, `container`, `gvisor`, `fleet`. | | `id` | `String` | | What the create passes as `gpu` (`paravirtual`). | | `label` | `String` | | "GPU acceleration". | | `experimental` | `bool` | | Shown as "(Experimental)". | | `supported` | `bool` | | Works on this machine now. | | `reason` | `Option` | | Why not, one line. | | `learn_more` / `learnMore` | `Option` | | The page "Learn more" opens. | ## `AppGpuRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | "GPU acceleration (Experimental)". | | `on` | `bool` | | Checked. | | `enabled` | `bool` | | It can be checked here. | | `reason` | `Option` | | Why not, one line (a disabled row). | | `learn_more_label` / `learnMoreLabel` | `String` | | "Learn more". | | `learn_more_url` / `learnMoreUrl` | `Option` | | The page it opens. | ## `AppAddressForm` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `url` | `String` | | `host:port`. | | `token` | `String` | | spacesd token. | | `name` | `String` | | Optional name. | | `busy` | `bool` | | The handshake is running. | | `error` | `Option` | | The last error, in words. | ## `AppAddressSubmit` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `url` | `String` | | `host:port`, trimmed. | | `token` | `Option` | | Token, when given. | | `name` | `Option` | | Name, when given. | ## `AppAddressView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `valid` | `bool` | | The address has the right shape. | | `show_invalid` / `showInvalid` | `bool` | | Mark the field invalid. | | `can_submit` / `canSubmit` | `bool` | | "Add Space" is enabled. | | `submit_label` / `submitLabel` | `String` | | "Add Space" / "Connecting...". | | `error` | `Option` | | Error line. | | `submit` | [`Option`](#appaddresssubmit-record) | | What "Add Space" sends (trimmed), when it can be pressed. | ## `AppStepDot` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `step` | [`AppOnboardingStep`]() | | Page. | | `label` | `String` | | Label. | | `current` | `bool` | | Current. | ## `AppStepView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | Title. | | `state` | [`AppStepState`](#appstepstate-enum) | | State. | ## `AppWizardEnv` record Returned by [`app_wizard_env_from_json`](#app_wizard_env_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `default_location` / `defaultLocation` | [`AppLocation`](#applocation-enum) | | Where "Run on" starts (Settings, `cua config set default.on`). | | `cloud_available` / `cloudAvailable` | `bool` | | Signed in (or client credentials). | | `local_available` / `localAvailable` | `bool` | | A local container backend or Lume exists. | | `local_reason` / `localReason` | `Option` | | Why local is unavailable. | | `local_backends` / `localBackends` | `Option>` | | Ready local backends, when known. | | `local_details` / `localDetails` | `Option>` | | Why each local backend that is not ready is not (the runtime doctor's detail, by backend name), when known: the wizard names the reason instead of only "This Mac cannot run container Spaces." | | `max_cpus` / `maxCpus` | `u32` | | Upper bound for the CPU slider. | | `host_arch` / `hostArch` | `Option` | | This machine's architecture (`arm64`, `amd64`), when known. | | `lume_source` / `lumeSource` | `Option` | | Which Lume macOS Spaces run on (`runtime.lume`: `auto`, `builtin`, `system`), when known. | | `linux_source` / `linuxSource` | `Option` | | Which engine local Linux Spaces run on (`runtime.linux`: `auto`, `builtin`, `system`), when known (a Mac). | | `storage` | [`Option`](#applocalstorage-record) | | Free space and pulled images here (the SDK's `Local.storage()`), when known. | | `cloud_pricing` / `cloudPricing` | [`Option`](#appcloudpricing-record) | | This account's Cua Cloud rates, when known (no estimate without). | | `clouds` | [`Vec`]() | | The user's connected clouds (`cloud_status`), for "Your cloud" (offered only while its experiment is on: `offered_clouds`). | | `hosts` | [`Vec`]() | | The user's machines that provide Spaces (`Spaces.hosts()`). | | `experiments` | [`AppExperiments`]() | | Settings, Experiments ("Your cloud" decides whether clouds show). | | `gpus` | [`Option>`](#appgpuchoice-record) | | The GPU option of each runtime that has one (the SDK's `gpu_support`), when known. A runtime not listed has none: the Resources step shows no GPU row for it. | ## `AppWizardFact` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `kind`, `arch`, `disk`, `download`, `available`. | | `label` | `String` | | Label. | | `value` | `String` | | Value. | | `symbol` | `Option` | | A symbol after the value (`exclamationmark.triangle`). | | `help` | `Option` | | The symbol's tooltip. | ## `AppWizardField` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `os`, `image`, `placement`, `kind`, `runtime`, `connect-cloud`, `connect-by-address`, `cpus`, `memory`, `disk`, `name`, `open-when-ready`, `address`, `token`, `address-name`. | | `label` | `String` | | Label (or the link's title). | | `placeholder` | `Option` | | Placeholder. | | `error` | `Option` | | One line under the field. | | `advanced` | `bool` | | Inside "Advanced". | ## `AppWizardLabels` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `cancel` | `String` | | "Cancel". | | `back` | `String` | | "Back". | | `advanced` | `String` | | "Advanced". | ## `AppWizardState` record Returned by [`app_wizard_initial`](#app_wizard_initial), [`app_wizard_reduce`](#app_wizard_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `mode` | [`AppWizardMode`](#appwizardmode-enum) | | Create or address. | | `step` | `u32` | | 0..=3. | | `placement` | [`AppLocation`](#applocation-enum) | | Where it runs. | | `picked` | `bool` | | The person chose a placement (the default no longer follows Settings). | | `image_ref` / `imageRef` | `String` | | The preset the Space is based on (its OS, kind and engines). With a custom ref typed, the Space runs that ref the same way. | | `image_text` / `imageText` | `String` | | The image field as typed: a preset's ref or any other reference. | | `suggest_open` / `suggestOpen` | `bool` | | The suggestion list is open. | | `suggest_filter` / `suggestFilter` | `bool` | | The list filters by the text (typing); opening it shows every preset. | | `suggest_index` / `suggestIndex` | `Option` | | The highlighted row (keyboard), over the visible rows in order. | | `cpus` | `u32` | | vCPUs as chosen (the view keeps them within where it runs). | | `memory_gb` / `memoryGb` | `u32` | | Memory GB as chosen (the view keeps it within where it runs). | | `disk_gb` / `diskGb` | `Option` | | Disk GB as chosen (local VMs that can grow); `None`: the image's. | | `name` | `String` | | Name as typed. | | `open_when_ready` / `openWhenReady` | `bool` | | Open the desktop when ready. | | `advanced` | `bool` | | Advanced shown. | | `runtime_choice` / `runtimeChoice` | [`AppRuntime`](#appruntime-enum) | | Runtime as chosen (the effective one is in the view). | | `cloud` | `Option` | | The connected cloud chosen for "Your cloud" (`aws`); `None`: the default one. | | `host` | `Option` | | The machine chosen for `Location::Host` (`SpaceHost.id`). | | `gpu` | `bool` | | The GPU box is checked (it applies where the effective runtime offers a GPU). | | `address` | [`AppAddressForm`](#appaddressform-record) | | The address form. | ## `AppWizardView` record Returned by [`app_wizard_view`](#app_wizard_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `mode` | [`AppWizardMode`](#appwizardmode-enum) | | Create or address. | | `step` | `u32` | | 0..=3. | | `steps` | [`Vec`](#appstepview-record) | | Step list. | | `title` | `String` | | Step heading. | | `os_tiles` / `osTiles` | [`Vec`](#apptile-record) | | OS tiles. | | `image_field` / `imageField` | [`AppImageFieldView`]() | | The image field. | | `image` | [`AppSandboxImage`]() | | The image. | | `placements` | [`Vec`]() | | The "Run on" menu: This Mac, your machines that provide Spaces, then your connected clouds (with the "Your cloud" experiment). | | `placement_id` / `placementId` | `String` | | The chosen entry's id (`PlacementOption.id`). | | `cloud` | `Option` | | The connected cloud "Your cloud" creates in (`aws`). | | `host` | `Option` | | The machine of yours it creates on (`SpaceHost.id`). | | `placement_error` / `placementError` | `Option` | | Why the chosen placement cannot be used. | | `placement_hint` / `placementHint` | `Option` | | One line under "Run on" when This Mac cannot run the Space and no machine of yours is listed: how another Mac joins. | | `runtime_switch` / `runtimeSwitch` | [`Option`]() | | Switches This Mac to the built-in runtime the Space needs, when it is set to use its own and that is missing. | | `advanced` | `bool` | | Advanced shown. | | `kind_tiles` / `kindTiles` | [`Vec`](#apptile-record) | | Kind tiles. | | `runtimes` | [`Vec`](#appmenuoption-record) | | Runtime menu. | | `runtime` | [`AppRuntime`](#appruntime-enum) | | Effective runtime. | | `runtime_enabled` / `runtimeEnabled` | `bool` | | The runtime menu has a choice. | | `cpus` | `u32` | | Cores. | | `min_cpus` / `minCpus` | `u32` | | Slider min. | | `max_cpus` / `maxCpus` | `u32` | | Slider max. | | `min_memory_gb` / `minMemoryGb` | `u32` | | Memory slider min, GB. | | `max_memory_gb` / `maxMemoryGb` | `u32` | | Memory slider max, GB. | | `cpus_text` / `cpusText` | `String` | | "2 cores". | | `memory_gb` / `memoryGb` | `u32` | | Memory GB. | | `memory_text` / `memoryText` | `String` | | "4 GB". | | `disk_editable` / `diskEditable` | `bool` | | The disk slider shows (a local VM whose disk can grow). | | `disk_gb` / `diskGb` | `u32` | | Disk GB (the slider's value). | | `min_disk_gb` / `minDiskGb` | `u32` | | Disk slider min, GB (the image's size). | | `max_disk_gb` / `maxDiskGb` | `u32` | | Disk slider max, GB. | | `disk_text` / `diskText` | `String` | | "150 GB". | | `disk_note` / `diskNote` | `Option` | | One quiet line under the slider: a VM disk is sparse, so it uses the unpacked size at first, up to the chosen size; and a resize when the chosen size is not the image's. | | `disk_reset_label` / `diskResetLabel` | `Option` | | "Reset" while the chosen size is not the image's. | | `disk_help` / `diskHelp` | `Option` | | The slider's tooltip: why it cannot go below the image's disk. | | `price` | `Option` | | What it costs, in one line: `About $0.18/hour` (cloud, at this account's rates); `None` on the user's own hardware (this Mac, or a machine added by address) and when the cloud rates are unknown. | | `resource_facts` / `resourceFacts` | [`Vec`](#appwizardfact-record) | | The Resources step's lines under the sliders: kind, architecture, disk (or download), available. | | `resources_error` / `resourcesError` | `Option` | | One line when the Space does not fit where it is written (blocks Continue). | | `gpu` | [`Option`](#appgpurow-record) | | The GPU row (Resources), where the runtime offers a GPU. | | `name` | `String` | | Name as typed. | | `name_invalid` / `nameInvalid` | `bool` | | The name breaks the DNS-label rule. | | `name_error` / `nameError` | `Option` | | Why, in one line. | | `open_when_ready` / `openWhenReady` | `bool` | | Open when ready. | | `stream_note` / `streamNote` | `Option` | | "No desktop stream for this image yet." | | `summary` | [`Vec`]() | | The Summary list. | | `can_continue` / `canContinue` | `bool` | | Continue is enabled. | | `show_back` / `showBack` | `bool` | | Back shows. | | `primary_label` / `primaryLabel` | `String` | | "Continue" or "Create Space". | | `plan` | [`AppCreatePlan`](#appcreateplan-record) | | What Create sends. | | `address` | [`AppAddressView`](#appaddressview-record) | | The address form. | | `fields` | [`Vec`](#appwizardfield-record) | | The fields of this step (or the address form), in order. | | `labels` | [`AppWizardLabels`](#appwizardlabels-record) | | Fixed words. | ## `AppStepState` enum **Swift** ```swift .done .current .todo ``` **Rust** ```rust AppStepState::Done AppStepState::Current AppStepState::Todo ``` | Variant | Description | | --- | --- | | `Done` | Done. | | `Current` | Current. | | `Todo` | Not reached. | ## `AppWizardAction` enum **Swift** ```swift .syncDefault(location: AppLocation) .chooseOs(os: AppSpaceOs) .chooseImage(imageRef: String) .setImageText(text: String) .openImageSuggestions .moveImageSuggestion(delta: Int32) .pickImageSuggestion .dismissImageSuggestions .setPlacement(placement: AppLocation) .chooseCloud(cloud: String) .choosePlacement(on: String) .toggleAdvanced .chooseKind(kind: AppSpaceKind) .setRuntime(runtime: AppRuntime) .setCpus(cpus: UInt32) .setMemory(memoryGb: UInt32) .setDisk(diskGb: UInt32) .resetDisk .setGpu(on: Bool) .setName(name: String) .setOpenWhenReady(on: Bool) .next .back .showAddress .hideAddress .setAddress(url: String) .setToken(token: String) .setAddressName(name: String) .submitAddress .addressFailed(error: String) ``` **Rust** ```rust AppWizardAction::SyncDefault { location: AppLocation } AppWizardAction::ChooseOs { os: AppSpaceOs } AppWizardAction::ChooseImage { image_ref: String } AppWizardAction::SetImageText { text: String } AppWizardAction::OpenImageSuggestions AppWizardAction::MoveImageSuggestion { delta: i32 } AppWizardAction::PickImageSuggestion AppWizardAction::DismissImageSuggestions AppWizardAction::SetPlacement { placement: AppLocation } AppWizardAction::ChooseCloud { cloud: String } AppWizardAction::ChoosePlacement { on: String } AppWizardAction::ToggleAdvanced AppWizardAction::ChooseKind { kind: AppSpaceKind } AppWizardAction::SetRuntime { runtime: AppRuntime } AppWizardAction::SetCpus { cpus: u32 } AppWizardAction::SetMemory { memory_gb: u32 } AppWizardAction::SetDisk { disk_gb: u32 } AppWizardAction::ResetDisk AppWizardAction::SetGpu { on: bool } AppWizardAction::SetName { name: String } AppWizardAction::SetOpenWhenReady { on: bool } AppWizardAction::Next AppWizardAction::Back AppWizardAction::ShowAddress AppWizardAction::HideAddress AppWizardAction::SetAddress { url: String } AppWizardAction::SetToken { token: String } AppWizardAction::SetAddressName { name: String } AppWizardAction::SubmitAddress AppWizardAction::AddressFailed { error: String } ``` | Variant | Description | | --- | --- | | `SyncDefault` | The configured default location arrived (ignored once picked). Fields: `location: AppLocation` | | `ChooseOs` | An OS tile. Fields: `os: AppSpaceOs` | | `ChooseImage` | A preset picked from the suggestions (fills the field). Fields: `image_ref: String` | | `SetImageText` | The image field changed (typing): filters the suggestions; a preset's ref selects it, anything else is used as typed. Fields: `text: String` | | `OpenImageSuggestions` | Show every preset (the field's menu button, or focus). | | `MoveImageSuggestion` | Up (-1) or Down (+1) in the suggestions; opens them when closed. Fields: `delta: i32` | | `PickImageSuggestion` | Return: picks the highlighted preset, or keeps the typed text. | | `DismissImageSuggestions` | Escape, or the field lost focus. | | `SetPlacement` | A "Run on" tile. Fields: `placement: AppLocation` | | `ChooseCloud` | A connected cloud: creates there. Fields: `cloud: String` | | `ChoosePlacement` | An entry of the "Run on" menu (`PlacementOption.id`): `local`, `cloud`, `host:` or a connected cloud's word. A disabled entry (offline, at its limit) is ignored. Fields: `on: String` | | `ToggleAdvanced` | Show or hide Advanced. | | `ChooseKind` | A kind tile (switches to the family's image of that kind). Fields: `kind: AppSpaceKind` | | `SetRuntime` | The runtime menu. Fields: `runtime: AppRuntime` | | `SetCpus` | CPU slider. Fields: `cpus: u32` | | `SetMemory` | Memory slider. Fields: `memory_gb: u32` | | `SetDisk` | Disk slider (VMs whose disk can grow). Fields: `disk_gb: u32` | | `ResetDisk` | The disk slider's Reset: back to the image's own disk. | | `SetGpu` | The GPU checkbox. Fields: `on: bool` | | `SetName` | Name field. Fields: `name: String` | | `SetOpenWhenReady` | "Open when ready". Fields: `on: bool` | | `Next` | Continue. | | `Back` | Back. | | `ShowAddress` | "Connect by address..." | | `HideAddress` | Back to create. | | `SetAddress` | Address field. Fields: `url: String` | | `SetToken` | Token field. Fields: `token: String` | | `SetAddressName` | Address name field. Fields: `name: String` | | `SubmitAddress` | "Add Space" pressed (the shell runs the handshake when the view's `address.canSubmit` was true). | | `AddressFailed` | The handshake failed with this raw error. Fields: `error: String` | ## `AppWizardMode` enum **Swift** ```swift .create .address ``` **Rust** ```rust AppWizardMode::Create AppWizardMode::Address ``` | Variant | Description | | --- | --- | | `Create` | The four steps. | | `Address` | Connect by address. | ## `app_wizard_action_from_json` A wizard action from JSON (fixtures, parity). **Swift** ```swift func appWizardActionFromJson(json: String) throws -> AppWizardAction ``` **Rust** ```rust pub fn app_wizard_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppWizardAction`](#appwizardaction-enum) · **Raises** [`CuaError`]() ## `app_wizard_create_args` The SDK call a plan makes (`create_space`). **Swift** ```swift func appWizardCreateArgs(plan: AppCreatePlan) -> AppCreateSpaceArgs ``` **Rust** ```rust pub fn app_wizard_create_args(plan: AppCreatePlan) -> AppCreateSpaceArgs ``` | Parameter | Type | Default | | --- | --- | --- | | `plan` | [`AppCreatePlan`](#appcreateplan-record) | required | **Returns** [`AppCreateSpaceArgs`](#appcreatespaceargs-record) ## `app_wizard_create_failed_text` The notice when `create_space` failed. **Swift** ```swift func appWizardCreateFailedText(error: String) -> String ``` **Rust** ```rust pub fn app_wizard_create_failed_text(error: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `error` | `String` | required | **Returns** `String` ## `app_wizard_creating_text` The notice while a plan is being created. **Swift** ```swift func appWizardCreatingText(plan: AppCreatePlan) -> String ``` **Rust** ```rust pub fn app_wizard_creating_text(plan: AppCreatePlan) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `plan` | [`AppCreatePlan`](#appcreateplan-record) | required | **Returns** `String` ## `app_wizard_env_from_json` The wizard's environment from JSON (fixtures, parity). **Swift** ```swift func appWizardEnvFromJson(json: String) throws -> AppWizardEnv ``` **Rust** ```rust pub fn app_wizard_env_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppWizardEnv`](#appwizardenv-record) · **Raises** [`CuaError`]() ## `app_wizard_image_suggestions` The presets matching the image field's text, grouped. **Swift** ```swift func appWizardImageSuggestions(query: String) -> [AppImageGroup] ``` **Rust** ```rust pub fn app_wizard_image_suggestions(query: String) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `query` | `String` | required | **Returns** [`Vec`]() ## `app_wizard_initial` The wizard's first state. **Swift** ```swift func appWizardInitial(env: AppWizardEnv) -> AppWizardState ``` **Rust** ```rust pub fn app_wizard_initial(env: AppWizardEnv) -> AppWizardState ``` | Parameter | Type | Default | | --- | --- | --- | | `env` | [`AppWizardEnv`](#appwizardenv-record) | required | **Returns** [`AppWizardState`](#appwizardstate-record) ## `app_wizard_reduce` Advances the wizard. **Swift** ```swift func appWizardReduce(state: AppWizardState, action: AppWizardAction, env: AppWizardEnv) -> AppWizardState ``` **Rust** ```rust pub fn app_wizard_reduce(state: AppWizardState, action: AppWizardAction, env: AppWizardEnv) -> AppWizardState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppWizardState`](#appwizardstate-record) | required | | `action` | [`AppWizardAction`](#appwizardaction-enum) | required | | `env` | [`AppWizardEnv`](#appwizardenv-record) | required | **Returns** [`AppWizardState`](#appwizardstate-record) ## `app_wizard_validate_image_ref` Why a typed image ref cannot be used, in one line (`None`: it can). **Swift** ```swift func appWizardValidateImageRef(imageRef: String) -> String? ``` **Rust** ```rust pub fn app_wizard_validate_image_ref(image_ref: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `image_ref` | `String` | required | **Returns** `Option` ## `app_wizard_view` The wizard as drawn. **Swift** ```swift func appWizardView(state: AppWizardState, env: AppWizardEnv) -> AppWizardView ``` **Rust** ```rust pub fn app_wizard_view(state: AppWizardState, env: AppWizardEnv) -> AppWizardView ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppWizardState`](#appwizardstate-record) | required | | `env` | [`AppWizardEnv`](#appwizardenv-record) | required | **Returns** [`AppWizardView`](#appwizardview-record) --- # Image catalog The images the New Space wizard offers: groups, sizes, the image field and its suggestions. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The catalog behind the New Space image field: each `AppSandboxImage` with its group, distro, tier and download sizes, and the suggestions the field shows as you type. ## `AppSandboxImage` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `image_ref` / `imageRef` | `String` | | OCI ref. | | `group` | `String` | | Group id. | | `os` | [`AppSpaceOs`]() | | OS. | | `name` | `String` | | Display name. | | `variant` | [`AppSpaceKind`]() | | Container or VM. | | `summary` | `String` | | One line. | | `spacesd` | `bool` | | Runs cua-spacesd (streams). | | `local` | [`Option`]() | | Local engine. | | `cloud` | [`Option`]() | | Cloud engine. | | `tier` | [`Option`](#appimagetier-enum) | | `slim`, `full` or `xcode` (canonical images only). | | `published` | `bool` | | Offered in pickers. | | `distro` | [`Option`](#appimagedistro-record) | | The guest's distribution. | | `arch` | `Vec` | | Platforms it is built for (`amd64`, `arm64`); empty when unknown. | | `sizes` | [`Option`](#appimagesizes-record) | | How big it is per platform, when measured. | ## `AppImageGroup` record Returned by [`app_wizard_image_suggestions`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Group id. | | `label` | `String` | | Title. | | `images` | [`Vec`](#appsandboximage-record) | | Published images. | ## `AppImageSizes` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `digest` | `String` | | The digest they were measured at. | | `platforms` | [`Vec`](#appplatformsize-record) | | One row per platform. | ## `AppPlatformSize` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `arch` | `String` | | `amd64` or `arm64`. | | `manifest` | `String` | | The platform manifest's digest. | | `download` | `u64` | | What a pull downloads. | | `unpacked` | `u64` | | What the pulled image takes on the host. | | `disk` | `u64` | | The disk the Space sees (a VM's virtual disk; a container's root filesystem). | ## `AppImageFieldView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `text` | `String` | | The text. | | `open` | `bool` | | Show the suggestions (open and something matches). | | `groups` | [`Vec`](#appsuggestiongroup-record) | | The suggestions, filtered while typing. | | `error` | `Option` | | One line when the ref cannot be used. | | `custom` | `bool` | | The text is not a preset. | ## `AppImageSuggestion` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `image_ref` / `imageRef` | `String` | | Image ref (what picking it fills in). | | `label` | `String` | | The image's name, shown after the ref. | | `highlighted` | `bool` | | The keyboard highlight. | | `selected` | `bool` | | The field holds this preset. | ## `AppSuggestionGroup` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Group id. | | `label` | `String` | | Title. | | `rows` | [`Vec`](#appimagesuggestion-record) | | Matching presets. | ## `AppImageDistro` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `ubuntu`, `omarchy`, `macos`, `windows`. | | `name` | `String` | | Pretty name ("Ubuntu 24.04", "Omarchy"). | ## `AppImageTier` enum **Swift** ```swift .slim .full .xcode ``` **Rust** ```rust AppImageTier::Slim AppImageTier::Full AppImageTier::Xcode ``` | Variant | Description | | --- | --- | | `Slim` | The minimum that passes `cua-spacesd doctor --strict` (what CI runs). | | `Full` | The default: slim plus dev tooling. | | `Xcode` | macOS: full plus one pinned Xcode. | --- # Teleport picker The teleport picker and its catalog. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The picker lists `Teleport.catalog` entries and drives the steps up to the review. The overlays that show a teleport in progress are on [Teleport transfer and drag](). ## `AppEntrySection` record Returned by [`app_picker_sections`](#app_picker_sections). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Recent", "Apps", "Not available". | | `entries` | [`Vec`](#appcatalogentry-record) | | Entries. | ## `AppCatalogEntry` record Returned by [`app_catalog_entries_from_json`](#app_catalog_entries_from_json), [`app_catalog_entry`](#app_catalog_entry). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Id. | | `name` | `String` | | Name. | | `host_path` / `hostPath` | `Option` | | Bundle path. | | `host_app_id` / `hostAppId` | `Option` | | Bundle id. | | `version` | `Option` | | Version. | | `capability` | [`AppTeleportCapability`]() | | Capability. | | `reason` | `Option` | | Why unsupported, or a caveat. | | `moves` | [`Vec`]() | | Offered moves, UI order. | | `provider_id` / `providerId` | `Option` | | Provider id. | | `sensitive_groups` / `sensitiveGroups` | [`Vec`]() | | The opt-in groups the signed-in state move offers, UI order. | | `install_source` / `installSource` | `Option` | | `manifest`, `image`, `space`. | | `install_id` / `installId` | `Option` | | Installable id. | | `install_version` / `installVersion` | `Option` | | Installable version. | | `launch_bin` / `launchBin` | `Option` | | Binary. | | `last_used_ms` / `lastUsedMs` | `Option` | | Last used (recents). | | `json` | `String` | | The SDK's JSON for this entry. | ## `AppPickerPrimary` record Returned by [`app_picker_primary`](#app_picker_primary). | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | "Teleport" / "Stream". | | `action` | [`AppPickerAction`](#apppickeraction-enum) | | Action. | | `enabled` | `bool` | | Enabled. | ## `AppPickerState` record Returned by [`app_picker_initial`](#app_picker_initial), [`app_picker_reduce`](#app_picker_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `step` | [`AppPickerStep`](#apppickerstep-enum) | | Step. | | `space_name` / `spaceName` | `String` | | The target Space's name. | | `entries` | [`Option>`](#appcatalogentry-record) | | The catalog, once loaded. | | `query` | `String` | | Search. | | `selected_id` / `selectedId` | `Option` | | Highlighted app. | | `entry` | [`Option`](#appcatalogentry-record) | | The chosen app. | | `moves` | [`Option`]() | | What moves. | | `files` | `Vec` | | Chosen files. | | `sensitive` | [`Vec`]() | | The opt-in groups checked on the options step (only planned with the signed-in state move). Each group's items are secrets the review lists and asks to acknowledge. | | `plan` | [`Option`]() | | The plan under review. | | `acknowledged` | `bool` | | Secrets acknowledged. | | `save_to_keyvault` / `saveToKeyvault` | `bool` | | "Save to Keyvault" checked: keep the captured session sealed in the Cua Keyvault after delivery, for reuse without asking again (shown alongside the acknowledgement, only when the plan is sensitive). | | `acknowledged_relay_plaintext` / `acknowledgedRelayPlaintext` | `bool` | | `Plan.relay_unsealed`'s warning acknowledged (S1). | | `choice` | [`AppReviewChoice`]() | | What the review sends: which sites, which items, from where. | | `events` | [`Vec`]() | | Run events (last `MAX_EVENTS`). | | `report` | [`Option`]() | | The run's report. | | `error` | `Option` | | Error text. | | `install_prompt` / `installPrompt` | [`Option`]() | | The "needs the Cua app" prompt. | | `error_back` / `errorBack` | [`AppPickerStep`](#apppickerstep-enum) | | Where Back from an error goes. | ## `AppPickerAction` enum **Swift** ```swift .teleport .streamRemote .streamLocalSoon ``` **Rust** ```rust AppPickerAction::Teleport AppPickerAction::StreamRemote AppPickerAction::StreamLocalSoon ``` | Variant | Description | | --- | --- | | `Teleport` | Teleport the app. | | `StreamRemote` | Stream the remote window here. | | `StreamLocalSoon` | Streaming a local window is not built yet: an honest notice. | ## `AppPickerEvent` enum **Swift** ```swift .loaded(entries: [AppCatalogEntry]) .failed(message: String, causeTexts: [String], causeInstalled: Bool) .query(query: String) .select(id: String) .choose(id: String?) .preselect(entry: AppCatalogEntry, files: [String]) .move(moves: AppTeleportMove) .files(files: [String]) .removeFile(path: String) .sensitive(group: AppSensitiveGroup, value: Bool) .plan .planned(plan: AppTeleportPlan) .acknowledge(value: Bool) .saveToKeyvault(value: Bool) .acknowledgeRelayPlaintext(value: Bool) .domainsLoaded(inventory: KvInventory, remembered: [String]?) .domainsFailed .toggleDomain(domain: String) .selectShownDomains(value: Bool) .domainQuery(text: String) .toggleItem(key: String) .sendFrom(source: AppSendSource) .vaultItems(count: UInt32, newestMs: Int64, nowMs: Int64, selected: [String], passwordIds: [String]) .togglePasswords(value: Bool) .vaultSelection(selected: [String]) .confirm .progress(event: AppTeleportRunEvent) .finished(report: AppTeleportRunReport) .back ``` **Rust** ```rust AppPickerEvent::Loaded { entries: Vec } AppPickerEvent::Failed { message: String, cause_texts: Vec, cause_installed: bool } AppPickerEvent::Query { query: String } AppPickerEvent::Select { id: String } AppPickerEvent::Choose { id: Option } AppPickerEvent::Preselect { entry: AppCatalogEntry, files: Vec } AppPickerEvent::Move { moves: AppTeleportMove } AppPickerEvent::Files { files: Vec } AppPickerEvent::RemoveFile { path: String } AppPickerEvent::Sensitive { group: AppSensitiveGroup, value: bool } AppPickerEvent::Plan AppPickerEvent::Planned { plan: AppTeleportPlan } AppPickerEvent::Acknowledge { value: bool } AppPickerEvent::SaveToKeyvault { value: bool } AppPickerEvent::AcknowledgeRelayPlaintext { value: bool } AppPickerEvent::DomainsLoaded { inventory: KvInventory, remembered: Option> } AppPickerEvent::DomainsFailed AppPickerEvent::ToggleDomain { domain: String } AppPickerEvent::SelectShownDomains { value: bool } AppPickerEvent::DomainQuery { text: String } AppPickerEvent::ToggleItem { key: String } AppPickerEvent::SendFrom { source: AppSendSource } AppPickerEvent::VaultItems { count: u32, newest_ms: i64, now_ms: i64, selected: Vec, password_ids: Vec } AppPickerEvent::TogglePasswords { value: bool } AppPickerEvent::VaultSelection { selected: Vec } AppPickerEvent::Confirm AppPickerEvent::Progress { event: AppTeleportRunEvent } AppPickerEvent::Finished { report: AppTeleportRunReport } AppPickerEvent::Back ``` | Variant | Description | | --- | --- | | `Loaded` | The catalog arrived. Fields: `entries: Vec` | | `Failed` | Something failed. Fields: `message: String`, `cause_texts: Vec`, `cause_installed: bool` | | `Query` | Search. Fields: `query: String` | | `Select` | Highlight. Fields: `id: String` | | `Choose` | Open the options for the highlighted (or given) app. Fields: `id: Option` | | `Preselect` | Start on an app (a drop or a window drag). Fields: `entry: AppCatalogEntry`, `files: Vec` | | `Move` | What moves. Fields: `moves: AppTeleportMove` | | `Files` | Add files. Fields: `files: Vec` | | `RemoveFile` | Remove a file. Fields: `path: String` | | `Sensitive` | An opt-in checkbox ("Keep me signed in", "Saved passwords", ...). Fields: `group: AppSensitiveGroup`, `value: bool` | | `Plan` | Plan. | | `Planned` | The plan arrived. Fields: `plan: AppTeleportPlan` | | `Acknowledge` | The secrets checkbox. Fields: `value: bool` | | `SaveToKeyvault` | The "Save to Keyvault" checkbox. Fields: `value: bool` | | `AcknowledgeRelayPlaintext` | The relay-plaintext warning's checkbox (S1). Fields: `value: bool` | | `DomainsLoaded` | The browser's sites with counts arrived (and what was picked last time for this app and Space, if anything). Fields: `inventory: KvInventory`, `remembered: Option>` | | `DomainsFailed` | The inventory could not be read (the review then sends what the plan lists, as before). | | `ToggleDomain` | Pick or drop one site. Fields: `domain: String` | | `SelectShownDomains` | Pick or drop every site the search shows. Fields: `value: bool` | | `DomainQuery` | The sites list's search text. Fields: `text: String` | | `ToggleItem` | Turn a consent line on or off. Fields: `key: String` | | `SendFrom` | Where to send from. Fields: `source: AppSendSource` | | `VaultItems` | The app's saved Keyvault items (what the Keyvault holds for it). Fields: `count: u32`, `newest_ms: i64`, `now_ms: i64`, `selected: Vec`, `password_ids: Vec` | | `TogglePasswords` | Tick or untick "Also send saved passwords". Fields: `value: bool` | | `VaultSelection` | The chosen saved items changed. Fields: `selected: Vec` | | `Confirm` | Confirm the review. | | `Progress` | A run event. Fields: `event: AppTeleportRunEvent` | | `Finished` | The run finished. Fields: `report: AppTeleportRunReport` | | `Back` | Back. | ## `AppPickerStep` enum **Swift** ```swift .loading .pick .options .planning .consent .running .done .error ``` **Rust** ```rust AppPickerStep::Loading AppPickerStep::Pick AppPickerStep::Options AppPickerStep::Planning AppPickerStep::Consent AppPickerStep::Running AppPickerStep::Done AppPickerStep::Error ``` | Variant | Description | | --- | --- | | `Loading` | Loading the catalog. | | `Pick` | Choosing an app. | | `Options` | Choosing what moves. | | `Planning` | Planning. | | `Consent` | Reviewing the consent list. | | `Running` | Running. | | `Done` | Done. | | `Error` | Failed. | ## `AppPickerTab` enum **Swift** ```swift .space .thisMac ``` **Rust** ```rust AppPickerTab::Space AppPickerTab::ThisMac ``` | Variant | Description | | --- | --- | | `Space` | This machine's windows, teleported into the Space. | | `ThisMac` | The Space's windows, streamed here. | ## `app_catalog_entries_from_json` Picker catalog entries from JSON (fixtures, parity flows). **Swift** ```swift func appCatalogEntriesFromJson(json: String) throws -> [AppCatalogEntry] ``` **Rust** ```rust pub fn app_catalog_entries_from_json(json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`Vec`](#appcatalogentry-record) · **Raises** [`CuaError`]() ## `app_catalog_entry` A catalog row from `Teleport.catalog`, as the picker's entry. **Swift** ```swift func appCatalogEntry(entry: TeleportCatalogEntry) -> AppCatalogEntry ``` **Rust** ```rust pub fn app_catalog_entry(entry: TeleportCatalogEntry) -> AppCatalogEntry ``` | Parameter | Type | Default | | --- | --- | --- | | `entry` | [`TeleportCatalogEntry`]() | required | **Returns** [`AppCatalogEntry`](#appcatalogentry-record) ## `app_picker_can_plan` Planning is possible. **Swift** ```swift func appPickerCanPlan(state: AppPickerState) -> Bool ``` **Rust** ```rust pub fn app_picker_can_plan(state: AppPickerState) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | **Returns** `bool` ## `app_picker_consent` The consent the confirmed review carries. **Swift** ```swift func appPickerConsent(state: AppPickerState) -> AppTeleportConsent ``` **Rust** ```rust pub fn app_picker_consent(state: AppPickerState) -> AppTeleportConsent ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | **Returns** [`AppTeleportConsent`]() ## `app_picker_initial` "Teleport an app..." first state. **Swift** ```swift func appPickerInitial(spaceName: String) -> AppPickerState ``` **Rust** ```rust pub fn app_picker_initial(space_name: String) -> AppPickerState ``` | Parameter | Type | Default | | --- | --- | --- | | `space_name` | `String` | required | **Returns** [`AppPickerState`](#apppickerstate-record) ## `app_picker_plan_sensitive` The groups the plan asks for (`TeleportPlanOptions.sensitive_groups`). **Swift** ```swift func appPickerPlanSensitive(state: AppPickerState) -> [AppSensitiveGroup] ``` **Rust** ```rust pub fn app_picker_plan_sensitive(state: AppPickerState) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | **Returns** [`Vec`]() ## `app_picker_primary` The primary button of the window picker. **Swift** ```swift func appPickerPrimary(tab: AppPickerTab, selectedSupported: Bool?) -> AppPickerPrimary ``` **Rust** ```rust pub fn app_picker_primary(tab: AppPickerTab, selected_supported: Option) -> AppPickerPrimary ``` | Parameter | Type | Default | | --- | --- | --- | | `tab` | [`AppPickerTab`](#apppickertab-enum) | required | | `selected_supported` | `Option` | required | **Returns** [`AppPickerPrimary`](#apppickerprimary-record) ## `app_picker_progress` Run progress in `[0, 1]`. **Swift** ```swift func appPickerProgress(state: AppPickerState) -> Double ``` **Rust** ```rust pub fn app_picker_progress(state: AppPickerState) -> f64 ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | **Returns** `f64` ## `app_picker_reduce` Advances the picker. **Swift** ```swift func appPickerReduce(state: AppPickerState, event: AppPickerEvent) -> AppPickerState ``` **Rust** ```rust pub fn app_picker_reduce(state: AppPickerState, event: AppPickerEvent) -> AppPickerState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | | `event` | [`AppPickerEvent`](#apppickerevent-enum) | required | **Returns** [`AppPickerState`](#apppickerstate-record) ## `app_picker_review` The review screen, when a plan is under review. **Swift** ```swift func appPickerReview(state: AppPickerState) -> AppReviewView? ``` **Rust** ```rust pub fn app_picker_review(state: AppPickerState) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | **Returns** [`Option`]() ## `app_picker_sections` The pick list's sections. **Swift** ```swift func appPickerSections(state: AppPickerState) -> [AppEntrySection] ``` **Rust** ```rust pub fn app_picker_sections(state: AppPickerState) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | **Returns** [`Vec`](#appentrysection-record) ## `app_picker_sensitive_options` The options step's opt-in checkboxes ("Keep me signed in", "Saved passwords", "Browsing history"), with the signed-in state move chosen. **Swift** ```swift func appPickerSensitiveOptions(state: AppPickerState) -> [AppSensitiveOption] ``` **Rust** ```rust pub fn app_picker_sensitive_options(state: AppPickerState) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | **Returns** [`Vec`]() ## `app_picker_status` What the run is doing, in words ("Packing profile", "Uploading 12 / 80 MB"), for the line under the progress bar; none unless running. **Swift** ```swift func appPickerStatus(state: AppPickerState) -> String? ``` **Rust** ```rust pub fn app_picker_status(state: AppPickerState) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`](#apppickerstate-record) | required | **Returns** `Option` --- # Teleport transfer and drag The overlays that show a teleport in progress: the transfer overlay and drag-and-drop onto a Space. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The transfer overlay follows a running teleport step by step; the drag overlay shows a file or app dragged onto a Space. ## `AppDragOverlayState` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `phase` | [`AppDragOverlayPhase`](#appdragoverlayphase-enum) | | Phase. | | `window_id` / `windowId` | `Option` | | CoreGraphics id of the dragged window. | | `app_name` / `appName` | `Option` | | The dragged app's name. | | `target_space_id` / `targetSpaceId` | `Option` | | The tile under the cursor (selector only). | | `ghost` | `Option` | | The captured ghost (a data URL or a shell-local image key). | ## `AppDragOverlayTransition` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `state` | [`AppDragOverlayState`](#appdragoverlaystate-record) | | New state. | | `effects` | [`Vec`](#appdragoverlayeffect-enum) | | Effects. | ## `AppTransferOverlayState` record Returned by [`app_transfer_reduce`](#app_transfer_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `phase` | [`AppTransferPhase`](#apptransferphase-enum) | | Phase. | | `app_name` / `appName` | `String` | | App being teleported. | | `message` | `Option` | | Failure reason. | | `sent_bytes` / `sentBytes` | `Option` | | Bytes sent. | | `total_bytes` / `totalBytes` | `Option` | | Total bytes. | ## `AppTransferSignal` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `status` | [`AppTransferStatus`](#apptransferstatus-enum) | | Status. | | `app_name` / `appName` | `Option` | | App name. | | `message` | `Option` | | Message. | | `sent_bytes` / `sentBytes` | `Option` | | Bytes sent. | | `total_bytes` / `totalBytes` | `Option` | | Total bytes. | ## `AppDragOverlayEffect` enum **Swift** ```swift .capture(windowId: UInt32?) .commit(spaceId: String) ``` **Rust** ```rust AppDragOverlayEffect::Capture { window_id: Option } AppDragOverlayEffect::Commit { space_id: String } ``` | Variant | Description | | --- | --- | | `Capture` | Capture the ghost (needs Screen Recording; none is fine). Fields: `window_id: Option` | | `Commit` | Teleport to this Space. Fields: `space_id: String` | ## `AppDragOverlayEvent` enum **Swift** ```swift .start(windowId: UInt32?, appName: String?) .enterNotch .leaveNotch .over(spaceId: String) .out .ghostReady(ghost: String?) .drop(spaceId: String?) .cancel ``` **Rust** ```rust AppDragOverlayEvent::Start { window_id: Option, app_name: Option } AppDragOverlayEvent::EnterNotch AppDragOverlayEvent::LeaveNotch AppDragOverlayEvent::Over { space_id: String } AppDragOverlayEvent::Out AppDragOverlayEvent::GhostReady { ghost: Option } AppDragOverlayEvent::Drop { space_id: Option } AppDragOverlayEvent::Cancel ``` | Variant | Description | | --- | --- | | `Start` | A supported window drag began. Fields: `window_id: Option`, `app_name: Option` | | `EnterNotch` | The cursor reached the notch box. | | `LeaveNotch` | The cursor left the selector. | | `Over` | Over a Space tile. Fields: `space_id: String` | | `Out` | Off every tile. | | `GhostReady` | The ghost capture finished. Fields: `ghost: Option` | | `Drop` | Released; the tile under the cursor, if any. Fields: `space_id: Option` | | `Cancel` | Abort. | ## `AppDragOverlayPhase` enum **Swift** ```swift .idle .prompt .selector ``` **Rust** ```rust AppDragOverlayPhase::Idle AppDragOverlayPhase::Prompt AppDragOverlayPhase::Selector ``` | Variant | Description | | --- | --- | | `Idle` | Nothing showing. | | `Prompt` | The "Teleport to Cua" box. | | `Selector` | The Space tiles as drop targets. | ## `AppTransferPhase` enum **Swift** ```swift .active .error ``` **Rust** ```rust AppTransferPhase::Active AppTransferPhase::Error ``` | Variant | Description | | --- | --- | | `Active` | Uploading. | | `Error` | Failed. | ## `AppTransferStatus` enum **Swift** ```swift .start .progress .done .error ``` **Rust** ```rust AppTransferStatus::Start AppTransferStatus::Progress AppTransferStatus::Done AppTransferStatus::Error ``` | Variant | Description | | --- | --- | | `Start` | Began (or retried). | | `Progress` | Bytes moved. | | `Done` | Finished. | | `Error` | Failed. | ## `app_transfer_reduce` Advances the transfer overlay (`None` hides it). **Swift** ```swift func appTransferReduce(state: AppTransferOverlayState?, signal: AppTransferSignal) -> AppTransferOverlayState? ``` **Rust** ```rust pub fn app_transfer_reduce(state: Option, signal: AppTransferSignal) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`Option`](#apptransferoverlaystate-record) | required | | `signal` | [`AppTransferSignal`](#apptransfersignal-record) | required | **Returns** [`Option`](#apptransferoverlaystate-record) ## `app_transfer_title` The transfer overlay's title. **Swift** ```swift func appTransferTitle(state: AppTransferOverlayState) -> String ``` **Rust** ```rust pub fn app_transfer_title(state: AppTransferOverlayState) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppTransferOverlayState`](#apptransferoverlaystate-record) | required | **Returns** `String` --- # Teleport plan and run The teleport plan, its consent and capabilities, and the run's steps. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_teleport_plan` turns a `Teleport.plan` into the consent the user sees; `AppTeleportRunEvent` and `AppTeleportRunReport` follow the run. ## `AppTeleportConsent` record Returned by [`app_picker_consent`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `approved` | `bool` | | Confirmed. | | `acknowledge_sensitive` / `acknowledgeSensitive` | `bool` | | Secrets acknowledged. | | `save_to_keyvault` / `saveToKeyvault` | `bool` | | "Save to Keyvault" checked. | | `acknowledge_relay_plaintext` / `acknowledgeRelayPlaintext` | `bool` | | `Plan.relay_unsealed`'s warning acknowledged (S1). | | `cookie_domains` / `cookieDomains` | `Option>` | | The sites whose cookies to send (the review's per-site choice); `None` when the review did not list sites. | | `exclude` | `Vec` | | Consent items (keys) the user turned off. | | `from_vault` / `fromVault` | `Option>` | | Send these saved Keyvault items instead of reading the live app. | | `include_passwords` / `includePasswords` | `bool` | | Also send the saved passwords (ticked in the review). | ## `AppTeleportPlan` record Returned by [`app_teleport_plan`](#app_teleport_plan), [`app_teleport_plan_from_json`](#app_teleport_plan_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `app` | [`AppCatalogEntry`]() | | The app. | | `space_id` / `spaceId` | `String` | | Target Space. | | `moves` | [`AppTeleportMove`](#appteleportmove-enum) | | What moves. | | `steps` | [`Vec`]() | | Steps. | | `consent` | [`Vec`]() | | Consent lines. | | `sensitive` | `bool` | | Any line is a secret. | | `total_bytes` / `totalBytes` | `u64` | | Bytes that leave this machine. | | `warnings` | `Vec` | | Caveats. | | `relay_unsealed` / `relayUnsealed` | `bool` | | This Space is reached through a relay connection that predates end-to-end sealing: a secret this plan sends would cross it in the clear (S1). Needs `acknowledgeRelayPlaintext` the same way a secret needs `acknowledgeSensitive`. Defaults to `false`: older fixtures and a plan from a relay that predates the capability both read as sealed (never retroactively flagged). | | `json` | `String` | | The SDK's JSON for this plan. | ## `AppTeleportRunEvent` record Returned by [`app_teleport_run_event`](#app_teleport_run_event). | Field | Type | Default | Description | | --- | --- | --- | --- | | `step` | `u32` | | Step index. | | `steps` | `u32` | | Steps. | | `kind` | `String` | | Step kind. | | `phase` | [`AppTeleportRunPhase`](#appteleportrunphase-enum) | | Phase. | | `detail` | `String` | | Detail. | | `done_bytes` / `doneBytes` | `u64` | | Bytes done. | | `total_bytes` / `totalBytes` | `u64` | | Bytes total. | ## `AppTeleportRunReport` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `app_id` / `appId` | `String` | | App id. | | `installed` | `Vec` | | Installed. | | `sent` | `Vec` | | Sent. | | `imported` | `Vec` | | Imported. | | `skipped` | `Vec` | | Skipped. | | `launched` | `bool` | | Launched. | ## `AppTeleportCapability` enum **Swift** ```swift .full .installOnly .unsupported ``` **Rust** ```rust AppTeleportCapability::Full AppTeleportCapability::InstallOnly AppTeleportCapability::Unsupported ``` | Variant | Description | | --- | --- | | `Full` | App and signed-in state. | | `InstallOnly` | App, empty or with files. | | `Unsupported` | Not available. | ## `AppTeleportMove` enum **Swift** ```swift .appOnly .appWithFiles .appWithState ``` **Rust** ```rust AppTeleportMove::AppOnly AppTeleportMove::AppWithFiles AppTeleportMove::AppWithState ``` | Variant | Description | | --- | --- | | `AppOnly` | Just the app. | | `AppWithFiles` | The app with files or folders. | | `AppWithState` | The app with its signed-in state. | ## `AppTeleportRunPhase` enum **Swift** ```swift .started .progress .finished .failed .done ``` **Rust** ```rust AppTeleportRunPhase::Started AppTeleportRunPhase::Progress AppTeleportRunPhase::Finished AppTeleportRunPhase::Failed AppTeleportRunPhase::Done ``` | Variant | Description | | --- | --- | | `Started` | A step started. | | `Progress` | Bytes moved. | | `Finished` | A step finished. | | `Failed` | A step failed. | | `Done` | All done. | ## `app_teleport_plan` A plan from `Teleport.plan`, as the review's plan. **Swift** ```swift func appTeleportPlan(plan: TeleportPlan) -> AppTeleportPlan ``` **Rust** ```rust pub fn app_teleport_plan(plan: TeleportPlan) -> AppTeleportPlan ``` | Parameter | Type | Default | | --- | --- | --- | | `plan` | [`TeleportPlan`]() | required | **Returns** [`AppTeleportPlan`](#appteleportplan-record) ## `app_teleport_plan_from_json` A teleport plan from JSON (fixtures, parity flows). **Swift** ```swift func appTeleportPlanFromJson(json: String) throws -> AppTeleportPlan ``` **Rust** ```rust pub fn app_teleport_plan_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppTeleportPlan`](#appteleportplan-record) · **Raises** [`CuaError`]() ## `app_teleport_run_event` An SDK run event as the picker's (`AppPickerEvent.progress`). **Swift** ```swift func appTeleportRunEvent(event: TeleportRunEvent) -> AppTeleportRunEvent ``` **Rust** ```rust pub fn app_teleport_run_event(event: TeleportRunEvent) -> AppTeleportRunEvent ``` | Parameter | Type | Default | | --- | --- | --- | | `event` | [`TeleportRunEvent`]() | required | **Returns** [`AppTeleportRunEvent`](#appteleportrunevent-record) ## `app_teleport_run_status` `app_picker_status` for a run's events as the SDK delivered them. **Swift** ```swift func appTeleportRunStatus(events: [TeleportRunEvent]) -> String? ``` **Rust** ```rust pub fn app_teleport_run_status(events: Vec) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `events` | [`Vec`]() | required | **Returns** `Option` --- # Teleport review The review and consent before a teleport: what to send per site, from the app or from the Keyvault, and the choice remembered for next time. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `AppReviewView` is the review sheet. `AppReviewToggle` and `AppReviewChoice` change what is sent, per site or per part; `app_review_remember` keeps the choice for the next teleport of the same app to the same Space, and `AppVaultSource` offers the items the Keyvault already holds, which need no Keychain prompt. ## `AppReviewView` record Returned by [`app_picker_review`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Teleport Slack to Aurora". | | `items` | [`Vec`](#appconsentitem-record) | | The consent lines. | | `steps` | [`Vec`](#appplanstepview-record) | | Plan steps. | | `needs_acknowledgement` / `needsAcknowledgement` | `bool` | | The secrets checkbox shows. | | `acknowledged` | `bool` | | Checked. | | `offers_save_to_keyvault` / `offersSaveToKeyvault` | `bool` | | The "Save to Keyvault" checkbox shows (a sensitive plan only: there is nothing to keep sealed otherwise). | | `save_to_keyvault` / `saveToKeyvault` | `bool` | | Checked. | | `needs_relay_plaintext_acknowledgement` / `needsRelayPlaintextAcknowledgement` | `bool` | | The relay-plaintext warning's checkbox shows (S1): this Space predates end-to-end sealing, and the Cua relay could read this secret in transit. | | `acknowledged_relay_plaintext` / `acknowledgedRelayPlaintext` | `bool` | | Checked. | | `can_confirm` / `canConfirm` | `bool` | | "Teleport" is enabled. | | `leaves_text` / `leavesText` | `Option` | | "12 MB leaves this Mac" (none when nothing leaves). | | `warnings` | `Vec` | | Caveats. | | `toggles` | [`Vec`](#appreviewtoggle-record) | | Lines the user can turn off (files, folders, state, secrets), each with whether it is sent. The cookie store is the site list below. | | `offers_domains` / `offersDomains` | `bool` | | The plan sends a browser's cookies, so the sites can be chosen. | | `needs_domains` / `needsDomains` | `bool` | | The sites have not been read yet (the shell asks the Keyvault for them, which may ask for Touch ID). | | `domains` | [`Vec`](#appreviewdomain-record) | | The sites with counts, filtered by the search. | | `domain_summary` / `domainSummary` | `String` | | "3 of 12 sites". | | `domain_query` / `domainQuery` | `String` | | The sites search text. | | `selected_domains` / `selectedDomains` | `Vec` | | The sites chosen (what is remembered for next time). | | `source` | [`AppSendSource`](#appsendsource-enum) | | Where it sends from. | | `offers_vault` / `offersVault` | `bool` | | The app has saved Keyvault items to send instead of reading the live app. | | `vault` | [`AppVaultSource`](#appvaultsource-record) | | The saved items. | | `vault_label` / `vaultLabel` | `String` | | "Send from the Keyvault (212 items, saved 2 days ago)". | | `live_label` / `liveLabel` | `String` | | "Read Chrome now (macOS asks for Keychain access)". | | `offers_passwords` / `offersPasswords` | `bool` | | "Also send saved passwords" shows (there are some to send). | | `include_passwords` / `includePasswords` | `bool` | | Ticked. | | `passwords_label` / `passwordsLabel` | `String` | | "Also send 14 saved passwords". | | `source_note` / `sourceNote` | `String` | | What the Keychain will do for this source, in a line. | ## `AppSendSource` enum **Swift** ```swift .live .vault ``` **Rust** ```rust AppSendSource::Live AppSendSource::Vault ``` | Variant | Description | | --- | --- | | `Live` | Read the live app now (a browser's cookies are decrypted, so macOS asks for Keychain access). | | `Vault` | Send the items saved in the Keyvault for this app. Nothing is read from the host. | ## `AppVaultSource` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `available` | `bool` | | The app has saved items that can be sent (passwords never are). | | `items` | `u32` | | How many. | | `saved` | `String` | | "saved 2 days ago". | | `selected` | `Vec` | | The items chosen to send (ids). | | `password_ids` / `passwordIds` | `Vec` | | The app's saved passwords (ids), sent only when ticked. | ## `AppRememberedChoice` record Returned by [`app_review_remember`](#app_review_remember). | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | `\|`. | | `domains` | `Vec` | | The sites (registrable domains) that were sent. | ## `AppPlanStepView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | `String` | | `install`, `files`, `state`, `launch`. | | `summary` | `String` | | One line. | ## `AppInstallCuaPrompt` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `installed` | `bool` | | Cua is installed but not running. | | `title` | `String` | | Title. | | `message` | `String` | | One line. | | `action_label` / `actionLabel` | `String` | | Button. | | `url` | `String` | | Button target: always one of the constants, never from the error. | ## `AppConsentItem` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`AppConsentKind`](#appconsentkind-enum) | | Kind. | | `key` | `String` | | Stable key. | | `label` | `String` | | Label. | | `detail` | `String` | | Detail. | | `bytes` | `u64` | | Bytes that leave. | | `sensitive` | `bool` | | Credentials, cookies, tokens. | ## `AppReviewChoice` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `source` | [`AppSendSource`](#appsendsource-enum) | | Where to send from. | | `domains` | [`Vec`]() | | The browser's sites with counts (none until read, or for an app that is not a browser). | | `selected_domains` / `selectedDomains` | `Vec` | | The sites chosen. | | `query` | `String` | | The sites list's search text. | | `excluded` | `Vec` | | Consent items (keys) turned off. | | `vault` | [`AppVaultSource`](#appvaultsource-record) | | The saved items. | | `loaded` | `bool` | | The sites were asked for (a failure counts: the review then sends what the plan lists). | | `include_passwords` / `includePasswords` | `bool` | | The saved passwords are ticked to send (off unless the user ticks them; never remembered). | ## `AppReviewDomain` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `domain` | `String` | | The site (`github.com`). | | `counts` | `String` | | "12 cookies, 2 storage values". | | `count` | `u32` | | Items the site holds. | | `signin` | `bool` | | It looks like it keeps a sign-in. | | `identity_provider` / `identityProvider` | `bool` | | An identity provider: its session unlocks other apps. | | `selected` | `bool` | | Chosen to send. | | `selectable` | `bool` | | Can be chosen: it holds something that can be sent. A site whose cookies are all unreadable is greyed out. | | `unavailable` | `u32` | | Cookies that cannot be read, shown greyed with `unavailable_note`. | | `unavailable_note` / `unavailableNote` | `String` | | "3 cookies cannot be sent: Chrome protects them with ...", or empty. | ## `AppReviewToggle` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | The consent item's key. | | `label` | `String` | | Label. | | `detail` | `String` | | Detail. | | `bytes` | `u64` | | Bytes that leave. | | `sensitive` | `bool` | | A secret. | | `selected` | `bool` | | Sent (not turned off). | ## `AppConsentKind` enum **Swift** ```swift .install .file .folder .state .secret ``` **Rust** ```rust AppConsentKind::Install AppConsentKind::File AppConsentKind::Folder AppConsentKind::State AppConsentKind::Secret ``` | Variant | Description | | --- | --- | | `Install` | An install into the Space. | | `File` | A file that leaves this machine. | | `Folder` | A folder that leaves this machine. | | `State` | App state that leaves this machine. | | `Secret` | A secret that leaves this machine. | ## `app_review_remember` `choices` with the sites just sent remembered for `key`. **Swift** ```swift func appReviewRemember(choices: [AppRememberedChoice], key: String, domains: [String]) -> [AppRememberedChoice] ``` **Rust** ```rust pub fn app_review_remember(choices: Vec, key: String, domains: Vec) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `choices` | [`Vec`](#apprememberedchoice-record) | required | | `key` | `String` | required | | `domains` | `Vec` | required | **Returns** [`Vec`](#apprememberedchoice-record) ## `app_review_remember_key` The key a review's site choice is remembered under (`|`). **Swift** ```swift func appReviewRememberKey(app: String, space: String) -> String ``` **Rust** ```rust pub fn app_review_remember_key(app: String, space: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `app` | `String` | required | | `space` | `String` | required | **Returns** `String` ## `app_review_remembered` What was picked last time for `key` (an app and a Space), if anything. **Swift** ```swift func appReviewRemembered(choices: [AppRememberedChoice], key: String) -> [String]? ``` **Rust** ```rust pub fn app_review_remembered(choices: Vec, key: String) -> Option> ``` | Parameter | Type | Default | | --- | --- | --- | | `choices` | [`Vec`](#apprememberedchoice-record) | required | | `key` | `String` | required | **Returns** `Option>` --- # Teleport picker grid The teleport picker's tiles in both apps: tabs, app and window tiles with their icon and preview sources, and the arrow keys. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_picker_app_grid`, `app_picker_window_grid` and `app_picker_remote_grid` build each tab's tiles; an app tile previews its frontmost window. `app_picker_grid_step` moves the selection for the arrow keys and `app_picker_grid_tabs` names the tabs. ## `AppPickerGrid` record Returned by [`app_picker_app_grid`](#app_picker_app_grid), [`app_picker_remote_grid`](#app_picker_remote_grid), [`app_picker_window_grid`](#app_picker_window_grid). | Field | Type | Default | Description | | --- | --- | --- | --- | | `sections` | [`Vec`](#apppickertilesection-record) | | Sections, in order. | | `empty_text` / `emptyText` | `Option` | | When there are no tiles: "No apps match." and the like. | ## `AppPickerGridPrimary` record Returned by [`app_picker_grid_primary`](#app_picker_grid_primary). | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | The label. | | `enabled` | `bool` | | Enabled. | ## `AppPickerGridTabItem` record Returned by [`app_picker_grid_tabs`](#app_picker_grid_tabs). | Field | Type | Default | Description | | --- | --- | --- | --- | | `tab` | [`AppPickerGridTab`](#apppickergridtab-enum) | | Which tab. | | `label` | `String` | | Its label. | ## `AppPickerTile` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | The catalog entry id (Apps) or the window id. | | `title` | `String` | | One line: the app's name, or the window's title. | | `help` | `String` | | The tooltip: what the app can take, why not, or the full title. | | `disabled` | `bool` | | Dimmed and not choosable. | | `selected` | `bool` | | Highlighted (keyboard or click). | | `icon` | [`AppPickerTileIcon`](#apppickertileicon-enum) | | The icon. | | `thumbnail` | [`AppPickerTileThumbnail`](#apppickertilethumbnail-enum) | | The preview. | ## `AppPickerTileSection` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | Title, empty for none. | | `tiles` | [`Vec`](#apppickertile-record) | | Tiles, in order. | ## `AppPickerGridTab` enum **Swift** ```swift .apps .windows .space ``` **Rust** ```rust AppPickerGridTab::Apps AppPickerGridTab::Windows AppPickerGridTab::Space ``` | Variant | Description | | --- | --- | | `Apps` | The app catalog. | | `Windows` | This machine's open windows. | | `Space` | The Space's windows. | ## `AppPickerTileIcon` enum **Swift** ```swift .host(path: String) .guest(appName: String, appId: String, pid: UInt32) .none ``` **Rust** ```rust AppPickerTileIcon::Host { path: String } AppPickerTileIcon::Guest { app_name: String, app_id: String, pid: u32 } AppPickerTileIcon::None ``` | Variant | Description | | --- | --- | | `Host` | This machine's app at `path` (the SDK's `Teleport.appIconPng`). Fields: `path: String` | | `Guest` | A Space's app (the SDK's `Space.appIcons`). Fields: `app_name: String`, `app_id: String`, `pid: u32` | | `None` | No icon to show. | ## `AppPickerTileThumbnail` enum **Swift** ```swift .hostWindow(windowId: UInt32) .guestWindow(windowId: String, epoch: UInt64) .none ``` **Rust** ```rust AppPickerTileThumbnail::HostWindow { window_id: u32 } AppPickerTileThumbnail::GuestWindow { window_id: String, epoch: u64 } AppPickerTileThumbnail::None ``` | Variant | Description | | --- | --- | | `HostWindow` | One of this machine's windows (the window-drag preview source, the SDK's `Teleport.captureWindowThumbnail`). Fields: `window_id: u32` | | `GuestWindow` | One of the Space's windows (the SDK's `Space.windowThumbnail`). Fields: `window_id: String`, `epoch: u64` | | `None` | No window to preview (the tile shows the icon large). | ## `app_picker_app_grid` The picker's Apps tab as tiles (`windows`: this machine's, front to back, for each app's frontmost-window preview). **Swift** ```swift func appPickerAppGrid(state: AppPickerState, windows: [AppOpenWindow]) -> AppPickerGrid ``` **Rust** ```rust pub fn app_picker_app_grid(state: AppPickerState, windows: Vec) -> AppPickerGrid ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppPickerState`]() | required | | `windows` | [`Vec`]() | required | **Returns** [`AppPickerGrid`](#apppickergrid-record) ## `app_picker_grid_primary` The picker grid's primary button for a tab ("Continue", "Teleport to <Space>", "Stream to This Mac"), live with a choosable tile selected. **Swift** ```swift func appPickerGridPrimary(tab: AppPickerGridTab, spaceName: String, grid: AppPickerGrid) -> AppPickerGridPrimary ``` **Rust** ```rust pub fn app_picker_grid_primary(tab: AppPickerGridTab, space_name: String, grid: AppPickerGrid) -> AppPickerGridPrimary ``` | Parameter | Type | Default | | --- | --- | --- | | `tab` | [`AppPickerGridTab`](#apppickergridtab-enum) | required | | `space_name` | `String` | required | | `grid` | [`AppPickerGrid`](#apppickergrid-record) | required | **Returns** [`AppPickerGridPrimary`](#apppickergridprimary-record) ## `app_picker_grid_step` Arrow keys over a picker grid: the tile `delta` choosable places away. **Swift** ```swift func appPickerGridStep(grid: AppPickerGrid, selected: String?, delta: Int32) -> String? ``` **Rust** ```rust pub fn app_picker_grid_step(grid: AppPickerGrid, selected: Option, delta: i32) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `grid` | [`AppPickerGrid`](#apppickergrid-record) | required | | `selected` | `Option` | required | | `delta` | `i32` | required | **Returns** `Option` ## `app_picker_grid_tabs` The teleport picker's tab strip. **Swift** ```swift func appPickerGridTabs(spaceName: String) -> [AppPickerGridTabItem] ``` **Rust** ```rust pub fn app_picker_grid_tabs(space_name: String) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `space_name` | `String` | required | **Returns** [`Vec`](#apppickergridtabitem-record) ## `app_picker_remote_grid` The picker's From <Space> tab. **Swift** ```swift func appPickerRemoteGrid(windows: [AppRemoteWindow], query: String, selected: String?) -> AppPickerGrid ``` **Rust** ```rust pub fn app_picker_remote_grid(windows: Vec, query: String, selected: Option) -> AppPickerGrid ``` | Parameter | Type | Default | | --- | --- | --- | | `windows` | [`Vec`]() | required | | `query` | `String` | required | | `selected` | `Option` | required | **Returns** [`AppPickerGrid`](#apppickergrid-record) ## `app_picker_window_grid` The picker's Open windows tab. **Swift** ```swift func appPickerWindowGrid(windows: [AppOpenWindow], query: String, selected: String?) -> AppPickerGrid ``` **Rust** ```rust pub fn app_picker_window_grid(windows: Vec, query: String, selected: Option) -> AppPickerGrid ``` | Parameter | Type | Default | | --- | --- | --- | | `windows` | [`Vec`]() | required | | `query` | `String` | required | | `selected` | `Option` | required | **Returns** [`AppPickerGrid`](#apppickergrid-record) --- # Your cloud The "Connect a cloud" sheet and the New Space wizard's "Your cloud": providers with their found mark, Test that creates nothing, Connect, and what a connected cloud runs at what cost. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_cloud_connect_reduce` applies an action and `app_cloud_connect_view` derives the sheet; its `request` is the `cloud_test` or `cloud_connect` call to run. `app_connected_clouds_from_status_json` turns the SDK's `cloud_status` into the wizard's `clouds`. ## `AppConnectedCloud` record Returned by [`app_connected_cloud_from_json`](#app_connected_cloud_from_json), [`app_connected_clouds_from_status_json`](#app_connected_clouds_from_status_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | The SDK's `on` word: `aws`, `gcp`, `modal`. | | `title` | `String` | | `AWS`, `Google Cloud`, `Modal`. | | `label` | `String` | | "AWS \u{b7} us-west-2". | | `is_default` / `isDefault` | `bool` | | It is the default location (`default.on`). | | `ttl_hours` / `ttlHours` | `u32` | | Hours after which a Space there deletes itself (0: never). | | `offers` | [`Vec`](#appcloudoffer-record) | | What it runs. | ## `AppCloudOffer` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `String` | | The image family: `linux`, `linux-slim`, `linux-vm`, `windows`, `omarchy`, `macos`. | | `kind` | `String` | | `container` or `vm`. | | `supported` | `bool` | | Whether it can run there. | | `reason` | `String` | | Why not (one line), or a note. | | `machine_type` / `machineType` | `String` | | The machine type the cloud picks (`t4g.medium`). | | `usd_per_hour` / `usdPerHour` | `f64` | | Estimated US dollars per hour while it runs. | ## `app_connected_clouds_from_status_json` The wizard's connected clouds from the SDK's `cloud_status` JSON. **Swift** ```swift func appConnectedCloudsFromStatusJson(json: String) throws -> [AppConnectedCloud] ``` **Rust** ```rust pub fn app_connected_clouds_from_status_json(json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`Vec`](#appconnectedcloud-record) · **Raises** [`CuaError`]() ## `app_connected_cloud_from_json` A connected cloud from JSON (parity flows). **Swift** ```swift func appConnectedCloudFromJson(json: String) throws -> AppConnectedCloud ``` **Rust** ```rust pub fn app_connected_cloud_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppConnectedCloud`](#appconnectedcloud-record) · **Raises** [`CuaError`]() ## `app_is_cloud_word` Whether a `default.on` value names one of the user's clouds. **Swift** ```swift func appIsCloudWord(on: String) -> Bool ``` **Rust** ```rust pub fn app_is_cloud_word(on: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `on` | `String` | required | **Returns** `bool` ## `AppCloudCheckInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | `credentials`, `permissions`, `quota`, ... | | `ok` | `bool` | | Passed. | | `detail` | `String` | | What was found, or what to fix. | ## `AppCloudCheckRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `ok` | `bool` | | Passed. | | `text` | `String` | | "credentials: account 418638388952". | ## `AppCloudConnectInput` record Returned by [`app_cloud_connect_input_from_json`](#app_cloud_connect_input_from_json), [`app_cloud_connect_input_from_status_json`](#app_cloud_connect_input_from_status_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `providers` | [`Vec`](#appcloudproviderinput-record) | | The providers this build supports. | ## `AppCloudConnectState` record Returned by [`app_cloud_connect_initial`](#app_cloud_connect_initial), [`app_cloud_connect_reduce`](#app_cloud_connect_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `selected` | `Option` | | The chosen provider (`aws`); `None`: the first with credentials. | | `value` | `String` | | The region, project or environment as typed (empty: the default). | | `profile` | `String` | | The profile as typed (empty: the default). | | `make_default` / `makeDefault` | `bool` | | Also make it the default location. | | `phase` | [`AppCloudConnectPhase`](#appcloudconnectphase-enum) | | Where it is. | ## `AppCloudConnectView` record Returned by [`app_cloud_connect_view`](#app_cloud_connect_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Connect a cloud". | | `rows` | [`Vec`](#appcloudproviderrow-record) | | The providers. | | `field` | [`Option`](#appcloudfield-record) | | The region, project or environment field. | | `profile_field` / `profileField` | [`Option`](#appcloudfield-record) | | The profile field (AWS, Modal). | | `checks` | [`Vec`](#appcloudcheckrow-record) | | The checks the last test ran. | | `result` | `Option` | | One line under the checks ("Ready. Nothing was created."). | | `touches` | `Vec` | | "What Cua will touch": what the selected provider creates, that only Cua's own tagged resources are ever touched, how cost and auto-stop work, and the command to remove everything. Empty when no real provider is chosen (including "No cloud"). | | `make_default_label` / `makeDefaultLabel` | `String` | | "Make default". | | `make_default` / `makeDefault` | `bool` | | Its value. | | `test_label` / `testLabel` | `String` | | "Test" / "Testing\u{2026}". | | `can_test` / `canTest` | `bool` | | Test can be pressed. | | `test_help` / `testHelp` | `String` | | Test's tooltip. | | `connect_label` / `connectLabel` | `String` | | "Connect" / "Connecting\u{2026}". | | `can_connect` / `canConnect` | `bool` | | Connect can be pressed. | | `cancel_label` / `cancelLabel` | `String` | | "Cancel". | | `error` | `Option` | | The last error. | | `done` | `bool` | | Connected: close the sheet and refresh. | | `request` | [`Option`](#appcloudconnectrequest-enum) | | The call to run now (while testing or connecting). | ## `AppCloudField` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `region`, `project`, `environment`, `profile`. | | `label` | `String` | | "Region". | | `placeholder` | `String` | | The default the provider uses when it is empty. | | `value` | `String` | | As typed. | ## `AppCloudProviderInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | `aws`, `gcp`, `modal`. | | `title` | `String` | | `AWS`, `Google Cloud`, `Modal`. | | `connected` | `bool` | | Connected already. | | `found` | `bool` | | This machine has its CLI sign-in (or config). | | `source` | `String` | | Where ("~/.aws profile default"), never a secret. | | `profile` | `String` | | AWS or Modal profile (the connected or detected one). | | `region` | `String` | | Region. | | `project` | `String` | | GCP project. | | `environment` | `String` | | Modal environment. | | `label` | `String` | | "AWS \u{b7} us-west-2". | ## `AppCloudProviderRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `aws`. | | `title` | `String` | | `AWS`. | | `detail` | `String` | | The one line after the title ("Connected, AWS \u{b7} us-west-2", "~/.aws profile default", "No sign-in found"). | | `found` | `bool` | | This machine has its sign-in (the found mark). | | `selected` | `bool` | | Chosen. | ## `AppCloudTargetArgs` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `provider` | `String` | | `aws`, `gcp`, `modal`. | | `profile` | `Option` | | AWS or Modal profile. | | `region` | `Option` | | AWS region. | | `project` | `Option` | | GCP project. | | `environment` | `Option` | | Modal environment. | ## `AppCloudConnectAction` enum **Swift** ```swift .select(name: String) .setValue(text: String) .setProfile(text: String) .setMakeDefault(on: Bool) .test .tested(ok: Bool, account: String, checks: [AppCloudCheckInput]) .connect .connected(label: String) .failed(error: String) ``` **Rust** ```rust AppCloudConnectAction::Select { name: String } AppCloudConnectAction::SetValue { text: String } AppCloudConnectAction::SetProfile { text: String } AppCloudConnectAction::SetMakeDefault { on: bool } AppCloudConnectAction::Test AppCloudConnectAction::Tested { ok: bool, account: String, checks: Vec } AppCloudConnectAction::Connect AppCloudConnectAction::Connected { label: String } AppCloudConnectAction::Failed { error: String } ``` | Variant | Description | | --- | --- | | `Select` | A provider row. Fields: `name: String` | | `SetValue` | The region, project or environment field. Fields: `text: String` | | `SetProfile` | The profile field. Fields: `text: String` | | `SetMakeDefault` | "Make default". Fields: `on: bool` | | `Test` | Test pressed (the shell runs the view's request). | | `Tested` | The test finished. Fields: `ok: bool`, `account: String`, `checks: Vec` | | `Connect` | Connect pressed (the shell runs the view's request). | | `Connected` | Connected. Fields: `label: String` | | `Failed` | A call failed. Fields: `error: String` | ## `AppCloudConnectPhase` enum **Swift** ```swift .idle .testing .tested(ok: Bool, account: String, checks: [AppCloudCheckInput]) .connecting .connected(label: String) .failed(error: String) ``` **Rust** ```rust AppCloudConnectPhase::Idle AppCloudConnectPhase::Testing AppCloudConnectPhase::Tested { ok: bool, account: String, checks: Vec } AppCloudConnectPhase::Connecting AppCloudConnectPhase::Connected { label: String } AppCloudConnectPhase::Failed { error: String } ``` | Variant | Description | | --- | --- | | `Idle` | Nothing running. | | `Testing` | `cloud_test` is running. | | `Tested` | A test finished. Fields: `ok: bool`, `account: String`, `checks: Vec` | | `Connecting` | `cloud_connect` is running. | | `Connected` | Connected (the shell closes the sheet and refreshes). Fields: `label: String` | | `Failed` | A call failed. Fields: `error: String` | ## `AppCloudConnectRequest` enum **Swift** ```swift .test(target: AppCloudTargetArgs) .connect(target: AppCloudTargetArgs, makeDefault: Bool) ``` **Rust** ```rust AppCloudConnectRequest::Test { target: AppCloudTargetArgs } AppCloudConnectRequest::Connect { target: AppCloudTargetArgs, make_default: bool } ``` | Variant | Description | | --- | --- | | `Test` | `cloud_test`. Fields: `target: AppCloudTargetArgs` | | `Connect` | `cloud_connect`. Fields: `target: AppCloudTargetArgs`, `make_default: bool` | ## `app_cloud_connect_action_from_json` A "Connect a cloud" action from JSON (parity flows). **Swift** ```swift func appCloudConnectActionFromJson(json: String) throws -> AppCloudConnectAction ``` **Rust** ```rust pub fn app_cloud_connect_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppCloudConnectAction`](#appcloudconnectaction-enum) · **Raises** [`CuaError`]() ## `app_cloud_connect_initial` A new "Connect a cloud" sheet. **Swift** ```swift func appCloudConnectInitial() -> AppCloudConnectState ``` **Rust** ```rust pub fn app_cloud_connect_initial() -> AppCloudConnectState ``` **Returns** [`AppCloudConnectState`](#appcloudconnectstate-record) ## `app_cloud_connect_input_from_json` "Connect a cloud" input from JSON (fixtures, parity flows). **Swift** ```swift func appCloudConnectInputFromJson(json: String) throws -> AppCloudConnectInput ``` **Rust** ```rust pub fn app_cloud_connect_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppCloudConnectInput`](#appcloudconnectinput-record) · **Raises** [`CuaError`]() ## `app_cloud_connect_input_from_status_json` The sheet's input from the SDK's `cloud_status` JSON. **Swift** ```swift func appCloudConnectInputFromStatusJson(json: String) throws -> AppCloudConnectInput ``` **Rust** ```rust pub fn app_cloud_connect_input_from_status_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppCloudConnectInput`](#appcloudconnectinput-record) · **Raises** [`CuaError`]() ## `app_cloud_connect_reduce` Advances the "Connect a cloud" sheet. **Swift** ```swift func appCloudConnectReduce(input: AppCloudConnectInput, state: AppCloudConnectState, action: AppCloudConnectAction) -> AppCloudConnectState ``` **Rust** ```rust pub fn app_cloud_connect_reduce(input: AppCloudConnectInput, state: AppCloudConnectState, action: AppCloudConnectAction) -> AppCloudConnectState ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppCloudConnectInput`](#appcloudconnectinput-record) | required | | `state` | [`AppCloudConnectState`](#appcloudconnectstate-record) | required | | `action` | [`AppCloudConnectAction`](#appcloudconnectaction-enum) | required | **Returns** [`AppCloudConnectState`](#appcloudconnectstate-record) ## `app_cloud_connect_view` The "Connect a cloud" sheet as drawn. **Swift** ```swift func appCloudConnectView(input: AppCloudConnectInput, state: AppCloudConnectState) -> AppCloudConnectView ``` **Rust** ```rust pub fn app_cloud_connect_view(input: AppCloudConnectInput, state: AppCloudConnectState) -> AppCloudConnectView ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppCloudConnectInput`](#appcloudconnectinput-record) | required | | `state` | [`AppCloudConnectState`](#appcloudconnectstate-record) | required | **Returns** [`AppCloudConnectView`](#appcloudconnectview-record) --- # Window drag trigger When dragging a window opens Teleport to Cua: move versus resize, the notch line and the dwell. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_drag_classify` tells a move from a resize by the window's frame. `app_drag_trigger_initial` and `app_drag_trigger_apply` run the trigger: the cursor passes a line 5 pt above the notch's bottom edge, or rests 0.3 s in the Teleport to Cua box. `app_drag_displays` and `app_drag_portal_displays` give the notch geometry of each display. ## `AppStillAnchor` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `x` | `f64` | | X. | | `y` | `f64` | | Y. | | `since_ms` / `sinceMs` | `u64` | | When (ms, the shell's monotonic clock). | ## `AppTriggerPhase` enum **Swift** ```swift .hidden .prompt .expanded ``` **Rust** ```rust AppTriggerPhase::Hidden AppTriggerPhase::Prompt AppTriggerPhase::Expanded ``` | Variant | Description | | --- | --- | | `Hidden` | Nothing (no drag, a resize, or not known yet). | | `Prompt` | The "Teleport to Cua" box. | | `Expanded` | The Space tiles as drop targets. | ## `AppDragDisplay` record Returned by [`app_drag_displays`](#app_drag_displays), [`app_drag_portal_displays`](#app_drag_portal_displays). | Field | Type | Default | Description | | --- | --- | --- | --- | | `frame` | [`AppLogicalRect`]() | | The display. | | `notch` | [`AppLogicalRect`]() | | The notch (or, without one, the menu-bar-high stand-in). | | `prompt` | [`AppLogicalRect`]() | | The "Teleport to Cua" box: the dwell area. | | `expanded` | [`AppLogicalRect`]() | | The expanded panel: the drag stays expanded inside it. | ## `AppDragTriggerState` record Returned by [`app_drag_trigger_initial`](#app_drag_trigger_initial). | Field | Type | Default | Description | | --- | --- | --- | --- | | `active` | `bool` | | A drag is in flight. | | `kind` | [`AppDragKind`](#appdragkind-enum) | | Move or resize. | | `phase` | [`AppTriggerPhase`](#apptriggerphase-enum) | | What shows. | | `window_id` / `windowId` | `Option` | | The dragged window. | | `app_name` / `appName` | `Option` | | Its app's name. | | `start_frame` / `startFrame` | [`Option`]() | | The window's frame at mouse down. | | `display` | `Option` | | The display (index into the shell's list) the panel expanded on. | | `still` | [`Option`](#appstillanchor-record) | | Where the cursor rests, while it rests in the box. | ## `AppDragTriggerTransition` record Returned by [`app_drag_trigger_apply`](#app_drag_trigger_apply). | Field | Type | Default | Description | | --- | --- | --- | --- | | `state` | [`AppDragTriggerState`](#appdragtriggerstate-record) | | New state. | | `overlay` | [`Vec`]() | | Feed these to the drag overlay, in order. A `drop` carries no Space: the shell puts in the tile under the cursor, if any. | | `tick_at_ms` / `tickAtMs` | `Option` | | Send a `tick` at this time (replacing any earlier request); none cancels it. | ## `AppDragKind` enum **Swift** ```swift .pending .move .resize ``` **Rust** ```rust AppDragKind::Pending AppDragKind::Move AppDragKind::Resize ``` | Variant | Description | | --- | --- | | `Pending` | Not known yet (no frames, or they have not changed enough). | | `Move` | The window follows the cursor at its size. | | `Resize` | An edge or a corner: the size changes. | ## `AppDragTriggerEvent` enum **Swift** ```swift .start(windowId: UInt32?, appName: String?, x: Double, y: Double, tMs: UInt64, startFrame: AppLogicalRect?, frame: AppLogicalRect?) .cursor(x: Double, y: Double, tMs: UInt64) .frame(frame: AppLogicalRect) .tick(tMs: UInt64) .end(x: Double, y: Double, tMs: UInt64) .cancel ``` **Rust** ```rust AppDragTriggerEvent::Start { window_id: Option, app_name: Option, x: f64, y: f64, t_ms: u64, start_frame: Option, frame: Option } AppDragTriggerEvent::Cursor { x: f64, y: f64, t_ms: u64 } AppDragTriggerEvent::Frame { frame: AppLogicalRect } AppDragTriggerEvent::Tick { t_ms: u64 } AppDragTriggerEvent::End { x: f64, y: f64, t_ms: u64 } AppDragTriggerEvent::Cancel ``` | Variant | Description | | --- | --- | | `Start` | A window drag started (the SDK's `start`). Fields: `window_id: Option`, `app_name: Option`, `x: f64`, `y: f64`, `t_ms: u64`, `start_frame: Option`, `frame: Option` | | `Cursor` | The cursor moved. Fields: `x: f64`, `y: f64`, `t_ms: u64` | | `Frame` | The dragged window's frame. Fields: `frame: AppLogicalRect` | | `Tick` | The timer `DragTriggerTransition.tick_at_ms` asked for fired. Fields: `t_ms: u64` | | `End` | Released. Fields: `x: f64`, `y: f64`, `t_ms: u64` | | `Cancel` | Abort (the notch was hidden, the monitor stopped). | ## `app_drag_classify` A window's frame at mouse down against a later one: move, resize, or not known yet. **Swift** ```swift func appDragClassify(start: AppLogicalRect, now: AppLogicalRect) -> AppDragKind ``` **Rust** ```rust pub fn app_drag_classify(start: AppLogicalRect, now: AppLogicalRect) -> AppDragKind ``` | Parameter | Type | Default | | --- | --- | --- | | `start` | [`AppLogicalRect`]() | required | | `now` | [`AppLogicalRect`]() | required | **Returns** [`AppDragKind`](#appdragkind-enum) ## `app_drag_displays` The trigger geometry for screens (the primary first), in global top-left points. **Swift** ```swift func appDragDisplays(screens: [AppScreenFacts]) -> [AppDragDisplay] ``` **Rust** ```rust pub fn app_drag_displays(screens: Vec) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `screens` | [`Vec`]() | required | **Returns** [`Vec`](#appdragdisplay-record) ## `app_drag_portal_displays` The same for a portal whose expanded panel is its top-centred switcher window (the Tauri app; the parity flows check it here too). **Swift** ```swift func appDragPortalDisplays(screens: [AppScreenFacts]) -> [AppDragDisplay] ``` **Rust** ```rust pub fn app_drag_portal_displays(screens: Vec) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `screens` | [`Vec`]() | required | **Returns** [`Vec`](#appdragdisplay-record) ## `app_drag_trigger_apply` Advances the window-drag trigger over the notch displays: move or resize, the trigger line, the dwell, expand and collapse. **Swift** ```swift func appDragTriggerApply(state: AppDragTriggerState, event: AppDragTriggerEvent, displays: [AppDragDisplay]) -> AppDragTriggerTransition ``` **Rust** ```rust pub fn app_drag_trigger_apply(state: AppDragTriggerState, event: AppDragTriggerEvent, displays: Vec) -> AppDragTriggerTransition ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppDragTriggerState`](#appdragtriggerstate-record) | required | | `event` | [`AppDragTriggerEvent`](#appdragtriggerevent-enum) | required | | `displays` | [`Vec`](#appdragdisplay-record) | required | **Returns** [`AppDragTriggerTransition`](#appdragtriggertransition-record) ## `app_drag_trigger_initial` The window-drag trigger's idle state. **Swift** ```swift func appDragTriggerInitial() -> AppDragTriggerState ``` **Rust** ```rust pub fn app_drag_trigger_initial() -> AppDragTriggerState ``` **Returns** [`AppDragTriggerState`](#appdragtriggerstate-record) --- # Experiments Settings > Experiments: the switches that hide Cua Volume, your own cloud and sharing until they are turned on. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_experiments_page` derives the Experiments tab from the saved `AppExperiments`; `app_experiments_choose` applies a switch. Off only hides a feature: a mounted Volume, a connected cloud and existing shares stay as they are. ## `app_experiments_from_json` The switches from JSON (fixtures, parity). **Swift** ```swift func appExperimentsFromJson(json: String) throws -> AppExperiments ``` **Rust** ```rust pub fn app_experiments_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppExperiments`](#appexperiments-record) · **Raises** [`CuaError`]() ## `app_settings_with_storage` The Settings page with Settings, Storage after General while the Cua Volume experiment is on (left out while it is off). **Swift** ```swift func appSettingsWithStorage(page: AppSettingsPage, storage: AppSettingsSection, experiments: AppExperiments) -> AppSettingsPage ``` **Rust** ```rust pub fn app_settings_with_storage(page: AppSettingsPage, storage: AppSettingsSection, experiments: AppExperiments) -> AppSettingsPage ``` | Parameter | Type | Default | | --- | --- | --- | | `page` | [`AppSettingsPage`]() | required | | `storage` | [`AppSettingsSection`]() | required | | `experiments` | [`AppExperiments`](#appexperiments-record) | required | **Returns** [`AppSettingsPage`]() ## `app_telemetry_experiments_changed` The events of a change in Settings, Experiments (call with the switches before and after it). **Swift** ```swift func appTelemetryExperimentsChanged(before: AppExperiments, after: AppExperiments) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_experiments_changed(before: AppExperiments, after: AppExperiments) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `before` | [`AppExperiments`](#appexperiments-record) | required | | `after` | [`AppExperiments`](#appexperiments-record) | required | **Returns** [`Vec`]() ## `app_telemetry_experiments_on` Which experiments are on (at launch). **Swift** ```swift func appTelemetryExperimentsOn(experiments: AppExperiments) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_experiments_on(experiments: AppExperiments) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `experiments` | [`AppExperiments`](#appexperiments-record) | required | **Returns** [`Vec`]() ## `AppExperiments` record Returned by [`app_experiments_choose`](#app_experiments_choose), [`app_experiments_from_json`](#app_experiments_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `cua_volume` / `cuaVolume` | `bool` | | Cua Volume in the first run and Settings, Storage. | | `your_cloud` / `yourCloud` | `bool` | | Your own clouds in the New Space wizard. | | `sharing` | `bool` | | Sharing a Space with other accounts. | ## `app_experiments_choose` The switches after a row's choice (`on` or `off`). **Swift** ```swift func appExperimentsChoose(experiments: AppExperiments, row: String, option: String) -> AppExperiments ``` **Rust** ```rust pub fn app_experiments_choose(experiments: AppExperiments, row: String, option: String) -> AppExperiments ``` | Parameter | Type | Default | | --- | --- | --- | | `experiments` | [`AppExperiments`](#appexperiments-record) | required | | `row` | `String` | required | | `option` | `String` | required | **Returns** [`AppExperiments`](#appexperiments-record) ## `app_experiments_page` Settings, Experiments: one switch and one line per experiment. **Swift** ```swift func appExperimentsPage(experiments: AppExperiments) -> AppSettingsPage ``` **Rust** ```rust pub fn app_experiments_page(experiments: AppExperiments) -> AppSettingsPage ``` | Parameter | Type | Default | | --- | --- | --- | | `experiments` | [`AppExperiments`](#appexperiments-record) | required | **Returns** [`AppSettingsPage`]() --- # Run on The New Space Run on menu: This Mac, your machines that provide Spaces, and connected clouds. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_wizard_placement_options` lists where a new Space can run: This Mac, each machine that provides Spaces (greyed out with why when offline or at its limit), and, with the Your cloud experiment on, each connected cloud. ## `app_wizard_placement_options` The "Run on" menu: This Mac, your machines, your clouds. **Swift** ```swift func appWizardPlacementOptions(state: AppWizardState, env: AppWizardEnv) -> [AppPlacementOption] ``` **Rust** ```rust pub fn app_wizard_placement_options(state: AppWizardState, env: AppWizardEnv) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppWizardState`]() | required | | `env` | [`AppWizardEnv`]() | required | **Returns** [`Vec`](#appplacementoption-record) ## `app_space_host` One of your machines that provides Spaces (the SDK's `Spaces.hosts()`) as the wizard reads it. **Swift** ```swift func appSpaceHost(host: SpacesHost) -> AppSpaceHost ``` **Rust** ```rust pub fn app_space_host(host: SpacesHost) -> AppSpaceHost ``` | Parameter | Type | Default | | --- | --- | --- | | `host` | [`SpacesHost`]() | required | **Returns** [`AppSpaceHost`](#appspacehost-record) ## `AppPlacementOption` record Returned by [`app_wizard_placement_options`](#app_wizard_placement_options). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | What choosing it sets as the create's `on`: `local`, `cloud`, `host:`, or a connected cloud's word (`aws`). | | `label` | `String` | | The menu's text: "This Mac", "Mac mini", "Mac mini (offline)", "AWS · us-west-2". | | `group` | `String` | | Its group, in menu order (a separator between groups): `this-mac`, `hosts`, `clouds`. | | `selected` | `bool` | | Chosen. | | `enabled` | `bool` | | It can be chosen now (offline, at its limit: not). | | `detail` | `String` | | One line: why not, or what it runs (the tooltip). | ## `AppSpaceHost` record Returned by [`app_space_host`](#app_space_host). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | What `on="host:"` takes: the relay machine id, or the name a direct host was added as. | | `name` | `String` | | Its name ("Mac mini"). | | `via` | `String` | | `relay` or `direct` (its Tailscale or LAN address). | | `online` | `bool` | | It answered just now. | | `os` | `String` | | Its operating system (`macos`, `linux`, `windows`), when it answered. | | `limits` | [`Vec`]() | | Its limits, when it answered. | --- # Power button The power button on a Space: suspend and resume, or turn off and on. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_power_button` derives the button for a Space from what its provider supports and any power change in flight (`app_creates_is_powering`). ## `app_creates_is_powering` Whether a power action runs for the Space `id`: its button waits. **Swift** ```swift func appCreatesIsPowering(state: AppCreatesState, id: String) -> Bool ``` **Rust** ```rust pub fn app_creates_is_powering(state: AppCreatesState, id: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppCreatesState`]() | required | | `id` | `String` | required | **Returns** `bool` ## `AppPendingPower` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | The Space's id. | | `on` | `bool` | | Turning it on (else off). | | `started_at` / `startedAt` | `i64` | | Epoch ms it started. | | `done` | `bool` | | The SDK returned: the row keeps saying so until the registry shows the Space on (or off). | | `error` | `Option` | | Why it failed: shown inline until the next power action. | ## `AppPowerButton` record Returned by [`app_power_button`](#app_power_button). | Field | Type | Default | Description | | --- | --- | --- | --- | | `symbol` | `String` | | SF Symbol (`POWER_SYMBOL`). | | `help` | `String` | | Tooltip and accessibility label: "Suspend", "Resume", "Turn off" or "Turn on"; while it runs, "Suspending…" and the like. | | `turn_on` / `turnOn` | `bool` | | A press turns it on (else off): the shell's SDK call. | | `enabled` | `bool` | | It can be pressed (not while an action runs). | | `busy` | `bool` | | An action runs: a spinner shows in its place. | ## `AppSpacePower` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `control` | [`AppPowerControl`](#apppowercontrol-enum) | | How it turns off. | | `off` | `bool` | | It is off: suspended or stopped as the SDK recorded it, or not answering with no recorded state (a Space one of your machines provides). | | `turning_on` / `turningOn` | `Option` | | Being turned on (`true`) or off (`false`) now. | | `error` | `Option` | | Why the last power action failed, until the next one. | ## `AppPowerControl` enum **Swift** ```swift .suspend .stop ``` **Rust** ```rust AppPowerControl::Suspend AppPowerControl::Stop ``` | Variant | Description | | --- | --- | | `Suspend` | Suspended in memory; it resumes where it was. | | `Stop` | Stopped with its disk kept; it boots again. | ## `app_power_button` The power button next to Delete, for a Space that turns off and on. **Swift** ```swift func appPowerButton(space: AppSpace) -> AppPowerButton? ``` **Rust** ```rust pub fn app_power_button(space: AppSpace) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `space` | [`AppSpace`]() | required | **Returns** [`Option`](#apppowerbutton-record) --- # Notch The notch switcher: state, events, layout on the screen and motion. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The notch is the Space switcher at the top of the screen. `app_notch_reduce` applies an event; `app_notch_layout`, `app_notch_radii` and `app_notch_motion` place and animate it. ## `AppScreenFacts` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `frame` | [`AppLogicalRect`](#applogicalrect-record) | | `NSScreen.frame`. | | `visible_frame` / `visibleFrame` | [`AppLogicalRect`](#applogicalrect-record) | | `NSScreen.visibleFrame`. | | `safe_area_top` / `safeAreaTop` | `f64` | | `safeAreaInsets.top` (0 without a notch). | | `aux_left_width` / `auxLeftWidth` | `Option` | | `auxiliaryTopLeftArea` width, when present. | | `aux_right_width` / `auxRightWidth` | `Option` | | `auxiliaryTopRightArea` width, when present. | ## `AppLogicalRect` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `x` | `f64` | | | | `y` | `f64` | | | | `width` | `f64` | | | | `height` | `f64` | | | ## `app_presence_name` The name on this user's presence cursor: the account's name, else its email's local part, else its username, else this computer's account (full, then short name). Never empty, never an agent's or "You". **Swift** ```swift func appPresenceName(name: String?, email: String?, username: String?, osFullName: String?, osUser: String?) -> String ``` **Rust** ```rust pub fn app_presence_name(name: Option, email: Option, username: Option, os_full_name: Option, os_user: Option) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `Option` | required | | `email` | `Option` | required | | `username` | `Option` | required | | `os_full_name` | `Option` | required | | `os_user` | `Option` | required | **Returns** `String` ## `app_presence_principal_id` The stable principal id this user joins presence as. **Swift** ```swift func appPresencePrincipalId(email: String?, subject: String?, username: String?, osUser: String?) -> String ``` **Rust** ```rust pub fn app_presence_principal_id(email: Option, subject: Option, username: Option, os_user: Option) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `email` | `Option` | required | | `subject` | `Option` | required | | `username` | `Option` | required | | `os_user` | `Option` | required | **Returns** `String` ## `app_os_icon_svg` An OS icon's artwork (a single-color 24 x 24 SVG), by the id a tile carries (`os-ubuntu`, ...). **Swift** ```swift func appOsIconSvg(id: String) -> String? ``` **Rust** ```rust pub fn app_os_icon_svg(id: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | **Returns** `Option` ## `app_os_icon_system_symbol` The system symbol drawn instead of the SVG on macOS (`apple.logo`). **Swift** ```swift func appOsIconSystemSymbol(id: String) -> String? ``` **Rust** ```rust pub fn app_os_icon_system_symbol(id: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | **Returns** `Option` ## `AppNotchAccess` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `text` | `String` | | "Keyvault sign-ins live in dev-1" (opens the Access page). | | `dismiss` | `String` | | "Dismiss". | ## `AppNotchActivity` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`AppNotchActivityKind`](#appnotchactivitykind-enum) | | Which. | | `label` | `String` | | Spoken label and tooltip. | | `symbol` | `Option` | | The symbol to draw (the hotspot), else a ring. | | `permille` | `Option` | | Real progress in thousandths (a transfer with a known total). | | `started_at` / `startedAt` | `Option` | | Epoch ms the ring's estimate runs from (the earliest starting Space); none means from when the ring appears. | | `estimate_ms` / `estimateMs` | `u32` | | The estimate reaches about 80% after this long (`estimated_progress`). | ## `AppNotchButton` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | [`AppNotchButtonId`](#appnotchbuttonid-enum) | | What it does. | | `symbol` | `String` | | SF Symbol. | | `label` | `String` | | Accessibility label. | | `help` | `String` | | Tooltip. | ## `AppNotchHeader` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `query` | `String` | | The search text. | | `placeholder` | `String` | | "Search". | | `search_label` / `searchLabel` | `String` | | The field's spoken label. | | `match_count` / `matchCount` | `Option` | | How many Spaces match, while searching. | | `buttons` | [`Vec`](#appnotchbutton-record) | | Left to right: the list, then Settings at the far right. | ## `AppNotchLayout` record Returned by [`app_notch_layout`](#app_notch_layout). | Field | Type | Default | Description | | --- | --- | --- | --- | | `has_notch` / `hasNotch` | `bool` | | A real camera housing. | | `notch` | [`AppLogicalRect`](#applogicalrect-record) | | The notch (or the virtual one), AppKit coordinates. | | `closed_frame` / `closedFrame` | [`AppLogicalRect`](#applogicalrect-record) | | The closed panel's hit area: the notch grown for hover and drags. | | `open_frame` / `openFrame` | [`AppLogicalRect`](#applogicalrect-record) | | The open panel frame (tiles and prompt), top-centred. | | `prompt_frame` / `promptFrame` | [`AppLogicalRect`](#applogicalrect-record) | | The "Teleport to Cua" box alone, hanging below the notch. | | `tab_frame` / `tabFrame` | [`AppLogicalRect`](#applogicalrect-record) | | The "N Spaces" tab beside the closed notch (right of it), at its widest. | | `tab_inset_notch` / `tabInsetNotch` | `f64` | | Space between a closed tab's content (the "N Spaces" rows, the activity ring on the left) and the tab's edge toward the notch. | | `tab_inset_outer` / `tabInsetOuter` | `f64` | | Space between a closed tab's content and its outer edge (before the outer ear). | | `stage_frame` / `stageFrame` | [`AppLogicalRect`](#applogicalrect-record) | | The overlay window: every frame above plus `SHADOW_PADDING`, so the shape animates inside one window that never resizes mid-spring. Its transparent pixels pass clicks through. | | `notch_style` / `notchStyle` | `bool` | | Draw the black notch shape (true) or a floating glass capsule. | ## `AppNotchMotion` record Returned by [`app_notch_motion`](#app_notch_motion). | Field | Type | Default | Description | | --- | --- | --- | --- | | `hover_dwell_ms` / `hoverDwellMs` | `u32` | | Hover dwell before the panel opens. | | `close_delay_ms` / `closeDelayMs` | `u32` | | Delay before closing after the pointer leaves. | | `open_response` / `openResponse` | `f64` | | Opening spring response (s). | | `open_damping` / `openDamping` | `f64` | | Opening spring damping fraction. | | `close_response` / `closeResponse` | `f64` | | Closing spring response (s). | | `close_damping` / `closeDamping` | `f64` | | Closing spring damping fraction. | | `reduced_duration` / `reducedDuration` | `f64` | | Reduce Motion: opacity-only duration (s). | | `hover_response` / `hoverResponse` | `f64` | | Hover acknowledgement spring response (s): the closed notch grows a little while the dwell runs. | | `hover_damping` / `hoverDamping` | `f64` | | Hover acknowledgement damping fraction. | | `hover_scale` / `hoverScale` | `f64` | | Horizontal scale of the closed notch (and its tab) under the pointer. | | `hover_scale_y` / `hoverScaleY` | `f64` | | Vertical scale of the closed notch under the pointer (a few points taller, anchored at the top). | | `content_delay_ms` / `contentDelayMs` | `u32` | | The content starts fading in this long after the shape starts opening (ms). | | `content_in` / `contentIn` | `f64` | | Content fade-in duration (s). | | `content_out` / `contentOut` | `f64` | | Content fade-out duration on close (s); the shape waits for it. | | `content_scale` / `contentScale` | `f64` | | Content scale at the start of its fade-in (anchored at the top). | ## `AppNotchPermission` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `text` | `String` | | The line. | | `action` | `String` | | The button. | | `pane` | `String` | | Which settings pane the button opens (`accessibility`). | ## `AppNotchPreview` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `closed` | [`AppLogicalRect`](#applogicalrect-record) | | The closed notch with its ears, top centred. | | `open` | [`AppLogicalRect`](#applogicalrect-record) | | The open panel, top centred. | | `closed_radii` / `closedRadii` | [`AppNotchRadii`](#appnotchradii-record) | | Closed radii, scaled. | | `open_radii` / `openRadii` | [`AppNotchRadii`](#appnotchradii-record) | | Open radii, scaled. | | `tab` | [`AppLogicalRect`](#applogicalrect-record) | | The "N Spaces" tab right of the notch. | | `notch` | [`AppLogicalRect`](#applogicalrect-record) | | The camera housing (the closed notch without its ears). | | `view` | [`AppNotchView`](#appnotchview-record) | | The open panel's content at real size (the header row flanking the notch, then the tiles): the shells lay it out at `content_width` by `content_height` points and scale it by `scale` into `open`. | | `content_width` / `contentWidth` | `f64` | | The content's real width. | | `content_height` / `contentHeight` | `f64` | | The content's real height. | | `notch_height` / `notchHeight` | `f64` | | The real notch's height (the header row's height, real points). | | `side` | `f64` | | The content's side inset, real points (open top radius + padding). | | `scale` | `f64` | | Real points to stage points. | ## `AppNotchRadii` record Returned by [`app_notch_radii`](#app_notch_radii). | Field | Type | Default | Description | | --- | --- | --- | --- | | `top` | `f64` | | Top (concave ear) radius. | | `bottom` | `f64` | | Bottom radius. | ## `AppNotchState` record Returned by [`app_notch_initial`](#app_notch_initial). | Field | Type | Default | Description | | --- | --- | --- | --- | | `open` | `bool` | | Opened by hover or click. | | `hovering` | `bool` | | The pointer is over the panel. | | `drop_targeted` / `dropTargeted` | `bool` | | A file or app drag (pasteboard) is over the panel. | | `drag` | [`AppDragOverlayState`]() | | The window drag state machine. | | `drag_permission_missing` / `dragPermissionMissing` | `bool` | | Window-drag detection needs a permission this process lacks (macOS Accessibility): the open panel says so instead of failing silently. | | `query` | `String` | | The header's search text (cleared when the panel closes). | | `hotspot` | `bool` | | This machine shares its network to a Space (the hotspot). | | `transfer` | [`Option`](#appnotchtransfer-record) | | A teleport or file transfer is in flight. | | `keyvault` | `Option` | | Keyvault sign-ins are live in a Space right now (the core's `crate.keyvault.view.sharing_label`), so access is never silent. | | `signed_in` / `signedIn` | `Vec` | | The Spaces signed in through the Keyvault, less the copies the user dismissed (`crate.keyvault.view.signed_in_spaces`): their tiles carry the key. | | `hidden` | `bool` | | "Spaces tab in the notch: Hide" (menu bar only): nothing shows in the notch, and hover, clicks and window drags do nothing. | ## `AppNotchTab` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `count` | `String` | | "9". | | `word` | `String` | | "Spaces" (or "Space"). | ## `AppNotchTile` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Space id. | | `name` | `String` | | Name. | | `os` | [`AppSpaceOs`]() | | OS. | | `status` | [`AppSpaceStatus`]() | | Status. | | `dim` | `bool` | | Drawn at reduced opacity. | | `drop_target` / `dropTarget` | `bool` | | Accepts a teleport drop (reachable). | | `targeted` | `bool` | | Under the dragged window. | | `symbol` | `String` | | The Space's OS icon id (`os_icon`: `os-macos`, `os-windows`, `os-ubuntu`, ..., `os-linux`); the artwork is `os_icon_svg`, or on macOS the system symbol `os_icon_system_symbol` names. | | `label` | `String` | | Accessibility label: name, OS, where it runs and status. | | `location` | `String` | | Where it runs, in words, on the tile's header line next to the OS logo: "This Mac", the machine that provides it ("Mac mini"), the address of one added by address, or the cloud's place (`location`). | | `progress` | `Option` | | While it is being created: overall progress in thousandths (a ring over the tile). | | `progress_label` / `progressLabel` | `Option` | | While it is being created: the phase in words ("Starting…"), or "Failed"; while it is being deleted, "Deleting…". | | `signed_in` / `signedIn` | `bool` | | Signed in through the Keyvault (and not dismissed): the key badge. | ## `AppNotchTransfer` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `sent` | `Option` | | Bytes sent so far. | | `total` | `Option` | | Total bytes, when known. | ## `AppNotchTransition` record Returned by [`app_notch_reduce`](#app_notch_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `state` | [`AppNotchState`](#appnotchstate-record) | | New state. | | `effects` | [`Vec`](#appnotcheffect-enum) | | Effects to run. | ## `AppNotchView` record Returned by [`app_notch_view`](#app_notch_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `phase` | [`AppNotchPhase`](#appnotchphase-enum) | | What shows. | | `tiles` | [`Vec`](#appnotchtile-record) | | The tiles (in `tiles`). | | `drop_mode` / `dropMode` | `bool` | | Tiles accept drops. | | `prompt` | `Option` | | The prompt line. | | `label` | `String` | | Accessibility label of the panel. | | `count_label` / `countLabel` | `String` | | "N Spaces": the tab's spoken label (remote Spaces only). | | `tab` | [`AppNotchTab`](#appnotchtab-record) | | The tab's two rows: the count over the word. | | `header` | [`Option`](#appnotchheader-record) | | The row flanking the notch in the open panel: the search left of it, the buttons right of it. None while dropping. | | `empty` | `Option` | | The line in place of the tiles when the search matches nothing. | | `activity` | [`Option`](#appnotchactivity-record) | | The indicator left of the closed notch. | | `hidden` | `bool` | | The notch is hidden (menu bar only): draw nothing. | | `show_tab` / `showTab` | `bool` | | The tab shows (closed, no drag in flight; a drag tucks it away). | | `hover_cue` / `hoverCue` | `bool` | | The pointer rests on the closed notch while the dwell runs: the shells grow it by `NotchMotion.hover_scale`. | | `permission` | [`Option`](#appnotchpermission-record) | | One line in the open panel when window drags cannot be detected. | | `access` | [`Option`](#appnotchaccess-record) | | One line in the open panel while Keyvault sign-ins are live in a Space (and not dismissed): what is live, and Dismiss. | ## `AppNotchActivityKind` enum **Swift** ```swift .transfer .remoteAccess .hotspot .provisioning .deleting .keyvault ``` **Rust** ```rust AppNotchActivityKind::Transfer AppNotchActivityKind::RemoteAccess AppNotchActivityKind::Hotspot AppNotchActivityKind::Provisioning AppNotchActivityKind::Deleting AppNotchActivityKind::Keyvault ``` | Variant | Description | | --- | --- | | `Transfer` | A teleport or file transfer: a progress ring. | | `RemoteAccess` | Someone is connected to this machine (host sharing): its symbol, pulsing, for as long as they are. | | `Hotspot` | The network hotspot: its symbol, pulsing. | | `Provisioning` | A Space starting: a progress ring on the startup estimate. | | `Deleting` | A Space being deleted: a ring on the delete estimate. | | `Keyvault` | Keyvault sign-ins live in a Space: the key symbol, pulsing. | ## `AppNotchButtonId` enum **Swift** ```swift .list .settings ``` **Rust** ```rust AppNotchButtonId::List AppNotchButtonId::Settings ``` | Variant | Description | | --- | --- | | `List` | Opens the main window (the Spaces list). | | `Settings` | Opens Settings. | ## `AppNotchEffect` enum **Swift** ```swift .startDwell(ms: UInt32) .startClose(ms: UInt32) .cancelTimers .drag(effect: AppDragOverlayEffect) ``` **Rust** ```rust AppNotchEffect::StartDwell { ms: u32 } AppNotchEffect::StartClose { ms: u32 } AppNotchEffect::CancelTimers AppNotchEffect::Drag { effect: AppDragOverlayEffect } ``` | Variant | Description | | --- | --- | | `StartDwell` | Start (or restart) the dwell timer. Fields: `ms: u32` | | `StartClose` | Start the close timer. Fields: `ms: u32` | | `CancelTimers` | Cancel pending timers. | | `Drag` | A window drag effect (capture the ghost, commit the teleport). Fields: `effect: AppDragOverlayEffect` | ## `AppNotchEvent` enum **Swift** ```swift .hoverEnter .hoverExit .dwellElapsed .closeElapsed .click .dismiss .dropTargeted(targeted: Bool) .drag(event: AppDragOverlayEvent) .dragPermission(granted: Bool) .search(query: String) .escape .visibility(shown: Bool) .activity(hotspot: Bool, transfer: AppNotchTransfer?) .keyvault(label: String?, signedIn: [String]) ``` **Rust** ```rust AppNotchEvent::HoverEnter AppNotchEvent::HoverExit AppNotchEvent::DwellElapsed AppNotchEvent::CloseElapsed AppNotchEvent::Click AppNotchEvent::Dismiss AppNotchEvent::DropTargeted { targeted: bool } AppNotchEvent::Drag { event: AppDragOverlayEvent } AppNotchEvent::DragPermission { granted: bool } AppNotchEvent::Search { query: String } AppNotchEvent::Escape AppNotchEvent::Visibility { shown: bool } AppNotchEvent::Activity { hotspot: bool, transfer: Option } AppNotchEvent::Keyvault { label: Option, signed_in: Vec } ``` | Variant | Description | | --- | --- | | `HoverEnter` | Pointer entered. | | `HoverExit` | Pointer left. | | `DwellElapsed` | The dwell timer the core asked for fired. | | `CloseElapsed` | The close timer the core asked for fired. | | `Click` | Click on the closed notch. | | `Dismiss` | Esc or a click outside. | | `DropTargeted` | A pasteboard drag entered (true) or left (false) the panel. Fields: `targeted: bool` | | `Drag` | A window drag event. Fields: `event: AppDragOverlayEvent` | | `DragPermission` | The shell checked the window-drag permission. Fields: `granted: bool` | | `Search` | The header's search text changed. Fields: `query: String` | | `Escape` | Esc: clears the search first, then closes. | | `Visibility` | The notch setting changed: shown, or hidden (menu bar only). Fields: `shown: bool` | | `Activity` | The hotspot or a transfer started, progressed or ended. Fields: `hotspot: bool`, `transfer: Option` | | `Keyvault` | Keyvault sharing started, changed or stopped (none: nothing live). Fields: `label: Option`, `signed_in: Vec` | ## `AppNotchPhase` enum **Swift** ```swift .closed .tiles .prompt ``` **Rust** ```rust AppNotchPhase::Closed AppNotchPhase::Tiles AppNotchPhase::Prompt ``` | Variant | Description | | --- | --- | | `Closed` | Just the notch. | | `Tiles` | The Space tiles. | | `Prompt` | The Teleport prompt ("Teleport to Cua"). | ## `app_notch_estimated_progress` The activity ring's fill without real progress, in thousandths. **Swift** ```swift func appNotchEstimatedProgress(elapsedMs: Int64, estimateMs: UInt32) -> UInt32 ``` **Rust** ```rust pub fn app_notch_estimated_progress(elapsed_ms: i64, estimate_ms: u32) -> u32 ``` | Parameter | Type | Default | | --- | --- | --- | | `elapsed_ms` | `i64` | required | | `estimate_ms` | `u32` | required | **Returns** `u32` ## `app_notch_initial` The notch panel's first state. **Swift** ```swift func appNotchInitial() -> AppNotchState ``` **Rust** ```rust pub fn app_notch_initial() -> AppNotchState ``` **Returns** [`AppNotchState`](#appnotchstate-record) ## `app_notch_layout` The notch rectangle and panel frames for a screen. **Swift** ```swift func appNotchLayout(screen: AppScreenFacts, prompt: Bool) -> AppNotchLayout ``` **Rust** ```rust pub fn app_notch_layout(screen: AppScreenFacts, prompt: bool) -> AppNotchLayout ``` | Parameter | Type | Default | | --- | --- | --- | | `screen` | [`AppScreenFacts`](#appscreenfacts-record) | required | | `prompt` | `bool` | required | **Returns** [`AppNotchLayout`](#appnotchlayout-record) ## `app_notch_motion` The springs and delays every shell animates with. **Swift** ```swift func appNotchMotion() -> AppNotchMotion ``` **Rust** ```rust pub fn app_notch_motion() -> AppNotchMotion ``` **Returns** [`AppNotchMotion`](#appnotchmotion-record) ## `app_notch_radii` Closed and open corner radii. **Swift** ```swift func appNotchRadii() -> [AppNotchRadii] ``` **Rust** ```rust pub fn app_notch_radii() -> Vec ``` **Returns** [`Vec`](#appnotchradii-record) ## `app_notch_reduce` Advances the notch panel. **Swift** ```swift func appNotchReduce(state: AppNotchState, event: AppNotchEvent) -> AppNotchTransition ``` **Rust** ```rust pub fn app_notch_reduce(state: AppNotchState, event: AppNotchEvent) -> AppNotchTransition ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppNotchState`](#appnotchstate-record) | required | | `event` | [`AppNotchEvent`](#appnotchevent-enum) | required | **Returns** [`AppNotchTransition`](#appnotchtransition-record) ## `app_notch_tile_at` The drop-target tile under a point (AppKit coordinates) in the open panel; `row` when the line above the tiles shows. **Swift** ```swift func appNotchTileAt(layout: AppNotchLayout, tiles: [AppNotchTile], row: Bool, x: Double, y: Double) -> String? ``` **Rust** ```rust pub fn app_notch_tile_at(layout: AppNotchLayout, tiles: Vec, row: bool, x: f64, y: f64) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `layout` | [`AppNotchLayout`](#appnotchlayout-record) | required | | `tiles` | [`Vec`](#appnotchtile-record) | required | | `row` | `bool` | required | | `x` | `f64` | required | | `y` | `f64` | required | **Returns** `Option` ## `app_notch_view` The notch panel as drawn. **Swift** ```swift func appNotchView(state: AppNotchState, spaces: [AppSpace]) -> AppNotchView ``` **Rust** ```rust pub fn app_notch_view(state: AppNotchState, spaces: Vec) -> AppNotchView ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppNotchState`](#appnotchstate-record) | required | | `spaces` | [`Vec`]() | required | **Returns** [`AppNotchView`](#appnotchview-record) --- # Keyvault The Keyvault broker client and its overview: items, pending requests, grants, rules and audit. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `KeyvaultClient` talks to the Keyvault broker and returns a `KeyvaultOverview`. The screens derived from it are on [Keyvault screens](). ## `AppSensitiveOption` record Returned by [`app_picker_sensitive_options`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `group` | [`AppSensitiveGroup`](#appsensitivegroup-enum) | | The group. | | `label` | `String` | | Label. | | `detail` | `String` | | The line under the label. | | `checked` | `bool` | | Checked. | ## `AppSensitiveGroup` enum **Swift** ```swift .signIns .passwords .history ``` **Rust** ```rust AppSensitiveGroup::SignIns AppSensitiveGroup::Passwords AppSensitiveGroup::History ``` | Variant | Description | | --- | --- | | `SignIns` | The session cookies: what keeps the app signed in. | | `Passwords` | Saved passwords. | | `History` | Browsing history. | ## `app_sensitive_group_sdk` The SDK's plan-option group for a picker group. **Swift** ```swift func appSensitiveGroupSdk(group: AppSensitiveGroup) -> TeleportSensitiveGroup ``` **Rust** ```rust pub fn app_sensitive_group_sdk(group: AppSensitiveGroup) -> TeleportSensitiveGroup ``` | Parameter | Type | Default | | --- | --- | --- | | `group` | [`AppSensitiveGroup`](#appsensitivegroup-enum) | required | **Returns** [`TeleportSensitiveGroup`]() ## `KeyvaultClient` The Keyvault broker client: the verified connection to the broker `cua daemon` hosts on `$CUA_HOME/keyvault.sock`. The broker checks this process's code signature; approvals, re-enabling and unattended-on make the daemon ask for Touch ID. No call returns a secret value. | Method | Description | | --- | --- | | [`new`](#keyvaultclientnew) | The broker in `cua_home` (`None`: `$CUA_HOME`, else `~/.cua`). | | [`execute`](#keyvaultclientexecute) | Runs one page action (a broker request). | | [`favicons`](#keyvaultclientfavicons) | Site icons the vault holds (not secret; empty while names are hidden). | | [`inventory`](#keyvaultclientinventory) | What `app` holds, per domain with counts (the daemon asks for Touch ID when the browse window is closed). | | [`lock`](#keyvaultclientlock) | Locks the vault. | | [`overview`](#keyvaultclientoverview) | Everything the page shows. | | [`setup_with_passphrase`](#keyvaultclientsetup_with_passphrase) | Creates the vault with a passphrase (and a recovery key, returned once). | | [`unlock_with_passphrase`](#keyvaultclientunlock_with_passphrase) | Unlocks with the passphrase (sent only to the broker; zeroized here). | ### `KeyvaultClient.new` The broker in `cua_home` (`None`: `$CUA_HOME`, else `~/.cua`). **Swift** ```swift init(cuaHome: String?) ``` **Rust** ```rust pub fn new(cua_home: Option) -> Arc ``` | Parameter | Type | Default | | --- | --- | --- | | `cua_home` | `Option` | required | **Returns** [`KeyvaultClient`](#keyvaultclient) ### `KeyvaultClient.execute` Runs one page action (a broker request). **Swift** ```swift func execute(command: KvCommand) async throws -> KvOutcome ``` **Rust** ```rust pub async fn execute(&self, command: KvCommand) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `command` | [`KvCommand`](#kvcommand-enum) | required | **Returns** [`KvOutcome`](#kvoutcome-enum) · **Async** · **Raises** [`CuaError`]() ### `KeyvaultClient.favicons` Site icons the vault holds (not secret; empty while names are hidden). **Swift** ```swift func favicons() async -> [KvFavicon] ``` **Rust** ```rust pub async fn favicons(&self) -> Vec ``` **Returns** [`Vec`](#kvfavicon-record) · **Async** ### `KeyvaultClient.inventory` What `app` holds, per domain with counts (the daemon asks for Touch ID when the browse window is closed). Never a value. **Swift** ```swift func inventory(app: String, profile: String?) async throws -> KvInventory ``` **Rust** ```rust pub async fn inventory(&self, app: String, profile: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `app` | `String` | required | | `profile` | `Option` | required | **Returns** [`KvInventory`](#kvinventory-record) · **Async** · **Raises** [`CuaError`]() ### `KeyvaultClient.lock` Locks the vault. **Swift** ```swift func lock() async throws ``` **Rust** ```rust pub async fn lock(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `KeyvaultClient.overview` Everything the page shows. Never fails: an unavailable Keyvault is a state (`availability`). **Swift** ```swift func overview() async -> KeyvaultOverview ``` **Rust** ```rust pub async fn overview(&self) -> KeyvaultOverview ``` **Returns** [`KeyvaultOverview`](#keyvaultoverview-record) · **Async** ### `KeyvaultClient.setup_with_passphrase` Creates the vault with a passphrase (and a recovery key, returned once). The passphrase goes only to the broker over the verified Keyvault socket; it is never logged or kept, and zeroized here. **Swift** ```swift func setupWithPassphrase(passphrase: String) async throws -> String? ``` **Rust** ```rust pub async fn setup_with_passphrase(&self, passphrase: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `passphrase` | `String` | required | **Returns** `Option` · **Async** · **Raises** [`CuaError`]() ### `KeyvaultClient.unlock_with_passphrase` Unlocks with the passphrase (sent only to the broker; zeroized here). **Swift** ```swift func unlockWithPassphrase(passphrase: String) async throws ``` **Rust** ```rust pub async fn unlock_with_passphrase(&self, passphrase: String) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `passphrase` | `String` | required | **Async** · **Raises** [`CuaError`]() ## `KeyvaultOverview` record Returned by [`KeyvaultClient.overview`](#keyvaultclientoverview), [`kv_overview_from_json`](#kv_overview_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `availability` | `String` | | `ready`, or why not: `not_running`, `impostor`, `connect`, `no_vault`, `locked`, `not_first_party`, `unsupported`, `error`. | | `message` | `Option` | | A sentence when not ready. | | `status` | [`Option`](#kvstatus-record) | | The broker's status. | | `server_verified` / `serverVerified` | `bool` | | The daemon was checked against Cua's signature. | | `items` | [`Vec`](#kvitem-record) | | The vault's secret items, in order (app, domain, key). | | `names_visible` / `namesVisible` | `bool` | | Domains and keys are present in `items` (the browse window is open). | | `items_total` / `itemsTotal` | `u32` | | Items in the vault. | | `pending` | [`Vec`](#kvpending-record) | | Pending requests. | | `grants` | [`Vec`](#kvgrant-record) | | Grants. | | `rules` | [`Vec`](#kvrule-record) | | Rules. | | `deliveries` | [`Vec`](#kvdelivery-record) | | Deliveries. | | `audit` | [`Vec`](#kvauditentry-record) | | Audit tail, oldest first. | | `audit_verification` / `auditVerification` | [`Option`](#kvverification-record) | | Chain check. | | `partial_errors` / `partialErrors` | `Vec` | | Sections that failed while the rest loaded. | ## `KvAccessRequest` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `selectors` | [`Vec`](#kvselector-enum) | | What. | | `targets` | `Vec` | | Where. | | `actions` | `Vec` | | Actions. | | `duration_secs` / `durationSecs` | `Option` | | For how long. | | `uses` | `Option` | | Uses. | | `reason` | `String` | | The caller's reason (unverified). | | `claimed_name` / `claimedName` | `Option` | | The caller's name for itself (unverified). | | `agent` | `Option` | | The named agent the request is for (unverified). | ## `KvAuditEntry` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `seq` | `u64` | | Sequence. | | `ts_ms` / `tsMs` | `u64` | | Time. | | `kind` | `String` | | Kind (`consent.allow`, ...). | | `actor` | `String` | | Actor. | | `caller_fp` / `callerFp` | `String` | | Caller fingerprint. | | `item` | `Option` | | Item. | | `target` | `Option` | | Target. | | `decision` | `String` | | `ok`, `allow`, `deny`, `error`. | | `detail` | `Option` | | Detail. | | `authless` | `Option` | | Written without the MAC key. | ## `KvCaller` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `i32` | | Pid. | | `uid` | `u32` | | Uid. | | `path` | `Option` | | Executable. | | `signing` | [`KvSigning`](#kvsigning-enum) | | Signing. | | `first_party` / `firstParty` | `bool` | | Satisfied the Cua requirement. | | `os_verified` / `osVerified` | `bool` | | The OS verified the signature. | | `launched_by` / `launchedBy` | `Option` | | Launched by. | | `verified_name` / `verifiedName` | `Option` | | OS-verified leaf name. | ## `KvCredentialForm` record Returned by [`kv_credential_form`](#kv_credential_form). | Field | Type | Default | Description | | --- | --- | --- | --- | | `mode` | [`KvFormMode`](#kvformmode-enum) | | Setup or unlock. | | `method` | [`KvMethod`](#kvmethod-enum) | | Touch ID or a passphrase. | | `help` | `String` | | One line under the form. | | `passphrase_label` / `passphraseLabel` | `Option` | | The passphrase field's label (passphrase only). | | `confirm_label` / `confirmLabel` | `Option` | | The second field's label (passphrase setup only). | | `submit_label` / `submitLabel` | `String` | | The button. | ## `KvDelivery` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `import_id` / `importId` | `String` | | Import id. | | `target` | `String` | | Target. | | `provider_id` / `providerId` | `String` | | Provider. | | `items` | `Vec` | | Items. | | `caller_fp` / `callerFp` | `String` | | Caller fingerprint. | | `delivered_ms` / `deliveredMs` | `u64` | | Delivered. | | `expires_ms` / `expiresMs` | `u64` | | Wiped at (`KV_NO_EXPIRY`: only when wiped). | | `wiped` | `bool` | | Already wiped. | ## `KvDomainCount` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `domain` | `String` | | Registrable domain. | | `cookies` | `u32` | | Cookies. | | `session_cookies` / `sessionCookies` | `u32` | | Cookies without an expiry. | | `local_storage` / `localStorage` | `u32` | | localStorage values. | | `passwords` | `u32` | | Saved passwords. | | `signin` | `bool` | | Looks like it keeps a sign-in. | | `identity_provider` / `identityProvider` | `bool` | | An identity provider. | | `unavailable` | `u32` | | Cookies that cannot be read (Chrome's app-bound encryption). | | `unavailable_reason` / `unavailableReason` | `String` | | Why they cannot be read. | ## `KvFavicon` record Returned by [`KeyvaultClient.favicons`](#keyvaultclientfavicons). | Field | Type | Default | Description | | --- | --- | --- | --- | | `site` | `String` | | The site (registrable domain). | | `png` | `String` | | PNG, base64. | ## `KvGrant` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Id. | | `request_id` / `requestId` | `String` | | Request. | | `caller_fp` / `callerFp` | `String` | | Caller fingerprint. | | `caller_display` / `callerDisplay` | `String` | | Caller. | | `items` | `Vec` | | Items. | | `targets` | `Vec` | | Targets. | | `actions` | `Vec` | | Actions. | | `created_ms` / `createdMs` | `u64` | | Created. | | `not_after_ms` / `notAfterMs` | `u64` | | Expires. | | `uses_left` / `usesLeft` | `Option` | | Uses left (none: unlimited until expiry). | | `revoked` | `bool` | | Revoked. | | `agent` | `Option` | | The named agent it was approved for. | ## `KvInventory` record Returned by [`KeyvaultClient.inventory`](#keyvaultclientinventory). | Field | Type | Default | Description | | --- | --- | --- | --- | | `provider_id` / `providerId` | `String` | | Provider id. | | `app_display` / `appDisplay` | `String` | | App name. | | `domains` | [`Vec`](#kvdomaincount-record) | | Domains with counts. | | `notes` | `Vec` | | Notes. | ## `KvItem` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Id. | | `kind` | `String` | | `cookie`, `local_storage`, `password` or `file`. | | `provider_id` / `providerId` | `String` | | The source app (`chrome`, `slack`). | | `app_display` / `appDisplay` | `String` | | The app's name. | | `domain` | `Option` | | A cookie's host, a storage value's or password's origin; none for a file or when the names are hidden. | | `key` | `String` | | A cookie name, storage key, username or file path; empty when the names are hidden. | | `path` | `Option` | | A cookie's path. | | `source` | `String` | | Profile. | | `session` | `bool` | | A cookie without an expiry. | | `expires_ms` / `expiresMs` | `Option` | | A cookie's expiry, Unix ms. | | `bytes` | `u64` | | Bytes. | | `blob` | `Option` | | The blob a big file's bytes live in. | | `identity_provider` / `identityProvider` | `bool` | | Identity providers always ask: they cannot be unlocked. | | `policy` | [`KvItemPolicy`](#kvitempolicy-record) | | Policy: `unattended` is the unlock. | | `created_ms` / `createdMs` | `u64` | | Created. | | `updated_ms` / `updatedMs` | `u64` | | Last saved. | | `rev` | `u64` | | Revision. | | `record_digest` / `recordDigest` | `String` | | Digest. | ## `KvItemPolicy` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `allowed_targets` / `allowedTargets` | `Vec` | | Allowed targets (empty: any). | | `ttl_secs` / `ttlSecs` | `u64` | | Delivery lifetime. | | `unattended` | `bool` | | Unattended rules may use it. | ## `KvLabels` record Returned by [`kv_labels`](#kv_labels). | Field | Type | Default | Description | | --- | --- | --- | --- | | `deny` | `String` | | "Deny". | | `review` | `String` | | "Review…" (opens the approval sheet). | | `cancel` | `String` | | "Cancel". | | `set_up` / `setUp` | `String` | | "Set up Keyvault". | | `unlock` | `String` | | "Unlock". | | `revoke_all` / `revokeAll` | `String` | | "Revoke all". | | `confirm_note` / `confirmNote` | `String` | | Under the approval sheet. | | `protection_title` / `protectionTitle` | `String` | | "Protection". | ## `KvPassphraseCheck` record Returned by [`kv_passphrase_check`](#kv_passphrase_check). | Field | Type | Default | Description | | --- | --- | --- | --- | | `can_submit` / `canSubmit` | `bool` | | The button is enabled. | | `strength` | [`Option`](#kvstrength-enum) | | Setup only, once something is typed. | | `hint` | `Option` | | One short line under the fields. | ## `KvPending` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Id. | | `caller` | [`KvCaller`](#kvcaller-record) | | Verified caller. | | `caller_fp` / `callerFp` | `String` | | Fingerprint. | | `caller_display` / `callerDisplay` | `String` | | One-line verified identity. | | `request` | [`KvAccessRequest`](#kvaccessrequest-record) | | The request. | | `items` | [`Vec`](#kvitem-record) | | Existing items it resolves to. | | `needs_import` / `needsImport` | [`Vec`](#kvselector-enum) | | Selectors approval would import. | | `created_ms` / `createdMs` | `u64` | | Created. | ## `KvRule` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Id. | | `items` | `Vec` | | Items. | | `targets` | `Vec` | | Targets (`*`: any Space). | | `callers` | [`Vec`](#kvrulecaller-record) | | Callers. | | `created_ms` / `createdMs` | `u64` | | Created. | | `not_after_ms` / `notAfterMs` | `u64` | | Expires. | | `enabled` | `bool` | | Enabled. | | `note` | `String` | | Note. | ## `KvRuleCaller` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `fp` | `String` | | Fingerprint. | | `display` | `String` | | Display. | ## `KvStatus` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `version` | `String` | | Version. | | `initialized` | `bool` | | A vault exists. | | `unlocked` | `bool` | | Unlocked. | | `disabled` | `bool` | | The kill switch is on. | | `caller_first_party` / `callerFirstParty` | `bool` | | This app is first party. | | `caller_display` / `callerDisplay` | `String` | | How the broker sees this app. | | `items` | `u32` | | Items. | | `pending` | `u32` | | Pending requests. | | `unlock_policy` / `unlockPolicy` | `Option` | | `auto` or `presence`. | | `auto_wipe` / `autoWipe` | `Option` | | Delivered copies wipe themselves after their TTL (off by default; none: not told, a broker before the setting or a third party). | | `os_protector_available` / `osProtectorAvailable` | `bool` | | The daemon can create the OS key store protector (setup offers Touch ID); false for a development daemon, which is passphrase-only. | | `passphrase_available` / `passphraseAvailable` | `bool` | | A passphrase can always be chosen. | | `unlock_protectors` / `unlockProtectors` | `Vec` | | Protector kinds that can unlock this vault now (`macos-keychain`, `windows-credential`, `passphrase`, `recovery`). | | `browse_until_ms` / `browseUntilMs` | `Option` | | The browse window is open until this time, Unix ms: item names are visible until then. | | `skip_unlock_prompt` / `skipUnlockPrompt` | `Option` | | "Never ask again" on the unlock prompt is on. | | `reset_notice` / `resetNotice` | `Option` | | An earlier preview's vault was found and set aside. | ## `KvVerification` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `ok` | `bool` | | Intact. | | `entries` | `u64` | | Entries. | | `unauthenticated` | `u64` | | Entries without a MAC. | | `tampered_line` / `tamperedLine` | `Option` | | First tampered line. | | `reason` | `Option` | | Why. | ## `KvCommand` enum **Swift** ```swift .setup .unlock .setDisabled(disabled: Bool) .setUnattended(itemIds: [String], unattended: Bool) .setLocked(itemIds: [String], locked: Bool) .deleteItems(itemIds: [String]) .setSkipUnlockPrompt(on: Bool) .browse .endBrowse .revokeGrant(id: String) .removeRule(id: String) .setAutoWipe(on: Bool) .release(target: String) .approve(requestId: String, items: [String]?) .deny(requestId: String) ``` **Rust** ```rust KvCommand::Setup KvCommand::Unlock KvCommand::SetDisabled { disabled: bool } KvCommand::SetUnattended { item_ids: Vec, unattended: bool } KvCommand::SetLocked { item_ids: Vec, locked: bool } KvCommand::DeleteItems { item_ids: Vec } KvCommand::SetSkipUnlockPrompt { on: bool } KvCommand::Browse KvCommand::EndBrowse KvCommand::RevokeGrant { id: String } KvCommand::RemoveRule { id: String } KvCommand::SetAutoWipe { on: bool } KvCommand::Release { target: String } KvCommand::Approve { request_id: String, items: Option> } KvCommand::Deny { request_id: String } ``` | Variant | Description | | --- | --- | | `Setup` | Create the vault with the OS key store (the daemon asks for presence). A passphrase never travels in a command: the shells pass it to `KeyvaultCommands::setup_with_passphrase` directly. | | `Unlock` | Unlock with the OS key store (a passphrase goes to `KeyvaultCommands::unlock_with_passphrase`). | | `SetDisabled` | The kill switch (turning it off asks for Touch ID). Fields: `disabled: bool` | | `SetUnattended` | Per-item unattended (turning on asks for Touch ID): the same as `KvCommand.SetLocked` with `locked` the other way round. Fields: `item_ids: Vec`, `unattended: bool` | | `SetLocked` | Lock or unlock items together. Unlocking allows unattended access and asks for Touch ID once for the whole batch; locking does not. Fields: `item_ids: Vec`, `locked: bool` | | `DeleteItems` | Delete items, wiping every live copy of them in Spaces. Fields: `item_ids: Vec` | | `SetSkipUnlockPrompt` | "Never ask again" on the unlock prompt (Settings turns it back on). Fields: `on: bool` | | `Browse` | Show item names for a few minutes (the daemon asks for Touch ID). | | `EndBrowse` | Hide item names again. | | `RevokeGrant` | Revoke a grant (`*`: all). Fields: `id: String` | | `RemoveRule` | Remove a rule. Fields: `id: String` | | `SetAutoWipe` | Auto-wipe of delivered copies (turning it off asks for Touch ID). Fields: `on: bool` | | `Release` | Wipe a Space's copies. Fields: `target: String` | | `Approve` | Approve a request for exactly these items (the daemon asks for presence). `None` keeps everything the request asked for, which the approval sheet only sends when the user ticked every row. Fields: `request_id: String`, `items: Option>` | | `Deny` | Deny a request. Fields: `request_id: String` | ## `KvFormMode` enum **Swift** ```swift .setup .unlock ``` **Rust** ```rust KvFormMode::Setup KvFormMode::Unlock ``` | Variant | Description | | --- | --- | | `Setup` | No vault yet. | | `Unlock` | The vault is locked. | ## `KvMethod` enum **Swift** ```swift .touchId .passphrase ``` **Rust** ```rust KvMethod::TouchId KvMethod::Passphrase ``` | Variant | Description | | --- | --- | | `TouchId` | The OS key store (the login keychain), confirmed with Touch ID. | | `Passphrase` | A passphrase. | ## `KvOutcome` enum **Swift** ```swift .done .recoveryKey(key: String?) .locked(changed: [String], skipped: [String]) .deleted(count: UInt32, wiped: [String]) .browsing(untilMs: UInt64) .revoked(count: UInt32) .wiped(imports: [String]) .granted(grant: KvGrant) ``` **Rust** ```rust KvOutcome::Done KvOutcome::RecoveryKey { key: Option } KvOutcome::Locked { changed: Vec, skipped: Vec } KvOutcome::Deleted { count: u32, wiped: Vec } KvOutcome::Browsing { until_ms: u64 } KvOutcome::Revoked { count: u32 } KvOutcome::Wiped { imports: Vec } KvOutcome::Granted { grant: KvGrant } ``` | Variant | Description | | --- | --- | | `Done` | Nothing to show. | | `RecoveryKey` | The recovery key, shown once and never stored. Fields: `key: Option` | | `Locked` | Items locked or unlocked. Identity providers always ask: they are in `skipped`, never changed. Fields: `changed: Vec`, `skipped: Vec` | | `Deleted` | Items deleted. Fields: `count: u32`, `wiped: Vec` | | `Browsing` | The browse window is open until then, Unix ms. Fields: `until_ms: u64` | | `Revoked` | Grants revoked. Fields: `count: u32` | | `Wiped` | Imports wiped. Fields: `imports: Vec` | | `Granted` | The grant an approval minted. Fields: `grant: KvGrant` | ## `KvSelector` enum **Swift** ```swift .item(id: String) .site(app: String, site: String) .app(app: String) .login(site: String) ``` **Rust** ```rust KvSelector::Item { id: String } KvSelector::Site { app: String, site: String } KvSelector::App { app: String } KvSelector::Login { site: String } ``` | Variant | Description | | --- | --- | | `Item` | An existing item. Fields: `id: String` | | `Site` | A site of an app. Fields: `app: String`, `site: String` | | `App` | A whole app. Fields: `app: String` | | `Login` | A site's saved password, used to sign in (never delivered). Fields: `site: String` | ## `KvSigning` enum **Swift** ```swift .signed(teamId: String, identifier: String, cdhash: String) .adHoc(identifier: String, cdhash: String) .unsigned .unknown ``` **Rust** ```rust KvSigning::Signed { team_id: String, identifier: String, cdhash: String } KvSigning::AdHoc { identifier: String, cdhash: String } KvSigning::Unsigned KvSigning::Unknown ``` | Variant | Description | | --- | --- | | `Signed` | Team-signed. Fields: `team_id: String`, `identifier: String`, `cdhash: String` | | `AdHoc` | Ad hoc. Fields: `identifier: String`, `cdhash: String` | | `Unsigned` | Unsigned. | | `Unknown` | Unknown platform. | ## `KvStrength` enum **Swift** ```swift .weak .fair .strong ``` **Rust** ```rust KvStrength::Weak KvStrength::Fair KvStrength::Strong ``` | Variant | Description | | --- | --- | | `Weak` | Too short, or easy to guess. | | `Fair` | Acceptable. | | `Strong` | Long and varied. | ## `kv_credential_form` The setup or unlock form for an overview (also `KvPage.form`): Touch ID when the daemon can use the OS key store, else a passphrase. **Swift** ```swift func kvCredentialForm(overview: KeyvaultOverview) -> KvCredentialForm? ``` **Rust** ```rust pub fn kv_credential_form(overview: KeyvaultOverview) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`](#keyvaultoverview-record) | required | **Returns** [`Option`](#kvcredentialform-record) ## `kv_duration` A duration in words ("5 min"). **Swift** ```swift func kvDuration(ms: Int64) -> String ``` **Rust** ```rust pub fn kv_duration(ms: i64) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `ms` | `i64` | required | **Returns** `String` ## `kv_labels` The Keyvault's fixed words. **Swift** ```swift func kvLabels() -> KvLabels ``` **Rust** ```rust pub fn kv_labels() -> KvLabels ``` **Returns** [`KvLabels`](#kvlabels-record) ## `kv_overview_from_json` A Keyvault overview from JSON (fixtures, parity flows). Fixture data only: it carries metadata, never a secret value. **Swift** ```swift func kvOverviewFromJson(json: String) throws -> KeyvaultOverview ``` **Rust** ```rust pub fn kv_overview_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`KeyvaultOverview`](#keyvaultoverview-record) · **Raises** [`CuaError`]() ## `kv_passphrase_check` The passphrase fields' hint and whether the form can be sent. Pure: the passphrase is only measured, never kept. **Swift** ```swift func kvPassphraseCheck(mode: KvFormMode, passphrase: String, confirm: String) -> KvPassphraseCheck ``` **Rust** ```rust pub fn kv_passphrase_check(mode: KvFormMode, passphrase: String, confirm: String) -> KvPassphraseCheck ``` | Parameter | Type | Default | | --- | --- | --- | | `mode` | [`KvFormMode`](#kvformmode-enum) | required | | `passphrase` | `String` | required | | `confirm` | `String` | required | **Returns** [`KvPassphraseCheck`](#kvpassphrasecheck-record) ## `kv_prune_dismissed` The dismissed copies still live. **Swift** ```swift func kvPruneDismissed(overview: KeyvaultOverview, nowMs: Int64, dismissed: [String]) -> [String] ``` **Rust** ```rust pub fn kv_prune_dismissed(overview: KeyvaultOverview, now_ms: i64, dismissed: Vec) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`](#keyvaultoverview-record) | required | | `now_ms` | `i64` | required | | `dismissed` | `Vec` | required | **Returns** `Vec` ## `kv_recovery_key_text` The banner after setup: the recovery key, shown once. **Swift** ```swift func kvRecoveryKeyText(key: String) -> String ``` **Rust** ```rust pub fn kv_recovery_key_text(key: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `key` | `String` | required | **Returns** `String` ## `kv_sharing_label` The always-visible signal while Keyvault sign-ins are live in a Space (the notch indicator and the menu bar line); none when nothing is live. **Swift** ```swift func kvSharingLabel(overview: KeyvaultOverview, nowMs: Int64) -> String? ``` **Rust** ```rust pub fn kv_sharing_label(overview: KeyvaultOverview, now_ms: i64) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`](#keyvaultoverview-record) | required | | `now_ms` | `i64` | required | **Returns** `Option` ## `kv_signed_in_spaces` The ids of `spaces` signed in through the Keyvault (a live copy is in them), less the `dismissed` copies: "Signed in" in the Spaces list (nothing dismissed), the key on notch tiles. **Swift** ```swift func kvSignedInSpaces(overview: KeyvaultOverview, nowMs: Int64, dismissed: [String], spaces: [AppSpace]) -> [String] ``` **Rust** ```rust pub fn kv_signed_in_spaces(overview: KeyvaultOverview, now_ms: i64, dismissed: Vec, spaces: Vec) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`](#keyvaultoverview-record) | required | | `now_ms` | `i64` | required | | `dismissed` | `Vec` | required | | `spaces` | [`Vec`]() | required | **Returns** `Vec` ## `kv_space_access_key` The Access row of `space`'s copies, to focus from its "Signed in" badge. **Swift** ```swift func kvSpaceAccessKey(overview: KeyvaultOverview, nowMs: Int64, space: AppSpace) -> String? ``` **Rust** ```rust pub fn kv_space_access_key(overview: KeyvaultOverview, now_ms: i64, space: AppSpace) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`](#keyvaultoverview-record) | required | | `now_ms` | `i64` | required | | `space` | [`AppSpace`]() | required | **Returns** `Option` ## `kv_visible_sharing_label` `kv_sharing_label` without the copies the user dismissed (import ids): the notch indicator. **Swift** ```swift func kvVisibleSharingLabel(overview: KeyvaultOverview, nowMs: Int64, dismissed: [String]) -> String? ``` **Rust** ```rust pub fn kv_visible_sharing_label(overview: KeyvaultOverview, now_ms: i64, dismissed: Vec) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`](#keyvaultoverview-record) | required | | `now_ms` | `i64` | required | | `dismissed` | `Vec` | required | **Returns** `Option` --- # Keyvault screens The Keyvault page, sidebar, the vault list (apps, sites and items with their locks), the unlock prompt and the approval prompt. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The `kv_*` functions derive each Keyvault screen from a `KeyvaultOverview`. `kv_vault_view` groups the vault by source app and site, with a lock on each row; `kv_vault_reduce` applies search, selection and open groups; `kv_unlock_prompt` is the "Allow unattended access?" prompt. `kv_approval_open`, `kv_approval_reduce` and `kv_approval_view` drive the approval prompt; the `*_command` functions return the `KvCommand` to send. ## `KvAccessRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`KvAccessKind`](#kvaccesskind-enum) | | Kind. | | `key` | `String` | | Stable key. | | `text` | `String` | | Main text. | | `detail` | `String` | | Muted detail. | | `action_label` / `actionLabel` | `String` | | "Revoke", "Remove", "Wipe". | | `command` | [`KvCommand`]() | | What the button sends. | | `imports` | `Vec` | | A Space's copies: their import ids, which Dismiss hides from the notch (empty for grants and rules). | ## `KvAccessKind` enum **Swift** ```swift .grant .rule .delivery ``` **Rust** ```rust KvAccessKind::Grant KvAccessKind::Rule KvAccessKind::Delivery ``` | Variant | Description | | --- | --- | | `Grant` | A grant. | | `Rule` | An unattended rule. | | `Delivery` | Copies in a Space. | ## `KvRecentRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `decision` | [`KvDecision`](#kvdecision-record) | | The decision. | | `age` | `String` | | "now", "5 min ago". | ## `KvDecision` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `entry` | [`KvAuditEntry`]() | | The entry. | | `verb` | `String` | | "Approved". | | `tone` | [`KvDecisionTone`](#kvdecisiontone-enum) | | Tone. | | `what` | `String` | | Item and target, or the actor. | ## `KvDecisionTone` enum **Swift** ```swift .ok .deny .info ``` **Rust** ```rust KvDecisionTone::Ok KvDecisionTone::Deny KvDecisionTone::Info ``` | Variant | Description | | --- | --- | | `Ok` | Allowed or delivered. | | `Deny` | Denied, refused, failed. | | `Info` | Everything else. | ## `KvTri` enum **Swift** ```swift .on .off .mixed ``` **Rust** ```rust KvTri::On KvTri::Off KvTri::Mixed ``` | Variant | Description | | --- | --- | | `On` | All on. | | `Off` | All off. | | `Mixed` | Some. | ## `KvSelection` enum **Swift** ```swift .category(category: KvCategory) .app(key: String) ``` **Rust** ```rust KvSelection::Category { category: KvCategory } KvSelection::App { key: String } ``` | Variant | Description | | --- | --- | | `Category` | A category. Fields: `category: KvCategory` | | `App` | One app's items. Fields: `key: String` | ## `KvSigningBadge` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `text` | `String` | | "signed, team X". | | `tone` | [`KvTone`](#kvtone-enum) | | Tone. | ## `KvTone` enum **Swift** ```swift .ok .warn .danger ``` **Rust** ```rust KvTone::Ok KvTone::Warn KvTone::Danger ``` | Variant | Description | | --- | --- | | `Ok` | Verified. | | `Warn` | Caution. | | `Danger` | Untrusted. | ## `KvAppRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | The provider id (`chrome`): the key, and what the icon is looked up by. | | `title` | `String` | | "Google Chrome". | | `items` | `u32` | | Items. | | `waiting` | `bool` | | Something waits for this app. | ## `KvApprovalRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | Row key: `\|`, `\|files`, or `import:`. | | `title` | `String` | | The site, or the app for files and imports. | | `account` | `String` | | "3 cookies, 1 password", or "Not saved yet". | | `provider_id` / `providerId` | `String` | | The app's provider id (its icon), when known. | | `items` | `u32` | | Items the row stands for. | | `item_ids` / `itemIds` | `Vec` | | The items' ids. | | `selected` | `bool` | | Ticked. | | `is_import` / `isImport` | `bool` | | A new item the approval imports. | ## `KvApprovalState` record Returned by [`kv_approval_open`](#kv_approval_open), [`kv_approval_reduce`](#kv_approval_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `request_id` / `requestId` | `String` | | The request. | | `selected` | `Vec` | | Ticked row keys (`\|`, or `import:` for new items). | ## `KvApprovalView` record Returned by [`kv_approval_view`](#kv_approval_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `request_id` / `requestId` | `String` | | The request. | | `title` | `String` | | "Allow com.example.koalabot?" | | `caller` | `String` | | Verified caller (short). | | `badge` | [`KvSigningBadge`](#kvsigningbadge-record) | | Signing badge. | | `targets` | `String` | | Where to ("dev-1"). | | `wants` | `String` | | For how long. | | `summary` | `String` | | One-line summary. | | `claims` | `Vec` | | Unverified claims. | | `rows` | [`Vec`](#kvapprovalrow-record) | | Rows. | | `can_approve` / `canApprove` | `bool` | | Approve is enabled. | | `blocked_reason` / `blockedReason` | `Option` | | Why not, in one line (none when it can, or nothing is ticked yet). | | `approve_label` / `approveLabel` | `String` | | "Approve 2 items". | | `gone` | `bool` | | The request is gone (answered elsewhere). | ## `KvCategoryRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `category` | [`KvCategory`](#kvcategory-enum) | | Category. | | `title` | `String` | | Title. | | `symbol` | `String` | | SF Symbol. | | `count` | `Option` | | Count (none for Recent). | | `badge` | `Option` | | The sidebar badge: Waiting's count while something waits. | ## `KvDeleteConfirm` record Returned by [`kv_delete_confirm`](#kv_delete_confirm). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Delete 3 items?" | | `message` | `String` | | What deleting does. | | `confirm` | `String` | | "Delete". | | `cancel` | `String` | | "Cancel". | ## `KvListView` record Returned by [`kv_list`](#kv_list). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | Title. | | `vault` | `bool` | | The vault list shows (All Items, or one app). | | `pending` | [`Vec`](#kvpendingrow-record) | | Waiting. | | `access` | [`Vec`](#kvaccessrow-record) | | Access. | | `recent` | [`Vec`](#kvrecentrow-record) | | Recent. | | `empty_text` / `emptyText` | `Option` | | "Nothing is waiting." and friends. | ## `KvPage` record Returned by [`kv_page`](#kv_page). | Field | Type | Default | Description | | --- | --- | --- | --- | | `ready` | `bool` | | Items can be shown. | | `unavailable_title` / `unavailableTitle` | `Option` | | The unavailable heading. | | `message` | `Option` | | The broker's sentence. | | `can_setup` / `canSetup` | `bool` | | "Set up Keyvault" shows. | | `can_unlock` / `canUnlock` | `bool` | | "Unlock" shows. | | `kill_switch_visible` / `killSwitchVisible` | `bool` | | The Disable switch shows (this app is first party, a vault exists). | | `kill_switch_enabled` / `killSwitchEnabled` | `bool` | | The switch can be flipped (unlocked). | | `disabled` | `bool` | | The kill switch is on. | | `disabled_banner` / `disabledBanner` | `Option` | | "Keyvault is off. Nothing can be teleported." | | `partial_errors` / `partialErrors` | `Vec` | | Sections that failed to load. | | `log_status` / `logStatus` | `Option` | | "Log verified" / "Log tampered at #n". | | `log_tampered` / `logTampered` | `bool` | | The log check failed. | | `protection` | [`Vec`]() | | Protection facts. | | `revoke_all` / `revokeAll` | `bool` | | "Revoke all" shows (more than one live grant). | | `has_items` / `hasItems` | `bool` | | Items exist. | | `search_visible` / `searchVisible` | `bool` | | Search shows (any item exists). | | `skip_unlock_prompt` / `skipUnlockPrompt` | `bool` | | "Never ask again" on the unlock prompt is on (Settings turns it off). | | `reset_notice` / `resetNotice` | `Option` | | An earlier preview's vault was set aside: one line to show. | | `pending_count` / `pendingCount` | `u32` | | Pending requests (the sidebar badge). | | `kill_switch_help` / `killSwitchHelp` | `String` | | The switch's tooltip (it reads "on" while the Keyvault works). | | `labels` | [`KvLabels`]() | | The page's fixed words. | | `form` | [`Option`]() | | The setup or unlock form, when one applies. | ## `KvPendingRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Request id. | | `caller` | `String` | | Short caller. | | `badge` | [`KvSigningBadge`](#kvsigningbadge-record) | | Signing badge. | | `summary` | `String` | | What and where. | | `wants` | `String` | | For how long. | | `claims` | `Vec` | | Unverified claims (tooltip). | ## `KvSidebar` record Returned by [`kv_sidebar`](#kv_sidebar). | Field | Type | Default | Description | | --- | --- | --- | --- | | `categories` | [`Vec`](#kvcategoryrow-record) | | All Items, Waiting, Access, Recent. | | `apps` | [`Vec`](#kvapprow-record) | | One row per app, by name. | ## `KvUnlockPrompt` record Returned by [`kv_unlock_prompt`](#kv_unlock_prompt), [`kv_unlock_prompt_always`](#kv_unlock_prompt_always). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Allow unattended access?" | | `message` | `String` | | What it allows. | | `subject` | `String` | | "3 items" (a batch), or the one item's name. | | `deny` | `String` | | "Deny". | | `allow` | `String` | | "Allow". | | `never_ask` / `neverAsk` | `String` | | "Never ask again". | ## `KvVaultApp` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | Group key: the provider id (`chrome`). | | `provider_id` / `providerId` | `String` | | The provider id, for the app's icon. | | `name` | `String` | | "Google Chrome". | | `count` | `u32` | | Items. | | `summary` | `String` | | "212 items, 14 unlocked". | | `updated` | `String` | | Grey time of the newest save. | | `selected` | [`KvTri`](#kvtri-enum) | | Selection of its items. | | `lock` | [`KvLock`](#kvlock-enum) | | Lock of its items. | | `unlock_ids` / `unlockIds` | `Vec` | | Items a click on the lock unlocks (locked, not identity providers). | | `lock_ids` / `lockIds` | `Vec` | | Items a click on the lock locks (unlocked). | | `open` | `bool` | | Open. | | `sites` | [`Vec`](#kvvaultsite-record) | | Sites with their items, by name. | | `files` | [`Option`](#kvvaultfiles-record) | | Files, when it has any. | ## `KvVaultFiles` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | Group key. | | `count` | `u32` | | Files. | | `selected` | [`KvTri`](#kvtri-enum) | | Selection of its items. | | `lock` | [`KvLock`](#kvlock-enum) | | Lock of its items. | | `unlock_ids` / `unlockIds` | `Vec` | | Items a click on the lock unlocks (locked, not identity providers). | | `lock_ids` / `lockIds` | `Vec` | | Items a click on the lock locks (unlocked). | | `open` | `bool` | | Open. | | `rows` | [`Vec`](#kvvaultrow-record) | | Its rows (empty while closed, unless searching). | ## `KvVaultRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Item id. | | `kind` | [`KvKind`](#kvkind-enum) | | Type. | | `kind_label` / `kindLabel` | `String` | | "Cookie". | | `symbol` | `String` | | The type's SF Symbol. | | `title` | `String` | | The key: a cookie name, storage key, username or file name. | | `subtitle` | `String` | | Grey line: the domain, or the file's folder. | | `updated` | `String` | | Grey time: "2 min ago". | | `locked` | `bool` | | Locked (needs approval for every use). | | `lock_symbol` / `lockSymbol` | `String` | | The lock icon: `lock.fill` or `lock.open`. | | `lock_help` / `lockHelp` | `String` | | The lock's tooltip and accessibility label. | | `identity_provider` / `identityProvider` | `bool` | | Identity provider: always asks, cannot be unlocked. | | `selected` | `bool` | | Selected. | ## `KvVaultSelection` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `count` | `u32` | | Selected items. | | `ids` | `Vec` | | Their ids, in list order. | | `title` | `String` | | "3 selected". | | `can_unlock` / `canUnlock` | `bool` | | Some selected item is locked and may be unlocked. | | `can_lock` / `canLock` | `bool` | | Some selected item is unlocked. | | `always_ask` / `alwaysAsk` | `u32` | | Selected items that always ask (identity providers). | | `unlock_ids` / `unlockIds` | `Vec` | | The ids to send when unlocking (locked, not identity providers). | | `lock_ids` / `lockIds` | `Vec` | | The ids to send when locking (unlocked). | ## `KvVaultSite` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | Group key. | | `site` | `String` | | "github.com". | | `count` | `u32` | | Items (all of them, even while closed). | | `counts` | `String` | | "12 cookies, 2 storage values". | | `updated` | `String` | | Grey time of the newest save. | | `selected` | [`KvTri`](#kvtri-enum) | | Selection of its items. | | `lock` | [`KvLock`](#kvlock-enum) | | Lock of its items. | | `unlock_ids` / `unlockIds` | `Vec` | | Items a click on the lock unlocks (locked, not identity providers). | | `lock_ids` / `lockIds` | `Vec` | | Items a click on the lock locks (unlocked). | | `open` | `bool` | | Open. | | `rows` | [`Vec`](#kvvaultrow-record) | | Its rows (empty while closed, unless searching). | ## `KvVaultSource` record Returned by [`kv_vault_source`](#kv_vault_source). | Field | Type | Default | Description | | --- | --- | --- | --- | | `count` | `u32` | | Items that can be sent. | | `newest_ms` / `newestMs` | `i64` | | When the newest was saved, Unix ms (0: none). | | `ids` | `Vec` | | Their ids, in list order. | | `password_ids` / `passwordIds` | `Vec` | | The app's saved passwords (ids), never part of `ids`: they are sent only when ticked in the review. | ## `KvVaultState` record Returned by [`kv_vault_prune`](#kv_vault_prune), [`kv_vault_reduce`](#kv_vault_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `query` | `String` | | The search text. | | `selected` | `Vec` | | Selected item ids. | | `expanded` | `Vec` | | Open groups (their keys); the rest are closed. | | `app` | `Option` | | Narrowed to one app (its provider id), as the sidebar picks it. | ## `KvVaultView` record Returned by [`kv_vault_view`](#kv_vault_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `apps` | [`Vec`](#kvvaultapp-record) | | Apps, by name. | | `shown` | `u32` | | Items shown (after search). | | `total` | `u32` | | Items in the vault. | | `empty_text` / `emptyText` | `Option` | | "No items yet." and friends. | | `names_hidden` / `namesHidden` | `bool` | | Domains and keys are hidden until the user confirms with Touch ID. | | `hidden_note` / `hiddenNote` | `Option` | | What to say about it. | | `show_names_label` / `showNamesLabel` | `String` | | The button that opens the names. | | `search_prompt` / `searchPrompt` | `String` | | The search field's prompt. | | `selection` | [`KvVaultSelection`](#kvvaultselection-record) | | The selection. | | `can_select_all` / `canSelectAll` | `bool` | | Select all is offered. | ## `KvApprovalAction` enum **Swift** ```swift .toggle(key: String) .selectAll .clear ``` **Rust** ```rust KvApprovalAction::Toggle { key: String } KvApprovalAction::SelectAll KvApprovalAction::Clear ``` | Variant | Description | | --- | --- | | `Toggle` | Tick or untick a row. Fields: `key: String` | | `SelectAll` | Tick every row. | | `Clear` | Untick every row. | ## `KvCategory` enum **Swift** ```swift .all .waiting .access .recent ``` **Rust** ```rust KvCategory::All KvCategory::Waiting KvCategory::Access KvCategory::Recent ``` | Variant | Description | | --- | --- | | `All` | Every item, grouped by app. | | `Waiting` | Requests waiting for approval. | | `Access` | Live grants, rules and copies. | | `Recent` | Recent decisions. | ## `KvKind` enum **Swift** ```swift .cookie .localStorage .password .file ``` **Rust** ```rust KvKind::Cookie KvKind::LocalStorage KvKind::Password KvKind::File ``` | Variant | Description | | --- | --- | | `Cookie` | A cookie. | | `LocalStorage` | A localStorage value. | | `Password` | A saved password. | | `File` | A file. | ## `KvLock` enum **Swift** ```swift .locked .unlocked .mixed ``` **Rust** ```rust KvLock::Locked KvLock::Unlocked KvLock::Mixed ``` | Variant | Description | | --- | --- | | `Locked` | Every item needs approval for each use. | | `Unlocked` | Every item allows unattended access. | | `Mixed` | Some of each. | ## `KvVaultAction` enum **Swift** ```swift .query(text: String) .toggle(id: String) .toggleGroup(key: String) .selectAll .clear .toggleOpen(key: String) ``` **Rust** ```rust KvVaultAction::Query { text: String } KvVaultAction::Toggle { id: String } KvVaultAction::ToggleGroup { key: String } KvVaultAction::SelectAll KvVaultAction::Clear KvVaultAction::ToggleOpen { key: String } ``` | Variant | Description | | --- | --- | | `Query` | The search text changed. Fields: `text: String` | | `Toggle` | Select or deselect one item. Fields: `id: String` | | `ToggleGroup` | Select or deselect every shown item of an app, a site or the files of an app (all of them when any is unselected, none when all are). Fields: `key: String` | | `SelectAll` | Select every shown item. | | `Clear` | Deselect everything. | | `ToggleOpen` | Open or close a group. Fields: `key: String` | ## `kv_approval_approve_command` What Approve sends (none while it cannot). **Swift** ```swift func kvApprovalApproveCommand(overview: KeyvaultOverview, state: KvApprovalState) -> KvCommand? ``` **Rust** ```rust pub fn kv_approval_approve_command(overview: KeyvaultOverview, state: KvApprovalState) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `state` | [`KvApprovalState`](#kvapprovalstate-record) | required | **Returns** [`Option`]() ## `kv_approval_deny_command` What Deny sends. **Swift** ```swift func kvApprovalDenyCommand(state: KvApprovalState) -> KvCommand ``` **Rust** ```rust pub fn kv_approval_deny_command(state: KvApprovalState) -> KvCommand ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`KvApprovalState`](#kvapprovalstate-record) | required | **Returns** [`KvCommand`]() ## `kv_approval_open` Opens the approval sheet with nothing selected. **Swift** ```swift func kvApprovalOpen(requestId: String) -> KvApprovalState ``` **Rust** ```rust pub fn kv_approval_open(request_id: String) -> KvApprovalState ``` | Parameter | Type | Default | | --- | --- | --- | | `request_id` | `String` | required | **Returns** [`KvApprovalState`](#kvapprovalstate-record) ## `kv_approval_reduce` Advances the approval sheet. **Swift** ```swift func kvApprovalReduce(overview: KeyvaultOverview, state: KvApprovalState, action: KvApprovalAction) -> KvApprovalState ``` **Rust** ```rust pub fn kv_approval_reduce(overview: KeyvaultOverview, state: KvApprovalState, action: KvApprovalAction) -> KvApprovalState ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `state` | [`KvApprovalState`](#kvapprovalstate-record) | required | | `action` | [`KvApprovalAction`](#kvapprovalaction-enum) | required | **Returns** [`KvApprovalState`](#kvapprovalstate-record) ## `kv_approval_view` The approval sheet as drawn. **Swift** ```swift func kvApprovalView(overview: KeyvaultOverview, state: KvApprovalState) -> KvApprovalView ``` **Rust** ```rust pub fn kv_approval_view(overview: KeyvaultOverview, state: KvApprovalState) -> KvApprovalView ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `state` | [`KvApprovalState`](#kvapprovalstate-record) | required | **Returns** [`KvApprovalView`](#kvapprovalview-record) ## `kv_delete_command` The command that deletes items and wipes their copies in Spaces. **Swift** ```swift func kvDeleteCommand(ids: [String]) -> KvCommand ``` **Rust** ```rust pub fn kv_delete_command(ids: Vec) -> KvCommand ``` | Parameter | Type | Default | | --- | --- | --- | | `ids` | `Vec` | required | **Returns** [`KvCommand`]() ## `kv_delete_confirm` The delete confirmation. **Swift** ```swift func kvDeleteConfirm(count: UInt32, liveCopies: UInt32) -> KvDeleteConfirm ``` **Rust** ```rust pub fn kv_delete_confirm(count: u32, live_copies: u32) -> KvDeleteConfirm ``` | Parameter | Type | Default | | --- | --- | --- | | `count` | `u32` | required | | `live_copies` | `u32` | required | **Returns** [`KvDeleteConfirm`](#kvdeleteconfirm-record) ## `kv_list` The pane for a sidebar selection (the vault list is `kv_vault_view`). **Swift** ```swift func kvList(overview: KeyvaultOverview, selection: KvSelection, nowMs: Int64) -> KvListView ``` **Rust** ```rust pub fn kv_list(overview: KeyvaultOverview, selection: KvSelection, now_ms: i64) -> KvListView ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `selection` | [`KvSelection`](#kvselection-enum) | required | | `now_ms` | `i64` | required | **Returns** [`KvListView`](#kvlistview-record) ## `kv_live_copy_spaces` How many Spaces hold a live copy of any of `ids`. **Swift** ```swift func kvLiveCopySpaces(overview: KeyvaultOverview, ids: [String], nowMs: Int64) -> UInt32 ``` **Rust** ```rust pub fn kv_live_copy_spaces(overview: KeyvaultOverview, ids: Vec, now_ms: i64) -> u32 ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `ids` | `Vec` | required | | `now_ms` | `i64` | required | **Returns** `u32` ## `kv_lock_command` The command that locks items. **Swift** ```swift func kvLockCommand(ids: [String]) -> KvCommand ``` **Rust** ```rust pub fn kv_lock_command(ids: Vec) -> KvCommand ``` | Parameter | Type | Default | | --- | --- | --- | | `ids` | `Vec` | required | **Returns** [`KvCommand`]() ## `kv_page` The page chrome. **Swift** ```swift func kvPage(overview: KeyvaultOverview, nowMs: Int64) -> KvPage ``` **Rust** ```rust pub fn kv_page(overview: KeyvaultOverview, now_ms: i64) -> KvPage ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `now_ms` | `i64` | required | **Returns** [`KvPage`](#kvpage-record) ## `kv_sidebar` The sidebar: All Items, Waiting, Access, Recent, and one row per app. **Swift** ```swift func kvSidebar(overview: KeyvaultOverview, nowMs: Int64) -> KvSidebar ``` **Rust** ```rust pub fn kv_sidebar(overview: KeyvaultOverview, now_ms: i64) -> KvSidebar ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `now_ms` | `i64` | required | **Returns** [`KvSidebar`](#kvsidebar-record) ## `kv_unlock_command` The command that unlocks items (the daemon asks for Touch ID once). **Swift** ```swift func kvUnlockCommand(ids: [String]) -> KvCommand ``` **Rust** ```rust pub fn kv_unlock_command(ids: Vec) -> KvCommand ``` | Parameter | Type | Default | | --- | --- | --- | | `ids` | `Vec` | required | **Returns** [`KvCommand`]() ## `kv_unlock_prompt` The unlock prompt for `count` items (`name`: the one item's name). None when the user chose "Never ask again". **Swift** ```swift func kvUnlockPrompt(overview: KeyvaultOverview, count: UInt32, name: String?) -> KvUnlockPrompt? ``` **Rust** ```rust pub fn kv_unlock_prompt(overview: KeyvaultOverview, count: u32, name: Option) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `count` | `u32` | required | | `name` | `Option` | required | **Returns** [`Option`](#kvunlockprompt-record) ## `kv_unlock_prompt_always` The unlock prompt whatever the setting says. **Swift** ```swift func kvUnlockPromptAlways(count: UInt32, name: String?) -> KvUnlockPrompt ``` **Rust** ```rust pub fn kv_unlock_prompt_always(count: u32, name: Option) -> KvUnlockPrompt ``` | Parameter | Type | Default | | --- | --- | --- | | `count` | `u32` | required | | `name` | `Option` | required | **Returns** [`KvUnlockPrompt`](#kvunlockprompt-record) ## `kv_vault_prune` The selection without what the vault no longer holds. **Swift** ```swift func kvVaultPrune(overview: KeyvaultOverview, state: KvVaultState) -> KvVaultState ``` **Rust** ```rust pub fn kv_vault_prune(overview: KeyvaultOverview, state: KvVaultState) -> KvVaultState ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `state` | [`KvVaultState`](#kvvaultstate-record) | required | **Returns** [`KvVaultState`](#kvvaultstate-record) ## `kv_vault_reduce` The vault list's next state (search, selection, open groups). **Swift** ```swift func kvVaultReduce(overview: KeyvaultOverview, state: KvVaultState, action: KvVaultAction) -> KvVaultState ``` **Rust** ```rust pub fn kv_vault_reduce(overview: KeyvaultOverview, state: KvVaultState, action: KvVaultAction) -> KvVaultState ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `state` | [`KvVaultState`](#kvvaultstate-record) | required | | `action` | [`KvVaultAction`](#kvvaultaction-enum) | required | **Returns** [`KvVaultState`](#kvvaultstate-record) ## `kv_vault_source` What the Keyvault holds for `provider_id` that a teleport can send. **Swift** ```swift func kvVaultSource(overview: KeyvaultOverview, providerId: String) -> KvVaultSource ``` **Rust** ```rust pub fn kv_vault_source(overview: KeyvaultOverview, provider_id: String) -> KvVaultSource ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `provider_id` | `String` | required | **Returns** [`KvVaultSource`](#kvvaultsource-record) ## `kv_vault_view` The vault list: apps, sites and items with their locks, the selection and the batch bar. **Swift** ```swift func kvVaultView(overview: KeyvaultOverview, state: KvVaultState, nowMs: Int64) -> KvVaultView ``` **Rust** ```rust pub fn kv_vault_view(overview: KeyvaultOverview, state: KvVaultState, now_ms: i64) -> KvVaultView ``` | Parameter | Type | Default | | --- | --- | --- | | `overview` | [`KeyvaultOverview`]() | required | | `state` | [`KvVaultState`](#kvvaultstate-record) | required | | `now_ms` | `i64` | required | **Returns** [`KvVaultView`](#kvvaultview-record) --- # Onboarding and settings First-run onboarding, its card miniatures, the settings file and the hotkey label. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_onboarding_reduce` steps through first-run onboarding. `app_settings_load` and `app_settings_save` read and write the apps' settings file. ## `app_format_hotkey` A hotkey label (`⌘⇧Space`), or none. **Swift** ```swift func appFormatHotkey(combo: AppKeyCombo) -> String? ``` **Rust** ```rust pub fn app_format_hotkey(combo: AppKeyCombo) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `combo` | [`AppKeyCombo`](#appkeycombo-record) | required | **Returns** `Option` ## `AppKeyCombo` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `key` | `String` | | The key (`" "`, `"k"`, `"ArrowUp"`, ...). | | `meta_key` / `metaKey` | `bool` | | Command. | | `ctrl_key` / `ctrlKey` | `bool` | | Control. | | `alt_key` / `altKey` | `bool` | | Option. | | `shift_key` / `shiftKey` | `bool` | | Shift. | ## `AppSwitcherTheme` enum **Swift** ```swift .island .glass ``` **Rust** ```rust AppSwitcherTheme::Island AppSwitcherTheme::Glass ``` | Variant | Description | | --- | --- | | `Island` | Opaque black, flush with the notch (default). | | `Glass` | Translucent, below the notch. | ## `AppUsageToggle` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | "Share anonymous usage data". | | `on` | `bool` | | On. | | `enabled` | `bool` | | It can be changed (the environment does not decide). | | `help` | `Option` | | Why it cannot ("Set by env DO_NOT_TRACK"). | ## `AppModeChoice` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `mode` | [`AppOnboardingMode`](#apponboardingmode-enum) | | What it means. | | `label` | `String` | | Label. | | `preselected` | `bool` | | Highlighted (the installer's preselection, else the first). | ## `AppDriverFrame` record Returned by [`app_driver_preview_frame`](#app_driver_preview_frame), [`app_driver_preview_still`](#app_driver_preview_still). | Field | Type | Default | Description | | --- | --- | --- | --- | | `pointer` | [`AppPreviewPoint`](#apppreviewpoint-record) | | The user's pointer tip. | | `pressed` | `bool` | | The user presses (drawn a little smaller). | | `selection` | `f64` | | How much of the selected line is highlighted, 0 to 1 from its left. | | `agent` | [`AppPreviewPoint`](#apppreviewpoint-record) | | The agent cursor's tip. | | `agent_pressed` / `agentPressed` | `bool` | | The agent presses (drawn a little smaller). | | `ripple` | `f64` | | The click rays, 0 hidden, else 0 to 1 through their burst (they grow out and fade). | | `checked` | `Vec` | | Each checkbox's tick, 0 to 1. | ## `AppDriverPreview` record Returned by [`app_driver_preview`](#app_driver_preview). | Field | Type | Default | Description | | --- | --- | --- | --- | | `width` | `f64` | | Stage width. | | `height` | `f64` | | Stage height. | | `loop_ms` / `loopMs` | `u32` | | One loop (ms). | | `back` | [`AppPreviewWindow`](#apppreviewwindow-record) | | The window the agent works in (behind). | | `front` | [`AppPreviewWindow`](#apppreviewwindow-record) | | The window the user works in (in front). | | `checkboxes` | [`Vec`]() | | The back window's checkboxes, top to bottom. | | `labels` | [`Vec`]() | | The label bar right of each checkbox. | | `lines` | [`Vec`]() | | The front window's text lines (placeholder bars). | | `selected_line` / `selectedLine` | `u32` | | The line the user selects (an index into `lines`). | | `pointer` | [`Vec`](#apppreviewpoint-record) | | The user's pointer outline, tip at the origin (fill black, stroke white). | | `agent_pointer` / `agentPointer` | [`Vec`](#apppreviewpoint-record) | | The agent cursor's outline, tip at the origin (fill `agent_fill`, stroke white, a soft glow of `agent_fill`). | | `agent_fill` / `agentFill` | `String` | | The agent cursor's fill. | | `agent_rays` / `agentRays` | [`Vec`](#apppreviewsegment-record) | | The click rays, relative to the agent cursor's tip. | ## `AppMenuPreview` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `icon` | [`AppLogicalRect`]() | | The Cua Spaces icon (the tray template). | | `highlight` | [`AppLogicalRect`]() | | The status item's highlight while pressed or open. | | `clock` | `String` | | The clock at the menu bar's right end. | | `clock_right` / `clockRight` | `f64` | | Its right edge. | | `menu` | [`AppLogicalRect`]() | | The menu. | | `radius` | `f64` | | Its corner radius. | | `rows` | [`Vec`](#appmenupreviewrow-record) | | The rows, top to bottom (`window.menu_bar` for `SPACES`). | | `inset` | `f64` | | Text and shortcut inset from the menu's edges. | | `font_size` / `fontSize` | `f64` | | The rows' font size. | ## `AppMenuPreviewRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `item` | [`AppMenuItem`]() | | The core's item. | | `frame` | [`AppLogicalRect`]() | | Its row on the stage. | ## `AppOnboardingCheckbox` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | "Launch at login". | | `checked` | `bool` | | Ticked. | | `note` | `String` | | The line under it. | ## `AppOnboardingCopy` record Returned by [`app_onboarding_copy`](#app_onboarding_copy). | Field | Type | Default | Description | | --- | --- | --- | --- | | `back` | `String` | | "Back". | | `skip` | `String` | | "Skip". | | `continue_label` / `continueLabel` | `String` | | "Continue". | | `try_again` / `tryAgain` | `String` | | "Try again". | | `checking` | `String` | | "Checking…" (the install plan loads). | | `install_script` / `installScript` | `String` | | The install script when the app bundles no `cua`. | | `install` | `String` | | "Install". | | `installing` | `String` | | "Installing…". | | `add_to_path` / `addToPath` | `String` | | The PATH switch. | | `shadowed` | `String` | | When another `cua` comes first on PATH. | | `sign_in` / `signIn` | `String` | | "Sign in". | | `sign_in_waiting` / `signInWaiting` | `String` | | While the browser is open. | | `agents_looking` / `agentsLooking` | `String` | | While agents are detected. | | `agents_none` / `agentsNone` | `String` | | No agent found. | | `agents_skills` / `agentsSkills` | `String` | | The skills switch. | | `agents_mcp` / `agentsMcp` | `String` | | The MCP switch. | | `agents_set_up` / `agentsSetUp` | `String` | | "Set up". | | `agents_setting_up` / `agentsSettingUp` | `String` | | "Setting up…". | | `agents_done_title` / `agentsDoneTitle` | `String` | | After setup: heading. | | `agents_done_lede` / `agentsDoneLede` | `String` | | After setup: one line. | | `agents_driver` / `agentsDriver` | `String` | | The background computer-use card's one line (its checkbox). | | `agents_driver_image` / `agentsDriverImage` | `String` | | The card's miniature, for accessibility. | | `permissions_title` / `permissionsTitle` | `String` | | Over the permission rows (Done, after host setup). | | `open_settings` / `openSettings` | `String` | | The permission rows' button. | | `teams` | `String` | | The Sign in page's Teams line ("Teams · coming soon"). | | `teams_link` / `teamsLink` | `String` | | Its link ("Join the waitlist"). | | `teams_url` / `teamsUrl` | `String` | | The link's URL: the website's Teams waitlist (the app collects nothing). | ## `AppOnboardingState` record Returned by [`app_onboarding_initial`](#app_onboarding_initial), [`app_onboarding_reduce`](#app_onboarding_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `step` | [`AppOnboardingStep`](#apponboardingstep-enum) | | Page. | | `cli_target` / `cliTarget` | `Option` | | Where `cua` was installed on first launch, when it was. | | `menu_bar` / `menuBar` | `bool` | | Menu bar only (no notch). The same setting as Settings' "Spaces tab in the notch". | | `identity` | `Option` | | Signed-in identity. | | `agents` | `Vec` | | Agents configured. | | `mode` | [`Option`](#apponboardingmode-enum) | | The choice. | | `installer_mode` / `installerMode` | [`Option`](#apponboardingmode-enum) | | The installer's preselection (`--mode host`, MDM file). | | `drive_os` / `driveOs` | [`Option`]() | | This machine's system, once the drive was checked. | | `drive_status` / `driveStatus` | [`Option`]() | | `volume_mount_status`, when the daemon answered. | | `drive_checked` / `driveChecked` | `bool` | | The drive was checked (answered or not). | | `drive_mount` / `driveMount` | `bool` | | The Cua Volume checkbox (off by default). | | `drive_request` / `driveRequest` | [`Option`]() | | What the shell is running for the page. | | `drive_error` / `driveError` | `Option` | | Why mounting failed. | | `drive_storage` / `driveStorage` | [`Option`]() | | `volume_storage`, when the daemon answered. | | `storage_choice` / `storageChoice` | [`AppStorageChoice`]() | | Where the files live, as picked. | | `drive_home` / `driveHome` | `Option` | | The home folder (paths show as `~/...`). | | `storage_picked` / `storagePicked` | `bool` | | The user picked (the saved backend no longer decides). | | `storage` | [`AppStorageState`]() | | The bucket form and its test or save (`crate.drive_settings`'s state; its `request` is the shell's). | | `launch_at_login` / `launchAtLogin` | `bool` | | Done's "Launch at login" checkbox (ticked unless unticked). The shell applies it when the first run finishes. | | `telemetry` | [`Option`]() | | The usage-data switch on Welcome: the machine's telemetry setting (Settings' "Share anonymous usage data", `cua telemetry off`), none until the shell read it (then it shows on). | | `experiments` | [`AppExperiments`]() | | Settings, Experiments: the Cua Volume page (and its Done line) only while Cua Volume is on. Off until the shell says otherwise. | ## `AppOnboardingView` record Returned by [`app_onboarding_view`](#app_onboarding_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `step` | [`AppOnboardingStep`](#apponboardingstep-enum) | | Page. | | `dots` | [`Vec`]() | | Page dots. | | `title` | `String` | | Heading. | | `lede` | `String` | | One line under it. | | `primary_label` / `primaryLabel` | `String` | | The primary button. | | `can_skip` / `canSkip` | `bool` | | A Skip button shows. | | `can_back` / `canBack` | `bool` | | A Back button shows. | | `show_mark` / `showMark` | `bool` | | The stacked Cua mark shows (Welcome). | | `preselected_mode` / `preselectedMode` | [`AppOnboardingMode`](#apponboardingmode-enum) | | The machine choice to highlight (the installer's preselection). | | `summary` | [`Vec`]() | | Done's facts. | | `choices` | [`Vec`](#appmodechoice-record) | | This machine's two answers (the installer's preselection first highlighted), on its page. | | `presentations` | [`Vec`](#apppresentationcard-record) | | The presentation page's two cards, side by side. | | `prompts` | `Vec` | | Done's example prompts (`PROMPTS`), one line each: shown under both columns, scrolling slowly, to suggest what to ask a coding agent. Not interactive. | | `notice` | `Option` | | The first-run telemetry notice (the Welcome page, so the first run itself can be measured): nothing is sent before it has been shown once. | | `notice_link_label` / `noticeLinkLabel` | `Option` | | Its link: label. | | `notice_link_url` / `noticeLinkUrl` | `Option` | | Its link: URL. | | `usage` | [`Option`](#appusagetoggle-record) | | Welcome's usage-data switch, next to the notice: nothing is sent before the user leaves Welcome, and nothing at all while it is off. | | `drive` | [`Option`]() | | The Cua Volume page's card. | | `launch_at_login` / `launchAtLogin` | [`Option`](#apponboardingcheckbox-record) | | Done's "Launch at login" checkbox. | ## `AppPresentationCard` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `menu_bar` / `menuBar` | `bool` | | Menu bar only. | | `title` | `String` | | "Notch and menu bar". | | `id` | `String` | | The card's id (`onboarding-notch`, `onboarding-menu-bar`): its accessibility identifier and list key. | | `image_label` / `imageLabel` | `String` | | The miniature's spoken description. | | `selected` | `bool` | | Picked. | ## `AppPresentationPreview` record Returned by [`app_presentation_preview`](#app_presentation_preview). | Field | Type | Default | Description | | --- | --- | --- | --- | | `menu_bar` / `menuBar` | `bool` | | The menu bar card (else the notch card). | | `width` | `f64` | | Stage width. | | `height` | `f64` | | Stage height. | | `menu_bar_height` / `menuBarHeight` | `f64` | | The menu bar strip along the top. | | `loop_ms` / `loopMs` | `u32` | | One loop (ms). | | `pointer` | [`Vec`](#apppreviewpoint-record) | | The pointer's outline, tip at the origin (fill black, stroke white). | | `notch` | [`Option`]() | | The notch card's miniature. | | `menu` | [`Option`](#appmenupreview-record) | | The menu bar card's miniature. | ## `AppPreviewFrame` record Returned by [`app_presentation_preview_frame`](#app_presentation_preview_frame), [`app_presentation_preview_still`](#app_presentation_preview_still). | Field | Type | Default | Description | | --- | --- | --- | --- | | `pointer` | [`AppPreviewPoint`](#apppreviewpoint-record) | | The pointer's tip. | | `pressed` | `bool` | | The pointer presses (drawn a little smaller). | | `hover` | `f64` | | Notch: the hover cue, 0 to 1 (the closed notch and its tab grow by 1 + (hover_scale - 1) * hover horizontally, about the top centre). | | `open` | `f64` | | Notch: the shape, closed 0 to open 1 (a spring: it can pass 1); menu: the menu's opacity. | | `content` | `f64` | | Notch: the content's fade, 0 to 1 (it also grows from `content_scale`, anchored at the top); menu: the same as `open`. | | `tab` | `f64` | | Notch: the tab, 1 shown to 0 tucked under the notch. | | `highlighted` | `Option` | | Menu: the row under the pointer (an enabled item), highlighted. | | `active` | `bool` | | Menu: the status item is highlighted (pressed, or its menu open). | ## `AppPreviewPoint` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `x` | `f64` | | Left. | | `y` | `f64` | | Down. | ## `AppPreviewSegment` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `from` | [`AppPreviewPoint`](#apppreviewpoint-record) | | Start. | | `to` | [`AppPreviewPoint`](#apppreviewpoint-record) | | End. | ## `AppPreviewWindow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `frame` | [`AppLogicalRect`]() | | Its frame on the stage. | | `title_bar` / `titleBar` | `f64` | | The title bar's height (the three window buttons sit in it). | | `radius` | `f64` | | Corner radius. | ## `AppSettings` record Returned by [`app_settings_load`](#app_settings_load). | Field | Type | Default | Description | | --- | --- | --- | --- | | `hotkey` | `String` | | Global hotkey label. | | `menu_bar` / `menuBar` | `bool` | | The switcher lives in the menu bar instead of the notch. | | `theme` | [`AppSwitcherTheme`](#appswitchertheme-enum) | | Switcher theme. | | `default_location` / `defaultLocation` | [`AppLocation`]() | | Where New Space starts. | | `hide_in_full_screen` / `hideInFullScreen` | `bool` | | Hide the notch over full-screen apps. | | `notifications_seen_ms` / `notificationsSeenMs` | `u64` | | The newest notification already posted (Unix ms; 0: never polled). | | `last_seen_version` / `lastSeenVersion` | `Option` | | The version and build the app last launched as (`crate.about.identity`; `None`: never recorded). A launch as another one follows an update (`crate.about.after_launch`). | | `update_channel` / `updateChannel` | [`AppUpdateChannel`]() | | Which releases the updater offers (Settings, About). | | `launch_at_login` / `launchAtLogin` | `Option` | | Launch at login, as the user chose it (the first run's Done page or Settings); `None`: never chosen (`crate.login_item.launch_plan`). | | `experiments` | [`AppExperiments`]() | | Settings, Experiments: every switch off unless turned on (`crate.experiments`). | | `dismissed_access` / `dismissedAccess` | `Vec` | | Keyvault copies (import ids) the user dismissed from the notch: it no longer shows them, nothing is revoked or wiped (`crate.keyvault.view.prune_dismissed` forgets the gone ones). | | `keyvault_site_icons` / `keyvaultSiteIcons` | `bool` | | Keyvault rows may load a site's icon from Google's favicon service when the source browser had none (Settings, Keyvault; on by default). Off, only icons read locally are shown. | | `teleport_choices` / `teleportChoices` | [`Vec`]() | | The sites the user sent to each Space last time, per app (`crate.teleport.review.remember`): the review starts from them. | | `auto_connect` / `autoConnect` | `bool` | | Settings, General: a running Space's desktop streams as soon as it is opened (on by default); off, it waits for Connect (`crate.spaces.cover`). | ## `AppOnboardingAction` enum **Swift** ```swift .start .cliInstalled(target: String?) .presentationPicked(menuBar: Bool) .presentationDone .signedIn(identity: String) .signinDone .agentsDone(configured: [String]) .modeChosen(mode: AppOnboardingMode) .driveChecked(os: AppSpaceOs, status: AppDriveMountInput?) .driveToggled(on: Bool) .driveContinue .driveMounted(status: AppDriveMountInput) .driveFailed(error: String) .driveStorageLoaded(storage: AppDriveStorageInput, home: String?) .storageChosen(choice: AppStorageChoice) .driveStorage(action: AppStorageAction) .driveStorageSaved(check: AppDriveCheckInput) .launchAtLoginToggled(on: Bool) .back .telemetryLoaded(telemetry: AppTelemetryInput) .usageDataToggled(on: Bool) .experimentsLoaded(experiments: AppExperiments) ``` **Rust** ```rust AppOnboardingAction::Start AppOnboardingAction::CliInstalled { target: Option } AppOnboardingAction::PresentationPicked { menu_bar: bool } AppOnboardingAction::PresentationDone AppOnboardingAction::SignedIn { identity: String } AppOnboardingAction::SigninDone AppOnboardingAction::AgentsDone { configured: Vec } AppOnboardingAction::ModeChosen { mode: AppOnboardingMode } AppOnboardingAction::DriveChecked { os: AppSpaceOs, status: Option } AppOnboardingAction::DriveToggled { on: bool } AppOnboardingAction::DriveContinue AppOnboardingAction::DriveMounted { status: AppDriveMountInput } AppOnboardingAction::DriveFailed { error: String } AppOnboardingAction::DriveStorageLoaded { storage: AppDriveStorageInput, home: Option } AppOnboardingAction::StorageChosen { choice: AppStorageChoice } AppOnboardingAction::DriveStorage { action: AppStorageAction } AppOnboardingAction::DriveStorageSaved { check: AppDriveCheckInput } AppOnboardingAction::LaunchAtLoginToggled { on: bool } AppOnboardingAction::Back AppOnboardingAction::TelemetryLoaded { telemetry: AppTelemetryInput } AppOnboardingAction::UsageDataToggled { on: bool } AppOnboardingAction::ExperimentsLoaded { experiments: AppExperiments } ``` | Variant | Description | | --- | --- | | `Start` | "Get started". | | `CliInstalled` | The app installed its bundled `cua` on first launch (any page). Fields: `target: Option` | | `PresentationPicked` | A presentation card was picked (it stays on the page). Fields: `menu_bar: bool` | | `PresentationDone` | Continue from the presentation page. | | `SignedIn` | Signed in. Fields: `identity: String` | | `SigninDone` | Continue (or Skip) from Sign in. | | `AgentsDone` | The agents step finished. Fields: `configured: Vec` | | `ModeChosen` | The machine choice was made. Fields: `mode: AppOnboardingMode` | | `DriveChecked` | What the drive mount can do here (`volume_mount_status`; none when the daemon could not answer). Any page. Fields: `os: AppSpaceOs`, `status: Option` | | `DriveToggled` | The Cua Volume checkbox. Fields: `on: bool` | | `DriveContinue` | Continue from Cua Volume: mounts or unmounts first when the checkbox and the daemon disagree. | | `DriveMounted` | `volume_mount` / `volume_unmount` answered. Fields: `status: AppDriveMountInput` | | `DriveFailed` | `volume_mount` / `volume_unmount` failed. Fields: `error: String` | | `DriveStorageLoaded` | `volume_storage` answered. Any page. Fields: `storage: AppDriveStorageInput`, `home: Option` | | `StorageChosen` | Where the files live was picked. Fields: `choice: AppStorageChoice` | | `DriveStorage` | An edit of the bucket form, Test connection, or a test's answer (`set-field`, `set-path-style`, `test`, `checked`, `failed`). Fields: `action: AppStorageAction` | | `DriveStorageSaved` | Continue's `volume_storage_set` answered. Fields: `check: AppDriveCheckInput` | | `LaunchAtLoginToggled` | Done's "Launch at login" checkbox. Fields: `on: bool` | | `Back` | Back one page (not from Welcome or Done). | | `TelemetryLoaded` | The machine's telemetry setting, as the shell read it. Any page. Fields: `telemetry: AppTelemetryInput` | | `UsageDataToggled` | Welcome's "Share anonymous usage data" switch (not while the environment decides). The shell writes the setting. Fields: `on: bool` | | `ExperimentsLoaded` | Settings, Experiments, as the shell read them (at the start, and whenever a switch changes). Any page; turning Cua Volume off on its page moves on. Fields: `experiments: AppExperiments` | ## `AppOnboardingMode` enum **Swift** ```swift .client .host ``` **Rust** ```rust AppOnboardingMode::Client AppOnboardingMode::Host ``` | Variant | Description | | --- | --- | | `Client` | Access other machines (nothing installed). | | `Host` | Set this machine up for unattended access. | ## `AppOnboardingStep` enum **Swift** ```swift .welcome .signin .agents .presentation .drive .mode .done ``` **Rust** ```rust AppOnboardingStep::Welcome AppOnboardingStep::Signin AppOnboardingStep::Agents AppOnboardingStep::Presentation AppOnboardingStep::Drive AppOnboardingStep::Mode AppOnboardingStep::Done ``` | Variant | Description | | --- | --- | | `Welcome` | Welcome. | | `Signin` | Sign in to Cua. | | `Agents` | Coding agents: cua skills and the cua MCP server. | | `Presentation` | Where Cua Spaces shows up: the notch and the menu bar, or the menu bar only (the "Spaces tab in the notch" setting). | | `Drive` | The Cua Volume as a Finder volume (a mount on Linux), off unless ticked. Skipped where it cannot mount (Windows). | | `Mode` | Access other machines, or set this one up for unattended access. | | `Done` | Done. | ## `app_driver_preview` The AI agents page's background computer-use card: its miniature (an agent clicking in a background window while the user works in front). **Swift** ```swift func appDriverPreview() -> AppDriverPreview ``` **Rust** ```rust pub fn app_driver_preview() -> AppDriverPreview ``` **Returns** [`AppDriverPreview`](#appdriverpreview-record) ## `app_driver_preview_frame` The card's miniature `t_ms` into its loop (it wraps). **Swift** ```swift func appDriverPreviewFrame(tMs: UInt32) -> AppDriverFrame ``` **Rust** ```rust pub fn app_driver_preview_frame(t_ms: u32) -> AppDriverFrame ``` | Parameter | Type | Default | | --- | --- | --- | | `t_ms` | `u32` | required | **Returns** [`AppDriverFrame`](#appdriverframe-record) ## `app_driver_preview_still` The card's miniature with Reduce Motion: mid-task, still. **Swift** ```swift func appDriverPreviewStill() -> AppDriverFrame ``` **Rust** ```rust pub fn app_driver_preview_still() -> AppDriverFrame ``` **Returns** [`AppDriverFrame`](#appdriverframe-record) ## `app_onboarding_action_from_json` A first-run action from JSON (parity flows). **Swift** ```swift func appOnboardingActionFromJson(json: String) throws -> AppOnboardingAction ``` **Rust** ```rust pub fn app_onboarding_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppOnboardingAction`](#apponboardingaction-enum) · **Raises** [`CuaError`]() ## `app_onboarding_copy` First run's fixed words. **Swift** ```swift func appOnboardingCopy() -> AppOnboardingCopy ``` **Rust** ```rust pub fn app_onboarding_copy() -> AppOnboardingCopy ``` **Returns** [`AppOnboardingCopy`](#apponboardingcopy-record) ## `app_onboarding_initial` First run's first state. **Swift** ```swift func appOnboardingInitial(installerMode: AppOnboardingMode?, identity: String?) -> AppOnboardingState ``` **Rust** ```rust pub fn app_onboarding_initial(installer_mode: Option, identity: Option) -> AppOnboardingState ``` | Parameter | Type | Default | | --- | --- | --- | | `installer_mode` | [`Option`](#apponboardingmode-enum) | required | | `identity` | `Option` | required | **Returns** [`AppOnboardingState`](#apponboardingstate-record) ## `app_onboarding_installed_at_text` The Command line page's line once `cua` is current. **Swift** ```swift func appOnboardingInstalledAtText(target: String) -> String ``` **Rust** ```rust pub fn app_onboarding_installed_at_text(target: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `target` | `String` | required | **Returns** `String` ## `app_onboarding_reduce` Advances first run. **Swift** ```swift func appOnboardingReduce(state: AppOnboardingState, action: AppOnboardingAction) -> AppOnboardingState ``` **Rust** ```rust pub fn app_onboarding_reduce(state: AppOnboardingState, action: AppOnboardingAction) -> AppOnboardingState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppOnboardingState`](#apponboardingstate-record) | required | | `action` | [`AppOnboardingAction`](#apponboardingaction-enum) | required | **Returns** [`AppOnboardingState`](#apponboardingstate-record) ## `app_onboarding_replaces_text` The install target's tooltip when a `cua` is already there. **Swift** ```swift func appOnboardingReplacesText(installedVersion: String?) -> String ``` **Rust** ```rust pub fn app_onboarding_replaces_text(installed_version: Option) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `installed_version` | `Option` | required | **Returns** `String` ## `app_onboarding_sign_in_code_text` The Sign in page's line while the browser is open. **Swift** ```swift func appOnboardingSignInCodeText(userCode: String?) -> String ``` **Rust** ```rust pub fn app_onboarding_sign_in_code_text(user_code: Option) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `user_code` | `Option` | required | **Returns** `String` ## `app_onboarding_signed_in_text` The Sign in page's line once signed in. **Swift** ```swift func appOnboardingSignedInText(identity: String?) -> String ``` **Rust** ```rust pub fn app_onboarding_signed_in_text(identity: Option) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `identity` | `Option` | required | **Returns** `String` ## `app_onboarding_view` First run as drawn. **Swift** ```swift func appOnboardingView(state: AppOnboardingState) -> AppOnboardingView ``` **Rust** ```rust pub fn app_onboarding_view(state: AppOnboardingState) -> AppOnboardingView ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppOnboardingState`](#apponboardingstate-record) | required | **Returns** [`AppOnboardingView`](#apponboardingview-record) ## `app_presentation_preview` A presentation card's animated miniature (the notch card, or with `menu_bar` the menu bar card). **Swift** ```swift func appPresentationPreview(menuBar: Bool) -> AppPresentationPreview ``` **Rust** ```rust pub fn app_presentation_preview(menu_bar: bool) -> AppPresentationPreview ``` | Parameter | Type | Default | | --- | --- | --- | | `menu_bar` | `bool` | required | **Returns** [`AppPresentationPreview`](#apppresentationpreview-record) ## `app_presentation_preview_frame` The miniature `t_ms` into its loop (it wraps). **Swift** ```swift func appPresentationPreviewFrame(menuBar: Bool, tMs: UInt32) -> AppPreviewFrame ``` **Rust** ```rust pub fn app_presentation_preview_frame(menu_bar: bool, t_ms: u32) -> AppPreviewFrame ``` | Parameter | Type | Default | | --- | --- | --- | | `menu_bar` | `bool` | required | | `t_ms` | `u32` | required | **Returns** [`AppPreviewFrame`](#apppreviewframe-record) ## `app_presentation_preview_still` The miniature with Reduce Motion: expanded, still. **Swift** ```swift func appPresentationPreviewStill(menuBar: Bool) -> AppPreviewFrame ``` **Rust** ```rust pub fn app_presentation_preview_still(menu_bar: bool) -> AppPreviewFrame ``` | Parameter | Type | Default | | --- | --- | --- | | `menu_bar` | `bool` | required | **Returns** [`AppPreviewFrame`](#apppreviewframe-record) ## `app_settings_load` Reads the settings file (defaults when missing or damaged). **Swift** ```swift func appSettingsLoad(path: String) -> AppSettings ``` **Rust** ```rust pub fn app_settings_load(path: String) -> AppSettings ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | **Returns** [`AppSettings`](#appsettings-record) ## `app_settings_save` Writes the settings file. **Swift** ```swift func appSettingsSave(path: String, settings: AppSettings) throws ``` **Rust** ```rust pub fn app_settings_save(path: String, settings: AppSettings) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `settings` | [`AppSettings`](#appsettings-record) | required | **Raises** [`CuaError`]() --- # About, updates and launch The About window and its update channel, what the apps do once they launch (the bundled cua install), and the daemon and refresh reports they read. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_about_view` builds the About window from the shell's `AppAboutInput`; `app_about_after_launch` and `app_about_restart_daemon` say what to do after an update or when the daemon is from another build. `AppCliInstaller` puts the bundled `cua` on PATH on first launch. ## `AppDaemonCheck` record Returned by [`app_daemon_check_from_json`](#app_daemon_check_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `daemon_exe` / `daemonExe` | `Option` | | The running daemon's executable (`None`: no daemon, or unknown). | | `bundle` | `String` | | This app's bundle (`/Applications/Cua Spaces.app`). | ## `app_daemon_check_from_json` A daemon check from JSON (parity flows). **Swift** ```swift func appDaemonCheckFromJson(json: String) throws -> AppDaemonCheck ``` **Rust** ```rust pub fn app_daemon_check_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppDaemonCheck`](#appdaemoncheck-record) · **Raises** [`CuaError`]() ## `AppRefreshReport` record Returned by [`app_refresh_report_from_json`](#app_refresh_report_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `agents_error` / `agentsError` | `Option` | | `cua agents update` failed (its words). | | `daemon_error` / `daemonError` | `Option` | | Restarting the daemon failed (its words). | ## `app_refresh_report_from_json` A refresh report from JSON (parity flows). **Swift** ```swift func appRefreshReportFromJson(json: String) throws -> AppRefreshReport ``` **Rust** ```rust pub fn app_refresh_report_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppRefreshReport`](#apprefreshreport-record) · **Raises** [`CuaError`]() ## `AppUpdateChannel` enum **Swift** ```swift .stable .beta ``` **Rust** ```rust AppUpdateChannel::Stable AppUpdateChannel::Beta ``` | Variant | Description | | --- | --- | | `Stable` | Releases (the default). | | `Beta` | Releases and prereleases (`X.Y.Z-suffix`). | ## `AppCliInstaller` Puts the app's bundled `cua` on PATH on first launch (there is no onboarding page for it). The same installer the Tauri app runs; `plan` reads what installing would do without writing. | Method | Description | | --- | --- | | [`for_executable`](#appcliinstallerfor_executable) | This machine: the `cua` next to `executable` (the app's own executable), `$HOME`, `$PATH` and `$SHELL`. | | [`with_paths`](#appcliinstallerwith_paths) | Explicit paths (tests): the bundled CLI, the bin directory, the PATH value to check and the shell profile a PATH change edits. | | [`install`](#appcliinstallerinstall) | Installs the CLI (after the user consented to the plan). | | [`plan`](#appcliinstallerplan) | What installing would do (nothing is written). | ### `AppCliInstaller.for_executable` This machine: the `cua` next to `executable` (the app's own executable), `$HOME`, `$PATH` and `$SHELL`. **Swift** ```swift static func forExecutable(executable: String) -> AppCliInstaller ``` **Rust** ```rust pub fn for_executable(executable: String) -> Arc ``` | Parameter | Type | Default | | --- | --- | --- | | `executable` | `String` | required | **Returns** [`AppCliInstaller`](#appcliinstaller) ### `AppCliInstaller.with_paths` Explicit paths (tests): the bundled CLI, the bin directory, the PATH value to check and the shell profile a PATH change edits. **Swift** ```swift static func withPaths(bundled: String?, binDir: String, pathEnv: String, profile: String) -> AppCliInstaller ``` **Rust** ```rust pub fn with_paths(bundled: Option, bin_dir: String, path_env: String, profile: String) -> Arc ``` | Parameter | Type | Default | | --- | --- | --- | | `bundled` | `Option` | required | | `bin_dir` | `String` | required | | `path_env` | `String` | required | | `profile` | `String` | required | **Returns** [`AppCliInstaller`](#appcliinstaller) ### `AppCliInstaller.install` Installs the CLI (after the user consented to the plan). **Swift** ```swift func install(request: AppCliInstallRequest) async throws -> AppCliInstallPlan ``` **Rust** ```rust pub async fn install(&self, request: AppCliInstallRequest) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `request` | [`AppCliInstallRequest`](#appcliinstallrequest-record) | required | **Returns** [`AppCliInstallPlan`](#appcliinstallplan-record) · **Async** · **Raises** [`CuaError`]() ### `AppCliInstaller.plan` What installing would do (nothing is written). **Swift** ```swift func plan() async -> AppCliInstallPlan ``` **Rust** ```rust pub async fn plan(&self) -> AppCliInstallPlan ``` **Returns** [`AppCliInstallPlan`](#appcliinstallplan-record) · **Async** ## `AppAboutInput` record Returned by [`app_about_input_from_json`](#app_about_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `platform` | `String` | | `macos`, `linux` or `windows`. | | `version` | `String` | | The full version (`0.2.0`, `0.2.0-staging.6`). | | `build` | `String` | | The build (the macOS app's `CFBundleVersion`, `0.2.0.123`; may be empty). | | `os` | `String` | | This machine for an issue report (`macOS 26.0 (arm64)`). | | `updater` | `bool` | | The app updates itself (its updater is running). | | `auto_check` / `autoCheck` | `bool` | | The updater checks on its own. | | `auto_install` / `autoInstall` | `bool` | | The updater downloads and installs on its own. | | `channel` | [`AppUpdateChannel`](#appupdatechannel-enum) | | The chosen channel. | | `last_check` / `lastCheck` | `Option` | | The last check, as the shell formats a date; `None`: never. | | `checking` | `bool` | | A check is running. | ## `AppAboutLink` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | [`AppAboutLinkId`](#appaboutlinkid-enum) | | Which. | | `label` | `String` | | "Privacy Policy". | | `url` | `Option` | | What it opens; `None`: the notices the app bundles. | ## `AppAboutUpdates` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `auto_check_label` / `autoCheckLabel` | `String` | | "Automatically check for updates". | | `auto_check` / `autoCheck` | `bool` | | Checked. | | `auto_install_label` / `autoInstallLabel` | `String` | | "Install updates automatically". | | `auto_install` / `autoInstall` | `bool` | | Checked. | | `auto_install_enabled` / `autoInstallEnabled` | `bool` | | It can be used (only with automatic checks). | | `channel_label` / `channelLabel` | `String` | | "Update to:". | | `channels` | [`Vec`]() | | Stable, Beta. | | `channel_help` / `channelHelp` | `String` | | The channel picker's tooltip. | | `check_label` / `checkLabel` | `String` | | "Check Now" (or "Checking…"). | | `check_enabled` / `checkEnabled` | `bool` | | Check Now can be used. | | `last_check` / `lastCheck` | `String` | | "Last check: …". | ## `AppAboutView` record Returned by [`app_about_view`](#app_about_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Cua Spaces for macOS". | | `version_line` / `versionLine` | `String` | | "Version 0.2.0 (0.2.0.123)". | | `links` | [`Vec`](#appaboutlink-record) | | Acknowledgements, Privacy Policy, Terms of Service, Report an Issue. | | `copyright` | `String` | | "© 2026 Cua AI, Inc. All rights reserved." | | `updates` | [`Option`](#appaboutupdates-record) | | The update controls, when the app updates itself. | ## `AppCliInstallPlan` record Returned by [`AppCliInstaller.install`](#appcliinstallerinstall), [`AppCliInstaller.plan`](#appcliinstallerplan). | Field | Type | Default | Description | | --- | --- | --- | --- | | `source` | `Option` | | The bundled CLI, when this build carries one. | | `target` | `String` | | Exact file the CLI is written to. | | `bin_dir` / `binDir` | `String` | | Directory of `target`. | | `method` | [`Option`](#appinstallmethod-enum) | | `symlink` or `copy`. | | `installed` | `bool` | | Something already exists at `target`. | | `up_to_date` / `upToDate` | `bool` | | `target` resolves to this app's bundled CLI (nothing to do). | | `installed_version` / `installedVersion` | `Option` | | `cua --version` of what is at `target`. | | `bundled_version` / `bundledVersion` | `Option` | | `cua --version` of the bundled CLI. | | `shadowed_by` / `shadowedBy` | `Option` | | Another `cua` found earlier on PATH (it would shadow `target`). | | `on_path` / `onPath` | `bool` | | `bin_dir` is already on PATH. | | `path_profile` / `pathProfile` | `Option` | | Shell profile (or "user PATH" on Windows) the PATH change would edit. | | `path_line` / `pathLine` | `Option` | | The line that would be added to `path_profile`. | ## `AppCliInstallRequest` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `modify_path` / `modifyPath` | `bool` | | Also add the bin dir to PATH (edits `path_profile`). | ## `AppLaunchInput` record Returned by [`app_launch_input_from_json`](#app_launch_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `last_seen` / `lastSeen` | `Option` | | `AppSettings.last_seen_version` (`None`: never recorded). | | `version` | `String` | | This version (`0.2.0-staging.6`). | | `build` | `String` | | This build (`0.2.0.123`; may be empty). | | `onboarded` | `bool` | | The first run finished (a record-less launch after it is an update from a build that kept no record; before it, a fresh install). | ## `AppLaunchPlan` record Returned by [`app_about_after_launch`](#app_about_after_launch). | Field | Type | Default | Description | | --- | --- | --- | --- | | `refresh` | `bool` | | This launch follows an update: refresh the agents' skills and MCP entries (`cua agents update`) and restart the app's own daemon. | | `save` | `Option` | | Save as `AppSettings.last_seen_version` (`None`: unchanged). | ## `AppAboutLinkId` enum **Swift** ```swift .acknowledgements .privacy .terms .issue ``` **Rust** ```rust AppAboutLinkId::Acknowledgements AppAboutLinkId::Privacy AppAboutLinkId::Terms AppAboutLinkId::Issue ``` | Variant | Description | | --- | --- | | `Acknowledgements` | The third-party notices the app bundles. | | `Privacy` | The privacy policy. | | `Terms` | The terms of service. | | `Issue` | A new issue. | ## `AppInstallMethod` enum **Swift** ```swift .symlink .copy ``` **Rust** ```rust AppInstallMethod::Symlink AppInstallMethod::Copy ``` | Variant | Description | | --- | --- | | `Symlink` | A symlink into the app bundle: app updates update the CLI too. | | `Copy` | A copy of the bundled binary. | ## `app_about_after_launch` What this launch does (refresh after an update; the version to record). **Swift** ```swift func appAboutAfterLaunch(input: AppLaunchInput) -> AppLaunchPlan ``` **Rust** ```rust pub fn app_about_after_launch(input: AppLaunchInput) -> AppLaunchPlan ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppLaunchInput`](#applaunchinput-record) | required | **Returns** [`AppLaunchPlan`](#applaunchplan-record) ## `app_about_allowed_channels` The Sparkle channels an updater on `channel` may install from. **Swift** ```swift func appAboutAllowedChannels(channel: AppUpdateChannel) -> [String] ``` **Rust** ```rust pub fn app_about_allowed_channels(channel: AppUpdateChannel) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `channel` | [`AppUpdateChannel`](#appupdatechannel-enum) | required | **Returns** `Vec` ## `app_about_input_from_json` The About input from JSON (parity flows). **Swift** ```swift func appAboutInputFromJson(json: String) throws -> AppAboutInput ``` **Rust** ```rust pub fn app_about_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppAboutInput`](#appaboutinput-record) · **Raises** [`CuaError`]() ## `app_about_refresh_notice` The one notice after a failed refresh (`None` when it worked). **Swift** ```swift func appAboutRefreshNotice(report: AppRefreshReport) -> String? ``` **Rust** ```rust pub fn app_about_refresh_notice(report: AppRefreshReport) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `report` | [`AppRefreshReport`](#apprefreshreport-record) | required | **Returns** `Option` ## `app_about_restart_daemon` Whether the running daemon is this app's own (restart it after an update). **Swift** ```swift func appAboutRestartDaemon(check: AppDaemonCheck) -> Bool ``` **Rust** ```rust pub fn app_about_restart_daemon(check: AppDaemonCheck) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `check` | [`AppDaemonCheck`](#appdaemoncheck-record) | required | **Returns** `bool` ## `app_about_view` Settings, About: name, version, links, copyright and the update controls. **Swift** ```swift func appAboutView(input: AppAboutInput) -> AppAboutView ``` **Rust** ```rust pub fn app_about_view(input: AppAboutInput) -> AppAboutView ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppAboutInput`](#appaboutinput-record) | required | **Returns** [`AppAboutView`](#appaboutview-record) ## `app_launch_input_from_json` A launch from JSON (parity flows). **Swift** ```swift func appLaunchInputFromJson(json: String) throws -> AppLaunchInput ``` **Rust** ```rust pub fn app_launch_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppLaunchInput`](#applaunchinput-record) · **Raises** [`CuaError`]() --- # Cua Cloud billing The Billing row in Settings and the out-of-credit notice on a refused cloud Space. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. The apps take no payment details: the website handles credit and cards. `app_billing_status` turns `Fleet.billing_status` into the core's `AppBillingStatus`, which Settings shows as the credit left and "Manage billing" (the website billing page). A cloud Space that Fleet refuses for want of credit (`CuaError::CloudCreditExhausted`) shows `AppCreditNotice`: one line and "Add credit", which opens the same page. Local Spaces never depend on it. ## `AppCreditNotice` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `text` | `String` | | `OUT_OF_CREDIT`. | | `button` | `String` | | `ADD_CREDIT`. | | `url` | `String` | | The website billing page. | ## `AppBillingCard` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `brand` | `String` | | `visa`, `mastercard`, ... | | `last4` | `String` | | Last four digits. | ## `AppBillingCredit` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `balance_usd_cents` / `balanceUsdCents` | `i64` | | What is left, US cents. | ## `AppBillingStatus` record Returned by [`app_billing_status`](#app_billing_status). | Field | Type | Default | Description | | --- | --- | --- | --- | | `billing_enabled` / `billingEnabled` | `bool` | | Billing is on for this account (off: Settings shows no Billing row). | | `card` | [`Option`](#appbillingcard-record) | | The saved default card. | | `credit` | [`Option`](#appbillingcredit-record) | | The account's credit, when it has any. | | `billing_url` / `billingUrl` | `Option` | | The website billing page (credit, cards, plans). | ## `app_billing_status` The SDK's billing status as the app core takes it (Settings' Billing row). **Swift** ```swift func appBillingStatus(status: FleetBillingStatus) -> AppBillingStatus ``` **Rust** ```rust pub fn app_billing_status(status: FleetBillingStatus) -> AppBillingStatus ``` | Parameter | Type | Default | | --- | --- | --- | | `status` | [`FleetBillingStatus`]() | required | **Returns** [`AppBillingStatus`](#appbillingstatus-record) --- # This machine The This machine roster entry, its page and the host setup form. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_with_this_machine` puts This machine first in the roster. `app_host_panel` derives its page from `app_host_state`; `app_host_form_reduce` and `app_host_form_view` drive the host setup form, whose `request` goes to `app_host_setup` (Swift: `host.setupRequest(...)`). ## `app_with_this_machine` Adds the "This machine" entry to a roster, first. **Swift** ```swift func appWithThisMachine(spaces: [AppSpace], status: AppHostSummaryInput?, nowMs: Int64) -> [AppSpace] ``` **Rust** ```rust pub fn app_with_this_machine(spaces: Vec, status: Option, now_ms: i64) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | [`Vec`]() | required | | `status` | [`Option`]() | required | | `now_ms` | `i64` | required | **Returns** [`Vec`]() ## `AppPermissionRow` record Returned by [`app_host_permission_rows`](#app_host_permission_rows). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Id. | | `title` | `String` | | "Screen Recording". | | `help` | `String` | | Tooltip (what to turn on). | | `settings_url` / `settingsUrl` | `Option` | | The pane (only a `x-apple.systempreferences:` URL is opened). | ## `AppHostAccess` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `at_ms` / `atMs` | `i64` | | Epoch ms. | | `via` | `String` | | How the caller authenticated: `relay`, `viewer` or `token`. | | `who` | `String` | | Who (a relay-verified account, a viewer, or the token holder). | | `what` | `String` | | What they used (a service name such as `ProcessService`, or `MCP`). | ## `AppHostAccessRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `text` | `String` | | "Ada · Files". | | `at_ms` / `atMs` | `i64` | | Epoch ms (each shell shows it relative, natively). | ## `AppHostAccount` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `Option` | | The account id (the session's `sub`). | | `email` | `Option` | | Its email. | | `display` | `Option` | | What the app shows for it (a display name, else the email). | ## `AppHostAction` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | [`AppHostActionId`](#apphostactionid-enum) | | What it does. | | `label` | `String` | | Label. | | `destructive` | `bool` | | Drawn as destructive. | | `confirm` | [`Option`](#apphostconfirm-record) | | Ask this first; the action runs only when the user confirms. | | `enabled` | `bool` | | Can be pressed now (Resume sharing is not while nothing is on to share). | | `help` | `Option` | | The tooltip, when it says something the label does not. | ## `AppHostClient` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Account id. | | `email` | `Option` | | Email. | | `name` | `Option` | | Display name. | | `streams` | `Option` | | Open streams. | ## `AppHostConfirm` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | The question. | | `message` | `String` | | What happens. | | `confirm_label` / `confirmLabel` | `String` | | The confirming button. | | `cancel_label` / `cancelLabel` | `String` | | The other button. | ## `AppHostFormChoice` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `desktop` or `spare`. | | `label` | `String` | | Label. | ## `AppHostFormField` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `name`, `allow`, `direct`, `listen`, `relay`. | | `label` | `String` | | Label. | | `placeholder` | `Option` | | Placeholder (text fields). | | `value` | `String` | | Text. | | `toggle` | `bool` | | The switch (`direct` only). | | `on` | `bool` | | On (`direct` only). | | `invalid` | `bool` | | Drawn as invalid. | | `advanced` | `bool` | | Under Advanced. | | `choices` | [`Vec`](#apphostformchoice-record) | | A choice of one (`profile` only): `value` is the chosen id. | ## `AppHostFormState` record Returned by [`app_host_form_initial`](#app_host_form_initial), [`app_host_form_reduce`](#app_host_form_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Display name. | | `allow` | `String` | | "Also allow" (relay). | | `advanced` | `bool` | | Advanced is open. | | `direct` | `bool` | | Direct instead of the relay (Advanced). | | `listen` | `String` | | Direct listen address. | | `relay_url` / `relayUrl` | `String` | | Relay URL (Advanced). | | `busy` | `bool` | | Setup is running. | | `error` | `Option` | | The last failure. | | `spare` | `bool` | | A spare machine: do not share its desktop, provide Spaces (relay only). | ## `AppHostFormView` record Returned by [`app_host_form_view`](#app_host_form_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Set up this machine". | | `lede` | `String` | | One line: what setting up does. | | `fields` | [`Vec`](#apphostformfield-record) | | Visible fields in order (Advanced ones only while it is open). | | `advanced_label` / `advancedLabel` | `String` | | "Advanced". | | `advanced_open` / `advancedOpen` | `bool` | | Advanced is open. | | `back_label` / `backLabel` | `String` | | "Back". | | `submit_label` / `submitLabel` | `String` | | "Set up for access" or "Setting up…". | | `can_submit` / `canSubmit` | `bool` | | The submit button is enabled. | | `busy` | `bool` | | Setup is running. | | `error` | `Option` | | The last failure, one line. | | `request` | [`Option`](#apphostsetuprequest-record) | | What host setup receives (validated), when it can submit. | ## `AppHostLimit` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `resource` | `String` | | `spaces` (every Space it provides) or `macos_vms` (macOS VMs on that Mac). | | `used` | `u32` | | In use now. | | `limit` | `u32` | | The limit (0: none). | | `reason` | `String` | | Why the limit exists, for people ("Apple's macOS license allows two macOS VMs per Mac"). | ## `AppHostPanelView` record Returned by [`app_host_panel`](#app_host_panel). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "This machine". | | `summary` | `String` | | One line: how it is shared, or that it is not. | | `notice` | `Option` | | Why relay sharing is paused, one line over the page (signed out, or signed in to another account). | | `notice_action` / `noticeAction` | [`Option`](#apphostaction-record) | | The notice's button ("Sign In"). | | `configured` | `bool` | | Set up for access. | | `facts` | [`Vec`]() | | Name, Access, Service. | | `clients_title` / `clientsTitle` | `Option` | | "Connected now" (configured only). | | `clients` | `Vec` | | One line per client. | | `clients_empty` / `clientsEmpty` | `Option` | | "Nobody" when nobody is connected. | | `recent_title` / `recentTitle` | `Option` | | "Recent access" (configured, when the log has any). | | `recent` | [`Vec`](#apphostaccessrow-record) | | Who reached this machine recently, newest first: the first `LOG_PREVIEW_ROWS` of `Self.recent_all`. | | `recent_more` / `recentMore` | `Option` | | "Show All…" when `recent_all` has more than `recent` shows. | | `recent_all` / `recentAll` | [`Vec`](#apphostaccessrow-record) | | Every recent access (connections, refusals, other people's thumbnails), repeats collapsed: the "Show All" sheet. | | `recent_with_background` / `recentWithBackground` | [`Vec`](#apphostaccessrow-record) | | The same with background probes and the owner's thumbnails too (the sheet's "Include background activity"). | | `access_warning` / `accessWarning` | `Option` | | Set when the access log does not verify. | | `toggles` | [`Vec`](#apphosttoggle-record) | | The two settings (configured only). | | `limits` | `Option` | | The limits, one line ("Up to 4 Spaces \u{b7} 2 macOS VMs"), while providing Spaces. | | `provided_title` / `providedTitle` | `Option` | | "Spaces for your devices", while providing Spaces. | | `provided` | [`Vec`](#apphostaccessrow-record) | | One row per Space this machine provides. | | `provided_empty` / `providedEmpty` | `Option` | | "None yet" when it provides none. | | `activity_title` / `activityTitle` | `Option` | | "Spaces activity" (when the audit has any). | | `activity` | [`Vec`](#apphostaccessrow-record) | | Remote creates, deletes and refusals, newest first: the first `LOG_PREVIEW_ROWS` of `Self.activity_all`. | | `activity_more` / `activityMore` | `Option` | | "Show All…" when `activity_all` has more than `activity` shows. | | `activity_all` / `activityAll` | [`Vec`](#apphostaccessrow-record) | | The whole Spaces activity, repeats collapsed. | | `activity_warning` / `activityWarning` | `Option` | | Set when the Spaces audit does not verify. | | `permissions_title` / `permissionsTitle` | `Option` | | Heading over the permission rows, when any is left to grant. | | `permissions` | [`Vec`](#apppermissionrow-record) | | Panes still to grant. | | `open_settings_label` / `openSettingsLabel` | `String` | | The permission rows' button. | | `actions` | [`Vec`](#apphostaction-record) | | Buttons, primary first. | | `intro` | `Option` | | Before setup: what this machine is and why set it up. | | `setup_choices` / `setupChoices` | [`Vec`](#apphostsetupchoice-record) | | Before setup: the ways to set it up, shown inline. | ## `AppHostPermissionInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `screen-recording`, `accessibility`. | | `title` | `String` | | "Screen Recording". | | `settings_url` / `settingsUrl` | `Option` | | The pane to open. | | `instructions` | `Option` | | What to turn on there. | | `granted` | `bool` | | Already granted. | ## `AppHostProvidedSpace` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `relay_machine` / `relayMachine` | `String` | | The relay machine it is reached as. | | `local_space` / `localSpace` | `String` | | The Space on this machine (`local:`). | | `name` | `String` | | Display name. | | `image` | `String` | | The image. | | `os` | `String` | | `linux`, `macos`. | | `kind` | `String` | | `container` or `vm`. | | `created_by` / `createdBy` | `String` | | Who created it. | | `created_at_ms` / `createdAtMs` | `i64` | | Epoch ms. | ## `AppHostSettingChange` record Returned by [`app_host_setting_change`](#app_host_setting_change). | Field | Type | Default | Description | | --- | --- | --- | --- | | `share_desktop` / `shareDesktop` | `Option` | | Share this desktop. | | `provide_spaces` / `provideSpaces` | `Option` | | Provide Spaces. | ## `AppHostSetupChoice` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | The form's profile: `desktop` or `spare`. | | `label` | `String` | | Label. | | `button_label` / `buttonLabel` | `String` | | The button that opens the form with this choice. | ## `AppHostSetupRequest` record Returned by [`app_host_validate_setup`](#app_host_validate_setup). | Field | Type | Default | Description | | --- | --- | --- | --- | | `mode` | `String` | | `relay` (default) or `direct`. | | `relay_url` / `relayUrl` | `Option` | | Relay URL; `None` uses the configured default (https://relay.cua.ai). | | `direct` | `Option` | | Direct `ip:port` (Advanced). | | `name` | `Option` | | Display name (default: the host name). | | `allow` | `Option>` | | Accounts (ids or emails) allowed besides the owner. | | `profile` | `Option` | | What the machine is for: `desktop` (share this desktop; the default) or `spare` (only run Spaces for your other devices). | | `share_desktop` / `shareDesktop` | `Option` | | Share this machine's own desktop (overrides the profile). | | `provide_spaces` / `provideSpaces` | `Option` | | Create Spaces for your other devices (overrides the profile). | ## `AppHostSpacesAudit` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `at_ms` / `atMs` | `i64` | | Epoch ms. | | `action` | `String` | | `create`, `delete`, `refused`, `failed`, `config`. | | `who` | `String` | | Who (an account, or `local`). | | `space` | `String` | | The relay machine or Space. | | `detail` | `String` | | Detail. | ## `AppHostState` record Returned by [`app_host_state`](#app_host_state), [`app_host_state_from_json`](#app_host_state_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `configured` | `bool` | | Set up for access. | | `mode` | `Option` | | `relay` or `direct`. | | `relay_url` / `relayUrl` | `Option` | | Relay URL. | | `direct_url` / `directUrl` | `Option` | | Direct URL. | | `name` | `Option` | | Display name. | | `sharing` | `bool` | | Accepting clients. | | `service_installed` / `serviceInstalled` | `bool` | | Service installed. | | `service_running` / `serviceRunning` | `bool` | | Service running. | | `service_kind` / `serviceKind` | `String` | | `launchd`, `systemd`, `windows-task`, `process`. | | `online` | `Option` | | Online at the relay. | | `clients` | [`Vec`](#apphostclient-record) | | Connected now. | | `permissions` | [`Vec`](#apphostpermissioninput-record) | | Panes to grant. | | `error` | `Option` | | Needs attention. | | `recent_access` / `recentAccess` | [`Vec`](#apphostaccess-record) | | Who reached this machine recently, newest first. | | `access_log_error` / `accessLogError` | `Option` | | Set when the access log does not verify (edited or truncated). | | `share_desktop` / `shareDesktop` | `bool` | | This machine's own desktop is a Space. | | `provide_spaces` / `provideSpaces` | `bool` | | This machine creates Spaces for your other devices. | | `max_spaces` / `maxSpaces` | `u32` | | Provided Spaces at once (0: no limit). | | `max_macos_vms` / `maxMacosVms` | `u32` | | macOS VMs at once (at most two; 0 off a Mac). | | `provided_spaces` / `providedSpaces` | [`Vec`](#apphostprovidedspace-record) | | The Spaces this machine provides now. | | `spaces_audit` / `spacesAudit` | [`Vec`](#apphostspacesaudit-record) | | Remote creates, deletes and refusals, and settings changes, newest first. | | `spaces_audit_error` / `spacesAuditError` | `Option` | | Set when the Spaces audit does not verify. | | `paused_signed_out` / `pausedSignedOut` | `bool` | | Relay sharing is paused until the owner signs in again (the host left the relay; its setup stays). | | `owner` | `Option` | | The account this machine is registered to (relay mode): its id. | | `owner_email` / `ownerEmail` | `Option` | | The owner's email, when the relay gave one. | | `account` | [`Option`](#apphostaccount-record) | | Who is signed in to Cua in this app (`None`: nobody, or the shell does not say). The shell fills it; the host does not know. | ## `AppHostToggle` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `desktop` or `spaces`. | | `label` | `String` | | Label. | | `help` | `String` | | One line under it. | | `on` | `bool` | | On now. | | `enabled` | `bool` | | Can be flipped (the last setting on cannot be turned off; Spaces need the relay). | | `action` | [`AppHostActionId`](#apphostactionid-enum) | | What flipping it runs. | ## `AppHostAccountStep` enum **Swift** ```swift .keep .pause .resume ``` **Rust** ```rust AppHostAccountStep::Keep AppHostAccountStep::Pause AppHostAccountStep::Resume ``` | Variant | Description | | --- | --- | | `Keep` | Nothing to do. | | `Pause` | Pause relay sharing (signed out, or signed in to another account). | | `Resume` | Resume the paused relay sharing (its owner is signed in again). | ## `AppHostActionId` enum **Swift** ```swift .setUp .stopSharing .resumeSharing .remove .shareDesktop .hideDesktop .provideSpaces .stopProvidingSpaces .signIn ``` **Rust** ```rust AppHostActionId::SetUp AppHostActionId::StopSharing AppHostActionId::ResumeSharing AppHostActionId::Remove AppHostActionId::ShareDesktop AppHostActionId::HideDesktop AppHostActionId::ProvideSpaces AppHostActionId::StopProvidingSpaces AppHostActionId::SignIn ``` | Variant | Description | | --- | --- | | `SetUp` | Open the host setup form. | | `StopSharing` | Cut clients and refuse new ones. | | `ResumeSharing` | Accept clients again. | | `Remove` | Unregister and uninstall the service. | | `ShareDesktop` | Share this machine's own desktop. | | `HideDesktop` | Stop sharing this machine's own desktop (Spaces keep running). | | `ProvideSpaces` | Create Spaces for your other devices. | | `StopProvidingSpaces` | Stop creating Spaces for your other devices. | | `SignIn` | Sign in to Cua (relay sharing paused while signed out resumes). | ## `AppHostFormAction` enum **Swift** ```swift .setName(name: String) .setAllow(allow: String) .toggleAdvanced .setDirect(on: Bool) .setListen(listen: String) .setRelayUrl(url: String) .setProfile(profile: String) .submit .failed(error: String) ``` **Rust** ```rust AppHostFormAction::SetName { name: String } AppHostFormAction::SetAllow { allow: String } AppHostFormAction::ToggleAdvanced AppHostFormAction::SetDirect { on: bool } AppHostFormAction::SetListen { listen: String } AppHostFormAction::SetRelayUrl { url: String } AppHostFormAction::SetProfile { profile: String } AppHostFormAction::Submit AppHostFormAction::Failed { error: String } ``` | Variant | Description | | --- | --- | | `SetName` | Name typed. Fields: `name: String` | | `SetAllow` | "Also allow" typed. Fields: `allow: String` | | `ToggleAdvanced` | Advanced opened or closed. | | `SetDirect` | "Direct connection" switched. Fields: `on: bool` | | `SetListen` | Listen address typed. Fields: `listen: String` | | `SetRelayUrl` | Relay URL typed. Fields: `url: String` | | `SetProfile` | What the machine is for: `desktop` or `spare`. Fields: `profile: String` | | `Submit` | "Set up for access" pressed (ignored unless it can submit). | | `Failed` | Setup failed. Fields: `error: String` | ## `app_host_account_key` The account id (else email) host resume names the signed-in account by. **Swift** ```swift func appHostAccountKey(account: AppHostAccount) -> String ``` **Rust** ```rust pub fn app_host_account_key(account: AppHostAccount) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `account` | [`AppHostAccount`](#apphostaccount-record) | required | **Returns** `String` ## `app_host_account_step` What to do with the host when the signed-in account changes (`None`: nobody is signed in; never pass `None` for "could not tell"). **Swift** ```swift func appHostAccountStep(state: AppHostState, account: AppHostAccount?) -> AppHostAccountStep ``` **Rust** ```rust pub fn app_host_account_step(state: AppHostState, account: Option) -> AppHostAccountStep ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppHostState`](#apphoststate-record) | required | | `account` | [`Option`](#apphostaccount-record) | required | **Returns** [`AppHostAccountStep`](#apphostaccountstep-enum) ## `app_host_arch` This machine's CPU architecture as the Spaces apps pass it to the core (`arm64`, `amd64`). **Swift** ```swift func appHostArch() -> String ``` **Rust** ```rust pub fn app_host_arch() -> String ``` **Returns** `String` ## `app_host_form_action_from_json` A host setup form action from JSON (parity flows). **Swift** ```swift func appHostFormActionFromJson(json: String) throws -> AppHostFormAction ``` **Rust** ```rust pub fn app_host_form_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppHostFormAction`](#apphostformaction-enum) · **Raises** [`CuaError`]() ## `app_host_form_initial` The host setup form's first state. **Swift** ```swift func appHostFormInitial() -> AppHostFormState ``` **Rust** ```rust pub fn app_host_form_initial() -> AppHostFormState ``` **Returns** [`AppHostFormState`](#apphostformstate-record) ## `app_host_form_reduce` Advances the host setup form. **Swift** ```swift func appHostFormReduce(state: AppHostFormState, action: AppHostFormAction) -> AppHostFormState ``` **Rust** ```rust pub fn app_host_form_reduce(state: AppHostFormState, action: AppHostFormAction) -> AppHostFormState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppHostFormState`](#apphostformstate-record) | required | | `action` | [`AppHostFormAction`](#apphostformaction-enum) | required | **Returns** [`AppHostFormState`](#apphostformstate-record) ## `app_host_form_view` The host setup form as drawn (`identity`: the signed-in account). **Swift** ```swift func appHostFormView(state: AppHostFormState, identity: String?) -> AppHostFormView ``` **Rust** ```rust pub fn app_host_form_view(state: AppHostFormState, identity: Option) -> AppHostFormView ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppHostFormState`](#apphostformstate-record) | required | | `identity` | `Option` | required | **Returns** [`AppHostFormView`](#apphostformview-record) ## `app_host_looks_like_listen` Whether a direct-mode address is `ip:port` or `host:port`. **Swift** ```swift func appHostLooksLikeListen(value: String) -> Bool ``` **Rust** ```rust pub fn app_host_looks_like_listen(value: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `value` | `String` | required | **Returns** `bool` ## `app_host_panel` The "This machine" page (`None` while the host status loads). **Swift** ```swift func appHostPanel(state: AppHostState?) -> AppHostPanelView ``` **Rust** ```rust pub fn app_host_panel(state: Option) -> AppHostPanelView ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`Option`](#apphoststate-record) | required | **Returns** [`AppHostPanelView`](#apphostpanelview-record) ## `app_host_parse_allow_list` Comma or space separated accounts, as the host setup form's "Also allow". **Swift** ```swift func appHostParseAllowList(value: String) -> [String] ``` **Rust** ```rust pub fn app_host_parse_allow_list(value: String) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `value` | `String` | required | **Returns** `Vec` ## `app_host_permission_rows` Permission panes still to grant, as rows. **Swift** ```swift func appHostPermissionRows(permissions: [AppHostPermissionInput]) -> [AppPermissionRow] ``` **Rust** ```rust pub fn app_host_permission_rows(permissions: Vec) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `permissions` | [`Vec`](#apphostpermissioninput-record) | required | **Returns** [`Vec`](#apppermissionrow-record) ## `app_host_setting_change` The settings change a host toggle action runs (`None` for the other actions): pass it to `Host.configure`. **Swift** ```swift func appHostSettingChange(id: AppHostActionId) -> AppHostSettingChange? ``` **Rust** ```rust pub fn app_host_setting_change(id: AppHostActionId) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | [`AppHostActionId`](#apphostactionid-enum) | required | **Returns** [`Option`](#apphostsettingchange-record) ## `app_host_setup` Host setup from the first-run form, as both Spaces apps send it: validated by the app core (`app_host_validate_setup`), then the SDK's `Host.setup` (the Swift app calls it as `host.setupRequest(...)`). **Swift** ```swift func appHostSetup(host: Host, request: AppHostSetupRequest, accountToken: String?) async throws -> HostStatus ``` **Rust** ```rust pub async fn app_host_setup(host: Arc, request: AppHostSetupRequest, account_token: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `host` | [`Host`]() | required | | `request` | [`AppHostSetupRequest`](#apphostsetuprequest-record) | required | | `account_token` | `Option` | required | **Returns** [`HostStatus`]() · **Async** · **Raises** [`CuaError`]() ## `app_host_state` The cua SDK's host status as the app core reads it. **Swift** ```swift func appHostState(status: HostStatus) -> AppHostState ``` **Rust** ```rust pub fn app_host_state(status: HostStatus) -> AppHostState ``` | Parameter | Type | Default | | --- | --- | --- | | `status` | [`HostStatus`]() | required | **Returns** [`AppHostState`](#apphoststate-record) ## `app_host_state_from_json` A host state from JSON (fixtures, parity flows). **Swift** ```swift func appHostStateFromJson(json: String) throws -> AppHostState ``` **Rust** ```rust pub fn app_host_state_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppHostState`](#apphoststate-record) · **Raises** [`CuaError`]() ## `app_host_summary_input` What the sidebar row's summary reads from a host state. **Swift** ```swift func appHostSummaryInput(state: AppHostState) -> AppHostSummaryInput ``` **Rust** ```rust pub fn app_host_summary_input(state: AppHostState) -> AppHostSummaryInput ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppHostState`](#apphoststate-record) | required | **Returns** [`AppHostSummaryInput`]() ## `app_host_validate_setup` Normalises and checks a host setup request (the Tauri app's `host_setup` runs the same check before any service is touched). **Swift** ```swift func appHostValidateSetup(request: AppHostSetupRequest) throws -> AppHostSetupRequest ``` **Rust** ```rust pub fn app_host_validate_setup(request: AppHostSetupRequest) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `request` | [`AppHostSetupRequest`](#apphostsetuprequest-record) | required | **Returns** [`AppHostSetupRequest`](#apphostsetuprequest-record) · **Raises** [`CuaError`]() --- # Window, menu bar and Settings The window chrome, a Space's toolbar and sections, the menu bar menu, Settings and the coding agents' rows. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_main_chrome`, `app_menu_bar` and `app_settings_page` give the window chrome, the menu bar menu and the Settings page. `app_space_detail_copy` holds the words of a Space's sections; the agent functions turn `AgentSetup` results into Settings rows. `app_login_item_launch_plan` decides when the apps turn on launch at login by themselves. ## `app_display_name` "brave-otter" as "Brave Otter". **Swift** ```swift func appDisplayName(name: String) -> String ``` **Rust** ```rust pub fn app_display_name(name: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** `String` ## `app_display_path` Home-relative path (`~/x`). **Swift** ```swift func appDisplayPath(path: String, home: String?) -> String ``` **Rust** ```rust pub fn app_display_path(path: String, home: Option) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `path` | `String` | required | | `home` | `Option` | required | **Returns** `String` ## `app_format_bytes` "12 MB". **Swift** ```swift func appFormatBytes(bytes: UInt64) -> String ``` **Rust** ```rust pub fn app_format_bytes(bytes: u64) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `bytes` | `u64` | required | **Returns** `String` ## `AppFact` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | Label. | | `value` | `String` | | Value. | | `copy` | [`Option`](#appfactcopy-record) | | A copy button after the value (the Space's identifier and image). | | `help` | `Option` | | The tooltip: the full value (and an image's digest). | | `warning` | [`Option`]() | | A warning symbol after the value, with its tooltip: a local Space emulating another architecture. | ## `AppFactCopy` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `text` | `String` | | What is copied (the full value). | | `symbol` | `String` | | SF Symbol (`doc.on.doc`). | | `help` | `String` | | Tooltip and accessibility label ("Copy"). | | `done_symbol` / `doneSymbol` | `String` | | The symbol while confirming (`checkmark`). | | `done_help` / `doneHelp` | `String` | | The tooltip while confirming ("Copied"). | | `confirm_ms` / `confirmMs` | `u32` | | How long the confirmation shows, in milliseconds. | ## `app_delete_failed_text` The banner when Delete fails. **Swift** ```swift func appDeleteFailedText(name: String, error: String) -> String ``` **Rust** ```rust pub fn app_delete_failed_text(name: String, error: String) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | | `error` | `String` | required | **Returns** `String` ## `AppDetailCopy` record Returned by [`app_space_detail_copy`](#app_space_detail_copy). | Field | Type | Default | Description | | --- | --- | --- | --- | | `stream_loading` / `streamLoading` | `String` | | "Looking for this Space's windows…" | | `stream_empty` / `streamEmpty` | `String` | | "No open windows in this Space yet." | | `stream_failed` / `streamFailed` | `String` | | When the window list could not be read. | | `stream_no_match` / `streamNoMatch` | `String` | | When a filter hides every window. | | `agents_loading` / `agentsLoading` | `String` | | "Looking for this Space's agents…" | | `agents_empty` / `agentsEmpty` | `String` | | "No agents have been started in this Space." | | `agents_failed` / `agentsFailed` | `String` | | When the runs could not be read (not the same as none). | | `agents_no_match` / `agentsNoMatch` | `String` | | When a filter hides every run. | | `drop_caption` / `dropCaption` | `String` | | The drop well's caption. | | `send_file` / `sendFile` | `String` | | "Send file…" | | `teleport_app` / `teleportApp` | `String` | | "Teleport an app…" | | `teleport_symbol` / `teleportSymbol` | `String` | | The teleport icon (`TELEPORT_SYMBOL`). | | `teleport_symbol_active` / `teleportSymbolActive` | `String` | | The teleport icon while a drag is over a drop target. | ## `app_space_detail_copy` The words of a Space's sections (Stream, Agents, Teleport). **Swift** ```swift func appSpaceDetailCopy() -> AppDetailCopy ``` **Rust** ```rust pub fn app_space_detail_copy() -> AppDetailCopy ``` **Returns** [`AppDetailCopy`](#appdetailcopy-record) ## `AppDetailAction` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | [`AppDetailActionId`](#appdetailactionid-enum) | | What it does. | | `label` | `String` | | Label. | | `symbol` | `Option` | | SF Symbol (icon buttons). | | `help` | `String` | | Tooltip. | | `enabled` | `bool` | | Enabled. | | `destructive` | `bool` | | Drawn as destructive. | | `primary` | `bool` | | The prominent one. | | `busy` | `bool` | | Its action runs: a spinner in place of the icon. | ## `AppDetailActionId` enum **Swift** ```swift .teleport .pip .share .power .delete .open .cancel ``` **Rust** ```rust AppDetailActionId::Teleport AppDetailActionId::Pip AppDetailActionId::Share AppDetailActionId::Power AppDetailActionId::Delete AppDetailActionId::Open AppDetailActionId::Cancel ``` | Variant | Description | | --- | --- | | `Teleport` | "Teleport an app…" into the Space. | | `Pip` | The desktop in a picture-in-picture panel. | | `Share` | "Share": let another cua.ai account watch or edit the Space. | | `Power` | Turn it off or on (the power button, next to Delete). | | `Delete` | Delete (or remove from the list). | | `Open` | The desktop in its own window. | | `Cancel` | Stop a create that is still running and remove what it made. | ## `AppDeleteConfirm` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Delete Brave Otter?" | | `message` | `String` | | What it means, one line. | | `confirm_label` / `confirmLabel` | `String` | | The destructive button. | | `confirm_enabled` / `confirmEnabled` | `bool` | | The destructive button can be pressed (a Space in your cloud that another device created cannot be deleted from here). | | `disabled_reason` / `disabledReason` | `Option` | | Why not, beside the disabled button. | | `remove_label` / `removeLabel` | `Option` | | For a Space in your cloud: "Remove from List", which forgets it and keeps it running there (`remove_space`). | | `cancel_label` / `cancelLabel` | `String` | | "Keep" (or "Cancel" beside Remove from List). | ## `AppSentFileInfo` record Returned by [`app_sent_file`](#app_sent_file), [`app_sent_files_from_json`](#app_sent_files_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | File name. | | `dest` | `String` | | Where it landed in the Space. | | `bytes` | `u64` | | Bytes. | ## `app_sent_file` A `send_file` report as the files the drop well names. **Swift** ```swift func appSentFile(report: SpaceSendFileReport) -> AppSentFileInfo ``` **Rust** ```rust pub fn app_sent_file(report: SpaceSendFileReport) -> AppSentFileInfo ``` | Parameter | Type | Default | | --- | --- | --- | | `report` | [`SpaceSendFileReport`]() | required | **Returns** [`AppSentFileInfo`](#appsentfileinfo-record) ## `app_sent_files_from_json` Sent files from JSON (parity flows). **Swift** ```swift func appSentFilesFromJson(json: String) throws -> [AppSentFileInfo] ``` **Rust** ```rust pub fn app_sent_files_from_json(json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`Vec`](#appsentfileinfo-record) · **Raises** [`CuaError`]() ## `app_drop_sending_text` The drop well's line while files go. **Swift** ```swift func appDropSendingText(paths: [String]) -> String ``` **Rust** ```rust pub fn app_drop_sending_text(paths: Vec) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `paths` | `Vec` | required | **Returns** `String` ## `app_drop_sent_text` The drop well's line once they landed. **Swift** ```swift func appDropSentText(files: [AppSentFileInfo]) -> String ``` **Rust** ```rust pub fn app_drop_sent_text(files: Vec) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `files` | [`Vec`](#appsentfileinfo-record) | required | **Returns** `String` ## `AppSignInPhase` enum **Swift** ```swift .idle .starting .waiting(userCode: String?) .failed(message: String) ``` **Rust** ```rust AppSignInPhase::Idle AppSignInPhase::Starting AppSignInPhase::Waiting { user_code: Option } AppSignInPhase::Failed { message: String } ``` | Variant | Description | | --- | --- | | `Idle` | Nothing started. | | `Starting` | Asking for a code. | | `Waiting` | The browser is open. Fields: `user_code: Option` | | `Failed` | It failed. Fields: `message: String` | ## `AppTelemetryInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | `bool` | | Usage data is shared. | | `locked_by` / `lockedBy` | `Option` | | The environment decides (`DO_NOT_TRACK`, `CUA_TELEMETRY`, CI): who. | ## `app_menu` The menu bar item's menu: the Spaces the user can open (the notch's count), Cua Volume's sync state next to it and its conflicts, then the actions. **Swift** ```swift func appMenu(input: AppMenuInput) -> [AppMenuItem] ``` **Rust** ```rust pub fn app_menu(input: AppMenuInput) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppMenuInput`](#appmenuinput-record) | required | **Returns** [`Vec`](#appmenuitem-record) ## `AppMenuInput` record Returned by [`app_menu_input_from_json`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `spaces` | [`Vec`]() | | The roster (with "This machine"). | | `keyvault` | `Option` | | Live Keyvault sign-ins (`crate.keyvault.view.sharing_label`). | | `sync` | [`Option`]() | | Cua Volume's `volume_sync_status`, when read. | | `now_ms` / `nowMs` | `u64` | | Now (Unix ms): a feed that has not answered for `OFFLINE_AFTER_MS` is offline. | | `backend` | `Option` | | `volume_storage`'s backend (`fs`, `s3`), when read: this machine's store with no other device has nothing to sync. | | `experiments` | [`Option`]() | | Settings, Experiments: Cua Volume's sync state and conflicts only with Cua Volume on (none: shown, for callers that predate experiments). Off hides them; the volume keeps syncing. | ## `app_openable_count` The Spaces the user can open (the notch tab and the menu bar item). **Swift** ```swift func appOpenableCount(spaces: [AppSpace]) -> UInt32 ``` **Rust** ```rust pub fn app_openable_count(spaces: Vec) -> u32 ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | [`Vec`]() | required | **Returns** `u32` ## `AppAgentSettingsRow` record Returned by [`app_agent_settings_rows`](#app_agent_settings_rows). | Field | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `String` | | Registry id. | | `name` | `String` | | Display name. | | `installed` | `bool` | | Installed. | | `configured` | `bool` | | cua skills and the cua MCP server are both in place (where supported). | | `detail` | `String` | | One line for the row. | | `skills_installed` / `skillsInstalled` | `u32` | | Bundled skills present. | | `skills_total` / `skillsTotal` | `u32` | | Bundled skills. | | `mcp_config` / `mcpConfig` | `Option` | | MCP config file. | | `skills_dir` / `skillsDir` | `Option` | | Skills directory. | ## `AppAgentSetupOutcomeInput` record Returned by [`app_agent_setup_outcome`](#app_agent_setup_outcome), [`app_agent_setup_outcomes_from_json`](#app_agent_setup_outcomes_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `agents` | `Vec` | | Agents it served. | | `target` | `String` | | `skill` or `mcp`. | | `item` | `String` | | Skill or server name. | | `change` | `String` | | `created`, `updated`, `unchanged`, `removed`, `skipped`, `failed`. | | `detail` | `String` | | Explanation. | ## `AppAgentSetupStatus` record Returned by [`app_agent_setup_status`](#app_agent_setup_status), [`app_agent_setup_statuses_from_json`](#app_agent_setup_statuses_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Registry id (`claude-code`, `codex`, ...). | | `name` | `String` | | Display name. | | `installed` | `bool` | | Looks installed. | | `skills_dir` / `skillsDir` | `Option` | | User skills directory, when it supports skills. | | `mcp_config` / `mcpConfig` | `Option` | | MCP config file, when it supports MCP. | | `cua_configured` / `cuaConfigured` | `bool` | | The cua MCP server is configured. | | `skills_installed` / `skillsInstalled` | `Vec` | | Bundled skills present. | | `skills_outdated` / `skillsOutdated` | `Vec` | | Installed skills that differ from the bundled copy. | | `error` | `Option` | | Problem reading its config. | ## `AppAgentSetupSummary` record Returned by [`app_agent_setup_summary`](#app_agent_setup_summary). | Field | Type | Default | Description | | --- | --- | --- | --- | | `text` | `String` | | "cua MCP server configured, 3 skills installed". | | `failed` | `Vec` | | "name: why" per failed target. | | `line` | `String` | | "Claude Code: done" / "Claude Code: failed". | ## `AppChromeInput` record Returned by [`app_chrome_input_from_json`](#app_chrome_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `identity` | `Option` | | The signed-in account. | | `cloud_configured` / `cloudConfigured` | `bool` | | Cua Cloud works (signed in, or client credentials). | | `can_sign_in` / `canSignIn` | `bool` | | Signing in is possible here (the native shell). | | `experiments` | [`Option`]() | | Settings, Experiments: the Volume page only with Cua Volume on (none: every page, for callers that predate experiments). | ## `AppLoginItemInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `status` | [`AppLoginItemStatus`](#apploginitemstatus-enum) | | What the system reports. | | `busy` | `bool` | | A change is running. | | `error` | `Option` | | Why the last change failed. | | `provides_spaces` / `providesSpaces` | `bool` | | This machine provides Spaces to your other devices (host setup's "A spare machine for Spaces"). | | `runs_agents` / `runsAgents` | `bool` | | This machine runs persistent agents. | ## `AppLoginItemPlan` record Returned by [`app_login_item_launch_plan`](#app_login_item_launch_plan). | Field | Type | Default | Description | | --- | --- | --- | --- | | `register` | `bool` | | Register the app as a login item now. | | `record` | `Option` | | Save this as the user's choice (`AppSettings::launch_at_login`). | ## `AppMainChrome` record Returned by [`app_main_chrome`](#app_main_chrome). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | Window title when nothing is selected. | | `new_space_label` / `newSpaceLabel` | `String` | | "New Space". | | `new_space_shortcut` / `newSpaceShortcut` | `String` | | "⌘N". | | `search_placeholder` / `searchPlaceholder` | `String` | | The sidebar search's placeholder. | | `keyvault_title` / `keyvaultTitle` | `String` | | The Keyvault section's title. | | `account` | `String` | | The account line: the identity, "API key" or "Not signed in". | | `sign_in_label` / `signInLabel` | `Option` | | "Sign in", when signing in is offered. | | `settings_label` / `settingsLabel` | `String` | | "Settings". | | `settings_shortcut` / `settingsShortcut` | `String` | | "⌘,". | | `empty_title` / `emptyTitle` | `String` | | With no Spaces: the heading. | | `empty_action` / `emptyAction` | `String` | | With no Spaces: the button. | | `volume_label` / `volumeLabel` | `Option` | | The sidebar's Volume page entry ("Volume"); none while the Cua Volume experiment is off (the page and its route are hidden; a mounted volume stays mounted). | ## `AppMenuItem` record Returned by [`app_menu`](#app_menu), [`app_menu_bar`](#app_menu_bar), [`app_menu_bar_with_keyvault`](#app_menu_bar_with_keyvault). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | [`AppMenuItemId`](#appmenuitemid-enum) | | What it does. | | `label` | `String` | | Label (empty for a separator). | | `shortcut` | `Option` | | "⌘,", "⌘Q". | | `enabled` | `bool` | | Enabled. | ## `AppSettingsInput` record Returned by [`app_settings_input_from_json`](#app_settings_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `identity` | `Option` | | The signed-in account. | | `api_key_client` / `apiKeyClient` | `Option` | | Cua Cloud through client credentials (an API key): its client id. | | `sign_in` / `signIn` | [`AppSignInPhase`](#appsigninphase-enum) | | The sign-in in progress. | | `can_sign_out` / `canSignOut` | `bool` | | Signing out is possible (a user session). | | `menu_bar` / `menuBar` | `bool` | | The Spaces tab is hidden from the notch (the menu bar item is the entry point). | | `default_location` / `defaultLocation` | [`AppLocation`]() | | Where New Space starts. | | `location_locked_by` / `locationLockedBy` | `Option` | | The environment sets the default location: which variable. | | `telemetry` | [`Option`](#apptelemetryinput-record) | | The telemetry switch (none until read). | | `agents` | [`Option>`](#appagentsettingsrow-record) | | Coding agents (none while detecting). | | `agents_busy` / `agentsBusy` | `bool` | | "Configure all" is running. | | `agents_pending` / `agentsPending` | `Vec` | | Agents with an action running. | | `billing` | [`Option`]() | | The account's Cua Cloud billing, once read (signed in). | | `login_item` / `loginItem` | [`Option`](#apploginiteminput-record) | | Launch at login, once the system was asked (none: no rows). | | `experiments` | [`AppExperiments`]() | | Settings, Experiments (what the page mentions follows them). | | `keyvault_auto_wipe` / `keyvaultAutoWipe` | `Option` | | The Keyvault's auto-wipe, once the broker told it (none: no Keyvault section). | | `keyvault_unlock_prompt` / `keyvaultUnlockPrompt` | `Option` | | The unlock prompt shows (false once the user chose "Never ask again"); none until the broker told it. | | `keyvault_site_icons` / `keyvaultSiteIcons` | `bool` | | Load site icons from Google when the browser had none (`AppSettings.keyvault_site_icons`). | | `keyvault_protection` / `keyvaultProtection` | [`Vec`](#appfact-record) | | The Keyvault's protection facts (Touch ID, the daemon's signature), shown in the section. | | `auto_connect` / `autoConnect` | `Option` | | "Connect to the desktop automatically" (none: no row; a shell without the preview cover leaves it out). | | `lume_source` / `lumeSource` | `Option` | | Which Lume macOS Spaces run on (`runtime.lume`: `auto`, `builtin`, `system`); none: no Runtimes section. | | `linux_source` / `linuxSource` | `Option` | | Which engine local Linux Spaces run on (`runtime.linux`: `auto`, `builtin`, `system`); none: no Linux row. | ## `AppSettingsOption` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `show`, `hide`, `local`, `cloud`, `on`, `off`. | | `label` | `String` | | Label. | | `active` | `bool` | | Chosen. | ## `AppSettingsPage` record Returned by [`app_experiments_page`](), [`app_settings_page`](#app_settings_page), [`app_settings_with_storage`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | "Settings". | | `sections` | [`Vec`](#appsettingssection-record) | | Account, General, Privacy (once read), AI agents. | ## `AppSettingsRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Stable id (`account`, `sign-in`, `notch`, `agent:codex`, ...). | | `kind` | [`AppSettingsRowKind`](#appsettingsrowkind-enum) | | How it draws. | | `label` | `String` | | Label (or the note's text). | | `value` | `Option` | | Muted value. | | `button` | `Option` | | A button's label. | | `options` | [`Vec`](#appsettingsoption-record) | | A choice's options. | | `enabled` | `bool` | | The button or choice can be used. | | `help` | `Option` | | Tooltip. | | `link_label` / `linkLabel` | `Option` | | A note's link: label. | | `link_url` / `linkUrl` | `Option` | | A note's link: URL (or what a Text row's button opens). | | `placeholder` | `Option` | | A field's placeholder. | ## `AppSettingsSection` record Returned by [`app_storage_section`](). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `account`, `general`, `privacy`, `agents`. | | `title` | `String` | | "Account", "General", "Privacy", "AI agents". | | `button` | `Option` | | A button in the header. | | `button_enabled` / `buttonEnabled` | `bool` | | That button can be used. | | `button_help` / `buttonHelp` | `Option` | | Its tooltip. | | `rows` | [`Vec`](#appsettingsrow-record) | | Rows. | ## `AppLoginItemStatus` enum **Swift** ```swift .enabled .notRegistered .requiresApproval .notFound ``` **Rust** ```rust AppLoginItemStatus::Enabled AppLoginItemStatus::NotRegistered AppLoginItemStatus::RequiresApproval AppLoginItemStatus::NotFound ``` | Variant | Description | | --- | --- | | `Enabled` | Registered and allowed: the app opens at login. | | `NotRegistered` | Not registered. | | `RequiresApproval` | Registered, waiting for the user to allow it in System Settings, Login Items. | | `NotFound` | The system cannot find the app as a login item. | ## `AppMenuItemId` enum **Swift** ```swift .status .separator .open .newSpace .settings .quit .volumeConflicts ``` **Rust** ```rust AppMenuItemId::Status AppMenuItemId::Separator AppMenuItemId::Open AppMenuItemId::NewSpace AppMenuItemId::Settings AppMenuItemId::Quit AppMenuItemId::VolumeConflicts ``` | Variant | Description | | --- | --- | | `Status` | The status line (disabled). | | `Separator` | A separator. | | `Open` | Show the main window. | | `NewSpace` | The New Space sheet. | | `Settings` | Settings. | | `Quit` | Quit. | | `VolumeConflicts` | Cua Volume's conflicts: opens the Volume page. | ## `AppSettingsRowKind` enum **Swift** ```swift .text .choice .note .error .field .secret .prompt .link .toggle ``` **Rust** ```rust AppSettingsRowKind::Text AppSettingsRowKind::Choice AppSettingsRowKind::Note AppSettingsRowKind::Error AppSettingsRowKind::Field AppSettingsRowKind::Secret AppSettingsRowKind::Prompt AppSettingsRowKind::Link AppSettingsRowKind::Toggle ``` | Variant | Description | | --- | --- | | `Text` | Label, value and an optional button. | | `Choice` | Label and a segmented choice. | | `Note` | A muted line (with an optional link). | | `Error` | An error line. | | `Field` | Label and a text field (`value` is its text). | | `Secret` | Label and a secure text field (`value` is its text). | | `Prompt` | A label over read-only text to copy (`value`), with a Copy button. | | `Link` | A small link-style button (the label). | | `Toggle` | Label and a switch: `options` are `on` and `off`, the active one is its state (a choice drawn as a switch). | ## `app_agent_settings_rows` Settings rows for the coding agents (`total`: bundled skills). **Swift** ```swift func appAgentSettingsRows(statuses: [AppAgentSetupStatus], total: UInt32) -> [AppAgentSettingsRow] ``` **Rust** ```rust pub fn app_agent_settings_rows(statuses: Vec, total: u32) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `statuses` | [`Vec`](#appagentsetupstatus-record) | required | | `total` | `u32` | required | **Returns** [`Vec`](#appagentsettingsrow-record) ## `app_agent_setup_outcome` The SDK's setup outcome as the app core reads it. **Swift** ```swift func appAgentSetupOutcome(outcome: AgentSetupOutcome) -> AppAgentSetupOutcomeInput ``` **Rust** ```rust pub fn app_agent_setup_outcome(outcome: AgentSetupOutcome) -> AppAgentSetupOutcomeInput ``` | Parameter | Type | Default | | --- | --- | --- | | `outcome` | [`AgentSetupOutcome`]() | required | **Returns** [`AppAgentSetupOutcomeInput`](#appagentsetupoutcomeinput-record) ## `app_agent_setup_outcomes_from_json` Agent setup outcomes from JSON (parity flows). **Swift** ```swift func appAgentSetupOutcomesFromJson(json: String) throws -> [AppAgentSetupOutcomeInput] ``` **Rust** ```rust pub fn app_agent_setup_outcomes_from_json(json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`Vec`](#appagentsetupoutcomeinput-record) · **Raises** [`CuaError`]() ## `app_agent_setup_status` The SDK's agent detection as the app core reads it. **Swift** ```swift func appAgentSetupStatus(info: AgentInfo) -> AppAgentSetupStatus ``` **Rust** ```rust pub fn app_agent_setup_status(info: AgentInfo) -> AppAgentSetupStatus ``` | Parameter | Type | Default | | --- | --- | --- | | `info` | [`AgentInfo`]() | required | **Returns** [`AppAgentSetupStatus`](#appagentsetupstatus-record) ## `app_agent_setup_statuses_from_json` Coding agents' setup states from JSON (fixtures, parity flows). **Swift** ```swift func appAgentSetupStatusesFromJson(json: String) throws -> [AppAgentSetupStatus] ``` **Rust** ```rust pub fn app_agent_setup_statuses_from_json(json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`Vec`](#appagentsetupstatus-record) · **Raises** [`CuaError`]() ## `app_agent_setup_summary` What an agent setup did for one agent. **Swift** ```swift func appAgentSetupSummary(outcomes: [AppAgentSetupOutcomeInput], agent: String, name: String) -> AppAgentSetupSummary ``` **Rust** ```rust pub fn app_agent_setup_summary(outcomes: Vec, agent: String, name: String) -> AppAgentSetupSummary ``` | Parameter | Type | Default | | --- | --- | --- | | `outcomes` | [`Vec`](#appagentsetupoutcomeinput-record) | required | | `agent` | `String` | required | | `name` | `String` | required | **Returns** [`AppAgentSetupSummary`](#appagentsetupsummary-record) ## `app_chrome_input_from_json` The chrome's input from JSON (parity flows). **Swift** ```swift func appChromeInputFromJson(json: String) throws -> AppChromeInput ``` **Rust** ```rust pub fn app_chrome_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppChromeInput`](#appchromeinput-record) · **Raises** [`CuaError`]() ## `app_login_item_launch_plan` What the app does about launch at login at launch (the user's choice, whether the first run finished, whether this machine provides Spaces or runs persistent agents, and what the system reports). **Swift** ```swift func appLoginItemLaunchPlan(choice: Bool?, onboarded: Bool, serves: Bool, status: AppLoginItemStatus) -> AppLoginItemPlan ``` **Rust** ```rust pub fn app_login_item_launch_plan(choice: Option, onboarded: bool, serves: bool, status: AppLoginItemStatus) -> AppLoginItemPlan ``` | Parameter | Type | Default | | --- | --- | --- | | `choice` | `Option` | required | | `onboarded` | `bool` | required | | `serves` | `bool` | required | | `status` | [`AppLoginItemStatus`](#apploginitemstatus-enum) | required | **Returns** [`AppLoginItemPlan`](#apploginitemplan-record) ## `app_main_chrome` The main window's chrome (account line, New Space, empty state). **Swift** ```swift func appMainChrome(input: AppChromeInput) -> AppMainChrome ``` **Rust** ```rust pub fn app_main_chrome(input: AppChromeInput) -> AppMainChrome ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppChromeInput`](#appchromeinput-record) | required | **Returns** [`AppMainChrome`](#appmainchrome-record) ## `app_menu_bar` The menu bar item's menu. **Swift** ```swift func appMenuBar(spaces: UInt32) -> [AppMenuItem] ``` **Rust** ```rust pub fn app_menu_bar(spaces: u32) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | `u32` | required | **Returns** [`Vec`](#appmenuitem-record) ## `app_menu_bar_with_keyvault` The menu bar item's menu with the live Keyvault sharing line (`kv_sharing_label`) when there is one. **Swift** ```swift func appMenuBarWithKeyvault(spaces: UInt32, keyvault: String?) -> [AppMenuItem] ``` **Rust** ```rust pub fn app_menu_bar_with_keyvault(spaces: u32, keyvault: Option) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `spaces` | `u32` | required | | `keyvault` | `Option` | required | **Returns** [`Vec`](#appmenuitem-record) ## `app_settings_input_from_json` The Settings page's input from JSON (parity flows). **Swift** ```swift func appSettingsInputFromJson(json: String) throws -> AppSettingsInput ``` **Rust** ```rust pub fn app_settings_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppSettingsInput`](#appsettingsinput-record) · **Raises** [`CuaError`]() ## `app_settings_page` The Settings page. **Swift** ```swift func appSettingsPage(input: AppSettingsInput) -> AppSettingsPage ``` **Rust** ```rust pub fn app_settings_page(input: AppSettingsInput) -> AppSettingsPage ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppSettingsInput`](#appsettingsinput-record) | required | **Returns** [`AppSettingsPage`](#appsettingspage-record) --- # Devices Settings > Devices: this device's enrollment, the account's devices, Recent Access, and the enroll and approval sheets. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_devices_view` derives the Devices page from `app_devices_input` (a `Devices.snapshot`): this device's enrollment, the account's devices with their buttons, approval prompts that ask for presence, and Recent Access in words. `app_enroll_reduce` / `app_enroll_view` drive the enroll sheet (sign in again, or a one-time code approved elsewhere); `app_approve_reduce` / `app_approve_view` drive the approval sheet, whose `request` goes to `Devices.approve` after Touch ID or the login password. `app_machine_access_notice` says why a device that is signed in but not enrolled cannot open the account's machines, for their greyed-out Connect. ## `AppAuditInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `ts` | `u64` | | Unix seconds. | | `kind` | `String` | | Kind. | | `device` | `Option` | | Acting device. | | `machine` | `Option` | | Machine id. | | `subject` | `Option` | | Other party. | | `detail` | `Option` | | Detail. | ## `AppActivityRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `ts` | `u64` | | Unix seconds. | | `text` | `String` | | The line. | | `notable` | `bool` | | Access by another account or an unenrolled device. | ## `AppBannerTone` enum **Swift** ```swift .info .warning .critical ``` **Rust** ```rust AppBannerTone::Info AppBannerTone::Warning AppBannerTone::Critical ``` | Variant | Description | | --- | --- | | `Info` | Informational. | | `Warning` | Needs the user soon. | | `Critical` | Blocks relay access now. | ## `AppThisDevice` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`AppEnrollmentKind`](#appenrollmentkind-enum) | | State. | | `title` | `String` | | "Enrolled until", "Grace ends", "Needs enrollment", ... | | `at` | `Option` | | The date after the title (Unix seconds); the shell formats it. | | `name` | `Option` | | The device's name, once registered. | | `action_label` / `actionLabel` | `Option` | | "Enroll…" when this device needs it. | ## `AppEnrollmentKind` enum **Swift** ```swift .enrolled .grace .needsEnrollment .waiting .due .revoked ``` **Rust** ```rust AppEnrollmentKind::Enrolled AppEnrollmentKind::Grace AppEnrollmentKind::NeedsEnrollment AppEnrollmentKind::Waiting AppEnrollmentKind::Due AppEnrollmentKind::Revoked ``` | Variant | Description | | --- | --- | | `Enrolled` | Enrolled: the relay lets it reach the account's machines. | | `Grace` | Not enrolled, still let through until the grace period ends. | | `NeedsEnrollment` | Not enrolled, and the relay refuses it. | | `Waiting` | Registered, waiting for approval from an enrolled device. | | `Due` | Enrollment ran out: one approval re-verifies it. | | `Revoked` | Revoked. | ## `AppApprovalPrompt` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `device_id` / `deviceId` | `String` | | Device id to approve. | | `text` | `String` | | The question. | | `requires_presence` / `requiresPresence` | `bool` | | Ask for Touch ID / the login password before approving. | | `name` | `String` | | The device's name. | | `expired` | `bool` | | Re-verification of a device whose enrollment ran out (no code). | | `notify_title` / `notifyTitle` | `String` | | The system notification's title. | | `notify_body` / `notifyBody` | `String` | | The system notification's body. | ## `AppMachineInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Machine id. | | `name` | `String` | | Display name. | | `confirmed` | `bool` | | Registered with an enrolled device's proof, or confirmed since (S5). `true` from a relay that predates the check. | ## `AppUnconfirmedMachine` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Machine id (the native side's `confirm_machine` call takes this). | | `title` | `String` | | Display name, or the id when it has none yet. | | `confirm` | [`AppDeviceConfirm`](#appdeviceconfirm-record) | | Asked before confirming (both shells show it as a native alert). | ## `app_machine_access_notice` Why this device cannot open the account's machines for its enrollment `kind` (`None` while it can). `pending_code` is the code it shows while it waits for approval. **Swift** ```swift func appMachineAccessNotice(kind: AppEnrollmentKind, pendingCode: String?) -> AppMachineAccessNotice? ``` **Rust** ```rust pub fn app_machine_access_notice(kind: AppEnrollmentKind, pending_code: Option) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `kind` | [`AppEnrollmentKind`](#appenrollmentkind-enum) | required | | `pending_code` | `Option` | required | **Returns** [`Option`](#appmachineaccessnotice-record) ## `AppMachineAccessNotice` record Returned by [`app_machine_access_notice`](#app_machine_access_notice). | Field | Type | Default | Description | | --- | --- | --- | --- | | `kind` | [`AppEnrollmentKind`](#appenrollmentkind-enum) | | This device's enrollment. | | `status` | `String` | | The machine's Status: "Not enrolled", "Waiting for approval", ... | | `text` | `String` | | The line above the disabled Connect. | | `action_label` / `actionLabel` | `String` | | The action's label; it opens the enroll sheet. | ## `AppApproveRequest` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `code` | `Option` | | The code (a new device). | | `device_id` / `deviceId` | `Option` | | The device id (re-verification). | ## `AppApproveSheetState` record Returned by [`app_approve_open`](#app_approve_open), [`app_approve_reduce`](#app_approve_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `device_id` / `deviceId` | `String` | | The device to approve. | | `name` | `String` | | Its name. | | `expired` | `bool` | | Re-verification (no code) rather than a new device. | | `code` | `String` | | The code as typed. | | `busy` | `bool` | | The approval runs (presence, then the relay). | | `error` | `Option` | | The last failure. | | `code_expired` / `codeExpired` | `bool` | | The last code the relay saw expired: offers approving the waiting device of this name by id instead (see `code_expired_fallback`). Typing a new code clears it. | ## `AppApproveSheetView` record Returned by [`app_approve_view`](#app_approve_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | Title. | | `message` | `String` | | What approving does. | | `needs_code` / `needsCode` | `bool` | | A code field shows. | | `code_label` / `codeLabel` | `String` | | The code field's label. | | `code_placeholder` / `codePlaceholder` | `String` | | Its placeholder. | | `code` | `String` | | The code, normalized (`K7QX-M2RP`). | | `can_approve` / `canApprove` | `bool` | | Approve is enabled. | | `approve_label` / `approveLabel` | `String` | | Approve. | | `deny_label` / `denyLabel` | `String` | | Deny (a new device; revokes it) or Not Now (re-verification). | | `deny_revokes` / `denyRevokes` | `bool` | | Deny revokes the device (one click, no confirmation). | | `presence_reason` / `presenceReason` | `String` | | The reason the presence prompt shows. | | `busy` | `bool` | | Working. | | `error` | `Option` | | A failure. | | `request` | [`Option`](#appapproverequest-record) | | What to send once presence is confirmed (`None` until ready). | ## `AppDeviceBanner` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `tone` | [`AppBannerTone`](#appbannertone-enum) | | Tone. | | `text` | `String` | | Text. | | `action` | [`Option`](#appdeviceaction-enum) | | Button, if any. | | `action_label` / `actionLabel` | `Option` | | The button's label. | ## `AppDeviceConfirm` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | The question. | | `message` | `String` | | What happens. | | `confirm_label` / `confirmLabel` | `String` | | The confirming button. | | `cancel_label` / `cancelLabel` | `String` | | The other button. | ## `AppDeviceInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `dev_…`. | | `name` | `String` | | Display name. | | `state` | `String` | | `pending`, `enrolled`, `expired` or `revoked`. | | `enrolled_until` / `enrolledUntil` | `Option` | | Re-verification due at (Unix seconds). | | `last_seen` / `lastSeen` | `Option` | | Last session (Unix seconds). | | `current` | `bool` | | This device. | | `platform` | `Option` | | Operating system the device reported (`macos`, `windows`, `linux`). | ## `AppDeviceRow` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Device id. | | `title` | `String` | | Name (`(this device)` suffix for the current one). | | `subtitle` | `String` | | State in words. | | `actions` | [`Vec`](#appdeviceaction-enum) | | Buttons. | | `name` | `String` | | The name as stored (Rename starts from it). | | `platform` | `String` | | Operating system in words (`macOS`), empty when unknown. | | `detail` | `String` | | Platform and state on one line. | | `last_seen` / `lastSeen` | `Option` | | Last session (Unix seconds); the shell says it relative to now. | | `current` | `bool` | | This device. | | `revoke_confirm` / `revokeConfirm` | [`Option`](#appdeviceconfirm-record) | | Asked before Revoke. | ## `AppDevicesInput` record Returned by [`app_devices_input`](#app_devices_input), [`app_devices_input_from_json`](#app_devices_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `devices` | [`Vec`](#appdeviceinput-record) | | The account's devices. | | `audit` | [`Vec`](#appauditinput-record) | | The account's audit events, newest last. | | `local_device_id` / `localDeviceId` | `Option` | | This device's id when it has a key (it may not be registered yet). | | `pending_code` / `pendingCode` | `Option` | | The one-time code this device shows while it waits for approval. | | `enforce_after` / `enforceAfter` | `Option` | | When the relay stops letting unenrolled devices through. | | `machine_names` / `machineNames` | `HashMap` | | Machine ids to display names. | | `machines` | [`Vec`](#appmachineinput-record) | | The account's relay machines, for the "New machine" confirm badge (S5). Empty on a relay that predates it, or when this device cannot list them, same as `machine_names`. | ## `AppDevicesLabels` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | Page title. | | `this_device` / `thisDevice` | `String` | | This device's section. | | `devices` | `String` | | The account's devices. | | `recent` | `String` | | The access log's section. | | `recent_empty` / `recentEmpty` | `String` | | When the access log is empty. | | `last_seen` / `lastSeen` | `String` | | "Last seen" before a relative time. | | `approve` | `String` | | Approve button (the row's, and the sheet's for a new device). | | `deny` | `String` | | The row's Deny: revokes a still-pending device in one click, or (a device due for re-verification) only dismisses it, like the sheet's Deny and Not Now. | | `rename` | `String` | | Rename button. | | `revoke` | `String` | | Revoke button. | | `rename_title` / `renameTitle` | `String` | | The rename prompt's title. | | `rename_confirm` / `renameConfirm` | `String` | | The rename prompt's confirming button. | | `cancel` | `String` | | Cancel. | | `signed_out` / `signedOut` | `String` | | Shown instead of the page while signed out. | | `new_machines` / `newMachines` | `String` | | The unconfirmed-machines section's heading. | | `confirm_machine` / `confirmMachine` | `String` | | Confirm button on a new machine. | ## `AppDevicesView` record Returned by [`app_devices_view`](#app_devices_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `banner` | [`Option`](#appdevicebanner-record) | | Banner, if something needs the user. | | `enrolled` | `bool` | | This device is enrolled (relay machines are reachable). | | `rows` | [`Vec`](#appdevicerow-record) | | Device rows, this device first. | | `approvals` | [`Vec`](#appapprovalprompt-record) | | Devices this device can approve. | | `activity` | [`Vec`](#appactivityrow-record) | | Access log, newest first. | | `this_device` / `thisDevice` | [`AppThisDevice`](#appthisdevice-record) | | This device's enrollment. | | `recent` | [`Vec`](#appactivityrow-record) | | "Recent access": which device (or account) opened which machine, newest first, at most `RECENT_ACCESS_LIMIT`. | | `unconfirmed_machines` / `unconfirmedMachines` | [`Vec`](#appunconfirmedmachine-record) | | Machines registered without an enrolled device's proof, still waiting to be confirmed (S5); empty most of the time. | | `labels` | [`AppDevicesLabels`](#appdeviceslabels-record) | | Words. | ## `AppEnrollOption` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `method` | [`AppEnrollMethod`](#appenrollmethod-enum) | | Method. | | `title` | `String` | | Title. | | `detail` | `String` | | One line under it. | ## `AppEnrollState` record Returned by [`app_enroll_initial`](#app_enroll_initial), [`app_enroll_reduce`](#app_enroll_reduce), [`app_enroll_state_from_json`](#app_enroll_state_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `phase` | [`AppEnrollPhase`](#appenrollphase-enum) | | Phase. | | `method` | [`Option`](#appenrollmethod-enum) | | The chosen method. | | `code` | `Option` | | The one-time code, while waiting. | | `error` | `Option` | | The last failure. | ## `AppEnrollView` record Returned by [`app_enroll_view`](#app_enroll_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | Title. | | `lede` | `String` | | One line under the title. | | `options` | [`Vec`](#appenrolloption-record) | | The methods (while choosing). | | `code` | `Option` | | The one-time code (while waiting). | | `code_help` / `codeHelp` | `Option` | | How to use the code. | | `status` | `Option` | | What is happening now. | | `error` | `Option` | | A failure. | | `busy` | `bool` | | Working (a spinner). | | `done` | `bool` | | Enrolled. | | `back_label` / `backLabel` | `Option` | | Back (after a failure). | | `close_label` / `closeLabel` | `String` | | Cancel, or Done once enrolled. | ## `AppApproveSheetAction` enum **Swift** ```swift .setCode(code: String) .submit .failed(error: String) ``` **Rust** ```rust AppApproveSheetAction::SetCode { code: String } AppApproveSheetAction::Submit AppApproveSheetAction::Failed { error: String } ``` | Variant | Description | | --- | --- | | `SetCode` | The code field changed. Fields: `code: String` | | `Submit` | Approve was pressed (the shell asks for presence, then the relay). | | `Failed` | Presence or the relay refused. Fields: `error: String` | ## `AppDeviceAction` enum **Swift** ```swift .enroll .approve .rename .revoke ``` **Rust** ```rust AppDeviceAction::Enroll AppDeviceAction::Approve AppDeviceAction::Rename AppDeviceAction::Revoke ``` | Variant | Description | | --- | --- | | `Enroll` | Enroll this device (the enroll sheet). | | `Approve` | Approve another device (after presence). | | `Rename` | Rename a device. | | `Revoke` | Revoke a device (after its confirmation). | ## `AppEnrollAction` enum **Swift** ```swift .choose(method: AppEnrollMethod) .signedIn .registered(enrolled: Bool, code: String?) .approved .failed(error: String) .back ``` **Rust** ```rust AppEnrollAction::Choose { method: AppEnrollMethod } AppEnrollAction::SignedIn AppEnrollAction::Registered { enrolled: bool, code: Option } AppEnrollAction::Approved AppEnrollAction::Failed { error: String } AppEnrollAction::Back ``` | Variant | Description | | --- | --- | | `Choose` | A method was chosen. Fields: `method: AppEnrollMethod` | | `SignedIn` | The interactive sign-in finished. | | `Registered` | The relay registered this device. Fields: `enrolled: bool`, `code: Option` | | `Approved` | An enrolled device approved this one. | | `Failed` | A step failed. Fields: `error: String` | | `Back` | Back to the choice. | ## `AppEnrollMethod` enum **Swift** ```swift .signIn .approve ``` **Rust** ```rust AppEnrollMethod::SignIn AppEnrollMethod::Approve ``` | Variant | Description | | --- | --- | | `SignIn` | A fresh interactive sign-in (enrolls this device at once). | | `Approve` | A one-time code approved from an enrolled device. | ## `AppEnrollPhase` enum **Swift** ```swift .choose .signingIn .registering .waiting .enrolled .failed ``` **Rust** ```rust AppEnrollPhase::Choose AppEnrollPhase::SigningIn AppEnrollPhase::Registering AppEnrollPhase::Waiting AppEnrollPhase::Enrolled AppEnrollPhase::Failed ``` | Variant | Description | | --- | --- | | `Choose` | Choosing a method. | | `SigningIn` | The shell runs an interactive sign-in. | | `Registering` | The shell registers this device with the relay. | | `Waiting` | The code shows; the shell polls until an enrolled device approves. | | `Enrolled` | Enrolled. | | `Failed` | Something failed; Back returns to the choice. | ## `app_approve_open` Opens the approval sheet for a prompt. **Swift** ```swift func appApproveOpen(prompt: AppApprovalPrompt) -> AppApproveSheetState ``` **Rust** ```rust pub fn app_approve_open(prompt: AppApprovalPrompt) -> AppApproveSheetState ``` | Parameter | Type | Default | | --- | --- | --- | | `prompt` | [`AppApprovalPrompt`](#appapprovalprompt-record) | required | **Returns** [`AppApproveSheetState`](#appapprovesheetstate-record) ## `app_approve_reduce` Advances the approval sheet. `devices` is the account's current devices (for approving the waiting device of a code's name by id once the code expires). **Swift** ```swift func appApproveReduce(state: AppApproveSheetState, action: AppApproveSheetAction, devices: [AppDeviceInput]) -> AppApproveSheetState ``` **Rust** ```rust pub fn app_approve_reduce(state: AppApproveSheetState, action: AppApproveSheetAction, devices: Vec) -> AppApproveSheetState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppApproveSheetState`](#appapprovesheetstate-record) | required | | `action` | [`AppApproveSheetAction`](#appapprovesheetaction-enum) | required | | `devices` | [`Vec`](#appdeviceinput-record) | required | **Returns** [`AppApproveSheetState`](#appapprovesheetstate-record) ## `app_approve_sheet_action_from_json` An approval sheet action from JSON (parity flows). **Swift** ```swift func appApproveSheetActionFromJson(json: String) throws -> AppApproveSheetAction ``` **Rust** ```rust pub fn app_approve_sheet_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppApproveSheetAction`](#appapprovesheetaction-enum) · **Raises** [`CuaError`]() ## `app_approve_view` The approval sheet as drawn. `devices` is the account's current devices (see `app_approve_reduce`). **Swift** ```swift func appApproveView(state: AppApproveSheetState, devices: [AppDeviceInput]) -> AppApproveSheetView ``` **Rust** ```rust pub fn app_approve_view(state: AppApproveSheetState, devices: Vec) -> AppApproveSheetView ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppApproveSheetState`](#appapprovesheetstate-record) | required | | `devices` | [`Vec`](#appdeviceinput-record) | required | **Returns** [`AppApproveSheetView`](#appapprovesheetview-record) ## `app_devices_clean_name` A device name as typed for Rename (`None` when empty). **Swift** ```swift func appDevicesCleanName(name: String) -> String? ``` **Rust** ```rust pub fn app_devices_clean_name(name: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `name` | `String` | required | **Returns** `Option` ## `app_devices_input` The SDK's devices snapshot as the Devices page reads it (`pending_code`: the code this device shows while it waits). **Swift** ```swift func appDevicesInput(snapshot: DevicesSnapshot, pendingCode: String?) -> AppDevicesInput ``` **Rust** ```rust pub fn app_devices_input(snapshot: DevicesSnapshot, pending_code: Option) -> AppDevicesInput ``` | Parameter | Type | Default | | --- | --- | --- | | `snapshot` | [`DevicesSnapshot`]() | required | | `pending_code` | `Option` | required | **Returns** [`AppDevicesInput`](#appdevicesinput-record) ## `app_devices_input_from_json` Devices page input from JSON (fixtures, parity flows). **Swift** ```swift func appDevicesInputFromJson(json: String) throws -> AppDevicesInput ``` **Rust** ```rust pub fn app_devices_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppDevicesInput`](#appdevicesinput-record) · **Raises** [`CuaError`]() ## `app_devices_view` The Devices page at `now` (Unix seconds). **Swift** ```swift func appDevicesView(input: AppDevicesInput, now: UInt64) -> AppDevicesView ``` **Rust** ```rust pub fn app_devices_view(input: AppDevicesInput, now: u64) -> AppDevicesView ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppDevicesInput`](#appdevicesinput-record) | required | | `now` | `u64` | required | **Returns** [`AppDevicesView`](#appdevicesview-record) ## `app_enroll_action_from_json` An enroll sheet action from JSON (parity flows). **Swift** ```swift func appEnrollActionFromJson(json: String) throws -> AppEnrollAction ``` **Rust** ```rust pub fn app_enroll_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppEnrollAction`](#appenrollaction-enum) · **Raises** [`CuaError`]() ## `app_enroll_initial` The enroll sheet's first state. **Swift** ```swift func appEnrollInitial() -> AppEnrollState ``` **Rust** ```rust pub fn app_enroll_initial() -> AppEnrollState ``` **Returns** [`AppEnrollState`](#appenrollstate-record) ## `app_enroll_reduce` Advances the enroll sheet. **Swift** ```swift func appEnrollReduce(state: AppEnrollState, action: AppEnrollAction) -> AppEnrollState ``` **Rust** ```rust pub fn app_enroll_reduce(state: AppEnrollState, action: AppEnrollAction) -> AppEnrollState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppEnrollState`](#appenrollstate-record) | required | | `action` | [`AppEnrollAction`](#appenrollaction-enum) | required | **Returns** [`AppEnrollState`](#appenrollstate-record) ## `app_enroll_state_from_json` The enroll sheet's state from JSON (fixtures, parity). **Swift** ```swift func appEnrollStateFromJson(json: String) throws -> AppEnrollState ``` **Rust** ```rust pub fn app_enroll_state_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppEnrollState`](#appenrollstate-record) · **Raises** [`CuaError`]() ## `app_enroll_view` The enroll sheet as drawn. **Swift** ```swift func appEnrollView(state: AppEnrollState) -> AppEnrollView ``` **Rust** ```rust pub fn app_enroll_view(state: AppEnrollState) -> AppEnrollView ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppEnrollState`](#appenrollstate-record) | required | **Returns** [`AppEnrollView`](#appenrollview-record) --- # Share sheet The Share sheet: who a Space is shared with (viewer or editor) and the share request to run. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_share_view` derives the Share sheet from `AppShareInput` (the Space and its `Space.shares`): one row per person with a role menu, the email field and why sharing is unavailable. `app_share_reduce` applies an action; its `request` goes to `Space.share` or `Space.unshare`, which ask for presence first. ## `AppRoleOption` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `viewer` or `editor`. | | `label` | `String` | | `Can view` or `Can edit`. | ## `AppShareEntryInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `who` | `String` | | Email or account id. | | `role` | `String` | | `viewer` or `editor`. | | `connected` | `bool` | | Connected through the relay now. | ## `AppShareInput` record Returned by [`app_share_input_from_json`](#app_share_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `space_id` / `spaceId` | `String` | | The Space's id. | | `space_name` / `spaceName` | `String` | | Its display name. | | `shares` | [`Vec`](#appshareentryinput-record) | | Who it is shared with. | | `signed_in` / `signedIn` | `bool` | | Signed in to cua.ai (sharing goes through the account's relay). | | `shareable` | `bool` | | The Space can be shared (a host, or a driver with `relay_attach`). | ## `AppShareRowView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `who` | `String` | | Email or account id. | | `role` | `String` | | `viewer` or `editor`. | | `connected` | `bool` | | Connected through the relay now (the shells show a status dot). | ## `AppShareSheetState` record Returned by [`app_share_initial`](#app_share_initial), [`app_share_reduce`](#app_share_reduce), [`app_share_state_from_json`](#app_share_state_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `who` | `String` | | The add field, as typed. | | `role` | `String` | | The role the add field shares with. | | `busy` | `bool` | | A command runs. | | `error` | `Option` | | The last failure. | | `request` | [`Option`](#appsharerequest-enum) | | The command waiting for the shell. | ## `AppShareSheetView` record Returned by [`app_share_view`](#app_share_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | `Share “studio”`. | | `rows` | [`Vec`](#appsharerowview-record) | | One person per line. | | `empty_text` / `emptyText` | `String` | | Shown with no rows. | | `who_placeholder` / `whoPlaceholder` | `String` | | The add field's placeholder. | | `who` | `String` | | The add field, as typed. | | `role` | `String` | | The add field's role. | | `roles` | [`Vec`](#approleoption-record) | | The role menu (for the add field and every row). | | `can_share` / `canShare` | `bool` | | Share is enabled. | | `share_label` / `shareLabel` | `String` | | Share. | | `remove_label` / `removeLabel` | `String` | | Remove (per row). | | `done_label` / `doneLabel` | `String` | | Done. | | `disabled_reason` / `disabledReason` | `Option` | | Why the sheet cannot share at all (not signed in, an old driver). | | `hint` | `Option` | | What the add field lacks (shown under it, only once typed). | | `busy` | `bool` | | A command runs. | | `error` | `Option` | | The last failure. | | `request` | [`Option`](#appsharerequest-enum) | | The command waiting for the shell. | ## `AppShareRequest` enum **Swift** ```swift .share(space: String, who: String, role: String) .unshare(space: String, who: String) ``` **Rust** ```rust AppShareRequest::Share { space: String, who: String, role: String } AppShareRequest::Unshare { space: String, who: String } ``` | Variant | Description | | --- | --- | | `Share` | `share_space` (also changes a role). Fields: `space: String`, `who: String`, `role: String` | | `Unshare` | `unshare_space`. Fields: `space: String`, `who: String` | ## `AppShareSheetAction` enum **Swift** ```swift .setWho(who: String) .setRole(role: String) .submit .changeRole(who: String, role: String) .remove(who: String) .done .failed(error: String) ``` **Rust** ```rust AppShareSheetAction::SetWho { who: String } AppShareSheetAction::SetRole { role: String } AppShareSheetAction::Submit AppShareSheetAction::ChangeRole { who: String, role: String } AppShareSheetAction::Remove { who: String } AppShareSheetAction::Done AppShareSheetAction::Failed { error: String } ``` | Variant | Description | | --- | --- | | `SetWho` | The add field changed. Fields: `who: String` | | `SetRole` | The add field's role menu changed. Fields: `role: String` | | `Submit` | Share was pressed. | | `ChangeRole` | A person's role menu changed. Fields: `who: String`, `role: String` | | `Remove` | A person's Remove was pressed. Fields: `who: String` | | `Done` | The command finished. | | `Failed` | The command failed (or presence was declined). Fields: `error: String` | ## `app_share_action_from_json` A Share sheet action from JSON (parity flows). **Swift** ```swift func appShareActionFromJson(json: String) throws -> AppShareSheetAction ``` **Rust** ```rust pub fn app_share_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppShareSheetAction`](#appsharesheetaction-enum) · **Raises** [`CuaError`]() ## `app_share_initial` A new Share sheet. **Swift** ```swift func appShareInitial() -> AppShareSheetState ``` **Rust** ```rust pub fn app_share_initial() -> AppShareSheetState ``` **Returns** [`AppShareSheetState`](#appsharesheetstate-record) ## `app_share_input_from_json` Share sheet input from JSON (fixtures, parity flows). **Swift** ```swift func appShareInputFromJson(json: String) throws -> AppShareInput ``` **Rust** ```rust pub fn app_share_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppShareInput`](#appshareinput-record) · **Raises** [`CuaError`]() ## `app_share_reduce` Advances the Share sheet. **Swift** ```swift func appShareReduce(input: AppShareInput, state: AppShareSheetState, action: AppShareSheetAction) -> AppShareSheetState ``` **Rust** ```rust pub fn app_share_reduce(input: AppShareInput, state: AppShareSheetState, action: AppShareSheetAction) -> AppShareSheetState ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppShareInput`](#appshareinput-record) | required | | `state` | [`AppShareSheetState`](#appsharesheetstate-record) | required | | `action` | [`AppShareSheetAction`](#appsharesheetaction-enum) | required | **Returns** [`AppShareSheetState`](#appsharesheetstate-record) ## `app_share_state_from_json` The Share sheet's state from JSON (fixtures, parity). **Swift** ```swift func appShareStateFromJson(json: String) throws -> AppShareSheetState ``` **Rust** ```rust pub fn app_share_state_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppShareSheetState`](#appsharesheetstate-record) · **Raises** [`CuaError`]() ## `app_share_view` The Share sheet as drawn. **Swift** ```swift func appShareView(input: AppShareInput, state: AppShareSheetState) -> AppShareSheetView ``` **Rust** ```rust pub fn app_share_view(input: AppShareInput, state: AppShareSheetState) -> AppShareSheetView ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppShareInput`](#appshareinput-record) | required | | `state` | [`AppShareSheetState`](#appsharesheetstate-record) | required | **Returns** [`AppShareSheetView`](#appsharesheetview-record) --- # Usage telemetry Which anonymous usage events a Spaces app step means, and sending them. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. Each `app_telemetry_*` function takes a reducer step (the state before it and the action) and returns the `AppTelemetrySignal`s it means: first-run pages, Space creates, Settings, Storage, sharing and enrollment, as fixed words only. `app_telemetry_record` sends them on the SDK's telemetry client, where every switch and the first-run notice apply. `app_telemetry_start` and `app_telemetry_welcome_left` hold the first events until the first run's Welcome page is left, with its usage-data switch deciding. What is sent: [Telemetry and privacy](). ## `AppTelemetrySignal` enum **Swift** ```swift .feature(feature: String) .step(step: String, ok: Bool) .onboardingPage(page: String, action: String, choice: String) .spaceWizard(action: String) .spaceCreate(location: String, guestOs: String, kind: String, outcome: String, failedPhase: String, stalled: Bool, elapsedMs: UInt64, gpu: Bool) .spaceCreateStarted(location: String, guestOs: String, kind: String, gpu: Bool) .volumeSetup(surface: String, storage: String, addToFinder: Bool, mountMethod: String, outcome: String) .share(action: String, role: String, outcome: String) .appUpdate(action: String, channel: String, trigger: String) .deviceEnroll(method: String, outcome: String) .experiment(action: String, experiment: String) .experimentsOn(experiments: [String]) ``` **Rust** ```rust AppTelemetrySignal::Feature { feature: String } AppTelemetrySignal::Step { step: String, ok: bool } AppTelemetrySignal::OnboardingPage { page: String, action: String, choice: String } AppTelemetrySignal::SpaceWizard { action: String } AppTelemetrySignal::SpaceCreate { location: String, guest_os: String, kind: String, outcome: String, failed_phase: String, stalled: bool, elapsed_ms: u64, gpu: bool } AppTelemetrySignal::SpaceCreateStarted { location: String, guest_os: String, kind: String, gpu: bool } AppTelemetrySignal::VolumeSetup { surface: String, storage: String, add_to_finder: bool, mount_method: String, outcome: String } AppTelemetrySignal::Share { action: String, role: String, outcome: String } AppTelemetrySignal::AppUpdate { action: String, channel: String, trigger: String } AppTelemetrySignal::DeviceEnroll { method: String, outcome: String } AppTelemetrySignal::Experiment { action: String, experiment: String } AppTelemetrySignal::ExperimentsOn { experiments: Vec } ``` | Variant | Description | | --- | --- | | `Feature` | A Spaces app feature was used (`cua_spaces_feature_used`). Fields: `feature: String` | | `Step` | An install or activation funnel step (`cua_onboarding_step`; `first_*` steps count once per install). Fields: `step: String`, `ok: bool` | | `OnboardingPage` | A first-run page was shown or left (`cua_onboarding_page`). Fields: `page: String`, `action: String`, `choice: String` | | `SpaceWizard` | The New Space panel (`cua_space_wizard`). Fields: `action: String` | | `SpaceCreate` | A Space create reached ready or failed (`cua_space_create`). Fields: `location: String`, `guest_os: String`, `kind: String`, `outcome: String`, `failed_phase: String`, `stalled: bool`, `elapsed_ms: u64`, `gpu: bool` | | `SpaceCreateStarted` | A Space create started (`cua_space_create_started`). Fields: `location: String`, `guest_os: String`, `kind: String`, `gpu: bool` | | `VolumeSetup` | A Cua Volume setting was saved (`cua_volume_setup`). Fields: `surface: String`, `storage: String`, `add_to_finder: bool`, `mount_method: String`, `outcome: String` | | `Share` | A Space was shared, unshared or its role changed (`cua_share`). Fields: `action: String`, `role: String`, `outcome: String` | | `AppUpdate` | An updater step (`cua_app_update`). Fields: `action: String`, `channel: String`, `trigger: String` | | `DeviceEnroll` | This device enrolled with the relay, or failed to (`cua_device_enroll`). Fields: `method: String`, `outcome: String` | | `Experiment` | An experiment was turned on or off in Settings, Experiments (`cua_experiment`). Fields: `action: String`, `experiment: String` | | `ExperimentsOn` | Which experiments are on now (no event of its own: the daily `cua_app_active` carries them as `experiments_on`). Fields: `experiments: Vec` | ## `app_telemetry_creates` The events one step of the Spaces being created means, at `now_ms` (call with the state before it). **Swift** ```swift func appTelemetryCreates(state: AppCreatesState, action: AppCreateAction, nowMs: Int64) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_creates(state: AppCreatesState, action: AppCreateAction, now_ms: i64) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppCreatesState`]() | required | | `action` | [`AppCreateAction`]() | required | | `now_ms` | `i64` | required | **Returns** [`Vec`](#apptelemetrysignal-enum) ## `app_telemetry_enroll` The events one step of the enroll sheet means. **Swift** ```swift func appTelemetryEnroll(state: AppEnrollState, action: AppEnrollAction) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_enroll(state: AppEnrollState, action: AppEnrollAction) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppEnrollState`]() | required | | `action` | [`AppEnrollAction`]() | required | **Returns** [`Vec`](#apptelemetrysignal-enum) ## `app_telemetry_feature` A Spaces app feature (a fixed name). **Swift** ```swift func appTelemetryFeature(feature: String) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_feature(feature: String) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `feature` | `String` | required | **Returns** [`Vec`](#apptelemetrysignal-enum) ## `app_telemetry_launched` The app started (`app_launched`). **Swift** ```swift func appTelemetryLaunched() -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_launched() -> Vec ``` **Returns** [`Vec`](#apptelemetrysignal-enum) ## `app_telemetry_onboarding` The events one first-run step means (call with the state before it). **Swift** ```swift func appTelemetryOnboarding(state: AppOnboardingState, action: AppOnboardingAction) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_onboarding(state: AppOnboardingState, action: AppOnboardingAction) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppOnboardingState`]() | required | | `action` | [`AppOnboardingAction`]() | required | **Returns** [`Vec`](#apptelemetrysignal-enum) ## `app_telemetry_onboarding_finished` "Start using Cua Spaces". **Swift** ```swift func appTelemetryOnboardingFinished(state: AppOnboardingState) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_onboarding_finished(state: AppOnboardingState) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppOnboardingState`]() | required | **Returns** [`Vec`](#apptelemetrysignal-enum) ## `app_telemetry_record` Records `signals` on the SDK's telemetry client (the switch, the notice and the schema apply) and marks the day active. Returns how many events were queued. **Swift** ```swift func appTelemetryRecord(signals: [AppTelemetrySignal]) -> UInt32 ``` **Rust** ```rust pub fn app_telemetry_record(signals: Vec) -> u32 ``` | Parameter | Type | Default | | --- | --- | --- | | `signals` | [`Vec`](#apptelemetrysignal-enum) | required | **Returns** `u32` ## `app_telemetry_share` The events one step of the Share sheet means. **Swift** ```swift func appTelemetryShare(input: AppShareInput, state: AppShareSheetState, action: AppShareSheetAction) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_share(input: AppShareInput, state: AppShareSheetState, action: AppShareSheetAction) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppShareInput`]() | required | | `state` | [`AppShareSheetState`]() | required | | `action` | [`AppShareSheetAction`]() | required | **Returns** [`Vec`](#apptelemetrysignal-enum) ## `app_telemetry_start` The app started: attributes this process's events to `spaces_app` at `version`, lets the app show the first-run notice itself (nothing is sent before it has been shown once), and records `app_launched` (on a first run, once the notice shows). Call once at launch, before the first `Cua`. Returns whether the notice was already shown here. **Swift** ```swift func appTelemetryStart(version: String) -> Bool ``` **Rust** ```rust pub fn app_telemetry_start(version: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `version` | `String` | required | **Returns** `bool` ## `app_telemetry_storage` The events one step of Settings, Storage means. **Swift** ```swift func appTelemetryStorage(input: AppStorageInput, state: AppStorageState, action: AppStorageAction) -> [AppTelemetrySignal] ``` **Rust** ```rust pub fn app_telemetry_storage(input: AppStorageInput, state: AppStorageState, action: AppStorageAction) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppStorageInput`]() | required | | `state` | [`AppStorageState`]() | required | | `action` | [`AppStorageAction`]() | required | **Returns** [`Vec`](#apptelemetrysignal-enum) ## `app_telemetry_welcome_left` The first run left Welcome with its usage-data switch at `on`: writes the machine's setting when it changed (the same as Settings and `cua telemetry off`), then records that the notice was shown. Nothing is queued or sent before; with the switch off, nothing after either. **Swift** ```swift func appTelemetryWelcomeLeft(on: Bool) throws ``` **Rust** ```rust pub fn app_telemetry_welcome_left(on: bool) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `on` | `bool` | required | **Raises** [`CuaError`]() --- # App core: Cua Volume storage, mount and sync Settings, Storage (this machine or an S3-compatible bucket, the Finder volume, the cache), the first run's Cua Volume page and the Drive page's devices and conflicts. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_storage_reduce` and `app_storage_section` drive Settings, Storage from the daemon's `volume_storage`, `volume_mount_status` and `volume_cache_stats` answers; its `request` names the tool to run (`volume_storage_set`, `volume_mount`, `volume_cache_set`, ...). S3 keys travel only in the `volume_storage_set` request. `AppDriveCard` is the first run's Cua Volume page (off by default), drawn over the `app_drive_mount_preview` miniature. `AppDriveSyncInput` feeds the Drive page's devices and conflicts. ## `AppConflictView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | The file (what Resolve sends). | | `text` | `String` | | | | `trailing` | `String` | | | | `reveal` | `Option` | | The copy to show in the file manager (mounted only). | | `open_label` / `openLabel` | `Option` | | "Open" when `reveal` is set. | | `resolve_label` / `resolveLabel` | `String` | | "Resolve". | ## `AppDriveCacheInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `size_bytes` / `sizeBytes` | `u64` | | | | `capacity_bytes` / `capacityBytes` | `u64` | | | ## `AppDriveCard` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `label` | `String` | | "Add Cua Volume to Finder" (Linux: "Mount Cua Volume"). | | `image_label` / `imageLabel` | `String` | | The miniature's spoken description. | | `checked` | `bool` | | Ticked. | | `enabled` | `bool` | | The checkbox can be used (the daemon can mount here). | | `busy` | `bool` | | Mounting or unmounting. | | `note` | `Option` | | One muted line under it (checking, why not, what to approve). | | `error` | `Option` | | Why mounting failed. | | `settings_label` / `settingsLabel` | `Option` | | "Open System Settings" while macOS waits for the extension's approval. | | `settings_url` / `settingsUrl` | `Option` | | Where it goes. | | `storage_title` / `storageTitle` | `Option` | | "Where your files live" (none until the daemon answered). | | `storage_options` / `storageOptions` | [`Vec`]() | | This Mac, Your S3 bucket, Set up later. | | `storage_rows` / `storageRows` | [`Vec`]() | | The bucket's fields, Test connection and its answer (Your S3 bucket), drawn like Settings' rows. | | `storage_note` / `storageNote` | `Option` | | One muted line for Set up later. | | `stored_in` / `storedIn` | `Option` | | This Mac: "Stored in ~/.cua/volume/data" (the daemon's path). | | `mounted_at` / `mountedAt` | `Option` | | The mounted volume: "In Finder at ~/Cua Volume". | | `stored_path` / `storedPath` | `Option` | | Full paths for tooltips and Show in Finder: stored, mounted. | | `mounted_path` / `mountedPath` | `Option` | | | | `can_continue` / `canContinue` | `bool` | | Continue can be pressed. | ## `AppDriveCheckInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `ok` | `bool` | | | | `reachable` | `bool` | | | | `authorized` | `bool` | | | | `versioning` | `bool` | | | | `detail` | `Option` | | | | `applied` | `bool` | | Saved, and the daemon switched to it. | ## `AppDriveConflictInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | The file (the drive's path). | | `conflict_path` / `conflictPath` | `String` | | The visible copy holding the losing write. | | `winner_device` / `winnerDevice` | `String` | | | | `loser_device` / `loserDevice` | `String` | | | | `ts_ms` / `tsMs` | `u64` | | | ## `AppDriveDeviceInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `name` | `String` | | | | `this_device` / `thisDevice` | `bool` | | | | `last_seen_ms` / `lastSeenMs` | `u64` | | Its last heartbeat or change (Unix ms). | | `last_change_ms` / `lastChangeMs` | `u64` | | | ## `AppDriveMountFrame` record Returned by [`app_drive_mount_preview_frame`](#app_drive_mount_preview_frame), [`app_drive_mount_preview_still`](#app_drive_mount_preview_still). | Field | Type | Default | Description | | --- | --- | --- | --- | | `volume` | `f64` | | The volume in the sidebar, 0 absent to 1 shown (and selected). | | `flight` | [`Option`]() | | The file in flight: its icon's top left (drawn above both windows). | | `arrived` | `Vec` | | Each file in the volume, 0 absent to 1 shown. | ## `AppDriveMountInput` record Returned by [`app_drive_mount_from_json`](#app_drive_mount_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | `bool` | | The user's opt-in. | | `state` | `String` | | `off`, `mounting`, `mounted`, `needs_approval`, `unsupported`, `error`. | | `method` | `String` | | `fskit`, `nfs`, `fuse`, `none`. | | `path` | `Option` | | The mount point when mounted. | | `volume_name` / `volumeName` | `String` | | "Cua Volume". | | `detail` | `Option` | | One sentence for `error`, `needs_approval`, `unsupported`. | | `settings_url` / `settingsUrl` | `Option` | | `needs_approval`: System Settings' file system extensions. | ## `AppDriveMountPreview` record Returned by [`app_drive_mount_preview`](#app_drive_mount_preview). | Field | Type | Default | Description | | --- | --- | --- | --- | | `width` | `f64` | | Stage width. | | `height` | `f64` | | Stage height. | | `loop_ms` / `loopMs` | `u32` | | One loop (ms). | | `space` | [`AppPreviewWindow`]() | | The Space (left). | | `finder` | [`AppPreviewWindow`]() | | The Finder window (right). | | `sidebar` | [`AppLogicalRect`]() | | The Finder window's sidebar (a shade darker than its content). | | `places` | [`Vec`]() | | The sidebar's other places (placeholder bars). | | `volume` | [`AppLogicalRect`]() | | The volume's row in the sidebar (the selection highlight). | | `volume_icon` / `volumeIcon` | [`AppLogicalRect`]() | | The volume's drive glyph. | | `volume_label` / `volumeLabel` | `String` | | The volume's name, drawn from `volume_label_x`, centred on the row. | | `volume_label_x` / `volumeLabelX` | `f64` | | Where the name starts. | | `font_size` / `fontSize` | `f64` | | The name's font size. | | `source_icons` / `sourceIcons` | [`Vec`]() | | The Space's files (icons), top to bottom. | | `source_labels` / `sourceLabels` | [`Vec`]() | | Their name bars. | | `dest_icons` / `destIcons` | [`Vec`]() | | Where each file lands in the volume (icons). | | `dest_labels` / `destLabels` | [`Vec`]() | | Their name bars. | ## `AppDriveS3Input` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `endpoint` | `Option` | | None: AWS. | | `region` | `String` | | | | `bucket` | `String` | | | | `root` | `String` | | A key prefix inside the bucket. | | `path_style` / `pathStyle` | `bool` | | | ## `AppDriveStorageInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `backend` | `String` | | `fs` or `s3` (`cloud` is never offered). | | `fs_path` / `fsPath` | `String` | | Where `fs` keeps the bytes. | | `s3` | [`Option`](#appdrives3input-record) | | | | `has_keys` / `hasKeys` | `bool` | | S3 keys are saved in the credential store. | ## `AppDriveStorageUpdate` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `backend` | `String` | | | | `s3` | [`Option`](#appdrives3input-record) | | | | `access_key_id` / `accessKeyId` | `Option` | | Both keys or neither. | | `secret_access_key` / `secretAccessKey` | `Option` | | | | `dry_run` / `dryRun` | `bool` | | Only check ("Test connection"). | ## `AppDriveSyncInput` record Returned by [`app_drive_sync_from_json`](#app_drive_sync_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `device_id` / `deviceId` | `String` | | | | `device_name` / `deviceName` | `String` | | | | `feed` | `String` | | `live`, `off` (one device on this machine's store), `error`. | | `last_poll_ms` / `lastPollMs` | `u64` | | This device's last poll of the feed (Unix ms). | | `pending_uploads` / `pendingUploads` | `u32` | | Local writes not yet in the bucket. | | `conflicts` | [`Vec`](#appdriveconflictinput-record) | | | | `devices` | [`Vec`](#appdrivedeviceinput-record) | | This device first. | | `last_error` / `lastError` | `Option` | | | ## `AppStorageForm` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `backend` | `String` | | | | `endpoint` | `String` | | | | `region` | `String` | | | | `bucket` | `String` | | | | `root` | `String` | | | | `path_style` / `pathStyle` | `bool` | | | | `access_key_id` / `accessKeyId` | `String` | | | | `secret_access_key` / `secretAccessKey` | `String` | | | ## `AppStorageInput` record Returned by [`app_storage_input_from_json`](#app_storage_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `os` | [`AppSpaceOs`]() | | This machine's system (the words: "This Mac", "Show in Finder"). | | `home` | `Option` | | The home folder, to show paths as `~/...`. | | `storage` | [`Option`](#appdrivestorageinput-record) | | None until read (or when the daemon cannot answer). | | `mount` | [`Option`](#appdrivemountinput-record) | | | | `cache` | [`Option`](#appdrivecacheinput-record) | | | ## `AppStorageState` record Returned by [`app_storage_initial`](#app_storage_initial), [`app_storage_reduce`](#app_storage_reduce), [`app_storage_state_from_json`](#app_storage_state_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `form` | [`AppStorageForm`](#appstorageform-record) | | | | `dirty` | `bool` | | The form differs from what is saved. | | `busy` | `bool` | | | | `request` | [`Option`](#appstoragerequest-enum) | | | | `check` | [`Option`](#appdrivecheckinput-record) | | The last test or save. | | `error` | `Option` | | | | `manual` | `bool` | | The bucket's fields show (else the agent prompt). | | `seen` | `Option` | | The last `volume_storage` seen (bucket, endpoint, region, keys), to notice the agent connecting a bucket while the prompt shows. | ## `AppDriveStepRequest` enum **Swift** ```swift .mount .unmount ``` **Rust** ```rust AppDriveStepRequest::Mount AppDriveStepRequest::Unmount ``` | Variant | Description | | --- | --- | | `Mount` | `volume_mount`; answer with `drive-mounted`. | | `Unmount` | `volume_unmount`; answer with `drive-mounted`. | ## `AppStorageAction` enum **Swift** ```swift .loaded(storage: AppDriveStorageInput) .setBackend(backend: String) .setField(field: AppStorageField, value: String) .setPathStyle(on: Bool) .test .save .checked(check: AppDriveCheckInput) .saved(check: AppDriveCheckInput) .adopted(check: AppDriveCheckInput) .showManual(on: Bool) .setMount(on: Bool) .reveal(path: String) .openUrl(url: String) .setCache(capacityBytes: UInt64) .clearCache .done .failed(error: String) ``` **Rust** ```rust AppStorageAction::Loaded { storage: AppDriveStorageInput } AppStorageAction::SetBackend { backend: String } AppStorageAction::SetField { field: AppStorageField, value: String } AppStorageAction::SetPathStyle { on: bool } AppStorageAction::Test AppStorageAction::Save AppStorageAction::Checked { check: AppDriveCheckInput } AppStorageAction::Saved { check: AppDriveCheckInput } AppStorageAction::Adopted { check: AppDriveCheckInput } AppStorageAction::ShowManual { on: bool } AppStorageAction::SetMount { on: bool } AppStorageAction::Reveal { path: String } AppStorageAction::OpenUrl { url: String } AppStorageAction::SetCache { capacity_bytes: u64 } AppStorageAction::ClearCache AppStorageAction::Done AppStorageAction::Failed { error: String } ``` | Variant | Description | | --- | --- | | `Loaded` | `volume_storage` was read: the form follows it unless edited. Fields: `storage: AppDriveStorageInput` | | `SetBackend` | The backend choice (`fs`, `s3`). Fields: `backend: String` | | `SetField` | Fields: `field: AppStorageField`, `value: String` | | `SetPathStyle` | Fields: `on: bool` | | `Test` | "Test connection". | | `Save` | The section's Save. | | `Checked` | A test's answer. Fields: `check: AppDriveCheckInput` | | `Saved` | A save's answer. Fields: `check: AppDriveCheckInput` | | `Adopted` | An adopted bucket's answer. Fields: `check: AppDriveCheckInput` | | `ShowManual` | "Enter details manually" (on) or "Use a prompt instead" (off). Fields: `on: bool` | | `SetMount` | "Show Cua Volume in Finder" on or off. Fields: `on: bool` | | `Reveal` | "Show in Finder" / "Open". Fields: `path: String` | | `OpenUrl` | "Open System Settings". Fields: `url: String` | | `SetCache` | Fields: `capacity_bytes: u64` | | `ClearCache` | | | `Done` | | | `Failed` | Fields: `error: String` | ## `AppStorageChoice` enum **Swift** ```swift .local .s3 .later ``` **Rust** ```rust AppStorageChoice::Local AppStorageChoice::S3 AppStorageChoice::Later ``` | Variant | Description | | --- | --- | | `Local` | This machine (the `fs` backend, no setup). | | `S3` | The user's own S3-compatible bucket. | | `Later` | Decide later, in Settings, Storage. | ## `AppStorageField` enum **Swift** ```swift .endpoint .region .bucket .accessKeyId .secretAccessKey ``` **Rust** ```rust AppStorageField::Endpoint AppStorageField::Region AppStorageField::Bucket AppStorageField::AccessKeyId AppStorageField::SecretAccessKey ``` | Variant | Description | | --- | --- | | `Endpoint` | | | `Region` | | | `Bucket` | | | `AccessKeyId` | | | `SecretAccessKey` | | ## `AppStorageRequest` enum **Swift** ```swift .test(update: AppDriveStorageUpdate) .save(update: AppDriveStorageUpdate) .adopt(update: AppDriveStorageUpdate) .mount .unmount .reveal(path: String) .openUrl(url: String) .setCache(capacityBytes: UInt64) .clearCache ``` **Rust** ```rust AppStorageRequest::Test { update: AppDriveStorageUpdate } AppStorageRequest::Save { update: AppDriveStorageUpdate } AppStorageRequest::Adopt { update: AppDriveStorageUpdate } AppStorageRequest::Mount AppStorageRequest::Unmount AppStorageRequest::Reveal { path: String } AppStorageRequest::OpenUrl { url: String } AppStorageRequest::SetCache { capacity_bytes: u64 } AppStorageRequest::ClearCache ``` | Variant | Description | | --- | --- | | `Test` | `volume_storage_set` with `dry_run`; answer with `checked`. Fields: `update: AppDriveStorageUpdate` | | `Save` | `volume_storage_set`; answer with `saved`. Fields: `update: AppDriveStorageUpdate` | | `Adopt` | `volume_storage_set` (keys from the credential store) for a bucket the user's agent configured; answer with `adopted`. Fields: `update: AppDriveStorageUpdate` | | `Mount` | `volume_mount`. | | `Unmount` | `volume_unmount`. | | `Reveal` | Show the mount in the file manager (the shell's own call). Fields: `path: String` | | `OpenUrl` | Open a URL (System Settings' file system extensions). Fields: `url: String` | | `SetCache` | `volume_cache_set`. Fields: `capacity_bytes: u64` | | `ClearCache` | `volume_cache_clear`. | ## `app_drive_mount_from_json` `volume_mount_status`'s JSON as the core takes it. **Swift** ```swift func appDriveMountFromJson(json: String) throws -> AppDriveMountInput ``` **Rust** ```rust pub fn app_drive_mount_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppDriveMountInput`](#appdrivemountinput-record) · **Raises** [`CuaError`]() ## `app_drive_mount_preview` The first run's Cua Volume page: its miniature (files from a Space arriving in the Cua Volume volume in Finder). **Swift** ```swift func appDriveMountPreview() -> AppDriveMountPreview ``` **Rust** ```rust pub fn app_drive_mount_preview() -> AppDriveMountPreview ``` **Returns** [`AppDriveMountPreview`](#appdrivemountpreview-record) ## `app_drive_mount_preview_frame` The page's miniature `t_ms` into its loop (it wraps). **Swift** ```swift func appDriveMountPreviewFrame(tMs: UInt32) -> AppDriveMountFrame ``` **Rust** ```rust pub fn app_drive_mount_preview_frame(t_ms: u32) -> AppDriveMountFrame ``` | Parameter | Type | Default | | --- | --- | --- | | `t_ms` | `u32` | required | **Returns** [`AppDriveMountFrame`](#appdrivemountframe-record) ## `app_drive_mount_preview_still` The page's miniature with Reduce Motion: files arriving, still. **Swift** ```swift func appDriveMountPreviewStill() -> AppDriveMountFrame ``` **Rust** ```rust pub fn app_drive_mount_preview_still() -> AppDriveMountFrame ``` **Returns** [`AppDriveMountFrame`](#appdrivemountframe-record) ## `app_drive_sync_from_json` `volume_sync_status`'s JSON as the core takes it. **Swift** ```swift func appDriveSyncFromJson(json: String) throws -> AppDriveSyncInput ``` **Rust** ```rust pub fn app_drive_sync_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppDriveSyncInput`](#appdrivesyncinput-record) · **Raises** [`CuaError`]() ## `app_storage_action_from_json` A Storage action from JSON (a test's or save's answer, parity flows). **Swift** ```swift func appStorageActionFromJson(json: String) throws -> AppStorageAction ``` **Rust** ```rust pub fn app_storage_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppStorageAction`](#appstorageaction-enum) · **Raises** [`CuaError`]() ## `app_storage_choose` What a Storage row's choice asks for. **Swift** ```swift func appStorageChoose(id: String, option: String) -> AppStorageAction? ``` **Rust** ```rust pub fn app_storage_choose(id: String, option: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | | `option` | `String` | required | **Returns** [`Option`](#appstorageaction-enum) ## `app_storage_edit` What a Storage field's edit asks for. **Swift** ```swift func appStorageEdit(id: String, value: String) -> AppStorageAction? ``` **Rust** ```rust pub fn app_storage_edit(id: String, value: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | | `value` | `String` | required | **Returns** [`Option`](#appstorageaction-enum) ## `app_storage_initial` Settings' Storage section before anything is read. **Swift** ```swift func appStorageInitial() -> AppStorageState ``` **Rust** ```rust pub fn app_storage_initial() -> AppStorageState ``` **Returns** [`AppStorageState`](#appstoragestate-record) ## `app_storage_input_from_json` Storage input from JSON (the daemon's answers, as they come). **Swift** ```swift func appStorageInputFromJson(json: String) throws -> AppStorageInput ``` **Rust** ```rust pub fn app_storage_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppStorageInput`](#appstorageinput-record) · **Raises** [`CuaError`]() ## `app_storage_press` What a Storage row's button asks for. **Swift** ```swift func appStoragePress(input: AppStorageInput, id: String) -> AppStorageAction? ``` **Rust** ```rust pub fn app_storage_press(input: AppStorageInput, id: String) -> Option ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppStorageInput`](#appstorageinput-record) | required | | `id` | `String` | required | **Returns** [`Option`](#appstorageaction-enum) ## `app_storage_reduce` Advances the Storage section. **Swift** ```swift func appStorageReduce(state: AppStorageState, action: AppStorageAction) -> AppStorageState ``` **Rust** ```rust pub fn app_storage_reduce(state: AppStorageState, action: AppStorageAction) -> AppStorageState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppStorageState`](#appstoragestate-record) | required | | `action` | [`AppStorageAction`](#appstorageaction-enum) | required | **Returns** [`AppStorageState`](#appstoragestate-record) ## `app_storage_request_text` One line naming a Storage command. **Swift** ```swift func appStorageRequestText(request: AppStorageRequest) -> String ``` **Rust** ```rust pub fn app_storage_request_text(request: AppStorageRequest) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `request` | [`AppStorageRequest`](#appstoragerequest-enum) | required | **Returns** `String` ## `app_storage_section` The Storage section as drawn (a Settings section). **Swift** ```swift func appStorageSection(input: AppStorageInput, state: AppStorageState) -> AppSettingsSection ``` **Rust** ```rust pub fn app_storage_section(input: AppStorageInput, state: AppStorageState) -> AppSettingsSection ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppStorageInput`](#appstorageinput-record) | required | | `state` | [`AppStorageState`](#appstoragestate-record) | required | **Returns** [`AppSettingsSection`]() ## `app_storage_state_from_json` Settings, Storage's state from JSON (fixtures, parity). **Swift** ```swift func appStorageStateFromJson(json: String) throws -> AppStorageState ``` **Rust** ```rust pub fn app_storage_state_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppStorageState`](#appstoragestate-record) · **Raises** [`CuaError`]() ## `app_storage_update_json` The Storage request as the tool's JSON arguments (`volume_storage_set`). **Swift** ```swift func appStorageUpdateJson(update: AppDriveStorageUpdate) -> String ``` **Rust** ```rust pub fn app_storage_update_json(update: AppDriveStorageUpdate) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `update` | [`AppDriveStorageUpdate`](#appdrivestorageupdate-record) | required | **Returns** `String` ## All items 37 items, across this page and its subpages, grouped by page. Methods are listed on their object. **App core: Cua Volume storage, mount and sync**: [`app_drive_mount_from_json`](#app_drive_mount_from_json), [`app_drive_mount_preview`](#app_drive_mount_preview), [`app_drive_mount_preview_frame`](#app_drive_mount_preview_frame), [`app_drive_mount_preview_still`](#app_drive_mount_preview_still), [`app_drive_sync_from_json`](#app_drive_sync_from_json), [`app_storage_action_from_json`](#app_storage_action_from_json), [`app_storage_choose`](#app_storage_choose), [`app_storage_edit`](#app_storage_edit), [`app_storage_initial`](#app_storage_initial), [`app_storage_input_from_json`](#app_storage_input_from_json), [`app_storage_press`](#app_storage_press), [`app_storage_reduce`](#app_storage_reduce), [`app_storage_request_text`](#app_storage_request_text), [`app_storage_section`](#app_storage_section), [`app_storage_state_from_json`](#app_storage_state_from_json), [`app_storage_update_json`](#app_storage_update_json), [`AppConflictView`](#appconflictview-record), [`AppDriveCacheInput`](#appdrivecacheinput-record), [`AppDriveCard`](#appdrivecard-record), [`AppDriveCheckInput`](#appdrivecheckinput-record), [`AppDriveConflictInput`](#appdriveconflictinput-record), [`AppDriveDeviceInput`](#appdrivedeviceinput-record), [`AppDriveMountFrame`](#appdrivemountframe-record), [`AppDriveMountInput`](#appdrivemountinput-record), [`AppDriveMountPreview`](#appdrivemountpreview-record), [`AppDriveS3Input`](#appdrives3input-record), [`AppDriveStepRequest`](#appdrivesteprequest-enum), [`AppDriveStorageInput`](#appdrivestorageinput-record), [`AppDriveStorageUpdate`](#appdrivestorageupdate-record), [`AppDriveSyncInput`](#appdrivesyncinput-record), [`AppStorageAction`](#appstorageaction-enum), [`AppStorageChoice`](#appstoragechoice-enum), [`AppStorageField`](#appstoragefield-enum), [`AppStorageForm`](#appstorageform-record), [`AppStorageInput`](#appstorageinput-record), [`AppStorageRequest`](#appstoragerequest-enum), [`AppStorageState`](#appstoragestate-record) --- # App core: agents, drive, notifications The Agents page (persistent agents, memory, routines, computer access), the Drive page and the notifications the apps post. **Note: Cua Spaces app export** These APIs are part of the Cua Spaces app export for building Spaces UIs. They are source-available under FSL-1.1-MIT and ship for Swift only (`import CuaSpacesFFI`); the Rust tab shows the `cua-spaces-ffi` crate they come from. The open source SDK packages for Python, TypeScript and Kotlin do not include them. `app_agents_reduce` and `app_agents_view` drive the Agents page from the daemon's persistent-agent, drive and routine tools; `app_drive_*` the Drive page; `app_notifications_plan` says which feed entries to post as system notifications (each once, none of the backlog on first run) and `app_notifications_view` draws the list. ## `AppLineView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `text` | `String` | | | | `trailing` | `String` | | Right-aligned secondary text. | | `action_label` / `actionLabel` | `Option` | | | | `secondary_label` / `secondaryLabel` | `Option` | | A second action (Deny next to Approve). | | `on` | `Option` | | | ## `AppDriveEntryInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | | | `name` | `String` | | | | `folder` | `bool` | | | | `size` | `u64` | | | ## `AppSystemNote` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `title` | `String` | | | | `body` | `String` | | | ## `app_agent_rows` A Space's agent rows from its run records (`Space.agent_list()`'s `json`, one record each): attention first, then newest. The same mapping the Tauri app uses. **Swift** ```swift func appAgentRows(runsJson: [String]) -> [AppSpaceAgentRun] ``` **Rust** ```rust pub fn app_agent_rows(runs_json: Vec) -> Vec ``` | Parameter | Type | Default | | --- | --- | --- | | `runs_json` | `Vec` | required | **Returns** [`Vec`]() ## `app_agent_status_label` A run's status word ("Running", "Idle", ...). **Swift** ```swift func appAgentStatusLabel(status: AppAgentStatus) -> String ``` **Rust** ```rust pub fn app_agent_status_label(status: AppAgentStatus) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `status` | [`AppAgentStatus`]() | required | **Returns** `String` ## `app_agent_subtitle` The line under an agent's name. **Swift** ```swift func appAgentSubtitle(run: AppSpaceAgentRun) -> String ``` **Rust** ```rust pub fn app_agent_subtitle(run: AppSpaceAgentRun) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `run` | [`AppSpaceAgentRun`]() | required | **Returns** `String` ## `AppAccessAuditInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `ts_ms` / `tsMs` | `u64` | | | | `action` | `String` | | `grant`, `revoke`, `use`, `denied`. | | `principal` | `String` | | `agent:ada` or `user`. | | `path` | `String` | | The machine. | | `detail` | `String` | | | ## `AppAgentDetailView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | | | `subtitle` | `String` | | `Home: agents/ada/ · saved 5m ago` (one line). | | `tabs` | [`Vec`](#apptabview-record) | | | | `memory` | [`Vec`](#applineview-record) | | | | `memory_empty` / `memoryEmpty` | `String` | | | | `file` | [`Option`](#appfileview-record) | | | | `routines` | [`Vec`](#applineview-record) | | | | `routines_empty` / `routinesEmpty` | `String` | | | | `form` | [`AppRoutineForm`](#approutineform-record) | | | | `schedules` | [`Vec`](#applineview-record) | | | | `can_add_routine` / `canAddRoutine` | `bool` | | | | `add_routine_label` / `addRoutineLabel` | `String` | | | | `access` | [`Vec`](#applineview-record) | | | | `access_empty` / `accessEmpty` | `String` | | | | `allow_this_machine_label` / `allowThisMachineLabel` | `Option` | | `Allow on this Mac` when this machine is set up and not granted. | | `audit` | [`Vec`](#applineview-record) | | | | `error` | `Option` | | | ## `AppAgentRowView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | | | `detail` | `String` | | `Hermes in local:dev`. | | `state` | `String` | | `Running`, `Idle`, `Paused`. | | `action_label` / `actionLabel` | `String` | | `Pause` or `Resume`. | | `selected` | `bool` | | | ## `AppAgentsInput` record Returned by [`app_agents_input_from_json`](#app_agents_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `agents` | [`Vec`](#apppersistentagentinput-record) | | | | `home` | [`Vec`](#appdriveentryinput-record) | | The selected agent's home, `agents//`, recursively (files only). | | `file` | [`Option`](#appopenfileinput-record) | | The file the shell loaded for the open path. | | `routines` | [`Vec`](#approutineinput-record) | | The selected agent's routines. | | `grants` | [`Vec`](#appcomputergrantinput-record) | | Every computer grant. | | `audit` | [`Vec`](#appaccessauditinput-record) | | Recent computer-access audit lines, newest first. | | `this_machine` / `thisMachine` | `Option` | | This machine's Space id when it is set up for access (`relay:`). | ## `AppAgentsState` record Returned by [`app_agents_initial`](#app_agents_initial), [`app_agents_reduce`](#app_agents_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `selected` | `Option` | | | | `tab` | [`AppAgentTab`](#appagenttab-enum) | | | | `open_path` / `openPath` | `Option` | | The memory file open in the detail. | | `form` | [`AppRoutineForm`](#approutineform-record) | | | | `busy` | `bool` | | | | `error` | `Option` | | | | `request` | [`Option`](#appagentsrequest-enum) | | | ## `AppAgentsView` record Returned by [`app_agents_view`](#app_agents_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | | | `rows` | [`Vec`](#appagentrowview-record) | | | | `empty_text` / `emptyText` | `String` | | | | `detail` | [`Option`](#appagentdetailview-record) | | | | `busy` | `bool` | | | | `error` | `Option` | | | | `request` | [`Option`](#appagentsrequest-enum) | | | | `request_text` / `requestText` | `Option` | | `AgentsRequest.text` of `request`. | ## `AppComputerGrantInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `String` | | | | `machine` | `String` | | | | `revoked` | `bool` | | | ## `AppDriveGrantInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `principal` | `String` | | | | `prefix` | `String` | | | | `mode` | `String` | | | | `revoked` | `bool` | | | ## `AppDriveInput` record Returned by [`app_drive_input_from_json`](#app_drive_input_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `requests` | [`Vec`](#appdriverequestinput-record) | | | | `grants` | [`Vec`](#appdrivegrantinput-record) | | | | `now_ms` / `nowMs` | `u64` | | Now (Unix ms), for "synced 5m ago". | | `mount` | [`Option`]() | | `volume_mount_status`, once read. | | `sync` | [`Option`]() | | `volume_sync_status`, once read. | | `home` | `Option` | | The home folder, to show the mount point as `~/...`. | ## `AppDriveRequestInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `principal` | `String` | | `agent:ada`. | | `prefix` | `String` | | | | `mode` | `String` | | `r` or `rw`. | | `reason` | `String` | | | ## `AppDriveState` record Returned by [`app_drive_initial`](#app_drive_initial), [`app_drive_reduce`](#app_drive_reduce). | Field | Type | Default | Description | | --- | --- | --- | --- | | `busy` | `bool` | | | | `error` | `Option` | | | | `request` | [`Option`](#appdriverequest-enum) | | | ## `AppDriveView` record Returned by [`app_drive_view`](#app_drive_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | | | `requests_title` / `requestsTitle` | `String` | | | | `requests` | [`Vec`](#applineview-record) | | | | `grants_title` / `grantsTitle` | `String` | | | | `grants` | [`Vec`](#applineview-record) | | | | `grants_empty` / `grantsEmpty` | `String` | | | | `busy` | `bool` | | | | `error` | `Option` | | | | `request` | [`Option`](#appdriverequest-enum) | | | | `request_text` / `requestText` | `Option` | | `DriveRequest.text` of `request`. | | `open_label` / `openLabel` | `Option` | | "Open in Finder" (Linux: "Open folder"): mounts first when the volume is not mounted. None where it cannot mount. | | `mount_path` / `mountPath` | `Option` | | The mount point, when mounted. | | `mount_line` / `mountLine` | `Option` | | One quiet line: where it is mounted ("In Finder at ~/Cua Volume"), or that it is not. | | `devices_title` / `devicesTitle` | `String` | | "Devices". | | `devices` | [`Vec`](#applineview-record) | | One line per device, this one first: when it last synced, what is pending. | | `sync_note` / `syncNote` | `Option` | | Why nothing syncs, or the feed's error. | | `sync_error` / `syncError` | `bool` | | The note is an error. | | `conflicts_title` / `conflictsTitle` | `String` | | "Conflicts". | | `conflicts` | [`Vec`]() | | | ## `AppFileVersionInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `version` | `String` | | | | `modified_ms` / `modifiedMs` | `u64` | | | | `deleted` | `bool` | | | | `latest` | `bool` | | | ## `AppFileView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | | | `text` | `String` | | | | `versions` | [`Vec`](#applineview-record) | | | | `close_label` / `closeLabel` | `String` | | | ## `AppNotificationInput` record Returned by [`app_notifications_from_json`](#app_notifications_from_json). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `at_ms` / `atMs` | `u64` | | | | `agent` | `Option` | | | | `kind` | `String` | | `turn_ended`, `message`, `approval`, `error`. | | `title` | `String` | | | | `body` | `String` | | | | `read` | `bool` | | | ## `AppNotificationsPlan` record Returned by [`app_notifications_plan`](#app_notifications_plan). | Field | Type | Default | Description | | --- | --- | --- | --- | | `post` | [`Vec`](#appsystemnote-record) | | | | `seen_ms` / `seenMs` | `u64` | | The new `notifications_seen_ms` (unchanged when nothing is newer). | ## `AppNotificationsView` record Returned by [`app_notifications_view`](#app_notifications_view). | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | | | `rows` | [`Vec`](#applineview-record) | | One line each: `title: body`, the time on the right; `on` is unread. | | `empty_text` / `emptyText` | `String` | | | | `unread` | `u32` | | | | `mark_all_label` / `markAllLabel` | `Option` | | | ## `AppOpenFileInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `path` | `String` | | | | `text` | `String` | | The text (empty for a binary file). | | `binary` | `bool` | | | | `versions` | [`Vec`](#appfileversioninput-record) | | | ## `AppPersistentAgentInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | | | `harness` | `String` | | Harness id (`claude-code`, `hermes`, ...). | | `space` | `String` | | The Space it works in. | | `paused` | `bool` | | | | `space_state` / `spaceState` | `String` | | `running`, `suspended` or `released`. | | `run_id` / `runId` | `Option` | | Its current run, if any. | | `saved_ms` / `savedMs` | `u64` | | Unix ms of the last home save (0: never). | | `last_error` / `lastError` | `Option` | | | ## `AppRoutineForm` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `title` | `String` | | | | `prompt` | `String` | | | | `schedule` | [`AppRoutineScheduleKind`](#approutineschedulekind-enum) | | | | `minutes` | `u32` | | Minutes for `every`. | | `time` | `String` | | `HH:MM` for daily and weekly. | | `weekday` | `String` | | `mon` ... `sun` for weekly. | ## `AppRoutineInput` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | | | `title` | `String` | | | | `label` | `String` | | `Every day at 8:00 AM`. | | `enabled` | `bool` | | | ## `AppTabView` record | Field | Type | Default | Description | | --- | --- | --- | --- | | `tab` | [`AppAgentTab`](#appagenttab-enum) | | | | `label` | `String` | | | | `selected` | `bool` | | | ## `AppAgentTab` enum **Swift** ```swift .memory .routines .access ``` **Rust** ```rust AppAgentTab::Memory AppAgentTab::Routines AppAgentTab::Access ``` | Variant | Description | | --- | --- | | `Memory` | | | `Routines` | | | `Access` | | ## `AppAgentsAction` enum **Swift** ```swift .select(name: String) .setTab(tab: AppAgentTab) .openFile(path: String) .closeFile .pause(name: String) .resume(name: String) .restore(version: String) .setTitle(title: String) .setPrompt(prompt: String) .setSchedule(schedule: AppRoutineScheduleKind) .setMinutes(minutes: UInt32) .setTime(time: String) .setWeekday(weekday: String) .addRoutine .toggleRoutine(id: String) .removeRoutine(id: String) .allowComputer(machine: String) .revokeComputer(machine: String) .done .failed(error: String) ``` **Rust** ```rust AppAgentsAction::Select { name: String } AppAgentsAction::SetTab { tab: AppAgentTab } AppAgentsAction::OpenFile { path: String } AppAgentsAction::CloseFile AppAgentsAction::Pause { name: String } AppAgentsAction::Resume { name: String } AppAgentsAction::Restore { version: String } AppAgentsAction::SetTitle { title: String } AppAgentsAction::SetPrompt { prompt: String } AppAgentsAction::SetSchedule { schedule: AppRoutineScheduleKind } AppAgentsAction::SetMinutes { minutes: u32 } AppAgentsAction::SetTime { time: String } AppAgentsAction::SetWeekday { weekday: String } AppAgentsAction::AddRoutine AppAgentsAction::ToggleRoutine { id: String } AppAgentsAction::RemoveRoutine { id: String } AppAgentsAction::AllowComputer { machine: String } AppAgentsAction::RevokeComputer { machine: String } AppAgentsAction::Done AppAgentsAction::Failed { error: String } ``` | Variant | Description | | --- | --- | | `Select` | Fields: `name: String` | | `SetTab` | Fields: `tab: AppAgentTab` | | `OpenFile` | Fields: `path: String` | | `CloseFile` | | | `Pause` | Fields: `name: String` | | `Resume` | Fields: `name: String` | | `Restore` | Fields: `version: String` | | `SetTitle` | Fields: `title: String` | | `SetPrompt` | Fields: `prompt: String` | | `SetSchedule` | Fields: `schedule: AppRoutineScheduleKind` | | `SetMinutes` | Fields: `minutes: u32` | | `SetTime` | Fields: `time: String` | | `SetWeekday` | Fields: `weekday: String` | | `AddRoutine` | | | `ToggleRoutine` | Fields: `id: String` | | `RemoveRoutine` | Fields: `id: String` | | `AllowComputer` | Fields: `machine: String` | | `RevokeComputer` | Fields: `machine: String` | | `Done` | | | `Failed` | Fields: `error: String` | ## `AppAgentsRequest` enum **Swift** ```swift .pause(name: String) .resume(name: String) .load(name: String) .readFile(path: String) .restore(path: String, version: String) .addRoutine(agent: String, title: String, prompt: String, everyMinutes: UInt32?, dailyAt: String?, weeklyOn: String?) .setRoutine(id: String, enabled: Bool) .removeRoutine(id: String) .allow(agent: String, machine: String) .revoke(agent: String, machine: String) ``` **Rust** ```rust AppAgentsRequest::Pause { name: String } AppAgentsRequest::Resume { name: String } AppAgentsRequest::Load { name: String } AppAgentsRequest::ReadFile { path: String } AppAgentsRequest::Restore { path: String, version: String } AppAgentsRequest::AddRoutine { agent: String, title: String, prompt: String, every_minutes: Option, daily_at: Option, weekly_on: Option } AppAgentsRequest::SetRoutine { id: String, enabled: bool } AppAgentsRequest::RemoveRoutine { id: String } AppAgentsRequest::Allow { agent: String, machine: String } AppAgentsRequest::Revoke { agent: String, machine: String } ``` | Variant | Description | | --- | --- | | `Pause` | `agent_pause`. Fields: `name: String` | | `Resume` | `agent_resume`. Fields: `name: String` | | `Load` | Load the selected agent's home, routines and grants. Fields: `name: String` | | `ReadFile` | `volume_read` + `volume_history` of a home file. Fields: `path: String` | | `Restore` | `volume_restore`. Fields: `path: String`, `version: String` | | `AddRoutine` | `routine_add` (one of the schedule fields is set). Fields: `agent: String`, `title: String`, `prompt: String`, `every_minutes: Option`, `daily_at: Option`, `weekly_on: Option` | | `SetRoutine` | `routine_set_enabled`. Fields: `id: String`, `enabled: bool` | | `RemoveRoutine` | `routine_remove`. Fields: `id: String` | | `Allow` | `computer_access_grant` (the daemon asks for presence). Fields: `agent: String`, `machine: String` | | `Revoke` | `computer_access_revoke`. Fields: `agent: String`, `machine: String` | ## `AppDriveAction` enum **Swift** ```swift .openVolume(mounted: String?) .approve(id: String) .deny(id: String) .revoke(id: String) .reveal(path: String) .resolve(path: String) .done .failed(error: String) ``` **Rust** ```rust AppDriveAction::OpenVolume { mounted: Option } AppDriveAction::Approve { id: String } AppDriveAction::Deny { id: String } AppDriveAction::Revoke { id: String } AppDriveAction::Reveal { path: String } AppDriveAction::Resolve { path: String } AppDriveAction::Done AppDriveAction::Failed { error: String } ``` | Variant | Description | | --- | --- | | `OpenVolume` | The page's main button: show the mount point in the file manager, mounting first when the volume is not mounted (`mounted`: the mount point, when mounted). Fields: `mounted: Option` | | `Approve` | Fields: `id: String` | | `Deny` | Fields: `id: String` | | `Revoke` | Fields: `id: String` | | `Reveal` | "Open in Finder" (the mount), or a conflict's Open. Fields: `path: String` | | `Resolve` | A conflict's Resolve (the files stay). Fields: `path: String` | | `Done` | | | `Failed` | Fields: `error: String` | ## `AppDriveRequest` enum **Swift** ```swift .load .mountAndReveal .approve(id: String) .deny(id: String) .revoke(id: String) .reveal(path: String) .resolve(path: String) ``` **Rust** ```rust AppDriveRequest::Load AppDriveRequest::MountAndReveal AppDriveRequest::Approve { id: String } AppDriveRequest::Deny { id: String } AppDriveRequest::Revoke { id: String } AppDriveRequest::Reveal { path: String } AppDriveRequest::Resolve { path: String } ``` | Variant | Description | | --- | --- | | `Load` | Read the page: `volume_requests`, `volume_grants`, `volume_mount_status`, `volume_sync_status`. | | `MountAndReveal` | `volume_mount`, then show the mount point in the file manager. | | `Approve` | `volume_approve` (the daemon asks for presence). Fields: `id: String` | | `Deny` | `volume_deny`. Fields: `id: String` | | `Revoke` | `volume_revoke`. Fields: `id: String` | | `Reveal` | Show a path in the file manager (the shell's own call). Fields: `path: String` | | `Resolve` | `volume_sync_resolve`. Fields: `path: String` | ## `AppRoutineScheduleKind` enum **Swift** ```swift .every .daily .weekly ``` **Rust** ```rust AppRoutineScheduleKind::Every AppRoutineScheduleKind::Daily AppRoutineScheduleKind::Weekly ``` | Variant | Description | | --- | --- | | `Every` | | | `Daily` | | | `Weekly` | | ## `app_agents_action_from_json` An Agents page action from JSON (parity flows). **Swift** ```swift func appAgentsActionFromJson(json: String) throws -> AppAgentsAction ``` **Rust** ```rust pub fn app_agents_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppAgentsAction`](#appagentsaction-enum) · **Raises** [`CuaError`]() ## `app_agents_initial` A new Agents page. **Swift** ```swift func appAgentsInitial() -> AppAgentsState ``` **Rust** ```rust pub fn app_agents_initial() -> AppAgentsState ``` **Returns** [`AppAgentsState`](#appagentsstate-record) ## `app_agents_input_from_json` Agents page input from JSON (tool results, fixtures, parity flows). **Swift** ```swift func appAgentsInputFromJson(json: String) throws -> AppAgentsInput ``` **Rust** ```rust pub fn app_agents_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppAgentsInput`](#appagentsinput-record) · **Raises** [`CuaError`]() ## `app_agents_reduce` Advances the Agents page. **Swift** ```swift func appAgentsReduce(input: AppAgentsInput, state: AppAgentsState, action: AppAgentsAction) -> AppAgentsState ``` **Rust** ```rust pub fn app_agents_reduce(input: AppAgentsInput, state: AppAgentsState, action: AppAgentsAction) -> AppAgentsState ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppAgentsInput`](#appagentsinput-record) | required | | `state` | [`AppAgentsState`](#appagentsstate-record) | required | | `action` | [`AppAgentsAction`](#appagentsaction-enum) | required | **Returns** [`AppAgentsState`](#appagentsstate-record) ## `app_agents_view` The Agents page as drawn at `now_ms`. **Swift** ```swift func appAgentsView(input: AppAgentsInput, state: AppAgentsState, nowMs: UInt64) -> AppAgentsView ``` **Rust** ```rust pub fn app_agents_view(input: AppAgentsInput, state: AppAgentsState, now_ms: u64) -> AppAgentsView ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppAgentsInput`](#appagentsinput-record) | required | | `state` | [`AppAgentsState`](#appagentsstate-record) | required | | `now_ms` | `u64` | required | **Returns** [`AppAgentsView`](#appagentsview-record) ## `app_drive_action_from_json` A Drive page action from JSON (parity flows). **Swift** ```swift func appDriveActionFromJson(json: String) throws -> AppDriveAction ``` **Rust** ```rust pub fn app_drive_action_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppDriveAction`](#appdriveaction-enum) · **Raises** [`CuaError`]() ## `app_drive_initial` A new Drive page (at the root, listing it). **Swift** ```swift func appDriveInitial() -> AppDriveState ``` **Rust** ```rust pub fn app_drive_initial() -> AppDriveState ``` **Returns** [`AppDriveState`](#appdrivestate-record) ## `app_drive_input_from_json` Drive page input from JSON. **Swift** ```swift func appDriveInputFromJson(json: String) throws -> AppDriveInput ``` **Rust** ```rust pub fn app_drive_input_from_json(json: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`AppDriveInput`](#appdriveinput-record) · **Raises** [`CuaError`]() ## `app_drive_reduce` Advances the Drive page. **Swift** ```swift func appDriveReduce(state: AppDriveState, action: AppDriveAction) -> AppDriveState ``` **Rust** ```rust pub fn app_drive_reduce(state: AppDriveState, action: AppDriveAction) -> AppDriveState ``` | Parameter | Type | Default | | --- | --- | --- | | `state` | [`AppDriveState`](#appdrivestate-record) | required | | `action` | [`AppDriveAction`](#appdriveaction-enum) | required | **Returns** [`AppDriveState`](#appdrivestate-record) ## `app_drive_view` The Drive page as drawn. **Swift** ```swift func appDriveView(input: AppDriveInput, state: AppDriveState) -> AppDriveView ``` **Rust** ```rust pub fn app_drive_view(input: AppDriveInput, state: AppDriveState) -> AppDriveView ``` | Parameter | Type | Default | | --- | --- | --- | | `input` | [`AppDriveInput`](#appdriveinput-record) | required | | `state` | [`AppDriveState`](#appdrivestate-record) | required | **Returns** [`AppDriveView`](#appdriveview-record) ## `app_notifications_from_json` Feed entries from JSON (the `notifications_list` result's array). **Swift** ```swift func appNotificationsFromJson(json: String) throws -> [AppNotificationInput] ``` **Rust** ```rust pub fn app_notifications_from_json(json: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `json` | `String` | required | **Returns** [`Vec`](#appnotificationinput-record) · **Raises** [`CuaError`]() ## `app_notifications_plan` Which feed entries to post now, and the marker to save (`AppSettings.notifications_seen_ms`; 0 on the first run). **Swift** ```swift func appNotificationsPlan(feed: [AppNotificationInput], seenMs: UInt64) -> AppNotificationsPlan ``` **Rust** ```rust pub fn app_notifications_plan(feed: Vec, seen_ms: u64) -> AppNotificationsPlan ``` | Parameter | Type | Default | | --- | --- | --- | | `feed` | [`Vec`](#appnotificationinput-record) | required | | `seen_ms` | `u64` | required | **Returns** [`AppNotificationsPlan`](#appnotificationsplan-record) ## `app_notifications_view` The notifications list at `now_ms`. **Swift** ```swift func appNotificationsView(feed: [AppNotificationInput], nowMs: UInt64) -> AppNotificationsView ``` **Rust** ```rust pub fn app_notifications_view(feed: Vec, now_ms: u64) -> AppNotificationsView ``` | Parameter | Type | Default | | --- | --- | --- | | `feed` | [`Vec`](#appnotificationinput-record) | required | | `now_ms` | `u64` | required | **Returns** [`AppNotificationsView`](#appnotificationsview-record) ## All items 47 items, across this page and its subpages, grouped by page. Methods are listed on their object. **App core: agents, drive, notifications**: [`app_agent_rows`](#app_agent_rows), [`app_agent_status_label`](#app_agent_status_label), [`app_agent_subtitle`](#app_agent_subtitle), [`app_agents_action_from_json`](#app_agents_action_from_json), [`app_agents_initial`](#app_agents_initial), [`app_agents_input_from_json`](#app_agents_input_from_json), [`app_agents_reduce`](#app_agents_reduce), [`app_agents_view`](#app_agents_view), [`app_drive_action_from_json`](#app_drive_action_from_json), [`app_drive_initial`](#app_drive_initial), [`app_drive_input_from_json`](#app_drive_input_from_json), [`app_drive_reduce`](#app_drive_reduce), [`app_drive_view`](#app_drive_view), [`app_notifications_from_json`](#app_notifications_from_json), [`app_notifications_plan`](#app_notifications_plan), [`app_notifications_view`](#app_notifications_view), [`AppAccessAuditInput`](#appaccessauditinput-record), [`AppAgentDetailView`](#appagentdetailview-record), [`AppAgentRowView`](#appagentrowview-record), [`AppAgentsAction`](#appagentsaction-enum), [`AppAgentsInput`](#appagentsinput-record), [`AppAgentsRequest`](#appagentsrequest-enum), [`AppAgentsState`](#appagentsstate-record), [`AppAgentsView`](#appagentsview-record), [`AppAgentTab`](#appagenttab-enum), [`AppComputerGrantInput`](#appcomputergrantinput-record), [`AppDriveAction`](#appdriveaction-enum), [`AppDriveEntryInput`](#appdriveentryinput-record), [`AppDriveGrantInput`](#appdrivegrantinput-record), [`AppDriveInput`](#appdriveinput-record), [`AppDriveRequest`](#appdriverequest-enum), [`AppDriveRequestInput`](#appdriverequestinput-record), [`AppDriveState`](#appdrivestate-record), [`AppDriveView`](#appdriveview-record), [`AppFileVersionInput`](#appfileversioninput-record), [`AppFileView`](#appfileview-record), [`AppLineView`](#applineview-record), [`AppNotificationInput`](#appnotificationinput-record), [`AppNotificationsPlan`](#appnotificationsplan-record), [`AppNotificationsView`](#appnotificationsview-record), [`AppOpenFileInput`](#appopenfileinput-record), [`AppPersistentAgentInput`](#apppersistentagentinput-record), [`AppRoutineForm`](#approutineform-record), [`AppRoutineInput`](#approutineinput-record), [`AppRoutineScheduleKind`](#approutineschedulekind-enum), [`AppSystemNote`](#appsystemnote-record), [`AppTabView`](#apptabview-record) --- # Devices This device as a client of the account on the relay: enroll, approve, rename, revoke and the audit log. `auth.devices(relay_url, name)` returns this device on the relay, its key in the session's credential store (shared with the `cua` CLI and daemon). A device lists and reaches the account's machines once enrolled: right after a fresh sign-in (a sign-in through `Auth.begin_login` re-registers a device that already has a key), or by a one-time code approved from an enrolled device (`cua devices approve`). A new key of the same machine replaces its old record. Enrollment lasts 30 days, then one approval or a fresh sign-in re-verifies it. Nothing here prompts; gate `approve` behind presence in your UI. ## `Devices` This device as a client of the signed-in account on a relay: its enrollment, the account's other devices and its audit log. The device key lives in the session's vault (shared with the `cua` CLI and daemon). Nothing here prompts: approving is the caller's to gate behind presence. Returned by [`Auth.devices`](). | Method | Description | | --- | --- | | [`approve`](#devicesapprove) | Approves the device showing `code`, or re-verifies `device_id`, from this enrolled device. | | [`check_enrolled`](#devicescheck_enrolled) | Whether the relay lets this device open a session now (enrolled), asked afresh. | | [`confirm_machine`](#devicesconfirm_machine) | Vouches for `machine_id`, a machine that registered without an enrolled device's signature or MFA (from this enrolled device) (S5). | | [`device_id`](#devicesdevice_id) | This device's id, when it has a key (no network). | | [`enroll`](#devicesenroll) | Registers this device (creating its key on first use). | | [`rename`](#devicesrename) | Renames a device (from this enrolled device). | | [`revoke`](#devicesrevoke) | Revokes a device (from this enrolled device). | | [`snapshot`](#devicessnapshot) | The account's devices, the grace period, the newest `audit_limit` audit events and the machines' names. | | Accessor | Returns | Description | | --- | --- | --- | | `relay_url()` | `String` | The relay base URL. | ### `Devices.approve` Approves the device showing `code`, or re-verifies `device_id`, from this enrolled device. **Python** ```python async def approve(self, code: Optional[str], device_id: Optional[str]) -> RelayDevice ``` **TypeScript** ```ts approve(code: string | undefined, deviceId: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func approve(code: String?, deviceId: String?) async throws -> RelayDevice ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun approve(code: String?, deviceId: String?): RelayDevice ``` **Rust** ```rust pub async fn approve(&self, code: Option, device_id: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `code` | `Option` | required | | `device_id` | `Option` | required | **Returns** [`RelayDevice`](#relaydevice-record) · **Async** · **Raises** [`CuaError`]() ### `Devices.check_enrolled` Whether the relay lets this device open a session now (enrolled), asked afresh. Polled while waiting for an approval. **Python** ```python async def check_enrolled(self) -> bool ``` **TypeScript** ```ts checkEnrolled(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func checkEnrolled() async -> Bool ``` **Kotlin** ```kotlin suspend fun checkEnrolled(): Boolean ``` **Rust** ```rust pub async fn check_enrolled(&self) -> bool ``` **Returns** `bool` · **Async** ### `Devices.confirm_machine` Vouches for `machine_id`, a machine that registered without an enrolled device's signature or MFA (from this enrolled device) (S5). Clears `RelayMachine.confirmed`'s "new" flag for every account member, not just this device. **Python** ```python async def confirm_machine(self, machine_id: str) -> RelayMachine ``` **TypeScript** ```ts confirmMachine(machineId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func confirmMachine(machineId: String) async throws -> RelayMachine ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun confirmMachine(machineId: String): RelayMachine ``` **Rust** ```rust pub async fn confirm_machine(&self, machine_id: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `machine_id` | `String` | required | **Returns** [`RelayMachine`]() · **Async** · **Raises** [`CuaError`]() ### `Devices.device_id` This device's id, when it has a key (no network). **Python** ```python def device_id(self) -> Optional[str] ``` **TypeScript** ```ts deviceId(): string | undefined ``` **Swift** ```swift func deviceId() throws -> String? ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun deviceId(): String? ``` **Rust** ```rust pub fn device_id(&self) -> Result, CuaError> ``` **Returns** `Option` · **Raises** [`CuaError`]() ### `Devices.enroll` Registers this device (creating its key on first use). Right after a fresh sign-in it is enrolled at once (replacing this machine's older device key, if any); otherwise the result carries a one-time code to approve from an enrolled device. **Python** ```python async def enroll(self) -> DeviceEnrollment ``` **TypeScript** ```ts enroll(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func enroll() async throws -> DeviceEnrollment ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun enroll(): DeviceEnrollment ``` **Rust** ```rust pub async fn enroll(&self) -> Result ``` **Returns** [`DeviceEnrollment`](#deviceenrollment-record) · **Async** · **Raises** [`CuaError`]() ### `Devices.rename` Renames a device (from this enrolled device). **Python** ```python async def rename(self, id: str, name: str) -> RelayDevice ``` **TypeScript** ```ts rename(id: string, name: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func rename(id: String, name: String) async throws -> RelayDevice ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun rename(id: String, name: String): RelayDevice ``` **Rust** ```rust pub async fn rename(&self, id: String, name: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | | `name` | `String` | required | **Returns** [`RelayDevice`](#relaydevice-record) · **Async** · **Raises** [`CuaError`]() ### `Devices.revoke` Revokes a device (from this enrolled device). Revoking this device also deletes its key. **Python** ```python async def revoke(self, id: str) -> RelayDevice ``` **TypeScript** ```ts revoke(id: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func revoke(id: String) async throws -> RelayDevice ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun revoke(id: String): RelayDevice ``` **Rust** ```rust pub async fn revoke(&self, id: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `id` | `String` | required | **Returns** [`RelayDevice`](#relaydevice-record) · **Async** · **Raises** [`CuaError`]() ### `Devices.snapshot` The account's devices, the grace period, the newest `audit_limit` audit events and the machines' names. Only the device list must succeed; the rest is empty when the relay refuses it. **Python** ```python async def snapshot(self, audit_limit: int) -> DevicesSnapshot ``` **TypeScript** ```ts snapshot(auditLimit: number, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func snapshot(auditLimit: UInt32) async throws -> DevicesSnapshot ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun snapshot(auditLimit: UInt): DevicesSnapshot ``` **Rust** ```rust pub async fn snapshot(&self, audit_limit: u32) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `audit_limit` | `u32` | required | **Returns** [`DevicesSnapshot`](#devicessnapshot-record) · **Async** · **Raises** [`CuaError`]() ## `DevicesSnapshot` record Everything the Devices page reads, in one call. Returned by [`Devices.snapshot`](#devicessnapshot). | Field | Type | Default | Description | | --- | --- | --- | --- | | `local_device_id` / `localDeviceId` | `Option` | | This device's id, when it has a key. | | `devices` | [`Vec`](#relaydevice-record) | | The account's devices (this one marked `current` once enrolled). | | `enforce_after` / `enforceAfter` | `Option` | | End of the relay's grace period for unenrolled devices. | | `audit` | [`Vec`](#relayauditevent-record) | | The account's audit log, newest last (empty when the relay refuses). | | `machine_names` / `machineNames` | `HashMap` | | Machine ids to names (empty when this device cannot list them). | | `machines` | [`Vec`]() | | The account's relay machines, `RelayMachine.confirmed` included (S5; empty when this device cannot list them, same as `machine_names`). | ## `RelayDevice` record A client device of the account, as the relay lists it. Returned by [`Devices.approve`](#devicesapprove), [`Devices.rename`](#devicesrename), [`Devices.revoke`](#devicesrevoke). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `dev_…`. | | `name` | `String` | | Display name. | | `state` | `String` | | `pending`, `enrolled`, `expired` or `revoked`. | | `platform` | `String` | | Operating system it reported (`macos`, `windows`, `linux`); empty when unknown. | | `created_at` / `createdAt` | `u64` | | Registered at (Unix seconds). | | `enrolled_until` / `enrolledUntil` | `Option` | | Re-verification due at (Unix seconds). | | `last_seen` / `lastSeen` | `Option` | | Last session (Unix seconds). | | `current` | `bool` | | This device. | ## `RelayAuditEvent` record One event of the account's audit log. | Field | Type | Default | Description | | --- | --- | --- | --- | | `ts` | `u64` | | Unix seconds. | | `kind` | `String` | | Kind (`machine_access`, `device_enrolled`, `share_added`, ...). | | `device` | `Option` | | Acting device. | | `machine` | `Option` | | Machine id. | | `subject` | `Option` | | Other party. | | `detail` | `Option` | | Detail. | ## `DeviceEnrollment` record `Devices.enroll`'s result. Returned by [`Devices.enroll`](#devicesenroll). | Field | Type | Default | Description | | --- | --- | --- | --- | | `device` | [`RelayDevice`](#relaydevice-record) | | This device. | | `enrolled` | `bool` | | Enrolled now (right after a fresh sign-in). | | `code` | `Option` | | The one-time code to approve from an enrolled device otherwise. | --- # Host and relay Unattended host setup and the cua-relay directory client. Set this machine up for unattended access, and reach account machines through the relay. ## `Host` This machine as an unattended-access host. | Method | Description | | --- | --- | | [`new`](#hostnew) | Host state under `cua_home` (default `$CUA_HOME` or `~/.cua`). | | [`configure`](#hostconfigure) | Changes what this machine shares: its own desktop, and whether it creates Spaces for your other devices (and their limits). | | [`pause_signed_out`](#hostpause_signed_out) | Pause relay sharing while nobody is signed in to the owner's account: the host leaves the relay and stays off it (also across a restart) with its setup kept. | | [`remove`](#hostremove) | Unregister, uninstall the service and delete the host state. | | [`resume_signed_in`](#hostresume_signed_in) | Resume relay sharing paused by `Host.pause_signed_out` once `account` (the signed-in account's id or email) is signed in; refused when it is not the machine's owner. | | [`setup`](#hostsetup) | Installs and starts the host service (relay mode needs `account_token`). | | [`start_sharing`](#hoststart_sharing) | Start sharing again. | | [`status`](#hoststatus) | Current state. | | [`stop_sharing`](#hoststop_sharing) | Stop sharing (cut clients, refuse new ones). | ### `Host.new` Host state under `cua_home` (default `$CUA_HOME` or `~/.cua`). **Python** ```python Host(cua_home: Optional[str]) ``` **TypeScript** ```ts new Host(cuaHome: string | undefined) ``` **Swift** ```swift init(cuaHome: String?) ``` **Kotlin** ```kotlin constructor(cuaHome: String?) ``` **Rust** ```rust pub fn new(cua_home: Option) -> Arc ``` | Parameter | Type | Default | | --- | --- | --- | | `cua_home` | `Option` | required | **Returns** [`Host`](#host) ### `Host.configure` Changes what this machine shares: its own desktop, and whether it creates Spaces for your other devices (and their limits). Turning the desktop on or off restarts the host service. **Python** ```python async def configure(self, change: HostSettingsChange) -> HostStatus ``` **TypeScript** ```ts configure(change: HostSettingsChange, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func configure(change: HostSettingsChange) async throws -> HostStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun configure(change: HostSettingsChange): HostStatus ``` **Rust** ```rust pub async fn configure(&self, change: HostSettingsChange) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `change` | [`HostSettingsChange`](#hostsettingschange-record) | required | **Returns** [`HostStatus`](#hoststatus-record) · **Async** · **Raises** [`CuaError`]() ### `Host.pause_signed_out` Pause relay sharing while nobody is signed in to the owner's account: the host leaves the relay and stays off it (also across a restart) with its setup kept. Direct mode is left alone. **Python** ```python async def pause_signed_out(self) -> HostStatus ``` **TypeScript** ```ts pauseSignedOut(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func pauseSignedOut() async throws -> HostStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun pauseSignedOut(): HostStatus ``` **Rust** ```rust pub async fn pause_signed_out(&self) -> Result ``` **Returns** [`HostStatus`](#hoststatus-record) · **Async** · **Raises** [`CuaError`]() ### `Host.remove` Unregister, uninstall the service and delete the host state. **Python** ```python async def remove(self) -> None ``` **TypeScript** ```ts remove(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func remove() async throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun remove() ``` **Rust** ```rust pub async fn remove(&self) -> Result<(), CuaError> ``` **Async** · **Raises** [`CuaError`]() ### `Host.resume_signed_in` Resume relay sharing paused by `Host.pause_signed_out` once `account` (the signed-in account's id or email) is signed in; refused when it is not the machine's owner. **Python** ```python async def resume_signed_in(self, account: str) -> HostStatus ``` **TypeScript** ```ts resumeSignedIn(account: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func resumeSignedIn(account: String) async throws -> HostStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun resumeSignedIn(account: String): HostStatus ``` **Rust** ```rust pub async fn resume_signed_in(&self, account: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `account` | `String` | required | **Returns** [`HostStatus`](#hoststatus-record) · **Async** · **Raises** [`CuaError`]() ### `Host.setup` Installs and starts the host service (relay mode needs `account_token`). **Python** ```python async def setup(self, options: HostSetupOptions, account_token: Optional[str]) -> HostStatus ``` **TypeScript** ```ts setup(options: HostSetupOptions, accountToken: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func setup(options: HostSetupOptions, accountToken: String?) async throws -> HostStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun setup(options: HostSetupOptions, accountToken: String?): HostStatus ``` **Rust** ```rust pub async fn setup(&self, options: HostSetupOptions, account_token: Option) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `options` | [`HostSetupOptions`](#hostsetupoptions-record) | required | | `account_token` | `Option` | required | **Returns** [`HostStatus`](#hoststatus-record) · **Async** · **Raises** [`CuaError`]() ### `Host.start_sharing` Start sharing again. **Python** ```python async def start_sharing(self) -> HostStatus ``` **TypeScript** ```ts startSharing(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func startSharing() async throws -> HostStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun startSharing(): HostStatus ``` **Rust** ```rust pub async fn start_sharing(&self) -> Result ``` **Returns** [`HostStatus`](#hoststatus-record) · **Async** · **Raises** [`CuaError`]() ### `Host.status` Current state. **Python** ```python async def status(self) -> HostStatus ``` **TypeScript** ```ts status(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func status() async throws -> HostStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun status(): HostStatus ``` **Rust** ```rust pub async fn status(&self) -> Result ``` **Returns** [`HostStatus`](#hoststatus-record) · **Async** · **Raises** [`CuaError`]() ### `Host.stop_sharing` Stop sharing (cut clients, refuse new ones). **Python** ```python async def stop_sharing(self) -> HostStatus ``` **TypeScript** ```ts stopSharing(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func stopSharing() async throws -> HostStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun stopSharing(): HostStatus ``` **Rust** ```rust pub async fn stop_sharing(&self) -> Result ``` **Returns** [`HostStatus`](#hoststatus-record) · **Async** · **Raises** [`CuaError`]() ## `Relay` The signed-in account's view of a cua-relay. | Method | Description | | --- | --- | | [`new`](#relaynew) | A relay (`None` = the relay `cua` uses: `CUA_RELAY_URL`, else this machine's host setup, else `https://relay.cua.ai`) seen as the account behind `account_token` (a cua.ai access token). | | [`connect`](#relayconnect) | Connects to a machine's spacesd through `/m/`, with the account token as bearer (the relay swaps it for its signed identity). | | [`machine`](#relaymachine) | One machine. | | [`machines`](#relaymachines) | Machines the account owns or that are shared with it. | | [`start_sharing`](#relaystart_sharing) | Accepts clients again. | | [`stop_sharing`](#relaystop_sharing) | Cuts every client of a machine the account owns and refuses new ones. | | Accessor | Returns | Description | | --- | --- | --- | | `url()` | `String` | Normalized relay base URL. | ### `Relay.new` A relay (`None` = the relay `cua` uses: `CUA_RELAY_URL`, else this machine's host setup, else `https://relay.cua.ai`) seen as the account behind `account_token` (a cua.ai access token). **Python** ```python Relay(relay_url: Optional[str], account_token: str) ``` **TypeScript** ```ts new Relay(relayUrl: string | undefined, accountToken: string) ``` **Swift** ```swift init(relayUrl: String?, accountToken: String) throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) constructor(relayUrl: String?, accountToken: String) ``` **Rust** ```rust pub fn new(relay_url: Option, account_token: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `relay_url` | `Option` | required | | `account_token` | `String` | required | **Returns** [`Relay`](#relay) · **Raises** [`CuaError`]() ### `Relay.connect` Connects to a machine's spacesd through `/m/`, with the account token as bearer (the relay swaps it for its signed identity). **Python** ```python async def connect(self, machine_id: str) -> SpacesdClient ``` **TypeScript** ```ts connect(machineId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func connect(machineId: String) async throws -> SpacesdClient ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun connect(machineId: String): SpacesdClient ``` **Rust** ```rust pub async fn connect(&self, machine_id: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `machine_id` | `String` | required | **Returns** [`SpacesdClient`]() · **Async** · **Raises** [`CuaError`]() ### `Relay.machine` One machine. **Python** ```python async def machine(self, machine_id: str) -> RelayMachine ``` **TypeScript** ```ts machine(machineId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func machine(machineId: String) async throws -> RelayMachine ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun machine(machineId: String): RelayMachine ``` **Rust** ```rust pub async fn machine(&self, machine_id: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `machine_id` | `String` | required | **Returns** [`RelayMachine`](#relaymachine-record) · **Async** · **Raises** [`CuaError`]() ### `Relay.machines` Machines the account owns or that are shared with it. **Python** ```python async def machines(self) -> List[RelayMachine] ``` **TypeScript** ```ts machines(asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func machines() async throws -> [RelayMachine] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun machines(): List ``` **Rust** ```rust pub async fn machines(&self) -> Result, CuaError> ``` **Returns** [`Vec`](#relaymachine-record) · **Async** · **Raises** [`CuaError`]() ### `Relay.start_sharing` Accepts clients again. **Python** ```python async def start_sharing(self, machine_id: str) -> RelayMachine ``` **TypeScript** ```ts startSharing(machineId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func startSharing(machineId: String) async throws -> RelayMachine ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun startSharing(machineId: String): RelayMachine ``` **Rust** ```rust pub async fn start_sharing(&self, machine_id: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `machine_id` | `String` | required | **Returns** [`RelayMachine`](#relaymachine-record) · **Async** · **Raises** [`CuaError`]() ### `Relay.stop_sharing` Cuts every client of a machine the account owns and refuses new ones. **Python** ```python async def stop_sharing(self, machine_id: str) -> RelayMachine ``` **TypeScript** ```ts stopSharing(machineId: string, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func stopSharing(machineId: String) async throws -> RelayMachine ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun stopSharing(machineId: String): RelayMachine ``` **Rust** ```rust pub async fn stop_sharing(&self, machine_id: String) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `machine_id` | `String` | required | **Returns** [`RelayMachine`](#relaymachine-record) · **Async** · **Raises** [`CuaError`]() ## `HostAccessRecord` record One access to this machine. | Field | Type | Default | Description | | --- | --- | --- | --- | | `at_ms` / `atMs` | `i64` | | Epoch ms. | | `via` | `String` | | `relay`, `viewer` or `token`. | | `who` | `String` | | Who. | | `what` | `String` | | What they used (a service name, or `MCP`). | ## `HostPermission` record A macOS privacy pane the user must grant. | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | `screen-recording` or `accessibility`. | | `title` | `String` | | Title. | | `settings_url` / `settingsUrl` | `String` | | `x-apple.systempreferences:` URL. | | `instructions` | `String` | | Instructions. | ## `HostProvidedSpace` record A Space this machine provides to one of your devices. | Field | Type | Default | Description | | --- | --- | --- | --- | | `relay_machine` / `relayMachine` | `String` | | The relay machine it is reached as (`relay:`). | | `local_space` / `localSpace` | `String` | | The Space on this machine (`local:`). | | `name` | `String` | | Display name. | | `image` | `String` | | The image, as requested. | | `os` | `String` | | `linux`, `macos` (empty when unknown). | | `kind` | `String` | | `container` or `vm`. | | `created_by` / `createdBy` | `String` | | Who created it. | | `created_at_ms` / `createdAtMs` | `i64` | | Epoch ms. | ## `HostSettingsChange` record A change to this machine's Spaces settings (`None` keeps a value). | Field | Type | Default | Description | | --- | --- | --- | --- | | `share_desktop` / `shareDesktop` | `Option` | `None` | Share this desktop. | | `provide_spaces` / `provideSpaces` | `Option` | `None` | Provide Spaces. | | `max_spaces` / `maxSpaces` | `Option` | `None` | Provided Spaces at once (0: no limit). | | `max_macos_vms` / `maxMacosVms` | `Option` | `None` | macOS VMs at once (0 to 2, Apple's license). | ## `HostSetupOptions` record `cua host setup` options. | Field | Type | Default | Description | | --- | --- | --- | --- | | `mode` | `Option` | | `relay` (default) or `direct`. | | `relay_url` / `relayUrl` | `Option` | | Relay URL (relay mode; default `CUA_RELAY_URL` / `https://relay.cua.ai`). | | `direct` | `Option` | | `ip:port` (direct mode; default `0.0.0.0:3211`). | | `name` | `Option` | | Display name (default: host name). | | `allow` | `Vec` | | Accounts (ids or emails) allowed besides the owner. | | `driver_bin` / `driverBin` | `Option` | | Driver binary (else `CUA_SPACESD_BIN`, bundled, or download). | | `runner` | `Option` | | `auto`, `systemd`, `launchd`, `windows-task` or `process`. | | `profile` | `Option` | `None` | `desktop` (share this desktop; the default) or `spare` (do not share the desktop; provide Spaces). The two settings below override it. | | `share_desktop` / `shareDesktop` | `Option` | `None` | Share this machine's own desktop as a Space. | | `provide_spaces` / `provideSpaces` | `Option` | `None` | Create Spaces for your enrolled devices on this machine (relay mode). | ## `HostSpacesAuditRecord` record One line of this machine's Spaces audit (remote creates, deletes, refusals and settings changes). | Field | Type | Default | Description | | --- | --- | --- | --- | | `at_ms` / `atMs` | `i64` | | Epoch ms. | | `action` | `String` | | `create`, `delete`, `refused`, `failed` or `config`. | | `who` | `String` | | Who (an account, or `local`). | | `space` | `String` | | The relay machine or Space. | | `detail` | `String` | | Detail. | ## `HostStatus` record Host status. Returned by [`Host.configure`](#hostconfigure), [`Host.pause_signed_out`](#hostpause_signed_out), [`Host.resume_signed_in`](#hostresume_signed_in), [`Host.setup`](#hostsetup), [`Host.start_sharing`](#hoststart_sharing), [`Host.status`](#hoststatus), [`Host.stop_sharing`](#hoststop_sharing). | Field | Type | Default | Description | | --- | --- | --- | --- | | `configured` | `bool` | | Set up. | | `mode` | `Option` | | `relay` or `direct`. | | `relay_url` / `relayUrl` | `Option` | | Relay URL. | | `direct_url` / `directUrl` | `Option` | | Direct URL. | | `env_token_path` / `envTokenPath` | `Option` | | Env token file (direct mode). | | `machine_id` / `machineId` | `Option` | | Machine id. | | `name` | `Option` | | Name. | | `sharing` | `bool` | | Accepting clients. | | `service_installed` / `serviceInstalled` | `bool` | | Service installed. | | `service_running` / `serviceRunning` | `bool` | | Service running. | | `service_kind` / `serviceKind` | `String` | | Runner kind. | | `online` | `Option` | | Online at the relay. | | `clients` | [`Vec`](#relayclientinfo-record) | | Connected clients. | | `allow` | `Vec` | | Allowlist. | | `permissions` | [`Vec`](#hostpermission-record) | | Permission panes (macOS). | | `error` | `Option` | | Relay error. | | `recent_access` / `recentAccess` | [`Vec`](#hostaccessrecord-record) | `[]` | Who reached this machine recently, newest first (the driver's hash-chained access log). | | `access_log_error` / `accessLogError` | `Option` | `None` | Set when the access log does not verify (edited or truncated). | | `share_desktop` / `shareDesktop` | `bool` | `true` | This machine's desktop is a Space. | | `provide_spaces` / `provideSpaces` | `bool` | `false` | This machine provides Spaces to your other devices. | | `max_spaces` / `maxSpaces` | `u32` | `0` | Provided Spaces at once (0: no limit). | | `max_macos_vms` / `maxMacosVms` | `u32` | `0` | macOS VMs at once (at most two). | | `provided_spaces` / `providedSpaces` | [`Vec`](#hostprovidedspace-record) | `[]` | The Spaces this machine provides now. | | `spaces_audit` / `spacesAudit` | [`Vec`](#hostspacesauditrecord-record) | `[]` | The Spaces audit, newest first. | | `spaces_audit_error` / `spacesAuditError` | `Option` | `None` | Set when the Spaces audit does not verify. | | `paused_signed_out` / `pausedSignedOut` | `bool` | `false` | Relay sharing is paused until the owner signs in again (`Host.pause_signed_out`). | | `owner` | `Option` | `None` | The account this machine is registered to (relay mode): its id. | | `owner_email` / `ownerEmail` | `Option` | `None` | The owner's email, when the relay gave one. | ## `RelayClientInfo` record Somebody connected to a machine through the relay. | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | User id. | | `email` | `Option` | | Email, when shared by the identity provider. | | `name` | `Option` | | Display name. | | `streams` | `u32` | | Open streams. | | `since` | `u64` | | Unix seconds of the first open stream. | ## `RelayMachine` record A machine of the account's relay directory (`relay:`). Returned by [`Devices.confirm_machine`](), [`Relay.machine`](#relaymachine), [`Relay.machines`](#relaymachines), [`Relay.start_sharing`](#relaystart_sharing), [`Relay.stop_sharing`](#relaystop_sharing). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Machine id. | | `space_id` / `spaceId` | `String` | | `relay:`. | | `name` | `String` | | Display name. | | `owner_id` / `ownerId` | `String` | | Owner account id. | | `owner_email` / `ownerEmail` | `Option` | | Owner email. | | `role` | `String` | | `owner` or `shared`. | | `online` | `bool` | | Connected to the relay. | | `sharing` | `bool` | | Accepting clients. | | `version` | `String` | | spacesd version. | | `url` | `String` | | `/m/`. | | `allow` | `Vec` | | Allowlist (owner view only). | | `clients` | [`Vec`](#relayclientinfo-record) | | Connected clients. | | `confirmed` | `bool` | | Registered with an enrolled device's signature or MFA, or confirmed since from one; `true` for anything a relay registered before this check existed. `false` shows the machine as "new" until an enrolled device confirms it (`Devices.confirm_machine`) (S5). | --- # Auth Sign-in and the credential store the SDK, the CLI and the daemon share. `cua.auth()` signs in and reads the shared session. Cloud calls use it when no Fleet token or client credentials are configured. Python programs usually use the high-level API instead: [`cua_sandbox.configure()`](). ## `Auth` Sign-in and the shared session. Returned by [`Cua.auth`](). | Method | Description | | --- | --- | | [`access_token`](#authaccess_token) | A valid access token (refreshed when it expires within a minute, or when `force`). | | [`begin_login`](#authbegin_login) | Starts a sign-in. | | [`devices`](#authdevices) | This device on `relay_url` (`None` = the relay `cua` uses: `CUA_RELAY_URL`, else this machine's host setup, else `https://relay.cua.ai`) as the signed-in account, shown as `name` (default: this computer's name). | | [`logout`](#authlogout) | Revokes (best effort) and removes the session. | | [`status`](#authstatus) | The stored session (no network). | ### `Auth.access_token` A valid access token (refreshed when it expires within a minute, or when `force`). `Unauthenticated` when not signed in. **Python** ```python async def access_token(self, force: bool) -> str ``` **TypeScript** ```ts accessToken(force: boolean, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func accessToken(force: Bool) async throws -> String ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun accessToken(force: Boolean): String ``` **Rust** ```rust pub async fn access_token(&self, force: bool) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `force` | `bool` | required | **Returns** `String` · **Async** · **Raises** [`CuaError`]() ([`Unauthenticated`]()) ### `Auth.begin_login` Starts a sign-in. `flow`: `auto` (default: browser, else device code), `browser` or `device`. **Python** ```python async def begin_login(self, flow: Optional[str]) -> LoginAttempt ``` **TypeScript** ```ts beginLogin(flow: string | undefined, asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func beginLogin(flow: String?) async throws -> LoginAttempt ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun beginLogin(flow: String?): LoginAttempt ``` **Rust** ```rust pub async fn begin_login(&self, flow: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `flow` | `Option` | required | **Returns** [`LoginAttempt`](#loginattempt) · **Async** · **Raises** [`CuaError`]() ### `Auth.devices` This device on `relay_url` (`None` = the relay `cua` uses: `CUA_RELAY_URL`, else this machine's host setup, else `https://relay.cua.ai`) as the signed-in account, shown as `name` (default: this computer's name). **Python** ```python def devices(self, relay_url: Optional[str], name: Optional[str]) -> Devices ``` **TypeScript** ```ts devices(relayUrl: string | undefined, name: string | undefined): DevicesLike ``` **Swift** ```swift func devices(relayUrl: String?, name: String?) throws -> Devices ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun devices(relayUrl: String?, name: String?): Devices ``` **Rust** ```rust pub fn devices(&self, relay_url: Option, name: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `relay_url` | `Option` | required | | `name` | `Option` | required | **Returns** [`Devices`]() · **Raises** [`CuaError`]() ### `Auth.logout` Revokes (best effort) and removes the session. Returns whether one existed. **Python** ```python async def logout(self) -> bool ``` **TypeScript** ```ts logout(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func logout() async throws -> Bool ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun logout(): Boolean ``` **Rust** ```rust pub async fn logout(&self) -> Result ``` **Returns** `bool` · **Async** · **Raises** [`CuaError`]() ### `Auth.status` The stored session (no network). **Python** ```python def status(self) -> AuthStatus ``` **TypeScript** ```ts status(): AuthStatus ``` **Swift** ```swift func status() throws -> AuthStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun status(): AuthStatus ``` **Rust** ```rust pub fn status(&self) -> Result ``` **Returns** [`AuthStatus`](#authstatus-record) · **Raises** [`CuaError`]() ## `LoginAttempt` A started sign-in. Show `url` (and `user_code`), then await `wait()`. Returned by [`Auth.begin_login`](#authbegin_login). | Method | Description | | --- | --- | | [`wait`](#loginattemptwait) | Waits for the user, stores the session and returns who signed in. | | Accessor | Returns | Description | | --- | --- | --- | | `method()` | [`LoginMethod`](#loginmethod-enum) | Browser or device. | | `note()` | `Option` | Why a device code is used although browser sign-in was asked for. | | `url()` | `String` | The URL to open. | | `user_code()` | `Option` | Device flow: the code to enter. | ### `LoginAttempt.wait` Waits for the user, stores the session and returns who signed in. Only the first call waits; later calls fail. This device then registers with the relay (the one `cua` uses: `CUA_RELAY_URL`, else this machine's host setup, else relay.cua.ai), creating its device key on first use, so the fresh sign-in enrolls or re-verifies it without an approval. That step is best effort and never fails the sign-in. **Python** ```python async def wait(self) -> AuthIdentity ``` **TypeScript** ```ts wait(asyncOpts_?: { signal: AbortSignal }): Promise ``` **Swift** ```swift func wait() async throws -> AuthIdentity ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun wait(): AuthIdentity ``` **Rust** ```rust pub async fn wait(&self) -> Result ``` **Returns** [`AuthIdentity`](#authidentity-record) · **Async** · **Raises** [`CuaError`]() ## `AuthIdentity` record Who is signed in (from token claims; display only). Returned by [`LoginAttempt.wait`](#loginattemptwait). | Field | Type | Default | Description | | --- | --- | --- | --- | | `username` | `Option` | | `preferred_username`. | | `email` | `Option` | | `email`. | | `name` | `Option` | | `name`. | | `subject` | `Option` | | `sub`. | | `display` | `Option` | | One display string (email, else username, else subject). | ## `AuthStatus` record The stored session, read without network access. Returned by [`Auth.status`](#authstatus). | Field | Type | Default | Description | | --- | --- | --- | --- | | `logged_in` / `loggedIn` | `bool` | | A session is stored. | | `identity` | [`Option`](#authidentity-record) | | Who. | | `expires_at` / `expiresAt` | `Option` | | RFC 3339 expiry of the current access token (it refreshes itself). | | `store` | `String` | | Where credentials live. | ## `LoginMethod` enum How a sign-in proceeds. **Python** ```python LoginMethod.BROWSER LoginMethod.DEVICE ``` **TypeScript** ```ts LoginMethod.Browser LoginMethod.Device ``` **Swift** ```swift .browser .device ``` **Kotlin** ```kotlin LoginMethod.BROWSER LoginMethod.DEVICE ``` **Rust** ```rust LoginMethod::Browser LoginMethod::Device ``` | Variant | Description | | --- | --- | | `Browser` | Open `url` in a browser on this machine; it redirects back here. | | `Device` | Open `url` on any device and enter `user_code`. | ## `may_have_fleet_session` Whether a `cua auth login` session may be stored, decided without reading the OS credential vault (the non-secret session marker, or the file store's file). Implicit callers (the default sandbox listing) check this before reading the session; an explicit read of a session stored before markers existed writes the marker. **Python** ```python def may_have_fleet_session() -> bool ``` **TypeScript** ```ts function mayHaveFleetSession(): boolean ``` **Swift** ```swift func mayHaveFleetSession() -> Bool ``` **Kotlin** ```kotlin fun mayHaveFleetSession(): Boolean ``` **Rust** ```rust pub fn may_have_fleet_session() -> bool ``` **Returns** `bool` --- # Agent setup Detect coding agents, install cua skills and configure their MCP servers. `cua.agent_setup()` onboards coding agents on this machine: skills and MCP configuration. ## `AgentSetup` Agent onboarding for this machine's user. Returned by [`Cua.agent_setup`](). | Method | Description | | --- | --- | | [`configure_mcp`](#agentsetupconfigure_mcp) | Configures the MCP server (default: `cua mcp`, `cua` resolved on PATH) for `agents`. | | [`install_skills`](#agentsetupinstall_skills) | Installs `skills` (empty: all bundled) for `agents`. | | [`remove`](#agentsetupremove) | Removes what cua added for `agents` (skills and/or MCP). | | [`select`](#agentsetupselect) | Resolves `all` (installed agents), `none` or `claude,codex` to ids. | | [`set_onboarding_declined`](#agentsetupset_onboarding_declined) | Saves or clears the "never" answer (`~/.cua/config`). | | [`setup_cua_driver`](#agentsetupsetup_cua_driver) | Background computer-use for `agents`: the bundled `cua-driver` skill (`skills`) and the `cua-driver mcp` server (`mcp`), launching `command` (default: the installed cua-driver). | | [`update`](#agentsetupupdate) | Refreshes installed cua skills and re-points managed MCP entries. | | Accessor | Returns | Description | | --- | --- | --- | | `detect()` | [`Vec`](#agentinfo-record) | Every supported agent, installed or not (read-only). | | `onboarding_declined()` | `bool` | Whether the user said "never" to agent onboarding. | | `skills()` | [`Vec`](#bundledskill-record) | The skills bundled with this SDK. | | `status()` | [`Vec`](#agentinfo-record) | Same as `detect`, named for status displays. | ### `AgentSetup.configure_mcp` Configures the MCP server (default: `cua mcp`, `cua` resolved on PATH) for `agents`. **Python** ```python async def configure_mcp(self, agents: List[str], server: Optional[AgentMcpServer]) -> List[AgentSetupOutcome] ``` **TypeScript** ```ts configureMcp(agents: Array, server: AgentMcpServer | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func configureMcp(agents: [String], server: AgentMcpServer?) async throws -> [AgentSetupOutcome] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun configureMcp(agents: List, server: AgentMcpServer?): List ``` **Rust** ```rust pub async fn configure_mcp(&self, agents: Vec, server: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `agents` | `Vec` | required | | `server` | [`Option`](#agentmcpserver-record) | required | **Returns** [`Vec`](#agentsetupoutcome-record) · **Async** · **Raises** [`CuaError`]() ### `AgentSetup.install_skills` Installs `skills` (empty: all bundled) for `agents`. Folders cua did not write are skipped unless `force`. **Python** ```python async def install_skills(self, agents: List[str], skills: List[str], force: bool) -> List[AgentSetupOutcome] ``` **TypeScript** ```ts installSkills(agents: Array, skills: Array, force: boolean, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func installSkills(agents: [String], skills: [String], force: Bool) async throws -> [AgentSetupOutcome] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun installSkills(agents: List, skills: List, force: Boolean): List ``` **Rust** ```rust pub async fn install_skills(&self, agents: Vec, skills: Vec, force: bool) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `agents` | `Vec` | required | | `skills` | `Vec` | required | | `force` | `bool` | required | **Returns** [`Vec`](#agentsetupoutcome-record) · **Async** · **Raises** [`CuaError`]() ### `AgentSetup.remove` Removes what cua added for `agents` (skills and/or MCP). **Python** ```python async def remove(self, agents: List[str], skills: bool, mcp: bool) -> List[AgentSetupOutcome] ``` **TypeScript** ```ts remove(agents: Array, skills: boolean, mcp: boolean, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func remove(agents: [String], skills: Bool, mcp: Bool) async throws -> [AgentSetupOutcome] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun remove(agents: List, skills: Boolean, mcp: Boolean): List ``` **Rust** ```rust pub async fn remove(&self, agents: Vec, skills: bool, mcp: bool) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `agents` | `Vec` | required | | `skills` | `bool` | required | | `mcp` | `bool` | required | **Returns** [`Vec`](#agentsetupoutcome-record) · **Async** · **Raises** [`CuaError`]() ### `AgentSetup.select` Resolves `all` (installed agents), `none` or `claude,codex` to ids. **Python** ```python def select(self, selection: str) -> List[str] ``` **TypeScript** ```ts select(selection: string): Array ``` **Swift** ```swift func select(selection: String) throws -> [String] ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun select(selection: String): List ``` **Rust** ```rust pub fn select(&self, selection: String) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `selection` | `String` | required | **Returns** `Vec` · **Raises** [`CuaError`]() ### `AgentSetup.set_onboarding_declined` Saves or clears the "never" answer (`~/.cua/config`). **Python** ```python def set_onboarding_declined(self, declined: bool) -> None ``` **TypeScript** ```ts setOnboardingDeclined(declined: boolean): void ``` **Swift** ```swift func setOnboardingDeclined(declined: Bool) throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun setOnboardingDeclined(declined: Boolean) ``` **Rust** ```rust pub fn set_onboarding_declined(&self, declined: bool) -> Result<(), CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `declined` | `bool` | required | **Raises** [`CuaError`]() ### `AgentSetup.setup_cua_driver` Background computer-use for `agents`: the bundled `cua-driver` skill (`skills`) and the `cua-driver mcp` server (`mcp`), launching `command` (default: the installed cua-driver). The same step as `cua agents setup --cua-driver` and the installers' `cua-driver` item. **Python** ```python async def setup_cua_driver(self, agents: List[str], command: Optional[str], skills: bool, mcp: bool) -> List[AgentSetupOutcome] ``` **TypeScript** ```ts setupCuaDriver(agents: Array, command: string | undefined, skills: boolean, mcp: boolean, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func setupCuaDriver(agents: [String], command: String?, skills: Bool, mcp: Bool) async throws -> [AgentSetupOutcome] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun setupCuaDriver(agents: List, command: String?, skills: Boolean, mcp: Boolean): List ``` **Rust** ```rust pub async fn setup_cua_driver(&self, agents: Vec, command: Option, skills: bool, mcp: bool) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `agents` | `Vec` | required | | `command` | `Option` | required | | `skills` | `bool` | required | | `mcp` | `bool` | required | **Returns** [`Vec`](#agentsetupoutcome-record) · **Async** · **Raises** [`CuaError`]() ### `AgentSetup.update` Refreshes installed cua skills and re-points managed MCP entries. **Python** ```python async def update(self, server: Optional[AgentMcpServer]) -> List[AgentSetupOutcome] ``` **TypeScript** ```ts update(server: AgentMcpServer | undefined, asyncOpts_?: { signal: AbortSignal }): Promise> ``` **Swift** ```swift func update(server: AgentMcpServer?) async throws -> [AgentSetupOutcome] ``` **Kotlin** ```kotlin @Throws(CuaException::class) suspend fun update(server: AgentMcpServer?): List ``` **Rust** ```rust pub async fn update(&self, server: Option) -> Result, CuaError> ``` | Parameter | Type | Default | | --- | --- | --- | | `server` | [`Option`](#agentmcpserver-record) | required | **Returns** [`Vec`](#agentsetupoutcome-record) · **Async** · **Raises** [`CuaError`]() ## `AgentInfo` record One AI coding agent and what cua configured for it. Returned by [`AgentSetup.detect`](#agentsetup), [`AgentSetup.status`](#agentsetup). | Field | Type | Default | Description | | --- | --- | --- | --- | | `id` | `String` | | Registry id (`claude-code`, `codex`, `cursor`, ...). | | `name` | `String` | | Display name. | | `installed` | `bool` | | Whether it looks installed. | | `evidence` | `Vec` | | What gave it away (`bin:`, `path:`, `app:`). | | `skills_dir` / `skillsDir` | `Option` | | User skills directory, when the agent supports Agent Skills. | | `mcp_config` / `mcpConfig` | `Option` | | User MCP config file, when the agent supports MCP. | | `mcp_format` / `mcpFormat` | `Option` | | `json`, `toml` or `yaml`. | | `cua_configured` / `cuaConfigured` | `bool` | | A `cua` MCP server launching a cua binary is configured. | | `cua_managed` / `cuaManaged` | `bool` | | That entry is the one cua wrote. | | `cua_driver_configured` / `cuaDriverConfigured` | `bool` | | A `cua-driver` MCP server (background computer-use) is configured. | | `skills_installed` / `skillsInstalled` | `Vec` | | Bundled skills present in the skills directory. | | `skills_outdated` / `skillsOutdated` | `Vec` | | Installed bundled skills that differ from this SDK's copy. | | `error` | `Option` | | Problem reading the MCP config (for example a malformed file). | | `unverified` | `Option` | | What could not be verified from the agent's official docs. | ## `AgentMcpServer` record The MCP server to register. | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | `"cua"` | Server name (`cua`). | | `command` | `String` | | Launch command (an absolute `cua` path is best: GUI agents often lack the shell PATH). | | `args` | `Vec` | | Arguments (`["mcp"]`). | | `env` | `HashMap` | | Extra environment. | ## `AgentSetupOutcome` record One target's outcome. Returned by [`AgentSetup.configure_mcp`](#agentsetupconfigure_mcp), [`AgentSetup.install_skills`](#agentsetupinstall_skills), [`AgentSetup.remove`](#agentsetupremove), [`AgentSetup.setup_cua_driver`](#agentsetupsetup_cua_driver), [`AgentSetup.update`](#agentsetupupdate). | Field | Type | Default | Description | | --- | --- | --- | --- | | `agents` | `Vec` | | Agents served by this target. | | `target` | [`AgentSetupTarget`](#agentsetuptarget-enum) | | Kind. | | `item` | `String` | | Skill or server name. | | `path` | `String` | | Skill folder or config file. | | `change` | [`AgentSetupChange`](#agentsetupchange-enum) | | Result. | | `detail` | `String` | | Explanation. | | `backup` | `Option` | | Backup taken before this write. | ## `BundledSkill` record A skill bundled with the SDK. Returned by [`AgentSetup.skills`](#agentsetup). | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `String` | | Name. | | `description` | `String` | | Description. | | `version` | `String` | | Version. | | `files` | `u32` | | Number of files. | ## `AgentSetupChange` enum What happened to one target. **Python** ```python AgentSetupChange.CREATED AgentSetupChange.UPDATED AgentSetupChange.UNCHANGED AgentSetupChange.REMOVED AgentSetupChange.SKIPPED AgentSetupChange.FAILED ``` **TypeScript** ```ts AgentSetupChange.Created AgentSetupChange.Updated AgentSetupChange.Unchanged AgentSetupChange.Removed AgentSetupChange.Skipped AgentSetupChange.Failed ``` **Swift** ```swift .created .updated .unchanged .removed .skipped .failed ``` **Kotlin** ```kotlin AgentSetupChange.CREATED AgentSetupChange.UPDATED AgentSetupChange.UNCHANGED AgentSetupChange.REMOVED AgentSetupChange.SKIPPED AgentSetupChange.FAILED ``` **Rust** ```rust AgentSetupChange::Created AgentSetupChange::Updated AgentSetupChange::Unchanged AgentSetupChange::Removed AgentSetupChange::Skipped AgentSetupChange::Failed ``` | Variant | Description | | --- | --- | | `Created` | Written where nothing was. | | `Updated` | Replaced or edited. | | `Unchanged` | Already as wanted. | | `Removed` | Removed (or the previous entry restored). | | `Skipped` | Deliberately left alone (see detail). | | `Failed` | Failed (see detail); the target was left untouched. | ## `AgentSetupTarget` enum What an operation touched. **Python** ```python AgentSetupTarget.SKILL AgentSetupTarget.MCP ``` **TypeScript** ```ts AgentSetupTarget.Skill AgentSetupTarget.Mcp ``` **Swift** ```swift .skill .mcp ``` **Kotlin** ```kotlin AgentSetupTarget.SKILL AgentSetupTarget.MCP ``` **Rust** ```rust AgentSetupTarget::Skill AgentSetupTarget::Mcp ``` | Variant | Description | | --- | --- | | `Skill` | A skill folder. | | `Mcp` | An MCP config entry. | --- # Telemetry Anonymous usage telemetry switches. What is collected and never collected: [Telemetry and privacy](). These calls read and change the same switches as `cua telemetry`; the SDK records its own events. ## `telemetry_status` Telemetry state, and exactly what every event carries. **Python** ```python def telemetry_status() -> TelemetryStatus ``` **TypeScript** ```ts function telemetryStatus(): TelemetryStatus ``` **Swift** ```swift func telemetryStatus() -> TelemetryStatus ``` **Kotlin** ```kotlin fun telemetryStatus(): TelemetryStatus ``` **Rust** ```rust pub fn telemetry_status() -> TelemetryStatus ``` **Returns** [`TelemetryStatus`](#telemetrystatus-record) ## `telemetry_set_enabled` Turns usage telemetry on or off for this machine (`$CUA_HOME/config.toml`, the same switch as `cua telemetry off` and the Spaces app setting). The environment (`DO_NOT_TRACK`, `CUA_TELEMETRY`) still wins. **Python** ```python def telemetry_set_enabled(enabled: bool) -> TelemetryStatus ``` **TypeScript** ```ts function telemetrySetEnabled(enabled: boolean): TelemetryStatus ``` **Swift** ```swift func telemetrySetEnabled(enabled: Bool) throws -> TelemetryStatus ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun telemetrySetEnabled(enabled: Boolean): TelemetryStatus ``` **Rust** ```rust pub fn telemetry_set_enabled(enabled: bool) -> Result ``` | Parameter | Type | Default | | --- | --- | --- | | `enabled` | `bool` | required | **Returns** [`TelemetryStatus`](#telemetrystatus-record) · **Raises** [`CuaError`]() ## `telemetry_show_last` The last `limit` events queued or sent from this machine, exactly as sent, as JSON (`[{status, payload}]`). **Python** ```python def telemetry_show_last(limit: int) -> str ``` **TypeScript** ```ts function telemetryShowLast(limit: number): string ``` **Swift** ```swift func telemetryShowLast(limit: UInt32) -> String ``` **Kotlin** ```kotlin fun telemetryShowLast(limit: UInt): String ``` **Rust** ```rust pub fn telemetry_show_last(limit: u32) -> String ``` | Parameter | Type | Default | | --- | --- | --- | | `limit` | `u32` | required | **Returns** `String` ## `telemetry_schema_json` Every event and property that may be sent, as JSON. **Python** ```python def telemetry_schema_json() -> str ``` **TypeScript** ```ts function telemetrySchemaJson(): string ``` **Swift** ```swift func telemetrySchemaJson() -> String ``` **Kotlin** ```kotlin fun telemetrySchemaJson(): String ``` **Rust** ```rust pub fn telemetry_schema_json() -> String ``` **Returns** `String` ## `telemetry_reset_id` Deletes the anonymous install id and its salt. **Python** ```python def telemetry_reset_id() -> None ``` **TypeScript** ```ts function telemetryResetId(): void ``` **Swift** ```swift func telemetryResetId() throws ``` **Kotlin** ```kotlin @Throws(CuaException::class) fun telemetryResetId() ``` **Rust** ```rust pub fn telemetry_reset_id() -> Result<(), CuaError> ``` **Raises** [`CuaError`]() ## `StreamStatsReport` record One stream session's aggregate stats (sampled). | Field | Type | Default | Description | | --- | --- | --- | --- | | `transport` | `String` | | `quic`, `webrtc`, `websocket` or `grpc`. | | `codec` | `String` | | `h264`, `hevc`, `av1`, `vp8`, `vp9`, `jpeg` or `png`. | | `hw_decode` / `hwDecode` | `Option` | | Hardware decode (None: not decoded here). | | `avg_fps` / `avgFps` | `f64` | | | | `p95_latency_ms` / `p95LatencyMs` | `Option` | | p95 input-to-present or control round trip, ms. | | `height` | `u32` | | Stream height in pixels. | | `duration_ms` / `durationMs` | `u64` | | | ## `TelemetryStatus` record Telemetry state. Returned by [`telemetry_set_enabled`](#telemetry_set_enabled), [`telemetry_status`](#telemetry_status). | Field | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | `bool` | | Usage events may be sent. | | `source` | `String` | | Why (`env CUA_TELEMETRY`, `env DO_NOT_TRACK`, `config `, `CI environment ...`, `default`). | | `source_kind` / `sourceKind` | `String` | | `do_not_track`, `env`, `legacy_env`, `config`, `ci` or `default`. | | `is_ci` / `isCi` | `bool` | | Running in CI. | | `install_id` / `installId` | `Option` | | The first 8 characters of the anonymous install id, if one exists. | | `notice_shown` / `noticeShown` | `bool` | | The first-run notice was shown on this machine. | | `endpoint` | `String` | | Where batches go. | | `product` | `String` | | Which Cua program events are attributed to. | | `envelope_json` / `envelopeJson` | `String` | | The exact properties every event carries, as JSON. | ## `telemetry_acknowledge_notice` Records that the first-run notice was shown in the app's UI (nothing is sent from a machine before that). Apps that show the notice themselves call `telemetry_use_app_notice` first so the SDK does not print it. **Python** ```python def telemetry_acknowledge_notice() -> None ``` **TypeScript** ```ts function telemetryAcknowledgeNotice(): void ``` **Swift** ```swift func telemetryAcknowledgeNotice() ``` **Kotlin** ```kotlin fun telemetryAcknowledgeNotice() ``` **Rust** ```rust pub fn telemetry_acknowledge_notice() ``` ## `telemetry_flush` Sends queued events now, waiting at most `timeout_ms`; anything left is kept on disk for a later process. **Python** ```python def telemetry_flush(timeout_ms: int) -> None ``` **TypeScript** ```ts function telemetryFlush(timeoutMs: number): void ``` **Swift** ```swift func telemetryFlush(timeoutMs: UInt32) ``` **Kotlin** ```kotlin fun telemetryFlush(timeoutMs: UInt) ``` **Rust** ```rust pub fn telemetry_flush(timeout_ms: u32) ``` | Parameter | Type | Default | | --- | --- | --- | | `timeout_ms` | `u32` | required | ## `telemetry_notice_text` The first-run notice text (an app shows it in its own UI, then calls `telemetry_acknowledge_notice`). **Python** ```python def telemetry_notice_text() -> str ``` **TypeScript** ```ts function telemetryNoticeText(): string ``` **Swift** ```swift func telemetryNoticeText() -> String ``` **Kotlin** ```kotlin fun telemetryNoticeText(): String ``` **Rust** ```rust pub fn telemetry_notice_text() -> String ``` **Returns** `String` ## `telemetry_record_bench_run` Records a finished cua-bench run: the taskset (catalog name, else `custom`), the aggregate success rate (0 to 1, bucketed), the task count (bucketed) and the outcome (`ok`, `error`, `cancelled`). **Python** ```python def telemetry_record_bench_run(taskset: str, success_rate: Optional[float], task_count: int, outcome: str) -> bool ``` **TypeScript** ```ts function telemetryRecordBenchRun(taskset: string, successRate: number | undefined, taskCount: bigint, outcome: string): boolean ``` **Swift** ```swift func telemetryRecordBenchRun(taskset: String, successRate: Double?, taskCount: UInt64, outcome: String) -> Bool ``` **Kotlin** ```kotlin fun telemetryRecordBenchRun(taskset: String, successRate: Double?, taskCount: ULong, outcome: String): Boolean ``` **Rust** ```rust pub fn telemetry_record_bench_run(taskset: String, success_rate: Option, task_count: u64, outcome: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `taskset` | `String` | required | | `success_rate` | `Option` | required | | `task_count` | `u64` | required | | `outcome` | `String` | required | **Returns** `bool` ## `telemetry_record_feature` Records use of a Spaces app feature (fixed names: `space_create_local`, `teleport_drop`, ... see the docs). Returns whether it was queued; unknown names are dropped. **Python** ```python def telemetry_record_feature(feature: str) -> bool ``` **TypeScript** ```ts function telemetryRecordFeature(feature: string): boolean ``` **Swift** ```swift func telemetryRecordFeature(feature: String) -> Bool ``` **Kotlin** ```kotlin fun telemetryRecordFeature(feature: String): Boolean ``` **Rust** ```rust pub fn telemetry_record_feature(feature: String) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `feature` | `String` | required | **Returns** `bool` ## `telemetry_record_onboarding_step` Records an install or onboarding funnel step (fixed names: `onboarding_shown`, `signed_in`, `first_space_created`, ...; `first_*` steps count once per install). Returns whether it was queued. **Python** ```python def telemetry_record_onboarding_step(step: str, ok: bool) -> bool ``` **TypeScript** ```ts function telemetryRecordOnboardingStep(step: string, ok: boolean): boolean ``` **Swift** ```swift func telemetryRecordOnboardingStep(step: String, ok: Bool) -> Bool ``` **Kotlin** ```kotlin fun telemetryRecordOnboardingStep(step: String, ok: Boolean): Boolean ``` **Rust** ```rust pub fn telemetry_record_onboarding_step(step: String, ok: bool) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `step` | `String` | required | | `ok` | `bool` | required | **Returns** `bool` ## `telemetry_record_stream_stats` Records one stream session's aggregate stats (bucketed and sampled). **Python** ```python def telemetry_record_stream_stats(stats: StreamStatsReport) -> bool ``` **TypeScript** ```ts function telemetryRecordStreamStats(stats: StreamStatsReport): boolean ``` **Swift** ```swift func telemetryRecordStreamStats(stats: StreamStatsReport) -> Bool ``` **Kotlin** ```kotlin fun telemetryRecordStreamStats(stats: StreamStatsReport): Boolean ``` **Rust** ```rust pub fn telemetry_record_stream_stats(stats: StreamStatsReport) -> bool ``` | Parameter | Type | Default | | --- | --- | --- | | `stats` | [`StreamStatsReport`](#streamstatsreport-record) | required | **Returns** `bool` ## `telemetry_set_surface` Names the SDK surface events are attributed to. Bindings call it at import: `sdk_python`, `sdk_typescript`, `sdk_swift`, `sdk_kotlin`; apps pass their own product (`spaces_app`). Unknown names are ignored. **Python** ```python def telemetry_set_surface(surface: str, version: str) -> None ``` **TypeScript** ```ts function telemetrySetSurface(surface: string, version: string): void ``` **Swift** ```swift func telemetrySetSurface(surface: String, version: String) ``` **Kotlin** ```kotlin fun telemetrySetSurface(surface: String, version: String) ``` **Rust** ```rust pub fn telemetry_set_surface(surface: String, version: String) ``` | Parameter | Type | Default | | --- | --- | --- | | `surface` | `String` | required | | `version` | `String` | required | ## `telemetry_use_app_notice` The host app shows the first-run notice itself (no stderr notice). **Python** ```python def telemetry_use_app_notice() -> None ``` **TypeScript** ```ts function telemetryUseAppNotice(): void ``` **Swift** ```swift func telemetryUseAppNotice() ``` **Kotlin** ```kotlin fun telemetryUseAppNotice() ``` **Rust** ```rust pub fn telemetry_use_app_notice() ``` --- # Errors Every error the Cua SDK raises: its variant, message, cause and fix. Every fallible call raises (throws) one error type, `CuaError`. Its variant says what went wrong; its message carries the detail; its doc URL links to the variant's entry on this page (`https://cua.ai/docs/cua-sdk/reference/errors#`). The Python high-level API raises its own exceptions (`SpacesdNotAvailable`, `PoolSpecMismatch`, ...), listed on [Python: configuration and errors](). cua-spacesd answers RPCs with a `google.rpc.Status` carrying [`ErrorInfo`](); the SDK maps it to the variants below. | Language | Type | A variant | Doc URL | | --- | --- | --- | --- | | Python | `cua.CuaError` | `cua.CuaError.InvalidArgument` | `e.doc_url` | | TypeScript | `CuaError` | `CuaError.InvalidArgument`, tested with `CuaError.InvalidArgument.instanceOf(e)` | `e.docUrl`, `cuaErrorDocUrl(e)` | | Swift | `CuaError` | `CuaError.InvalidArgument(message:)` | `e.docUrl` | | Kotlin | `CuaException` | `CuaException.InvalidArgument` | `e.docUrl` | | Rust | `cua_sdk::CuaError` | `CuaError::InvalidArgument(String)` | `e.doc_url()` | **Example** **Python** ```python test="docs" id="cua-error-py" session="cua-error-py" prelude="spacesd,spacesd-vars" import asyncio import cua async def main(): sb = await cua.embedded().sandboxes().connect_url(URL, "wrong-token", None) try: await sb.spacesd(5000) except cua.CuaError.Unauthenticated as e: # one variant print("rejected:", e) except cua.CuaError as e: # any other raise asyncio.run(main()) ``` **TypeScript** ```ts test="docs" id="cua-error-ts" prelude="spacesd,spacesd-vars" import { CuaError, embedded } from '@trycua/cua'; const sb = await embedded().sandboxes().connectUrl(URL, 'wrong-token', undefined); try { await sb.spacesd(5000); } catch (e) { if (!CuaError.Unauthenticated.instanceOf(e)) throw e; console.log('rejected:', e.message); } ``` **Swift** ```swift test="swift" id="cua-error-swift" import Cua let sb = try await Cua.embedded().sandboxes().connectUrl(url: "http://10.0.0.5:3211", token: "wrong-token", name: nil) do { _ = try await sb.spacesd(probeTimeoutMs: 5000) } catch CuaError.Unauthenticated(let message) { // one case; any other CuaError propagates print("rejected:", message) } ``` **Kotlin** ```kotlin test="kotlin" id="cua-error-kt" import ai.cua.sdk.Cua import ai.cua.sdk.CuaConfig import ai.cua.sdk.CuaException val sb = Cua.embedded(CuaConfig()).sandboxes().connectUrl("http://10.0.0.5:3211", "wrong-token", null) try { sb.spacesd(5000u) } catch (e: CuaException.Unauthenticated) { // one subclass; any other CuaException propagates println("rejected: ${e.message}") } ``` ## Variants | Variant | Message | Cause | | --- | --- | --- | | [`InvalidArgument`](#invalidargument) | `invalid argument: ` | Bad input. | | [`InvalidPlacement`](#invalidplacement) | `invalid placement: ` | A location, kind or runtime that does not exist, or a combination of them (or with the image) that does not. | | [`NotFound`](#notfound) | `not found: ` | No such sandbox, process, path, forward or session. | | [`ProviderNotConfigured`](#providernotconfigured) | `provider not configured: ` | The provider is not configured (for example no Fleet credentials). | | [`Unsupported`](#unsupported) | `unsupported: ` | Not supported by this provider, guest or build. | | [`SpacesdNotAvailable`](#spacesdnotavailable) | `cua-spacesd is not available: ` | No cua-spacesd answered in the sandbox or at the URL. | | [`Timeout`](#timeout) | `timed out: ` | A deadline elapsed. | | [`Fleet`](#fleet) | `fleet: ` | Fleet API failure. | | [`FleetAdmissionDenied`](#fleetadmissiondenied) | `fleet admission denied: ` | Fleet's admission refused the request: a sandbox or pool size over this account's limits, or a template its policy does not admit. | | [`CloudCreditExhausted`](#cloudcreditexhausted) | `` | The account is out of Cua Cloud credit: no credit left, and no card or plan. | | [`Runtime`](#runtime) | `local runtime: ` | Local runtime failure. | | [`Env`](#env) | `env: ` | spacesd call failure. | | [`Http`](#http) | `http: ` | HTTP failure talking to a sandbox service. | | [`Unauthenticated`](#unauthenticated) | `unauthenticated: ` | Missing or wrong token or ticket. | | [`PermissionDenied`](#permissiondenied) | `permission denied: ` | The guest OS denied permission. | | [`Transport`](#transport) | `transport: ` | Could not reach the daemon or the endpoint. | | [`DaemonNotRunning`](#daemonnotrunning) | `` | No `cua daemon` is running: nothing listens at its socket or port, or its discovery file (`~/.cua/daemon.json`) was left by a daemon that exited. | | [`Closed`](#closed) | `closed: ` | The object was closed. | | [`CapabilityMissing`](#capabilitymissing) | `capability missing: ` | The Space's spacesd reports a feature this call needs as unsupported (the message names it). | | [`HostCapabilityMissing`](#hostcapabilitymissing) | `host capability missing: ` | A host-side prerequisite (app-session providers, an operator display, a local runtime) is not available. | | [`TeleportRefused`](#teleportrefused) | `teleport refused: ` | The teleport consent gate refused (or the approver declined). | | [`PoolSpecMismatch`](#poolspecmismatch) | `pool spec mismatch: ` | A named cloud pool's template differs from the requested sandbox fields; the message holds the diff (pass `apply` to update it). | | [`ClaimSecretsNotDelivered`](#claimsecretsnotdelivered) | `claim secrets not delivered: ` | A claim bound but its per-claim secrets (the env token) never reached the sandbox within the bounded wait; the claim was released. | | [`Internal`](#internal) | `internal: ` | Bug or I/O failure. | | [`AmbiguousSandbox`](#ambiguoussandbox) | `ambiguous sandbox name: ` | A bare sandbox name matches sandboxes in more than one location. | | [`InsufficientDisk`](#insufficientdisk) | `insufficient disk: ` | A pull, build or VM create would leave less than the configured minimum free disk space (`CUA_DISK_MIN_FREE`, default 5 GiB); nothing was written. | | [`ImageNotPublished`](#imagenotpublished) | `image not published: ` | A catalog image (an `Image.*` constructor, a tier or `cua sb create `) that CI has not published yet. | | [`Cancelled`](#cancelled) | `cancelled: ` | The create was cancelled (`Spaces.cancel_create`, Ctrl-C in `cua`, or its caller went away). | | [`Cloud`](#cloud) | `your cloud: ` | Your own cloud account (AWS, Google Cloud, Modal) refused or failed a call Cua made for a Space there: a missing permission, a quota, a region without the machine type. | ## `InvalidArgument` **Cause** Bad input. **Fix** Check the value against the reference; the message names the argument and what it accepts. **Message** `invalid argument: ` **Raised by** [`canonical_image_tier`](), [`error_doc_url`](), [`fleet_resolve_runtime`](), [`qualify_sandbox_ref`]() ## `InvalidPlacement` **Cause** A location, kind or runtime that does not exist, or a combination of them (or with the image) that does not. The message ends with the valid values for that context. **Fix** Use one of the values the message lists, or leave `kind` and `runtime` unset (`auto`). `cua config list` shows the defaults in effect. **Message** `invalid placement: ` **Raised by** [`check_placement`]() ## `NotFound` **Cause** No such sandbox, process, path, forward or session. **Fix** Check the ref, path or id. `Sandboxes.list` (`cua sb ls`) lists the sandboxes this client can see. **Message** `not found: ` **Raised by** [`resolve_image`]() ## `ProviderNotConfigured` **Cause** The provider is not configured (for example no Fleet credentials). **Fix** Set `FLEETS_TOKEN`, or `CUA_CLIENT_ID` and `CUA_CLIENT_SECRET`, or run `cua auth login` and create the client with `fleet_from_session`. **Message** `provider not configured: ` ## `Unsupported` **Cause** Not supported by this provider, guest or build. **Fix** Use a provider, image or runtime that supports the call; the message names what is missing. **Message** `unsupported: ` **Raised by** [`Sandbox.guest_sh`](), [`fleet_image_variant`](), [`fleet_resolve_runtime`](), [`resolve_image`]() ## `SpacesdNotAvailable` **Cause** No cua-spacesd answered in the sandbox or at the URL. **Fix** Use an image that runs cua-spacesd (the canonical Linux image does), or reach the sandbox through the services it declares. **Message** `cua-spacesd is not available: ` **Raised by** [`Sandbox.spacesd`]() ## `Timeout` **Cause** A deadline elapsed. **Fix** Raise the timeout (for example `ready_timeout_ms`), and check that the sandbox or service actually starts. **Message** `timed out: ` ## `Fleet` **Cause** Fleet API failure. **Fix** Read the status in the message. Retry a transient failure; check the credentials and the account's capacity otherwise. **Message** `fleet: ` ## `FleetAdmissionDenied` **Cause** Fleet's admission refused the request: a sandbox or pool size over this account's limits, or a template its policy does not admit. The message is Fleet's own and names the limit. **Fix** Pick a size within the limit the message names (most accounts run 1-8 vCPUs and 1-32 GiB per sandbox), or contact Cua support to raise the account's limits. **Message** `fleet admission denied: ` **Raised by** [`fleet_size_limits`]() ## `CloudCreditExhausted` **Cause** The account is out of Cua Cloud credit: no credit left, and no card or plan. New cloud sandboxes and Spaces are refused; running ones keep running, and local ones are never affected. The message ends with the website billing page's URL. **Fix** Add credit on that page (a plan, or a card for pay as you go), or run the sandbox locally. **Message** `` ## `Runtime` **Cause** Local runtime failure. **Fix** Run `cua runtime doctor` (`Local.doctor`) and follow the steps it prints. **Message** `local runtime: ` ## `Env` **Cause** spacesd call failure. **Fix** Read the status in the message; `SpacesdClient.capabilities` tells whether this spacesd supports the call. **Message** `env: ` ## `Http` **Cause** HTTP failure talking to a sandbox service. **Fix** Check that the service listens on its declared port and answers HTTP. **Message** `http: ` ## `Unauthenticated` **Cause** Missing or wrong token or ticket. **Fix** Pass the sandbox's env token, refresh the Fleet credentials, or run `cua auth login` again. **Message** `unauthenticated: ` **Raised by** [`Auth.access_token`](), [`resolve_image`]() ## `PermissionDenied` **Cause** The guest OS denied permission. **Fix** Run the call as a guest user allowed to do it, or change the permissions of the path. **Message** `permission denied: ` ## `Transport` **Cause** Could not reach the daemon or the endpoint. **Fix** Check the address. For a daemon client, start the daemon with `cua daemon start`. **Message** `transport: ` ## `DaemonNotRunning` **Cause** No `cua daemon` is running: nothing listens at its socket or port, or its discovery file (`~/.cua/daemon.json`) was left by a daemon that exited. The message says so in plain words. **Fix** Start Cua (the Spaces app starts the daemon), or run `cua daemon start`. `Cua.auto` uses the runtime in this process when no daemon runs. **Message** `` **Raised by** [`Cua.auto`](), [`Cua.connect`]() ## `Closed` **Cause** The object was closed. **Fix** Open a new handle or session; a closed one stays closed. **Message** `closed: ` ## `CapabilityMissing` **Cause** The Space's spacesd reports a feature this call needs as unsupported (the message names it). **Fix** Use a Space whose spacesd supports the feature (`Space.supports`), or update cua-spacesd on it. **Message** `capability missing: ` **Raised by** [`Space.screenshot`]() ## `HostCapabilityMissing` **Cause** A host-side prerequisite (app-session providers, an operator display, a local runtime) is not available. **Fix** Install or start the prerequisite the message names on this machine. **Message** `host capability missing: ` **Raised by** [`Space.stream_session`]() ## `TeleportRefused` **Cause** The teleport consent gate refused (or the approver declined). **Fix** Approve the manifest in the consent callback, or teleport a smaller scope. **Message** `teleport refused: ` **Raised by** [`Space.teleport`]() ## `PoolSpecMismatch` **Cause** A named cloud pool's template differs from the requested sandbox fields; the message holds the diff (pass `apply` to update it). **Fix** Pass the pool's own fields, or pass `apply` to update its template. **Message** `pool spec mismatch: ` **Raised by** [`Fleet.check_pool_spec`]() ## `ClaimSecretsNotDelivered` **Cause** A claim bound but its per-claim secrets (the env token) never reached the sandbox within the bounded wait; the claim was released. **Fix** Retry. If it keeps failing, check the pool's claim-secrets setup and the Fleet status. **Message** `claim secrets not delivered: ` **Raised by** [`Fleet.acquire_with`]() ## `Internal` **Cause** Bug or I/O failure. **Fix** Report it with the message at https://github.com/trycua/cua/issues. **Message** `internal: ` ## `AmbiguousSandbox` **Cause** A bare sandbox name matches sandboxes in more than one location. The message ends with the qualified candidates (`use one of: local:box, cloud:box`); `ambiguous_sandbox_candidates` extracts them. **Fix** Use one of the qualified refs the message lists (`local:`, `cloud:`). **Message** `ambiguous sandbox name: ` **Raised by** [`Sandboxes.connect`](), [`ambiguous_sandbox_candidates`]() ## `InsufficientDisk` **Cause** A pull, build or VM create would leave less than the configured minimum free disk space (`CUA_DISK_MIN_FREE`, default 5 GiB); nothing was written. **Fix** Free space with `cua cache prune`, or lower `CUA_DISK_MIN_FREE`. **Message** `insufficient disk: ` ## `ImageNotPublished` **Cause** A catalog image (an `Image.*` constructor, a tier or `cua sb create <word>`) that CI has not published yet. The message names the reference. **Fix** Pass the reference explicitly (`Image.from_registry("")`, `cua sb create `) to use it anyway, or pick a published image (`cua images ls`). **Message** `image not published: ` **Raised by** [`canonical_image_tier`](), [`omarchy_image`]() ## `Cancelled` **Cause** The create was cancelled (`Spaces.cancel_create`, Ctrl-C in `cua`, or its caller went away). What it made is gone; the message says what was removed and what stays (finished image downloads stay cached, so the next create resumes). **Fix** Nothing to clean up. Create it again when you want it. **Message** `cancelled: ` **Raised by** [`Sandboxes.cancel_create`](), [`Spaces.cancel_create`]() ## `Cloud` **Cause** Your own cloud account (AWS, Google Cloud, Modal) refused or failed a call Cua made for a Space there: a missing permission, a quota, a region without the machine type. The message names the cloud's own error. **Fix** Fix what the message names in that account (`cua cloud test <provider>` checks it without creating anything), or connect another region or project. **Message** `your cloud: ` --- # Types How the SDK types map to each language, and every object, record, enum and function in one list. Tables on these pages use one neutral notation for types (the Rust spelling). This page maps it to each language and lists the items of the SDK. ## Type notation | Notation | Python | TypeScript | Swift | Kotlin | Rust | | --- | --- | --- | --- | --- | --- | | `String` | `str` | `string` | `String` | `String` | `String` | | `bool` | `bool` | `boolean` | `Bool` | `Boolean` | `bool` | | `u32` | `int` | `number` | `UInt32` | `UInt` | `u32` | | `i64` | `int` | `bigint` | `Int64` | `Long` | `i64` | | `u64` | `int` | `bigint` | `UInt64` | `ULong` | `u64` | | `f64` | `float` | `number` | `Double` | `Double` | `f64` | | `Vec` | `bytes` | `ArrayBuffer` | `Data` | `ByteArray` | `Vec` | | `Vec` | `List[T]` | `Array` | `[T]` | `List` | `Vec` | | `Option` | `Optional[T]` | `T \| undefined` | `T?` | `T?` | `Option` | | `HashMap` | `dict[K, V]` | `Map` | `[K: V]` | `Map` | `HashMap` | | `SystemTime` | `datetime.datetime` | `Date` | `Date` | `java.time.Instant` | `SystemTime` | | `Duration` | `datetime.timedelta` | `number` | `TimeInterval` | `java.time.Duration` | `Duration` | | `Sandbox (an object)` | `Sandbox` | `SandboxLike` | `Sandbox` | `Sandbox` | `Arc` | `Option` may be absent: `None`, `undefined`, `nil`, `null`. A default of `None` means the argument or field can be left out. ## Names in each language | | Python | TypeScript | Swift | Kotlin | Rust | | --- | --- | --- | --- | --- | --- | | Import | `import cua` | `import { ... } from '@trycua/cua'` | `import Cua` | `import ai.cua.sdk.*` | `use cua_sdk::*;` | | Methods, arguments, fields | `snake_case` | `camelCase` | `camelCase` | `camelCase` | `snake_case` | | Enum variants | `UPPER_SNAKE` | `PascalCase` | `lowerCamel` | `UPPER_SNAKE` (plain), `PascalCase` (with fields) | `PascalCase` | | Async methods | `async def`, awaited | return a `Promise`, optional `{ signal }` | `async` | `suspend` | `async fn` | | Errors | `CuaError.` | `CuaError.` | `CuaError.(message:)` | `CuaException.` | `CuaError::(String)` | | Object handles | the class | the class; arguments and results use the `Like` interface | the class | the class | `Arc` | | Optional arguments | positional (`sb.spacesd(None)`) | `undefined` | `nil` | `null` | `None` | Renamed members: in Kotlin, objects implement `AutoCloseable`, whose `close()` frees the handle, so the async `close` is renamed (`McpClient.closeClient`, `MediaSession.closeSession`, `PortForward.closeForward`, `SpaceStreamSession.closeStream`). In TypeScript, reserved words get a trailing underscore (`Sandbox.delete_()`). The signature tabs show the renamed form. ## All items 291 items. Methods are listed on their object. The 723 items of the Cua Spaces app export (source-available, FSL-1.1-MIT, Swift only) are listed on their pages: [Decoded media](), [Host teleport](), [Teleport an app](), [Spaces app core](), [App core: Cua Volume storage, mount and sync](), [App core: agents, drive, notifications](). | Item | Kind | Summary | | --- | --- | --- | | [`agent_event_category`]() | function | The category of an event kind: `message`, `user`, `activity` or `hidden` (the rule `AgentEvent.category` and `AgentTranscript` use). | | [`agent_harnesses`]() | function | The harnesses, their readiness, installs, key variables and limits, as JSON. | | [`AgentActionReport`]() | record | Result of `Space.agent_message` / `Space.agent_stop`. | | [`AgentArtifact`]() | record | A file the run created or changed. | | [`AgentDelivery`]() | record | What `Spaces.persistent_agent_send` did. | | [`AgentEvent`]() | record | One normalized event. | | [`AgentEventPage`]() | record | A page of events. | | [`AgentFile`]() | record | A file for the first prompt or a follow-up. | | [`AgentInfo`]() | record | One AI coding agent and what cua configured for it. | | [`AgentMcpServer`]() | record | The MCP server to register. | | [`AgentPauseReport`]() | record | What `Spaces.agent_pause` did. | | [`AgentResumeReport`]() | record | What `Spaces.agent_resume` did. | | [`AgentRun`]() | object | One run. | | [`AgentRunInfo`]() | record | A run's state. | | [`AgentRunMcpServer`]() | record | An MCP server a run's agent gets: `url` (streamable HTTP, as reachable from inside the sandbox) or `command` (stdio, run in the sandbox). | | [`AgentRunOptions`]() | record | Options for `Agents.run`. | | [`AgentRunResult`]() | record | What the last turn produced. | | [`AgentRunStatus`]() | record | One agent run's status. | | [`Agents`]() | object | Agent runs in one sandbox. | | [`AgentSetup`]() | object | Agent onboarding for this machine's user. | | [`AgentSetupChange`]() | enum | What happened to one target. | | [`AgentSetupOutcome`]() | record | One target's outcome. | | [`AgentSetupTarget`]() | enum | What an operation touched. | | [`AgentStartReport`]() | record | Result of `Space.agent_start`. | | [`AgentTranscript`]() | object | A run's events folded into conversation items, the same rule in every language: the agent's message chunks join into `message` items, the prompt is a `user` item (never repeated as agent text), and consecutive activity of one turn folds into one `activity` group. | | [`AgentTranscriptItem`]() | record | One item of an `AgentTranscript`. | | [`ambiguous_sandbox_candidates`]() | function | The qualified candidates an `AmbiguousSandbox` message lists (empty for any other message). | | [`AudioPacket`]() | record | One audio packet (MEDIA.md §12.2). | | [`AudioSink`]() | object | Receives audio packets. | | [`Auth`]() | object | Sign-in and the shared session. | | [`AuthIdentity`]() | record | Who is signed in (from token claims; display only). | | [`AuthStatus`]() | record | The stored session, read without network access. | | [`BuildFile`]() | record | A local file copied into a built image. | | [`BundledSkill`]() | record | A skill bundled with the SDK. | | [`canonical_image`]() | function | The canonical image for `os` (`linux`, `windows`, `macos`) and `version` (`None` = default: `ghcr.io/trycua/linux:24.04`, `ghcr.io/trycua/windows:2022`, `ghcr.io/trycua/macos:26`; macOS accepts `tahoe`/`sequoia`). | | [`canonical_image_tier`]() | function | The canonical image for `os`, `version` and `tier`: `slim` (the minimum that passes `cua-spacesd doctor --strict`; CI and benchmarks use it), `full` or `None` (the default, with dev tooling) or, on macOS, `xcode` / `xcode-` (full plus one pinned Xcode). | | [`check_placement`]() | function | Checks a location, kind and runtime against each other without creating anything: `InvalidPlacement` (listing the valid values) when the combination does not exist. | | [`CloudCheck`]() | record | One check `cloud_test` ran. | | [`CloudConnected`]() | record | `cloud_connect`: the connected cloud and the checks it passed. | | [`CloudCredentials`]() | record | Where a cloud's credentials were found on this machine (never a secret). | | [`CloudDisconnected`]() | record | `cloud_disconnect`. | | [`CloudKind`]() | record | What a cloud can run for one image family, and the estimated cost. | | [`CloudOptions`]() | record | Advanced options for cloud sandboxes (warm capacity, limits, TTL, a dedicated pool). | | [`CloudProvider`]() | record | One cloud as `cloud_status` lists it. | | [`CloudResource`]() | record | One thing Cua created in a cloud. | | [`CloudStatus`]() | record | `cloud_status`. | | [`CloudSweepItem`]() | record | One row of `cloud_sweep`. | | [`CloudSweepReport`]() | record | `cloud_sweep`. | | [`CloudTarget`]() | record | Where in a cloud account Spaces go. | | [`CloudTestReport`]() | record | `cloud_test`. | | [`ComputerGrant`]() | record | A persistent agent's grant on one of the user's computers. | | [`config_get`]() | function | One setting's effective value and source. | | [`config_list`]() | function | Every setting with its effective value and source. | | [`config_path`]() | function | The config file's path (`$CUA_HOME/config.toml`). | | [`config_set`]() | function | Writes a setting to the config file (checked and normalised: `default.on` takes `local`, `cloud` or a provider). | | [`config_unset`]() | function | Removes a setting from the config file. | | [`ConfigEntry`]() | record | One setting's effective value. | | [`Container`]() | record | An extra container next to a sandbox (a compose-style sidecar), addressed by name on every runtime: the sandbox reaches it at its `name` (on its `ports`), it reaches the sandbox at `main`, and `services` may name its ports (`{"db": 5432}`). | | [`contrib_providers_built`]() | function | The contrib providers this build includes (empty unless built with the `contrib` feature). | | [`Cua`]() | object | The entry point of the SDK: create one with `Cua::embedded` or `Cua::connect`, then reach sandboxes, Fleet, Spaces and the rest through its accessors. | | [`cua_sdk_version`]() | function | Version of the SDK library. | | [`CuaConfig`]() | record | Configuration of an embedded SDK runtime. | | [`CuaError`]() | error | Every error the SDK raises. | | [`CuaInfo`]() | record | Identity of the SDK (and daemon, when connected). | | [`CuaMode`]() | enum | Where the SDK runtime lives. | | [`CursorArt`]() | record | One cursor shape's shared art: an SVG path on a square canvas, filled with the participant's color over an outline. | | [`DeviceEnrollment`]() | record | `Devices.enroll`'s result. | | [`Devices`]() | object | This device as a client of the signed-in account on a relay: its enrollment, the account's other devices and its audit log. | | [`DevicesSnapshot`]() | record | Everything the Devices page reads, in one call. | | [`DoctorReport`]() | record | Doctor output. | | [`DriveAccessRequest`]() | record | An agent's request for more access, waiting for the user. | | [`DriveAudit`]() | record | The newest audit events and whether the hash chain verified. | | [`DriveAuditEvent`]() | record | One audit event. | | [`DriveCacheStats`]() | record | The block cache in front of a remote store. | | [`DriveConflict`]() | record | A write that lost to a later one, kept in history and as a copy. | | [`DriveDevice`]() | record | A device sharing the drive's bucket. | | [`DriveEntry`]() | record | One row of a drive listing. | | [`DriveFile`]() | record | A file's content and version. | | [`DriveFileSync`]() | record | One file's sync state. | | [`DriveGrant`]() | record | A widening of an agent's or a Space's access. | | [`DriveListing`]() | record | A folder's listing. | | [`DriveMountStatus`]() | record | The drive as a volume. | | [`DriveObject`]() | record | A written version. | | [`DrivePendingUpload`]() | record | A file waiting to upload. | | [`DriveS3Settings`]() | record | Where an S3-compatible bucket is (no keys). | | [`DriveSpaceVolume`]() | record | The volume mounted in a Space's guest. | | [`DriveStorage`]() | record | Where the drive keeps its bytes (Settings > Storage). | | [`DriveStorageCheck`]() | record | What a storage test found. | | [`DriveStorageUpdate`]() | record | A storage change, or with `dry_run` a connection test. | | [`DriveSyncEvent`]() | record | One sync event. | | [`DriveSyncEvents`]() | record | Events after a sequence number, and the sequence to ask after next. | | [`DriveSyncStatus`]() | record | Sync across devices. | | [`DriveVersion`]() | record | One version in a file's history. | | [`DriveView`]() | record | Whose view of the drive a call takes: the user (the default, the whole drive) or a persistent agent in a Space. | | [`error_doc_url`]() | function | A link to the errors-reference entry of a `CuaError` variant, by name (`InvalidArgument`, case-insensitive). | | [`ExitInfo`]() | record | How a process ended. | | [`FileEntry`]() | record | A file or directory in the guest. | | [`Fleet`]() | object | Fleet control plane. | | [`fleet_check_runtime`]() | function | `fleet_resolve_runtime` with the image variant already known (`variant` as `fleet_image_variant` returns it); `None` means the manifest could not be read (an unset runtime then falls back to the reference). | | [`fleet_generate_claim_token`]() | function | A fresh per-claim env token (64 hex characters). | | [`fleet_image_variant`]() | function | The variant of an image from its registry documents (`manifest`: an image manifest or index, `config`: the platform manifest's config blob): `container-disk`, `rootfs` or `other`. | | [`fleet_resolve_runtime`]() | function | The Fleet runtime for `image`: `runtime` (`kubevirt`, `gvisor`) when given, else the one the image needs. | | [`fleet_size_limits`]() | function | The everyday sizes of a cloud sandbox (1-8 vCPUs, 1-32 GiB), which the Cua apps offer. | | [`FleetBillingCard`]() | record | The saved card (`Fleet.billing_status`): never its number. | | [`FleetBillingCredit`]() | record | The account's Cua Cloud credit. | | [`FleetBillingStatus`]() | record | The account's Cua Cloud billing (`GET /api/billing/status`). | | [`FleetClaim`]() | record | A claim. | | [`FleetClaimOptions`]() | record | Claim options for `Fleet.acquire_with`. | | [`FleetGcReport`]() | record | What a pool GC did. | | [`FleetManagedPool`]() | record | A managed pool (`cua-auto-*`, or a legacy `cua-eph-*`). | | [`FleetPool`]() | record | A pool. | | [`FleetPoolExport`]() | record | A pool read back as the shared model, plus its Terraform block. | | [`FleetPools`]() | object | Managed pools: what `Sandboxes.create` without a pool uses. | | [`FleetPoolSpec`]() | record | A warm pool spec (`Pool.apply` semantics: pool, namespace and template share `name`). | | [`FleetSandbox`]() | record | A bound sandbox. | | [`FleetSettings`]() | record | Fleet credentials and endpoints. | | [`FleetSignedUrl`]() | record | A signed, shareable service URL. | | [`FleetSizeLimits`]() | record | The everyday sizes of a cloud sandbox, which the Cua apps offer (`cua_fleet::CLOUD_DEFAULT_RANGE_CPUS`, `cua_fleet::CLOUD_DEFAULT_RANGE_MEMORY_MB`). | | [`FleetUsagePricing`]() | record | This account's Cua Cloud rates (`Fleet.usage_pricing`). | | [`FrameSink`]() | object | Receives video frames and control events. | | [`GpuOption`]() | record | One GPU option of a runtime (see `Spaces.gpu_support`). | | [`GpuSupport`]() | record | The GPU options of one runtime: empty, with the reason, when it has none. | | [`GuestDisplay`]() | object | The guest display of a sandbox without cua-spacesd (`Sandbox.guest_display`). | | [`HomeTransfer`]() | record | What moved between the drive and a Space. | | [`Host`]() | object | This machine as an unattended-access host. | | [`HostAccessRecord`]() | record | One access to this machine. | | [`HostPermission`]() | record | A macOS privacy pane the user must grant. | | [`HostProvidedSpace`]() | record | A Space this machine provides to one of your devices. | | [`HostSettingsChange`]() | record | A change to this machine's Spaces settings (`None` keeps a value). | | [`HostSetupOptions`]() | record | `cua host setup` options. | | [`HostSpacesAuditRecord`]() | record | One line of this machine's Spaces audit (remote creates, deletes, refusals and settings changes). | | [`HostStatus`]() | record | Host status. | | [`HttpHeader`]() | record | An HTTP header. | | [`HttpResponse`]() | record | An HTTP response from a sandbox service. | | [`image_alias`]() | function | The canonical image a CLI alias (`linux`, `windows`, `macos[:version]`, bare `ubuntu`) names, or `None` for a plain reference (`ubuntu:24.04` is Docker Hub's image). | | [`ImageBuild`]() | record | Image layers built on top of a sandbox's image. | | [`ImageFormat`]() | enum | Screenshot encoding. | | [`ImageInfo`]() | record | The image a sandbox runs, as resolved and pinned at create time. | | [`ImageLayer`]() | enum | One image build step (as in `images.cua.ai/v1alpha1` recipes). | | [`Local`]() | object | Local runtimes and images. | | [`LocalImage`]() | record | A local image. | | [`LocalStorage`]() | record | What local Spaces have room for (see `Local.storage`). | | [`LocationInfo`]() | record | The locations `on` accepts: `local`, `cloud`, then registered providers. | | [`locations`]() | function | Every location with the kinds and runtimes it offers (for pickers and help text). | | [`LoginAttempt`]() | object | A started sign-in. | | [`LoginMethod`]() | enum | How a sign-in proceeds. | | [`may_have_fleet_session`]() | function | Whether a `cua auth login` session may be stored, decided without reading the OS credential vault (the non-secret session marker, or the file store's file). | | [`mcp_connect`]() | function | Connects an MCP client to `config` (from `Sandbox.mcp_config`). | | [`mcp_connect_url`]() | function | Connects an MCP client to an endpoint URL (`http://host:8765/mcp`; a bare origin means `/mcp`) with `headers` on every request. | | [`McpClient`]() | object | An MCP client (rmcp, streamable HTTP; protocol 2026-07-28 when the server supports it, else the server's revision). | | [`McpConfig`]() | record | Where an MCP endpoint is and what every request needs. | | [`MediaBridge`]() | record | A media bridge for webviews (daemon mode). | | [`MediaEvent`]() | record | A control message (`hello`, `session_opened`, `lifecycle`, `audio_config`, `stats`, `error`, ...) or the close of the socket (`kind = "closed"`, `json = {"code":…, "reason":…}`). | | [`MediaOpenOptions`]() | record | Media session options. | | [`MediaSession`]() | object | A live media session. | | [`MediaStats`]() | record | Delivery counters. | | [`normalize_image`]() | function | Normalises a reference like the docker CLI (`python` is `docker.io/library/python:latest`). | | [`NotificationInfo`]() | record | One entry of the notifications feed. | | [`omarchy_image`]() | function | The Omarchy image, `ghcr.io/trycua/omarchy:` (default `edge`; also `rc`, `stable`): Omarchy (Arch Linux, Hyprland) with cua-spacesd, as an amd64 VM (QEMU locally, KubeVirt in the cloud; emulated on arm64 hosts). | | [`Overlay`]() | record | A binary to inject into a sandbox. | | [`OverlayResult`]() | record | What an overlay did. | | [`parse_sandbox_ref`]() | function | Parses a sandbox ref: `local:`, `cloud:`, `direct:`, `relay:`, a legacy spelling (`space://fleet//`, `fleet::`, `url:`, a URL), or a bare name. | | [`PersistentAgentInfo`]() | record | A persistent agent: a named harness whose memory (its home in the Cua Drive) outlives its runs and its Space. | | [`PersistentAgentOptions`]() | record | How to create a persistent agent. | | [`Point`]() | record | A point. | | [`PoolOptions`]() | record | How a pool keeps capacity for a `SandboxSpec`. | | [`PortForward`]() | object | An active port forward. | | [`presence_color`]() | function | The stable presence color of an agent or other principal (`#rrggbb`): a cursor palette color picked by a hash of `id`. | | [`presence_conformance_json`]() | function | The presence conformance vectors (JSON), for binding test suites. | | [`presence_cursor_art`]() | function | The shared art for `shape` (unknown names give the arrow). | | [`presence_cursor_art_all`]() | function | The shared art for every shape. | | [`presence_cursor_art_svg`]() | function | A standalone SVG of `shape` in `color`, `size` pixels square. | | [`presence_now_ms`]() | function | The local clock `PresenceView` expects: Unix time in milliseconds. | | [`presence_text_color`]() | function | Black or white text (`#000000` / `#ffffff`), whichever reads better on `background` (`#rrggbb`). | | [`PresenceCursor`]() | record | A normalized cursor. | | [`PresenceDrawable`]() | record | One cursor to draw now (see `PresenceView.drawables`). | | [`PresenceEvent`]() | record | A presence event. | | [`PresenceIdentity`]() | record | Who joins presence. | | [`PresenceMember`]() | record | A roster entry. | | [`PresenceParticipant`]() | record | A presence participant. | | [`PresencePoint`]() | record | A normalized point. | | [`PresenceView`]() | object | The shared presence netcode model: who is present and what to draw, with remote cursors interpolated (render delay, Catmull-Rom, extrapolation cap), idle cursors faded, stale participants dropped and your own cursor at the local pointer (libs/cua/proto/PRESENCE.md section 4). | | [`ProcessEvent`]() | record | One output chunk or the exit. | | [`ProcessEventKind`]() | enum | Kind of a process event. | | [`ProcessOutput`]() | record | Collected output of a finished process. | | [`PtySize`]() | record | A PTY size. | | [`PublicUrl`]() | record | A shareable URL of a sandbox service that expires. | | [`qualify_sandbox_ref`]() | function | Qualifies `name` for a lookup: `local = true` narrows a bare name to `local:`, `false` to `cloud:`, `None` keeps it (a bare name is then searched across locations). | | [`ReadinessProbe`]() | record | A user-declared readiness probe. | | [`RegistrySecret`]() | enum | Credentials for a private registry image. | | [`Relay`]() | object | The signed-in account's view of a cua-relay. | | [`RelayAuditEvent`]() | record | One event of the account's audit log. | | [`RelayClientInfo`]() | record | Somebody connected to a machine through the relay. | | [`RelayDevice`]() | record | A client device of the account, as the relay lists it. | | [`RelayMachine`]() | record | A machine of the account's relay directory (`relay:`). | | [`ReplayMode`]() | enum | Scrollback replay when attaching. | | [`resolve_image`]() | function | Resolves `reference` for `backend` (`auto`, `local`, `fleet`, `container`/`docker`/`gvisor`, `vm`/`qemu`/`kubevirt`, `lume`) on `arch` (`amd64`/`arm64`; `None` = this host, or amd64 for `fleet`). | | [`resolve_image_with_secret`]() | function | `resolve_image` for a private image: `secret` joins the head of the registry auth chain (it is read in this process and never cached or logged). | | [`ResolvedImage`]() | record | A reference resolved for a backend. | | [`RoutineInfo`]() | record | A routine: a recurring turn of a persistent agent, fired by the daemon. | | [`RuntimeCheck`]() | record | One doctor check (one backend). | | [`RuntimeCheckStatus`]() | enum | Status of one local runtime component. | | [`Sandbox`]() | object | A sandbox handle. | | [`sandbox_locations`]() | function | Every location word `on=` accepts, in display order: `local`, `cloud`, then the contrib providers (`e2b`, `daytona`, `modal`, ...) and your own clouds (`aws`, `gcp`). | | [`SandboxCreateOptions`]() | record | Options for `Sandboxes.create`. | | [`Sandboxes`]() | object | Creates, finds and manages sandboxes. | | [`SandboxInfo`]() | record | A sandbox as listed. | | [`SandboxListing`]() | record | A sandbox listing and what it left out. | | [`SandboxPhase`]() | enum | Portable lifecycle phase, the same words local and in the cloud. | | [`SandboxRefParts`]() | record | A parsed sandbox ref. | | [`SandboxSpec`]() | record | What a sandbox runs: the one model behind `Sandbox.create`, managed pools and `Fleet.apply`. | | [`SandboxStatus`]() | enum | Coarse lifecycle status. | | [`Screenshot`]() | record | A captured screenshot. | | [`ScreenshotOptions`]() | record | Screenshot options. | | [`Service`]() | object | A named service of a sandbox. | | [`ServiceEndpoint`]() | record | Where a sandbox service is reachable from this process. | | [`SetupStep`]() | record | One setup step. | | [`SiteLoginOptions`]() | record | Options for `Space.request_site_login`. | | [`SiteLoginReport`]() | record | What `Space.request_site_login` did. | | [`Space`]() | object | A connected Space. | | [`SpaceAgentOptions`]() | record | Options for `Space.agent_start` beyond the harness and prompt. | | [`SpaceAppIcon`]() | record | An app icon from `Space.app_icons`: the icon the guest desktop uses, normalized by the SDK's icon cache. | | [`SpaceAppIconRequest`]() | record | One window's app, for `Space.app_icons`. | | [`SpaceBashResult`]() | record | Output of `Space.bash`. | | [`SpaceCancelOutcome`]() | record | What `Spaces.cancel_create` did. | | [`SpaceCreateListener`]() | object | Receives `SpaceCreateProgress` while `Spaces.create_with_progress` runs, in order, ending with `ready`. | | [`SpaceCreateOptions`]() | record | Options for `Spaces.create`: the same location / kind / runtime model as sandboxes. | | [`SpaceCreateProgress`]() | record | What a Space create is doing (see `Spaces.create_with_progress`). | | [`SpaceCreateResult`]() | record | Result of `Spaces.create`. | | [`SpaceDisplay`]() | record | One of a Space's displays (`Space.displays`). | | [`SpaceHotspotStatus`]() | record | Hotspot state. | | [`SpaceInfo`]() | record | A registered Space. | | [`SpacePowerReport`]() | record | What `Spaces.stop` or `Spaces.start` did. | | [`SpacePresence`]() | object | A joined presence session. | | [`SpaceRelayRegistration`]() | record | A Space published on the relay (`Spaces.relay_register`). | | [`Spaces`]() | object | The Spaces registry: add a machine by URL, claim a Fleet Space, provision a local one, and get `Space` handles. | | [`spaces_tool_methods`]() | function | Which SDK method covers each Spaces contract tool. | | [`SpacesdCapabilities`]() | record | `SystemService.GetCapabilities`, summarized. | | [`SpacesdClient`]() | object | Client for one cua-spacesd. | | [`SpacesdCommand`]() | record | A command to run in the guest. | | [`SpacesdFeature`]() | record | One capability flag. | | [`SpacesdHttpHeader`]() | record | One HTTP header (duplicates are kept in order). | | [`SpacesdHttpRequest`]() | record | A plain-HTTP request to the spacesd port (for example MCP at `/mcp`). | | [`SpacesdHttpResponse`]() | record | The spacesd's HTTP answer. | | [`SpacesdProcess`]() | object | A guest process, from `spawn` or `attach`. | | [`SpaceSendFileOptions`]() | record | Options for `Space.send_file`. | | [`SpaceSendFileReport`]() | record | Result of `Space.send_file`. | | [`SpaceSentFile`]() | record | One file placed by `Space.send_file`. | | [`SpaceShareEntry`]() | record | One account a Space is shared with. | | [`SpaceShares`]() | record | Who a Space is shared with. | | [`SpacesHost`]() | record | One of your machines that provides Spaces (`Spaces.hosts`). | | [`SpacesHostLimit`]() | record | One limit of a host that provides Spaces and how much of it is used. | | [`SpacesToolMethod`]() | record | One row of `SPACES_TOOL_METHODS`. | | [`SpaceStreamOptions`]() | record | What to stream and how. | | [`SpaceStreamSession`]() | object | A live Space media session (see `Space.stream_session`). | | [`SpaceStreamStats`]() | record | Delivery counters of a `SpaceStreamSession`. | | [`SpaceStreamTicket`]() | record | A minted media session. | | [`SpaceThumbnail`]() | record | A Space's latest thumbnail, from the cache every client on this machine shares (`crate.native.spaces.Space.thumbnail`). | | [`SpaceToolInfo`]() | record | A tool of a Space's MCP service. | | [`SpaceToolResult`]() | record | The result of a tool call (MCP content as JSON). | | [`SpaceTransferReport`]() | record | Result of `Space.upload` / `Space.download`. | | [`SpaceUsage`]() | record | Memory and storage use (`Space.usage`). | | [`SpaceWindow`]() | record | A streamable window. | | [`SpaceWriteReport`]() | record | Result of `Space.write`. | | [`StorageVolume`]() | record | Space on the volume a local engine writes to. | | [`StreamStatsReport`]() | record | One stream session's aggregate stats (sampled). | | [`telemetry_acknowledge_notice`]() | function | Records that the first-run notice was shown in the app's UI (nothing is sent from a machine before that). | | [`telemetry_flush`]() | function | Sends queued events now, waiting at most `timeout_ms`; anything left is kept on disk for a later process. | | [`telemetry_notice_text`]() | function | The first-run notice text (an app shows it in its own UI, then calls `telemetry_acknowledge_notice`). | | [`telemetry_record_bench_run`]() | function | Records a finished cua-bench run: the taskset (catalog name, else `custom`), the aggregate success rate (0 to 1, bucketed), the task count (bucketed) and the outcome (`ok`, `error`, `cancelled`). | | [`telemetry_record_feature`]() | function | Records use of a Spaces app feature (fixed names: `space_create_local`, `teleport_drop`, ... see the docs). | | [`telemetry_record_onboarding_step`]() | function | Records an install or onboarding funnel step (fixed names: `onboarding_shown`, `signed_in`, `first_space_created`, ...; `first_*` steps count once per install). | | [`telemetry_record_stream_stats`]() | function | Records one stream session's aggregate stats (bucketed and sampled). | | [`telemetry_reset_id`]() | function | Deletes the anonymous install id and its salt. | | [`telemetry_schema_json`]() | function | Every event and property that may be sent, as JSON. | | [`telemetry_set_enabled`]() | function | Turns usage telemetry on or off for this machine (`$CUA_HOME/config.toml`, the same switch as `cua telemetry off` and the Spaces app setting). | | [`telemetry_set_surface`]() | function | Names the SDK surface events are attributed to. | | [`telemetry_show_last`]() | function | The last `limit` events queued or sent from this machine, exactly as sent, as JSON (`[{status, payload}]`). | | [`telemetry_status`]() | function | Telemetry state, and exactly what every event carries. | | [`telemetry_use_app_notice`]() | function | The host app shows the first-run notice itself (no stderr notice). | | [`TelemetryStatus`]() | record | Telemetry state. | | [`TeleportApprover`]() | object | The consent gate of `Space.teleport`: shown the manifest, returns the human's decision, or `None` to cancel. | | [`TeleportDecision`]() | record | A human's answer to a teleport manifest. | | [`TeleportItem`]() | record | One item a teleport would move. | | [`TeleportManifest`]() | record | Exactly what would leave this machine. | | [`TeleportReceipt`]() | record | What a teleport moved, or how to retry a pending Keyvault approval. | | [`TransferResult`]() | record | Result of an upload or download. | | [`UploadOptions`]() | record | Upload options. | | [`VideoFrame`]() | record | One encoded video access unit. | | [`ViewerLink`]() | record | A link to the sandbox desktop in the cua-spacesd HTML5 viewer. | | [`ViewerOptions`]() | record | Options for `Sandbox.viewer_url`. | --- # Python high-level API cua_sandbox and the cua package helpers: packages, configuration, and the page for each class. The high-level Python API: `cua_sandbox` (`Sandbox`, `Image`, the computer interfaces, `Pool`) and the helpers the `cua` package adds. It is a thin layer over the [Cua SDK objects](): the cloud, the local runtimes and cua-spacesd all go through `cua`, whose generated binding (`cua._native`) those pages document. | Package | Install | Import | | --- | --- | --- | | `cua-sandbox` 0.9.0 (Python 3.11 to 3.13) | `pip install cua-sandbox` | `from cua_sandbox import Sandbox, Image` | | `cua` 0.4.1 with the extra | `pip install "cua[sandbox]"` | `from cua import Sandbox, Image` (the same classes) | | MCP client support | `pip install "cua-sandbox[mcp]"` | `sb.mcp()` | | Typed Cua Driver access | `pip install "cua-sandbox[driver]"` | `sb.driver` | `configure()` sets process-wide options; environment variables override it: | Variable | Purpose | Default | | --- | --- | --- | | `FLEETS_TOKEN` | Cloud bearer token; wins over client credentials | None | | `CUA_CLIENT_ID`, `CUA_CLIENT_SECRET` | Cloud OAuth client credentials | None; then the `cua auth login` session | | `CUA_FLEET_BASE_URL`, `CUA_TOKEN_URL` | Cloud API and token endpoints | `https://run.cua.ai`, the cua.ai token endpoint | | `CUA_FLEET_MAX_POOL_SIZE`, `CUA_FLEET_CLAIM_TTL`, `CUA_FLEET_WARM`, `CUA_FLEET_POOL_IDLE_GC` | Cloud capacity defaults (`CUA_FLEET_POOL_IDLE_GC=off` disables idle cleanup) | 10, 15 min, warm for canonical images, 30 min | | `CUA_IMAGE_LINUX`, `CUA_IMAGE_WINDOWS`, `CUA_IMAGE_MACOS` | Override `Image.linux()`, `Image.windows()`, `Image.macos()` | The canonical images | ## Pages | Page | Covers | | --- | --- | | [Sandbox]() | Sandbox, SandboxInfo, the sandbox() helper and the creation options of cua-sandbox. | | [Image]() | The Image builder, sidecar containers and registry credentials of cua-sandbox. | | [Pool]() | Pools, templates, sandbox specs and pool errors of cua-sandbox. | | [Interfaces]() | Shell, mouse, keyboard, screen, clipboard, window, terminal, files, apps, mobile and driver interfaces. | | [Services, tunnels and MCP]() | Named services, signed and public URLs, tunnels and MCP endpoints of a cua-sandbox sandbox. | | [Runtimes]() | Local runtimes (Docker, QEMU, Lume, Hyper-V, Tart, Android emulator) and local support checks. | | [Configuration and errors]() | configure, login, whoami, transports and the errors cua-sandbox raises. | ## The cua package The hand-written layer of `cua`: two module-level constructors, a canonical-image helper and lazy re-exports of the extras. ### embedded ```python def embedded( *, state_dir: Optional[str] = None, fleet: Optional[FleetSettings] = None, fleet_from_env: bool = True, fleet_from_session: bool = False, env_probe_timeout_ms: Optional[int] = None, spaces_home: Optional[str] = None, teleport_home: Optional[str] = None, fleet_pool_home: Optional[str] = None, ) -> Cua ``` An SDK runtime in this process (no I/O until the first call). Fleet uses `CUA_CLIENT_ID`/`CUA_CLIENT_SECRET` (or `FLEETS_TOKEN`); `fleet_from_session=True` falls back to the `cua auth login` session (cua-sandbox's `Sandbox` does this by default). `spaces_home` is the Spaces registry directory (default `~/.cua`); `teleport_home` makes teleport read app sessions under that directory with no host side effects (tests); `fleet_pool_home` is where managed Fleet pools keep their name cache and GC lock (default `~/.cua`). ### connect ```python def connect(address: Optional[str] = None, token: Optional[str] = None) -> Cua ``` A client of a running `cua daemon` (socket path or loopback URL). ### Image Without `cua[sandbox]`, `cua.Image` is this helper. With it installed, `cua.Image` is the richer [cua_sandbox Image](). ```python class Image ``` Canonical image references (plain strings for `sandboxes().create`). **Example** ```python test="docs" id="image-py" session="image-py" from cua_sandbox import Image img = Image.linux().apt_install("curl").pip_install("requests").env(DEBUG="1").expose(8080) print(img.to_dict()) # {'os_type': 'linux', 'distro': 'ubuntu', 'version': '24.04', 'kind': None, # 'layers': [{'type': 'apt_install', 'packages': ['curl']}, # {'type': 'pip_install', 'packages': ['requests']}], # 'env': {'DEBUG': '1'}, 'ports': [8080]} ``` #### Image.linux ```python @staticmethod def linux(version: Optional[str] = None, tier: Optional[str] = None) -> str ``` `slim` or `full` (the default). #### Image.windows ```python @staticmethod def windows(version: Optional[str] = None, tier: Optional[str] = None) -> str ``` #### Image.macos ```python @staticmethod def macos(version: Optional[str] = None, tier: Optional[str] = None) -> str ``` `slim`, `full` (the default), `xcode` or `xcode-`. #### Image.omarchy ```python @staticmethod def omarchy(channel: Optional[str] = None) -> str ``` Omarchy (Arch Linux, Hyprland) with cua-spacesd: an amd64 VM (`ghcr.io/trycua/omarchy:edge`; emulated on arm64 hosts). #### Image.from_registry ```python @staticmethod def from_registry(reference: str) -> str ``` #### Image.resolve ```python @staticmethod def resolve( reference: str, backend: str = 'local', arch: Optional[str] = None, ) -> ResolvedImage ``` ### Names from the extras `cua` resolves these names lazily from the optional extras. Install the extra to use them. | Name | Resolves to | Extra | | --- | --- | --- | | `cua.AgentResponse` | `cua_agent.AgentResponse` | `cua[agent]` | | `cua.AndroidEmulatorRuntime` | [`cua_sandbox.runtime.AndroidEmulatorRuntime`]() | `cua[sandbox]` | | `cua.check_local_support` | [`cua_sandbox.check_local_support`]() | `cua[sandbox]` | | `cua.Clipboard` | [`cua_sandbox.interfaces.clipboard.Clipboard`]() | `cua[sandbox]` | | `cua.CloudTransport` | [`cua_sandbox.CloudTransport`]() | `cua[sandbox]` | | `cua.CommandResult` | [`cua_sandbox.interfaces.shell.CommandResult`]() | `cua[sandbox]` | | `cua.ComputerAgent` | `cua_agent.ComputerAgent` | `cua[agent]` | | `cua.configure` | [`cua_sandbox.configure`]() | `cua[sandbox]` | | `cua.DockerRuntime` | [`cua_sandbox.runtime.DockerRuntime`]() | `cua[sandbox]` | | `cua.EnvTransport` | [`cua_sandbox.EnvTransport`]() | `cua[sandbox]` | | `cua.HyperVRuntime` | [`cua_sandbox.runtime.HyperVRuntime`]() | `cua[sandbox]` | | `cua.Image` | [`cua_sandbox.Image`]() | `cua[sandbox]` | | `cua.Keyboard` | [`cua_sandbox.interfaces.keyboard.Keyboard`]() | `cua[sandbox]` | | `cua.login` | [`cua_sandbox.login`]() | `cua[sandbox]` | | `cua.LumeRuntime` | [`cua_sandbox.runtime.LumeRuntime`]() | `cua[sandbox]` | | `cua.Messages` | `cua_agent.Messages` | `cua[agent]` | | `cua.Mobile` | [`cua_sandbox.interfaces.mobile.Mobile`]() | `cua[sandbox]` | | `cua.Mouse` | [`cua_sandbox.interfaces.mouse.Mouse`]() | `cua[sandbox]` | | `cua.Pool` | [`cua_sandbox.Pool`]() | `cua[sandbox]` | | `cua.PoolAccessDeniedError` | [`cua_sandbox.PoolAccessDeniedError`]() | `cua[sandbox]` | | `cua.QEMURuntime` | [`cua_sandbox.runtime.QEMURuntime`]() | `cua[sandbox]` | | `cua.register_agent` | `cua_agent.register_agent` | `cua[agent]` | | `cua.RuntimeInfo` | [`cua_sandbox.runtime.RuntimeInfo`]() | `cua[sandbox]` | | `cua.RuntimeSupport` | [`cua_sandbox.RuntimeSupport`]() | `cua[sandbox]` | | `cua.sandbox` | [`cua_sandbox.sandbox`]() | `cua[sandbox]` | | `cua.Sandbox` | [`cua_sandbox.Sandbox`]() | `cua[sandbox]` | | `cua.SandboxInfo` | [`cua_sandbox.SandboxInfo`]() | `cua[sandbox]` | | `cua.Screen` | [`cua_sandbox.interfaces.screen.Screen`]() | `cua[sandbox]` | | `cua.Shell` | [`cua_sandbox.interfaces.shell.Shell`]() | `cua[sandbox]` | | `cua.skip_if_unsupported` | [`cua_sandbox.skip_if_unsupported`]() | `cua[sandbox]` | | `cua.SpacesdNotAvailable` | [`cua_sandbox.SpacesdNotAvailable`]() | `cua[sandbox]` | | `cua.TartRuntime` | [`cua_sandbox.runtime.TartRuntime`]() | `cua[sandbox]` | | `cua.Template` | [`cua_sandbox.Template`]() | `cua[sandbox]` | | `cua.Terminal` | [`cua_sandbox.interfaces.terminal.Terminal`]() | `cua[sandbox]` | | `cua.Tunnel` | [`cua_sandbox.interfaces.tunnel.Tunnel`]() | `cua[sandbox]` | | `cua.TunnelInfo` | [`cua_sandbox.interfaces.tunnel.TunnelInfo`]() | `cua[sandbox]` | | `cua.whoami` | [`cua_sandbox.whoami`]() | `cua[sandbox]` | | `cua.Window` | [`cua_sandbox.interfaces.window.Window`]() | `cua[sandbox]` | ### cua.runtime cua.runtime: sandbox runtime backends. Exports: [`Runtime`](), [`RuntimeInfo`](), [`DockerRuntime`](), [`QEMURuntime`](), [`QEMUDockerRuntime`](), [`QEMUBaremetalRuntime`](), [`QEMUWSL2Runtime`](), [`LumeRuntime`](), [`HyperVRuntime`](), [`AndroidEmulatorRuntime`](), [`TartRuntime`](). ### cua.tools cua.tools: agent tool classes and registry. Exports: `BaseTool`, `BaseComputerTool`, `register_tool`, `get_registered_tools`, `get_tool`, `TOOL_REGISTRY`, `BrowserTool`, `ToolError`, `IllegalArgumentError`. ### cua.callbacks cua.callbacks: agent lifecycle callback handlers. Exports: `AsyncCallbackHandler`, `ImageRetentionCallback`, `LoggingCallback`, `TrajectorySaverCallback`, `BudgetManagerCallback`, `TelemetryCallback`, `OtelCallback`, `OtelErrorCallback`, `OperatorNormalizerCallback`, `PromptInstructionsCallback`. --- # Sandbox Sandbox, SandboxInfo, the sandbox() helper and the creation options of cua-sandbox. The `Sandbox` class is the entry point of `cua-sandbox`: create, connect to, or reattach a sandbox, then use its interfaces. Local is the default: `Sandbox.create(image)` runs on this machine, `local=False` or `cloud=CloudOptions(...)` in the cloud, and every other parameter means the same on both. `Sandbox.connect(name)` and the class methods take `local=None`: the local sandbox of that name when one exists, else the cloud. ## Sandboxes ### Sandbox ```python class Sandbox Sandbox(transport: Transport, name: Optional[str] = None) ``` A sandboxed computer environment. Provides programmatic control of a VM or container through a unified interface: `.mouse`, `.keyboard`, `.screen`, `.clipboard`, `.shell`, `.window`, and `.terminal`. Sandboxes are always isolated: they never control the host machine directly. To control the local machine, use cua-driver. There are three ways to obtain a Sandbox: 1. **Persistent**: provision and keep alive after the script exits: ```python sb = await Sandbox.create(Image.linux()) await sb.shell.run("whoami") await sb.disconnect() ``` 2. **Connect**: attach to an already-running sandbox by name: ```python sb = await Sandbox.connect("my-sandbox") await sb.screenshot() await sb.disconnect() ``` 3. **Ephemeral**: auto-destroyed when the `async with` block exits: ```python async with Sandbox.ephemeral(Image.linux()) as sb: await sb.shell.run("whoami") ``` | Attribute | Type | Description | | --- | --- | --- | | `name` | `Optional[str]` | The sandbox name (`None` until a cloud sandbox is bound). | | `telemetry_enabled` | `bool` | Whether this sandbox records anonymous usage events. | | `screen` | `Screen` | Screenshots and screen size (cua-spacesd). | | `mouse` | `Mouse` | Pointer input in screen pixels (cua-spacesd). | | `keyboard` | `Keyboard` | Typing and key presses (cua-spacesd). | | `clipboard` | `Clipboard` | Clipboard text (cua-spacesd). | | `shell` | `Shell` | Run shell commands (cua-spacesd). | | `files` | `Files` | Read, write and transfer files (cua-spacesd). | | `window` | `Window` | The active window (cua-spacesd). | | `terminal` | `Terminal` | PTY terminal sessions (cua-spacesd). | | `mobile` | `Mobile` | Android touch and hardware keys (Android sandboxes). | | `tunnel` | `Tunnel` | Forward sandbox ports to loopback ports on this machine. | | `services` | `Services` | Older named-service helpers; prefer `service(name)`. | | `driver` | `Driver` | Typed Cua Driver access (`cua-sandbox[driver]`). | | `apps` | `Apps` | Install and launch catalog applications (cua-spacesd). | | `exposed_ports` | `dict` | Map each Image.expose() port to the host port forwarding it. | | `image_info` | `Optional[ImageInfo]` | The image this sandbox runs, as resolved and pinned at create time (digest, variant, arch), or `None` when unknown: direct (URL) connections, images not resolved from a registry. A claim on a named Fleet pool reports its template's image (`pinned_ref`/`digest` empty when the registry could not be read). | | `claim_name` | `str \| None` | Fleet claim name, distinct from the bound sandbox name. | | `pool_name` | `str \| None` | Fleet pool that owns this claim (advanced; see `(await sb.info()).provider_details`). | | `location` | `str` | Where it runs: `local`, `cloud` or `direct`. | | `id` | `str \| None` | The qualified ref, the same kind of value local and in the cloud: `local:`, `cloud:` or `direct:`. It is what `Sandbox.connect(id)` and `Sandbox.delete(id)` take, and what listings print. | #### Sandbox.disconnect ```python async def disconnect() -> None ``` Drop the transport connection. The sandbox keeps running. A claim on a managed Fleet pool stops being renewed and lives until its shutdown time (`claim_ttl`, or later if `keep_alive` was called). #### Sandbox.overlay ```python async def overlay( overlay: 'dict[str, Any] | list', timeout: Optional[float] = None, ) -> list ``` Injects binaries into this sandbox (see `create(overlay=...)`): `{"cua-driver": "./target/release/cua-driver"}`. Returns one `cua.OverlayResult` per binary (`name`, `target`, `sha256`, `previous_sha256`, `restarted`); pass `NAME=sha256:` to `cua doctor --expect`. `timeout` (seconds) bounds a cua-spacesd restart. #### Sandbox.info ```python async def info() -> SandboxInfo ``` Portable info: `status` (provisioning, starting, ready, stopped), `location` (local, cloud or direct), `services` (name -> guest port), `expires_at` (cloud) and `provider_details`. #### Sandbox.service ```python def service(name: str) -> 'ServiceHandle' ``` A named service (`services={"mcp": 8765}` at create): `request`, `url` and `public_url`, the same local and in the cloud. #### Sandbox.public_url ```python async def public_url( service: str, *, ttl: float = 3600, label: Optional[str] = None, ) -> PublicUrl ``` A shareable URL for `service` that stops working after `ttl` seconds (60 s to 24 h). Cloud: a signed service URL. Local: a loopback URL with its own token, served by the cua daemon (started if needed). #### Sandbox.revoke_public_url ```python async def revoke_public_url(url: 'PublicUrl | str') -> None ``` Revokes a URL from `public_url` (or its id). #### Sandbox.to_dict ```python def to_dict() -> dict[str, Any] ``` Serialize a durable Fleet sandbox reference. #### Sandbox.from_dict ```python @classmethod def from_dict(data: dict[str, Any]) -> '_ConnectResult' ``` Reconnect to a serialized Fleet claim reference. #### Sandbox.keep_alive ```python async def keep_alive(*, minutes: float) -> None ``` Push a Fleet claim's controller-enforced shutdown time forward. #### Sandbox.close ```python async def close() -> None ``` Release this Fleet claim; repeated calls are safe. #### Sandbox.snapshot ```python async def snapshot(name: str | None = None, stateful: bool = False) -> 'Image' ``` Snapshot this sandbox's current state. Returns an Image. The returned Image can be passed to Sandbox.create() or Sandbox.ephemeral() to boot a new sandbox from the snapshot (COW fork: instant on btrfs). | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `str \| None` | `None` | Optional human-readable name for the snapshot. | | `stateful` | `bool` | `False` | Whether to capture memory state (VMs only). | **Returns:** An Image with _snapshot_source set, ready to pass to Sandbox.ephemeral(). #### Sandbox.destroy ```python async def destroy() -> None ``` Disconnect and permanently delete the sandbox (VM/container). #### Sandbox.screenshot ```python async def screenshot( text: Optional[str] = None, format: str = 'png', quality: int = 95, ) -> bytes ``` #### Sandbox.screenshot_base64 ```python async def screenshot_base64( text: Optional[str] = None, format: str = 'png', quality: int = 95, ) -> str ``` #### Sandbox.get_environment ```python async def get_environment() -> str ``` #### Sandbox.get_display_url ```python async def get_display_url(*, share: bool = False) -> str ``` Return a URL to view this sandbox's display. With cua-spacesd this is a link to its HTML5 viewer, carrying a scoped viewer ticket that expires. Images without cua-spacesd return the page of a declared web display service, or a VNC address. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `share` | `bool` | `False` | If True, return a link meant for sharing (an expiring public link for a legacy web display service; viewer links always expire). If False, return a direct connection URL. | #### Sandbox.viewer_url ```python async def viewer_url() -> str ``` A browser link to this sandbox's desktop in the cua-spacesd HTML5 viewer (video, audio, input, clipboard, files). The link carries a scoped viewer ticket that expires after an hour. Same as `get_display_url()` for sandboxes with cua-spacesd. #### Sandbox.get_dimensions ```python async def get_dimensions() -> tuple[int, int] ``` #### Sandbox.mcp ```python def mcp(service: str, *, path: str = '/mcp') -> Any ``` An official MCP SDK client for the MCP server behind `service`. An async context manager (`pip install "cua-sandbox[mcp]"`): ```python async with sb.mcp("mcp") as client: tools = await client.list_tools() result = await client.call_tool("add", {"a": 2, "b": 3}) ``` Requests go through the sandbox's service route (loopback locally, the Fleet gateway in the cloud), streamed both ways, so every protocol feature and content block works unchanged. No cua-spacesd needed. #### Sandbox.mcp_config ```python async def mcp_config(service: str, *, path: str = '/mcp') -> dict ``` `{"url": ..., "headers": {...}}` of `service`'s MCP endpoint, for any MCP client (Claude Code, Cursor, other SDKs). The headers can carry a short-lived Fleet bearer: fetch a fresh config per connection. #### Sandbox.spacesd ```python async def spacesd() -> Any ``` The raw `cua.SpacesdClient` for this sandbox's cua-spacesd. The typed escape hatch to every `cua.env.v1` RPC (`call_json`), process streaming, chunked transfers and media sessions. Raises `cua_sandbox._sdk.SpacesdNotAvailable` when the sandbox has no spacesd (for example a plain VNC-only image). #### Sandbox.create ```python @classmethod async def create( image: Image | None = None, *, pool: 'Pool | str | None' = None, name: Optional[str] = None, replicas: int = 1, service: str = ENV_SERVICE, claim_spec: Any = None, keep_alive_minutes: float | None = None, api_key: Optional[str] = None, on: Optional[str] = None, local: Optional[bool] = None, kind: Optional[str] = None, runtime: 'Runtime | str | None' = None, cpu: Optional[int] = None, memory_mb: Optional[int] = None, disk_gb: Optional[int] = None, region: str = 'us-east-1', time_to_start: Optional[float] = None, request_timeout: Optional[float] = None, server_port: Optional[int] = None, telemetry_enabled: bool = True, warm: Optional[bool] = None, max_pool_size: Optional[int] = None, claim_ttl: 'float | timedelta | None' = None, progress: Optional[Callable[[Any], Any]] = None, command: Optional[list[str]] = None, sidecars: Optional[list] = None, env: Optional[dict[str, str]] = None, services: Optional[dict[str, int]] = None, wait_for: WaitFor = None, cloud: Optional[CloudOptions] = None, memory: 'str | int | None' = None, network: Optional[str] = None, overlay: 'dict[str, Any] | list | None' = None, ) -> 'Sandbox' ``` Provision or claim a persistent sandbox and return it connected. Where, what kind and which engine are three separate choices: * `on`: `"local"` or `"cloud"`. `local=True`/`False` is the same switch; `on` and a contradicting `local` is an `InvalidArgument`, and so is `local=True` with `cloud=`. Unset, `cloud=` implies the cloud; a cloud-only argument (a Fleet `pool`, `warm`, `max_pool_size`, `claim_ttl`, `claim_spec`, `api_key`, ...) still implies it with a `DeprecationWarning`; otherwise the user default applies (`CUA_DEFAULT_ON`, else `cua config set default.on cloud` in `$CUA_HOME/config.toml`, else local, with a one-time notice that `CUA_QUIET_DEFAULT=1` hides). * `kind`: `"auto"`, `"container"` or `"vm"`. Unset: the image's `kind` when it has one, else `default.kind`, else auto (macOS and Windows images are VMs, a container rootfs is a container, a disk-only image is a VM). * `runtime`: the engine, `"auto"` or one the location offers for the kind: locally `"gvisor"`/`"runc"` (containers) and `"qemu"`/`"lume"` (VMs); in the cloud `"gvisor"` and `"kubevirt"`. Unset: `default.runtime`, else auto. A local sandbox with `sidecars` needs `"runc"` where gVisor would run (separate gVisor containers cannot share a network namespace). A `Runtime` object (`DockerRuntime()`, `TartRuntime()`, ...) still picks a local adapter, as before. A combination that does not exist (`kind="container", runtime="qemu"`, or `runtime="kubevirt"` locally) raises `InvalidPlacement`, listing the valid values. The same options mean the same thing local and in the cloud: * `command`: argv replacing the image's entrypoint. * `env`: environment variables. * `services`: named guest ports, `{"mcp": 8765}`; reach them with `sb.service("mcp")`, `sb.public_url("mcp")` or `sb.tunnel.forward(8765)`. * `wait_for`: `tcp("mcp")` / `http("mcp", "/health")` (or a list); readiness otherwise is "the sandbox is running". * `cloud`: `CloudOptions` (warm capacity, limits, TTL, a dedicated pool). The flat `pool`, `warm`, `max_pool_size` and `claim_ttl` keywords are deprecated aliases. * `memory`: `"4GB"` / `"512MB"` (or `memory_mb`). * `network`: `"default"` (outbound network, like a Docker container) or `"none"` (no egress; the SDK still reaches the guest's published ports). `"none"` needs a local QEMU VM; containers, Lume and cloud sandboxes raise `Unsupported`. * `overlay`: binaries to inject once it is up, so tests run the build under test and not the copy the image bundles: `{"cua-driver": "./target/release/cua-driver"}` (`cua-driver` and `cua-spacesd` resolve their guest path; other names take `(path, "/guest/path")`). Each replaces the guest file atomically, is recorded with its sha256 (`cua doctor --expect`) and what runs it is restarted. If it fails, the sandbox is deleted and the error raised. See `overlay`. Supplying `pool` claims from an existing Fleet pool without changing its configuration. A Fleet registry image without `pool` is claimed from this account's managed pool for that image spec (`cua-auto-*`), created on first use and reused afterwards. The first start of an image can take a few minutes; later starts reuse its capacity. Fleet-only options: * `warm`: seed one ready replica when the pool is first created. * `max_pool_size`: autoscaling ceiling (default 10). * `claim_ttl`: seconds (or a timedelta) the claim outlives this process; it is renewed while the process holds it (default 15 min). Use `keep_alive(minutes=...)` to keep a sandbox longer. * `progress`: callback receiving `AcquireProgress` events. Without `on`/`local`, passing one of them keeps the sandbox in the cloud (deprecated: pass `local=False`); with `local=True` they are ignored. An existing `pool` keeps its own kind and runtime (`Pool.apply(..., runtime=)`). `server_port` names a port your image serves itself: it is exposed (Fleet: as the `server` service) and becomes the readiness probe. Leave it unset for the default, daemon-agnostic readiness (the provider reports the sandbox running; cua-spacesd on 3211 is used by the interfaces when the image has it). **Example** ```python test="container" id="sandbox-create-py" session="sandbox-create-py" import asyncio from cua_sandbox import Image, Sandbox, http async def main(): sb = await Sandbox.create( Image.from_registry("python:3.12-slim"), command=["python", "-m", "http.server", "8000"], services={"web": 8000}, wait_for=http("web", "/"), cpu=2, memory="4GB", ) print(sb.id) await sb.destroy() asyncio.run(main()) ``` #### Sandbox.connect ```python @classmethod def connect( name: Optional[str] = None, *, url: Optional[str] = None, token: Optional[str] = None, api_key: Optional[str] = None, local: Optional[bool] = None, ws_url: Optional[str] = None, http_url: Optional[str] = None, container_name: Optional[str] = None, cpu: Optional[int] = None, memory_mb: Optional[int] = None, disk_gb: Optional[int] = None, region: str = 'us-east-1', telemetry_enabled: bool = True, ) -> '_ConnectResult' ``` Connect to an existing sandbox by name. Supports both `await` and `async with`. When used as a context manager, `disconnect()` is called on exit: the sandbox keeps running. Examples: ```python # plain await sb = await Sandbox.connect("my-sandbox") await sb.screenshot() await sb.disconnect() # context manager: disconnects on exit, sandbox keeps running async with Sandbox.connect("my-sandbox") as sb: await sb.screenshot() ``` | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `Optional[str]` | `None` | A sandbox ref (`local:`, `cloud:`, `direct:`, a legacy id) or a name. A bare name must be unique across locations, else `AmbiguousSandbox` lists the refs it matches. | | `local` | `Optional[bool]` | `None` | `None` (default): search every location. `True`/ `False` narrow a bare name to local or cloud. | | `url` | `Optional[str]` | `None` | A cua-spacesd URL to connect to directly (`http://host:3211`, a relay URL or a Fleet service URL). | | `token` | `Optional[str]` | `None` | The spacesd token for `url`. | | `api_key` | `Optional[str]` | `None` | Legacy CUA API key (API-key cloud VMs were removed; see `cua_sandbox.transport.cloud`). | | `ws_url` | `Optional[str]` | `None` | Removed (the computer-server WebSocket protocol is gone). | | `http_url` | `Optional[str]` | `None` | Base URL of a cua-spacesd (same as `url`). | | `container_name` | `Optional[str]` | `None` | Unused; kept for compatibility. | | `region` | `str` | `'us-east-1'` | Cloud region (default `"us-east-1"`). | **Example** ```python test="docs" id="sandbox-connect-py" session="sandbox-connect-py" prelude="spacesd,spacesd-vars" import asyncio from cua_sandbox import Sandbox async def main(): async with Sandbox.connect(url=URL, token=TOKEN) as sb: # or Sandbox.connect("my-sandbox") result = await sb.shell.run("echo hello") print([result.stdout, result.returncode, result.success]) asyncio.run(main()) ``` #### Sandbox.ephemeral ```python @classmethod async def ephemeral( image: Image | None = None, *, pool: 'Pool | str | None' = None, name: Optional[str] = None, replicas: int = 1, service: str = ENV_SERVICE, claim_spec: Any = None, keep_alive_minutes: float | None = None, keep_pool: bool = False, api_key: Optional[str] = None, on: Optional[str] = None, local: Optional[bool] = None, kind: Optional[str] = None, runtime: 'Runtime | str | None' = None, cpu: Optional[int] = None, memory_mb: Optional[int] = None, disk_gb: Optional[int] = None, region: str = 'us-east-1', time_to_start: Optional[float] = None, request_timeout: Optional[float] = None, server_port: Optional[int] = None, telemetry_enabled: bool = True, warm: Optional[bool] = None, max_pool_size: Optional[int] = None, claim_ttl: 'float | timedelta | None' = None, progress: Optional[Callable[[Any], Any]] = None, command: Optional[list[str]] = None, sidecars: Optional[list] = None, env: Optional[dict[str, str]] = None, services: Optional[dict[str, int]] = None, wait_for: WaitFor = None, cloud: Optional[CloudOptions] = None, memory: 'str | int | None' = None, network: Optional[str] = None, ) -> AsyncIterator['Sandbox'] ``` A sandbox that is released when the `async with` block exits. Takes the same `on`/`local`, `kind`, `runtime`, `command`, `env`, `services`, `wait_for`, `cloud`, `memory` and `network` options as `create`. A Fleet registry image without `pool` is claimed from this account's reusable managed pool (see `create` for `warm`, `max_pool_size`, `claim_ttl` and `progress`). Exiting releases the claim; the pool stays for the next run and idle GC deletes it once unused. If the process dies, the claim expires after `claim_ttl`. `keep_pool` is deprecated and has no effect. #### Sandbox.list ```python @classmethod async def list( *, location: Optional[str] = None, local: Optional[bool] = None, all: Optional[bool] = None, api_key: Optional[str] = None, ) -> 'list[SandboxInfo]' ``` List sandboxes, each with its `location`, `kind` and `runtime`: local and cloud ones by default. The cloud part never fails the default listing: without cloud credentials it is left out silently, and when the cloud fails or does not answer within 5 s the local rows come back with a logged warning ("cloud sandboxes not listed: ..."). | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `location` | `Optional[str]` | `None` | `None` (default): local and cloud. `"local"`: only the local ones (containers, QEMU, Lume, Android). `"cloud"`: only the cloud ones. | | `local` | `Optional[bool]` | `None` | The same switch as a bool (`True` is `"local"`, `False` is `"cloud"`). | | `all` | `Optional[bool]` | `None` | Deprecated; everything is the default. | | `api_key` | `Optional[str]` | `None` | Legacy CUA API key for api.cua.ai VMs. | #### Sandbox.get_info ```python @classmethod async def get_info( name: str, *, local: Optional[bool] = None, api_key: Optional[str] = None, ) -> 'SandboxInfo' ``` Get metadata for a specific sandbox. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `str` | required | Sandbox ref (`local:`, `cloud:`) or a name unique across locations. | | `local` | `Optional[bool]` | `None` | `True`/`False` narrow a bare name to local or cloud. `None` (default): the local sandbox `name` when one exists (and no cloud sandbox shares it), else the cloud. | | `api_key` | `Optional[str]` | `None` | CUA API key for cloud. | #### Sandbox.suspend ```python @classmethod async def suspend( name: str, *, local: Optional[bool] = None, api_key: Optional[str] = None, ) -> None ``` Suspend the sandbox `name`, keeping its state; `resume` continues it. Only that sandbox is affected. Local: the SDK suspends it on its backend (a container is paused or checkpointed, a QEMU VM snapshotted, a Lume VM stopped with its disk). Cloud: a Fleet sandbox cannot be suspended on its own, so this raises `Unsupported`; keep it with `keep_alive()` or release it with `delete`. A legacy api.cua.ai VM (an API key) stops. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `str` | required | Sandbox name. | | `local` | `Optional[bool]` | `None` | `None` (default): the local sandbox `name` when one exists, else the cloud. `True`/`False` decide. | | `api_key` | `Optional[str]` | `None` | Legacy CUA API key (api.cua.ai VMs). | #### Sandbox.resume ```python @classmethod async def resume( name: str, *, local: Optional[bool] = None, api_key: Optional[str] = None, ) -> 'Sandbox' ``` Resume the suspended sandbox `name` and return it connected. Local: the backend resumes it. Cloud: a running sandbox just reconnects (a Fleet sandbox is never suspended); one that is gone raises `Unsupported`. A legacy api.cua.ai VM (an API key) starts. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `str` | required | Sandbox name. | | `local` | `Optional[bool]` | `None` | `None` (default): the local sandbox `name` when one exists, else the cloud. | | `api_key` | `Optional[str]` | `None` | Legacy CUA API key (api.cua.ai VMs). | **Returns:** A connected Sandbox ready to use. #### Sandbox.restart ```python @classmethod async def restart( name: str, *, local: Optional[bool] = None, api_key: Optional[str] = None, ) -> 'Sandbox' ``` Restart the sandbox `name` (suspend then resume) and return it connected. Local only (see `suspend`): a Fleet sandbox raises `Unsupported`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `str` | required | Sandbox name. | | `local` | `Optional[bool]` | `None` | `None` (default): the local sandbox `name` when one exists, else the cloud. | | `api_key` | `Optional[str]` | `None` | Legacy CUA API key (api.cua.ai VMs). | **Returns:** A connected Sandbox ready to use. #### Sandbox.delete ```python @classmethod async def delete( name: str, *, local: Optional[bool] = None, api_key: Optional[str] = None, ) -> None ``` Permanently delete a sandbox. For local sandboxes, stops the VM and removes the state file. For cloud sandboxes, calls DELETE /v1/vms/{name}. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `str` | required | Sandbox name. | | `local` | `Optional[bool]` | `None` | If True, delete a local sandbox. `None` (default): the local sandbox `name` when one exists, else the cloud. | | `api_key` | `Optional[str]` | `None` | CUA API key for cloud. | ### sandbox ```python async def sandbox( *, on: Optional[str] = None, local: Optional[bool] = None, kind: Optional[str] = None, ws_url: Optional[str] = None, http_url: Optional[str] = None, url: Optional[str] = None, token: Optional[str] = None, api_key: Optional[str] = None, container_name: Optional[str] = None, image: Optional[Image] = None, runtime: 'Runtime | str | None' = None, name: Optional[str] = None, ephemeral: Optional[bool] = None, cpu: Optional[int] = None, memory_mb: Optional[int] = None, disk_gb: Optional[int] = None, region: str = 'us-east-1', warm: Optional[bool] = None, max_pool_size: Optional[int] = None, claim_ttl: Any = None, progress: Optional[Callable[[Any], Any]] = None, ) -> AsyncIterator[Sandbox] ``` Async context manager for a sandboxed environment. `on`/`local`, `kind` and `runtime` mean what they mean for `Sandbox.create`. .. deprecated: ```python Prefer `Sandbox.create()`, `Sandbox.connect()`, or `Sandbox.ephemeral()` instead. ``` ### SandboxInfo ```python class SandboxInfo SandboxInfo( name: str, status: str, source: str, os_type: Optional[str] = None, host: Optional[str] = None, vnc_url: Optional[str] = None, api_url: Optional[str] = None, created_at: Optional[str] = None, id: Optional[str] = None, location: Optional[str] = None, services: dict = dict(), expires_at: Optional[str] = None, provider_details: dict = dict(), kind: Optional[str] = None, runtime: Optional[str] = None, ) ``` Metadata for a local or cloud sandbox. `status` from `Sandbox.info` is portable: `provisioning`, `starting`, `ready` or `stopped`. Listings keep the provider's word (`running`, `suspended`, ...). Provider internals (the cloud pool, namespace and claim; the local backend) are in `provider_details`. | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | | `status` | `str` | | | `source` | `str` | | | `os_type` | `Optional[str]` | | | `host` | `Optional[str]` | | | `vnc_url` | `Optional[str]` | | | `api_url` | `Optional[str]` | | | `created_at` | `Optional[str]` | | | `id` | `Optional[str]` | | | `location` | `Optional[str]` | | | `services` | `dict` | | | `expires_at` | `Optional[str]` | | | `provider_details` | `dict` | | | `kind` | `Optional[str]` | | | `runtime` | `Optional[str]` | | ## Creation options ### CloudOptions ```python class CloudOptions CloudOptions( warm: Optional[bool] = None, max_pool_size: Optional[int] = None, claim_ttl: Union[float, timedelta, None] = None, pool: 'Pool | str | None' = None, apply: bool = False, ) ``` Advanced options for cloud sandboxes. Passing them implies the cloud; with `on="local"`/`local=True` they are an `InvalidArgument`. The engine is `Sandbox.create(runtime="gvisor"|"kubevirt")`, not a cloud option. * `warm`: keep one sandbox of this image ready (faster next start). * `max_pool_size`: most sandboxes of this image at once (default 10). * `claim_ttl`: how long the sandbox outlives this process without a `keep_alive` (seconds or a timedelta; renewed while held; default 15 min). * `pool`: dedicated capacity: claim from this existing pool. Sandbox fields given with it (`image`, `command`, `env`, `services`, `sidecars`, `cpu`, `memory`, a registry secret) must match the pool's template, else `cua_sandbox.PoolSpecMismatch` (with a readable diff) is raised; fields left unset are not compared. * `apply`: with `pool`, update the pool's template to the given fields instead (the pool's capacity is kept; managed `cua-auto-*` pools refuse it). | Attribute | Type | Description | | --- | --- | --- | | `warm` | `Optional[bool]` | | | `max_pool_size` | `Optional[int]` | | | `claim_ttl` | `Union[float, timedelta, None]` | | | `pool` | `'Pool \| str \| None'` | | | `apply` | `bool` | | ### Probe ```python class Probe Probe(service: str, path: Optional[str] = None) ``` A readiness probe on a declared service: TCP, or `GET path` returning 2xx when `path` is set. Build it with `tcp` or `http`. | Attribute | Type | Description | | --- | --- | --- | | `service` | `str` | | | `path` | `Optional[str]` | | #### Probe.native ```python def native() -> Any ``` The `cua.ReadinessProbe` for this probe. ### http ```python def http(service: str, path: str = '/') -> Probe ``` Ready once `GET path` on the declared `service` returns 2xx. ### tcp ```python def tcp(service: str) -> Probe ``` Ready once a TCP connect to the declared `service` succeeds. ### PublicUrl ```python class PublicUrl PublicUrl( url: str, expires_at: str, id: str, service: str, provider_details: dict = dict(), ) ``` A shareable URL of a sandbox service that stops working at `expires_at`. Cloud: a signed service URL; local: a loopback URL with its own token, served by the cua daemon. | Attribute | Type | Description | | --- | --- | --- | | `url` | `str` | | | `expires_at` | `str` | | | `id` | `str` | | | `service` | `str` | | | `provider_details` | `dict` | | ## Sandbox references ### AmbiguousSandbox ```python class AmbiguousSandbox(LookupError) AmbiguousSandbox(name: str, candidates: list[str]) ``` A bare sandbox name matches sandboxes in more than one location. `candidates` are the qualified refs it matches (`local:box`, `cloud:box`); pass one of them, or `local=True`/`local=False`. The native SDK raises `cua.CuaError.AmbiguousSandbox` for the same case. | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | | `candidates` | `list` | | --- # Image The Image builder, sidecar containers and registry credentials of cua-sandbox. `Image` is an immutable, chainable description of what a sandbox boots. Builder methods return a new `Image`. Builder methods add layers and return a new `Image`. Locally, a container image is built into your container engine before boot; in the cloud the same layers run as a remote build. Both are cached by content. Layers a build cannot run (`brew_install`, `choco_install`, ...) are applied after boot through cua-spacesd, so they need an image that runs it. ## Image builder ### Image ```python class Image Image(os_type: str, distro: str, version: str, kind: Optional[str] = None) ``` Immutable, chainable image specification. Each mutation method returns a new Image instance so that builders can be forked at any point. | Attribute | Type | Description | | --- | --- | --- | | `os_type` | `str` | | | `distro` | `str` | | | `version` | `str` | | | `kind` | `Optional[str]` | | **Example** ```python test="docs" id="image-py" session="image-py" from cua_sandbox import Image img = Image.linux().apt_install("curl").pip_install("requests").env(DEBUG="1").expose(8080) print(img.to_dict()) # {'os_type': 'linux', 'distro': 'ubuntu', 'version': '24.04', 'kind': None, # 'layers': [{'type': 'apt_install', 'packages': ['curl']}, # {'type': 'pip_install', 'packages': ['requests']}], # 'env': {'DEBUG': '1'}, 'ports': [8080]} ``` #### Image.linux ```python @classmethod def linux( distro: str = 'ubuntu', version: str = '24.04', kind: Optional[str] = None, tier: Optional[str] = None, ) -> Image ``` Linux: the canonical `ghcr.io/trycua/linux:` image. `kind=None` (or `"auto"`) runs what the image is (the rootfs: a gVisor container locally and in the cloud); `kind="vm"` its `-disk` containerDisk (QEMU locally, KubeVirt in the cloud); `kind="container"` the rootfs. `Sandbox.create(kind=...)` overrides it. `tier`: `"full"` (the default: dev tooling) or `"slim"` (`24.04-slim`: cua-spacesd and Chromium only; what CI runs). A tier CI has not published yet raises `CuaError.ImageNotPublished`; use `Image.from_registry(ref)` to run it anyway. #### Image.macos ```python @classmethod def macos(version: str = '26', kind: str = 'vm', tier: Optional[str] = None) -> Image ``` macOS: the canonical `ghcr.io/trycua/macos:` image (Lume locally). Supported versions: `"15"` / `"sequoia"`, `"26"` / `"tahoe"`. `tier`: `"full"` (the default), `"slim"` or `"xcode"` / `"xcode-"` (full plus one pinned Xcode); unpublished tiers raise `CuaError.ImageNotPublished`. #### Image.windows ```python @classmethod def windows( version: str = '2022', kind: str = 'vm', tier: Optional[str] = None, ) -> Image ``` Windows: the canonical `ghcr.io/trycua/windows:2022` containerDisk. Other versions, including `"11"`, have no canonical image: on Fleet they are unsupported, and locally they are installed from a downloaded evaluation ISO. `tier="slim"` names `2022-slim` once published. #### Image.omarchy ```python @classmethod def omarchy(channel: Optional[str] = None) -> Image ``` Omarchy (Arch Linux, Hyprland) with cua-spacesd: `ghcr.io/trycua/omarchy:edge` (or `"rc"`/`"stable"`). An amd64 VM: QEMU locally (emulated on arm64 hosts, so slow there), KubeVirt in the cloud. Until CI publishes it this raises `CuaError.ImageNotPublished`; use `Image.from_registry("ghcr.io/trycua/omarchy:edge", kind="vm")` to run it anyway. #### Image.android ```python @classmethod def android(version: str = '14', kind: str = 'vm') -> Image ``` Android image. Always a VM (QEMU emulator). #### Image.from_registry ```python @classmethod def from_registry( ref: str, *, os_type: str = 'linux', kind: Optional[str] = None, agent_type: Optional[str] = None, secret: Optional[Any] = None, ) -> Image ``` Create an image from a registry reference. `secret` (a `cua_sandbox.RegistrySecret`) pulls a private image, the same way locally (the pull) and in the cloud (a registry pull secret for the sandbox). It is never logged or saved. os_type selects the firmware: Windows guest disks are built UEFI-only, so a Windows containerDisk pulled from a registry must say so or it is handed BIOS and will not boot. kind is resolved after pull when omitted. agent_type names the guest control server the disk already runs, the same hint as `from_file`: `"osworld"` selects the OSWorld Flask server on port 5000 instead of the computer-server on 8000, both for local QEMU and for Fleet pools. #### Image.from_file ```python @classmethod def from_file( path: str, *, os_type: str = 'windows', kind: str = 'vm', agent_type: Optional[str] = None, ) -> Image ``` Create an image from a local disk, ISO file, or URL. Supported formats: qcow2, vhdx, raw, img, iso. URLs (http/https) are downloaded automatically. Zip files are extracted. For ISOs, the runtime will create a qcow2 disk and attach the ISO as a CD-ROM for installation/boot. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `path` | `str` | required | Local file path or URL (http/https). | | `os_type` | `str` | `'windows'` | OS type hint ("linux", "windows", "macos", "android"). | | `kind` | `str` | `'vm'` | "vm" or "container". | | `agent_type` | `Optional[str]` | `None` | Agent type hint (e.g. "osworld" for OSWorld Flask server). | #### Image.from_dict ```python @classmethod def from_dict(data: Dict[str, Any]) -> Image ``` Reconstruct an Image from a serialized spec dict. **Example** ```python test="docs" id="image-from-dict-py" session="image-from-dict-py" from cua_sandbox import Image base = Image.linux().apt_install("git") dev = base.pip_install("rich") same = Image.from_dict(dev.to_dict()).to_dict() == dev.to_dict() print([same, len(base.to_dict()["layers"]), len(dev.to_dict()["layers"])]) # [True, 1, 2] ``` #### Image.apt_install ```python def apt_install(*packages: str) -> Image ``` Install packages via apt (Linux only). #### Image.brew_install ```python def brew_install(*packages: str) -> Image ``` Install packages via Homebrew (macOS). #### Image.choco_install ```python def choco_install(*packages: str) -> Image ``` Install packages via Chocolatey (Windows). #### Image.winget_install ```python def winget_install(*packages: str) -> Image ``` Install packages via winget (Windows). #### Image.apk_install ```python def apk_install(*apk_paths: str) -> Image ``` Install APK files via adb (Android only). #### Image.pwa_install ```python def pwa_install( manifest_url: str, package_name: Optional[str] = None, keystore: Optional[str] = None, keystore_alias: str = 'android', keystore_password: str = 'android', builder: str = 'pwa2apk', push_timeout: Optional[float] = None, ) -> 'Image' ``` Build an APK from a PWA manifest URL and install it (Android only). Two builder backends are supported: * `"pwa2apk"` (default): generates a lightweight WebView-based APK. No Chrome dependency, no "Running in Chrome" banner, no Digital Asset Links needed. ~10 KB APK. * `"bubblewrap"`: generates a Chrome Trusted Web Activity (TWA) APK. Requires Chrome on the device and a matching `/.well-known/assetlinks.json` on the server. Shows a mandatory "Running in Chrome" privacy disclosure on every launch. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `manifest_url` | `str` | required | Full URL to the PWA's `manifest.json` or `manifest.webmanifest`. Example: `"http://10.0.2.2:3000/manifest.json"` | | `package_name` | `Optional[str]` | `None` | Android package ID. Defaults to a reversed-hostname derivation (e.g. `"com.example.app"`). | | `keystore` | `Optional[str]` | `None` | Path to a `*.keystore` / `*.jks` file. When omitted a fresh keystore is generated and cached. Pass the keystore bundled with your PWA repo so the fingerprint is deterministic. | | `keystore_alias` | `str` | `'android'` | Key alias inside the keystore (default `"android"`). | | `keystore_password` | `str` | `'android'` | Password for both the store and the key (default `"android"`). | | `builder` | `str` | `'pwa2apk'` | `"pwa2apk"` (default) or `"bubblewrap"`. | | `push_timeout` | `Optional[float]` | `None` | Optional seconds for the `write_bytes` `adb push` of the built APK. When `None` the server default applies. | #### Image.uv_install ```python def uv_install(*packages: str) -> Image ``` Install Python packages via uv add into the cua-server project. #### Image.pip_install ```python def pip_install(*packages: str) -> Image ``` Install Python packages via pip. #### Image.app_install ```python def app_install(app_id: str) -> Image ``` Install an app from the cua-sandbox-apps catalog. Requires `cua-sandbox-apps` to be installed. The app's install script for this image's OS is executed as a build layer. #### Image.run ```python def run(command: str) -> Image ``` Run a shell command during image build. #### Image.env ```python def env(**variables: str) -> Image ``` Set environment variables. #### Image.copy ```python def copy(src: str, dst: str) -> Image ``` Copy a file into the image. #### Image.expose ```python def expose(port: int) -> Image ``` Expose a port. #### Image.to_build_recipe ```python def to_build_recipe( *, name: str, namespace: str, tags: Mapping[str, str] | None = None, timeout_seconds: int | None = None, disk_size: str | None = None, file_references: Mapping[str, ImageFileReference] | None = None, ) -> Dict[str, Any] ``` Return a CRD-validated Image custom-resource manifest. #### Image.to_dict ```python def to_dict() -> Dict[str, Any] ``` Serialize to a plain dict suitable for JSON or cloud API. #### Image.to_cloud_init ```python def to_cloud_init() -> str ``` Generate a cloud-init user-data script from the image layers. #### Image.local_support ```python def local_support() ``` Check whether this image can run locally on the current host. Returns a `cua_sandbox.runtime.compat.RuntimeSupport` describing: - `supported` : runtime is available or auto-installable on this OS - `hw_accel` : hardware acceleration (HVF / KVM / Hyper-V) is available - `runtime_installed`: runtime binary found right now (no install needed) - `auto_installable`: SDK can install the runtime automatically - `reason` : human-readable explanation In tests, use the bundled helper instead of checking manually: ```python from cua_sandbox.runtime.compat import skip_if_unsupported async def test_something(): skip_if_unsupported(Image.macos()) async with Sandbox.ephemeral(Image.macos(), local=True) as sb: ... ``` ### ImageInfo ```python class ImageInfo ImageInfo( reference: str, pinned_ref: str, digest: str, variant: str, arch: Optional[str], os: str, emulated: bool, ) ``` The image a sandbox runs, as resolved and pinned at create time. `reference` is the reference as requested (normalised, e.g. `docker.io/library/python:3.12-slim`), `pinned_ref` the `registry/repo@sha256:...` that ran, `variant` one of `rootfs`, `containerdisk` or `lume`, `arch` the architecture that ran (`amd64`/`arm64`, when known), `os` the guest OS and `emulated` whether it ran under emulation. | Attribute | Type | Description | | --- | --- | --- | | `reference` | `str` | | | `pinned_ref` | `str` | | | `digest` | `str` | | | `variant` | `str` | | | `arch` | `Optional[str]` | | | `os` | `str` | | | `emulated` | `bool` | | ## Containers and registries ### Container ```python class Container Container( image: str, command: Optional[Sequence[str]] = None, env: Optional[Mapping[str, str]] = None, ports: Optional[Sequence[int]] = None, name: Optional[str] = None, ) ``` A sidecar container, reachable from the sandbox at its `name`. `name` (a DNS label, not `main`) defaults from the image (`redis` for `redis:7-alpine`); the sidecar reaches the sandbox at `main`. `command` replaces the image's entrypoint. `env` holds plain values, not secrets. | Attribute | Type | Description | | --- | --- | --- | | `image` | `str` | | | `command` | `Optional[Sequence[str]]` | | | `env` | `Optional[Mapping[str, str]]` | | | `ports` | `Optional[Sequence[int]]` | | | `name` | `Optional[str]` | | #### Container.native ```python def native() -> Any ``` ### RegistrySecret ```python class RegistrySecret RegistrySecret( username: str, password: str, *, registry: Optional[str] = None, ) ``` Credentials for a private registry image. * `RegistrySecret(username, password)`: explicit values (a token works as the password). * `RegistrySecret.from_env()`: read `CUA_REGISTRY_USERNAME` / `CUA_REGISTRY_PASSWORD` (or the variables you name) at create time. * `RegistrySecret.aws_ecr(region=None)`: a private Amazon ECR image, with a login token from the AWS CLI. `registry` scopes them to one host; by default they apply to the image's registry. The password never appears in `repr` or logs. | Attribute | Type | Description | | --- | --- | --- | | `registry` | `Optional[str]` | | | `username` | `Optional[str]` | | **Example** ```python test="docs" id="registry-secret-py" session="registry-secret-py" from cua_sandbox import Image, RegistrySecret print(Image.from_registry("python:3.12-slim").to_dict()["registry"]) print(repr(RegistrySecret("robot", "s3cr3t", registry="registry.example.com"))) # the password never shows ``` #### RegistrySecret.from_env ```python @classmethod def from_env( username_var: str = 'CUA_REGISTRY_USERNAME', password_var: str = 'CUA_REGISTRY_PASSWORD', *, registry: Optional[str] = None, ) -> 'RegistrySecret' ``` #### RegistrySecret.aws_ecr ```python @classmethod def aws_ecr(region: Optional[str] = None) -> 'RegistrySecret' ``` #### RegistrySecret.native ```python def native() -> Any ``` ### ImageFileReference ```python class ImageFileReference(BaseModel) ``` | Attribute | Type | Description | | --- | --- | --- | | `model_config` | `ConfigDict` | | | `digest` | `constr(pattern='^sha256:[0-9a-f]{64}$')` | | | `reference` | `constr(pattern='^uploads/[a-z0-9]([-a-z0-9]*[a-z0-9])?/[A-Za-z0-9_-]+$', min_length=1, max_length=2048)` | | | `size_bytes` | `conint(ge=0, le=9007199254740991)` | | --- # Pool Pools, templates, sandbox specs and pool errors of cua-sandbox. `Pool` keeps warm cloud capacity for a `SandboxSpec`. `Sandbox.create` uses shared capacity; a named pool is for dedicated capacity. Most programs never touch pools: a cloud `Sandbox.create` claims from a pool the SDK manages per image and shape (`cua-auto-*`), tuned with `CloudOptions`. A named `Pool` is dedicated capacity you size and own; `Sandbox.create(cloud=CloudOptions(pool=...))` claims from it, and fields that differ from its template raise `PoolSpecMismatch`. ## Pools and templates ### Pool ```python class Pool Pool( resource: Any, *, owned_template: Any = None, agent_type: str | None = None, ) ``` A Fleet warm pool that can provide durable Sandbox claims. | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | | `resource` | `Any` | | #### Pool.reconcile ```python @classmethod async def reconcile(request: CreatePoolRequest) -> 'Pool' ``` #### Pool.get ```python @classmethod async def get(name: str) -> 'Pool' ``` #### Pool.apply ```python @classmethod async def apply(*args: Any, **kwargs: Any) -> 'Pool' ``` Reconcile a Fleet pool: `Pool.apply(name, spec, options)`. `spec` (`cua_sandbox.SandboxSpec`) is what the pool's sandboxes run and `options` (`cua_sandbox.PoolOptions`) how the pool keeps capacity for them. The pool, its namespace and its template share `name`, which is globally unique on Fleet. This is the SDK's one pool writer (`Fleet.apply`): the image is pinned to the variant the runtime runs, a new pool is rolled back if its template fails, and a runtime the image cannot run on raises `cua.CuaError.InvalidArgument` before anything is created. The old form `Pool.apply(image, *, name=..., replicas=..., cpu=..., memory_mb=..., services=..., autoscaling=..., ttl_seconds_after_created=..., runtime=...)` still works and is converted to the new one, with a `DeprecationWarning`. **Example** ```python test="docs" id="pool-apply-py" session="pool-apply-py" prelude="fakefleet" import asyncio from cua_sandbox import CloudOptions, Image, Pool, PoolOptions, Sandbox, SandboxSpec async def main(): pool = await Pool.apply("my-team-linux", SandboxSpec(image=Image.linux()), PoolOptions(replicas=2)) sb = await Sandbox.create(cloud=CloudOptions(pool=pool)) # or CloudOptions(pool="my-team-linux") print(sb.id) await sb.destroy() await pool.delete() asyncio.run(main()) ``` #### Pool.export ```python @staticmethod async def export(name: str, *, terraform: bool = False) -> 'PoolExport | str' ``` Read pool `name` back as the shared model (a `PoolExport` whose `spec` / `options` are the native records), or with `terraform=True` the equivalent Terraform `fleets_pool` block (attributes the provider lacks yet are commented). #### Pool.check ```python @staticmethod async def check(name: str, spec: SandboxSpec) -> None ``` Raise `cua_sandbox.PoolSpecMismatch` (with a readable diff) when the set fields of `spec` differ from pool `name`'s template. #### Pool.apply_template ```python @staticmethod async def apply_template(name: str, spec: SandboxSpec) -> None ``` Lay the set fields of `spec` over pool `name`'s template and write it (the pool's capacity is kept; a no-op when nothing differs). #### Pool.delete ```python async def delete() -> None ``` Delete this Fleet pool. #### Pool.create_claim ```python async def create_claim( *, spec: ClaimSpec | None = None, name: str | None = None, ttl_seconds_after_created: int | None = None, ) -> _ClaimHandle ``` #### Pool.claim ```python def claim( *, spec: ClaimSpec | None = None, name: str | None = None, service: str = ENV_SERVICE, time_to_start: float | None = None, ttl_seconds_after_created: int | None = None, agent_type: str | None = None, claim_token: str | None = None, ) -> _ClaimResult[Sandbox] ``` Claim a sandbox. `agent_type="osworld"` selects the OSWorld transport for pools fetched with `Pool.get` (pools from `Pool.apply` remember it). `claim_token` (see `generate_claim_token`) is delivered into the sandbox at `/run/cua/env-token` through the claim's Secret; the pool's spec must set `claim_secrets=True`. The claim waits (at most 90 s after it binds) until the sandbox has it, else it is released and `cua_sandbox.ClaimSecretsNotDelivered` is raised. ### Template ```python class Template Template(resource: Any) ``` A reconciled Fleet sandbox template. | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | | `resource` | `Any` | | #### Template.reconcile ```python @classmethod async def reconcile(request: CreateTemplateRequest) -> 'Template' ``` ## Managed pools (cua_sandbox.pools) ### pools Managed Fleet pools: list them and garbage-collect them. `Sandbox.ephemeral(image, local=False)` claims sandboxes from this account's managed pools (`cua-auto-*`), one per image spec, created on first use and scaled to zero when idle. Idle pools are deleted automatically after `CUA_FLEET_POOL_IDLE_GC`; these calls inspect them or collect now. ```python from cua_sandbox import pools for pool in await pools.list_pools(): print(pool.name, pool.claims, pool.last_used) report = await pools.gc(idle_after=1800) ``` ### list_pools ```python async def list_pools(cfg: Optional[AutoPoolConfig] = None) -> list[ManagedPoolInfo] ``` This account's managed pools (`cua-auto-*` and legacy `cua-eph-*`). ### list_claims ```python async def list_claims(cfg: Optional[AutoPoolConfig] = None) -> list[ClaimInfo] ``` Claims in every pool this account can see (managed and explicit). ### gc ```python async def gc( idle_after: Optional[Duration] = None, cfg: Optional[AutoPoolConfig] = None, ) -> GcReport ``` Delete idle managed pools and stuck managed claims now. ### gc_pools ```python async def gc_pools(names: list[str], idle_after: Duration = 0) -> GcReport ``` Delete the named managed pools once they have no claims and have been idle for `idle_after` (default: now). Pools with live claims are kept. Tests use this to remove exactly the pools they created. ### is_managed_pool_name ```python def is_managed_pool_name(name: str, prefix: str = MANAGED_PREFIX) -> bool ``` ### ManagedPoolInfo ```python class ManagedPoolInfo ManagedPoolInfo( name: str, spec_hash: Optional[str], managed: bool, replicas: Optional[int], ready_replicas: Optional[int], claims: int, last_used: Optional[datetime], created_at: Optional[str], ) ``` | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | | `spec_hash` | `Optional[str]` | | | `managed` | `bool` | | | `replicas` | `Optional[int]` | | | `ready_replicas` | `Optional[int]` | | | `claims` | `int` | | | `last_used` | `Optional[datetime]` | | | `created_at` | `Optional[str]` | | ### ClaimInfo ```python class ClaimInfo ClaimInfo( name: str, pool: str, phase: Optional[str], managed: bool, created_at: Optional[str], ) ``` | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | | `pool` | `str` | | | `phase` | `Optional[str]` | | | `managed` | `bool` | | | `created_at` | `Optional[str]` | | ### GcReport ```python class GcReport GcReport( pools_deleted: list[str] = list(), claims_deleted: list[str] = list(), namespaces_deleted: list[str] = list(), errors: list[str] = list(), ) ``` | Attribute | Type | Description | | --- | --- | --- | | `pools_deleted` | `list[str]` | | | `claims_deleted` | `list[str]` | | | `namespaces_deleted` | `list[str]` | | | `errors` | `list[str]` | | ## Specs ### SandboxSpec ```python class SandboxSpec SandboxSpec( image: Any = None, command: Optional[Sequence[str]] = None, args: Optional[Sequence[str]] = None, env: dict[str, str] = dict(), services: dict[str, int] = dict(), wait_for: Any = None, cpu: Optional[int] = None, memory_mb: Optional[int] = None, memory: Union[str, int, None] = None, efi: bool = False, sidecars: Sequence[Any] = (), registry_secret: Any = None, registry_secret_name: Optional[str] = None, process_mode: Optional[str] = None, claim_secrets: bool = False, ) ``` What a sandbox runs. Unset fields keep Fleet's defaults (and are not compared against a named pool's template). * `image`: an `cua_sandbox.Image` (`Image.from_registry(ref, secret=...)` carries its registry secret) or a registry reference. * `command` / `args`: argv replacing the image ENTRYPOINT / CMD. * `env`: plain environment variables (not secrets). * `services`: named guest ports, `{"mcp": 8765}`. * `wait_for`: `tcp("mcp")` / `http("mcp", "/health")`: a replica binds a claim only once it passes. * `cpu` / `memory_mb` (or `memory="4GB"`). * `sidecars`: `cua_sandbox.Container` (or image strings). * `registry_secret`: a `cua_sandbox.RegistrySecret`, stored as the pool's `cua-registry-*` pull Secret; `registry_secret_name` references an existing one. * `process_mode`: `"Legacy"` or `"Run"`. * `claim_secrets`: claims may carry a per-claim env token. | Attribute | Type | Description | | --- | --- | --- | | `image` | `Any` | | | `command` | `Optional[Sequence[str]]` | | | `args` | `Optional[Sequence[str]]` | | | `env` | `dict[str, str]` | | | `services` | `dict[str, int]` | | | `wait_for` | `Any` | | | `cpu` | `Optional[int]` | | | `memory_mb` | `Optional[int]` | | | `memory` | `Union[str, int, None]` | | | `efi` | `bool` | | | `sidecars` | `Sequence[Any]` | | | `registry_secret` | `Any` | | | `registry_secret_name` | `Optional[str]` | | | `process_mode` | `Optional[str]` | | | `claim_secrets` | `bool` | | #### SandboxSpec.reference ```python def reference() -> str ``` The image's registry reference (`""` when unset). #### SandboxSpec.native ```python def native() -> Any ``` The `cua.SandboxSpec` record. ### PoolOptions ```python class PoolOptions PoolOptions( runtime: Optional[str] = None, replicas: Optional[int] = None, warm: Optional[bool] = None, min_pool_size: Optional[int] = None, max_pool_size: Optional[int] = None, idle_ttl: Seconds = None, ttl_policy: Optional[str] = None, pool_ttl: Seconds = None, claim_ttl: Seconds = None, ) ``` How a pool keeps capacity for a `SandboxSpec`. Unset fields keep Fleet's defaults. * `runtime`: `"gvisor"` or `"kubevirt"` (unset: from the image). * `replicas`: size of a pool without autoscaling (default 1). * `warm`: keep one sandbox ready (`minPoolSize: 1`). * `min_pool_size` / `max_pool_size`: autoscaling floor / ceiling. * `idle_ttl`: delete the pool after this long without claims. * `ttl_policy`: `"Retain"` or `"Cascade"` (what TTL expiry deletes). * `pool_ttl`: pool creation-age TTL. * `claim_ttl`: default TTL of claims made on the pool. Durations are seconds or a `timedelta`. | Attribute | Type | Description | | --- | --- | --- | | `runtime` | `Optional[str]` | | | `replicas` | `Optional[int]` | | | `warm` | `Optional[bool]` | | | `min_pool_size` | `Optional[int]` | | | `max_pool_size` | `Optional[int]` | | | `idle_ttl` | `Seconds` | | | `ttl_policy` | `Optional[str]` | | | `pool_ttl` | `Seconds` | | | `claim_ttl` | `Seconds` | | #### PoolOptions.native ```python def native() -> Any ``` The `cua.PoolOptions` record. ### PoolExport ```python class PoolExport PoolExport(name: str, runtime: str, spec: Any, options: Any, terraform: str) ``` A pool read back as the shared model (`Pool.export`). | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | | `runtime` | `str` | | | `spec` | `Any` | | | `options` | `Any` | | | `terraform` | `str` | | ### generate_claim_token ```python def generate_claim_token() -> str ``` A fresh per-claim env token (64 hex characters) for `pool.claim(claim_token=...)`. ## Errors ### PoolSpecMismatch ```python class PoolSpecMismatch(InvalidArgument) ``` A named cloud pool's template differs from the sandbox fields given with it. The message holds a readable diff; pass `CloudOptions(pool=..., apply=True)` to update the pool's template instead, or omit the fields to use the pool as it is. ### ClaimSecretsNotDelivered ```python class ClaimSecretsNotDelivered(TimeoutError) ``` A claim bound, but its per-claim secrets (the env token) never reached the sandbox within the bounded wait (90 s); the claim was released. ### PoolAccessDeniedError ```python class PoolAccessDeniedError(PermissionError) ``` Fleet refused a pool or template operation for this credential. ## Re-exported names `cua_sandbox` also re-exports these names from other packages; see their own documentation. | Module | Names | | --- | --- | | `fleet_sdk` | `ClaimSpec`, `CreatePoolRequest`, `CreatePoolRequestBuilder`, `CreateTemplateRequest`, `CreateTemplateRequestBuilder`, `Firmware`, `OsGymSandboxTemplateSpec`, `OsGymSandboxTemplateSpecBuilder`, `OsGymSandboxWarmPoolSpec`, `OsGymSandboxWarmPoolSpecBuilder`, `RuntimeKind`, `SandboxService`, `SandboxServiceBuilder`, `SandboxTemplateRef`, `SandboxTemplateRefBuilder`, `ServiceProtocol`, `TemplateResource`, `VmTemplate`, `VmTemplateBuilder`, `WarmPoolAutoscaling`, `WarmPoolAutoscalingBuilder` | --- # Interfaces Shell, mouse, keyboard, screen, clipboard, window, terminal, files, apps, mobile and driver interfaces. Every sandbox exposes these interfaces as attributes (`sb.shell`, `sb.mouse`, ...). Input goes through cua-driver inside the guest. | Needs | Interfaces | | --- | --- | | Nothing in the guest | `sb.service(name)`, `sb.public_url()`, `sb.tunnel`, `sb.mcp()` ([services]()) | | cua-spacesd (the canonical `Image.linux()` has it) | `sb.shell`, `sb.files`, `sb.mouse`, `sb.keyboard`, `sb.screen`, `sb.clipboard`, `sb.terminal`, `sb.window`, `sb.apps` | Without cua-spacesd these fall back to the runtime's agentless path (QMP, VNC, SSH or ADB) where one exists, and otherwise raise `SpacesdNotAvailable`. Coordinates are screen pixels; input goes through Cua Driver in the guest. ## Shell commands ### Shell ```python class Shell ``` Shell command execution. #### Shell.run ```python async def run( command: str, timeout: int = 30, background: bool = False, ) -> CommandResult ``` Run a shell command and return the result. When `background=True`, returns immediately with `stdout=str(pid)` and `returncode=0`; poll for completion via a sentinel file or by inspecting the process list. ### CommandResult ```python class CommandResult ``` | Attribute | Type | Description | | --- | --- | --- | | `stdout` | `str` | | | `stderr` | `str` | | | `returncode` | `int` | | | `success` | `bool` | | ## Input and display ### Mouse ```python class Mouse ``` Mouse control. #### Mouse.click ```python async def click(x: int, y: int, button: str = 'left') -> None ``` **Example** ```python test="docs" id="mouse-click-py" session="mouse-click-py" prelude="spacesd,spacesd-vars" import asyncio from cua_sandbox import Sandbox async def main(): async with Sandbox.connect(url=URL, token=TOKEN) as sb: width, height = await sb.screen.size() await sb.mouse.click(width // 2, height // 2) await sb.keyboard.type("hello") await sb.keyboard.keypress(["ctrl", "a"]) png = await sb.screenshot() print(png[:4]) # b'\x89PNG' asyncio.run(main()) ``` #### Mouse.right_click ```python async def right_click(x: int, y: int) -> None ``` #### Mouse.double_click ```python async def double_click(x: int, y: int) -> None ``` #### Mouse.move ```python async def move(x: int, y: int) -> None ``` #### Mouse.scroll ```python async def scroll(x: int, y: int, scroll_x: int = 0, scroll_y: int = 3) -> None ``` Scroll the wheel with the pointer at (x, y). `scroll_x`/`scroll_y` are wheel notches, not a position: positive `scroll_y` scrolls up, negative scrolls down; positive `scroll_x` scrolls right. #### Mouse.mouse_down ```python async def mouse_down(x: int, y: int, button: str = 'left') -> None ``` #### Mouse.mouse_up ```python async def mouse_up(x: int, y: int, button: str = 'left') -> None ``` #### Mouse.drag ```python async def drag( start_x: int, start_y: int, end_x: int, end_y: int, button: str = 'left', ) -> None ``` ### Keyboard ```python class Keyboard ``` Keyboard control. #### Keyboard.type ```python async def type(text: str) -> None ``` Type a string of text. #### Keyboard.keypress ```python async def keypress(keys: Union[List[str], str]) -> None ``` Press a key combination (e.g. ["ctrl", "c"] or "enter"). #### Keyboard.key_down ```python async def key_down(key: str) -> None ``` #### Keyboard.key_up ```python async def key_up(key: str) -> None ``` ### Screen ```python class Screen ``` Screen capture and info. #### Screen.screenshot ```python async def screenshot(format: str = 'png', quality: int = 95) -> bytes ``` Capture a screenshot and return raw image bytes. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `format` | `str` | `'png'` | "png" (lossless, default) or "jpeg" (lossy, ~5-10x smaller). | | `quality` | `int` | `95` | JPEG quality 1-95, ignored for PNG. | #### Screen.screenshot_base64 ```python async def screenshot_base64(format: str = 'png', quality: int = 95) -> str ``` Capture a screenshot and return as a base64-encoded string. #### Screen.size ```python async def size() -> Tuple[int, int] ``` Return (width, height) of the screen. ### Clipboard ```python class Clipboard ``` Clipboard read/write. #### Clipboard.get ```python async def get() -> str ``` Return the current clipboard text. #### Clipboard.set ```python async def set(text: str) -> None ``` Set the clipboard text. ### Window ```python class Window ``` Window management. #### Window.get_active_title ```python async def get_active_title() -> str ``` Return the title of the currently focused window ("" when none). cua-spacesd answers from WindowsService.ListWindows (the focused window); computer-server never implemented this command. ## Terminal, files and apps ### Terminal ```python class Terminal ``` PTY terminal sessions. #### Terminal.create ```python async def create(command: Optional[str] = None, cols: int = 80, rows: int = 24) -> dict ``` Create a new PTY session. Returns {"pid": int, "cols": int, "rows": int}. #### Terminal.send_input ```python async def send_input(pid: int, data: str) -> None ``` Send input to a PTY session. #### Terminal.info ```python async def info(pid: int) -> Optional[dict] ``` Return session info or None if gone. #### Terminal.close ```python async def close(pid: int) -> bool ``` Kill a PTY session. ### Files ```python class Files ``` File and directory operations inside the sandbox. All paths are inside the sandbox unless otherwise noted. #### Files.exists ```python async def exists(path: str) -> bool ``` #### Files.is_dir ```python async def is_dir(path: str) -> bool ``` #### Files.size ```python async def size(path: str) -> int ``` #### Files.list ```python async def list(path: str) -> list[FileEntry] ``` #### Files.make_dir ```python async def make_dir(path: str) -> None ``` #### Files.remove_dir ```python async def remove_dir(path: str) -> None ``` #### Files.remove ```python async def remove(path: str) -> None ``` #### Files.read_text ```python async def read_text(path: str) -> str ``` #### Files.write_text ```python async def write_text(path: str, content: str) -> None ``` **Example** ```python test="docs" id="files-write-text-py" session="files-write-text-py" prelude="spacesd,spacesd-vars" import asyncio from cua_sandbox import Sandbox async def main(): async with Sandbox.connect(url=URL, token=TOKEN) as sb: await sb.files.write_text("/tmp/note.txt", "hi") print([await sb.files.read_text("/tmp/note.txt")]) # ['hi'] asyncio.run(main()) ``` #### Files.read_bytes ```python async def read_bytes(path: str, offset: int = 0, length: Optional[int] = None) -> bytes ``` #### Files.write_bytes ```python async def write_bytes(path: str, content: bytes) -> None ``` #### Files.upload ```python async def upload(local_path: Union[str, Path], remote_path: str) -> None ``` Push a file from the host into the sandbox. #### Files.download ```python async def download(remote_path: str, local_path: Union[str, Path]) -> None ``` Pull a file from the sandbox down to the host. ### FileEntry ```python class FileEntry ``` | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | | `path` | `str` | | | `is_dir` | `bool` | | | `size` | `Optional[int]` | | ### Apps ```python class Apps ``` Install and launch cataloged applications inside the sandbox. #### Apps.install ```python async def install(app_id: str) -> CommandResult ``` Install an app by its catalog ID (e.g. `"unity"`, `"godot-engine"`). #### Apps.launch ```python async def launch(app_id: str) -> CommandResult ``` Launch a previously-installed app by its catalog ID. ## Mobile gestures ### Mobile ```python class Mobile ``` Mobile (Android) touch and hardware-key control. All touch coordinates are in screen pixels. Single-touch methods use `input tap/swipe` via `adb shell`. Multi-touch gestures delegate to the `multitouch_gesture` transport action, which uses `adb root` + MT Protocol B `sendevent` for reliable injection on both local and cloud transports. #### Mobile.tap ```python async def tap(x: int, y: int) -> None ``` #### Mobile.long_press ```python async def long_press(x: int, y: int, duration_ms: int = 1000) -> None ``` #### Mobile.double_tap ```python async def double_tap(x: int, y: int, delay: float = 0.1) -> None ``` #### Mobile.type_text ```python async def type_text(text: str) -> None ``` #### Mobile.swipe ```python async def swipe(x1: int, y1: int, x2: int, y2: int, duration_ms: int = 300) -> None ``` #### Mobile.scroll_up ```python async def scroll_up(x: int, y: int, distance: int = 600, duration_ms: int = 400) -> None ``` #### Mobile.scroll_down ```python async def scroll_down( x: int, y: int, distance: int = 600, duration_ms: int = 400, ) -> None ``` #### Mobile.scroll_left ```python async def scroll_left( x: int, y: int, distance: int = 400, duration_ms: int = 300, ) -> None ``` #### Mobile.scroll_right ```python async def scroll_right( x: int, y: int, distance: int = 400, duration_ms: int = 300, ) -> None ``` #### Mobile.fling ```python async def fling(x1: int, y1: int, x2: int, y2: int) -> None ``` #### Mobile.gesture ```python async def gesture( *finger_paths: tuple[int, int], duration_ms: int = 400, steps: int = 0, ) -> None ``` Inject an arbitrary N-finger gesture via MT Protocol B `sendevent`. Each positional argument is a sequence of `(x, y)` waypoints for one finger. Pass an even number of `(x, y)` tuples and they are paired into start/end positions per finger: ```python # two-finger pinch-out await mobile.gesture( (cx - 20, cy), (cx - 200, cy), # finger 0 (cx + 20, cy), (cx + 200, cy), # finger 1 ) ``` Delegates to the `multitouch_gesture` transport action, which uses `adb root` + MT Protocol B `sendevent` for reliable injection on both local ADB and cloud HTTP transports. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `*finger_paths` | `tuple[int, int]` | `()` | Alternating start/end `(x, y)` tuples, two per finger. Must have an even length >= 4 (at least 2 fingers × 2 points). | | `duration_ms` | `int` | `400` | Total gesture duration in milliseconds. | | `steps` | `int` | `0` | Interpolation steps (0 = auto: duration_ms // 20, min 5). | #### Mobile.pinch_in ```python async def pinch_in(cx: int, cy: int, spread: int = 300, duration_ms: int = 400) -> None ``` Pinch-in (zoom out) with two real simultaneous fingers. #### Mobile.pinch_out ```python async def pinch_out(cx: int, cy: int, spread: int = 300, duration_ms: int = 400) -> None ``` Pinch-out (zoom in) with two real simultaneous fingers. #### Mobile.key ```python async def key(keycode: int) -> None ``` #### Mobile.home ```python async def home() -> None ``` #### Mobile.back ```python async def back() -> None ``` #### Mobile.recents ```python async def recents() -> None ``` #### Mobile.power ```python async def power() -> None ``` #### Mobile.volume_up ```python async def volume_up() -> None ``` #### Mobile.volume_down ```python async def volume_down() -> None ``` #### Mobile.enter ```python async def enter() -> None ``` #### Mobile.backspace ```python async def backspace() -> None ``` #### Mobile.wake ```python async def wake() -> None ``` #### Mobile.notifications ```python async def notifications() -> None ``` #### Mobile.close_notifications ```python async def close_notifications() -> None ``` ## Cua Driver ### Driver ```python class Driver ``` Accessor for optional typed Driver sessions; does not replace Sandbox transport. Each connection is a host-bound session with a launcher-selected permission mode (Standard by default). Remote clients cannot select its authority. Use typed inputs with `session=None` where optional. For required session fields, obtain the bound name with `sandbox.driver.session_name(driver)`. Creating or rebinding trusted sessions is unsupported. Remote cleanup is best effort with a bounded timeout; failures warn and do not block claim release. #### Driver.session_name ```python def session_name(driver: CuaDriver) -> str ``` Return this active connection's host-bound public session label. This label is not a credential or permission grant. It can populate canonical typed inputs whose `session` field is required. #### Driver.connect ```python async def connect( *, service: str | None = None, transport: Literal['envelope', 'mcp'] | None = None, ) -> AsyncIterator[CuaDriver] ``` Yield the canonical Driver; the receiver must support typed envelopes. With no arguments this uses the image's Fleet `driver` service when it publishes one (the private envelope HTTP carrier), and otherwise cua-spacesd's `/mcp` (`service="env"`, `transport="mcp"`) through the cua SDK. `service="mcp", transport="mcp"` selects a Fleet named MCP service instead. MCP selection verifies the `ai.cua.driver.envelopes` extension before opening a receiver. No fallback occurs. #### Driver.close ```python async def close() -> None ``` Invalidate all connections before the owning transport is closed. ### DriverConnectionError ```python class DriverConnectionError(RuntimeError) ``` Sanitized failure to establish or use a sandbox Driver service. --- # Services, tunnels and MCP Named services, signed and public URLs, tunnels and MCP endpoints of a cua-sandbox sandbox. Reach ports inside a sandbox: named services, tunnels, shareable URLs and MCP servers. ## Named services ### ServiceHandle ```python class ServiceHandle ``` One named service of a sandbox (`sb.service("mcp")`). The same calls work local and in the cloud: requests carry your headers (the cloud gateway's credentials are added for you), `url()` is usable from this machine and `public_url()` can be shared. | Attribute | Type | Description | | --- | --- | --- | | `name` | `str` | | #### ServiceHandle.request ```python async def request( method: str, path: str = '/', *, headers: Headers = None, body: bytes | str | None = None, json: Any = None, timeout: float = 30.0, ) -> Any ``` One HTTP request to `path` on the service; returns an `httpx.Response`. `authorization` is refused in the cloud, where the gateway credentials own it. #### ServiceHandle.url ```python async def url() -> str ``` A URL for the service usable from this machine with no credentials (no trailing slash): the published loopback port locally, a signed URL (1 h, renewed on later calls) in the cloud. #### ServiceHandle.public_url ```python async def public_url(ttl: float = 3600, *, label: Optional[str] = None) -> 'PublicUrl' ``` A shareable URL that stops working after `ttl` seconds (60 s to 24 h). See `cua_sandbox.Sandbox.public_url`. ### Services ```python class Services ``` Request a named service exposed by the active sandbox connection. #### Services.request ```python async def request( name: str, *, method: str, path: str, json: Any = None, headers: dict[str, str] | None = None, ) -> Any ``` Send a request to a named service on this sandbox. Prefer `sb.service(name).request(...)`, which works the same local and in the cloud. #### Services.create_signed_url ```python async def create_signed_url( name: str, *, expires_in_seconds: int, label: str | None = None, ) -> SignedServiceURL ``` Create a revocable public URL for a named cloud service. Prefer `sb.public_url(name, ttl=...)`, which also works locally. #### Services.list_signed_urls ```python async def list_signed_urls() -> list[SignedServiceURL] ``` List signed service URLs created for this cloud sandbox. #### Services.revoke_signed_url ```python async def revoke_signed_url(signed_url: SignedServiceURL) -> None ``` Revoke a previously created signed service URL. ### SignedServiceURL ```python class SignedServiceURL ``` A revocable public URL for one sandbox service. Provider internals (the cloud namespace and claim) live in `details`. | Attribute | Type | Description | | --- | --- | --- | | `id` | `str` | | | `sandbox` | `str` | | | `service` | `str` | | | `label` | `str \| None` | | | `url` | `str` | | | `created_at` | `str` | | | `expires_at` | `str` | | | `revoked_at` | `str \| None` | | | `details` | `dict` | | | `namespace` | `str \| None` | Deprecated: `details["namespace"]`. | | `claim` | `str \| None` | Deprecated: `details["claim"]`. | #### SignedServiceURL.from_resource ```python @classmethod def from_resource(resource: Any) -> 'SignedServiceURL' ``` ## Tunnels ### Tunnel ```python class Tunnel ``` Port-forwarding interface: exposes sandbox ports on the host. #### Tunnel.forward ```python def forward(*ports: int | str) -> _TunnelContext ``` Forward one or more sandbox ports (or abstract sockets) to the host. *ports* may be: - `int`: TCP port inside the sandbox (e.g. `8080`) - `str`: Android abstract socket name (e.g. `"chrome_devtools_remote"`) Returns a context manager (or awaitable) that yields: - a single `TunnelInfo` when one target is given - a `dict[sandbox_port, TunnelInfo]` when multiple targets are given Example: Chrome DevTools over ADB: ```python async with sb.tunnel.forward("chrome_devtools_remote") as t: # http://localhost:/json lists CDP targets print(t.url) ``` ### TunnelInfo ```python class TunnelInfo ``` A single forwarded port. | Attribute | Type | Description | | --- | --- | --- | | `host` | `str` | | | `port` | `int` | | | `sandbox_port` | `int` | | | `url` | `str` | | #### TunnelInfo.close ```python async def close() -> None ``` Close this tunnel (no-op if already closed or inside a context manager). ## MCP ### mcp_config ```python async def mcp_config( service: Callable[[], Awaitable[Any]], path: str = '/mcp', ) -> Dict[str, Any] ``` `{"url": ..., "headers": {...}}` of the MCP endpoint at `path`. ### connect ```python async def connect(config: Dict[str, Any]) -> AsyncIterator[Any] ``` An initialized official-SDK client for `config` (`url`, `headers`). With `mcp` 2.x this is an `mcp.Client` (protocol negotiated with `server/discover`, falling back to `initialize`); with 1.x an initialized `mcp.ClientSession`. ### open_mcp ```python async def open_mcp( service: Callable[[], Awaitable[Any]], path: str = '/mcp', ) -> AsyncIterator[Any] ``` `connect(await mcp_config(service, path))`. --- # Runtimes Local runtimes (Docker, QEMU, Lume, Hyper-V, Tart, Android emulator) and local support checks. Local sandboxes run on a runtime the SDK picks for the image. Pass one explicitly to `Sandbox.create(..., runtime=...)` to override it. ## Support checks ### RuntimeSupport ```python class RuntimeSupport RuntimeSupport( supported: bool, hw_accel: bool, runtime_installed: bool, auto_installable: bool, runtime_name: str, host_os: str, host_arch: str, guest_os: str, reason: str, ) ``` Result of a local runtime compatibility check for a given Image. | Attribute | Type | Description | | --- | --- | --- | | `supported` | `bool` | | | `hw_accel` | `bool` | | | `runtime_installed` | `bool` | | | `auto_installable` | `bool` | | | `runtime_name` | `str` | | | `host_os` | `str` | | | `host_arch` | `str` | | | `guest_os` | `str` | | | `reason` | `str` | | ### check_local_support ```python def check_local_support(image: 'Image') -> RuntimeSupport ``` Return a RuntimeSupport describing whether *image* can run locally. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `image` | `'Image'` | required | The Image to check. Use Image.linux(), Image.macos(), etc. | **Returns:** RuntimeSupport with .supported, .hw_accel, .reason, and more. ### skip_if_unsupported ```python def skip_if_unsupported(image: 'Image', *, require_hw_accel: bool = False) -> None ``` Call at the top of a pytest test/fixture to skip when the host can't run *image*. Example: ```python async def test_macos_brew_install(): skip_if_unsupported(Image.macos()) async with Sandbox.ephemeral(Image.macos().brew_install("wget"), local=True) as sb: assert (await sb.shell.run("wget --version")).success ``` | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `image` | `'Image'` | required | The Image the test intends to boot with local=True. | | `require_hw_accel` | `bool` | `False` | If True, also skip when hardware acceleration is unavailable (e.g. skip Android tests that would run in software emulation). | ## Runtimes ### Runtime ```python class Runtime(ABC) ``` Base class for local runtimes that create VMs/containers from an Image. #### Runtime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` Start a VM/container and return connection info. #### Runtime.stop ```python async def stop(name: str) -> None ``` Stop and clean up a VM/container. #### Runtime.is_ready ```python async def is_ready(info: RuntimeInfo, timeout: float = 120) -> bool ``` Wait until the sandbox is ready. Daemon-agnostic: "ready" is the runtime's own notion (instance running, plus any probe the caller declared), never a specific guest agent. #### Runtime.suspend ```python async def suspend(name: str) -> None ``` Suspend (pause/save state of) a running VM. #### Runtime.resume ```python async def resume(image: 'Image', name: str, **opts) -> RuntimeInfo ``` Resume a suspended VM and return its RuntimeInfo. #### Runtime.list ```python async def list() -> list[dict] ``` List known VMs managed by this runtime. Returns a list of dicts with at minimum: name, status. #### Runtime.ensure_base ```python async def ensure_base(image: 'Image', base_name: str) -> CheckpointInfo ``` Pull/build image into a stopped base sandbox if not already present. Idempotent. First call does the full pull; subsequent calls return immediately. The base sandbox is never started: it only serves as a fork source for ephemeral VMs. #### Runtime.fork ```python async def fork(base_name: str, new_name: str, **opts) -> None ``` Create a new sandbox from a base/checkpoint using CoW where possible. #### Runtime.checkpoint ```python async def checkpoint(name: str, checkpoint_name: str, **opts) -> CheckpointInfo ``` Capture the current state of a running sandbox as a named checkpoint. #### Runtime.list_checkpoints ```python async def list_checkpoints() -> list[CheckpointInfo] ``` #### Runtime.delete_checkpoint ```python async def delete_checkpoint(checkpoint_name: str) -> None ``` ### RuntimeInfo ```python class RuntimeInfo RuntimeInfo( host: str, api_port: int, vnc_port: Optional[int] = None, api_key: Optional[str] = None, container_id: Optional[str] = None, name: Optional[str] = None, qmp_port: Optional[int] = None, environment: Optional[str] = None, agent_type: Optional[str] = None, guest_server_port: int = 3211, exposed_ports: Optional[dict] = None, ssh_port: Optional[int] = None, ssh_username: Optional[str] = None, ssh_password: Optional[str] = None, ssh_key_filename: Optional[str] = None, vnc_host: Optional[str] = None, vnc_password: Optional[str] = None, grpc_port: Optional[int] = None, native: Any = None, env_ready_timeout: Optional[float] = None, env_token: Optional[str] = None, ) ``` Connection info returned after a runtime spins up. | Attribute | Type | Description | | --- | --- | --- | | `host` | `str` | | | `api_port` | `int` | | | `vnc_port` | `Optional[int]` | | | `api_key` | `Optional[str]` | | | `container_id` | `Optional[str]` | | | `name` | `Optional[str]` | | | `qmp_port` | `Optional[int]` | | | `environment` | `Optional[str]` | | | `agent_type` | `Optional[str]` | | | `guest_server_port` | `int` | | | `exposed_ports` | `Optional[dict]` | | | `ssh_port` | `Optional[int]` | | | `ssh_username` | `Optional[str]` | | | `ssh_password` | `Optional[str]` | | | `ssh_key_filename` | `Optional[str]` | | | `vnc_host` | `Optional[str]` | | | `vnc_password` | `Optional[str]` | | | `grpc_port` | `Optional[int]` | | | `native` | `Any` | | | `env_ready_timeout` | `Optional[float]` | | | `env_token` | `Optional[str]` | | ### DockerRuntime ```python class DockerRuntime(NativeRuntime) DockerRuntime( *, api_port: int = DEFAULT_API_PORT, vnc_port: int = DEFAULT_VNC_PORT, ephemeral: bool = True, volumes: Optional[list[str]] = None, environment: Optional[dict[str, str]] = None, devices: Optional[list[str]] = None, platform: Optional[str] = None, privileged: bool = False, stop_timeout: int = 120, cpus: Optional[int] = None, memory_mb: Optional[int] = None, server_port: Optional[int] = None, ) ``` Runs OCI container images through the SDK's container backend. `api_port`/`vnc_port` are accepted for compatibility; the SDK picks free host ports and `Sandbox.exposed_ports` reports them. `volumes`, `devices`, `platform` and `privileged` are not supported by the sandboxed (gVisor) backend and are rejected. | Attribute | Type | Description | | --- | --- | --- | | `runtime_type` | `str` | | | `env_ready_timeout` | `float` | | | `delivers_guest_env` | `bool` | | | `api_port` | `int` | | | `vnc_port` | `int` | | | `stop_timeout` | `int` | | #### DockerRuntime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` ### QEMURuntime ```python def QEMURuntime(mode: str = 'docker', **kwargs) -> Runtime ``` Factory that returns the appropriate QEMU runtime. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `mode` | `str` | `'docker'` | "docker" (default), "bare-metal", or "wsl2" | ### QEMUDockerRuntime ```python class QEMUDockerRuntime(NativeQEMURuntime) QEMUDockerRuntime( *, api_port: int = DEFAULT_API_PORT, vnc_port: int = QEMU_VNC_PORT, ephemeral: bool = True, storage_dir: Optional[str | Path] = None, memory_mb: int = 8192, cpu_count: int = 4, ) ``` `QEMURuntime(mode="docker")`: kept for compatibility. The trycua/cua-qemu-* wrapper images drove their guest through computer-server, which is gone; Linux and Windows guests now boot on the SDK's QEMU backend (the SDK provisions QEMU itself). Android guests use `cua_sandbox.runtime.AndroidEmulatorRuntime` or bare-metal QEMU. | Attribute | Type | Description | | --- | --- | --- | | `api_port` | `int` | | | `vnc_port` | `int` | | #### QEMUDockerRuntime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` ### QEMUBaremetalRuntime ```python class QEMUBaremetalRuntime(Runtime) QEMUBaremetalRuntime( *, api_port: int = DEFAULT_API_PORT, vnc_display: int = 0, memory_mb: int = 4096, cpu_count: int = 2, arch: str = 'x86_64', qmp_port: int = 4444, use_qmp_transport: bool = False, extra_args: Optional[list[str]] = None, use_sdk: Optional[bool] = None, ) ``` Bare-metal QEMU: launches qemu-system-* directly on the host. Requires: - qemu-system-x86_64 (or qemu-system-aarch64) on PATH - A disk image (qcow2/vhdx/raw) with computer-server pre-installed, provided via Image.from_file('/path/to/disk.qcow2') `use_sdk=False` keeps the legacy in-process launcher, which boots the given disk in place (the image builder needs that: the SDK always puts an instance overlay on top of the disk). | Attribute | Type | Description | | --- | --- | --- | | `api_port` | `int` | | | `vnc_display` | `int` | | | `memory_mb` | `int` | | | `cpu_count` | `int` | | | `arch` | `str` | | | `qmp_port` | `int` | | | `use_qmp_transport` | `bool` | | | `extra_args` | `Optional[list[str]]` | | | `use_sdk` | `Optional[bool]` | | #### QEMUBaremetalRuntime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` #### QEMUBaremetalRuntime.stop ```python async def stop(name: str) -> None ``` #### QEMUBaremetalRuntime.is_ready ```python async def is_ready(info: RuntimeInfo, timeout: float = 120) -> bool ``` #### QEMUBaremetalRuntime.suspend ```python async def suspend(name: str) -> None ``` Save VM state via QMP savevm then quit QEMU. #### QEMUBaremetalRuntime.resume ```python async def resume(image: 'Image', name: str, **opts) -> RuntimeInfo ``` Relaunch QEMU with -loadvm to restore saved state. #### QEMUBaremetalRuntime.list ```python async def list() -> list[dict] ``` List known bare-metal QEMU sandboxes from state files, checking if alive. ### QEMUWSL2Runtime ```python class QEMUWSL2Runtime(Runtime) QEMUWSL2Runtime( *, api_port: int = DEFAULT_API_PORT, vnc_display: int = 0, memory_mb: int = 4096, cpu_count: int = 4, arch: str = 'x86_64', extra_args: Optional[list[str]] = None, ) ``` QEMU via WSL2 with KVM hardware acceleration. Runs qemu-system-x86_64 inside WSL2 where /dev/kvm is available, while keeping disk images on the Windows filesystem. Windows paths are automatically converted to /mnt/c/... paths for WSL access. This is the fastest way to run QEMU on Windows: native KVM speeds vs TCG software emulation (bare-metal Windows) or Hyper-V (needs admin). | Attribute | Type | Description | | --- | --- | --- | | `api_port` | `int` | | | `vnc_display` | `int` | | | `memory_mb` | `int` | | | `cpu_count` | `int` | | | `arch` | `str` | | | `extra_args` | `Optional[list[str]]` | | #### QEMUWSL2Runtime.available ```python @staticmethod def available() -> bool ``` Check if WSL2 + QEMU + KVM are available. #### QEMUWSL2Runtime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` #### QEMUWSL2Runtime.stop ```python async def stop(name: str) -> None ``` #### QEMUWSL2Runtime.is_ready ```python async def is_ready(info: RuntimeInfo, timeout: float = 120) -> bool ``` ### LumeRuntime ```python class LumeRuntime(NativeRuntime) LumeRuntime( *, lume_host: str = 'localhost', lume_port: int = LUME_PROVIDER_PORT, api_port: int = LUME_API_PORT, ephemeral: bool = True, cpus: Optional[int] = None, memory_mb: Optional[int] = None, ) ``` Runs macOS VMs via Lume, through the cua SDK. | Attribute | Type | Description | | --- | --- | --- | | `runtime_type` | `str` | | | `env_ready_timeout` | `float` | | | `lume_host` | `str` | | | `lume_port` | `int` | | | `api_port` | `int` | | #### LumeRuntime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` #### LumeRuntime.ensure_base ```python async def ensure_base(image: Image, base_name: str) ``` Pull an OCI image into a stopped golden base VM, if it doesn't exist yet. Idempotent: if *base_name* already exists and is stopped, returns immediately. Raises if the base VM is running, because cloning a live VM can produce dirty or inconsistent clones (same constraint as cloud snapshots). The base VM is never started; it only serves as a fork source. #### LumeRuntime.fork ```python async def fork(base_name: str, new_name: str, **opts) -> None ``` Clone a stopped VM *base_name* → *new_name* via APFS clonefile (instant). The source VM must be stopped before calling this: cloning a running VM risks dirty state or clone failures (mirrors cloud snapshot behaviour). #### LumeRuntime.checkpoint ```python async def checkpoint(name: str, checkpoint_name: str, **opts) ``` Stop *name*, clone it into a checkpoint, then restart *name*. Mirrors the cloud behaviour (stop → snapshot → restart) so clones are always taken from a clean stopped state. #### LumeRuntime.list_checkpoints ```python async def list_checkpoints() ``` Return VMs that are checkpoints (cua-base-* or cua-ckpt-* prefix). #### LumeRuntime.delete_checkpoint ```python async def delete_checkpoint(checkpoint_name: str) -> None ``` Delete a checkpoint VM. Only operates on cua-base-* / cua-ckpt-* names. #### LumeRuntime.list ```python async def list() -> list[dict] ``` List all Lume VMs. ### HyperVRuntime ```python class HyperVRuntime(Runtime) HyperVRuntime( *, api_port: int = DEFAULT_API_PORT, switch_name: str = 'Default Switch', memory_mb: int = 4096, cpu_count: int = 4, disk_size_gb: int = 64, cache_dir: Optional[Path] = None, windows_iso: Optional[str] = None, product_key: Optional[str] = None, ) ``` Runs Windows VMs via Hyper-V with layered VHDX caching. On first use, builds a base Windows image from ISO using unattended install (same Autounattend.xml as the QEMU builder). The base image includes CUA computer-server pre-installed. This is cached and reused. Each sandbox session gets its own ephemeral differencing disk on top. | Attribute | Type | Description | | --- | --- | --- | | `api_port` | `int` | | | `switch_name` | `str` | | | `memory_mb` | `int` | | | `cpu_count` | `int` | | | `disk_size_gb` | `int` | | | `cache_dir` | `Optional[Path]` | | | `windows_iso` | `Optional[str]` | | | `product_key` | `Optional[str]` | | #### HyperVRuntime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` #### HyperVRuntime.stop ```python async def stop(name: str) -> None ``` Stop the VM, remove it, and delete the ephemeral session disk. #### HyperVRuntime.is_ready ```python async def is_ready(info: RuntimeInfo, timeout: float = 120) -> bool ``` Daemon-agnostic readiness: the VM is running and has an address. `start()` already waited for the guest IP; the guest agent port is a real TCP endpoint on the Hyper-V switch, so wait until it accepts a connection (any daemon listening there), bounded by *timeout*. ### TartRuntime ```python class TartRuntime(Runtime) TartRuntime( *, ephemeral: bool = True, cpu_count: Optional[int] = None, memory_mb: Optional[int] = None, disk_gb: Optional[int] = None, display: str = '1024x768', ssh_username: str = TART_DEFAULT_USERNAME, ssh_password: str = TART_DEFAULT_PASSWORD, ) ``` Apple Virtualization.framework runtime via Tart CLI. Uses VNC (host-side, random port) for screenshots and SSH for shell commands. | Attribute | Type | Description | | --- | --- | --- | | `ephemeral` | `bool` | | | `cpu_count` | `Optional[int]` | | | `memory_mb` | `Optional[int]` | | | `disk_gb` | `Optional[int]` | | | `display` | `str` | | | `ssh_username` | `str` | | | `ssh_password` | `str` | | | `vnc_host` | `Optional[str]` | | | `vnc_port` | `Optional[int]` | | | `vnc_password` | `Optional[str]` | | #### TartRuntime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` #### TartRuntime.is_ready ```python async def is_ready(info: RuntimeInfo, timeout: float = 120) -> bool ``` Wait until SSH is reachable. #### TartRuntime.stop ```python async def stop(name: str) -> None ``` ### AndroidEmulatorRuntime ```python class AndroidEmulatorRuntime(Runtime) AndroidEmulatorRuntime( *, api_level: int = 34, img_type: str = 'google_apis', device_id: str = 'pixel', memory_mb: int = 4096, cpu_count: int = 4, adb_port: Optional[int] = None, no_boot_anim: bool = True, gpu_mode: str = 'swiftshader_indirect', headless: bool = True, grpc_port: Optional[int] = None, ) ``` Android emulator runtime using the official Android SDK. Creates an AVD and launches the Android emulator, following the docker-android pattern. Uses ADB for boot detection, APK install, and shell commands. | Attribute | Type | Description | | --- | --- | --- | | `api_level` | `int` | | | `img_type` | `str` | | | `device_id` | `str` | | | `memory_mb` | `int` | | | `cpu_count` | `int` | | | `adb_port` | `Optional[int]` | | | `no_boot_anim` | `bool` | | | `gpu_mode` | `str` | | | `headless` | `bool` | | | `grpc_port` | `Optional[int]` | | #### AndroidEmulatorRuntime.start ```python async def start(image: Image, name: str, **opts) -> RuntimeInfo ``` #### AndroidEmulatorRuntime.is_ready ```python async def is_ready(info: RuntimeInfo, timeout: float = 300) -> bool ``` Wait for `sys.boot_completed == 1` via ADB, like docker-android. #### AndroidEmulatorRuntime.list ```python async def list() -> list[dict] ``` List known Android emulator sandboxes from state files, checking if alive. #### AndroidEmulatorRuntime.stop ```python async def stop(name: str) -> None ``` #### AndroidEmulatorRuntime.take_screenshot ```python async def take_screenshot() -> bytes ``` Take a screenshot via ADB screencap. --- # Configuration and errors configure, login, whoami, transports and the errors cua-sandbox raises. Process-wide settings, sign-in helpers, transports and the typed errors of `cua-sandbox`. ## Configuration and sign-in ### configure ```python def configure( *, api_key: Optional[str] = None, base_url: Optional[str] = None, fleet_base_url: Optional[str] = None, token_url: Optional[str] = None, client_id: Optional[str] = None, client_secret: Optional[str] = None, fleet_token: Optional[str] = None, ) -> None ``` Set global configuration for cloud sandboxes. API-key cloud operations use `base_url`. Fleet uses `fleet_base_url` and OAuth client credentials. ### fleet_auth_source ```python def fleet_auth_source(*, read_session: bool = True) -> Optional[str] ``` Where Fleet credentials come from, in the order the SDK uses them. `"FLEETS_TOKEN"` (a workload token), `"client credentials"` (`CUA_CLIENT_ID`/`CUA_CLIENT_SECRET`), `"cua auth login session"` (the stored session, refreshed by the SDK while it is used) or `None` when nothing is configured. A stored session is only looked up when neither of the first two is set. `read_session=False` decides that from the non-secret session marker alone, without reading the OS credential vault (for dashboards and other implicit checks). ### login ```python def login(*, base_url: Optional[str] = None) -> None ``` Open the CUA login page in a browser and store credentials. This initiates a Clerk-based browser authentication flow. The user completes login in their browser, and the resulting API key is stored in ~/.cua/credentials. ### whoami ```python def whoami(*, api_key: Optional[str] = None) -> Dict[str, Any] ``` Return info about the authenticated user. **Returns:** Dict with user info (id, email, etc.) from the CUA API. ## Errors ### SpacesdNotAvailable ```python class SpacesdNotAvailable(RuntimeError) ``` The sandbox has no reachable cua-spacesd. Sandboxes are daemon-agnostic: creating, listing and deleting them never needs a guest agent. The computer interfaces (`screen`, `mouse`, `shell`, `files` ...) need cua-spacesd, or one of the agentless transports (QMP, VNC, SSH, ADB) the runtime exposes. ### InvalidArgument ```python class InvalidArgument(ValueError) ``` Contradictory or invalid options (the native SDK's `CuaError.InvalidArgument`), for example `local=True` together with `cloud=CloudOptions(...)`. ### InvalidPlacement ```python class InvalidPlacement(InvalidArgument) ``` A location, kind and runtime that do not go together (the native SDK's `CuaError.InvalidPlacement`), for example `kind="container"` with `runtime="qemu"`, or `runtime="kubevirt"` locally. The message lists the valid values. ### Unsupported ```python class Unsupported(NotImplementedError) ``` The operation is not supported for this sandbox, image or provider (the native SDK's `CuaError.Unsupported`). A `NotImplementedError`, so older `except NotImplementedError` handlers still catch it. ## Transports ### CloudTransport ```python class CloudTransport(Transport) ``` Compatibility shim for the removed API-key cloud transport. Constructing it still works (and keeps the requested image), so code that builds one fails at `connect()` with a message pointing to Fleet. | Attribute | Type | Description | | --- | --- | --- | | `name` | `Optional[str]` | | #### CloudTransport.connect ```python async def connect() -> None ``` #### CloudTransport.disconnect ```python async def disconnect() -> None ``` #### CloudTransport.delete_vm ```python async def delete_vm() -> None ``` Delete an existing API-key VM through the platform API. #### CloudTransport.create_snapshot ```python async def create_snapshot(name: str | None = None, stateful: bool = False) -> dict ``` #### CloudTransport.send ```python async def send(action: str, **params: Any) -> Any ``` #### CloudTransport.screenshot ```python async def screenshot(format: str = 'png', quality: int = 95) -> bytes ``` #### CloudTransport.get_screen_size ```python async def get_screen_size() -> Dict[str, int] ``` #### CloudTransport.get_environment ```python async def get_environment() -> str ``` ### EnvTransport ```python class EnvTransport(Transport) ``` Drives a sandbox through cua-spacesd. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `url` | `Optional[str]` | `None` | spacesd URL (`http://host:3211`, a relay or Fleet service URL). Ignored when `env_factory` is given. | | `token` | `Optional[str]` | `None` | spacesd token, if the driver requires one. | | `env_factory` | `Optional[EnvFactory]` | `None` | coroutine returning a `cua.SpacesdClient` (used by Fleet and local runtimes, which know how to reach the driver). | | `environment` | `Optional[str]` | `None` | OS hint (`linux`, `windows`, `mac`) used before the driver has been reached. | | `fallback` | `Optional[Transport]` | `None` | an agentless transport (QMP/VNC/SSH/ADB) to use when the sandbox has no spacesd. | | `ready_timeout` | `float` | `0.0` | how long to keep retrying the spacesd probe on first use (a freshly booted VM starts its driver late). `0` probes once. | | `native_sandbox` | `Any` | `None` | the `cua.Sandbox` handle, for port forwarding and named services. | | Attribute | Type | Description | | --- | --- | --- | | `url` | `Optional[str]` | | #### EnvTransport.connect ```python async def connect() -> None ``` #### EnvTransport.disconnect ```python async def disconnect() -> None ``` #### EnvTransport.spacesd ```python async def spacesd() -> Any ``` The raw `cua.SpacesdClient` (typed client escape hatch). #### EnvTransport.send ```python async def send(action: str, **params: Any) -> Any ``` #### EnvTransport.screenshot ```python async def screenshot(format: str = 'png', quality: int = 95) -> bytes ``` #### EnvTransport.get_screen_size ```python async def get_screen_size() -> Dict[str, int] ``` #### EnvTransport.get_environment ```python async def get_environment() -> str ``` #### EnvTransport.get_display_url ```python async def get_display_url(*, share: bool = False) -> str ``` A browser URL for the sandbox's display. With cua-spacesd (an `env` or `viewer` service), a link to its HTML5 viewer carrying a scoped, expiring viewer ticket; the same link serves `share=True`. For images without cua-spacesd, the page of the first declared legacy web display service (`novnc`, `display`, `web`; a raw RFB port 59xx is skipped, and `share=True` returns its expiring public link), then the agentless fallback's VNC address. #### EnvTransport.request_service ```python async def request_service( name: str, *, method: str, path: str, json_body: Any = None, headers: Any = None, ) -> Any ``` #### EnvTransport.native_handle ```python async def native_handle() -> Any ``` The `cua.Sandbox` handle behind this transport (services, forwards, public URLs). #### EnvTransport.native_service ```python async def native_service(name: str) -> Any ``` #### EnvTransport.forward_tunnel ```python async def forward_tunnel(sandbox_port: int | str) -> 'TunnelInfo' ``` #### EnvTransport.close_tunnel ```python async def close_tunnel(info: 'TunnelInfo') -> None ``` #### EnvTransport.pty_create ```python async def pty_create( command: Optional[str] = None, cols: int = 120, rows: int = 40, cwd: Optional[str] = None, envs: Optional[Dict[str, str]] = None, ) -> Dict[str, Any] ``` #### EnvTransport.pty_send ```python async def pty_send(pid: int, data: str) -> None ``` #### EnvTransport.pty_kill ```python async def pty_kill(pid: int) -> bool ``` #### EnvTransport.pty_info ```python async def pty_info(pid: int) -> Optional[Dict[str, Any]] ``` --- # TypeScript additions The hand-written helpers of @trycua/cua: embedded, connect, Image, probes, sidecars, specs and MCP. Import from `@trycua/cua`. This page covers the hand-written TypeScript layer; the generated native binding (sandboxes, guest, fleet, spaces objects) is documented per object in the [Cua SDK reference](). `npm install @trycua/cua` (0.4.1) installs the Node package; native code ships as per-platform optional packages (`@trycua/cua-darwin-arm64`, `@trycua/cua-linux-x64-gnu`, ...). | Entry point | Use it for | | --- | --- | | `@trycua/cua` | `embedded()`, `connect()`, every generated object, and the helpers on this page | | `@trycua/cua/spaces`, `/spaces/transport`, `/spaces/host` | Spaces threads, approvals and transports | | `@trycua/cua/browser` | A WebAssembly subset: cua-spacesd over gRPC-Web and a Fleet client with a static bearer. No sandboxes, local runtimes, Spaces, daemon, file transfer or media. Give it a short-lived token from your backend, never client secrets | cua does not implement MCP: `connectMcp(await sb.mcpConfig(service, undefined))` hands the endpoint to the official SDK (`npm install @modelcontextprotocol/client`). Cloud bearers are short-lived, so fetch a config per connection. ## Interfaces ### ImageOptions Options for `Image.linux()` / `windows()` / `macos()`. #### Properties | Property | Type | | ------ | ------ | | `tier?` | [`ImageTier`](#imagetier) | | `version?` | `string` | *** ### McpEndpointConfig Where an MCP endpoint is: `Sandbox.mcpConfig()` returns one. #### Properties | Property | Type | | ------ | ------ | | `headers` | `object`[] \| `Record`\<`string`, `string`\> | | `url` | `string` | ## Type Aliases ### ImageTier ```ts type ImageTier = "slim" | "full" | "xcode" | `xcode-${string}`; ``` An image tier: `slim`, `full` (the default), or on macOS `xcode` / `xcode-`. ## Variables ### Image ```ts const Image: object; ``` Canonical images: `Image.linux()` is `ghcr.io/trycua/linux:24.04` (`CUA_IMAGE_LINUX` overrides it), `Image.windows()` `ghcr.io/trycua/windows:2022`, `Image.macos()` `ghcr.io/trycua/macos:26`. These are the full tier (dev tooling); `{ tier: "slim" }` is the minimal image CI runs and, on macOS, `{ tier: "xcode" }` adds a pinned Xcode. `Image.omarchy()` is `ghcr.io/trycua/omarchy:edge`, an amd64 VM. Images CI has not published yet throw `ImageNotPublished`; pass the reference to `fromRegistry` to use one anyway. `Image.resolve(ref, backend)` is the one resolver: the digest-pinned variant a backend runs (rootfs, the `-disk` containerDisk, Lume). #### Type Declaration | Name | Type | Description | | ------ | ------ | ------ | | `fromRegistry()` | (`reference`) => `string` | - | | `linux()` | (`version?`, `tier?`) => `string` | - | | `macos()` | (`version?`, `tier?`) => `string` | - | | `omarchy()` | (`channel?`) => `string` | Omarchy (Arch Linux, Hyprland) with cua-spacesd: an amd64 VM. | | `resolve()` | (`reference`, `backend`, `arch?`) => `ResolvedImage` | - | | `windows()` | (`version?`, `tier?`) => `string` | - | *** ### Pool ```ts const Pool: object; ``` `Pool.apply(fleet, name, spec, options)`: the one pool writer, the same call as `fleet.apply`. Named pools are compared against a spec with `fleet.checkPoolSpec` (raises `CuaError.PoolSpecMismatch` with a diff) and updated with `fleet.applyPoolTemplate`; `fleet.exportPool(name).terraform` prints the equivalent `fleets_pool` block. #### Type Declaration | Name | Type | | ------ | ------ | | `apply()` | (`fleet`, `name`, `spec`, `options`) => `Promise`\<`FleetPool`\> | *** ### registrySecret ```ts const registrySecret: object; ``` Credentials for a private registry image (`registrySecret` on `SandboxCreateOptions`). Locally they authenticate the pull; in the cloud the SDK stores them as the sandbox's registry pull secret. Never logged. #### Type Declaration | Name | Type | Description | | ------ | ------ | ------ | | `awsEcr()` | (`region?`) => `RegistrySecret` | A private Amazon ECR image: a login token from the AWS CLI. | | `basic()` | (`username`, `password`, `registry?`) => `RegistrySecret` | A user name and password (or token). | | `fromEnv()` | (`usernameVar`, `passwordVar`, `registry?`) => `RegistrySecret` | Read from environment variables at create time. | ## Functions ### ambiguousCandidates() ```ts function ambiguousCandidates(error): string[]; ``` The qualified refs (`local:box`, `cloud:box`) a `CuaError.AmbiguousSandbox` lists: a bare sandbox name that matches sandboxes in more than one location. Empty for any other error. #### Parameters | Parameter | Type | | ------ | ------ | | `error` | `unknown` | #### Returns `string`[] *** ### connect() ```ts function connect(address?, token?): CuaLike; ``` A client of a running `cua daemon` (socket path or loopback URL). #### Parameters | Parameter | Type | | ------ | ------ | | `address?` | `string` | | `token?` | `string` | #### Returns `CuaLike` *** ### connectMcp() ```ts function connectMcp(config, clientInfo?): Promise; ``` Connects the official SDK's `Client` to `config` over streamable HTTP. Fleet bearers are short-lived: fetch a fresh config per connection. #### Parameters | Parameter | Type | | ------ | ------ | | `config` | [`McpEndpointConfig`](#mcpendpointconfig) | | `clientInfo` | \{ `name`: `string`; `version`: `string`; \} | | `clientInfo.name` | `string` | | `clientInfo.version` | `string` | #### Returns `Promise`\<`any`\> *** ### cuaErrorDocUrl() ```ts function cuaErrorDocUrl(error): string | undefined; ``` The link to a `CuaError`'s entry (cause and fix) on the errors reference; `undefined` for any other value. Every `CuaError` also carries it as `docUrl`. #### Parameters | Parameter | Type | | ------ | ------ | | `error` | `unknown` | #### Returns `string` \| `undefined` *** ### embedded() ```ts function embedded(config?): CuaLike; ``` An SDK runtime in this process (no I/O until the first call). Fleet uses `CUA_CLIENT_ID`/`CUA_CLIENT_SECRET` (or `FLEETS_TOKEN`); pass `fleetFromSession: true` to fall back to the `cua auth login` session. #### Parameters | Parameter | Type | | ------ | ------ | | `config` | `Partial`\<`CuaConfig`\> | #### Returns `CuaLike` *** ### http() ```ts function http(service, path?): ReadinessProbe; ``` Ready once `GET path` on the declared `service` returns 2xx. #### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `service` | `string` | `undefined` | | `path` | `string` | `"/"` | #### Returns `ReadinessProbe` *** ### mcpHeaders() ```ts function mcpHeaders(config): Record; ``` The headers as a plain object. #### Parameters | Parameter | Type | | ------ | ------ | | `config` | [`McpEndpointConfig`](#mcpendpointconfig) | #### Returns `Record`\<`string`, `string`\> *** ### poolOptions() ```ts function poolOptions(opts?): PoolOptions; ``` How a pool keeps capacity for a `SandboxSpec` (warm floor, size, TTLs, runtime). #### Parameters | Parameter | Type | | ------ | ------ | | `opts` | `Partial`\<`PoolOptions`\> | #### Returns `PoolOptions` *** ### sandboxSpec() ```ts function sandboxSpec(image, opts?): SandboxSpec; ``` What a sandbox runs: the one model behind `Sandbox.create`, managed pools and `fleet.apply(name, spec, options)`. `env` and `services` take plain objects too. Unset fields keep Fleet's defaults (and are not compared by `fleet.checkPoolSpec`). ```ts const spec = sandboxSpec("python:3.12-slim", { command: ["python", "-m", "srv"], services: { mcp: 8765 }, readiness: http("mcp", "/health") }) await c.fleet().apply("my-pool", spec, poolOptions({ warm: true, idleTtlSeconds: 3600 })) ``` #### Parameters | Parameter | Type | | ------ | ------ | | `image` | `string` | | `opts` | `Partial`\<`Omit`\<`SandboxSpec`, `"image"` \| `"env"` \| `"services"`\>\> & `object` | #### Returns `SandboxSpec` *** ### sidecar() ```ts function sidecar(image, opts?): Container; ``` A sidecar container, addressed by name on every runtime: the sandbox reaches it at its `name` (and on `localhost` where they share a network namespace), it reaches the sandbox at `main`, and `services` may name its ports. Local containers (with `runtime: "runc"`) and every cloud sandbox; local VM sandboxes refuse sidecars. With sidecars the service names `main`, `sidecars` and `sc` are reserved. ```ts SandboxCreateOptions.create({ image: "python:3.12-slim", sidecars: [sidecar("redis:7-alpine", { ports: [6379] })], services: new Map([["db", 6379]]) }) ``` #### Parameters | Parameter | Type | | ------ | ------ | | `image` | `string` | | `opts` | \{ `command?`: `string`[]; `env?`: `Record`\<`string`, `string`\>; `name?`: `string`; `ports?`: `number`[]; \} | | `opts.command?` | `string`[] | | `opts.env?` | `Record`\<`string`, `string`\> | | `opts.name?` | `string` | | `opts.ports?` | `number`[] | #### Returns `Container` *** ### tcp() ```ts function tcp(service): ReadinessProbe; ``` Ready once a TCP connect to the declared `service` succeeds. #### Parameters | Parameter | Type | | ------ | ------ | | `service` | `string` | #### Returns `ReadinessProbe` --- # Spaces (TypeScript) The @trycua/cua/spaces helpers: threads, events, teleport approvals and typed errors. Import from `@trycua/cua/spaces`. This page covers the hand-written TypeScript layer; the generated native binding (sandboxes, guest, fleet, spaces objects) is documented per object in the [Cua SDK reference](). ## Classes ### CursorSmoother One remote cursor's interpolation buffer. #### Constructors ##### Constructor ```ts new CursorSmoother(delayMs?): CursorSmoother; ``` ###### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `delayMs` | `number` | `STREAM_DELAY_MS` | ###### Returns [`CursorSmoother`](#cursorsmoother) #### Properties | Property | Modifier | Type | Default value | | ------ | ------ | ------ | ------ | | `delayMs` | `readonly` | `number` | `STREAM_DELAY_MS` | #### Methods ##### alpha() ```ts alpha(localMs): number; ``` Opacity at `localMs`. ###### Parameters | Parameter | Type | | ------ | ------ | | `localMs` | `number` | ###### Returns `number` ##### position() ```ts position(localMs): [number, number] | undefined; ``` The position to draw at `localMs`, or undefined before any sample. ###### Parameters | Parameter | Type | | ------ | ------ | | `localMs` | `number` | ###### Returns \[`number`, `number`\] \| `undefined` ##### push() ```ts push( serverMs, localMs, x, y, target ): void; ``` Adds a sample (`serverMs` 0 = unknown: the local time is used). Out-of-order samples are dropped. ###### Parameters | Parameter | Type | | ------ | ------ | | `serverMs` | `number` | | `localMs` | `number` | | `x` | `number` | | `y` | `number` | | `target` | `string` | ###### Returns `void` ##### reset() ```ts reset(): void; ``` Forgets every sample (the next one snaps). ###### Returns `void` *** ### GroupChat A group chat. The 2..6 bound holds for every instance: construction and membership changes throw. #### Constructors ##### Constructor ```ts new GroupChat( title, members, options? ): GroupChat; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `title` | `string` | | `members` | readonly `string`[] | | `options` | \{ `createdAt?`: `Date`; `id?`: `string`; \} | | `options.createdAt?` | `Date` | | `options.id?` | `string` | ###### Returns [`GroupChat`](#groupchat) #### Properties | Property | Modifier | Type | Default value | | ------ | ------ | ------ | ------ | | `createdAt` | `readonly` | `string` | `undefined` | | `id` | `readonly` | `string` | `undefined` | | `messages` | `public` | [`GroupMessage`](#groupmessage)[] | `[]` | | `title` | `public` | `string` | `undefined` | #### Accessors ##### isAtFloor ###### Get Signature ```ts get isAtFloor(): boolean; ``` ###### Returns `boolean` ##### isFull ###### Get Signature ```ts get isFull(): boolean; ``` ###### Returns `boolean` ##### memberIDs ###### Get Signature ```ts get memberIDs(): readonly string[]; ``` Bot ids in join order. The human is implicit. ###### Returns readonly `string`[] ##### membershipLabel ###### Get Signature ```ts get membershipLabel(): string; ``` `4 of 6 bots`. ###### Returns `string` ##### remainingSeats ###### Get Signature ```ts get remainingSeats(): number; ``` ###### Returns `number` #### Methods ##### add() ```ts add(botID): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | ###### Returns `void` ##### remove() ```ts remove(botID): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | ###### Returns `void` *** ### GroupChatError A membership rule was broken. The message says which number. #### Extends - `Error` #### Constructors ##### Constructor ```ts new GroupChatError(code, message): GroupChatError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `code` | [`GroupChatErrorCode`](#groupchaterrorcode) | | `message` | `string` | ###### Returns [`GroupChatError`](#groupchaterror) ###### Overrides ```ts Error.constructor ``` #### Properties | Property | Modifier | Type | | ------ | ------ | ------ | | `code` | `readonly` | [`GroupChatErrorCode`](#groupchaterrorcode) | #### Methods ##### alreadyAMember() ```ts static alreadyAMember(id): GroupChatError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns [`GroupChatError`](#groupchaterror) ##### atFloor() ```ts static atFloor(): GroupChatError; ``` ###### Returns [`GroupChatError`](#groupchaterror) ##### full() ```ts static full(): GroupChatError; ``` ###### Returns [`GroupChatError`](#groupchaterror) ##### notAMember() ```ts static notAMember(id): GroupChatError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns [`GroupChatError`](#groupchaterror) ##### tooFewBots() ```ts static tooFewBots(have): GroupChatError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `have` | `number` | ###### Returns [`GroupChatError`](#groupchaterror) ##### tooManyBots() ```ts static tooManyBots(have): GroupChatError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `have` | `number` | ###### Returns [`GroupChatError`](#groupchaterror) ##### unknownChat() ```ts static unknownChat(id): GroupChatError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns [`GroupChatError`](#groupchaterror) *** ### GroupChatStore Group chats: membership, fan-out, and the merged transcript. #### Constructors ##### Constructor ```ts new GroupChatStore(messenger?): GroupChatStore; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `messenger?` | [`GroupMessenger`](#groupmessenger) | ###### Returns [`GroupChatStore`](#groupchatstore) #### Properties | Property | Modifier | Type | Default value | Description | | ------ | ------ | ------ | ------ | ------ | | `chats` | `public` | [`GroupChat`](#groupchat)[] | `[]` | - | | `lastError` | `public` | `string` \| `undefined` | `undefined` | The last membership complaint, cleared on the next success. | | `working` | `readonly` | `Map`\<`string`, `Set`\<`string`\>\> | `undefined` | Bots producing output right now, per chat. | #### Methods ##### add() ```ts add(botID, chatID): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | | `chatID` | `string` | ###### Returns `void` ##### attach() ```ts attach(messenger): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `messenger` | [`GroupMessenger`](#groupmessenger) | ###### Returns `void` ##### chat() ```ts chat(id): GroupChat | undefined; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns [`GroupChat`](#groupchat) \| `undefined` ##### collectReplies() ```ts collectReplies(chatID): Promise; ``` Folds what members said since the last poll into the transcript, attributed. ###### Parameters | Parameter | Type | | ------ | ------ | | `chatID` | `string` | ###### Returns `Promise`\<[`GroupMessage`](#groupmessage)[]\> ##### create() ```ts create(title, members): GroupChat; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `title` | `string` | | `members` | readonly `string`[] | ###### Returns [`GroupChat`](#groupchat) ##### delete() ```ts delete(id): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns `void` ##### displayName() ```ts displayName(botID): string; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | ###### Returns `string` ##### react() ```ts react(emoji, chatID): void; ``` Attaches a reaction to the last line. ###### Parameters | Parameter | Type | | ------ | ------ | | `emoji` | `string` | | `chatID` | `string` | ###### Returns `void` ##### refreshWorking() ```ts refreshWorking(chatID): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `chatID` | `string` | ###### Returns `void` ##### remove() ```ts remove(botID, chatID): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | | `chatID` | `string` | ###### Returns `void` ##### send() ```ts send(text, chatID): Promise; ``` Sends one message to every member; returns each member's delivery. ###### Parameters | Parameter | Type | | ------ | ------ | | `text` | `string` | | `chatID` | `string` | ###### Returns `Promise`\<[`GroupDelivery`](#groupdelivery)[]\> ##### subscribe() ```ts subscribe(fn): () => void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `fn` | () => `void` | ###### Returns () => `void` ##### workingBots() ```ts workingBots(chatID): string[]; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `chatID` | `string` | ###### Returns `string`[] *** ### PresenceRoster Who is in a Space's presence and where their cursors are. Pure state. #### Constructors ##### Constructor ```ts new PresenceRoster(): PresenceRoster; ``` ###### Returns [`PresenceRoster`](#presenceroster) #### Properties | Property | Type | | ------ | ------ | | `meId` | `string` \| `undefined` | #### Accessors ##### entries ###### Get Signature ```ts get entries(): PresenceEntry[]; ``` Everyone, me first, then in join order. ###### Returns [`PresenceEntry`](#presenceentry)[] ##### others ###### Get Signature ```ts get others(): PresenceEntry[]; ``` Everyone but me: the cursors to draw (mine is the real pointer). ###### Returns [`PresenceEntry`](#presenceentry)[] #### Methods ##### apply() ```ts apply(e): boolean; ``` Folds one event in. Returns whether anything changed. ###### Parameters | Parameter | Type | | ------ | ------ | | `e` | `PresenceEvent` | ###### Returns `boolean` ##### colorOf() ```ts colorOf(principalId): string; ``` The color an agent shows as, for its avatar and its cursor alike: the color the server assigned once it is present (a requested color is kept unless another participant already holds it), else its stable [presenceColor](#presencecolor). Keyed by the id it joined with. ###### Parameters | Parameter | Type | | ------ | ------ | | `principalId` | `string` | ###### Returns `string` ##### from() ```ts static from(me, members?): PresenceRoster; ``` A roster seeded from a fresh session: `me` and the members at join. ###### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `me` | `PresenceParticipant` | `undefined` | | `members` | readonly `PresenceMember`[] | `[]` | ###### Returns [`PresenceRoster`](#presenceroster) ##### get() ```ts get(participantId): PresenceEntry | undefined; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `participantId` | `string` | ###### Returns [`PresenceEntry`](#presenceentry) \| `undefined` *** ### PresenceView Who is present and what to draw for each, folded from presence events. #### Constructors ##### Constructor ```ts new PresenceView(me, delayMs?): PresenceView; ``` A view for `me` (the caller's participant id). ###### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `me` | `string` | `undefined` | | `delayMs` | `number` | `STREAM_DELAY_MS` | ###### Returns [`PresenceView`](#presenceview) #### Properties | Property | Modifier | Type | Default value | | ------ | ------ | ------ | ------ | | `delayMs` | `readonly` | `number` | `STREAM_DELAY_MS` | | `me` | `readonly` | `string` | `undefined` | #### Methods ##### apply() ```ts apply(e, localMs): boolean; ``` Folds one event in at `localMs`. Returns whether anything changed. ###### Parameters | Parameter | Type | | ------ | ------ | | `e` | `PresenceEvent` | | `localMs` | `number` | ###### Returns `boolean` ##### drawables() ```ts drawables(localMs, pointer?): PresenceDrawable[]; ``` Everything to draw at `localMs`: remote cursors interpolated and faded, and your own cursor at `pointer` (normalized; undefined when the pointer is not over the surface) with its server shape. Also applies [expire](#expire). ###### Parameters | Parameter | Type | | ------ | ------ | | `localMs` | `number` | | `pointer?` | \{ `x`: `number`; `y`: `number`; \} | | `pointer.x?` | `number` | | `pointer.y?` | `number` | ###### Returns [`PresenceDrawable`](#presencedrawable)[] ##### expire() ```ts expire(localMs): string[]; ``` Drops everyone but the caller when heartbeats stopped for 3 intervals. Returns the removed ids. ###### Parameters | Parameter | Type | | ------ | ------ | | `localMs` | `number` | ###### Returns `string`[] ##### from() ```ts static from( me, members?, delayMs?, localMs? ): PresenceView; ``` A view seeded from a fresh session: `me` and the members at join. ###### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `me` | `PresenceParticipant` | `undefined` | | `members` | readonly `PresenceMember`[] | `[]` | | `delayMs` | `number` | `STREAM_DELAY_MS` | | `localMs` | `number` | `...` | ###### Returns [`PresenceView`](#presenceview) ##### participant() ```ts participant(id): PresenceParticipant | undefined; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns `PresenceParticipant` \| `undefined` ##### participantIds() ```ts participantIds(): string[]; ``` Participant ids, the caller first, then in join order. ###### Returns `string`[] ##### setHeartbeatIntervalMs() ```ts setHeartbeatIntervalMs(ms): void; ``` The expected heartbeat interval (default 5 s). ###### Parameters | Parameter | Type | | ------ | ------ | | `ms` | `number` | ###### Returns `void` ##### shapeOf() ```ts shapeOf(id): CursorShapeName | undefined; ``` A participant's current shape (the caller's included). ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns [`CursorShapeName`](#cursorshapename) \| `undefined` ##### upsert() ```ts upsert(participant): void; ``` Adds or updates a participant. ###### Parameters | Parameter | Type | | ------ | ------ | | `participant` | `PresenceParticipant` | ###### Returns `void` *** ### RoutineStore Routines: storage, editing, and the clock that fires them. #### Constructors ##### Constructor ```ts new RoutineStore(storage, runner?): RoutineStore; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `storage` | [`RoutineStorage`](#routinestorage) | | `runner?` | [`RoutineRunner`](#routinerunner) | ###### Returns [`RoutineStore`](#routinestore) #### Properties | Property | Modifier | Type | Default value | Description | | ------ | ------ | ------ | ------ | ------ | | `log` | `public` | [`FiringRecord`](#firingrecord)[] | `[]` | What the scheduler did, newest first (at most 50). | | `routines` | `public` | [`Routine`](#routine-1)[] | `[]` | - | | `storage` | `readonly` | [`RoutineStorage`](#routinestorage) | `undefined` | - | #### Accessors ##### isSchedulerRunning ###### Get Signature ```ts get isSchedulerRunning(): boolean; ``` ###### Returns `boolean` #### Methods ##### attach() ```ts attach(runner): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `runner` | [`RoutineRunner`](#routinerunner) | ###### Returns `void` ##### create() ```ts create(input): Routine; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `input` | \{ `botID`: `string`; `enabled?`: `boolean`; `now?`: `Date`; `prompt`: `string`; `schedule`: [`RoutineSchedule`](#routineschedule); `title`: `string`; \} | | `input.botID` | `string` | | `input.enabled?` | `boolean` | | `input.now?` | `Date` | | `input.prompt` | `string` | | `input.schedule` | [`RoutineSchedule`](#routineschedule) | | `input.title` | `string` | ###### Returns [`Routine`](#routine-1) ##### delete() ```ts delete(id): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns `void` ##### due() ```ts due(now): Routine[]; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `now` | `Date` | ###### Returns [`Routine`](#routine-1)[] ##### fire() ```ts fire(routine, now?): Promise; ``` Fires one routine now, whatever the clock says ("Run now", and the scheduler's step). ###### Parameters | Parameter | Type | | ------ | ------ | | `routine` | [`Routine`](#routine-1) | | `now` | `Date` | ###### Returns `Promise`\<[`FiringRecord`](#firingrecord)\> ##### load() ```ts load(): void; ``` Reads the list back. A corrupt file starts empty, with a logged complaint. ###### Returns `void` ##### routine() ```ts routine(id): Routine | undefined; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | ###### Returns [`Routine`](#routine-1) \| `undefined` ##### routinesFor() ```ts routinesFor(botID): Routine[]; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | ###### Returns [`Routine`](#routine-1)[] ##### save() ```ts save(): boolean; ``` ###### Returns `boolean` ##### setEnabled() ```ts setEnabled(id, enabled): void; ``` Enable or disable without deleting; the firing history is kept. ###### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | | `enabled` | `boolean` | ###### Returns `void` ##### startScheduler() ```ts startScheduler(intervalMs?, clock?): void; ``` One loop for every routine. Ticks never overlap. ###### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `intervalMs` | `number` | `15_000` | | `clock` | () => `Date` | `...` | ###### Returns `void` ##### stopScheduler() ```ts stopScheduler(): void; ``` ###### Returns `void` ##### subscribe() ```ts subscribe(fn): () => void; ``` Called after every change. Returns an unsubscribe. ###### Parameters | Parameter | Type | | ------ | ------ | | `fn` | () => `void` | ###### Returns () => `void` ##### tick() ```ts tick(now?): Promise; ``` Evaluates the clock once and fires whatever is due; returns what it fired. ###### Parameters | Parameter | Type | | ------ | ------ | | `now` | `Date` | ###### Returns `Promise`\<[`FiringRecord`](#firingrecord)[]\> ##### update() ```ts update(routine): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `routine` | [`Routine`](#routine-1) | ###### Returns `void` *** ### SpacesError #### Extends - `Error` #### Constructors ##### Constructor ```ts new SpacesError( code, message, options? ): SpacesError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `code` | [`SpacesErrorCode`](#spaceserrorcode) | | `message` | `string` | | `options` | [`SpacesErrorOptions`](#spaceserroroptions) | ###### Returns [`SpacesError`](#spaceserror) ###### Overrides ```ts Error.constructor ``` #### Properties | Property | Modifier | Type | | ------ | ------ | ------ | | `code` | `readonly` | [`SpacesErrorCode`](#spaceserrorcode) | | `detail` | `readonly` | `string` \| `undefined` | | `status` | `readonly` | `number` \| `undefined` | | `target` | `readonly` | `string` \| `undefined` | #### Methods ##### auth() ```ts static auth(message, options?): SpacesError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `message` | `string` | | `options?` | [`SpacesErrorOptions`](#spaceserroroptions) | ###### Returns [`SpacesError`](#spaceserror) ##### protocol() ```ts static protocol(message, options?): SpacesError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `message` | `string` | | `options?` | [`SpacesErrorOptions`](#spaceserroroptions) | ###### Returns [`SpacesError`](#spaceserror) ##### timeout() ```ts static timeout(message, options?): SpacesError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `message` | `string` | | `options?` | [`SpacesErrorOptions`](#spaceserroroptions) | ###### Returns [`SpacesError`](#spaceserror) ##### usage() ```ts static usage(message, options?): SpacesError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `message` | `string` | | `options?` | [`SpacesErrorOptions`](#spaceserroroptions) | ###### Returns [`SpacesError`](#spaceserror) *** ### SpaceTurnLock Serializes turns per Space, shared by every thread on that Space, so two bots on one Space take the Space in turn instead of fighting over it. #### Constructors ##### Constructor ```ts new SpaceTurnLock(): SpaceTurnLock; ``` ###### Returns [`SpaceTurnLock`](#spaceturnlock) #### Accessors ##### active ###### Get Signature ```ts get active(): string | null; ``` The turn currently holding the Space, or null. ###### Returns `string` \| `null` #### Methods ##### enqueue() ```ts enqueue(turnId): object; ``` Queues `turnId` now (call order, not scheduling order). ###### Parameters | Parameter | Type | | ------ | ------ | | `turnId` | `string` | ###### Returns `object` | Name | Type | | ------ | ------ | | `acquired` | `Promise`\<() => `void`\> | | `waitingBehind` | `string` \| `null` | *** ### Thread An agent run in a Space. #### Constructors ##### Constructor ```ts new Thread(init): Thread; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `init` | \{ `agent`: [`AgentId`](#agentid); `deleteOnClose`: `boolean`; `isolation`: `"space"` \| `"none"`; `label?`: `string`; `notes?`: `string`[]; `runId`: `string`; `space`: `SpaceLike`; `spaces`: `SpacesLike`; \} | | `init.agent` | [`AgentId`](#agentid) | | `init.deleteOnClose` | `boolean` | | `init.isolation` | `"space"` \| `"none"` | | `init.label?` | `string` | | `init.notes?` | `string`[] | | `init.runId` | `string` | | `init.space` | `SpaceLike` | | `init.spaces` | `SpacesLike` | ###### Returns [`Thread`](#thread) #### Properties | Property | Modifier | Type | Description | | ------ | ------ | ------ | ------ | | `agent` | `readonly` | [`AgentId`](#agentid) | - | | `isolation` | `readonly` | `"space"` \| `"none"` | `space` when the thread has its Space to itself, `none` when shared. | | `label` | `readonly` | `string` \| `undefined` | - | | `notes` | `readonly` | `string`[] | Preparation steps the runtime could not complete, named. | | `runId` | `readonly` | `string` | - | | `space` | `readonly` | `SpaceLike` | - | #### Accessors ##### id ###### Get Signature ```ts get id(): string; ``` ###### Returns `string` #### Methods ##### close() ```ts close(): Promise; ``` Stops the run and deletes a dedicated Space created for it. ###### Returns `Promise`\<`void`\> ##### events() ```ts events(options?): AsyncGenerator; ``` Polls the run and yields derived events: text, files, links, approval prompts, and a `state` event on every status change. Stops once the run settles or after `maxPolls` (bounded). ###### Parameters | Parameter | Type | | ------ | ------ | | `options` | [`EventOptions`](#eventoptions) | ###### Returns `AsyncGenerator`\<[`ThreadEvent`](#threadevent)\> ##### send() ```ts send(text, options?): Promise; ``` Sends a follow-up. Turns on one Space are serialized. A run whose published `acceptsMessage` is false (a turn is running, or it could not be read) refuses unless `force`, and the refusal is returned, not hidden. ###### Parameters | Parameter | Type | | ------ | ------ | | `text` | `string` | | `options` | \{ `force?`: `boolean`; \} | | `options.force?` | `boolean` | ###### Returns `Promise`\<[`Turn`](#turn)\> ##### status() ```ts status(tail?): Promise; ``` The run's status on the closed ladder, with the raw output tail. ###### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `tail` | `number` | `200` | ###### Returns `Promise`\<[`ThreadStatus`](#threadstatus)\> ##### stop() ```ts stop(): Promise<{ reason: string; stopped: boolean; }>; ``` Stops the run; `stopped` says whether its death was witnessed. ###### Returns `Promise`\<\{ `reason`: `string`; `stopped`: `boolean`; \}\> *** ### TranscriptAdapter Turns a growing transcript into events, without re-emitting what it already emitted. The runtime gives us a *tail*, not a stream, so the adapter tracks what it has consumed by content, not by offset: a tail that scrolled past our last known position is detected, and only the portion it can prove is new is emitted. When it cannot prove overlap at all: the tail scrolled entirely past: it emits the whole tail and an `error` event saying output was lost, rather than silently dropping it. #### Constructors ##### Constructor ```ts new TranscriptAdapter(): TranscriptAdapter; ``` ###### Returns [`TranscriptAdapter`](#transcriptadapter) #### Methods ##### ingest() ```ts ingest(tail, now?): ThreadEvent[]; ``` Feed the newest transcript tail; get back only the newly observed events. ###### Parameters | Parameter | Type | | ------ | ------ | | `tail` | `string` | | `now` | () => `Date` | ###### Returns [`ThreadEvent`](#threadevent)[] ##### reset() ```ts reset(): void; ``` Reset for a new turn that restarts the session (codex `resume` does this). ###### Returns `void` ## Interfaces ### ApprovalRequestEvent The agent is blocked on a human decision. No agent runtime in a Space emits this today (they run auto-approved). It is defined because an app that puts an agent in front of a user needs the shape, and because a derived detector can spot a REPL prompt that is waiting. Answer it with `thread.send(...)`: there is no separate approval RPC yet, and this SDK will not invent one that silently does nothing. #### Extends - `ThreadEventBase` #### Properties | Property | Type | Description | Overrides | Inherited from | | ------ | ------ | ------ | ------ | ------ | | `derived` | `boolean` | `true` when this event was inferred from unstructured terminal output rather than reported by the agent runtime. Always check it before treating an event as authoritative. | - | `ThreadEventBase.derived` | | `kind` | `"approval_request"` | - | `ThreadEventBase.kind` | - | | `observedAt` | `string` | Wall clock at the moment the SDK observed it, not when the agent produced it. | - | `ThreadEventBase.observedAt` | | `options` | `string`[] | Choices parsed from the prompt, when they were parseable. | - | - | | `prompt` | `string` | - | - | - | | `seq` | `number` | Monotonic within a thread, assigned by the SDK. | - | `ThreadEventBase.seq` | *** ### CursorArtShape One shape's art: an SVG path (absolute M, L, C, Z; nonzero fill) and its hot spot. #### Properties | Property | Type | | ------ | ------ | | `d` | `string` | | `hotspot` | readonly \[`number`, `number`\] | *** ### ErrorEvent #### Extends - `ThreadEventBase` #### Properties | Property | Type | Description | Overrides | Inherited from | | ------ | ------ | ------ | ------ | ------ | | `derived` | `boolean` | `true` when this event was inferred from unstructured terminal output rather than reported by the agent runtime. Always check it before treating an event as authoritative. | - | `ThreadEventBase.derived` | | `kind` | `"error"` | - | `ThreadEventBase.kind` | - | | `message` | `string` | - | - | - | | `observedAt` | `string` | Wall clock at the moment the SDK observed it, not when the agent produced it. | - | `ThreadEventBase.observedAt` | | `seq` | `number` | Monotonic within a thread, assigned by the SDK. | - | `ThreadEventBase.seq` | *** ### EventOptions #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `intervalMs?` | `number` | Delay between polls (default 1000 ms). | | `maxPolls?` | `number` | Hard bound on status polls (default 600). | | `signal?` | `AbortSignal` | - | | `tail?` | `number` | Lines of output per poll (default 200). | *** ### FileEvent A file the agent appears to have produced, by absolute path inside the Space. Fetch it with `space.download(path, destDir)`: the SDK does not pre-fetch. #### Extends - `ThreadEventBase` #### Properties | Property | Type | Description | Overrides | Inherited from | | ------ | ------ | ------ | ------ | ------ | | `derived` | `boolean` | `true` when this event was inferred from unstructured terminal output rather than reported by the agent runtime. Always check it before treating an event as authoritative. | - | `ThreadEventBase.derived` | | `kind` | `"file"` | - | `ThreadEventBase.kind` | - | | `observedAt` | `string` | Wall clock at the moment the SDK observed it, not when the agent produced it. | - | `ThreadEventBase.observedAt` | | `path` | `string` | - | - | - | | `seq` | `number` | Monotonic within a thread, assigned by the SDK. | - | `ThreadEventBase.seq` | *** ### FiringRecord One line of the scheduler's log, newest first. #### Properties | Property | Type | | ------ | ------ | | `at` | `string` | | `firing` | [`RoutineFiring`](#routinefiring) | | `routineID` | `string` | | `title` | `string` | *** ### GroupDelivery The result of fanning one message out to one member. #### Properties | Property | Type | | ------ | ------ | | `accepted` | `boolean` | | `botID` | `string` | | `reason` | `string` | *** ### GroupMessage #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `at` | `string` | - | | `id` | `string` | - | | `reaction?` | `string` | - | | `speaker` | [`GroupSpeaker`](#groupspeaker) | - | | `text` | `string` | - | | `undelivered` | `boolean` | This line records a message that did not reach its Bot. | *** ### GroupMessenger How a group reaches its Bots. `deliver` never throws: a refusal is a result. #### Methods ##### deliver() ```ts deliver(text, botID): Promise; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `text` | `string` | | `botID` | `string` | ###### Returns `Promise`\<[`GroupDelivery`](#groupdelivery)\> ##### displayName() ```ts displayName(botID): string; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | ###### Returns `string` ##### isWorking() ```ts isWorking(botID): boolean; ``` Whether the Bot is producing output right now (the typing row). ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | ###### Returns `boolean` ##### latestReply() ```ts latestReply(botID): Promise; ``` The Bot's most recent utterance, or `undefined`. ###### Parameters | Parameter | Type | | ------ | ------ | | `botID` | `string` | ###### Returns `Promise`\<`string` \| `undefined`\> *** ### ImageEvent An image the agent produced. `data` is base64; `path` is set instead when the image is only known by its in-Space location. #### Extends - `ThreadEventBase` #### Properties | Property | Type | Description | Overrides | Inherited from | | ------ | ------ | ------ | ------ | ------ | | `data?` | `string` | - | - | - | | `derived` | `boolean` | `true` when this event was inferred from unstructured terminal output rather than reported by the agent runtime. Always check it before treating an event as authoritative. | - | `ThreadEventBase.derived` | | `kind` | `"image"` | - | `ThreadEventBase.kind` | - | | `mimeType` | `string` | - | - | - | | `observedAt` | `string` | Wall clock at the moment the SDK observed it, not when the agent produced it. | - | `ThreadEventBase.observedAt` | | `path?` | `string` | - | - | - | | `seq` | `number` | Monotonic within a thread, assigned by the SDK. | - | `ThreadEventBase.seq` | *** ### LinkEvent #### Extends - `ThreadEventBase` #### Properties | Property | Type | Description | Overrides | Inherited from | | ------ | ------ | ------ | ------ | ------ | | `derived` | `boolean` | `true` when this event was inferred from unstructured terminal output rather than reported by the agent runtime. Always check it before treating an event as authoritative. | - | `ThreadEventBase.derived` | | `kind` | `"link"` | - | `ThreadEventBase.kind` | - | | `observedAt` | `string` | Wall clock at the moment the SDK observed it, not when the agent produced it. | - | `ThreadEventBase.observedAt` | | `seq` | `number` | Monotonic within a thread, assigned by the SDK. | - | `ThreadEventBase.seq` | | `url` | `string` | - | - | - | *** ### PresenceDrawable One cursor to draw now. #### Properties | Property | Type | | ------ | ------ | | `alpha` | `number` | | `color` | `string` | | `displayId` | `string` | | `displayName` | `string` | | `isAgent` | `boolean` | | `isMe` | `boolean` | | `participantId` | `string` | | `shape` | [`CursorShapeName`](#cursorshapename) | | `shapeSource` | `string` | | `windowId?` | `string` | | `x` | `number` | | `y` | `number` | *** ### PresenceEntry One participant and their last cursor (normalized 0..1 over the streamed surface). #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `agent` | `boolean` | - | | `color` | `string` | - | | `cursor?` | `object` | - | | `cursor.shape?` | [`CursorShapeName`](#cursorshapename) | - | | `cursor.visible` | `boolean` | - | | `cursor.windowId?` | `string` | - | | `cursor.x` | `number` | - | | `cursor.y` | `number` | - | | `displayName` | `string` | - | | `participantId` | `string` | - | | `principalId` | `string` | The identity it joined with (`PresenceIdentity.id`), unless the server asserts one. | *** ### Routine One routine. Dates are ISO 8601 (UTC, whole seconds). #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `botID` | `string` | The Bot that runs it. A routine never spans Bots; a group chat does. | | `createdAt` | `string` | - | | `id` | `string` | - | | `isEnabled` | `boolean` | - | | `lastFiredAt?` | `string` | When the scheduler last started a firing (persisted, so a restart does not re-fire the past). | | `lastOutcome?` | `string` | The scheduler's words about the last firing: started, refused, failed. | | `lastRunID?` | `string` | The agent run the last firing produced. | | `prompt` | `string` | The text handed to the Bot when it fires. | | `schedule` | [`RoutineSchedule`](#routineschedule) | - | | `title` | `string` | - | *** ### RoutineRunner Gives a Bot its routine turn. Never throws: a refusal is a result. #### Methods ##### fire() ```ts fire(routine): Promise; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `routine` | [`Routine`](#routine-1) | ###### Returns `Promise`\<[`RoutineFiring`](#routinefiring)\> *** ### RoutineStorage Where the routine list lives. `load` returns `undefined` when nothing was saved. #### Methods ##### load() ```ts load(): string | undefined; ``` ###### Returns `string` \| `undefined` ##### save() ```ts save(json): void; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `json` | `string` | ###### Returns `void` *** ### SpacesErrorOptions #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `cause?` | `unknown` | - | | `detail?` | `string` | Free-form, already-redacted detail shown in the message. | | `status?` | `number` | HTTP status, when code is 'http'. | | `target?` | `string` | Request target, for triage. Never carries credentials. | *** ### StartThreadOptions #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `agent` | [`AgentId`](#agentid) | - | | `label?` | `string` | Your own label, echoed on the thread. | | `placement` | [`ThreadPlacement`](#threadplacement) | - | | `prompt` | `string` | The first task. There is no empty thread. | | `show?` | `boolean` | Open a terminal on the Space desktop tailing the run (default false). | *** ### StateEvent #### Extends - `ThreadEventBase` #### Properties | Property | Type | Description | Overrides | Inherited from | | ------ | ------ | ------ | ------ | ------ | | `derived` | `boolean` | `true` when this event was inferred from unstructured terminal output rather than reported by the agent runtime. Always check it before treating an event as authoritative. | - | `ThreadEventBase.derived` | | `kind` | `"state"` | - | `ThreadEventBase.kind` | - | | `observedAt` | `string` | Wall clock at the moment the SDK observed it, not when the agent produced it. | - | `ThreadEventBase.observedAt` | | `reason` | `string` | The ladder's reason for this status. | - | - | | `seq` | `number` | Monotonic within a thread, assigned by the SDK. | - | `ThreadEventBase.seq` | | `state` | [`AgentStatus`](#agentstatus) | - | - | - | *** ### TextEvent #### Extends - `ThreadEventBase` #### Properties | Property | Type | Description | Overrides | Inherited from | | ------ | ------ | ------ | ------ | ------ | | `derived` | `boolean` | `true` when this event was inferred from unstructured terminal output rather than reported by the agent runtime. Always check it before treating an event as authoritative. | - | `ThreadEventBase.derived` | | `kind` | `"text"` | - | `ThreadEventBase.kind` | - | | `observedAt` | `string` | Wall clock at the moment the SDK observed it, not when the agent produced it. | - | `ThreadEventBase.observedAt` | | `seq` | `number` | Monotonic within a thread, assigned by the SDK. | - | `ThreadEventBase.seq` | | `text` | `string` | - | - | - | *** ### ThreadStatus Everything the SDK knows about a run's current moment. #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `acceptsMessage` | `boolean` | A follow-up sent now starts the next turn: the server's published rule (idle, or a crashed or failed run it restarts; not mid-turn). | | `desktopWindow` | `string` \| `null` | The agent's terminal window on the Space desktop, when it was found. `null` means "not found", which is not the same as "does not exist". | | `reason` | `string` | Why the ladder landed there, in words. Always populated. | | `runId` | `string` | - | | `status` | [`AgentStatus`](#agentstatus) | - | | `transcript` | `string` | The raw transcript tail exactly as the Space reported it. Always present, because every derived event above is an interpretation of this. | *** ### Turn One delivered (or refused) message. #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `id` | `string` | - | | `queuedBehind?` | `string` | The turn this one waited behind on a shared Space, if any. | | `reason` | `string` | - | | `state` | `"delivered"` \| `"refused"` | `delivered` means the process started, not that the work is done. | | `text` | `string` | - | ## Type Aliases ### AgentId ```ts type AgentId = | "claude-code" | "gemini-cli" | "google-antigravity" | "goose" | "hermes" | "openai-codex" | "openclaw" | "opencode" | "pi"; ``` Agent CLIs the Spaces runtime can start (the contract's `AGENT_IDS`). *** ### AgentStatus ```ts type AgentStatus = | "running" | "awaiting_input" | "idle" | "finished" | "failed" | "crashed" | "unknown"; ``` The agent status ladder: one closed vocabulary for every harness and every provider. A UI can rely on this being closed. `idle` and `finished` are deliberately different: both mean "no turn is running", but `idle` means the session can still be continued and `finished` means it cannot. `unknown` is a real, reachable state and is **not** a synonym for "done". A probe that could not run, an unreachable Space, and an interactive REPL whose working-vs-waiting we cannot tell apart all land here. Reporting any of them as `idle` is how a caller concludes an agent finished when the Space actually fell over: and `crashed` exists so a dead process that never recorded an exit status is never read as a clean finish. *** ### CursorShapeName ```ts type CursorShapeName = | "arrow" | "text" | "pointer" | "resize_ew" | "resize_ns" | "resize_nwse" | "resize_nesw" | "move" | "crosshair" | "wait" | "progress" | "not_allowed" | "grab" | "grabbing"; ``` A presence cursor shape (the names of `cua.env.v1.CursorShape`). *** ### GroupChatErrorCode ```ts type GroupChatErrorCode = | "tooFewBots" | "tooManyBots" | "full" | "atFloor" | "alreadyAMember" | "notAMember" | "unknownChat"; ``` *** ### GroupSpeaker ```ts type GroupSpeaker = | { kind: "human"; } | { botID: string; kind: "bot"; } | { kind: "system"; }; ``` Who said a line: the human, a Bot, or the group itself (joins, leaves, refusals). *** ### RoutineFiring ```ts type RoutineFiring = | { kind: "started"; runId: string; } | { kind: "refused"; reason: string; } | { kind: "failed"; reason: string; }; ``` What happened when a routine fired. A refusal is not a failure. *** ### RoutineSchedule ```ts type RoutineSchedule = | { kind: "everyMinutes"; minutes: number; } | { hour: number; kind: "dailyAt"; minute: number; } | { hour: number; kind: "weeklyOn"; minute: number; weekday: number; }; ``` The three recurrence shapes. `weekday` is 1 = Sunday … 7 = Saturday. *** ### SpacesErrorCode ```ts type SpacesErrorCode = | "auth" | "transport" | "http" | "protocol" | "usage" | "not_found" | "ambiguous_sandbox" | "timeout" | "verification" | "isolation" | "space_command" | "host_unavailable" | "unsupported" | "capability_missing" | "host_capability_missing" | "spacesd_not_available" | "teleport_refused"; ``` Stable, matchable error codes. Adding a code is a minor release; removing or repurposing one is a breaking release. *** ### ThreadEvent ```ts type ThreadEvent = | TextEvent | FileEvent | ImageEvent | LinkEvent | ApprovalRequestEvent | StateEvent | ErrorEvent; ``` *** ### ThreadEventKind ```ts type ThreadEventKind = | "text" | "file" | "image" | "link" | "approval_request" | "state" | "error"; ``` What an agent sends back. The honest situation, as of this release: the agent runtime inside a Space is a terminal. `agent_status` returns the tail of the run's output (the agent runs as a detached, tagged cua-spacesd process). There is no structured result channel yet: no file manifest, no image parts, no approval protocol. The supported agents are launched auto-approved and the sandbox is the safety boundary. So this module does two things and is careful about the difference: 1. It defines the *result model* a chat app with agent coworkers needs: text, files, images, links, approval requests: as a stable discriminated union, so an app can be written against it today and keep working when the runtime starts emitting the real thing. 2. It derives what it can from the terminal transcript, and marks every derived event `derived: true`. A consumer can therefore tell a fact ("the agent emitted this file") from an inference ("a path-shaped string appeared in the scrollback"). Nothing here pretends to be ground truth. When the agent runtime gains a structured channel (see `docs/`), the adapter gains a branch that emits the same events with `derived: false`. The union is the stable contract; the adapter is not. *** ### ThreadPlacement ```ts type ThreadPlacement = | { deleteOnClose?: boolean; image?: string; kind?: string; on?: string; runtime?: string; type: "dedicated"; } | { acknowledgeNoIsolation: true; space: string | SpaceLike; type: "shared"; }; ``` Where a thread's agent runs. There is no default; choosing is the point. #### Union Members ##### Type Literal ```ts { deleteOnClose?: boolean; image?: string; kind?: string; on?: string; runtime?: string; type: "dedicated"; } ``` | Name | Type | Description | | ------ | ------ | ------ | | `deleteOnClose?` | `boolean` | Delete the Space when the thread closes. Default true. | | `image?` | `string` | Image of the Space this thread creates (default: the canonical Linux image). | | `kind?` | `string` | `auto` (default), `container` or `vm`. | | `on?` | `string` | Where: `local` or `cloud` (default: the user's default location). | | `runtime?` | `string` | `auto` (default) or an engine the location offers for the kind. | | `type` | `"dedicated"` | - | *** ##### Type Literal ```ts { acknowledgeNoIsolation: true; space: string | SpaceLike; type: "shared"; } ``` | Name | Type | Description | | ------ | ------ | ------ | | `acknowledgeNoIsolation` | `true` | Required, and required to be literally `true`: this thread shares a filesystem, a browser profile, cookies and every app login with every other thread on the Space; nothing inside a Space is a security boundary. | | `space` | `string` \| `SpaceLike` | The Space (id or handle) to place this thread on. | | `type` | `"shared"` | - | ## Variables ### AGENT_IDS ```ts const AGENT_IDS: readonly AgentId[]; ``` *** ### CURSOR_ART ```ts const CURSOR_ART: object; ``` The shared presence cursor art: fill with the participant's color over the outline. #### Type Declaration | Name | Type | | ------ | ------ | | `canvas` | `number` | | `outline` | `object` | | `outline.color` | `string` | | `outline.width` | `number` | | `shapes` | `Readonly`\<`Record`\<[`CursorShapeName`](#cursorshapename), [`CursorArtShape`](#cursorartshape)\>\> | *** ### CURSOR_SHAPES ```ts const CURSOR_SHAPES: readonly CursorShapeName[]; ``` Every shape, in wire order. *** ### DATAGRAM_DELAY_MS ```ts const DATAGRAM_DELAY_MS: 66 = 66; ``` Render delay on the 30 Hz datagram channel. *** ### GROUP_MAX_BOTS ```ts const GROUP_MAX_BOTS: 6 = 6; ``` *** ### GROUP_MIN_BOTS ```ts const GROUP_MIN_BOTS: 2 = 2; ``` The product bound: a group of one is a thread; every message fans out to every member. *** ### PRESENCE_FALLBACK_COLOR ```ts const PRESENCE_FALLBACK_COLOR: "#3b82f6" = "#3b82f6"; ``` Used when the server assigned no color. *** ### PRESENCE_PALETTE ```ts const PRESENCE_PALETTE: readonly string[]; ``` The cursor palette, in cua-spacesd's assignment order. *** ### ROUTINE_PREFIX ```ts const ROUTINE_PREFIX: "[routine]" = "[routine]"; ``` Marks a routine-originated turn in a transcript. *** ### SETTLED ```ts const SETTLED: ReadonlySet; ``` Statuses after which `events()` stops polling. *** ### STREAM_DELAY_MS ```ts const STREAM_DELAY_MS: 100 = 100; ``` The presence netcode model, mirrored exactly from the Rust core (`cua_spaces::presence::view`) and checked against the same conformance vectors. Pure and deterministic: every method takes the local time in milliseconds (`Date.now()`). - your own cursor is drawn at the local pointer, never from the network; - remote cursors render [STREAM_DELAY_MS](#stream_delay_ms) (100 ms; 66 on the datagram channel) behind the newest sample, Catmull-Rom through at least 4 samples, else linear; - past the newest sample they extrapolate for at most 100 ms, then hold; - a jump over 25% of the surface, a target change or a re-show snaps; - a cursor idle for 5 s fades out over 300 ms; - a participant a heartbeat does not list, or everyone when heartbeats stop for 3 intervals, is removed. ## Functions ### adoptThread() ```ts function adoptThread( spaces, spaceId, runId ): Promise; ``` Adopts a run that already exists (started by another process or the MCP). #### Parameters | Parameter | Type | | ------ | ------ | | `spaces` | `SpacesLike` | | `spaceId` | `string` | | `runId` | `string` | #### Returns `Promise`\<[`Thread`](#thread)\> *** ### agentIdentity() ```ts function agentIdentity(id, displayName): object; ``` An agent's presence identity, requesting its stable [presenceColor](#presencecolor). #### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | | `displayName` | `string` | #### Returns `object` | Name | Type | | ------ | ------ | | `agent` | `true` | | `color` | `string` | | `displayName` | `string` | | `id` | `string` | *** ### approve() ```ts function approve(decide): TeleportApprover; ``` A TeleportApprover from a function. The function sees exactly what would leave this machine and returns the human's decision, or `undefined` to cancel (the teleport then fails with `TeleportRefused`). It runs on a worker thread of the SDK and must be synchronous. #### Parameters | Parameter | Type | | ------ | ------ | | `decide` | (`manifest`) => `TeleportDecision` \| `undefined` | #### Returns `TeleportApprover` *** ### canCreateGroup() ```ts function canCreateGroup(members): boolean; ``` Whether a Create button should be enabled for `members`. #### Parameters | Parameter | Type | | ------ | ------ | | `members` | readonly `string`[] | #### Returns `boolean` *** ### clockLabel() ```ts function clockLabel(hour, minute): string; ``` `8:05 AM`. #### Parameters | Parameter | Type | | ------ | ------ | | `hour` | `number` | | `minute` | `number` | #### Returns `string` *** ### cursorArt() ```ts function cursorArt(shape): CursorArtShape; ``` The art for `shape`; unknown names get the arrow. #### Parameters | Parameter | Type | | ------ | ------ | | `shape` | `string` | #### Returns [`CursorArtShape`](#cursorartshape) *** ### cursorArtSvg() ```ts function cursorArtSvg( shape, color, size? ): string; ``` A standalone SVG of `shape` filled with `color`, `size` pixels square. The hot spot is at `cursorArt(shape).hotspot * size / CURSOR_ART.canvas`. #### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `shape` | `string` | `undefined` | | `color` | `string` | `undefined` | | `size` | `number` | `CURSOR_ART.canvas` | #### Returns `string` *** ### detectApproval() ```ts function detectApproval(fresh): | { options: string[]; prompt: string; } | null; ``` Spot a terminal prompt that is waiting on a human. Only fires on a prompt at the very end of the fresh output: a question in the middle of the scrollback has already been answered. Recognizes the two common shapes: a `[y/N]`-style suffix and a numbered choice list followed by a prompt line. #### Parameters | Parameter | Type | | ------ | ------ | | `fresh` | `string` | #### Returns \| \{ `options`: `string`[]; `prompt`: `string`; \} \| `null` *** ### excerpt() ```ts function excerpt(body, max?): string; ``` Trim an untrusted body for an error message without hiding it entirely. #### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `body` | `string` | `undefined` | | `max` | `number` | `400` | #### Returns `string` *** ### firingSummary() ```ts function firingSummary(f): string; ``` `started run `, `refused: `, `failed: `. #### Parameters | Parameter | Type | | ------ | ------ | | `f` | [`RoutineFiring`](#routinefiring) | #### Returns `string` *** ### frameGroupMessage() ```ts function frameGroupMessage( text, botID, chat, name ): string; ``` The framing each member receives: the room, then the human's words on the last line (so an agent that reads the last line still sees them). #### Parameters | Parameter | Type | | ------ | ------ | | `text` | `string` | | `botID` | `string` | | `chat` | [`GroupChat`](#groupchat) | | `name` | (`botID`) => `string` | #### Returns `string` *** ### idleAlpha() ```ts function idleAlpha(idleMs): number; ``` Opacity of a cursor idle for `idleMs`. #### Parameters | Parameter | Type | | ------ | ------ | | `idleMs` | `number` | #### Returns `number` *** ### isCursorShape() ```ts function isCursorShape(name): name is CursorShapeName; ``` Whether `name` is a known shape. #### Parameters | Parameter | Type | | ------ | ------ | | `name` | `unknown` | #### Returns `name is CursorShapeName` *** ### isDue() ```ts function isDue(routine, now): boolean; ``` Whether the scheduler should fire `routine` at `now`. Measured from the last firing, not the tick, so a sleeping scheduler fires once on waking. #### Parameters | Parameter | Type | | ------ | ------ | | `routine` | [`Routine`](#routine-1) | | `now` | `Date` | #### Returns `boolean` *** ### isoSeconds() ```ts function isoSeconds(d): string; ``` ISO 8601 in UTC with whole seconds, the portable form every SDK reads. #### Parameters | Parameter | Type | | ------ | ------ | | `d` | `Date` | #### Returns `string` *** ### isSpacesError() ```ts function isSpacesError(value, code?): value is SpacesError; ``` Narrowing helper for consumers: `if (isSpacesError(e, 'verification')) …` #### Parameters | Parameter | Type | | ------ | ------ | | `value` | `unknown` | | `code?` | [`SpacesErrorCode`](#spaceserrorcode) | #### Returns `value is SpacesError` *** ### lockFor() ```ts function lockFor(spaceId): SpaceTurnLock; ``` One lock per Space id, so two handles to the same Space share it. #### Parameters | Parameter | Type | | ------ | ------ | | `spaceId` | `string` | #### Returns [`SpaceTurnLock`](#spaceturnlock) *** ### memoryStorage() ```ts function memoryStorage(initial?): RoutineStorage & object; ``` A [RoutineStorage](#routinestorage) in memory. #### Parameters | Parameter | Type | | ------ | ------ | | `initial?` | `string` | #### Returns [`RoutineStorage`](#routinestorage) & `object` *** ### nextFireDate() ```ts function nextFireDate(schedule, reference): Date | undefined; ``` The first slot strictly after `reference`, in local time; `undefined` for an invalid schedule. #### Parameters | Parameter | Type | | ------ | ------ | | `schedule` | [`RoutineSchedule`](#routineschedule) | | `reference` | `Date` | #### Returns `Date` \| `undefined` *** ### parseRoutines() ```ts function parseRoutines(json): Routine[]; ``` Parses a saved routine list. Throws on anything malformed rather than guessing. #### Parameters | Parameter | Type | | ------ | ------ | | `json` | `string` | #### Returns [`Routine`](#routine-1)[] *** ### presenceColor() ```ts function presenceColor(id): string; ``` The stable presence color of an agent or other principal: a palette color picked by a hash (FNV-1a, 32-bit, over the UTF-8 id), the same answer as the Rust core's `presence_color` in every binding. Request it when the agent joins presence (`PresenceIdentity.color`) and use it as the background of its avatar, so avatar and cursor come from one source. #### Parameters | Parameter | Type | | ------ | ------ | | `id` | `string` | #### Returns `string` *** ### presenceTextColor() ```ts function presenceTextColor(background): string; ``` Black or white text, whichever has the better WCAG contrast on `background` (`#rrggbb`). #### Parameters | Parameter | Type | | ------ | ------ | | `background` | `string` | #### Returns `string` *** ### routineNextFire() ```ts function routineNextFire(routine, reference): Date | undefined; ``` When `routine` next fires after `reference`, or `undefined` when disabled. #### Parameters | Parameter | Type | | ------ | ------ | | `routine` | [`Routine`](#routine-1) | | `reference` | `Date` | #### Returns `Date` \| `undefined` *** ### routineTurnText() ```ts function routineTurnText(routine): string; ``` The text a runner hands the Bot: `[routine] : <prompt>`. #### Parameters | Parameter | Type | | ------ | ------ | | `routine` | `Pick`\<[`Routine`](#routine-1), `"title"` \| `"prompt"`\> | #### Returns `string` *** ### scheduleLabel() ```ts function scheduleLabel(schedule): string; ``` The one-line description of a schedule: `Every day at 8:00 AM`. #### Parameters | Parameter | Type | | ------ | ------ | | `schedule` | [`RoutineSchedule`](#routineschedule) | #### Returns `string` *** ### startThread() ```ts function startThread(spaces, options): Promise<Thread>; ``` Starts an agent thread. `placement` is required and has no default. #### Parameters | Parameter | Type | | ------ | ------ | | `spaces` | `SpacesLike` | | `options` | [`StartThreadOptions`](#startthreadoptions) | #### Returns `Promise`\<[`Thread`](#thread)\> *** ### toSpacesError() ```ts function toSpacesError(error): SpacesError; ``` Wraps anything the native SDK threw as a [SpacesError](#spaceserror) with a stable code (the `CuaError` variant stays reachable as `cause`). Already-wrapped errors pass through. #### Parameters | Parameter | Type | | ------ | ------ | | `error` | `unknown` | #### Returns [`SpacesError`](#spaceserror) *** ### validatePlacement() ```ts function validatePlacement(placement): ThreadPlacement; ``` Throws unless the caller genuinely chose a placement. #### Parameters | Parameter | Type | | ------ | ------ | | `placement` | [`ThreadPlacement`](#threadplacement) \| `undefined` | #### Returns [`ThreadPlacement`](#threadplacement) *** ### waitForPresence() ```ts function waitForPresence( session, match, timeoutMs, maxEvents?, roster? ): Promise<PresenceEvent>; ``` Reads `session` until an event matches, folding every event into `roster` when given. Throws after `timeoutMs` or `maxEvents` events. #### Parameters | Parameter | Type | Default value | | ------ | ------ | ------ | | `session` | `PresenceEventSource` | `undefined` | | `match` | (`e`) => `boolean` | `undefined` | | `timeoutMs` | `number` | `undefined` | | `maxEvents` | `number` | `50` | | `roster?` | [`PresenceRoster`](#presenceroster) | `undefined` | #### Returns `Promise`\<`PresenceEvent`\> *** ### wrapErrors() ```ts function wrapErrors<T>(body): Promise<T>; ``` Runs `body`, rethrowing native errors as [SpacesError](#spaceserror). #### Type Parameters | Type Parameter | | ------ | | `T` | #### Parameters | Parameter | Type | | ------ | ------ | | `body` | () => `Promise`\<`T`\> | #### Returns `Promise`\<`T`\> --- # Spaces transport (TypeScript) MCP-over-HTTP and Tauri transports for Spaces in webviews and browsers. Import from `@trycua/cua/spaces/transport`. This page covers the hand-written TypeScript layer; the generated native binding (sandboxes, guest, fleet, spaces objects) is documented per object in the [Cua SDK reference](</docs/cua-sdk/reference>). ## Classes ### McpHttpTransport The one interface a host has to implement. Implementations must: - resolve the reply whose `id` matches the request's, and not some other reply that happened to arrive first; - resolve `null` for a notification; - reject every in-flight call if the peer dies, rather than leaving callers hanging on a reply that is never coming; - be safe to call again after `close()`, either by restarting or by rejecting clearly. #### Implements - [`Transport`](#transport) #### Constructors ##### Constructor ```ts new McpHttpTransport(options): McpHttpTransport; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `options` | [`McpHttpOptions`](#mcphttpoptions) | ###### Returns [`McpHttpTransport`](#mcphttptransport) #### Properties | Property | Modifier | Type | Default value | Description | | ------ | ------ | ------ | ------ | ------ | | `kind` | `readonly` | `"http"` | `"http"` | A short name for diagnostics: `"stdio"`, `"tauri"`. | #### Accessors ##### sessionId ###### Get Signature ```ts get sessionId(): string | undefined; ``` The session id the daemon minted, once initialized. ###### Returns `string` \| `undefined` #### Methods ##### close() ```ts close(): Promise<void>; ``` Ends the MCP session (`DELETE /mcp`). Never throws. ###### Returns `Promise`\<`void`\> ###### Implementation of [`Transport`](#transport).[`close`](#close-3) ##### send() ```ts send(message, options?): Promise<RpcResponse | null>; ``` Send one message; resolve its reply, or `null` for a notification. ###### Parameters | Parameter | Type | | ------ | ------ | | `message` | [`RpcOutgoing`](#rpcoutgoing) | | `options` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<[`RpcResponse`](#rpcresponse) \| `null`\> ###### Implementation of [`Transport`](#transport).[`send`](#send-2) *** ### McpSession #### Constructors ##### Constructor ```ts new McpSession(transport, options?): McpSession; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `transport` | [`Transport`](#transport) | | `options` | [`SessionOptions`](#sessionoptions) | ###### Returns [`McpSession`](#mcpsession) #### Properties | Property | Modifier | Type | | ------ | ------ | ------ | | `transport` | `readonly` | [`Transport`](#transport) | #### Methods ##### call() ```ts call( name, args?, options? ): Promise<ContentPart[]>; ``` Call a tool, throwing [ToolError](#toolerror) if it failed. ###### Parameters | Parameter | Type | | ------ | ------ | | `name` | `string` | | `args` | `Record`\<`string`, [`Json`](#json)\> | | `options?` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<[`ContentPart`](#contentpart)[]\> ##### callJson() ```ts callJson<T>( name, args?, options? ): Promise<T>; ``` Call a tool whose text part is JSON, and parse it. Worth its own method because the parse failure needs to say which tool produced the unparsable text: without that, a server-side format change surfaces as a bare `SyntaxError` with no attribution. ###### Type Parameters | Type Parameter | Default type | | ------ | ------ | | `T` | [`Json`](#json) | ###### Parameters | Parameter | Type | | ------ | ------ | | `name` | `string` | | `args` | `Record`\<`string`, [`Json`](#json)\> | | `options?` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<`T`\> ##### callRaw() ```ts callRaw( name, args?, options? ): Promise<ToolResult>; ``` Call a tool and return its envelope, including a failure. Use this when a caller wants to inspect `isError` itself: for instance a probe that treats "not available" as an answer rather than an exception. Most callers want [call](#call). ###### Parameters | Parameter | Type | | ------ | ------ | | `name` | `string` | | `args` | `Record`\<`string`, [`Json`](#json)\> | | `options?` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<[`ToolResult`](#toolresult)\> ##### callText() ```ts callText( name, args?, options? ): Promise<string>; ``` Call a tool and return its text parts joined: the common case, since most of these tools answer with one text part. ###### Parameters | Parameter | Type | | ------ | ------ | | `name` | `string` | | `args` | `Record`\<`string`, [`Json`](#json)\> | | `options?` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<`string`\> ##### close() ```ts close(): Promise<void>; ``` ###### Returns `Promise`\<`void`\> ##### initialize() ```ts initialize(options?): Promise<ServerInfo | undefined>; ``` Perform the MCP handshake, at most once per session. The server does not require this: it builds its dispatch table before the read loop starts, so `tools/call` works cold. We do it anyway, and we memoize it: it is what makes this session usable against a *conforming* MCP server rather than only against this one, and the cost is a single round trip. The result is cached as a promise, not a value, so concurrent first calls share one handshake instead of racing two. ###### Parameters | Parameter | Type | | ------ | ------ | | `options?` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<[`ServerInfo`](#serverinfo) \| `undefined`\> ##### listTools() ```ts listTools(options?): Promise<ToolDescriptor[]>; ``` Every tool the server serves, with its input schema. ###### Parameters | Parameter | Type | | ------ | ------ | | `options?` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<[`ToolDescriptor`](#tooldescriptor)[]\> *** ### TauriTransport The one interface a host has to implement. Implementations must: - resolve the reply whose `id` matches the request's, and not some other reply that happened to arrive first; - resolve `null` for a notification; - reject every in-flight call if the peer dies, rather than leaving callers hanging on a reply that is never coming; - be safe to call again after `close()`, either by restarting or by rejecting clearly. #### Implements - [`Transport`](#transport) #### Constructors ##### Constructor ```ts new TauriTransport(options): TauriTransport; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `options` | [`TauriOptions`](#taurioptions) | ###### Returns [`TauriTransport`](#tauritransport) #### Properties | Property | Modifier | Type | Default value | Description | | ------ | ------ | ------ | ------ | ------ | | `kind` | `readonly` | `"tauri"` | `"tauri"` | A short name for diagnostics: `"stdio"`, `"tauri"`. | #### Methods ##### close() ```ts close(): Promise<void>; ``` Ask the bridge to close the server's stdin. A failure here is swallowed: the transport is being torn down, the app may already be quitting, and turning "could not reach the bridge while closing" into an exception makes shutdown paths fragile for no gain. ###### Returns `Promise`\<`void`\> ###### Implementation of [`Transport`](#transport).[`close`](#close-3) ##### send() ```ts send(message, options?): Promise<RpcResponse | null>; ``` Send one message; resolve its reply, or `null` for a notification. ###### Parameters | Parameter | Type | | ------ | ------ | | `message` | [`RpcOutgoing`](#rpcoutgoing) | | `options` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<[`RpcResponse`](#rpcresponse) \| `null`\> ###### Implementation of [`Transport`](#transport).[`send`](#send-2) *** ### ToolError A tool that ran and failed. This is a separate type from [TransportError](#transporterror) on purpose. The server reports a failing tool as a *successful* JSON-RPC response carrying `isError: true`; the only JSON-RPC `error` objects it ever sends are `-32601` for an unknown method or tool. Collapsing the two would make "the sandbox refused this command" indistinguishable from "the control plane is not running", and those need different handling by every caller. #### Extends - `Error` #### Constructors ##### Constructor ```ts new ToolError( tool, content, structured? ): ToolError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `tool` | `string` | | `content` | [`ContentPart`](#contentpart)[] | | `structured?` | [`Json`](#json) | ###### Returns [`ToolError`](#toolerror) ###### Overrides ```ts Error.constructor ``` #### Properties | Property | Modifier | Type | Default value | Description | | ------ | ------ | ------ | ------ | ------ | | `content` | `readonly` | [`ContentPart`](#contentpart)[] | `undefined` | - | | `kind` | `readonly` | `string` \| `undefined` | `undefined` | The server's stable error kind (`capability_missing`, `teleport_refused`, `not_found`, ...), when it sent one. | | `tag` | `readonly` | `"ToolError"` | `"ToolError"` | - | | `tool` | `readonly` | `string` | `undefined` | - | *** ### TransportError Thrown for anything that goes wrong beneath the tool call: a dead peer, a timeout, a JSON-RPC `error` object. A failing *tool* is not this: see `ToolError`, because this server reports tool failure as a successful response carrying `isError: true`. #### Extends - `Error` #### Constructors ##### Constructor ```ts new TransportError(message, code?): TransportError; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `message` | `string` | | `code?` | `number` | ###### Returns [`TransportError`](#transporterror) ###### Overrides ```ts Error.constructor ``` #### Properties | Property | Modifier | Type | Default value | | ------ | ------ | ------ | ------ | | `code` | `readonly` | `number` \| `undefined` | `undefined` | | `tag` | `readonly` | `"TransportError"` | `"TransportError"` | ## Interfaces ### ClientInfo #### Properties | Property | Type | | ------ | ------ | | `name` | `string` | | `version` | `string` | *** ### ContentPart One part of a tool result. `call_tool` passes through `image`, `audio` and `resource` parts from an in-space tool unchanged, so this is not only ever text. #### Indexable ```ts [key: string]: Json | undefined ``` #### Properties | Property | Type | | ------ | ------ | | `text?` | `string` | | `type` | `string` | *** ### McpHttpOptions #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `fetch?` | [`FetchFn`](#fetchfn) | Inject a fetch (tests, a Tauri HTTP plugin). Defaults to `globalThis.fetch`. | | `token?` | `string` | The daemon's loopback bearer token. | | `url` | `string` | Daemon loopback base URL (`http://127.0.0.1:<port>`) or the full `/mcp` URL. | *** ### RpcError `@trycua/cua/spaces/transport`: the Spaces control plane for hosts that cannot load the native binding (a Tauri webview, a browser). Dependency-free TypeScript: `McpSession` speaks MCP to the Rust Spaces server over any `Transport`: - `McpHttpTransport`: the `cua daemon` loopback `/mcp` (streamable HTTP, bearer = the daemon's loopback token); - `TauriTransport`: `invoke()` into the Tauri app's Rust side. Node code should use `@trycua/cua/spaces` (the typed SDK) instead. See `types.ts` for why the seam exchanges whole JSON-RPC messages rather than bytes. #### Properties | Property | Type | | ------ | ------ | | `code` | `number` | | `data?` | [`Json`](#json) | | `message` | `string` | *** ### RpcNotification A message with no `id`. It gets no reply, and a transport must resolve `null` for it rather than inventing one. #### Properties | Property | Type | | ------ | ------ | | `jsonrpc` | `"2.0"` | | `method` | `string` | | `params?` | [`Json`](#json) | *** ### RpcRequest `@trycua/cua/spaces/transport`: the Spaces control plane for hosts that cannot load the native binding (a Tauri webview, a browser). Dependency-free TypeScript: `McpSession` speaks MCP to the Rust Spaces server over any `Transport`: - `McpHttpTransport`: the `cua daemon` loopback `/mcp` (streamable HTTP, bearer = the daemon's loopback token); - `TauriTransport`: `invoke()` into the Tauri app's Rust side. Node code should use `@trycua/cua/spaces` (the typed SDK) instead. See `types.ts` for why the seam exchanges whole JSON-RPC messages rather than bytes. #### Properties | Property | Type | | ------ | ------ | | `id` | [`RpcId`](#rpcid) | | `jsonrpc` | `"2.0"` | | `method` | `string` | | `params?` | [`Json`](#json) | *** ### RpcResponse `@trycua/cua/spaces/transport`: the Spaces control plane for hosts that cannot load the native binding (a Tauri webview, a browser). Dependency-free TypeScript: `McpSession` speaks MCP to the Rust Spaces server over any `Transport`: - `McpHttpTransport`: the `cua daemon` loopback `/mcp` (streamable HTTP, bearer = the daemon's loopback token); - `TauriTransport`: `invoke()` into the Tauri app's Rust side. Node code should use `@trycua/cua/spaces` (the typed SDK) instead. See `types.ts` for why the seam exchanges whole JSON-RPC messages rather than bytes. #### Properties | Property | Type | | ------ | ------ | | `error?` | [`RpcError`](#rpcerror) | | `id` | [`RpcId`](#rpcid) | | `jsonrpc` | `"2.0"` | | `result?` | [`Json`](#json) | *** ### SendOptions `@trycua/cua/spaces/transport`: the Spaces control plane for hosts that cannot load the native binding (a Tauri webview, a browser). Dependency-free TypeScript: `McpSession` speaks MCP to the Rust Spaces server over any `Transport`: - `McpHttpTransport`: the `cua daemon` loopback `/mcp` (streamable HTTP, bearer = the daemon's loopback token); - `TauriTransport`: `invoke()` into the Tauri app's Rust side. Node code should use `@trycua/cua/spaces` (the typed SDK) instead. See `types.ts` for why the seam exchanges whole JSON-RPC messages rather than bytes. #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `signal?` | `AbortSignal` | Aborts the wait. A transport should still expect the server to finish the work: this server has no `notifications/cancelled` handling, so aborting abandons the reply rather than stopping the call. | | `timeoutSeconds?` | `number` | Seconds to wait for a reply. The default is deliberately long. `teleport_app` and provisioning run with `timeout=900` server-side, and the agent-CLI registration this server ships with sets `requestTimeoutMs: 900000`. A transport that imposed a short deadline would silently break the app's slowest and most valuable operations, so a conformer must not shorten this on its own initiative. | *** ### ServerInfo #### Properties | Property | Type | | ------ | ------ | | `name` | `string` | | `version` | `string` | *** ### SessionOptions #### Properties | Property | Type | | ------ | ------ | | `clientInfo?` | [`ClientInfo`](#clientinfo) | *** ### TauriOptions #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `invoke` | [`InvokeFn`](#invokefn) | Usually `(await import("@tauri-apps/api/core")).invoke`. | | `requestCommand?` | `string` | The Rust command name (default `spaces_mcp_request`). | | `shutdownCommand?` | `string` | The Rust command that ends the session (default `spaces_mcp_shutdown`). | *** ### ToolDescriptor #### Properties | Property | Type | | ------ | ------ | | `description?` | `string` | | `inputSchema?` | [`Json`](#json) | | `name` | `string` | *** ### ToolResult #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `content` | [`ContentPart`](#contentpart)[] | - | | `isError` | `boolean` | - | | `structured?` | [`Json`](#json) | `structuredContent`, when the tool returned one (tool errors carry `{error: {kind, message}}` with a stable `kind`). | *** ### Transport The one interface a host has to implement. Implementations must: - resolve the reply whose `id` matches the request's, and not some other reply that happened to arrive first; - resolve `null` for a notification; - reject every in-flight call if the peer dies, rather than leaving callers hanging on a reply that is never coming; - be safe to call again after `close()`, either by restarting or by rejecting clearly. #### Properties | Property | Modifier | Type | Description | | ------ | ------ | ------ | ------ | | `kind` | `readonly` | `string` | A short name for diagnostics: `"stdio"`, `"tauri"`. | #### Methods ##### close() ```ts close(): Promise<void>; ``` Release the peer. Idempotent. ###### Returns `Promise`\<`void`\> ##### send() ```ts send(message, options?): Promise<RpcResponse | null>; ``` Send one message; resolve its reply, or `null` for a notification. ###### Parameters | Parameter | Type | | ------ | ------ | | `message` | [`RpcOutgoing`](#rpcoutgoing) | | `options?` | [`SendOptions`](#sendoptions) | ###### Returns `Promise`\<[`RpcResponse`](#rpcresponse) \| `null`\> ## Type Aliases ### FetchFn ```ts type FetchFn = (input, init) => Promise<{ headers: { get: string | null; }; ok: boolean; status: number; text: Promise<string>; }>; ``` The `fetch` shape this needs (the global one, or an injected fake). #### Parameters | Parameter | Type | | ------ | ------ | | `input` | `string` | | `init` | \{ `body?`: `string`; `headers`: `Record`\<`string`, `string`\>; `method`: `string`; `signal?`: `AbortSignal`; \} | | `init.body?` | `string` | | `init.headers` | `Record`\<`string`, `string`\> | | `init.method` | `string` | | `init.signal?` | `AbortSignal` | #### Returns `Promise`\<\{ `headers`: \{ `get`: `string` \| `null`; \}; `ok`: `boolean`; `status`: `number`; `text`: `Promise`\<`string`\>; \}\> *** ### InvokeFn ```ts type InvokeFn = <T>(command, args?) => Promise<T>; ``` The shape of Tauri's `invoke`, narrowed to what this needs. #### Type Parameters | Type Parameter | | ------ | | `T` | #### Parameters | Parameter | Type | | ------ | ------ | | `command` | `string` | | `args?` | `Record`\<`string`, `unknown`\> | #### Returns `Promise`\<`T`\> *** ### Json ```ts type Json = | null | boolean | number | string | Json[] | { [key: string]: Json; }; ``` A JSON value, as it crosses the wire. *** ### RpcId ```ts type RpcId = number | string; ``` JSON-RPC 2.0 ids: this SDK only ever sends numbers, but a reply echoes whatever it was given, and a conforming peer may use a string. *** ### RpcOutgoing ```ts type RpcOutgoing = RpcRequest | RpcNotification; ``` `@trycua/cua/spaces/transport`: the Spaces control plane for hosts that cannot load the native binding (a Tauri webview, a browser). Dependency-free TypeScript: `McpSession` speaks MCP to the Rust Spaces server over any `Transport`: - `McpHttpTransport`: the `cua daemon` loopback `/mcp` (streamable HTTP, bearer = the daemon's loopback token); - `TauriTransport`: `invoke()` into the Tauri app's Rust side. Node code should use `@trycua/cua/spaces` (the typed SDK) instead. See `types.ts` for why the seam exchanges whole JSON-RPC messages rather than bytes. ## Variables ### DEFAULT_REQUEST_COMMAND ```ts const DEFAULT_REQUEST_COMMAND: "spaces_mcp_request" = "spaces_mcp_request"; ``` *** ### DEFAULT_SHUTDOWN_COMMAND ```ts const DEFAULT_SHUTDOWN_COMMAND: "spaces_mcp_shutdown" = "spaces_mcp_shutdown"; ``` *** ### PROTOCOL_VERSION ```ts const PROTOCOL_VERSION: "2025-06-18" = "2025-06-18"; ``` The MCP revision offered. The Rust server accepts 2025-06-18, 2025-03-26 and 2024-11-05, and answers with the one it picked. *** ### SESSION_HEADER ```ts const SESSION_HEADER: "mcp-session-id" = "mcp-session-id"; ``` ## Functions ### textOf() ```ts function textOf(content): string; ``` Concatenate the text parts, which is what almost every caller wants. #### Parameters | Parameter | Type | | ------ | ------ | | `content` | [`ContentPart`](#contentpart)[] | #### Returns `string` --- # Spaces host (TypeScript) The @trycua/cua/spaces/host helpers for hosting a Space from Node. Import from `@trycua/cua/spaces/host`. This page covers the hand-written TypeScript layer; the generated native binding (sandboxes, guest, fleet, spaces objects) is documented per object in the [Cua SDK reference](</docs/cua-sdk/reference>). ## Classes ### HostPresentation #### Constructors ##### Constructor ```ts new HostPresentation(options?): HostPresentation; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `options` | [`HostPresentationOptions`](#hostpresentationoptions) | ###### Returns [`HostPresentation`](#hostpresentation) #### Methods ##### available() ```ts available(): Promise<boolean>; ``` Is the desktop app reachable right now? ###### Returns `Promise`\<`boolean`\> ##### openViewer() ```ts openViewer(spaceId): Promise<void>; ``` Open the full viewer window for a Local Space. ###### Parameters | Parameter | Type | | ------ | ------ | | `spaceId` | `string` | ###### Returns `Promise`\<`void`\> ##### pin() ```ts pin(spaceId): Promise<void>; ``` Pin a Local Space as picture-in-picture on this Mac's desktop. ###### Parameters | Parameter | Type | | ------ | ------ | | `spaceId` | `string` | ###### Returns `Promise`\<`void`\> ##### streamWindow() ```ts streamWindow(input): Promise<void>; ``` Stream one window of a Local Space back to a bidirectional window here. `rcdpToken` must be read fresh: a Local Space's rcdp token rotates on every rcdpd restart, so a cached one silently stops working. `replica: true` opens an *additional* stream for the same target rather than focusing the existing one. Note the real limit: only one page per app can hardware-decode H.264, so a second concurrent stream falls back to an uncompressed codec and is visibly heavier. ###### Parameters | Parameter | Type | | ------ | ------ | | `input` | \{ `appName?`: `string`; `rcdpToken`: `string`; `replica?`: `boolean`; `spaceId`: `string`; `title?`: `string`; `windowId`: `string`; \} | | `input.appName?` | `string` | | `input.rcdpToken` | `string` | | `input.replica?` | `boolean` | | `input.spaceId` | `string` | | `input.title?` | `string` | | `input.windowId` | `string` | ###### Returns `Promise`\<`void`\> ##### unpin() ```ts unpin(spaceId): Promise<void>; ``` ###### Parameters | Parameter | Type | | ------ | ------ | | `spaceId` | `string` | ###### Returns `Promise`\<`void`\> ## Interfaces ### ControlEndpoint #### Properties | Property | Type | | ------ | ------ | | `port` | `number` | | `token` | `string` | *** ### HostPresentationOptions #### Properties | Property | Type | Description | | ------ | ------ | ------ | | `controlFile?` | `string` | Override the discovery file. Mostly for tests. | | `fetch?` | (`input`, `init?`) => `Promise`\<`Response`\> | Inject a fetch (tests, or a different agent). | ## Functions ### cuaHome() ```ts function cuaHome(home, env?): string; ``` `$CUA_HOME` when set and non-empty, else `~/.cua` (the Rust core's rule). #### Parameters | Parameter | Type | | ------ | ------ | | `home` | `string` | | `env` | `Record`\<`string`, `string` \| `undefined`\> | #### Returns `string` *** ### readControlEndpoint() ```ts function readControlEndpoint(controlFile?): Promise<ControlEndpoint>; ``` Read `{port, token}` from the app's control file. #### Parameters | Parameter | Type | | ------ | ------ | | `controlFile?` | `string` | #### Returns `Promise`\<[`ControlEndpoint`](#controlendpoint)\> --- # Rust crates The public Rust crates of the Cua SDK, how to depend on them, and what is stable. Rust hosts (CLIs, Tauri apps, services) use the crates directly instead of a binding. The crates are not on crates.io, so there is no docs.rs page: each crate has one page here with every public item. | Crate | What it is | Reference | | --- | --- | --- | | `cua-sdk` | The cua SDK: the only `#[uniffi::export]` crate of `libs/cua`. | [cua-sdk](</docs/cua-sdk/reference/rust/cua-sdk>) | | `cua-spacesd-client` | Ergonomic async client for cua-spacesd (`cua.env.v1`). | [cua-spacesd-client](</docs/cua-sdk/reference/rust/cua-spacesd-client>) | | `cua-fleet` | Fleet for the cua SDK. | [cua-fleet](</docs/cua-sdk/reference/rust/cua-fleet>) | | `cua-sandbox-core` | One daemon-agnostic `Sandbox` over three providers: | [cua-sandbox-core](</docs/cua-sdk/reference/rust/cua-sandbox-core>) | | `cua-spaces` | Cua Spaces: interactive sandboxes on any image, and everything a person or an agent does with one. cua-spacesd, when the image runs it, adds the desktop ... | [cua-spaces: registry and primitives](</docs/cua-sdk/reference/rust/cua-spaces>), [cua-spaces: client](</docs/cua-sdk/reference/rust/cua-spaces-client>), [cua-spaces: agents and MCP](</docs/cua-sdk/reference/rust/cua-spaces-agents-mcp>) | ## Depend on a crate Depend on them from git, pinned to a release tag or a commit: ```toml test="config" id="cargo-dependency" [dependencies] cua-sdk = { git = "https://github.com/trycua/cua", tag = "cua-sdk-v0.4.1" } tokio = { version = "1", features = ["full"] } ``` Inside this repository, use a path dependency (`path = "libs/cua/crates/cua-sdk"`). The crates build with the toolchain in `libs/cua/rust-toolchain.toml`. ## Stability - All crates release in lockstep with the SDK version under one `cua-sdk-v<version>` tag. Mix crates from one tag only. - Stable: `cua-sdk` and the `cua-spacesd-client` client. Before 1.0 a minor release may break them, with a deprecated alias kept for at least one minor release where possible (for example `EnvClient`, now `SpacesdClient`). Patch releases never do. - Unstable: `cua-fleet`, `cua-sandbox-core` and `cua-spaces` follow the Fleet and Spaces services and can change in any minor release. Items named `testing`, `conformance` or `coverage` are test support, not API. - Not API: items hidden from these pages (`#[doc(hidden)]`), the UniFFI scaffolding (`uniffi_*`, `ffi_*`), and cargo features other than the defaults. Other workspace crates (`cua-proto`, `cua-daemon`, `cua-vmm`, `cua-image`, the media crates, `cua-cli`) are implementation details. The Cua Spaces crates (`cua-spaces-ext`, `cua-spaces-ffi`, `cua-spaces-cli`, `cua-spaces-app-core`, `cua-teleport`, `cua-keyvault`, `cua-volume`) are source-available under FSL-1.1-MIT and not part of the SDK; the Swift API of the app export is on the [Cua Spaces app export pages](</docs/cua-sdk/reference/spaces/app-core>). the wire contract is the [protocol reference](</docs/cua-sdk/reference/protocol>). Each page shows item summaries and declarations; `cargo doc --open` in `libs/cua` has the full comments. --- # cua-sdk The Rust host API: the Cua object and everything the language bindings export, plus the Rust-only items. Public API of the `cua-sdk` crate (`cua_sdk`). Summaries and declarations only (`cargo doc -p cua-sdk --open` in `libs/cua` has the full comments); see [Rust crates](</docs/cua-sdk/reference/rust>) for stability. The cua SDK: the only `#[uniffi::export]` crate of `libs/cua`. ## Exported API These are the objects, records, enums and functions the language bindings export, documented once in the [Cua SDK reference](</docs/cua-sdk/reference>), whose Rust tabs show each signature. Objects are `Arc<T>` handles whose methods are `async fn` returning `Result<_, CuaError>`, records are structs with public fields, and names are snake_case. [`agent_event_category`](</docs/cua-sdk/reference/agents#agent_event_category>), [`agent_harnesses`](</docs/cua-sdk/reference/agents#agent_harnesses>), [`AgentActionReport`](</docs/cua-sdk/reference/spaces/agents#agentactionreport-record>), [`AgentArtifact`](</docs/cua-sdk/reference/agents#agentartifact-record>), [`AgentDelivery`](</docs/cua-sdk/reference/spaces/persistent-agents#agentdelivery-record>), [`AgentEvent`](</docs/cua-sdk/reference/agents#agentevent-record>), [`AgentEventPage`](</docs/cua-sdk/reference/agents#agenteventpage-record>), [`AgentFile`](</docs/cua-sdk/reference/agents#agentfile-record>), [`AgentInfo`](</docs/cua-sdk/reference/agent-setup#agentinfo-record>), [`AgentMcpServer`](</docs/cua-sdk/reference/agent-setup#agentmcpserver-record>), [`AgentPauseReport`](</docs/cua-sdk/reference/spaces/persistent-agents#agentpausereport-record>), [`AgentResumeReport`](</docs/cua-sdk/reference/spaces/persistent-agents#agentresumereport-record>), [`AgentRun`](</docs/cua-sdk/reference/agents#agentrun>), [`AgentRunInfo`](</docs/cua-sdk/reference/agents#agentruninfo-record>), [`AgentRunMcpServer`](</docs/cua-sdk/reference/agents#agentrunmcpserver-record>), [`AgentRunOptions`](</docs/cua-sdk/reference/agents#agentrunoptions-record>), [`AgentRunResult`](</docs/cua-sdk/reference/agents#agentrunresult-record>), [`AgentRunStatus`](</docs/cua-sdk/reference/spaces/agents#agentrunstatus-record>), [`Agents`](</docs/cua-sdk/reference/agents#agents>), [`AgentSetup`](</docs/cua-sdk/reference/agent-setup#agentsetup>), [`AgentSetupChange`](</docs/cua-sdk/reference/agent-setup#agentsetupchange-enum>), [`AgentSetupOutcome`](</docs/cua-sdk/reference/agent-setup#agentsetupoutcome-record>), [`AgentSetupTarget`](</docs/cua-sdk/reference/agent-setup#agentsetuptarget-enum>), [`AgentStartReport`](</docs/cua-sdk/reference/spaces/agents#agentstartreport-record>), [`AgentTranscript`](</docs/cua-sdk/reference/agents#agenttranscript>), [`AgentTranscriptItem`](</docs/cua-sdk/reference/agents#agenttranscriptitem-record>), [`ambiguous_sandbox_candidates`](</docs/cua-sdk/reference/sandbox#ambiguous_sandbox_candidates>), [`AudioPacket`](</docs/cua-sdk/reference/spacesd/media#audiopacket-record>), [`AudioSink`](</docs/cua-sdk/reference/spacesd/media#audiosink>), [`Auth`](</docs/cua-sdk/reference/auth#auth>), [`AuthIdentity`](</docs/cua-sdk/reference/auth#authidentity-record>), [`AuthStatus`](</docs/cua-sdk/reference/auth#authstatus-record>), [`BuildFile`](</docs/cua-sdk/reference/image#buildfile-record>), [`BundledSkill`](</docs/cua-sdk/reference/agent-setup#bundledskill-record>), [`canonical_image`](</docs/cua-sdk/reference/image#canonical_image>), [`canonical_image_tier`](</docs/cua-sdk/reference/image#canonical_image_tier>), [`check_placement`](</docs/cua-sdk/reference/cua#check_placement>), [`CloudCheck`](</docs/cua-sdk/reference/spaces/cloud#cloudcheck-record>), [`CloudConnected`](</docs/cua-sdk/reference/spaces/cloud#cloudconnected-record>), [`CloudCredentials`](</docs/cua-sdk/reference/spaces/cloud#cloudcredentials-record>), [`CloudDisconnected`](</docs/cua-sdk/reference/spaces/cloud#clouddisconnected-record>), [`CloudKind`](</docs/cua-sdk/reference/spaces/cloud#cloudkind-record>), [`CloudOptions`](</docs/cua-sdk/reference/sandbox/options#cloudoptions-record>), [`CloudProvider`](</docs/cua-sdk/reference/spaces/cloud#cloudprovider-record>), [`CloudResource`](</docs/cua-sdk/reference/spaces/cloud#cloudresource-record>), [`CloudStatus`](</docs/cua-sdk/reference/spaces/cloud#cloudstatus-record>), [`CloudSweepItem`](</docs/cua-sdk/reference/spaces/cloud#cloudsweepitem-record>), [`CloudSweepReport`](</docs/cua-sdk/reference/spaces/cloud#cloudsweepreport-record>), [`CloudTarget`](</docs/cua-sdk/reference/spaces/cloud#cloudtarget-record>), [`CloudTestReport`](</docs/cua-sdk/reference/spaces/cloud#cloudtestreport-record>), [`ComputerGrant`](</docs/cua-sdk/reference/spaces/persistent-agents#computergrant-record>), [`config_get`](</docs/cua-sdk/reference/cua#config_get>), [`config_list`](</docs/cua-sdk/reference/cua#config_list>), [`config_path`](</docs/cua-sdk/reference/cua#config_path>), [`config_set`](</docs/cua-sdk/reference/cua#config_set>), [`config_unset`](</docs/cua-sdk/reference/cua#config_unset>), [`ConfigEntry`](</docs/cua-sdk/reference/cua#configentry-record>), [`Container`](</docs/cua-sdk/reference/sandbox/options#container-record>), [`contrib_providers_built`](</docs/cua-sdk/reference/sandbox#contrib_providers_built>), [`Cua`](</docs/cua-sdk/reference/cua#cua>), [`CuaConfig`](</docs/cua-sdk/reference/cua#cuaconfig-record>), [`CuaInfo`](</docs/cua-sdk/reference/cua#cuainfo-record>), [`CuaMode`](</docs/cua-sdk/reference/cua#cuamode-enum>), [`CursorArt`](</docs/cua-sdk/reference/spaces/streams#cursorart-record>), [`DeviceEnrollment`](</docs/cua-sdk/reference/spaces/devices#deviceenrollment-record>), [`Devices`](</docs/cua-sdk/reference/spaces/devices#devices>), [`DevicesSnapshot`](</docs/cua-sdk/reference/spaces/devices#devicessnapshot-record>), [`DoctorReport`](</docs/cua-sdk/reference/local#doctorreport-record>), [`DriveAccessRequest`](</docs/cua-sdk/reference/spaces/volume#driveaccessrequest-record>), [`DriveAudit`](</docs/cua-sdk/reference/spaces/volume#driveaudit-record>), [`DriveAuditEvent`](</docs/cua-sdk/reference/spaces/volume#driveauditevent-record>), [`DriveCacheStats`](</docs/cua-sdk/reference/spaces/volume-sync#drivecachestats-record>), [`DriveConflict`](</docs/cua-sdk/reference/spaces/volume-sync#driveconflict-record>), [`DriveDevice`](</docs/cua-sdk/reference/spaces/volume-sync#drivedevice-record>), [`DriveEntry`](</docs/cua-sdk/reference/spaces/volume#driveentry-record>), [`DriveFile`](</docs/cua-sdk/reference/spaces/volume#drivefile-record>), [`DriveFileSync`](</docs/cua-sdk/reference/spaces/volume#drivefilesync-record>), [`DriveGrant`](</docs/cua-sdk/reference/spaces/volume#drivegrant-record>), [`DriveListing`](</docs/cua-sdk/reference/spaces/volume#drivelisting-record>), [`DriveMountStatus`](</docs/cua-sdk/reference/spaces/volume-sync#drivemountstatus-record>), [`DriveObject`](</docs/cua-sdk/reference/spaces/volume#driveobject-record>), [`DrivePendingUpload`](</docs/cua-sdk/reference/spaces/volume-sync#drivependingupload-record>), [`DriveS3Settings`](</docs/cua-sdk/reference/spaces/volume-sync#drives3settings-record>), [`DriveSpaceVolume`](</docs/cua-sdk/reference/spaces/volume-sync#drivespacevolume-record>), [`DriveStorage`](</docs/cua-sdk/reference/spaces/volume-sync#drivestorage-record>), [`DriveStorageCheck`](</docs/cua-sdk/reference/spaces/volume-sync#drivestoragecheck-record>), [`DriveStorageUpdate`](</docs/cua-sdk/reference/spaces/volume-sync#drivestorageupdate-record>), [`DriveSyncEvent`](</docs/cua-sdk/reference/spaces/volume-sync#drivesyncevent-record>), [`DriveSyncEvents`](</docs/cua-sdk/reference/spaces/volume-sync#drivesyncevents-record>), [`DriveSyncStatus`](</docs/cua-sdk/reference/spaces/volume-sync#drivesyncstatus-record>), [`DriveVersion`](</docs/cua-sdk/reference/spaces/volume#driveversion-record>), [`DriveView`](</docs/cua-sdk/reference/spaces/volume#driveview-record>), [`Fleet`](</docs/cua-sdk/reference/errors#fleet>), [`fleet_check_runtime`](</docs/cua-sdk/reference/fleet/images#fleet_check_runtime>), [`fleet_generate_claim_token`](</docs/cua-sdk/reference/fleet#fleet_generate_claim_token>), [`fleet_image_variant`](</docs/cua-sdk/reference/fleet/images#fleet_image_variant>), [`fleet_resolve_runtime`](</docs/cua-sdk/reference/fleet/images#fleet_resolve_runtime>), [`fleet_size_limits`](</docs/cua-sdk/reference/fleet/images#fleet_size_limits>), [`FleetBillingCard`](</docs/cua-sdk/reference/fleet/images#fleetbillingcard-record>), [`FleetBillingCredit`](</docs/cua-sdk/reference/fleet/images#fleetbillingcredit-record>), [`FleetBillingStatus`](</docs/cua-sdk/reference/fleet/images#fleetbillingstatus-record>), [`FleetClaim`](</docs/cua-sdk/reference/fleet/claims#fleetclaim-record>), [`FleetClaimOptions`](</docs/cua-sdk/reference/fleet/claims#fleetclaimoptions-record>), [`FleetGcReport`](</docs/cua-sdk/reference/fleet#fleetgcreport-record>), [`FleetManagedPool`](</docs/cua-sdk/reference/fleet#fleetmanagedpool-record>), [`FleetPool`](</docs/cua-sdk/reference/fleet#fleetpool-record>), [`FleetPoolExport`](</docs/cua-sdk/reference/fleet#fleetpoolexport-record>), [`FleetPools`](</docs/cua-sdk/reference/fleet#fleetpools>), [`FleetPoolSpec`](</docs/cua-sdk/reference/fleet#fleetpoolspec-record>), [`FleetSandbox`](</docs/cua-sdk/reference/fleet/claims#fleetsandbox-record>), [`FleetSettings`](</docs/cua-sdk/reference/cua#fleetsettings-record>), [`FleetSignedUrl`](</docs/cua-sdk/reference/fleet/claims#fleetsignedurl-record>), [`FleetSizeLimits`](</docs/cua-sdk/reference/fleet/images#fleetsizelimits-record>), [`FleetUsagePricing`](</docs/cua-sdk/reference/fleet/images#fleetusagepricing-record>), [`FrameSink`](</docs/cua-sdk/reference/spacesd/media#framesink>), [`GpuOption`](</docs/cua-sdk/reference/spaces/create#gpuoption-record>), [`GpuSupport`](</docs/cua-sdk/reference/spaces/create#gpusupport-record>), [`GuestDisplay`](</docs/cua-sdk/reference/sandbox/without-spacesd#guestdisplay>), [`HomeTransfer`](</docs/cua-sdk/reference/spaces/persistent-agents#hometransfer-record>), [`Host`](</docs/cua-sdk/reference/spaces/host#host>), [`HostAccessRecord`](</docs/cua-sdk/reference/spaces/host#hostaccessrecord-record>), [`HostPermission`](</docs/cua-sdk/reference/spaces/host#hostpermission-record>), [`HostProvidedSpace`](</docs/cua-sdk/reference/spaces/host#hostprovidedspace-record>), [`HostSettingsChange`](</docs/cua-sdk/reference/spaces/host#hostsettingschange-record>), [`HostSetupOptions`](</docs/cua-sdk/reference/spaces/host#hostsetupoptions-record>), [`HostSpacesAuditRecord`](</docs/cua-sdk/reference/spaces/host#hostspacesauditrecord-record>), [`HostStatus`](</docs/cua-sdk/reference/spaces/host#hoststatus-record>), [`HttpHeader`](</docs/cua-sdk/reference/services#httpheader-record>), [`HttpResponse`](</docs/cua-sdk/reference/services#httpresponse-record>), [`image_alias`](</docs/cua-sdk/reference/image#image_alias>), [`ImageBuild`](</docs/cua-sdk/reference/image#imagebuild-record>), [`ImageInfo`](</docs/cua-sdk/reference/image#imageinfo-record>), [`ImageLayer`](</docs/cua-sdk/reference/image#imagelayer-enum>), [`Local`](</docs/cua-sdk/reference/local#local>), [`LocalImage`](</docs/cua-sdk/reference/local#localimage-record>), [`LocalStorage`](</docs/cua-sdk/reference/local#localstorage-record>), [`LocationInfo`](</docs/cua-sdk/reference/cua#locationinfo-record>), [`locations`](</docs/cua-sdk/reference/cua#locations>), [`LoginAttempt`](</docs/cua-sdk/reference/auth#loginattempt>), [`LoginMethod`](</docs/cua-sdk/reference/auth#loginmethod-enum>), [`may_have_fleet_session`](</docs/cua-sdk/reference/auth#may_have_fleet_session>), [`mcp_connect`](</docs/cua-sdk/reference/services#mcp_connect>), [`mcp_connect_url`](</docs/cua-sdk/reference/services#mcp_connect_url>), [`McpClient`](</docs/cua-sdk/reference/services#mcpclient>), [`McpConfig`](</docs/cua-sdk/reference/services#mcpconfig-record>), [`MediaBridge`](</docs/cua-sdk/reference/spacesd/media#mediabridge-record>), [`MediaEvent`](</docs/cua-sdk/reference/spacesd/media#mediaevent-record>), [`MediaSession`](</docs/cua-sdk/reference/spacesd/media#mediasession>), [`MediaStats`](</docs/cua-sdk/reference/spacesd/media#mediastats-record>), [`normalize_image`](</docs/cua-sdk/reference/image#normalize_image>), [`NotificationInfo`](</docs/cua-sdk/reference/spaces/persistent-agents#notificationinfo-record>), [`omarchy_image`](</docs/cua-sdk/reference/image#omarchy_image>), [`Overlay`](</docs/cua-sdk/reference/sandbox#overlay-record>), [`OverlayResult`](</docs/cua-sdk/reference/sandbox#overlayresult-record>), [`parse_sandbox_ref`](</docs/cua-sdk/reference/sandbox#parse_sandbox_ref>), [`PersistentAgentInfo`](</docs/cua-sdk/reference/spaces/persistent-agents#persistentagentinfo-record>), [`PersistentAgentOptions`](</docs/cua-sdk/reference/spaces/persistent-agents#persistentagentoptions-record>), [`PoolOptions`](</docs/cua-sdk/reference/fleet#pooloptions-record>), [`PortForward`](</docs/cua-sdk/reference/services#portforward>), [`presence_color`](</docs/cua-sdk/reference/spaces/streams#presence_color>), [`presence_conformance_json`](</docs/cua-sdk/reference/spaces/streams#presence_conformance_json>), [`presence_cursor_art`](</docs/cua-sdk/reference/spaces/streams#presence_cursor_art>), [`presence_cursor_art_all`](</docs/cua-sdk/reference/spaces/streams#presence_cursor_art_all>), [`presence_cursor_art_svg`](</docs/cua-sdk/reference/spaces/streams#presence_cursor_art_svg>), [`presence_now_ms`](</docs/cua-sdk/reference/spaces/streams#presence_now_ms>), [`presence_text_color`](</docs/cua-sdk/reference/spaces/streams#presence_text_color>), [`PresenceCursor`](</docs/cua-sdk/reference/spaces/streams#presencecursor-record>), [`PresenceDrawable`](</docs/cua-sdk/reference/spaces/streams#presencedrawable-record>), [`PresenceEvent`](</docs/cua-sdk/reference/spaces/streams#presenceevent-record>), [`PresenceIdentity`](</docs/cua-sdk/reference/spaces/streams#presenceidentity-record>), [`PresenceMember`](</docs/cua-sdk/reference/spaces/streams#presencemember-record>), [`PresenceParticipant`](</docs/cua-sdk/reference/spaces/streams#presenceparticipant-record>), [`PresencePoint`](</docs/cua-sdk/reference/spaces/streams#presencepoint-record>), [`PresenceView`](</docs/cua-sdk/reference/spaces/streams#presenceview>), [`PublicUrl`](</docs/cua-sdk/reference/services#publicurl-record>), [`qualify_sandbox_ref`](</docs/cua-sdk/reference/sandbox#qualify_sandbox_ref>), [`ReadinessProbe`](</docs/cua-sdk/reference/sandbox/options#readinessprobe-record>), [`RegistrySecret`](</docs/cua-sdk/reference/image#registrysecret-enum>), [`Relay`](</docs/cua-sdk/reference/spaces/host#relay>), [`RelayAuditEvent`](</docs/cua-sdk/reference/spaces/devices#relayauditevent-record>), [`RelayClientInfo`](</docs/cua-sdk/reference/spaces/host#relayclientinfo-record>), [`RelayDevice`](</docs/cua-sdk/reference/spaces/devices#relaydevice-record>), [`RelayMachine`](</docs/cua-sdk/reference/spaces/host#relaymachine-record>), [`ReplayMode`](</docs/cua-sdk/reference/spacesd/shell#replaymode-enum>), [`resolve_image`](</docs/cua-sdk/reference/image#resolve_image>), [`resolve_image_with_secret`](</docs/cua-sdk/reference/image#resolve_image_with_secret>), [`ResolvedImage`](</docs/cua-sdk/reference/image#resolvedimage-record>), [`RoutineInfo`](</docs/cua-sdk/reference/spaces/persistent-agents#routineinfo-record>), [`RuntimeCheck`](</docs/cua-sdk/reference/local#runtimecheck-record>), [`RuntimeCheckStatus`](</docs/cua-sdk/reference/local#runtimecheckstatus-enum>), [`Sandbox`](</docs/cua-sdk/reference/sandbox#sandbox>), [`sandbox_locations`](</docs/cua-sdk/reference/sandbox#sandbox_locations>), [`SandboxCreateOptions`](</docs/cua-sdk/reference/sandbox/options#sandboxcreateoptions-record>), [`Sandboxes`](</docs/cua-sdk/reference/sandbox#sandboxes>), [`SandboxInfo`](</docs/cua-sdk/reference/sandbox#sandboxinfo-record>), [`SandboxListing`](</docs/cua-sdk/reference/sandbox#sandboxlisting-record>), [`SandboxPhase`](</docs/cua-sdk/reference/sandbox#sandboxphase-enum>), [`SandboxRefParts`](</docs/cua-sdk/reference/sandbox#sandboxrefparts-record>), [`SandboxSpec`](</docs/cua-sdk/reference/fleet#sandboxspec-record>), [`SandboxStatus`](</docs/cua-sdk/reference/sandbox#sandboxstatus-enum>), [`Service`](</docs/cua-sdk/reference/services#service>), [`ServiceEndpoint`](</docs/cua-sdk/reference/services#serviceendpoint-record>), [`SetupStep`](</docs/cua-sdk/reference/local#setupstep-record>), [`SiteLoginOptions`](</docs/cua-sdk/reference/spaces/space#siteloginoptions-record>), [`SiteLoginReport`](</docs/cua-sdk/reference/spaces/space#siteloginreport-record>), [`Space`](</docs/cua-sdk/reference/spaces/space#space>), [`SpaceAgentOptions`](</docs/cua-sdk/reference/spaces/agents#spaceagentoptions-record>), [`SpaceAppIcon`](</docs/cua-sdk/reference/spaces/windows#spaceappicon-record>), [`SpaceAppIconRequest`](</docs/cua-sdk/reference/spaces/windows#spaceappiconrequest-record>), [`SpaceBashResult`](</docs/cua-sdk/reference/spaces/space#spacebashresult-record>), [`SpaceCancelOutcome`](</docs/cua-sdk/reference/spaces/create#spacecanceloutcome-record>), [`SpaceCreateListener`](</docs/cua-sdk/reference/spaces/create#spacecreatelistener>), [`SpaceCreateOptions`](</docs/cua-sdk/reference/spaces/create#spacecreateoptions-record>), [`SpaceCreateProgress`](</docs/cua-sdk/reference/spaces/create#spacecreateprogress-record>), [`SpaceCreateResult`](</docs/cua-sdk/reference/spaces/create#spacecreateresult-record>), [`SpaceDisplay`](</docs/cua-sdk/reference/spaces/windows#spacedisplay-record>), [`SpaceHotspotStatus`](</docs/cua-sdk/reference/spaces/space#spacehotspotstatus-record>), [`SpaceInfo`](</docs/cua-sdk/reference/spaces#spaceinfo-record>), [`SpacePowerReport`](</docs/cua-sdk/reference/spaces#spacepowerreport-record>), [`SpacePresence`](</docs/cua-sdk/reference/spaces/streams#spacepresence>), [`SpaceRelayRegistration`](</docs/cua-sdk/reference/spaces/space#spacerelayregistration-record>), [`Spaces`](</docs/cua-sdk/reference/spaces#spaces>), [`spaces_tool_methods`](</docs/cua-sdk/reference/spaces#spaces_tool_methods>), [`SpacesdClient`](</docs/cua-sdk/reference/spacesd#spacesdclient>), [`SpacesdHttpHeader`](</docs/cua-sdk/reference/spacesd#spacesdhttpheader-record>), [`SpacesdHttpRequest`](</docs/cua-sdk/reference/spacesd#spacesdhttprequest-record>), [`SpacesdHttpResponse`](</docs/cua-sdk/reference/spacesd#spacesdhttpresponse-record>), [`SpacesdProcess`](</docs/cua-sdk/reference/spacesd/shell#spacesdprocess>), [`SpaceSendFileOptions`](</docs/cua-sdk/reference/spaces/space#spacesendfileoptions-record>), [`SpaceSendFileReport`](</docs/cua-sdk/reference/spaces/space#spacesendfilereport-record>), [`SpaceSentFile`](</docs/cua-sdk/reference/spaces/space#spacesentfile-record>), [`SpaceShareEntry`](</docs/cua-sdk/reference/spaces/space#spaceshareentry-record>), [`SpaceShares`](</docs/cua-sdk/reference/spaces/space#spaceshares-record>), [`SpacesHost`](</docs/cua-sdk/reference/spaces/space#spaceshost-record>), [`SpacesHostLimit`](</docs/cua-sdk/reference/spaces/space#spaceshostlimit-record>), [`SpacesToolMethod`](</docs/cua-sdk/reference/spaces#spacestoolmethod-record>), [`SpaceStreamOptions`](</docs/cua-sdk/reference/spaces/streams#spacestreamoptions-record>), [`SpaceStreamSession`](</docs/cua-sdk/reference/spaces/streams#spacestreamsession>), [`SpaceStreamStats`](</docs/cua-sdk/reference/spaces/streams#spacestreamstats-record>), [`SpaceStreamTicket`](</docs/cua-sdk/reference/spaces/streams#spacestreamticket-record>), [`SpaceToolInfo`](</docs/cua-sdk/reference/spaces/space#spacetoolinfo-record>), [`SpaceToolResult`](</docs/cua-sdk/reference/spaces/space#spacetoolresult-record>), [`SpaceTransferReport`](</docs/cua-sdk/reference/spaces/space#spacetransferreport-record>), [`SpaceUsage`](</docs/cua-sdk/reference/spaces/windows#spaceusage-record>), [`SpaceWindow`](</docs/cua-sdk/reference/spaces/space#spacewindow-record>), [`SpaceWriteReport`](</docs/cua-sdk/reference/spaces/space#spacewritereport-record>), [`StorageVolume`](</docs/cua-sdk/reference/local#storagevolume-record>), [`StreamStatsReport`](</docs/cua-sdk/reference/telemetry#streamstatsreport-record>), [`telemetry_acknowledge_notice`](</docs/cua-sdk/reference/telemetry#telemetry_acknowledge_notice>), [`telemetry_flush`](</docs/cua-sdk/reference/telemetry#telemetry_flush>), [`telemetry_notice_text`](</docs/cua-sdk/reference/telemetry#telemetry_notice_text>), [`telemetry_record_bench_run`](</docs/cua-sdk/reference/telemetry#telemetry_record_bench_run>), [`telemetry_record_feature`](</docs/cua-sdk/reference/telemetry#telemetry_record_feature>), [`telemetry_record_onboarding_step`](</docs/cua-sdk/reference/telemetry#telemetry_record_onboarding_step>), [`telemetry_record_stream_stats`](</docs/cua-sdk/reference/telemetry#telemetry_record_stream_stats>), [`telemetry_reset_id`](</docs/cua-sdk/reference/telemetry#telemetry_reset_id>), [`telemetry_schema_json`](</docs/cua-sdk/reference/telemetry#telemetry_schema_json>), [`telemetry_set_enabled`](</docs/cua-sdk/reference/telemetry#telemetry_set_enabled>), [`telemetry_set_surface`](</docs/cua-sdk/reference/telemetry#telemetry_set_surface>), [`telemetry_show_last`](</docs/cua-sdk/reference/telemetry#telemetry_show_last>), [`telemetry_status`](</docs/cua-sdk/reference/telemetry#telemetry_status>), [`telemetry_use_app_notice`](</docs/cua-sdk/reference/telemetry#telemetry_use_app_notice>), [`TelemetryStatus`](</docs/cua-sdk/reference/telemetry#telemetrystatus-record>), [`TeleportApprover`](</docs/cua-sdk/reference/spaces/teleport#teleportapprover>), [`TeleportDecision`](</docs/cua-sdk/reference/spaces/teleport#teleportdecision-record>), [`TeleportItem`](</docs/cua-sdk/reference/spaces/teleport#teleportitem-record>), [`TeleportManifest`](</docs/cua-sdk/reference/spaces/teleport#teleportmanifest-record>), [`TeleportReceipt`](</docs/cua-sdk/reference/spaces/teleport#teleportreceipt-record>), [`VideoFrame`](</docs/cua-sdk/reference/spacesd/media#videoframe-record>), [`ViewerLink`](</docs/cua-sdk/reference/sandbox#viewerlink-record>), [`ViewerOptions`](</docs/cua-sdk/reference/sandbox#vieweroptions-record>). ## Types ### `CuaError` Every error the SDK raises. ```rust pub enum CuaError { InvalidArgument(String), InvalidPlacement(String), NotFound(String), ProviderNotConfigured(String), Unsupported(String), SpacesdNotAvailable(String), Timeout(String), Fleet(String), FleetAdmissionDenied(String), CloudCreditExhausted(String), Runtime(String), Env(String), Http(String), Unauthenticated(String), PermissionDenied(String), Transport(String), DaemonNotRunning(String), Closed(String), CapabilityMissing(String), HostCapabilityMissing(String), TeleportRefused(String), PoolSpecMismatch(String), ClaimSecretsNotDelivered(String), Internal(String), AmbiguousSandbox(String), InsufficientDisk(String), ImageNotPublished(String), Cancelled(String), Cloud(String), } impl CuaError { fn variant(&self) -> &'static str; fn doc_url(&self) -> String; } ``` ### `ExitInfo` How a process ended. ```rust pub struct ExitInfo { pub code: Option<i32>, pub signal: Option<String>, pub timed_out: bool, pub error: Option<String>, pub success: bool, } ``` ### `FileEntry` A file or directory in the guest. ```rust pub struct FileEntry { pub name: String, pub path: String, pub kind: String, pub size: u64, pub mode: u32, pub modified_ms: Option<i64>, pub symlink_target: Option<String>, } ``` ### `ImageFormat` Screenshot encoding. ```rust pub enum ImageFormat { Png, Jpeg, Webp, } ``` ### `MediaOpenOptions` Media session options. ```rust pub struct MediaOpenOptions { pub display: Option<String>, pub window_handle: Option<String>, pub max_fps: u32, pub max_dimension: u32, pub audio: bool, pub disable_video: bool, pub request_json: Option<String>, } ``` ### `Point` A point. ```rust pub struct Point { pub x: f64, pub y: f64, } ``` ### `ProcessEvent` One output chunk or the exit. ```rust pub struct ProcessEvent { pub kind: ProcessEventKind, pub offset: u64, pub data: Vec<u8>, pub exit: Option<ExitInfo>, } ``` ### `ProcessEventKind` Kind of a process event. ```rust pub enum ProcessEventKind { Stdout, Stderr, Pty, Exit, } ``` ### `ProcessOutput` Collected output of a finished process. ```rust pub struct ProcessOutput { pub exit: ExitInfo, pub stdout: Vec<u8>, pub stderr: Vec<u8>, pub pty: Vec<u8>, } ``` ### `PtySize` A PTY size. ```rust pub struct PtySize { pub cols: u32, pub rows: u32, } ``` ### `Screenshot` A captured screenshot. ```rust pub struct Screenshot { pub image: Vec<u8>, pub format: ImageFormat, pub width: u32, pub height: u32, pub scale: f64, pub screenshot_id: String, } ``` ### `ScreenshotOptions` Screenshot options. ```rust pub struct ScreenshotOptions { pub display: Option<String>, pub format: Option<ImageFormat>, pub quality: Option<u32>, pub max_dimension: Option<u32>, pub include_cursor: bool, } ``` ### `SpacesdCapabilities` `SystemService.GetCapabilities`, summarized. ```rust pub struct SpacesdCapabilities { pub version: String, pub protocol_version: u32, pub protocol_revision: u32, pub os_family: String, pub os_name: String, pub os_version: String, pub arch: String, pub hostname: String, pub initialized: bool, pub features: Vec<SpacesdFeature>, pub json: String, } ``` ### `SpacesdCommand` A command to run in the guest. ```rust pub struct SpacesdCommand { pub program: String, pub args: Vec<String>, pub env: HashMap<String, String>, pub cwd: Option<String>, pub user: Option<String>, pub timeout_ms: Option<u32>, pub tag: Option<String>, pub stdin: bool, pub pty: Option<PtySize>, } ``` ### `SpacesdFeature` One capability flag. ```rust pub struct SpacesdFeature { pub name: String, pub supported: bool, pub limitation: Option<String>, pub attributes: HashMap<String, String>, } ``` ### `SpaceThumbnail` A Space's latest thumbnail, from the cache every client ... ```rust pub struct SpaceThumbnail { pub image: Vec<u8>, pub format: ImageFormat, pub width: u32, pub height: u32, pub captured_at_ms: u64, } ``` ### `TransferResult` Result of an upload or download. ```rust pub struct TransferResult { pub size: u64, pub sha256: String, pub resumes: u32, } ``` ### `UploadOptions` Upload options. ```rust pub struct UploadOptions { pub create_new: bool, pub append: bool, pub permissions: u32, pub create_parents: bool, } ``` ## Functions ```rust /// Version of the SDK library. pub fn cua_sdk_version() -> String /// A link to the errors-reference entry of ... pub fn error_doc_url(variant: String) -> String ``` ## Constants ```rust /// The errors reference. pub const ERRORS_DOC_URL: &str = "https://cua.ai/docs/cua-sdk/reference/errors"; /// Server-streaming RPCs reachable through ... pub const JSON_STREAM_METHODS: &[&str] = _; /// Unary RPCs reachable through ... pub const JSON_UNARY_METHODS: &[&str] = _; /// Where overlay records live in the guest. pub const OVERLAY_RECORD_DIR: &str = "/var/lib/cua/overlays"; /// SDK methods that have no contract tool ... pub const SPACES_SDK_ONLY_METHODS: &[&str] = _; /// Every Spaces contract tool and the SDK ... pub const SPACES_TOOL_METHODS: &[(&str, &str)] = _; /// RPCs exposed only through typed methods ... pub const TYPED_ONLY_METHODS: &[&str] = _; /// The released cua SDK version ... pub const VERSION: &str = "0.4.1"; ``` ## Type aliases ```rust /// Result alias. pub type Result<T, E = CuaError> = Result<T, E>; ``` --- # cua-spacesd-client The async cua-spacesd client crate (native gRPC or gRPC-Web). Public API of the `cua-spacesd-client` crate (`cua_spacesd_client`). Summaries and declarations only (`cargo doc -p cua-spacesd-client --open` in `libs/cua` has the full comments); see [Rust crates](</docs/cua-sdk/reference/rust>) for stability. Ergonomic async client for cua-spacesd (`cua.env.v1`). ## Modules - `diagnose`: `SystemService.Diagnose`: client calls, and the ... - `error`: Typed errors. - `expect`: `--expect COMPONENT=WANT`: fail a doctor report unless ... - `manifest`: `/etc/cua-image/manifest.json`: what an image claims. - `transport`: Channel construction: native gRPC (HTTP/2, h2c or TLS) ... - `tunnel`: Client side of `TunnelService.Forward`: a loopback TCP ... ## Types ### `BearerProvider` Supplies the Fleet gateway bearer (for example ... ```rust pub trait BearerProvider: Send + Sync { fn bearer(&self, force_refresh: bool) -> Pin<Box<dyn Future<Output = Result<String, BoxError>> + Send + '_>>; fn extra_headers(&self) -> Pin<Box<dyn Future<Output = Result<ExtraHeaders, BoxError>> + Send + '_>>; } ``` ### `ChannelError` Transport-level failure of a `GrpcChannel` call. ```rust pub struct ChannelError(pub BoxError); ``` ### `Command` A command to run. ```rust pub struct Command { pub program: String, pub args: Vec<String>, pub env: BTreeMap<String, String>, pub cwd: Option<String>, pub user: Option<String>, pub timeout: Option<Duration>, pub tag: Option<String>, pub stdin: bool, pub pty: Option<(u32, u32)>, pub scrollback_bytes: u64, pub kill_on_disconnect: bool, pub keepalive: Option<Duration>, } impl Command { fn new(program: impl Into<String>) -> Self; fn shell(line: impl Into<String>) -> Self; fn arg(self, a: impl Into<String>) -> Self; fn args<I, S>(self, args: I) -> Self where I: IntoIterator<Item = S>, S: Into<String>; fn env(self, k: impl Into<String>, v: impl Into<String>) -> Self; fn cwd(self, cwd: impl Into<String>) -> Self; fn user(self, user: impl Into<String>) -> Self; fn timeout(self, t: Duration) -> Self; fn tag(self, tag: impl Into<String>) -> Self; fn stdin(self, on: bool) -> Self; fn pty(self, cols: u32, rows: u32) -> Self; fn keepalive(self, interval: Duration) -> Self; fn scrollback(self, bytes: u64) -> Self; } ``` ### `ConnectOptions` Options for `SpacesdClient::connect`. ```rust pub struct ConnectOptions { pub endpoint: Endpoint, pub token: Option<String>, pub principal: Option<Principal>, pub transport: TransportPreference, pub gateway: Option<GatewayAuth>, pub connect_timeout: Duration, pub probe_timeout: Duration, pub keepalive: Keepalive, pub retry: RetryPolicy, pub probe: bool, } impl ConnectOptions { fn new(endpoint: Endpoint) -> Self; fn parse(endpoint: &str) -> Result<Self>; fn token(self, token: impl Into<String>) -> Self; fn principal(self, principal: Principal) -> Self; fn transport(self, transport: TransportPreference) -> Self; fn fleet_gateway(self, bearer: Arc<dyn BearerProvider>, claim: Option<String>) -> Self; fn retry(self, retry: RetryPolicy) -> Self; fn keepalive(self, keepalive: Keepalive) -> Self; fn probe_timeout(self, timeout: Duration) -> Self; fn probe(self, probe: bool) -> Self; } ``` ### `DesktopReadiness` Desktop readiness, from SpacesdClie ... ```rust pub struct DesktopReadiness { pub ready: bool, pub detail: String, pub reported: bool, } ``` ### `diagnose::Artifact` Evidence produced by a check. ```rust pub struct Artifact { pub name: String, pub media_type: String, pub data: Vec<u8>, pub path: String, } ``` ### `diagnose::Check` The result of one check. ```rust pub struct Check { pub id: String, pub group: String, pub status: Status, pub severity: Severity, pub claimed_by: Vec<String>, pub message: String, pub remediation: String, pub duration_ms: u32, pub facts: BTreeMap<String, String>, pub artifacts: Vec<Artifact>, } impl Check { fn new(id: impl Into<String>, status: Status, message: impl Into<String>) -> Self; fn fact(self, key: impl Into<String>, value: impl ToString) -> Self; fn fix(self, remediation: impl Into<String>) -> Self; fn skip_reason(self, reason: &str) -> Self; fn to_pb(&self) -> DiagnoseCheck; } ``` ### `diagnose::Effects` Which checks may act on the guest. ```rust pub enum Effects { None, VirtualOnly, } impl Effects { fn parse(value: &str) -> Option<Self>; fn to_pb(self) -> DiagnoseEffects; fn from_pb(value: i32) -> Self; } ``` ### `diagnose::Environment` Where the guest runs. ```rust pub struct Environment { pub runtime: String, pub runtime_detail: String, pub arch: String, pub init: String, pub display_server: String, pub os: String, } ``` ### `diagnose::Fidelity` Facts that change task behaviour between variants. ```rust pub struct Fidelity { pub display: String, pub fonts_sha256: String, pub tz: String, pub locale: String, pub clock_skew_ms: i64, pub encoder: String, pub a11y_backend: String, pub audio_backend: String, pub cpu_model: String, pub cpu_count: u32, pub memory_mib: u64, pub gpu: String, pub kernel: String, pub runtime: String, pub init: String, pub packages_sha256: String, pub app_versions: BTreeMap<String, String>, pub tool_versions: BTreeMap<String, String>, pub simulator_runtimes: Vec<String>, } impl Fidelity { fn flatten(&self) -> BTreeMap<String, String>; } ``` ### `diagnose::Image` The image under test. ```rust pub struct Image { pub name: String, pub reference: String, pub variant: String, pub os: String, pub manifest_sha256: String, pub manifest_source: String, } ``` ### `diagnose::Report` A whole report, JSON schema version 1. ```rust pub struct Report { pub schema_version: u32, pub producer: String, pub started_at: Option<Timestamp>, pub spacesd: Option<Spacesd>, pub image: Image, pub environment: Environment, pub summary: Summary, pub checks: Vec<Check>, pub fidelity: Fidelity, } impl Report { fn from_json(text: &str) -> Result<Self, String>; fn to_json(&self) -> String; fn from_pb(report: DiagnoseReport) -> Self; fn to_pb(&self) -> DiagnoseReport; fn finalize(&mut self, strict: bool, duration: Duration); fn exit_code(&self) -> i32; fn to_human(&self) -> String; fn to_junit(&self) -> String; } ``` ### `diagnose::Severity` How much a check matters for the image, from its ... ```rust pub enum Severity { Required, Recommended, Info, } impl Severity { fn as_str(self) -> &'static str; } ``` ### `diagnose::Spacesd` The daemon that ran the checks. ```rust pub struct Spacesd { pub version: String, pub protocol_revision: u32, pub git_sha: String, pub cua_driver_version: String, } ``` ### `diagnose::Status` Outcome of one check. ```rust pub enum Status { Pass, Warn, Fail, Skip, } impl Status { fn as_str(self) -> &'static str; } ``` ### `diagnose::Summary` Totals. ```rust pub struct Summary { pub status: Status, pub pass: u32, pub warn: u32, pub fail: u32, pub skip: u32, pub duration_ms: u32, pub strict: bool, } ``` ### `DownloadOptions` Download options. ```rust pub struct DownloadOptions { pub offset: u64, pub length: u64, pub chunk_size: u32, pub progress: Option<Arc<AtomicU64>>, } ``` ### `DownloadResult` Result of a download to a sink. ```rust pub struct DownloadResult { pub size: u64, pub sha256: String, pub resumes: u32, } ``` ### `DownloadSink` Destination of a download. ```rust pub trait DownloadSink: Send { fn write(&mut self, chunk: Bytes) -> impl Future<Output = Result<()>> + Send; } ``` ### `Endpoint` A parsed spacesd endpoint: an absolute base URL ... ```rust pub struct Endpoint { /* private fields */ } impl Endpoint { fn parse(input: &str) -> Result<Self>; fn fleet_service(fleet_base_url: &str, namespace: &str, sandbox: &str, service: &str) -> Result<Self>; fn base_url(&self) -> &Url; fn kind(&self) -> &EndpointKind; fn path_prefix(&self) -> &str; fn is_tls(&self) -> bool; fn host(&self) -> &str; fn port(&self) -> u16; fn origin(&self) -> String; fn http_url(&self, path: &str) -> String; fn ws_url(&self, path: &str) -> String; } ``` ### `EndpointKind` How the endpoint is reached. ```rust pub enum EndpointKind { Direct, FleetGateway { namespace: String, service: String }, Relay { machine_id: String }, } ``` ### `Error` Every error the env client can return. ```rust pub enum Error { InvalidEndpoint(String), Transport(String), SpacesdNotAvailable { endpoint: String, reason: String }, Unauthenticated(ErrorDetails), FeatureUnsupported { feature: String, details: ErrorDetails }, PermissionDenied(ErrorDetails), StaleHandle(ErrorDetails), StaleGeometry(ErrorDetails), StaleSnapshot(ErrorDetails), WouldRequireActivation(ErrorDetails), TargetUnavailable(ErrorDetails), DeliveryFailed(ErrorDetails), ProcessNotFound(ErrorDetails), PathNotFound(ErrorDetails), PathExists(ErrorDetails), NotADirectory(ErrorDetails), IsADirectory(ErrorDetails), DiskFull(ErrorDetails), ChecksumMismatch { expected: String, actual: String }, SessionNotFound(ErrorDetails), OffsetMismatch { expected_offset: Option<u64>, details: ErrorDetails }, SequenceGap { expected_sequence: Option<u64>, details: ErrorDetails }, RateLimited { retry_after: Option<Duration>, details: ErrorDetails }, LimitExceeded(ErrorDetails), NotInitialized(ErrorDetails), Internal(ErrorDetails), Rpc(ErrorDetails), Timeout(Duration), DesktopNotReady(String), Protocol(String), Io(Error), } impl Error { fn code(&self) -> Option<Code>; fn details(&self) -> Option<&ErrorDetails>; fn is_retryable(&self) -> bool; } ``` ### `error::RpcStatus` `google.rpc.Status` (the payload of ... ```rust pub struct RpcStatus { pub code: i32, pub message: String, pub details: Vec<Any>, } ``` ### `ErrorDetails` Context shared by every RPC error variant. ```rust pub struct ErrorDetails { pub code: i32, pub message: String, pub metadata: HashMap<String, String>, } ``` ### `ExitStatus` How a process ended. ```rust pub struct ExitStatus { pub code: Option<i32>, pub signal: Option<Signal>, pub timed_out: bool, pub error: Option<String>, } impl ExitStatus { fn success(&self) -> bool; } ``` ### `expect::Expectation` One `COMPONENT=WANT`. ```rust pub struct Expectation { pub component: String, pub want: Want, } ``` ### `expect::Instance` One running build of a component, as the guest reported ... ```rust pub struct Instance { pub label: String, pub version: String, pub git_sha: String, pub exe_sha256: String, } ``` ### `expect::Want` What to compare. ```rust pub enum Want { Sha256(String), Git(String), Version(String), } ``` ### `ForwardOptions` Options for `SpacesdClient::forward_tcp`. ```rust pub struct ForwardOptions { pub port: u16, pub host: String, pub bind: SocketAddr, pub ttl: Option<Duration>, pub max_connections: usize, } impl ForwardOptions { fn new(port: u16) -> Self; fn bind(self, bind: SocketAddr) -> Self; } ``` ### `ForwardStats` Counters of a `TcpForward`. ```rust pub struct ForwardStats { pub connections: AtomicU64, pub active: AtomicU64, pub bytes_to_guest: AtomicU64, pub bytes_from_guest: AtomicU64, pub failed: AtomicU64, } ``` ### `GatewayAuth` Fleet gateway credentials: the gateway bearer and the ... ```rust pub struct GatewayAuth { pub bearer: Arc<dyn BearerProvider>, pub claim: Option<String>, } ``` ### `HttpCall` One HTTP request to the spacesd. ```rust pub struct HttpCall { pub method: String, pub path: String, pub headers: Vec<(String, String)>, pub body: Vec<u8>, pub timeout: Option<Duration>, pub max_response_bytes: Option<usize>, } ``` ### `HttpReply` The spacesd's answer. ```rust pub struct HttpReply { pub status: u16, pub headers: Vec<(String, String)>, pub body: Vec<u8>, } ``` ### `Keepalive` Connection-level keepalive settings. ```rust pub struct Keepalive { pub interval: Duration, pub timeout: Duration, } ``` ### `KeySpec` A key: named (`"enter"`, `"KEY_ENTER"` ... ```rust pub enum KeySpec { Named(Key), Char(String), } impl KeySpec { fn parse(name: &str) -> Result<Self>; } ``` ### `manifest::AudioClaim` Audio plumbing. ```rust pub struct AudioClaim { pub server: String, pub default_sink: String, pub default_source: String, pub uplink_input_sink: String, } ``` ### `manifest::CompatLink` A compatibility alias. ```rust pub struct CompatLink { pub path: String, pub target: String, pub unit_alias: String, pub unit: String, } ``` ### `manifest::DisplayClaim` The desktop session. ```rust pub struct DisplayClaim { pub name: String, pub resolution: String, pub user: String, } impl DisplayClaim { fn size(&self) -> Option<(u32, u32)>; } ``` ### `manifest::Fixtures` Conformance fixtures shipped in the image. ```rust pub struct Fixtures { pub root: String, pub apps: Vec<String>, } impl Fixtures { fn has(&self, name: &str) -> bool; } ``` ### `manifest::LaunchApp` The app `LaunchApp` must start. ```rust pub struct LaunchApp { pub app: String, pub args: Vec<String>, pub window_match: String, pub timeout_s: u32, pub limits: Vec<LaunchAppLimit>, } impl LaunchApp { fn limit_for(&self, runtime: &str, arch: &str) -> Option<&LaunchAppLimit>; } ``` ### `manifest::LaunchAppLimit` A runtime and architecture where `LaunchApp::app` ... ```rust pub struct LaunchAppLimit { pub runtime: String, pub arch: String, pub reason: String, pub instead: Option<Box<LaunchApp>>, } ``` ### `manifest::Loaded` A loaded manifest with its provenance. ```rust pub struct Loaded { pub manifest: Manifest, pub source: String, pub sha256: String, pub error: Option<String>, } impl Loaded { fn present(&self) -> bool; fn parse(bytes: &[u8], source: &str) -> Self; fn read(path: &Path) -> Self; fn severity(&self, claims: &[&str]) -> Severity; } ``` ### `manifest::Manifest` The image's claims. ```rust pub struct Manifest { pub schema_version: u32, pub name: String, pub aliases: Vec<String>, pub os: String, pub variant: String, pub init: String, pub arch: String, pub reference: String, pub source_revision: String, pub annotations: BTreeMap<String, String>, pub spacesd: SpacesdClaim, pub features_required: Vec<String>, pub features_optional: Vec<String>, pub features_not_applicable: Vec<String>, pub feature_attributes: BTreeMap<String, BTreeMap<String, String>>, pub codecs_required: Vec<String>, pub auth_modes: Vec<String>, pub services: Vec<ServiceClaim>, pub units: Vec<String>, pub unit_processes: BTreeMap<String, String>, pub display: DisplayClaim, pub fixtures: Fixtures, pub audio: AudioClaim, pub compat_links: Vec<CompatLink>, pub tier: String, pub apps: BTreeMap<String, SoftwareClaim>, pub tools: BTreeMap<String, SoftwareClaim>, pub simulator_runtimes: Vec<String>, pub launch_app: Option<LaunchApp>, pub parity: Parity, pub resources: Resources, pub tz: String, pub locale: String, pub dns_probe: String, pub av_skew_budget_ms: BTreeMap<String, u32>, } impl Manifest { fn is_named(&self, name: &str) -> bool; fn requires(&self, feature: &str) -> bool; fn allows(&self, feature: &str) -> bool; fn not_applicable(&self, feature: &str) -> bool; fn lists(&self, feature: &str) -> bool; fn av_skew_budget(&self, runtime: &str) -> u32; } ``` ### `manifest::Parity` Parity settings. ```rust pub struct Parity { pub expected_diff: Vec<String>, } ``` ### `manifest::Resources` Resource floors. ```rust pub struct Resources { pub min_disk_gib: f64, pub min_shm_mib: u64, pub min_memory_mib: u64, } ``` ### `manifest::ServiceClaim` A network service the image serves. ```rust pub struct ServiceClaim { pub name: String, pub port: u16, pub protocol: String, pub component: String, pub readiness: String, } ``` ### `manifest::SoftwareClaim` A claimed app or tool: its version command, as a bare ... ```rust pub enum SoftwareClaim { Argv(Vec<String>), Spec { argv: Vec<String>, expect: String }, } impl SoftwareClaim { fn argv(&self) -> &[String]; fn expect(&self) -> &str; } ``` ### `manifest::SpacesdClaim` The baked cua-spacesd, from `cua-spacesd build-info` at ... ```rust pub struct SpacesdClaim { pub present: bool, pub source: String, pub path: String, pub version: String, pub protocol_revision: u32, pub git_sha: String, pub cua_driver_version: String, pub tools_sha256: String, pub tools_count: u32, pub codecs_compiled: Vec<String>, } ``` ### `MediaOptions` Options for `SpacesdClient::open_media`. ```rust pub struct MediaOptions { pub display: Option<String>, pub window: Option<WindowRef>, pub codecs: Vec<MediaCodec>, pub max_fps: u32, pub max_dimension: u32, pub ticket_ttl: Option<Duration>, pub prefer_quic: bool, pub audio: Option<AudioOptions>, pub disable_video: bool, } ``` ### `MediaSession` A media session ready for the rcdp wire. ```rust pub struct MediaSession { pub session_id: String, pub ticket: String, pub ws_url: String, pub raw: OpenMediaResponse, } ``` ### `Output` Collected result of `SpacesdClient::run`. ```rust pub struct Output { pub status: ExitStatus, pub stdout: Vec<u8>, pub stderr: Vec<u8>, pub pty: Vec<u8>, } impl Output { fn success(&self) -> bool; fn stdout_str(&self) -> String; fn stderr_str(&self) -> String; } ``` ### `ProcessEvent` One output chunk or the exit. ```rust pub enum ProcessEvent { Stdout { offset: u64, data: Bytes }, Stderr { offset: u64, data: Bytes }, Pty { offset: u64, data: Bytes }, Exit(ExitStatus), } ``` ### `ProcessHandle` A running (or exited) guest process. ```rust pub struct ProcessHandle { /* private fields */ } impl ProcessHandle { fn pid(&self) -> u32; fn tag(&self) -> Option<&str>; fn keepalives_seen(&self) -> u64; fn reconnects(&self) -> u64; fn output_offset(&self) -> u64; async fn next_event(&mut self) -> Result<Option<ProcessEvent>>; async fn wait(self) -> Result<Output>; fn detach(self); async fn write_stdin(&self, data: impl Into<Bytes>) -> Result<()>; async fn write_pty(&self, data: impl Into<Bytes>) -> Result<()>; async fn close_stdin(&self) -> Result<()>; async fn signal(&self, signal: Signal) -> Result<()>; async fn kill(&self) -> Result<()>; async fn resize(&self, cols: u32, rows: u32) -> Result<()>; } ``` ### `ProcessRef` Identifies a process to attach to. ```rust pub enum ProcessRef { Pid(u32), Tag(String), } ``` ### `Replay` Scrollback replay when attaching. ```rust pub enum Replay { None, LastBytes(u64), All, FromOffset(u64), } ``` ### `RetryPolicy` Exponential backoff for reconnects and idempotent ... ```rust pub struct RetryPolicy { pub max_attempts: u32, pub initial_backoff: Duration, pub max_backoff: Duration, } impl RetryPolicy { fn backoff(&self, attempt: u32) -> Duration; async fn run<T, F, Fut>(&self, op: F) -> Result<T> where F: FnMut(u32) -> Fut, Fut: Future<Output = Result<T>>; } ``` ### `Screenshot` A captured screenshot. ```rust pub struct Screenshot { pub image: Bytes, pub format: ImageFormat, pub width: u32, pub height: u32, pub scale: f64, pub screenshot_id: String, pub raw: ScreenshotResponse, } ``` ### `ScreenshotOptions` Screenshot options. ```rust pub struct ScreenshotOptions { pub display: Option<String>, pub format: ImageFormat, pub quality: u32, pub max_dimension: u32, pub include_cursor: bool, } ``` ### `SpacesdClient` Async client for one cua-spacesd. ```rust pub struct SpacesdClient { /* private fields */ } impl SpacesdClient { async fn connect_url(endpoint: &str, token: Option<String>) -> Result<Self>; async fn connect(options: ConnectOptions) -> Result<Self>; fn token(&self) -> Option<&str>; fn endpoint(&self) -> &Endpoint; fn transport(&self) -> Transport; fn retry_policy(&self) -> RetryPolicy; fn channel(&self) -> GrpcChannel; fn system(&self) -> SystemServiceClient<GrpcChannel>; fn process(&self) -> ProcessServiceClient<GrpcChannel>; fn filesystem(&self) -> FilesystemServiceClient<GrpcChannel>; fn computer(&self) -> ComputerServiceClient<GrpcChannel>; fn windows(&self) -> WindowsServiceClient<GrpcChannel>; fn accessibility(&self) -> AccessibilityServiceClient<GrpcChannel>; fn driver(&self) -> DriverServiceClient<GrpcChannel>; fn stream(&self) -> StreamServiceClient<GrpcChannel>; fn presence(&self) -> PresenceServiceClient<GrpcChannel>; fn teleport(&self) -> TeleportServiceClient<GrpcChannel>; fn tunnel(&self) -> TunnelServiceClient<GrpcChannel>; fn volume(&self) -> VolumeServiceClient<GrpcChannel>; fn host_spaces(&self) -> HostSpacesServiceClient<GrpcChannel>; async fn capabilities(&self) -> Result<GetCapabilitiesResponse>; async fn refresh_capabilities(&self) -> Result<GetCapabilitiesResponse>; async fn machine_seal_public_key(&self) -> Result<Option<[u8; 32]>>; async fn has_feature(&self, feature: &str) -> Result<bool>; async fn init(&self, request: InitRequest) -> Result<InitResponse>; async fn health(&self) -> Result<HealthResponse>; async fn metrics(&self) -> Result<MetricsResponse>; async fn chunk_limits(&self) -> (usize, usize); async fn screenshot(&self, options: ScreenshotOptions) -> Result<Screenshot>; async fn pointer(&self, target: Option<InputTarget>, action: PtrAction) -> Result<PointerResponse>; async fn click(&self, x: f64, y: f64) -> Result<PointerResponse>; async fn double_click(&self, x: f64, y: f64) -> Result<PointerResponse>; async fn right_click(&self, x: f64, y: f64) -> Result<PointerResponse>; async fn click_with(&self, x: f64, y: f64, button: MouseButton, count: u32) -> Result<PointerResponse>; async fn move_to(&self, x: f64, y: f64) -> Result<PointerResponse>; async fn scroll(&self, dx: f64, dy: f64) -> Result<PointerResponse>; async fn drag(&self, from: (f64, f64), to: (f64, f64)) -> Result<PointerResponse>; async fn keyboard(&self, target: Option<InputTarget>, action: KbAction) -> Result<KeyboardResponse>; async fn type_text(&self, text: &str) -> Result<KeyboardResponse>; async fn press(&self, key: impl TryInto<KeySpec, Error = Error>) -> Result<KeyboardResponse>; async fn hotkey(&self, keys: &[&str]) -> Result<KeyboardResponse>; async fn get_clipboard(&self) -> Result<Option<String>>; async fn set_clipboard(&self, text: &str) -> Result<u64>; async fn cursor_position(&self) -> Result<(f64, f64)>; async fn displays(&self) -> Result<Vec<Display>>; async fn desktop_readiness(&self) -> Result<DesktopReadiness>; async fn wait_desktop_ready(&self, timeout: Duration) -> Result<DesktopReadiness>; async fn open_media(&self, options: MediaOptions) -> Result<MediaSession>; async fn diagnose(&self, options: DiagnoseOptions) -> Result<DiagnoseStream>; async fn diagnose_once(&self, options: DiagnoseOptions) -> Result<Report>; async fn diagnose_report(&self, options: DiagnoseOptions, on_check: impl FnMut(&Check)) -> Result<Report>; async fn upload(&self, path: &str, source: impl Into<UploadSource>, options: UploadOptions) -> Result<UploadResult>; async fn download(&self, path: &str) -> Result<Bytes>; async fn download_to_file(&self, path: &str, local: &Path, options: DownloadOptions) -> Result<DownloadResult>; async fn download_with<S: DownloadSink>(&self, path: &str, options: DownloadOptions, sink: &mut S) -> Result<DownloadResult>; async fn stat(&self, path: &str) -> Result<EntryInfo>; async fn list_dir(&self, path: &str, depth: u32) -> Result<Vec<EntryInfo>>; async fn make_dir(&self, path: &str) -> Result<EntryInfo>; async fn remove(&self, path: &str, recursive: bool) -> Result<()>; async fn http(&self, call: HttpCall) -> Result<HttpReply>; async fn spawn(&self, cmd: impl Into<Command>) -> Result<ProcessHandle>; async fn run(&self, cmd: impl Into<Command>) -> Result<Output>; async fn attach(&self, selector: ProcessRef, replay: Replay) -> Result<ProcessHandle>; async fn list_processes(&self, include_exited: bool) -> Result<Vec<ProcessInfo>>; async fn supports_tunnel_forward(&self) -> Result<bool>; async fn forward_tcp(&self, options: ForwardOptions) -> Result<TcpForward>; async fn open_websocket(&self, ws_path: &str) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>>; } ``` ### `StaticBearer` A static bearer. ```rust pub struct StaticBearer(pub String); ``` ### `TcpForward` A running TCP forward. ```rust pub struct TcpForward { /* private fields */ } impl TcpForward { fn local_addr(&self) -> SocketAddr; fn guest_port(&self) -> u16; async fn forward_id(&self) -> String; fn stats(&self) -> &ForwardStats; async fn close(self) -> Result<()>; } ``` ### `Transport` The protocol a connected client actually uses. ```rust pub enum Transport { Native, GrpcWeb, } ``` ### `transport::GrpcWebFrames` Re-chunks a binary gRPC-Web response body so every ... ```rust pub struct GrpcWebFrames<B> { /* private fields */ } ``` ### `TransportPreference` Wire protocol. ```rust pub enum TransportPreference { Auto, Native, GrpcWeb, } ``` ### `UploadOptions` Upload options. ```rust pub struct UploadOptions { pub mode: WriteMode, pub permissions: u32, pub create_parents: bool, pub chunk_size: usize, pub resumable: Option<bool>, pub upload_id: Option<String>, pub ttl: Option<Duration>, pub progress: Option<Arc<AtomicU64>>, } ``` ### `UploadResult` Result of an upload. ```rust pub struct UploadResult { pub entry: Option<EntryInfo>, pub sha256: String, pub size: u64, pub resumable: bool, pub resumes: u32, } ``` ### `UploadSource` What to upload. ```rust pub enum UploadSource { Bytes(Bytes), File(PathBuf), } ``` ## Functions ```rust // mod diagnose /// Encodes artifact bytes for display ... pub fn encode_artifact(data: &[u8]) -> String /// SHA-256 over the tool registry's ... pub fn tools_sha256(tools: &[ToolInfo]) -> String // mod error /// Decodes `ErrorInfo` from a status, if ... pub fn error_info(status: &Status) -> Option<ErrorInfo> /// Maps a `tonic::Status` into a typed ... pub fn from_status(status: &Status) -> Error /// One human-readable line for a status ... pub fn status_text(status: &Status) -> String /// Builds a `tonic::Status` carrying ... pub fn status_with_info(code: Code, info: ErrorInfo) -> Status /// Shorthand for `status_with_info` with a ... pub fn status_with_reason(code: Code, reason: ErrorReason, message: impl Into<String>, metadata: impl IntoIterator<Item = (String, String)>) -> Status /// The innermost cause of a status raised ... pub fn transport_cause(status: &Status) -> Option<String> // mod expect /// Appends one required `expect.*` check ... pub fn apply(report: &mut Report, expectations: &[Expectation]) /// The expect.<compone ... pub fn evaluate_one(report: &Report, expectation: &Expectation) -> Check /// The running instances of `component` ... pub fn instances(report: &Report, component: &str) -> Vec<Instance> /// Parses every spec, failing on the first ... pub fn parse_all(specs: &[String]) -> Result<Vec<Expectation>, String> // mod manifest /// Where an image built for this platform ... pub fn local_path() -> &'static str // mod transport /// Install rustls' `ring` provider as the ... pub fn ensure_crypto_provider() ``` ## Constants ```rust /// Default chunk size for bulk transfer ... pub const DEFAULT_CHUNK_BYTES: usize = _; /// Default cap on a response body. pub const DEFAULT_HTTP_RESPONSE_LIMIT: usize = _; /// Timeout when the caller sets none. pub const DEFAULT_HTTP_TIMEOUT: Duration = _; /// Default server-side keepalive interval ... pub const DEFAULT_PROCESS_KEEPALIVE: Duration = _; /// Name of the `SystemService.Health` ... pub const DESKTOP_COMPONENT: &str = "desktop"; /// Largest request body ... pub const MAX_HTTP_REQUEST_BYTES: usize = _; /// Largest message the client accepts ... pub const MAX_MESSAGE_BYTES: usize = _; /// Capability name a spacesd advertises ... pub const TUNNEL_FORWARD_FEATURE: &str = "tunnel.forward"; // mod diagnose /// Most events a `Diagnose` stream may ... pub const MAX_EVENTS: usize = 10_000; /// Largest artifact inlined into a report ... pub const MAX_INLINE_ARTIFACT_BYTES: usize = _; /// `DiagnoseReport.schema_version` this ... pub const SCHEMA_VERSION: u32 = cua_proto::DIAGNOSE_REPORT_SCHEMA_VERSION; /// Skip reasons that `strict` accepts on a ... pub const STRICT_ALLOWED_SKIPS: &[&str] = _; // mod error /// Type URL of a packed ... pub const ERROR_INFO_TYPE_URL: &str = "type.googleapis.com/cua.env.v1.ErrorInfo"; // mod expect /// The id of the guest check that carries ... pub const IDENTITY_CHECK: &str = "build.identity"; // mod manifest /// Default location inside a Linux or ... pub const DEFAULT_PATH: &str = "/etc/cua-image/manifest.json"; /// Manifest schema version this doctor ... pub const SCHEMA_VERSION: u32 = 1; /// Location inside a Windows image ... pub const WINDOWS_PATH: &str = r"C:\ProgramData\cua-image\manifest.json"; // mod transport /// Header carrying the spacesd token when ... pub const ENV_TOKEN_HEADER: &str = "x-cua-env-authorization"; /// Header carrying the Fleet claim for ... pub const FLEET_CLAIM_HEADER: &str = "x-cua-fleet-claim"; // mod tunnel /// Default cap on concurrently relayed ... pub const DEFAULT_MAX_CONNECTIONS: usize = 256; /// Largest WebSocket message accepted from ... pub const MAX_TUNNEL_MESSAGE_BYTES: usize = _; /// Bytes read from the local socket per ... pub const RELAY_CHUNK_BYTES: usize = _; ``` ## Type aliases ```rust /// Extra request headers a ... pub type ExtraHeaders = Vec<(String, String)>; /// The boxed channel every generated ... pub type GrpcChannel = BoxCloneSyncService<Request<Body>, Response<Body>, ChannelError>; /// Result alias. pub type Result<T, E = Error> = Result<T, E>; // mod diagnose /// A server-streamed `Diagnose` run. pub type DiagnoseStream = Streaming<DiagnoseResponse>; // mod error /// Boxed error used by transports. pub type BoxError = Box<dyn Error + Send + Sync + 'static>; ``` ## Re-exports `cua_proto::env::v1`. --- # cua-fleet The Fleet (cloud sandboxes) crate: pools, claims, builds and claim secrets. Public API of the `cua-fleet` crate (`cua_fleet`). Summaries and declarations only (`cargo doc -p cua-fleet --open` in `libs/cua` has the full comments); see [Rust crates](</docs/cua-sdk/reference/rust>) for stability. Fleet for the cua SDK. ## Modules - `autopool`: Automatic Fleet pools: `Sandbox.create(image)` on Fleet ... - `billing`: Cua Cloud billing from an app: the account's billing ... - `builds`: Remote image builds: Image layers (`pip_install` ... - `claim_secrets`: Per-claim env tokens delivered by Fleet's claim Secrets. - `limits`: The sandbox sizes the SDK asks Cua Cloud for. - `parity`: Sandbox parity with the local runtimes: sidecars on ... - `pricing`: Cua Cloud usage rates, read from Fleet. - `runtime`: The Fleet runtime ↔ image pairing, validated in exactly ... - `spec`: One sandbox model for pools and `Sandbox.create`. ## Types ### `AcquireOpts` Per-acquire options. ```rust pub struct AcquireOpts { pub name: Option<String>, pub warm: Option<bool>, pub registry_credentials: Option<RegistryCredentials>, pub max_pool_size: Option<u32>, pub claim_ttl: Option<Duration>, pub labels: BTreeMap<String, String>, pub bind_deadline: Option<Duration>, pub claim_token: Option<String>, } ``` ### `AutoPoolConfig` Pool manager settings. ```rust pub struct AutoPoolConfig { pub warm: bool, pub max_pool_size: u32, pub claim_ttl: Duration, pub heartbeat_every: Option<Duration>, pub bind_deadline: Duration, pub pool_ttl: Duration, pub pool_ttl_renew_window: Duration, pub idle_gc: Option<Duration>, pub auto_gc_every: Duration, pub home: PathBuf, pub tenant: Option<String>, pub clock: Clock, } impl AutoPoolConfig { fn from_env() -> Self; fn from_lookup(get: impl Fn(&str) -> Option<String>) -> Self; fn with_state_dir(self, dir: &Path) -> Self; } ``` ### `BillingCard` The saved card, as Fleet describes it (never its ... ```rust pub struct BillingCard { pub brand: String, pub last4: String, pub exp_month: u32, pub exp_year: u32, } ``` ### `BillingCredit` The account's Cua Cloud credit. ```rust pub struct BillingCredit { pub balance_usd_cents: i64, pub signup_grant_usd_cents: i64, pub signup_grant_unused: bool, } ``` ### `BillingStatus` `GET /api/billing/status`. ```rust pub struct BillingStatus { pub billing_enabled: bool, pub payment_method_present: bool, pub card: Option<BillingCard>, pub plan: String, pub payg_available: bool, pub credit: Option<BillingCredit>, pub billing_url: Option<String>, } impl BillingStatus { fn disabled() -> Self; } ``` ### `BuildFile` A local file copied into the image. ```rust pub struct BuildFile { pub source: PathBuf, pub destination: String, } ``` ### `BuildSpec` What to build. ```rust pub struct BuildSpec { pub from: String, pub layers: Vec<ImageLayer>, pub env: BTreeMap<String, String>, pub ports: Vec<u16>, pub files: Vec<BuildFile>, pub timeout: Option<Duration>, } impl BuildSpec { fn is_empty(&self) -> bool; fn validate(&self) -> Result<()>; } ``` ### `BuiltImage` A built (or cached) image. ```rust pub struct BuiltImage { pub reference: String, pub digest: String, pub namespace: String, pub name: String, pub cached: bool, } ``` ### `claim_secrets::EnvTokenProbe` The real probe: connects to the `env` service through ... ```rust pub struct EnvTokenProbe; ``` ### `ClaimOptions` Claim options. ```rust pub struct ClaimOptions { pub name: Option<String>, pub spec: Option<ClaimSpec>, pub ttl_seconds_after_created: Option<u32>, pub labels: Option<HashMap<String, String>>, pub claim_token: Option<String>, } ``` ### `ClaimSecretsWait` Probe settings of a `FleetClient` (tests shorten them). ```rust pub struct ClaimSecretsWait { pub budget: Duration, pub every: Duration, pub probe: Arc<dyn TokenProbe>, } ``` ### `Error` Errors from this crate. ```rust pub enum Error { MissingCredentials, InvalidArgument(String), Sdk(SdkError), AdmissionDenied { operation: String, status: u16, message: String }, CreditExhausted { message: String, billing_url: String }, UnknownService { sandbox: String, service: String, available: Vec<String> }, Timeout(String), Env(Error), Unsupported(String), PoolSpecMismatch { pool: String, diffs: Vec<SpecDiff> }, ClaimSecretsNotDelivered { claim: String, runtime: String, waited: Duration, detail: String }, } impl Error { fn is_not_found(&self) -> bool; } ``` ### `FleetClient` A Fleet client. ```rust pub struct FleetClient { /* private fields */ } impl FleetClient { async fn get_pool_json(&self, name: &str) -> Result<Option<Value>>; async fn patch_pool_json(&self, name: &str, patch: Value) -> Result<Value>; async fn billing_status(&self) -> Result<BillingStatus>; async fn build_image(&self, spec: &BuildSpec, creds: Option<&RegistryCredentials>, progress: Option<BuildProgress<'_>>) -> Result<BuiltImage>; fn with_claim_secrets_wait(self, wait: ClaimSecretsWait) -> Self; async fn delete_claim_secret(&self, ns: &str, claim: &str) -> Result<()>; async fn await_claim_secrets(&self, sandbox: &BoundSandbox, token: &str, runtime: &RuntimeKind) -> Result<()>; async fn put_registry_secret(&self, namespace: &str, name: &str, registry: &str, creds: &RegistryCredentials) -> Result<()>; async fn delete_registry_secret(&self, namespace: &str, name: &str) -> Result<()>; async fn apply_pool(&self, spec: &PoolSpec) -> Result<PoolHandle>; // Deprecated. async fn get_pool(&self, name: &str) -> Result<PoolHandle>; async fn pool_runtime(&self, pool: &Pool) -> Option<RuntimeKind>; async fn pool_services(&self, pool: &Pool) -> Result<BTreeMap<String, u16>>; async fn delete_pool(&self, handle: PoolHandle) -> Result<()>; async fn set_pool_replicas(&self, handle: &mut PoolHandle, replicas: u32) -> Result<()>; async fn wait_pool_ready(&self, handle: &PoolHandle, timeout: Duration) -> Result<Pool>; async fn claim(&self, pool: &Pool, options: ClaimOptions) -> Result<(Claim, bool)>; async fn wait_claim(&self, claim: &Claim) -> Result<BoundSandbox>; async fn acquire(&self, pool: &Pool, options: ClaimOptions) -> Result<BoundSandbox>; async fn attach_claim(&self, namespace: &str, name: &str) -> Result<BoundSandbox>; async fn claim_image(&self, namespace: &str, name: &str) -> Result<Option<(String, RuntimeKind)>>; async fn release(&self, namespace: &str, name: &str) -> Result<()>; async fn keep_alive(&self, namespace: &str, name: &str, duration: Duration) -> Result<String>; async fn list_claims(&self, namespace: &str) -> Result<Vec<Claim>>; async fn find_claims(&self, name: &str) -> Result<Vec<Claim>>; async fn usage_pricing(&self) -> Result<Option<UsagePricing>>; async fn apply(&self, name: &str, spec: &SandboxSpec, options: &PoolOptions) -> Result<PoolHandle>; async fn apply_with_credentials(&self, name: &str, spec: &SandboxSpec, options: &PoolOptions, credentials: Option<&RegistryCredentials>) -> Result<PoolHandle>; async fn pool_template(&self, pool: &str) -> Result<(SandboxSpec, RuntimeKind, Value)>; async fn export_pool(&self, pool: &str) -> Result<(SandboxSpec, PoolOptions, RuntimeKind)>; async fn check_pool_spec(&self, pool: &str, requested: &SandboxSpec) -> Result<()>; async fn apply_pool_template(&self, pool: &str, requested: &SandboxSpec, credentials: Option<&RegistryCredentials>) -> Result<()>; fn from_env() -> Result<Self>; fn connect(config: FleetConfig) -> Result<Self>; fn connect_with_http_client(config: FleetConfig, http: Arc<dyn HttpClient>) -> Result<Self>; fn connect_with_token_provider(config: FleetConfig, provider: Arc<dyn AccessTokenProvider>, http: Option<Arc<dyn HttpClient>>) -> Result<Self>; fn sdk(&self) -> Arc<CyclopsClient>; fn config(&self) -> &FleetConfig; async fn access_token(&self, force_refresh: bool) -> Result<String>; fn service_url(&self, sandbox: &BoundSandbox, service: &str) -> Result<String>; async fn service_request(&self, sandbox: &BoundSandbox, service: &str, path: &str, method: &str, body: Option<Vec<u8>>, timeout: Option<Duration>) -> Result<HttpResponse>; async fn service_request_with_headers(&self, sandbox: &BoundSandbox, service: &str, path: &str, method: &str, headers: &[(String, String)], body: Option<Vec<u8>>, timeout: Option<Duration>) -> Result<HttpResponse>; fn env_connect_options(&self, sandbox: &BoundSandbox, service: &str, env_token: Option<String>) -> Result<ConnectOptions>; async fn spacesd(&self, sandbox: &BoundSandbox, service: &str, env_token: Option<String>) -> Result<SpacesdClient>; async fn list_images(&self, namespace: &str) -> Result<Vec<Value>>; async fn get_image(&self, namespace: &str, name: &str) -> Result<Value>; async fn create_image(&self, namespace: &str, manifest: Value) -> Result<Value>; async fn delete_image(&self, namespace: &str, name: &str) -> Result<()>; async fn upload_image_file(&self, namespace: &str, name: &str, contents: Vec<u8>) -> Result<ImageUploadInstruction>; async fn create_signed_service_url(&self, sandbox: &BoundSandbox, service: &str, label: Option<String>, expires_in: Duration) -> Result<SignedServiceUrl>; async fn list_signed_service_urls(&self, sandbox: &BoundSandbox) -> Result<Vec<SignedServiceUrl>>; async fn revoke_signed_service_url(&self, url: SignedServiceUrl) -> Result<()>; } ``` ### `FleetConfig` How to reach and authenticate to Fleet. ```rust pub struct FleetConfig { pub base_url: String, pub token_url: String, pub client_id: Option<String>, pub client_secret: Option<String>, pub fleet_token: Option<String>, pub pool_poll_interval_ms: u64, pub pool_poll_limit: u32, pub claim_poll_interval_ms: u64, pub claim_poll_limit: u32, } impl FleetConfig { fn from_env() -> Self; fn from_lookup(get: impl Fn(&str) -> Option<String>) -> Self; fn has_auth(&self) -> bool; } ``` ### `FleetImage` An image resolved for Fleet: the reference to put in ... ```rust pub struct FleetImage { pub image: String, pub runtime: RuntimeKind, pub resolved: Option<ResolvedImage>, } ``` ### `GcReport` What `PoolManager::gc` did. ```rust pub struct GcReport { pub deleted_pools: Vec<String>, pub deleted_namespaces: Vec<String>, pub deleted_claims: Vec<String>, pub kept: Vec<String>, pub errors: Vec<String>, } ``` ### `ImageEvidence` What is known about an image before choosing its ... ```rust pub enum ImageEvidence { Known(ImageVariant), Unsupported(String), Unavailable(String), } ``` ### `ImageInspector` Reads an image's manifest for `resolve_runtime`. ```rust pub trait ImageInspector: Send + Sync { async fn inspect(&self, image: &str) -> ImageEvidence; } ``` ### `ImageResolver` Resolves an image tag to a digest-pinned reference ... ```rust pub trait ImageResolver: Send + Sync { async fn resolve(&self, image: &str) -> Option<String>; } ``` ### `ImageVariant` What a Fleet image is, read from its manifest. ```rust pub enum ImageVariant { ContainerDisk, Rootfs, Other, } impl ImageVariant { fn as_str(self) -> &'static str; fn parse(value: &str) -> Result<Self>; fn runtime(self) -> Option<RuntimeKind>; } ``` ### `ManagedClaim` A claim on a managed pool. ```rust pub struct ManagedClaim { pub pool: String, pub claim: String, pub sandbox: BoundSandbox, pub created_pool: bool, pub reattached: bool, pub bind_time: Duration, pub claim_ttl: Duration, } impl ManagedClaim { fn set_release_on_drop(&mut self, release: bool); fn extend_until(&self, until: i64); fn heartbeat_active(&self) -> bool; async fn release(self) -> Result<()>; fn detach(self) -> BoundSandbox; } ``` ### `ManagedPoolInfo` A managed pool as `PoolManager::list` reports it. ```rust pub struct ManagedPoolInfo { pub name: String, pub managed: bool, pub spec_hash: Option<String>, pub image: Option<String>, pub replicas: u32, pub ready_replicas: Option<u32>, pub max_pool_size: Option<u32>, pub claims: u32, pub bound_claims: u32, pub last_used: Option<i64>, pub created: Option<i64>, pub expires_at: Option<i64>, pub terminating: bool, } ``` ### `PoolHandle` A reconciled pool plus the template it owns (when ... ```rust pub struct PoolHandle { pub pool: Pool, pub template: Option<Template>, pub runtime: Option<RuntimeKind>, pub claim_ttl: Option<Duration>, } impl PoolHandle { fn name(&self) -> &str; } ``` ### `PoolManager` The auto pool manager. ```rust pub struct PoolManager { /* private fields */ } impl PoolManager { fn new(fleet: FleetClient, cfg: AutoPoolConfig) -> Self; fn with_resolver(self, resolver: Arc<dyn ImageResolver>) -> Self; fn fleet(&self) -> &FleetClient; fn config(&self) -> &AutoPoolConfig; async fn tenant(&self) -> Result<String>; async fn resolve_key(&self, key: PoolSpecKey) -> PoolSpecKey; async fn acquire(&self, key: PoolSpecKey, opts: AcquireOpts) -> Result<ManagedClaim>; fn adopt(&self, sandbox: BoundSandbox, claim_ttl: Option<Duration>) -> ManagedClaim; async fn ensure_pool(&self, key: &PoolSpecKey, hash: &str, opts: &AcquireOpts) -> Result<(Pool, bool)>; async fn list(&self) -> Result<Vec<ManagedPoolInfo>>; async fn gc(&self, idle_after: Duration) -> Result<GcReport>; async fn gc_pools(&self, idle_after: Duration, only: &[String]) -> Result<GcReport>; async fn gc_if_due(&self) -> Option<GcReport>; } ``` ### `PoolOptions` How a pool keeps capacity for a `SandboxSpec`. ```rust pub struct PoolOptions { pub runtime: Option<RuntimeKind>, pub replicas: Option<u32>, pub warm: Option<bool>, pub min_pool_size: Option<u32>, pub max_pool_size: Option<u32>, pub idle_ttl: Option<Duration>, pub ttl_policy: Option<TtlPolicy>, pub pool_ttl: Option<Duration>, pub claim_ttl: Option<Duration>, } impl PoolOptions { fn autoscaled(&self) -> bool; fn autoscaling(&self) -> Option<WarmPoolAutoscaling>; fn validate(&self) -> Result<()>; fn spec_replicas(&self) -> u32; fn lifecycle_patch(&self) -> Map<String, Value>; fn from_pool_json(pool: &Value, runtime: Option<RuntimeKind>) -> Self; } ``` ### `PoolSpec` Everything needed to reconcile a pool and its template ... ```rust pub struct PoolSpec { pub name: String, pub image: String, pub runtime: RuntimeKind, pub replicas: u32, pub cpu: Option<u32>, pub memory_mb: Option<u32>, pub services: BTreeMap<String, u16>, pub readiness_tcp_port: Option<u16>, pub readiness: Option<ReadinessProbe>, pub efi: bool, pub command: Option<Vec<String>>, pub args: Option<Vec<String>>, pub env: BTreeMap<String, String>, pub process_mode: Option<ProcessMode>, pub autoscaling: Option<WarmPoolAutoscaling>, pub ttl_seconds_after_created: Option<u32>, pub idle_ttl_seconds: Option<u32>, pub ttl_policy: Option<TtlPolicy>, pub claim_secrets: bool, pub sidecars: Vec<Sidecar>, pub image_pull_secret: Option<String>, pub registry_credentials: Option<RegistryCredentials>, } impl PoolSpec { fn new(name: impl Into<String>, image: impl Into<String>) -> Self; fn parts(&self) -> (SandboxSpec, PoolOptions); fn from_parts(name: impl Into<String>, spec: &SandboxSpec, options: &PoolOptions) -> Self; fn uses_parity(&self) -> bool; fn validate_parity(&self) -> Result<()>; fn template_json(&self) -> Result<Value>; fn claim_secrets(self, enabled: bool) -> Self; fn runtime(self, runtime: RuntimeKind) -> Self; fn services<I, S>(self, services: I) -> Self where I: IntoIterator<Item = (S, u16)>, S: Into<String>; fn readiness_tcp(self, port: u16) -> Self; fn pool_request(&self) -> CreatePoolRequest; fn template_request(&self) -> Result<CreateTemplateRequest>; } ``` ### `PoolSpecKey` What makes two sandboxes interchangeable: they may ... ```rust pub struct PoolSpecKey { pub image: String, pub runtime: RuntimeKind, pub efi: bool, pub cpu: Option<u32>, pub memory_mb: Option<u32>, pub services: BTreeMap<String, u16>, pub readiness_tcp_port: Option<u16>, pub command: Option<Vec<String>>, pub env: BTreeMap<String, String>, pub sidecars: Vec<Sidecar>, pub pull_secret: Option<String>, pub claim_secrets: bool, } impl PoolSpecKey { fn new(image: impl Into<String>) -> Self; fn runtime(self, runtime: RuntimeKind) -> Self; fn resources(self, cpu: Option<u32>, memory_mb: Option<u32>) -> Self; fn services<I, S>(self, services: I) -> Self where I: IntoIterator<Item = (S, u16)>, S: Into<String>; fn canonical(&self) -> String; fn spec_hash(&self) -> String; fn sandbox_spec(&self) -> SandboxSpec; fn pool_options(&self, initial: u32, max: u32, ttl: Duration) -> PoolOptions; fn pool_spec(&self, name: &str, initial: u32, max: u32, ttl: Duration) -> PoolSpec; } ``` ### `ProcessMode` How a sandbox runs `command` / `args` / `env` ... ```rust pub enum ProcessMode { Legacy, Run, } impl ProcessMode { fn as_str(&self) -> &'static str; fn parse(s: &str) -> Result<Self>; } ``` ### `ReadinessProbe` A readiness probe on a guest port ( ... ```rust pub enum ReadinessProbe { Tcp { port: u16 }, Http { port: u16, path: String }, } impl ReadinessProbe { fn port(&self) -> u16; } ``` ### `runtime::RegistryInspector` The default `ImageInspector`: `cua_image::resolve` ... ```rust pub struct RegistryInspector { /* private fields */ } ``` ### `SandboxSpec` What a sandbox runs. ```rust pub struct SandboxSpec { pub image: String, pub command: Option<Vec<String>>, pub args: Option<Vec<String>>, pub env: BTreeMap<String, String>, pub services: BTreeMap<String, u16>, pub readiness: Option<ReadinessProbe>, pub cpu: Option<u32>, pub memory_mb: Option<u32>, pub efi: bool, pub sidecars: Vec<Sidecar>, pub registry_secret: Option<String>, pub process_mode: Option<ProcessMode>, pub claim_secrets: bool, } impl SandboxSpec { fn new(image: impl Into<String>) -> Self; fn effective_process_mode(&self) -> Option<ProcessMode>; fn validate(&self, runtime: &RuntimeKind) -> Result<()>; fn typed_template(&self, runtime: &RuntimeKind) -> Result<OSGymSandboxTemplateSpec>; fn template_json(&self, name: &str, runtime: &RuntimeKind) -> Result<Value>; fn from_template_json(template: &Value) -> Self; fn diff(&self, current: &SandboxSpec, image_matches: bool) -> Vec<SpecDiff>; fn overlay(&self, current: &SandboxSpec) -> SandboxSpec; } ``` ### `Sidecar` An extra container next to a sandbox. ```rust pub struct Sidecar { pub name: String, pub image: String, pub command: Option<Vec<String>>, pub env: BTreeMap<String, String>, pub ports: Vec<u16>, pub args: Option<Vec<String>>, pub cpu: Option<String>, pub memory: Option<String>, } impl Sidecar { fn new(image: impl Into<String>) -> Self; } ``` ### `SpecDiff` One field where a pool's template differs from the ... ```rust pub struct SpecDiff { pub field: String, pub pool: String, pub requested: String, } ``` ### `TokenProbe` Asks a bound sandbox whether its driver has the claim's ... ```rust pub trait TokenProbe: Send + Sync { async fn probe(&self, fleet: &FleetClient, sandbox: &BoundSandbox, token: &str) -> TokenState; } ``` ### `TokenState` What one token probe saw. ```rust pub enum TokenState { Delivered, Awaiting(String), Unknown(String), } ``` ### `TtlPolicy` What expiry of the pool TTL or idle TTL deletes ... ```rust pub enum TtlPolicy { Retain, Cascade, } impl TtlPolicy { fn as_str(&self) -> &'static str; fn parse(s: &str) -> Result<Self>; } ``` ### `UsagePricing` Cua Cloud rates in USD. ```rust pub struct UsagePricing { pub vcpu_hour_usd: f64, pub memory_gib_hour_usd: f64, } impl UsagePricing { fn from_config(config: &Value) -> Option<Self>; fn hourly_usd(&self, cpus: u32, memory_mib: u32) -> f64; } ``` ## Functions ```rust /// A canonical alias (`linux`, `ubuntu` ... pub fn canonical_alias(name: &str, env: &dyn Fn(&str) -> Option<String>) -> Option<(String, String)> /// The canonical image for `os` (`linux` ... pub fn canonical_image(os: &str) -> String /// Checks a cloud sandbox size against ... pub fn check_cloud_size(cpu: Option<u32>, memory_mb: Option<u32>) -> Result<()> /// Checks a pool size field (`what` ... pub fn check_pool_size(what: &str, n: u32) -> Result<()> /// Refuses `RESERVED_SERVICE_NAMES` in ... pub fn check_reserved_service_names<'a>(services: impl IntoIterator<Item = &'a String>, has_sidecars: bool) -> Result<()> /// Validates the runtime/image pairing ... pub fn check_runtime(runtime: Option<RuntimeKind>, image: &str, evidence: &ImageEvidence) -> Result<RuntimeKind> /// Forgets cached rates (sign-out, tests). pub fn clear_pricing_cache() /// redis for docker. ... pub fn default_sidecar_name(image: &str) -> String /// Refuses a runtime this SDK does not ... pub fn ensure_runtime_offered(runtime: &RuntimeKind) -> Result<()> /// Name of an ephemeral pool: cua-eph-<12 ... pub fn ephemeral_pool_name() -> String /// The readable diff of a ... pub fn format_diffs(diffs: &[SpecDiff]) -> String /// Reads what `image` is through the ... pub async fn inspect_image(image: &str) -> ImageEvidence /// Whether `image` is one of the canonical ... pub fn is_canonical_image(image: &str) -> bool /// Parses a runtime name to create or ... pub fn parse_runtime(value: &str) -> Result<Option<RuntimeKind>> /// The deterministic Secret name for ... pub fn registry_secret_name(registry: &str, username: &str) -> String /// The error for a remote image build ... pub fn remote_builds_unsupported(what: &str) -> Error /// The one Fleet image rule: resolve ... pub async fn resolve_fleet_image(runtime: Option<RuntimeKind>, image: &str) -> Result<FleetImage> /// `resolve_fleet_image` reading a private ... pub async fn resolve_fleet_image_with(runtime: Option<RuntimeKind>, image: &str, creds: Option<&RegistryCredentials>) -> Result<FleetImage> /// `resolve_fleet_image`, returning only ... pub async fn resolve_runtime(runtime: Option<RuntimeKind>, image: &str) -> Result<RuntimeKind> /// The fallback runtime for an image whose ... pub fn runtime_from_reference(image: &str) -> RuntimeKind /// The wire spelling of a runtime (`macos` ... pub fn runtime_name(runtime: &RuntimeKind) -> &'static str /// `Error::is_not_found` for a bare ... pub fn sdk_error_is_not_found(e: &SdkError) -> bool /// Replaces the process-wide ... pub fn set_image_inspector(inspector: Option<Arc<dyn ImageInspector>>) /// A `fleets_pool` resource block (the ... pub fn terraform_pool_block(name: &str, spec: &SandboxSpec, options: &PoolOptions, runtime: &RuntimeKind) -> String /// Checks sidecars the way Fleet's ... pub fn validate_sidecars(sidecars: &[Sidecar], reserved: &[u16]) -> Result<()> // mod autopool /// The base pool name for a tenant and ... pub fn auto_pool_name(tenant: &str, spec_hash: &str) -> String /// Every candidate name, in probe order ... pub fn candidate_names(tenant: &str, spec_hash: &str) -> Vec<String> /// Default home: `$CUA_HOME`, else ... pub fn default_cua_home() -> PathBuf /// The tenant a bearer belongs to: the JWT ... pub fn tenant_from_token(token: &str) -> String // mod builds /// The content-addressed Image name for a ... pub fn build_name(recipe: &ImageRecipe) -> String /// The build namespace for a tenant ... pub fn build_namespace(tenant: &str) -> String /// The recipe a build of `spec` on `base` ... pub fn build_recipe(spec: &BuildSpec, base: &str, files: Vec<ImageFile>, from_pull_secret: Option<String>) -> ImageRecipe /// A local build's content-addressed ... pub fn local_build_file(f: &BuildFile) -> Result<ImageFile> // mod claim_secrets /// The Opaque Secret the SDK writes for ... pub fn claim_secret_body(namespace: &str, claim: &str, token: &str) -> Value /// A fresh env token: 64 hex characters ... pub fn generate_claim_token() -> String /// Checks a claim token with cua-spacesd's ... pub fn validate_claim_token(token: &str) -> Result<()> // mod parity /// The Secret body #7887's admission ... pub fn registry_secret_body(namespace: &str, name: &str, registry: &str, creds: &RegistryCredentials) -> Result<Value> /// `vmTemplate.sidecars[]` entries ... pub fn sidecars_json(sidecars: &[Sidecar]) -> Value // mod runtime /// Classifies already-fetched documents ... pub fn classify_manifest(index: Option<&ImageIndex>, manifest: Option<&ImageManifest>, config: Option<&Value>) -> Result<ImageVariant> /// `classify_manifest` over raw JSON ... pub fn classify_manifest_json(manifest: &str, config: Option<&str>) -> Result<ImageVariant> /// The tag of an image reference, or ... pub fn image_tag(image: &str) -> Option<&str> // mod spec /// `4096Mi`, `4Gi`, `512M`, `4G` → MiB. pub fn parse_memory_mib(q: &str) -> Option<u32> ``` ## Constants ```rust /// The everyday vCPU range of a cloud ... pub const CLOUD_DEFAULT_RANGE_CPUS: RangeInclusive<u32> = _; /// The everyday memory range of a cloud ... pub const CLOUD_DEFAULT_RANGE_MEMORY_MB: RangeInclusive<u32> = _; /// Default Fleet API endpoint ... pub const DEFAULT_FLEET_BASE_URL: &str = "https://run.cua.ai"; /// Poll interval cua-sandbox uses for ... pub const DEFAULT_POLL_INTERVAL_MS: u64 = 2000; /// Poll limit cua-sandbox uses for pools ... pub const DEFAULT_POLL_LIMIT: u32 = 300; /// Default OAuth token endpoint ... pub const DEFAULT_TOKEN_URL: &str = /* ... */; /// vCPUs per cloud sandbox ... pub const FLEET_ABSOLUTE_CPUS: RangeInclusive<u32> = _; /// Most sandboxes per pool ... pub const FLEET_ABSOLUTE_MAX_POOL_SIZE: u32 = 50; /// Memory per cloud sandbox in MiB ... pub const FLEET_ABSOLUTE_MEMORY_MB: RangeInclusive<u32> = _; /// Why a macOS image or the `macos` ... pub const MACOS_UNSUPPORTED: &str = cua_image::resolve::FLEET_MACOS_UNSUPPORTED; /// The hostname sidecars reach the sandbox ... pub const MAIN_CONTAINER_NAME: &str = "main"; /// Most sidecars a template may carry ... pub const MAX_SIDECARS: usize = 8; /// The one "no Fleet credentials" message ... pub const MISSING_CREDENTIALS: &str = "Fleet credentials missing: run `cua auth login` or set \ CUA_CLIENT_ID/CUA_CLIENT_SECRET, or pass local=True"; /// How long an answer is reused. pub const PRICING_TTL: Duration = _; /// Name prefix of tenant registry pull ... pub const REGISTRY_SECRET_PREFIX: &str = "cua-registry-"; /// Whether Fleet builds `kind: container` ... pub const REMOTE_BUILDS_SUPPORTED: bool = false; /// Service names a sandbox with sidecars ... pub const RESERVED_SERVICE_NAMES: [&str; 3] = _; // mod autopool /// Name prefix of managed pools. pub const AUTO_POOL_PREFIX: &str = "cua-auto-"; /// Default idle threshold of the automatic ... pub const DEFAULT_IDLE_GC_SECS: u64 = _; /// Name prefix of legacy ephemeral pools ... pub const EPHEMERAL_POOL_PREFIX: &str = "cua-eph-"; /// Label carrying the last use (unix ... pub const LABEL_LAST_USED: &str = "cua.ai/last-used"; /// Label naming the creator. pub const LABEL_MANAGED_BY: &str = "cua.ai/managed-by"; /// Label carrying the first 32 hex digits ... pub const LABEL_SPEC_HASH: &str = "cua.ai/spec-hash"; /// Value of `LABEL_MANAGED_BY`. pub const MANAGED_BY: &str = "cua-sdk"; /// Candidate names tried per key before ... pub const MAX_NAME_PROBES: usize = 8; /// Floor of a claim's `bindDeadline`: live ... pub const MIN_BIND_DEADLINE_SECS: u64 = 900; /// Hex digits of the spec hash stored in ... pub const SPEC_HASH_LABEL_LEN: usize = 32; // mod builds /// Prefix of content-named Image resources. pub const BUILD_NAME_PREFIX: &str = "cua-b-"; /// Prefix of the per-account build ... pub const BUILD_NAMESPACE_PREFIX: &str = "cua-build-"; /// Default build budget. pub const DEFAULT_BUILD_TIMEOUT: Duration = _; /// Most environment variables a remote ... pub const MAX_BUILD_ENV: usize = 128; /// Longest environment value a remote ... pub const MAX_BUILD_ENV_VALUE: usize = 8192; /// Largest file `copy` uploads (read into ... pub const MAX_COPY_BYTES: u64 = _; // mod claim_secrets /// Label on a claim Secret naming its ... pub const CLAIM_SECRET_CLAIM_LABEL: &str = "osgym.cua.ai/claim"; /// Name prefix of a claim's Secret ... pub const CLAIM_SECRET_PREFIX: &str = "cua-claim-"; /// Longest wait after Bound for the token ... pub const DEFAULT_WAIT: Duration = _; /// The Secret key (and in-guest file name) ... pub const ENV_TOKEN_KEY: &str = "env-token"; /// Pause between token probes. pub const PROBE_EVERY: Duration = _; /// Claim secrets are always available ... pub const SUPPORTED: bool = true; // mod runtime /// Set to `0`/`off`/`false` to skip ... pub const INSPECT_ENV: &str = "CUA_FLEET_IMAGE_INSPECT"; // mod spec /// Default autoscaling ceiling of a pool ... pub const DEFAULT_MAX_POOL_SIZE: u32 = 10; ``` ## Type aliases ```rust /// Result alias. pub type Result<T, E = Error> = Result<T, E>; // mod autopool /// Clock returning unix seconds (tests ... pub type Clock = Arc<dyn Fn() -> i64 + Send + Sync>; // mod builds /// Progress of a remote build, in the ... pub type BuildProgress<'a> = &'a dyn Fn(&str) + Send + Sync; ``` ## Re-exports `cyclops_sdk::Sandbox`, `cyclops_sdk::Claim`, `cyclops_sdk::HttpClient`, `cua_image::spec::ImageLayer`, `cua_image::normalize_registry`, `cua_image::RegistryCredentials`, `cyclops_sdk::Pool`, `cua_image::registry_of`, `parity::RegistryCredentials`, `pool::RuntimeKind`, `cyclops_sdk_schema`, `cyclops_sdk`, `cyclops_sdk::SdkError`, `cyclops_sdk::Template`. --- # cua-sandbox-core The daemon-agnostic Sandbox over the Fleet, local and direct providers. Public API of the `cua-sandbox-core` crate (`cua_sandbox_core`). Summaries and declarations only (`cargo doc -p cua-sandbox-core --open` in `libs/cua` has the full comments); see [Rust crates](</docs/cua-sdk/reference/rust>) for stability. One daemon-agnostic `Sandbox` over three providers: ## Modules - `byoc`: Your own cloud account as a sandbox location (--on ... - `byoc::meta`: Relay machine metadata (`meta`) a cloud sandbox's ... - `fleet_local`: Run a Fleet template (or the template behind a Fleet ... - `http`: HTTP to sandbox services: whole requests (readiness ... - `mcp`: MCP to sandbox services with the official Rust SDK ... - `placement`: Where a sandbox runs, what kind of machine it is, and ... - `pool_image`: The `ImageInfo` of a named (user-owned) Fleet pool: its ... - `power`: Turning a sandbox off and on again, the same words for ... - `provider`: Pluggable sandbox providers: third-party platforms ... - `proxy`: A small loopback HTTP reverse proxy. - `refs`: Sandbox references: one scheme for sandboxes and Spaces ... - `settings`: User defaults: where sandboxes run when nothing says ... - `state`: Persistent sandbox state: ~/.cua/sa ... ## Types ### `byoc::CloudCheck` One check of `cloud_test`. ```rust pub struct CloudCheck { pub name: String, pub ok: bool, pub detail: String, } ``` ### `byoc::CloudConnected` `cloud_connect`: the connected cloud plus the checks it ... ```rust pub struct CloudConnected { pub provider: CloudProvider, pub checks: Vec<CloudCheck>, } ``` ### `byoc::CloudCredentials` Where a cloud's credentials were found on this machine. ```rust pub struct CloudCredentials { pub found: bool, pub source: String, } ``` ### `byoc::CloudDisconnected` `cloud_disconnect`. ```rust pub struct CloudDisconnected { pub provider: String, pub disconnected: bool, pub left: Vec<CloudResource>, } ``` ### `byoc::CloudKind` What a cloud can run for one image family, and at what ... ```rust pub struct CloudKind { pub image: String, pub kind: String, pub supported: bool, pub reason: String, pub machine_type: String, pub usd_per_hour: f64, } ``` ### `byoc::CloudManager` Connects, checks and sweeps your clouds (implemented in ... ```rust pub trait CloudManager: Send + Sync + 'static { async fn status(&self, provider: Option<&str>) -> Result<CloudStatusReport>; async fn test(&self, target: &CloudTarget) -> Result<CloudTestReport>; async fn connect(&self, target: &CloudTarget, make_default: bool, ttl_hours: Option<u32>) -> Result<CloudConnected>; async fn disconnect(&self, provider: &str) -> Result<CloudDisconnected>; async fn sweep(&self, provider: Option<&str>, dry_run: bool, all: bool) -> Result<CloudSweepReport>; } ``` ### `byoc::CloudProvider` One cloud as `cloud_status` lists it. ```rust pub struct CloudProvider { pub name: String, pub title: String, pub tier: String, pub connected: bool, pub default: bool, pub credentials: CloudCredentials, pub account: String, pub profile: String, pub region: String, pub zone: String, pub project: String, pub environment: String, pub label: String, pub ttl_hours: u32, pub kinds: Vec<CloudKind>, } ``` ### `byoc::CloudResource` One thing Cua created in a cloud. ```rust pub struct CloudResource { pub provider: String, pub id: String, pub resource_type: String, pub sandbox: String, pub machine: String, pub region: String, pub state: String, pub created: String, pub expires: String, pub expired: bool, } ``` ### `byoc::CloudStatusReport` `cloud_status`. ```rust pub struct CloudStatusReport { pub default_on: String, pub providers: Vec<CloudProvider>, pub resources: Vec<CloudResource>, } ``` ### `byoc::CloudSweepItem` One row of `cloud_sweep`. ```rust pub struct CloudSweepItem { pub resource: CloudResource, pub action: String, pub reason: String, } ``` ### `byoc::CloudSweepReport` `cloud_sweep`. ```rust pub struct CloudSweepReport { pub dry_run: bool, pub resources: Vec<CloudSweepItem>, } ``` ### `byoc::CloudTarget` Where in a cloud account sandboxes go (`cloud_connect` ... ```rust pub struct CloudTarget { pub provider: String, pub profile: Option<String>, pub region: Option<String>, pub zone: Option<String>, pub project: Option<String>, pub environment: Option<String>, } ``` ### `byoc::CloudTestReport` `cloud_test`. ```rust pub struct CloudTestReport { pub provider: String, pub ok: bool, pub account: String, pub checks: Vec<CloudCheck>, } ``` ### `ConnectOptionsOverride` Overrides for `Sandbox::env_with`. ```rust pub struct ConnectOptionsOverride { pub probe_timeout: Option<Duration>, } ``` ### `CreateOptions` Options for `Sandboxes::create`. ```rust pub struct CreateOptions { pub provider: ProviderKind, pub name: Option<String>, pub image: String, pub os: String, pub cpus: u32, pub memory_mb: u64, pub disk_gb: Option<u32>, pub ports: Vec<u16>, pub services: BTreeMap<String, u16>, pub wait_for: Vec<Probe>, pub ready_timeout: Duration, pub ready_timeout_given: bool, pub env: BTreeMap<String, String>, pub command: Option<Vec<String>>, pub env_token: Option<String>, pub fleet: FleetOptions, pub firmware: Option<String>, pub sidecars: Vec<Sidecar>, pub registry_credentials: Option<RegistryCredentials>, pub build: Option<BuildSpec>, pub kind: Kind, pub runtime: Runtime, pub container_runtime: Option<String>, pub network: NetworkMode, pub owner_pid: Option<u32>, pub contrib: Option<String>, pub keep_on_failure: bool, pub gpu: Option<String>, pub ephemeral_name: Option<String>, } impl CreateOptions { fn new(provider: ProviderKind, image: impl Into<String>) -> Self; fn sidecar(self, sidecar: Sidecar) -> Self; fn default_warm(&self) -> Option<bool>; fn on(&self) -> Option<On>; fn apply_placement(&mut self) -> Result<()>; fn name(self, name: impl Into<String>) -> Self; fn wait_for(self, probe: Probe) -> Self; fn service(self, name: impl Into<String>, port: u16) -> Self; fn command<I, S>(self, argv: I) -> Self where I: IntoIterator<Item = S>, S: Into<String>; fn wait_for_service(self, service: &str, http_path: Option<&str>) -> Result<Self>; fn service_probe(&self, service: &str, http_path: Option<&str>) -> Result<Probe>; async fn fleet_pool_key(&self) -> Result<PoolSpecKey>; async fn fleet_pool_key_resolved(&self) -> Result<(PoolSpecKey, Option<ImageInfo>)>; fn pool_sandbox_spec(&self) -> SandboxSpec; fn validate_sidecars(&self) -> Result<()>; } ``` ### `EphemeralLease` The lease of an ephemeral local sandbox: which process ... ```rust pub struct EphemeralLease { pub name: String, pub pid: u32, pub created_at: u64, } ``` ### `Error` Errors. ```rust pub enum Error { InvalidArgument(String), InvalidPlacement(PlacementError), NotFound(String), AmbiguousSandbox { name: String, candidates: Vec<String>, message: String }, ProviderNotConfigured(ProviderKind), ContribNotConfigured(String), Unsupported { provider: ProviderKind, op: String }, SpacesdNotAvailable { sandbox: String, reason: String }, UnsupportedImage(String), Timeout(String), Http(String), Fleet(Error), Runtime(RuntimeError), Env(Error), Mcp(String), Cloud(String), Io(Error), Json(Error), Cancelled(String), } ``` ### `FleetOptions` Fleet-specific create options. ```rust pub struct FleetOptions { pub pool: Option<String>, pub runtime: Option<RuntimeKind>, pub warm: Option<bool>, pub max_pool_size: Option<u32>, pub replicas: Option<u32>, pub ttl_seconds_after_created: Option<u32>, pub apply: bool, pub cpus_given: bool, pub memory_given: bool, } ``` ### `FleetState` A named Fleet claim (`sandbox_state.save_fleet_claim`). ```rust pub struct FleetState { pub name: String, pub runtime_type: String, pub pool_name: String, pub status: String, pub created_at: String, pub extra: Map<String, Value>, } ``` ### `Forward` An active forward. ```rust pub struct Forward { pub guest_port: u16, pub local_addr: Option<SocketAddr>, pub url: Option<String>, pub via: ForwardVia, } impl Forward { async fn close(self) -> Result<()>; } ``` ### `ForwardVia` How a `Forward` reaches the guest port. ```rust pub enum ForwardVia { Tcp, EnvTunnel, GatewayUrl, GatewayProxy, } ``` ### `GuestDisplay` The guest display, reachable without an in-guest agent ... ```rust pub struct GuestDisplay { pub url: String, pub via: String, pub open_command: Option<Vec<String>>, } impl GuestDisplay { fn redacted_url(&self) -> String; } ``` ### `GuestOutput` Output of a guest command run without an in-guest agent ... ```rust pub enum GuestOutput { Stdout(Vec<u8>), Stderr(Vec<u8>), } ``` ### `GuestScreenshot` A guest framebuffer captured without an in-guest agent ... ```rust pub struct GuestScreenshot { pub png: Vec<u8>, pub width: u32, pub height: u32, pub via: String, } ``` ### `HttpResponse` A whole HTTP response. ```rust pub struct HttpResponse { pub status: u16, pub headers: Vec<(String, String)>, pub body: Vec<u8>, } impl HttpResponse { fn text(&self) -> String; } ``` ### `ImageInfo` The image a sandbox runs, as the one resolver ... ```rust pub struct ImageInfo { pub reference: String, pub pinned_ref: String, pub digest: String, pub variant: String, pub arch: Option<String>, pub os: String, pub emulated: bool, pub spacesd: Option<bool>, } ``` ### `ImageMode` How a provider gets a registry image onto its platform. ```rust pub enum ImageMode { Direct, Template, } ``` ### `InstanceStatus` Instance status (mirror of `cua_vmm::Status`). ```rust pub enum InstanceStatus { Running, Paused, Stopped, Provisioning, Unknown(String), } ``` ### `LocalEndpoints` How to reach a running instance (subset of ... ```rust pub struct LocalEndpoints { pub host: String, pub ports: BTreeMap<u16, u16>, pub vnc: Option<String>, pub qmp: Option<String>, pub ssh: Option<String>, pub serial_log: Option<String>, pub container_id: Option<String>, } ``` ### `LocalFleetPlan` A Fleet template translated for the local provider. ```rust pub struct LocalFleetPlan { pub template: String, pub reference: String, pub runtime: RuntimeKind, pub image: String, pub firmware: Option<String>, pub cpus: Option<u32>, pub memory_mb: Option<u64>, pub services: BTreeMap<String, u16>, pub probes: Vec<Probe>, } impl LocalFleetPlan { fn apply(&self, o: &mut CreateOptions); } ``` ### `LocalInstance` A started instance. ```rust pub struct LocalInstance { pub name: String, pub backend: String, pub status: InstanceStatus, pub endpoints: LocalEndpoints, } ``` ### `LocalProbe` A readiness probe handed to the local backend. ```rust pub struct LocalProbe { pub port: u16, pub http_path: Option<String>, } ``` ### `LocalRuntime` A local VM / container runtime. ```rust pub trait LocalRuntime: Send + Sync { fn backend(&self) -> String; async fn start(&self, spec: &LocalStartSpec) -> RuntimeResult<LocalInstance>; async fn start_resolved(&self, spec: &LocalStartSpec) -> RuntimeResult<(LocalInstance, Option<ImageInfo>)>; async fn gpu_support(&self) -> Vec<GpuSupport>; async fn stop(&self, name: &str) -> RuntimeResult<()>; async fn suspend(&self, name: &str) -> RuntimeResult<()>; async fn resume(&self, name: &str) -> RuntimeResult<LocalInstance>; fn power_control(&self, runtime_type: &str) -> Option<PowerControl>; async fn fork(&self, source: &str, new_name: &str) -> RuntimeResult<()>; async fn checkpoint(&self, name: &str, checkpoint: &str) -> RuntimeResult<()>; async fn list(&self) -> RuntimeResult<Vec<LocalSummary>>; async fn status(&self, name: &str) -> RuntimeResult<InstanceStatus>; async fn status_on(&self, name: &str, runtime_type: &str) -> RuntimeResult<InstanceStatus>; async fn delete(&self, name: &str) -> RuntimeResult<()>; async fn endpoints(&self, name: &str) -> RuntimeResult<LocalEndpoints>; async fn guest_tcp_listening(&self, name: &str, guest_port: u16) -> RuntimeResult<Option<bool>>; async fn guest_exec(&self, name: &str, script: &str, timeout: Option<Duration>, sink: Sender<GuestOutput>) -> RuntimeResult<i64>; async fn guest_screenshot(&self, name: &str) -> RuntimeResult<GuestScreenshot>; async fn guest_display(&self, name: &str) -> RuntimeResult<GuestDisplay>; async fn build_image(&self, spec: &BuildSpec, creds: Option<&RegistryCredentials>) -> RuntimeResult<String>; } ``` ### `LocalStartSpec` What to start (subset of `cua_vmm::StartSpec`). ```rust pub struct LocalStartSpec { pub name: String, pub image: String, pub os: String, pub cpus: u32, pub memory_mb: u64, pub disk_size_gb: Option<u32>, pub ports: Vec<u16>, pub env: BTreeMap<String, String>, pub command: Option<Vec<String>>, pub ready_timeout: Duration, pub firmware: Option<String>, pub sidecars: Vec<Sidecar>, pub registry_credentials: Option<RegistryCredentials>, pub container_runtime: Option<String>, pub kind: Option<String>, pub runtime: Option<String>, pub restrict_network: bool, pub gpu: Option<String>, pub probes: Vec<LocalProbe>, pub ephemeral: bool, } ``` ### `LocalState` Fields of a local sandbox state file ... ```rust pub struct LocalState { pub name: String, pub runtime_type: String, pub image: Value, pub host: String, pub api_port: u16, pub exposed_ports: Option<BTreeMap<String, u16>>, pub vnc_port: Option<u16>, pub qmp_port: Option<u16>, pub grpc_port: Option<u16>, pub adb_serial: Option<String>, pub sdk_root: Option<String>, pub disk_path: Option<String>, pub os_type: Option<String>, pub vnc_display: Option<i64>, pub memory_mb: Option<u64>, pub cpu_count: Option<u32>, pub arch: Option<String>, pub status: String, pub created_at: String, pub extra: Map<String, Value>, } ``` ### `LocalSummary` One row of `LocalRuntime::list`. ```rust pub struct LocalSummary { pub name: String, pub backend: String, pub status: InstanceStatus, } ``` ### `Location` Where a sandbox runs: the `location` vocabulary of ... ```rust pub enum Location { Local, Cloud, Direct, Relay, Contrib, } impl Location { const ALL: [Location; 4] = _;; fn as_str(self) -> &'static str; fn parse(s: &str) -> Option<Self>; } ``` ### `McpConfig` Everything an MCP client needs to reach a service's MCP ... ```rust pub struct McpConfig { pub url: String, pub headers: Vec<(String, String)>, } impl McpConfig { fn of(endpoint: &ServiceEndpoint, path: Option<&str>) -> Self; fn for_url(url: &str, headers: Vec<(String, String)>) -> Result<Self>; async fn connect(&self) -> Result<McpClient>; } ``` ### `NetworkMode` Guest network of a sandbox. ```rust pub enum NetworkMode { Default, None, } impl NetworkMode { fn parse(s: &str) -> Result<Self>; fn as_str(self) -> &'static str; } ``` ### `placement::Axis` Which axis a `PlacementError` is about. ```rust pub enum Axis { On, Kind, Runtime, Image, } impl Axis { fn as_str(self) -> &'static str; } ``` ### `placement::Capabilities` What a location can run. ```rust pub struct Capabilities { pub name: String, pub description: String, pub kinds: Vec<KindSupport>, } impl Capabilities { fn runtimes(&self, kind: Kind) -> Vec<Runtime>; fn kinds(&self) -> Vec<Kind>; fn kind_of(&self, runtime: &Runtime) -> Option<Kind>; } ``` ### `placement::HostFacts` What is known about this host. ```rust pub struct HostFacts { pub gvisor: Option<bool>, pub apple_silicon: bool, } impl HostFacts { fn current() -> Self; } ``` ### `placement::ImageFacts` What is known about the image before a placement is ... ```rust pub struct ImageFacts { pub os: Option<String>, pub variants: Vec<Variant>, } ``` ### `placement::Kind` What kind of machine a sandbox is. ```rust pub enum Kind { Auto, Container, Vm, } impl Kind { fn as_str(self) -> &'static str; fn parse(s: &str) -> Result<Self, PlacementError>; } ``` ### `placement::KindSupport` The kinds and runtimes of one kind a location offers. ```rust pub struct KindSupport { pub kind: Kind, pub runtimes: Vec<Runtime>, } ``` ### `placement::On` Where a sandbox runs (the `--on` flag, the `on` ... ```rust pub enum On { Local, Cloud, Direct(String), Relay(String), Host(String), Provider(String), } impl On { fn location(&self) -> &str; fn is_existing_machine(&self) -> bool; fn parse(s: &str) -> Result<Self, PlacementError>; fn parse_or_host(s: &str) -> Result<Self, PlacementError>; } ``` ### `placement::PlacementError` An invalid location, kind or runtime, or a combination ... ```rust pub struct PlacementError { pub axis: Axis, pub given: String, pub message: String, pub valid: Vec<String>, } impl PlacementError { fn new(axis: Axis, given: &str, why: String, valid: Vec<String>) -> Self; } ``` ### `placement::Runtime` Which engine runs a sandbox. ```rust pub enum Runtime { Auto, Gvisor, Runc, Qemu, Lume, Kubevirt, Other(String), } impl Runtime { const BUILTIN: [Runtime; 5] = _;; fn as_str(&self) -> &str; fn parse(s: &str) -> Result<Self, PlacementError>; fn builtin_kind(&self) -> Option<Kind>; } ``` ### `placement::Selection` A checked placement. ```rust pub struct Selection { pub on: On, pub kind: Kind, pub runtime: Runtime, pub reason: String, } ``` ### `PortExposure` How guest ports are reachable from outside. ```rust pub enum PortExposure { None, Https, } ``` ### `PortTarget` Where a guest port can be reached. ```rust pub enum PortTarget { Addr { host: String, port: u16 }, Url(String), } ``` ### `PowerControl` How a sandbox turns off. ```rust pub enum PowerControl { Suspend, Stop, } impl PowerControl { fn as_str(self) -> &'static str; fn parse(word: &str) -> Option<Self>; fn off_state(self) -> PowerState; } ``` ### `PowerState` Whether a sandbox is on. ```rust pub enum PowerState { Running, Suspended, Stopped, } impl PowerState { fn as_str(self) -> &'static str; fn parse(word: &str) -> Option<Self>; fn is_off(self) -> bool; } ``` ### `Probe` A user-declared readiness probe. ```rust pub enum Probe { Tcp(u16), Http { port: u16, path: String, status: Option<u16> }, } ``` ### `Provider` A third-party sandbox platform. ```rust pub trait Provider: Send + Sync + 'static { fn name(&self) -> &'static str; fn capabilities(&self) -> ProviderCapabilities; fn check_configured(&self) -> Result<()>; async fn create(&self, spec: &ProviderCreate) -> Result<ProviderInstance>; async fn abort_create(&self, name: &str) -> Result<()>; async fn get(&self, id: &str) -> Result<ProviderInstance>; async fn list(&self) -> Result<Vec<ProviderInstance>>; async fn delete(&self, id: &str) -> Result<()>; fn endpoint(&self, instance: &ProviderInstance, port: u16) -> Result<ServiceEndpoint>; async fn suspend(&self, _id: &str) -> Result<()>; async fn resume(&self, _id: &str) -> Result<ProviderInstance>; async fn keep_alive(&self, _id: &str, _duration: Duration) -> Result<()>; fn joins_relay(&self) -> bool; fn connect_options(&self, _instance: &ProviderInstance, _port: u16) -> Option<Result<ConnectOptions>>; fn power(&self) -> Option<PowerControl>; async fn stop(&self, _id: &str) -> Result<()>; async fn start(&self, _id: &str) -> Result<ProviderInstance>; } ``` ### `ProviderCapabilities` What a provider can do. ```rust pub struct ProviderCapabilities { pub kinds: Vec<RunKind>, pub runtime: &'static str, pub arches: Vec<&'static str>, pub image_mode: ImageMode, pub ports: PortExposure, pub command: bool, pub env_to_entrypoint: bool, pub private_registry: bool, pub suspend: bool, pub max_cpus: Option<u32>, pub max_memory_mb: Option<u64>, pub credential_env: &'static [&'static str], pub gpus: Vec<GpuOption>, } ``` ### `ProviderCreate` What the core asks a provider to create. ```rust pub struct ProviderCreate { pub name: String, pub image: ProviderImage, pub cpus: u32, pub memory_mb: u64, pub env: BTreeMap<String, String>, pub command: Option<Vec<String>>, pub ports: Vec<u16>, pub ttl: Option<Duration>, pub labels: BTreeMap<String, String>, pub registry_credentials: Option<RegistryCredentials>, pub timeout: Duration, pub runtime: Runtime, pub gpu: Option<String>, } ``` ### `ProviderImage` The image the core resolved for a provider. ```rust pub struct ProviderImage { pub reference: String, pub pinned_ref: String, pub digest: String, pub kind: RunKind, pub arch: String, pub spacesd: Option<bool>, } ``` ### `ProviderInstance` A sandbox on a provider. ```rust pub struct ProviderInstance { pub id: String, pub name: String, pub status: Status, pub endpoints: BTreeMap<u16, ServiceEndpoint>, pub details: BTreeMap<String, String>, } impl ProviderInstance { fn new(id: impl Into<String>, name: impl Into<String>, status: Status) -> Self; } ``` ### `ProviderKind` Which provider backs a sandbox. ```rust pub enum ProviderKind { Fleet, Local, Direct, Contrib, } ``` ### `proxy::HttpProxy` A running proxy. ```rust pub struct HttpProxy { /* private fields */ } impl HttpProxy { async fn start(bind: SocketAddr, router: Arc<dyn ProxyRouter>) -> Result<Self>; fn local_addr(&self) -> SocketAddr; fn url(&self) -> String; fn close(self); } ``` ### `proxy::ProxyRoute` Where one proxied request goes. ```rust pub struct ProxyRoute { pub url: String, pub headers: Vec<(String, String)>, } ``` ### `proxy::ProxyRouter` Maps an incoming `path?query` to an upstream. ```rust pub trait ProxyRouter: Send + Sync + 'static { fn route(&self, path_and_query: &str) -> RouteFuture; } ``` ### `proxy::RouteDecision` The answer of a `ProxyRouter` for a request path. ```rust pub enum RouteDecision { Forward(ProxyRoute), Refuse(u16, String), } ``` ### `RunKind` How a sandbox runs on a provider (the `--kind` axis). ```rust pub enum RunKind { Container, Vm, } impl RunKind { fn as_str(self) -> &'static str; } ``` ### `RuntimeError` A local-runtime failure. ```rust pub enum RuntimeError { NotFound(String), Unsupported { backend: String, op: String }, UnsupportedImage(String), InsufficientDisk(String), InvalidPlacement(PlacementError), Other(String), } ``` ### `Sandbox` A sandbox handle. ```rust pub struct Sandbox { /* private fields */ } impl Sandbox { async fn mcp_config(&self, service: &str, path: Option<&str>) -> Result<McpConfig>; async fn mcp(&self, service: &str, path: Option<&str>) -> Result<McpClient>; fn name(&self) -> &str; fn image_info(&self) -> Option<&ImageInfo>; fn provider(&self) -> ProviderKind; fn runtime_type(&self) -> &str; fn is_ephemeral(&self) -> bool; fn id(&self) -> String; fn sandbox_ref(&self) -> SandboxRef; fn location(&self) -> &'static str; fn expires_at(&self) -> Option<SystemTime>; fn provider_details(&self) -> BTreeMap<String, String>; fn placement(&self) -> (Kind, Runtime); fn fleet_sandbox(&self) -> Option<&BoundSandbox>; fn services(&self) -> &BTreeMap<String, u16>; fn ports(&self) -> BTreeMap<u16, PortTarget>; fn port(&self, port: u16) -> Result<PortTarget>; fn service(&self, name: &str) -> Result<Service>; fn tunnel(&self) -> Tunnel; async fn spacesd(&self) -> Result<SpacesdClient>; async fn spacesd_with(&self, o: ConnectOptionsOverride) -> Result<SpacesdClient>; fn lacks_spacesd(&self) -> bool; async fn guest_exec(&self, script: &str, timeout: Option<Duration>, sink: Sender<GuestOutput>) -> Result<i64>; async fn guest_screenshot(&self) -> Result<GuestScreenshot>; async fn guest_display(&self) -> Result<GuestDisplay>; fn env_token(&self) -> Option<String>; fn declares_spacesd(&self, options: &CreateOptions) -> bool; async fn wait_spacesd(&self, timeout: Duration) -> Result<()>; async fn wait_ready(&self, probes: &[Probe], timeout: Duration) -> Result<()>; async fn status(&self) -> Result<Status>; async fn suspend(&self) -> Result<()>; async fn resume(&self) -> Result<()>; async fn restart(&self) -> Result<()>; async fn keep_alive(&self, duration: Duration) -> Result<()>; fn detach(&self); async fn delete(self) -> Result<()>; } ``` ### `Sandboxes` Creates, finds and manages sandboxes across providers. ```rust pub struct Sandboxes { /* private fields */ } impl Sandboxes { fn builder() -> SandboxesBuilder; fn state(&self) -> &StateStore; fn pools(&self) -> Result<&PoolManager>; fn contrib_provider(&self, name: &str) -> Result<&Arc<dyn Provider>>; fn clouds(&self) -> Result<&Arc<dyn CloudManager>>; fn persisted_details(&self, name: &str) -> BTreeMap<String, String>; fn cloud_of_relay_machine(&self, machine: &str) -> Option<(String, String)>; fn by_relay_machine(&self, machine: &str) -> Result<Option<String>>; fn contrib_providers(&self) -> Vec<Arc<dyn Provider>>; async fn create(&self, options: CreateOptions) -> Result<Sandbox>; async fn create_cancellable(&self, options: CreateOptions, cancel: CancellationToken) -> Result<Sandbox>; async fn create_tracked(&self, options: CreateOptions) -> Result<Sandbox>; async fn cancel_create(&self, name: &str) -> Result<Option<String>>; fn connect_url(&self, url: &str, token: Option<String>) -> Result<Sandbox>; fn connect_url_named(&self, url: &str, token: Option<String>, name: Option<&str>) -> Result<Sandbox>; async fn gpu_support(&self, provider: ProviderKind, contrib: Option<&str>) -> Vec<GpuSupport>; async fn local_name_in_use(&self, name: &str) -> bool; async fn delete_local_instance(&self, name: &str) -> Result<bool>; async fn connect(&self, name: &str) -> Result<Sandbox>; async fn connect_ref(&self, wanted: &SandboxRef) -> Result<Sandbox>; async fn connect_cloud(&self, name: &str, namespace: Option<&str>) -> Result<Sandbox>; fn recorded_cloud_image(&self, name: &str, pool: &str) -> Option<ImageInfo>; async fn known_refs(&self) -> Result<Vec<SandboxRef>>; async fn resolve_ref(&self, wanted: &SandboxRef) -> Result<SandboxRef>; async fn resolve_ref_among(&self, wanted: &SandboxRef, extra: Vec<(String, SandboxRef)>) -> Result<SandboxRef>; fn remember_direct(&self, name: &str, url: &str) -> Result<()>; async fn list(&self) -> Result<Vec<SandboxInfo>>; async fn get(&self, name: &str) -> Result<SandboxInfo>; async fn suspend(&self, name: &str) -> Result<()>; async fn resume(&self, name: &str) -> Result<()>; async fn restart(&self, name: &str) -> Result<()>; fn power_control(&self, name: &str) -> Option<PowerControl>; fn power_state(&self, name: &str) -> Option<PowerState>; async fn power_off(&self, name: &str) -> Result<PowerState>; async fn power_off_as(&self, name: &str, control: PowerControl) -> Result<PowerState>; async fn power_on(&self, name: &str) -> Result<()>; async fn delete(&self, name: &str) -> Result<()>; async fn keep_alive(&self, name: &str, duration: Duration) -> Result<()>; } ``` ### `SandboxesBuilder` Builder for `Sandboxes`. ```rust pub struct SandboxesBuilder { /* private fields */ } impl SandboxesBuilder { fn fleet(self, fleet: FleetClient) -> Self; fn pool_manager(self, pools: PoolManager) -> Self; fn local(self, runtime: Arc<dyn LocalRuntime>) -> Self; fn provider(self, provider: Arc<dyn Provider>) -> Self; fn clouds(self, clouds: Arc<dyn CloudManager>) -> Self; fn state_dir(self, dir: impl Into<PathBuf>) -> Self; fn build(self) -> Sandboxes; } ``` ### `SandboxInfo` Listing row. ```rust pub struct SandboxInfo { pub id: String, pub name: String, pub provider: ProviderKind, pub runtime_type: String, pub status: Status, } ``` ### `SandboxRef` A parsed sandbox reference. ```rust pub enum SandboxRef { Local { name: String }, Cloud { name: String, namespace: Option<String> }, Direct { authority: String }, Relay { machine_id: String }, Contrib { provider: String, name: String }, Bare { name: String }, } impl SandboxRef { fn parse(input: &str) -> Result<Self>; fn qualified(location: Location, name: &str) -> Result<Self>; fn location(&self) -> Option<Location>; fn location_word(&self) -> Option<&str>; fn is_qualified(&self) -> bool; fn name(&self) -> &str; fn namespace_hint(&self) -> Option<&str>; fn narrow(self, location: Option<Location>) -> Result<Self>; } ``` ### `SandboxState` One state file. ```rust pub enum SandboxState { Fleet(FleetState), Local(LocalState), } impl SandboxState { fn sandbox_ref(&self) -> SandboxRef; fn name(&self) -> &str; fn runtime_type(&self) -> &str; fn status(&self) -> &str; } ``` ### `Service` A named service of a sandbox. ```rust pub struct Service { /* private fields */ } impl Service { async fn mcp_config(&self, path: Option<&str>) -> Result<McpConfig>; async fn mcp(&self, path: Option<&str>) -> Result<McpClient>; fn name(&self) -> &str; fn url(&self) -> &str; async fn request(&self, method: &str, path: &str, body: Option<Vec<u8>>, timeout: Duration) -> Result<HttpResponse>; async fn endpoint(&self) -> Result<ServiceEndpoint>; async fn open(&self, method: &str, path: &str, headers: &[(String, String)], body: RequestBody, head_timeout: Option<Duration>) -> Result<StreamingResponse>; async fn request_with_headers(&self, method: &str, path: &str, headers: &[(String, String)], body: Option<Vec<u8>>, timeout: Duration) -> Result<HttpResponse>; } ``` ### `ServiceEndpoint` Where a service is reachable from this process: a base ... ```rust pub struct ServiceEndpoint { pub url: String, pub headers: Vec<(String, String)>, } impl ServiceEndpoint { fn url_for(&self, path: &str) -> String; } ``` ### `settings::Entry` A setting's effective value. ```rust pub struct Entry { pub key: Key, pub value: String, pub source: Source, } ``` ### `settings::Key` A setting. ```rust pub struct Key { pub name: &'static str, pub env: &'static str, pub default: &'static str, pub description: &'static str, } ``` ### `settings::Resolved` The effective location, kind and runtime of a request ... ```rust pub struct Resolved { pub on: On, pub on_source: Source, pub kind: Kind, pub kind_source: Source, pub runtime: Runtime, pub runtime_source: Source, } ``` ### `settings::Settings` The settings as one process sees them: an environment ... ```rust pub struct Settings { /* private fields */ } impl Settings { fn load() -> Result<Self, SettingsError>; fn load_with(path: impl Into<PathBuf>, env: impl Fn(&str) -> Option<String>) -> Result<Self, SettingsError>; fn path(&self) -> &Path; fn entry(&self, key: Key) -> Entry; fn list(&self) -> Vec<Entry>; fn configured(&self, key: Key) -> Option<String>; fn lookup(&self, env_var: &str) -> Option<String>; fn set(&mut self, key: Key, value: &str) -> Result<String, SettingsError>; fn unset(&mut self, key: Key) -> Result<bool, SettingsError>; fn default_on(&self) -> Result<(On, Source), SettingsError>; fn default_kind(&self) -> Result<(Kind, Source), SettingsError>; fn default_runtime(&self) -> Result<(Runtime, Source), SettingsError>; fn resolve(&self, on: Option<On>, kind: Option<Kind>, runtime: Option<Runtime>) -> Result<Resolved, SettingsError>; } ``` ### `settings::SettingsError` A bad setting name or value. ```rust pub struct SettingsError(pub String); ``` ### `settings::Source` Where an effective value came from. ```rust pub enum Source { Explicit, Env(&'static str), Config(PathBuf), Default, } impl Source { fn kind(&self) -> &'static str; fn is_user_set(&self) -> bool; } ``` ### `StateStore` The state directory (`~/.cua/sandboxes` by default). ```rust pub struct StateStore { /* private fields */ } impl StateStore { fn new(dir: impl Into<PathBuf>) -> Self; fn dir(&self) -> &Path; fn save(&self, state: &SandboxState) -> Result<()>; fn restrict(&self, name: &str) -> Result<()>; fn save_fleet_claim(&self, name: &str, pool_name: &str) -> Result<()>; fn load(&self, name: &str) -> Option<SandboxState>; fn load_raw(&self, name: &str) -> Option<Map<String, Value>>; fn update(&self, name: &str, fields: Map<String, Value>) -> Result<()>; fn set_status(&self, name: &str, status: &str) -> Result<()>; fn delete(&self, name: &str) -> Result<()>; fn write_lease(&self, name: &str, pid: u32) -> Result<()>; fn remove_lease(&self, name: &str) -> Result<()>; fn leases(&self) -> Vec<EphemeralLease>; fn list_all(&self) -> Vec<SandboxState>; } ``` ### `Status` Coarse lifecycle status. ```rust pub enum Status { Running, Suspended, Stopped, Provisioning, Unknown(String), } ``` ### `Tunnel` TCP forwarding for a sandbox. ```rust pub struct Tunnel { /* private fields */ } impl Tunnel { async fn forward(&self, port: u16) -> Result<Forward>; } ``` ## Functions ```rust /// Whether `word` names your own cloud ... pub fn is_cloud_location(word: &str) -> bool /// Whether `word` is a contrib location ... pub fn is_contrib_location(word: &str) -> bool /// Looks up `name` on Fleet (read-only) as ... pub async fn local_from_fleet_template(fleet: &FleetClient, name: &str) -> Result<LocalFleetPlan> /// State-file fields of a sandbox on a ... pub fn placement_of_backend(backend: &str) -> (Kind, Runtime) /// Translates a template for the local ... pub fn plan_from_template(t: &Template) -> Result<LocalFleetPlan> /// The pool an image string names ... pub fn pool_image(image: &str) -> Option<&str> /// Pool `pool`'s template image as an ... pub async fn pool_image_info(fleet: &FleetClient, pool: &str, creds: Option<&RegistryCredentials>) -> Result<Option<ImageInfo>> /// Deletes the runtimes cua set up itself ... pub async fn remove_builtin_runtimes() -> Result<(), String> // mod byoc /// The error a cloud word without a ... pub fn not_built() -> Error // mod fleet_local /// Parses Kubernetes quantities as MiB ... pub fn parse_memory_mb(q: &str) -> Option<u64> /// Readiness probes in a template's ... pub fn probes_from_json(v: &Value, services: &BTreeMap<String, u16>) -> Vec<Probe> /// The template name an image string / ... pub fn template_name(o: &CreateOptions) -> Option<&str> // mod http /// Reads a streamed response whole. pub async fn collect(resp: StreamingResponse) -> Result<HttpResponse> /// A request body from bytes. pub fn full(body: impl Into<Bytes>) -> RequestBody /// Hop-by-hop headers (RFC 9110 §7.6.1) a ... pub fn is_hop_by_hop(name: &str) -> bool /// Opens a streaming request to `endpoint` ... pub async fn open(endpoint: &ServiceEndpoint, method: &str, path: &str, headers: &[(String, String)], body: RequestBody, head_timeout: Option<Duration>) -> Result<StreamingResponse> // mod mcp /// Connects rmcp to `config`. pub async fn connect(config: &McpConfig) -> Result<McpClient> /// The protocol revisions cua asks for ... pub fn preferred_versions() -> Vec<ProtocolVersion> // mod placement /// The capabilities of `on` (an existing ... pub fn capabilities(on: &On) -> Capabilities /// The built-in `cloud` capabilities. pub fn cloud_capabilities() -> Capabilities /// The built-in `local` capabilities. pub fn local_capabilities() -> Capabilities /// Every location: `local`, `cloud`, then ... pub fn locations() -> Vec<Capabilities> /// The registered third-party provider ... pub fn provider(name: &str) -> Option<Capabilities> /// Registers a third-party location (--on ... pub fn register_provider(caps: Capabilities) -> Result<(), PlacementError> /// Chooses the kind and runtime for a ... pub fn select(on: &On, kind: Kind, runtime: &Runtime, image: &ImageFacts, host: &HostFacts) -> Result<Selection, PlacementError> /// Checks a request against the location's ... pub fn validate(on: &On, kind: Kind, runtime: &Runtime) -> Result<Kind, PlacementError> // mod pool_image /// A template image as an `ImageInfo` for ... pub async fn template_image_info(image: &str, runtime: &RuntimeKind, creds: Option<&RegistryCredentials>) -> ImageInfo // mod provider /// Whether `word` is a provider location ... pub fn is_provider_location(word: &str) -> bool /// The error for `--on <word>` when this ... pub fn not_built(word: &str) -> Error /// `Error::Unsupported` for a contrib ... pub fn unsupported(provider: &str, op: impl Into<String>) -> Error // mod proxy /// A proxy to one upstream base URL with ... pub async fn forward_to(base: String, headers: Vec<(String, String)>) -> Result<HttpProxy> /// Joins an upstream base URL and a ... pub fn join_url(base: &str, path_and_query: &str) -> String /// Validates that `url` is a plain-HTTP ... pub fn require_loopback_http(url: &str) -> Result<()> // mod refs /// The message of Erro ... pub fn ambiguous_message(name: &str, candidates: &[SandboxRef]) -> String /// `host:port`, bracketing an IPv6 literal. pub fn authority(host: &str, port: u16) -> String /// Canonicalizes any accepted spelling ... pub fn canonical(input: &str) -> Result<String> /// Resolves `wanted` among `known` (every ... pub fn resolve(wanted: &SandboxRef, known: impl IntoIterator<Item = SandboxRef>) -> Result<SandboxRef> /// `resolve` over `(display name, ref)` ... pub fn resolve_named(wanted: &SandboxRef, known: impl IntoIterator<Item = (String, SandboxRef)>) -> Result<SandboxRef> /// Relay machine ids are DNS-label safe ... pub fn valid_machine_id(id: &str) -> bool // mod settings /// The cloud pool settings with the user's ... pub fn auto_pool_config() -> AutoPoolConfig /// The hint appended to "no cloud ... pub fn cloud_default_hint(source: &Source) -> Option<String> /// `$CUA_HOME/config.toml`. pub fn config_path() -> PathBuf /// The cua home: `$CUA_HOME`, else ... pub fn cua_home() -> PathBuf /// The setting named `name`. pub fn key(name: &str) -> Result<Key, SettingsError> /// Checks and normalises a value for `key`. pub fn normalize(key: Key, value: &str) -> Result<String, SettingsError> // mod state /// `~/.cua/sandboxes` ( ... pub fn default_state_dir() -> PathBuf /// datetime.now(timezo ... pub fn python_utc_now() -> String /// Image.from_registry(ref, os_type=... ... pub fn registry_image_dict(reference: &str, os_type: &str, kind: Option<&str>) -> Value ``` ## Constants ```rust /// Your own cloud accounts as locations ... pub const CLOUD_LOCATIONS: &[&str] = _; /// Every contrib location word the refs ... pub const CONTRIB_LOCATIONS: &[&str] = _; /// Default spacesd port. pub const ENV_PORT: u16 = 3211; /// Why a cloud (Fleet) sandbox cannot have ... pub const FLEET_NO_GPU_REASON: &str = sandbox::FLEET_NO_GPU; /// Directory of the ephemeral-sandbox ... pub const LEASE_DIR: &str = ".ephemeral"; /// How long a bare-name lookup waits for ... pub const LOOKUP_CLOUD_TIMEOUT: Duration = _; /// The error for Fleet `macos` templates ... pub const MACOS_FLEET_UNSUPPORTED: &str = /* ... */; /// The status word of a local record whose ... pub const MISSING: &str = "missing"; /// Image-string prefix naming a Fleet pool ... pub const POOL_PREFIX: &str = "pool:"; /// How long `Sandboxes::create` waits for ... pub const SPACESD_READY_TIMEOUT: Duration = _; // mod byoc /// The instance detail naming where a ... pub const DETAIL_PLACE: &str = "place"; /// The instance detail naming the relay ... pub const DETAIL_RELAY_MACHINE: &str = "relay_machine"; // mod byoc::meta /// That home's device name, for "delete it ... pub const DEVICE: &str = "cua.cloud.device"; /// That home's own relay machine, when it ... pub const HOST: &str = "cua.cloud.host"; /// The cua home that created it (its ... pub const OWNER: &str = "cua.cloud.owner"; /// Where it runs, for people ("AWS · ... pub const PLACE: &str = "cua.cloud.place"; /// The provider word (`aws`, `gcp` ... pub const PROVIDER: &str = "cua.cloud.provider"; /// The sandbox's name. pub const SANDBOX: &str = "cua.cloud.sandbox"; // mod fleet_local /// Deprecated spelling of `POOL_PREFIX` ... pub const FLEET_PREFIX: &str = "fleet:"; // mod mcp /// The default MCP endpoint path of a ... pub const DEFAULT_PATH: &str = "/mcp"; // mod settings /// Every setting, in display order. pub const KEYS: [Key; 9] = _; ``` ## Type aliases ```rust /// A connected rmcp client ... pub type McpClient = RunningService<RoleClient, ClientConfig>; /// A streamed request body. pub type RequestBody = UnsyncBoxBody<Bytes, BodyError>; /// Result alias. pub type Result<T, E = Error> = Result<T, E>; /// Result of a runtime call; errors are ... pub type RuntimeResult<T> = Result<T, RuntimeError>; /// A streamed response (the body arrives ... pub type StreamingResponse = Response<Incoming>; // mod http /// Error type of streamed bodies. pub type BodyError = Box<dyn Error + Send + Sync>; // mod proxy /// Boxed future of a `ProxyRouter`. pub type RouteFuture = Pin<Box<dyn Future<Output = RouteDecision> + Send>>; ``` ## Re-exports `sandbox::BuildFile`, `sandbox::BuildSpec`, `tokio_util::sync::CancellationToken`, `cua_vmm::cleanup`, `cua_fleet`, `cua_spacesd_client`, `cua_vmm::gpu`, `sandbox::ImageLayer`, `rmcp`, `cua_vmm::progress`, `sandbox::RegistryCredentials`, `sandbox::Sidecar`. --- # cua-spaces: registry and primitives The Spaces registry and primitives (files, exec, streams, presence, services, teleport, hotspot, relay). Public API of the `cua-spaces` crate (`cua_spaces`). Summaries and declarations only (`cargo doc -p cua-spaces --open` in `libs/cua` has the full comments); see [Rust crates](</docs/cua-sdk/reference/rust>) for stability. Cua Spaces: interactive sandboxes on any image, and everything a person or an agent does with one. cua-spacesd, when the image runs it, adds the desktop, shell, files, streaming and teleport primitives. More of this crate: [client](</docs/cua-sdk/reference/rust/cua-spaces-client>), [agents and MCP](</docs/cua-sdk/reference/rust/cua-spaces-agents-mcp>). ## Modules - `app_icon`: App icons for a Space's windows, as the guest's own ... - `audit`: A hash-chained audit log (the share audit). - `cloud`: Spaces in your own cloud account: a cloud sandbox ... - `creating`: Creates in flight: cancelling one, and finishing what a ... - `error`: One error type for every Spaces call, with a stable ... - `exec`: Shell and literal-file primitives over the spacesd's ... - `extension`: Extension points for the capabilities that ship with ... - `files`: Moving files between this host and a Space. - `fleet_runtime`: The Fleet runtime ↔ image pairing for Spaces. - `groups`: Group chats: one human and two to six Bots in one ... - `host_spaces`: Spaces on your machines: a machine set up with cua ... - `hotspot`: Hotspot: a Space borrows this machine's network. - `id`: Space ids: the unified sandbox ref scheme (a Space is a ... - `install_cache`: Pinned install archives, fetched once on this machine ... - `operator`: Presenting a Space to the human: picture-in-picture ... - `presence`: Presence: who is looking at a Space and where their ... - `presence::art`: The shared presence cursor art (ass ... - `presence::view`: The presence netcode model every client draws from ... - `reattach`: Keeping a host-served link to a Space alive across ... - `registry`: The persisted Spaces registry. - `relay`: `relay:<machine-id>`: machines of the signed-in cua.ai ... - `routines`: Routines: recurring tasks a Bot runs on a schedule. - `routines::iso`: ISO 8601 in UTC with whole seconds, the portable form ... - `routines::iso::option` - `services`: Tools inside a Space, from two kinds of MCP service: - `share`: Sharing a Space with other accounts, to watch or to ... - `site_login`: The Keyvault seam and the whole `request_site_login` ... - `stream`: Streams: targets, media tickets, and a Rust media ... - `teleport_broker`: The Keyvault broker seam the `teleport_app` MCP tool ... - `thumbnails`: Space thumbnails, cached once for every client on this ... - `volume`: A volume mounted in a Space's guest, served from this ... - `walk`: Host-side folder walks for transfers, optionally ... ## Types ### `app_icon::IconPlatform` Which lookup a guest gets. ```rust pub enum IconPlatform { Macos, Linux, } impl IconPlatform { fn of(os: OsFamily) -> Option<IconPlatform>; } ``` ### `AppIcon` An app icon, normalized by the icon cache. ```rust pub struct AppIcon { pub bytes: Vec<u8>, pub bytes_1x: Vec<u8>, pub content_type: &'static str, } ``` ### `audit::AuditLog` An append-only, hash-chained log file. ```rust pub struct AuditLog { /* private fields */ } impl AuditLog { fn new(path: impl Into<PathBuf>) -> AuditLog; fn path(&self) -> &Path; fn append(&self, principal: &str, action: &str, path: &str, detail: &str) -> Result<()>; fn tail(&self, limit: usize) -> Result<(Vec<Event>, Result<(), String>)>; fn verify(&self) -> Result<()>; } ``` ### `audit::Event` The hashed part of a line (field order is the canonical ... ```rust pub struct Event { pub seq: u64, pub ts_ms: u64, pub principal: String, pub action: String, pub path: String, pub detail: String, pub prev: String, } ``` ### `CancelOutcome` The result of `Spaces::cancel_create`. ```rust pub struct CancelOutcome { pub id: String, pub state: CancelState, pub message: String, } ``` ### `CancelState` What `Spaces::cancel_create` found and did. ```rust pub enum CancelState { Cancelled, NotCreating, AlreadyCreated, } impl CancelState { fn as_str(&self) -> &'static str; } ``` ### `creating::Made` One thing a create made, recorded as soon as it exists. ```rust pub enum Made { LocalSandbox { name: String, fresh: bool }, FleetClaim { namespace: String, name: String }, RelayMachine { id: String }, HostSpace { host: String, machine: String }, } ``` ### `Error` Spaces errors. ```rust pub enum Error { InvalidArgument(String), NotFound(String), SpacesdNotAvailable { space: String, reason: String }, CapabilityMissing { space: String, feature: String, limitation: String }, HostCapabilityMissing { what: String, why: String }, WrongProvider { op: String, provider: String }, Transfer(String), TeleportRefused(String), LoginRefused(String), Agent(String), Timeout(String), Stream(String), Env(Error), Fleet(Error), Sandbox(Error), Io(Error), Json(Error), Relay(Error), Mcp(String), LimitExceeded(String), AmbiguousHost { query: String, choices: Vec<String>, message: String }, Extension { kind: &'static str, message: String }, Cancelled(String), } impl Error { fn tag(&self) -> &'static str; fn extension(kind: &str, message: impl Into<String>) -> Self; fn needs_cua_spaces(what: &str) -> Self; fn invalid(msg: impl Into<String>) -> Self; fn host(what: &str, why: impl Into<String>) -> Self; } ``` ### `exec::BashOutput` The result of `Space::bash`. ```rust pub struct BashOutput { pub stdout: String, pub stderr: String, pub exit_code: Option<i32>, pub signal: Option<String>, pub timed_out: bool, pub error: Option<String>, } impl BashOutput { fn success(&self) -> bool; fn render(&self) -> String; } ``` ### `exec::WriteReport` The result of `Space::write`. ```rust pub struct WriteReport { pub path: String, pub bytes: u64, pub sha256: String, } ``` ### `extension::SpacesExtension` A capability served outside this crate. ```rust pub trait SpacesExtension: Send + Sync + 'static { fn name(&self) -> &str; fn serves(&self, tool: &str) -> bool; fn call_tool<'a>(&'a self, call: ToolCall<'a>) -> BoxFuture<'a, Result<ToolOutcome>>; fn space_connected(&self, spaces: &Spaces, space: &str); fn space_dropped<'a>(&'a self, spaces: &'a Spaces, space: &'a str) -> BoxFuture<'a, ()>; fn as_any(&self) -> &dyn Any; } ``` ### `extension::ToolCall` One tool call routed to an extension. ```rust pub struct ToolCall<'a> { pub spaces: &'a Spaces, pub tool: &'a str, pub args: Value, pub broker: Option<&'a Arc<dyn SessionBroker>>, pub pinned: Option<&'a Space>, } ``` ### `files::Conflict` What happens when a sent file already exists in the ... ```rust pub enum Conflict { Overwrite, Rename, Skip, } ``` ### `files::DownloadReport` The result of `Space::download`. ```rust pub struct DownloadReport { pub source: String, pub dest: String, pub kind: &'static str, pub files: usize, pub bytes: u64, pub sha256: Option<String>, pub verified: bool, } ``` ### `files::SendFileOptions` Options for `Space::send_file`. ```rust pub struct SendFileOptions { pub subdir: String, pub respect_ignore_files: bool, pub conflict: Conflict, } ``` ### `files::SendFileReport` The result of `Space::send_file`. ```rust pub struct SendFileReport { pub source: String, pub destination: String, pub dest: String, pub kind: &'static str, pub files: Vec<SentFile>, pub bytes: u64, pub respect_ignorefiles: bool, pub skipped_by_ignorefiles: Vec<String>, pub skipped_count: usize, pub skipped_by_guest: Vec<String>, pub verified: bool, } ``` ### `files::SentFile` One file placed by `Space::send_file`. ```rust pub struct SentFile { pub path: String, pub size: u64, pub sha256: String, } ``` ### `files::UploadReport` The result of `Space::upload`. ```rust pub struct UploadReport { pub source: String, pub dest: String, pub kind: &'static str, pub files: usize, pub directories: usize, pub bytes: u64, pub sha256: Option<String>, pub verified: bool, } ``` ### `groups::GroupChat` A group chat. ```rust pub struct GroupChat { pub id: String, pub title: String, pub created_at: DateTime<Utc>, pub messages: Vec<GroupMessage>, } impl GroupChat { fn new(title: &str, members: &[String]) -> Result<Self, GroupChatError>; fn member_ids(&self) -> &[String]; fn is_full(&self) -> bool; fn is_at_floor(&self) -> bool; fn membership_label(&self) -> String; fn remaining_seats(&self) -> usize; fn add(&mut self, bot_id: &str) -> Result<(), GroupChatError>; fn remove(&mut self, bot_id: &str) -> Result<(), GroupChatError>; } ``` ### `groups::GroupChatError` A membership rule was broken. ```rust pub enum GroupChatError { TooFewBots(usize), TooManyBots(usize), Full, AtFloor, AlreadyAMember(String), NotAMember(String), UnknownChat(String), } ``` ### `groups::GroupChatStore` Group chats: membership, fan-out, and the merged ... ```rust pub struct GroupChatStore { pub chats: Vec<GroupChat>, pub last_error: Option<String>, pub working: HashMap<String, BTreeSet<String>>, } impl GroupChatStore { fn new() -> Self; fn attach(&mut self, messenger: Arc<dyn GroupMessenger>); fn chat(&self, id: &str) -> Option<&GroupChat>; fn create(&mut self, title: &str, members: &[String]) -> Result<GroupChat, GroupChatError>; fn delete(&mut self, id: &str); async fn add(&mut self, bot_id: &str, chat_id: &str) -> Result<(), GroupChatError>; async fn remove(&mut self, bot_id: &str, chat_id: &str) -> Result<(), GroupChatError>; async fn send(&mut self, text: &str, chat_id: &str) -> Vec<GroupDelivery>; async fn collect_replies(&mut self, chat_id: &str) -> Vec<GroupMessage>; fn react(&mut self, emoji: &str, chat_id: &str); async fn refresh_working(&mut self, chat_id: &str); fn working_bots(&self, chat_id: &str) -> Vec<String>; } ``` ### `groups::GroupDelivery` The result of fanning one message out to one member. ```rust pub struct GroupDelivery { pub bot_id: String, pub accepted: bool, pub reason: String, } ``` ### `groups::GroupMessage` One line of a group transcript. ```rust pub struct GroupMessage { pub id: String, pub speaker: GroupSpeaker, pub text: String, pub at: DateTime<Utc>, pub undelivered: bool, pub reaction: Option<String>, } ``` ### `groups::GroupMessenger` How a group reaches its Bots. ```rust pub trait GroupMessenger: Send + Sync { async fn deliver(&self, text: &str, bot_id: &str) -> GroupDelivery; async fn latest_reply(&self, bot_id: &str) -> Option<String>; async fn is_working(&self, bot_id: &str) -> bool; async fn display_name(&self, bot_id: &str) -> String; } ``` ### `groups::GroupSpeaker` Who said a line: the human, a Bot, or the group itself. ```rust pub enum GroupSpeaker { Human, Bot { bot_id: String }, System, } ``` ### `host_spaces::HostCaller` Who asked the host for a Space. ```rust pub struct HostCaller { pub account: String, pub email: Option<String>, pub name: Option<String>, pub role: String, pub via: String, } impl HostCaller { fn local() -> Self; fn from_metadata(value: Option<&str>) -> Result<Self>; fn from_grpc_metadata(metadata: &MetadataMap) -> Result<Self>; fn label(&self) -> String; fn is_owner(&self) -> bool; fn may_create(&self) -> bool; } ``` ### `host_spaces::HostLimit` One limit of a host and how much of it is used ... ```rust pub struct HostLimit { pub resource: String, pub used: u32, pub limit: u32, pub reason: String, } ``` ### `host_spaces::HostOffer` One of your machines that provides Spaces, as ... ```rust pub struct HostOffer { pub id: String, pub name: String, pub via: String, pub online: bool, pub os: String, pub limits: Vec<HostLimit>, } ``` ### `host_spaces::HostSpacesServer` The host half: what this machine's cua daemon does for ... ```rust pub struct HostSpacesServer { /* private fields */ } impl HostSpacesServer { fn new(spaces: Spaces) -> Arc<Self>; async fn restore_direct_forwards(&self); async fn get(&self, caller: &HostCaller) -> Result<GetHostSpacesResponse>; async fn create(&self, caller: &HostCaller, req: CreateHostSpaceRequest) -> Result<HostSpace>; async fn cancel(&self, caller: &HostCaller, space: &str) -> Result<String>; async fn delete_cloud(&self, caller: &HostCaller, space: &str) -> Result<String>; async fn delete(&self, caller: &HostCaller, space: &str) -> Result<String>; async fn set_power(&self, caller: &HostCaller, space: &str, on: bool) -> Result<SetHostSpacePowerResponse>; } ``` ### `host_spaces::ResolvedHost` A machine that provides Spaces, as ... ```rust pub enum ResolvedHost { Relay(RelayMachine), Direct(DirectHost), } impl ResolvedHost { fn via(&self) -> &'static str; fn name(&self) -> String; } ``` ### `hotspot::Hotspot` A running hotspot. ```rust pub struct Hotspot { /* private fields */ } impl Hotspot { fn id(&self) -> &str; fn socks_address(&self) -> &str; fn space(&self) -> &Space; fn is_running(&self) -> bool; async fn status(&self) -> Result<HotspotStatus>; async fn stop(self) -> Result<()>; } ``` ### `hotspot::HotspotOptions` Options for `Space::start_hotspot`. ```rust pub struct HotspotOptions { pub socks_port: u32, pub set_system_proxy: bool, pub bypass: Vec<String>, pub dialer: Dialer, } ``` ### `hotspot::HotspotStatus` Hotspot state as reported by the Space. ```rust pub struct HotspotStatus { pub space: String, pub state: String, pub hotspot_id: String, pub socks_address: String, pub active_connections: u32, pub bytes_out: u64, pub bytes_in: u64, pub served_here: bool, } ``` ### `IconRequest` One app to look up: a window's app as the window list ... ```rust pub struct IconRequest { pub app_name: String, pub app_id: String, pub pid: u32, } ``` ### `install_cache::StageEvent` One staging report line, with bytes. ```rust pub struct StageEvent { pub detail: String, pub done: u64, pub total: u64, } ``` ### `operator::ControlServerDisplay` The Cua Spaces app's loopback control server. ```rust pub struct ControlServerDisplay { /* private fields */ } impl ControlServerDisplay { fn new(config: impl Into<PathBuf>) -> Self; } ``` ### `operator::NoDisplay` No display: every call reports host ... ```rust pub struct NoDisplay; ``` ### `operator::OperatorDisplay` Something that can present Spaces on the operator's ... ```rust pub trait OperatorDisplay: Send + Sync { async fn stream_window(&self, request: WindowStreamRequest) -> Result<()>; async fn pin_pip(&self, space_id: &str) -> Result<()>; async fn unpin_pip(&self, space_id: &str) -> Result<()>; async fn open_viewer(&self, space_id: &str) -> Result<()>; } ``` ### `operator::WindowStreamRequest` A request to draw one Space window on the operator's ... ```rust pub struct WindowStreamRequest { pub space_id: String, pub window_id: String, pub app_name: String, pub title: String, pub media_url: String, pub media_session_id: String, } ``` ### `PendingSpace` A Space `Spaces::create` started with `wait = false`. ```rust pub struct PendingSpace { pub id: String, pub phase: &'static str, } ``` ### `presence::art::CursorArt` One shape's art. ```rust pub struct CursorArt { pub shape: CursorShape, pub path_d: String, pub hotspot_x: f64, pub hotspot_y: f64, pub canvas: f64, pub outline_color: String, pub outline_width: f64, } ``` ### `presence::Cursor` A cursor position, normalized to `[0, 1]` within the ... ```rust pub struct Cursor { pub display_id: String, pub window_id: Option<String>, pub x: f64, pub y: f64, pub visible: bool, pub pressed: bool, pub shape: CursorShape, pub shape_source: ShapeSource, pub at_ms: f64, pub received_ms: f64, } impl Cursor { fn at(x: f64, y: f64) -> Self; fn hidden(self) -> Self; } ``` ### `presence::CursorSender` Publishes the caller's cursor at most every ... ```rust pub struct CursorSender { /* private fields */ } impl CursorSender { fn uses_datagrams(&self) -> bool; async fn update(&self, cursor: &Cursor) -> Result<()>; } ``` ### `presence::CursorShape` A pointer shape, the portable vocabulary of ... ```rust pub enum CursorShape { Arrow, Text, Pointer, ResizeNs, ResizeEw, ResizeNesw, ResizeNwse, Wait, Progress, NotAllowed, Crosshair, Grab, Grabbing, Move, } impl CursorShape { const ALL: [CursorShape; 14] = _;; fn from_wire(v: i32) -> Self; fn to_wire(self) -> i32; fn as_str(self) -> &'static str; fn parse(name: &str) -> Self; } ``` ### `presence::CursorUplink` Where outgoing cursors go: the datagram channel when ... ```rust pub trait CursorUplink: Send + Sync { async fn send(&self, cursor: &Cursor) -> Result<bool>; } ``` ### `presence::Drawable` One cursor to draw. ```rust pub struct Drawable { pub participant_id: String, pub display_name: String, pub color: String, pub is_me: bool, pub is_agent: bool, pub x: f64, pub y: f64, pub shape: CursorShape, pub shape_source: ShapeSource, pub alpha: f64, pub display_id: String, pub window_id: Option<String>, } ``` ### `presence::Identity` Who is joining. ```rust pub struct Identity { pub id: String, pub display_name: String, pub color: String, pub agent: bool, } impl Identity { fn agent(id: &str, display_name: &str) -> Self; } ``` ### `presence::Participant` A participant as seen in the roster. ```rust pub struct Participant { pub participant_id: String, pub principal_id: String, pub display_name: String, pub color: String, pub kind: String, } ``` ### `presence::PresenceDatagrams` The presence datagram channel (`cua-presence/1` over ... ```rust pub trait PresenceDatagrams: Send + Sync + 'static { fn connect<'a>(&'a self, space: &'a Space, info: &'a PresenceDatagrams, timeout: Duration) -> BoxFuture<'a, Result<DatagramChannel>>; } ``` ### `presence::PresenceEvent` One presence event. ```rust pub enum PresenceEvent { Joined { participant: Participant }, Left { participant_id: String, reason: String }, CursorMoved { participant_id: String, cursor: Cursor }, ShapeChanged { participant_id: String, shape: CursorShape, source: ShapeSource }, Heartbeat { participant_ids: Vec<String> }, KeepAlive, } ``` ### `presence::PresenceSession` A joined presence session. ```rust pub struct PresenceSession { /* private fields */ } impl PresenceSession { fn me(&self) -> &Participant; fn roster(&self) -> &[(Participant, Option<Cursor>)]; fn uses_datagrams(&self) -> bool; fn view(&self) -> PresenceView; fn sender(&self) -> CursorSender; async fn update_cursor(&self, cursor: &Cursor) -> Result<()>; async fn next_event(&mut self, timeout: Duration) -> Result<Option<PresenceEvent>>; async fn wait_for(&mut self, timeout: Duration, max_events: usize, pred: impl FnMut(&PresenceEvent) -> bool) -> Result<PresenceEvent>; async fn leave(self) -> Result<()>; } ``` ### `presence::PresenceView` Who is present and what to draw for each, folded from ... ```rust pub struct PresenceView { /* private fields */ } impl PresenceView { fn new(me: &str, delay_ms: f64) -> Self; fn me(&self) -> &str; fn set_heartbeat_interval_ms(&mut self, ms: f64); fn upsert(&mut self, participant: Participant); fn participant_ids(&self) -> Vec<String>; fn participant(&self, id: &str) -> Option<&Participant>; fn shape_of(&self, id: &str) -> Option<(CursorShape, ShapeSource)>; fn apply(&mut self, event: &PresenceEvent, local_ms: f64) -> bool; fn expire(&mut self, local_ms: f64) -> Vec<String>; fn drawables(&mut self, local_ms: f64, local_pointer: Option<(f64, f64)>) -> Vec<Drawable>; } ``` ### `presence::Roster` Who is present and where their cursors are, folded from ... ```rust pub struct Roster { pub me: String, pub entries: Vec<(Participant, Option<Cursor>)>, } impl Roster { fn from_session(session: &PresenceSession) -> Self; fn get(&self, participant_id: &str) -> Option<&(Participant, Option<Cursor>)>; fn color_of(&self, principal_id: &str) -> String; fn others(&self) -> impl Iterator<Item = &(Participant, Option<Cursor>)>; fn apply(&mut self, e: &PresenceEvent) -> bool; } ``` ### `presence::ShapeSource` How a shape was determined (cua.env ... ```rust pub enum ShapeSource { Unspecified, HitTest, System, Probe, } impl ShapeSource { fn from_wire(v: i32) -> Self; fn to_wire(self) -> i32; fn as_str(self) -> &'static str; fn parse(name: &str) -> Self; } ``` ### `presence::view::CursorSmoother` One remote cursor's interpolation buffer. ```rust pub struct CursorSmoother { /* private fields */ } impl CursorSmoother { fn new(delay_ms: f64) -> Self; fn push(&mut self, server_ms: f64, local_ms: f64, x: f64, y: f64, target: &str); fn reset(&mut self); fn position(&self, local_ms: f64) -> Option<(f64, f64)>; fn alpha(&self, local_ms: f64) -> f64; } ``` ### `ProgressSink` Where `SpaceCreate::progress` reports go. ```rust pub struct ProgressSink(pub Sink); impl ProgressSink { fn new(f: impl Fn(&CreateProgress) + Send + Sync + 'static) -> Self; } ``` ### `Provider` Which provider backs a Space: the `location` vocabulary. ```rust pub enum Provider { Cloud, Local, Direct, Relay, } impl Provider { fn as_str(self) -> &'static str; fn parse(s: &str) -> Option<Self>; } ``` ### `reattach::Attempt` What a re-attach attempt did. ```rust pub enum Attempt { Attached, Unsupported, } ``` ### `reattach::Backoff` Exponential backoff between failed attempts. ```rust pub struct Backoff { /* private fields */ } impl Backoff { fn new(policy: &Policy) -> Self; fn fail(&mut self) -> Duration; fn reset(&mut self); } ``` ### `reattach::Health` What a link looks like right now. ```rust pub enum Health { Healthy, Down, Gone, } ``` ### `reattach::Policy` Timing of `supervise`. ```rust pub struct Policy { pub poll: Duration, pub initial_backoff: Duration, pub max_backoff: Duration, } ``` ### `RecoveredCreate` `container:ghcr.io/x:1` -> `ghcr.io/x:1` (the local ... ```rust pub struct RecoveredCreate { pub id: String, pub outcome: RecoveryOutcome, } ``` ### `RecoveryOutcome` The fate of an interrupted create. ```rust pub enum RecoveryOutcome { Registered, Deleted(String), Failed(String), AlreadyRegistered, NothingLeft, } ``` ### `registry::Credential` How to reach a Space again: its URL and spacesd token. ```rust pub struct Credential { pub url: Option<String>, pub token: Option<String>, pub service_urls: BTreeMap<String, String>, pub namespace: Option<String>, } ``` ### `registry::DirectHost` A machine that provides Spaces over its direct listener ... ```rust pub struct DirectHost { pub name: String, pub space: String, pub spaces: BTreeMap<String, String>, } ``` ### `registry::Registry` The registry on disk. ```rust pub struct Registry { /* private fields */ } impl Registry { fn new(dir: impl Into<PathBuf>) -> Self; fn dir(&self) -> &Path; fn spaces_path(&self) -> PathBuf; fn credentials_path(&self) -> PathBuf; fn list(&self) -> Result<Vec<Space>>; fn get(&self, id: &str) -> Result<Option<Space>>; fn credential(&self, id: &str) -> Result<Option<Credential>>; fn lock_path(&self) -> PathBuf; fn direct_hosts_path(&self) -> PathBuf; fn direct_hosts(&self) -> Result<Vec<DirectHost>>; fn direct_host_of(&self, id: &str) -> Result<Option<(DirectHost, String)>>; fn update_direct_hosts(&self, change: impl FnOnce(&mut Vec<DirectHost>)) -> Result<()>; fn upsert_direct_host(&self, name: &str, space: &str) -> Result<()>; fn forget_direct(&self, id: &str) -> Result<bool>; fn upsert(&self, space: Space, credential: Credential) -> Result<()>; fn remove(&self, id: &str) -> Result<bool>; } ``` ### `RelayAccount` The relay and the account Spaces lists machines for. ```rust pub struct RelayAccount { pub url: String, pub tokens: Arc<dyn AccountTokens>, pub device: Option<Arc<DeviceAuth>>, } impl RelayAccount { fn new(url: impl Into<String>, tokens: Arc<dyn AccountTokens>) -> Self; fn with_device(self, device: Arc<DeviceAuth>) -> Self; async fn device_session(&self) -> Option<String>; async fn client(&self) -> Result<RelayClient>; } ``` ### `routines::FileStorage` A JSON file (written atomically). ```rust pub struct FileStorage(pub PathBuf); ``` ### `routines::FiringRecord` One line of the scheduler's log, newest first. ```rust pub struct FiringRecord { pub routine_id: String, pub title: String, pub at: DateTime<Utc>, pub firing: RoutineFiring, } ``` ### `routines::MemoryStorage` In memory. ```rust pub struct MemoryStorage(pub Mutex<Option<String>>); ``` ### `routines::Routine` One routine. ```rust pub struct Routine { pub id: String, pub bot_id: String, pub title: String, pub prompt: String, pub schedule: Schedule, pub is_enabled: bool, pub created_at: DateTime<Utc>, pub last_fired_at: Option<DateTime<Utc>>, pub last_run_id: Option<String>, pub last_outcome: Option<String>, } impl Routine { fn next_fire(&self, reference: DateTime<Utc>) -> Option<DateTime<Utc>>; fn is_due(&self, now: DateTime<Utc>) -> bool; fn turn_text(&self) -> String; } ``` ### `routines::RoutineFiring` What happened when a routine fired. ```rust pub enum RoutineFiring { Started { run_id: String }, Refused { reason: String }, Failed { reason: String }, } impl RoutineFiring { fn summary(&self) -> String; fn run_id(&self) -> Option<&str>; } ``` ### `routines::RoutineRunner` Gives a Bot its routine turn. ```rust pub trait RoutineRunner: Send + Sync { async fn fire(&self, routine: &Routine) -> RoutineFiring; } ``` ### `routines::RoutineStorage` Where the routine list lives. ```rust pub trait RoutineStorage: Send + Sync { fn load(&self) -> Result<Option<String>>; fn save(&self, json: &str) -> Result<()>; } ``` ### `routines::RoutineStore` Routines: storage, editing, and the clock that fires ... ```rust pub struct RoutineStore { pub routines: Vec<Routine>, pub log: Vec<FiringRecord>, } impl RoutineStore { fn new(storage: Box<dyn RoutineStorage>) -> Self; fn attach(&mut self, runner: Arc<dyn RoutineRunner>); fn runner(&self) -> Option<Arc<dyn RoutineRunner>>; fn load(&mut self); fn save(&mut self) -> bool; fn routines_for(&self, bot_id: &str) -> Vec<Routine>; fn routine(&self, id: &str) -> Option<&Routine>; fn create(&mut self, bot_id: &str, title: &str, prompt: &str, schedule: Schedule, enabled: bool, now: DateTime<Utc>) -> Routine; fn update(&mut self, routine: Routine); fn delete(&mut self, id: &str); fn set_enabled(&mut self, id: &str, enabled: bool); fn due(&self, now: DateTime<Utc>) -> Vec<Routine>; fn record(&mut self, routine: &Routine, firing: RoutineFiring, now: DateTime<Utc>) -> FiringRecord; async fn fire(&mut self, routine: &Routine, now: DateTime<Utc>) -> FiringRecord; async fn tick(&mut self, now: DateTime<Utc>) -> Vec<FiringRecord>; } ``` ### `routines::Schedule` The three recurrence shapes. ```rust pub enum Schedule { EveryMinutes { minutes: i64 }, DailyAt { hour: u32, minute: u32 }, WeeklyOn { weekday: u32, hour: u32, minute: u32 }, } impl Schedule { fn next_fire(&self, reference: DateTime<Utc>) -> Option<DateTime<Utc>>; fn label(&self) -> String; } ``` ### `services::ServiceRoute` Where a `service` argument leads. ```rust pub enum ServiceRoute { Driver, Mcp(String), } impl ServiceRoute { fn name(&self) -> &str; } ``` ### `services::ToolInfo` A tool description. ```rust pub struct ToolInfo { pub name: String, pub description: String, pub input_schema: Value, pub output_schema: Option<Value>, pub read_only: bool, pub destructive: bool, } ``` ### `services::ToolResult` A tool call's result, as MCP content parts. ```rust pub struct ToolResult { pub content: Vec<Value>, pub structured: Option<Value>, pub is_error: bool, pub meta: Option<Value>, } impl ToolResult { fn from_mcp(raw: Value) -> Self; } ``` ### `ServiceSource` How a declared service is reached. ```rust pub enum ServiceSource { Sandbox(Service), Fleet { fleet: FleetClient, bound: BoundSandbox, service: String }, Url(ServiceEndpoint), } ``` ### `share::RelayRegistration` A Space published on the relay. ```rust pub struct RelayRegistration { pub space: String, pub relay_space: String, pub machine: String, pub url: String, pub online: bool, } ``` ### `share::ShareAudit` One owner-side audit line (newest first in listings). ```rust pub struct ShareAudit { pub ts_ms: u64, pub action: String, pub space: String, pub detail: String, } ``` ### `share::ShareConsent` The user's confirmation before a Space is shared ... ```rust pub trait ShareConsent: Send + Sync { fn confirm(&self, reason: &str) -> Result<(), String>; } ``` ### `share::ShareEntry` One person a Space is shared with. ```rust pub struct ShareEntry { pub who: String, pub role: ShareRole, pub connected: bool, } ``` ### `share::ShareRole` What a person may do in a shared Space. ```rust pub enum ShareRole { Viewer, Editor, } impl ShareRole { fn parse(s: &str) -> Result<ShareRole>; fn as_str(self) -> &'static str; } ``` ### `share::SpaceShares` A Space's shares. ```rust pub struct SpaceShares { pub space: String, pub machine: String, pub invitee_space: String, pub url: String, pub online: bool, pub shares: Vec<ShareEntry>, } ``` ### `site_login::BrowserTab` The browser tab to sign in (cua-driver ids, all ... ```rust pub struct BrowserTab { pub session: Option<String>, pub target_id: Option<String>, pub tab_id: Option<String>, } ``` ### `site_login::SiteLoginAsk` One sign-in request. ```rust pub struct SiteLoginAsk { pub target: String, pub url: String, pub username: Option<String>, pub agent: Option<String>, pub browser: BrowserTab, pub relay_plaintext_ack: bool, } ``` ### `site_login::SiteLoginBroker` The Keyvault operations `request_site_login` uses. ```rust pub trait SiteLoginBroker: Send + Sync { async fn request_login(&self, ask: &SiteLoginAsk) -> Result<String, SiteLoginError>; async fn await_and_fill(&self, request_id: &str, ask: &SiteLoginAsk, timeout: Duration) -> Result<SiteLoginOutcome, SiteLoginError>; } ``` ### `site_login::SiteLoginError` A Keyvault failure, surfaced with its code (`denied` ... ```rust pub struct SiteLoginError { pub code: String, pub message: String, } ``` ### `site_login::SiteLoginFilled` What a sign-in did. ```rust pub struct SiteLoginFilled { pub site: String, pub origin: String, pub username_hint: String, pub submitted: bool, pub page_url: String, pub browser: BrowserTab, } ``` ### `site_login::SiteLoginOutcome` The outcome of awaiting a decision and (when granted) ... ```rust pub enum SiteLoginOutcome { Pending, Denied(String), Filled(SiteLoginFilled), } ``` ### `Space` A connected Space. ```rust pub struct Space { /* private fields */ } impl Space { async fn app_icon(&self, app_name: &str, app_id: &str, pid: u32) -> Result<Option<AppIcon>>; async fn app_icons(&self, requests: &[IconRequest]) -> Result<Vec<Option<AppIcon>>>; fn shell_command(&self, line: &str) -> Command; async fn bash(&self, command: &str, timeout: Duration) -> Result<BashOutput>; async fn write(&self, path: &str, content: impl Into<Bytes>) -> Result<WriteReport>; async fn home(&self) -> Result<String>; fn services(&self) -> Vec<String>; fn resolve_service(&self, service: Option<&str>) -> Result<ServiceRoute>; async fn mcp(&self, service: &str) -> Result<Arc<McpClient>>; async fn list_tools(&self, service: Option<&str>) -> Result<(Vec<ToolInfo>, String)>; async fn call_tool(&self, service: Option<&str>, tool: &str, arguments: Map<String, Value>, timeout: Option<Duration>) -> Result<ToolResult>; fn generic(id: SpaceId, name: impl Into<String>, services: BTreeMap<String, SpaceService>) -> Self; fn attach(id: SpaceId, name: impl Into<String>, env: SpacesdClient, caps: GetCapabilitiesResponse, ws_headers: Vec<(String, String)>) -> Self; fn with_services(self, services: BTreeMap<String, SpaceService>) -> Self; fn image(&self) -> Option<(&str, &str)>; fn platform(&self) -> (String, String); fn id(&self) -> &SpaceId; fn name(&self) -> &str; fn provider(&self) -> Provider; fn spacesd(&self) -> Result<&SpacesdClient>; fn has_spacesd(&self) -> bool; fn capabilities(&self) -> &GetCapabilitiesResponse; fn declared_services(&self) -> &BTreeMap<String, SpaceService>; fn env_token(&self) -> Option<&str>; fn feature(&self, name: &str) -> Option<&Feature>; fn supports(&self, name: &str) -> bool; fn require(&self, name: &str) -> Result<()>; fn require_any(&self, names: &[&str]) -> Result<()>; fn os_family(&self) -> OsFamily; fn reported_os(&self) -> Option<(&'static str, &str, &str)>; fn is_windows(&self) -> bool; async fn websocket_headers(&self) -> Result<Vec<(String, String)>>; fn websocket_url(&self, ws_path: &str) -> Result<String>; async fn agents(&self) -> Result<Agents>; async fn send_file(&self, local: &Path, options: SendFileOptions) -> Result<SendFileReport>; async fn upload(&self, local: &Path, dest: Option<&str>) -> Result<UploadReport>; async fn download(&self, remote: &str, dest_dir: &Path) -> Result<DownloadReport>; async fn start_hotspot(&self, options: HotspotOptions) -> Result<Hotspot>; async fn hotspot_status(&self) -> Result<HotspotStatus>; async fn stage_install_archives(&self, ids: &[&str], arch: &str, home: &str, events: UnboundedSender<StageEvent>) -> Result<Vec<String>>; fn presence_participant(&self) -> Option<String>; async fn join_presence(&self, identity: Identity, timeout: Duration) -> Result<PresenceSession>; async fn join_presence_with(&self, identity: Identity, timeout: Duration, datagrams: bool) -> Result<PresenceSession>; async fn set_presence_settings(&self, cursor_probe: Option<bool>) -> Result<()>; async fn displays(&self) -> Result<Vec<DisplayInfo>>; async fn window_thumbnail(&self, window_id: &str, epoch: u64, max_dimension: u32) -> Result<Option<Vec<u8>>>; async fn usage(&self) -> Result<Usage>; async fn stream_targets(&self, include_windows: bool) -> Result<ListTargetsResponse>; async fn windows(&self, app: Option<&str>) -> Result<Vec<WindowTarget>>; async fn all_windows(&self, app: Option<&str>) -> Result<Vec<WindowTarget>>; async fn find_window(&self, app: &str) -> Result<WindowTarget>; async fn open_stream(&self, target: StreamTarget, options: StreamOptions) -> Result<StreamTicket>; async fn close_stream(&self, media_session_id: &str) -> Result<()>; async fn stream_session(&self, target: StreamTarget, options: StreamOptions, frames: Arc<dyn FrameSink>, audio: Option<Arc<dyn AudioSink>>) -> Result<StreamSession>; async fn attach_volume<D, F>(&self, dial: D, mount_path: Option<String>) -> Result<VolumeAttachment> where D: Fn(String, u16) -> F + Send + Sync + 'static, F: Future<Output = Result<TcpStream>> + Send + 'static; fn volume_backend(&self) -> Option<String>; async fn volume_status(&self) -> Result<VolumeGuestStatus>; } ``` ### `SpaceCreate` Options for `Spaces::create`: the same location / kind ... ```rust pub struct SpaceCreate { pub image: Option<String>, pub on: Option<On>, pub kind: Kind, pub runtime: Runtime, pub name: Option<String>, pub cpus: Option<u32>, pub memory_mb: Option<u64>, pub disk_gb: Option<u32>, pub timeout: Option<Duration>, pub wait: Option<bool>, pub reuse: bool, pub command: Option<Vec<String>>, pub env: BTreeMap<String, String>, pub services: BTreeMap<String, u16>, pub spacesd: Option<bool>, pub env_token: Option<String>, pub progress: Option<ProgressSink>, pub create_id: Option<String>, pub gpu: Option<String>, } ``` ### `SpaceCreated` What `Spaces::create` returns. ```rust pub enum SpaceCreated { Ready { info: SpaceInfo, reused: bool }, Starting(PendingSpace), } impl SpaceCreated { fn ready(self) -> Result<SpaceInfo, PendingSpace>; fn reused(&self) -> bool; } ``` ### `SpaceId` A parsed Space id. ```rust pub enum SpaceId { Cloud { name: String, namespace: Option<String> }, Local { name: String }, Direct { authority: String }, Relay { machine_id: String }, } impl SpaceId { fn parse(input: &str) -> Result<Self>; fn from_ref(r: SandboxRef) -> Option<Self>; fn to_ref(&self) -> SandboxRef; fn provider(&self) -> Provider; fn short_name(&self) -> &str; } ``` ### `SpaceInfo` A registered Space as listed, whether or not it is ... ```rust pub struct SpaceInfo { pub id: String, pub name: String, pub provider: Provider, pub spacesd_version: String, pub features: Vec<String>, pub os: String, pub os_name: String, pub os_pretty_name: String, pub image: String, pub image_digest: String, pub kind: String, pub arch: String, pub services: Vec<String>, pub added_at: Option<String>, pub host: String, pub host_name: String, pub power: String, pub power_state: String, pub cloud: String, pub cloud_place: String, pub cloud_delete: String, pub status: String, } ``` ### `SpacePower` What `crate::Spaces::stop` or `crate::Spaces::start` ... ```rust pub struct SpacePower { pub space: String, pub state: String, pub power: String, pub message: String, } impl SpacePower { fn new(space: &str, control: PowerControl, state: PowerState) -> Self; } ``` ### `Spaces` The Spaces runtime: a registry of spacesd sandboxes and ... ```rust pub struct Spaces { /* private fields */ } impl Spaces { fn cloud_sandbox_of(&self, space: &str) -> Result<Option<String>>; async fn cancel_create(&self, key: &str) -> Result<CancelOutcome>; async fn hosts(&self) -> Result<Vec<HostOffer>>; async fn resolve_host(&self, query: &str) -> Result<ResolvedHost>; async fn add_direct_host(&self, url: &str, token: Option<String>, name: Option<String>) -> Result<SpaceInfo>; async fn host_spaces(&self, host: &str) -> Result<GetHostSpacesResponse>; fn builder() -> SpacesBuilder; fn home_dir(&self) -> &Path; fn registry(&self) -> &Registry; fn fleet(&self) -> Option<&FleetClient>; fn sandboxes(&self) -> &Sandboxes; fn operator_display(&self) -> &Arc<dyn OperatorDisplay>; fn fleet_namespace(&self) -> &str; fn list(&self) -> Result<Vec<SpaceInfo>>; async fn list_all(&self) -> Result<Vec<SpaceInfo>>; fn this_relay_machine_id(&self) -> Option<String>; fn relay_account(&self) -> Option<RelayAccount>; fn set_site_login_broker(&self, broker: Option<Arc<dyn SiteLoginBroker>>); fn site_login_broker(&self) -> Option<Arc<dyn SiteLoginBroker>>; fn set_share_consent(&self, consent: Option<Arc<dyn ShareConsent>>); fn has_share_consent(&self) -> bool; fn set_relay(&self, relay: Option<RelayAccount>); async fn relay_machines(&self) -> Result<Vec<RelayMachine>>; fn resolve(&self, space: &str) -> Result<SpaceId>; async fn add(&self, url: &str, token: Option<String>, name: Option<String>) -> Result<SpaceInfo>; async fn add_with_service(&self, url: &str, token: Option<String>, name: Option<String>, service: Option<String>) -> Result<SpaceInfo>; async fn remove(&self, space: &str) -> Result<SpaceInfo>; async fn space(&self, space: &str) -> Result<Space>; async fn forget_connection(&self, space: &str) -> Result<()>; async fn create(&self, opts: SpaceCreate) -> Result<SpaceCreated>; async fn gpu_support(&self, on: &On) -> Vec<GpuSupport>; fn fleet_pool_name(&self, runtime: FleetRuntime, image: &str) -> String; async fn recover_interrupted_creates(&self, budget: Duration) -> Vec<RecoveredCreate>; async fn delete(&self, space: &str) -> Result<String>; fn thumbnails(&self) -> &ThumbnailCache; async fn thumbnail(&self, space: &str, max_age: Option<Duration>) -> Result<Thumbnail>; async fn refresh_thumbnails(&self) -> usize; fn spawn_thumbnail_refresh(&self) -> JoinHandle<()>; fn power_of(&self, id: &str) -> (String, String); async fn stop(&self, space: &str) -> Result<SpacePower>; async fn start(&self, space: &str) -> Result<SpacePower>; fn download_dir(&self) -> &Path; fn extensions(&self) -> &[Arc<dyn SpacesExtension>]; fn extension<T: SpacesExtension>(&self) -> Option<&T>; fn extension_for(&self, tool: &str) -> Option<&Arc<dyn SpacesExtension>>; async fn call_extension(&self, what: &str, op: &str, args: Value) -> Result<Value>; async fn hotspot_start(&self, space: &str, options: HotspotOptions) -> Result<HotspotStatus>; async fn hotspot_stop(&self, space: Option<&str>) -> Result<Vec<String>>; async fn hotspot_statuses(&self, space: Option<&str>) -> Result<Vec<HotspotStatus>>; fn share_audit(&self, space: Option<&str>, limit: usize) -> Result<Vec<ShareAudit>>; async fn share_space(&self, space: &str, who: &str, role: ShareRole) -> Result<SpaceShares>; async fn unshare_space(&self, space: &str, who: Option<&str>) -> Result<SpaceShares>; async fn relay_register(&self, space: &str) -> Result<RelayRegistration>; async fn relay_unregister(&self, space: &str) -> Result<bool>; async fn space_shares(&self, space: &str) -> Result<SpaceShares>; } ``` ### `SpacesBuilder` Builder for `Spaces`. ```rust pub struct SpacesBuilder { /* private fields */ } impl SpacesBuilder { fn share_consent(self, consent: Arc<dyn ShareConsent>) -> Self; fn extension(self, extension: Arc<dyn SpacesExtension>) -> Self; fn relay(self, relay: RelayAccount) -> Self; fn home(self, dir: impl Into<PathBuf>) -> Self; fn fleet(self, fleet: FleetClient) -> Self; fn local_runtime(self, runtime: Arc<dyn LocalRuntime>) -> Self; fn sandboxes(self, sandboxes: Sandboxes) -> Self; fn fleet_namespace(self, ns: impl Into<String>) -> Self; fn operator_display(self, display: Arc<dyn OperatorDisplay>) -> Self; fn probe_timeout(self, timeout: Duration) -> Self; fn download_dir(self, dir: impl Into<PathBuf>) -> Self; fn build(self) -> Spaces; } ``` ### `SpaceService` A service a Space declares (an MCP server, a web app ... ```rust pub struct SpaceService { pub source: ServiceSource, pub mcp_path: String, } impl SpaceService { async fn endpoint(&self) -> Result<ServiceEndpoint>; async fn mcp_config(&self) -> Result<McpConfig>; } ``` ### `stream::AudioPacket` One audio packet of a negotiated track. ```rust pub struct AudioPacket { pub track_id: u16, pub sequence: u32, pub pts_us: u64, pub frame_samples: u16, pub lost: u32, pub dtx: bool, pub data: Vec<u8>, } ``` ### `stream::AudioSink` Receives audio packets on the session's delivery thread. ```rust pub trait AudioSink: Send + Sync { fn on_audio(&self, packet: AudioPacket); } ``` ### `stream::DisplayInfo` One of the Space's displays (Comput ... ```rust pub struct DisplayInfo { pub id: String, pub name: String, pub primary: bool, pub width_px: u32, pub height_px: u32, pub scale_factor: f64, } ``` ### `stream::FrameSink` Receives video frames and control events. ```rust pub trait FrameSink: Send + Sync { fn on_frame(&self, frame: VideoFrame); fn on_event(&self, _event: StreamEvent); } ``` ### `stream::StreamClient` The streaming client behind `StreamSession`: it ... ```rust pub trait StreamClient: Send + Sync + 'static { fn attach<'a>(&'a self, ticket: &'a StreamTicket, headers: &'a [(String, String)], frames: Arc<dyn FrameSink>, audio: Option<Arc<dyn AudioSink>>) -> BoxFuture<'a, Result<Box<dyn StreamConnection>>>; } ``` ### `stream::StreamConnection` One attached media socket (see `StreamClient`). ```rust pub trait StreamConnection: Send + Sync { fn media_session_id(&self) -> &str; fn codec(&self) -> &str; fn is_closed(&self) -> bool; fn is_open(&self) -> bool; fn stats(&self) -> StreamStats; fn request_keyframe(&self) -> Result<()>; fn send_input(&self, events: Vec<Value>) -> Result<()>; fn send_text(&self, json: String) -> Result<()>; fn close(&mut self, wait: Duration) -> BoxFuture<'_, ()>; } ``` ### `stream::StreamEvent` A control event. ```rust pub enum StreamEvent { Opened(Value), AudioConfigured(Value), Lifecycle(Value), Stats(Value), Message(Value), Closed { code: u16, reason: String }, } ``` ### `stream::StreamOptions` Options for `Space::open_stream`. ```rust pub struct StreamOptions { pub codecs: Vec<MediaCodec>, pub max_fps: u32, pub max_dimension: u32, pub audio: bool, pub ticket_ttl: Option<Duration>, pub policy: Option<SessionPolicy>, } ``` ### `stream::StreamSession` A live media session. ```rust pub struct StreamSession { /* private fields */ } impl StreamSession { async fn attach(ticket: &StreamTicket, headers: &[(String, String)], frames: Arc<dyn FrameSink>, audio: Option<Arc<dyn AudioSink>>) -> Result<StreamSession>; fn media_session_id(&self) -> &str; fn codec(&self) -> &str; fn is_closed(&self) -> bool; fn is_open(&self) -> bool; fn stats(&self) -> StreamStats; fn request_keyframe(&self) -> Result<()>; fn send_input<E: Serialize>(&self, events: Vec<E>) -> Result<()>; fn send_text(&self, json: String) -> Result<()>; async fn close(self) -> Result<StreamStats>; } ``` ### `stream::StreamStats` Delivery counters. ```rust pub struct StreamStats { pub frames: u64, pub keyframes: u64, pub frames_dropped: u64, pub frames_gated: u64, pub keyframe_requests: u64, pub audio_packets: u64, pub audio_lost: u64, pub events: u64, pub malformed: u64, } ``` ### `stream::StreamTarget` Which surface to stream. ```rust pub enum StreamTarget { Display(Option<String>), Window(String), } ``` ### `stream::StreamTicket` A minted media session: everything a client needs to ... ```rust pub struct StreamTicket { pub space: String, pub media_session_id: String, pub ws_url: String, pub ticket: String, pub ticket_expires_at: Option<String>, pub codec: String, pub wire_version: u32, pub frame_size: [u32; 2], pub needs_gateway_headers: bool, pub raw: OpenMediaResponse, } ``` ### `stream::Usage` Memory and storage use (`SystemService.Metrics`). ```rust pub struct Usage { pub memory_used: u64, pub memory_total: u64, pub memory_limited: bool, pub disk_used: u64, pub disk_total: u64, pub disk_limited: bool, } ``` ### `stream::VideoFrame` One encoded video access unit, keyframe-gated by the ... ```rust pub struct VideoFrame { pub sequence: u64, pub codec: String, pub keyframe: bool, pub width: u32, pub height: u32, pub capture_timestamp_us: u64, pub codec_epoch: u64, pub geometry_epoch: u64, pub data: Vec<u8>, } ``` ### `stream::WindowTarget` A window target as listed. ```rust pub struct WindowTarget { pub window_id: String, pub epoch: u64, pub title: String, pub app_name: String, pub app_id: String, pub pid: u32, pub bounds: [f64; 4], pub on_screen: bool, pub focused: bool, pub available: bool, pub limitation: String, } impl WindowTarget { fn is_streamable(&self) -> bool; } ``` ### `teleport_broker::SessionBroker` The Keyvault operations `teleport_app` uses. ```rust pub trait SessionBroker: Send + Sync { async fn request_access(&self, app: &str, target: &str, duration_secs: u64, reason: &str) -> Result<String, SessionBrokerError>; async fn await_and_deliver(&self, app: &str, target: &str, request_id: &str, timeout: Duration) -> Result<SessionDelivery, SessionBrokerError>; } ``` ### `teleport_broker::SessionBrokerError` A Keyvault failure, surfaced to the tool with its code. ```rust pub struct SessionBrokerError { pub code: String, pub message: String, } ``` ### `teleport_broker::SessionDelivery` The outcome of awaiting and (when granted) performing a ... ```rust pub enum SessionDelivery { Pending, Denied(String), Delivered { app: String, target: String, items: Vec<String>, imported: Vec<String>, import_ids: Vec<String>, expires_ms: u64 }, } ``` ### `thumbnails::CaptureFailure` The last failed capture of a Space (cleared by a good ... ```rust pub struct CaptureFailure { pub at: Instant, pub attempts: u32, pub denied: bool, pub message: String, } impl CaptureFailure { fn backoff(&self) -> Duration; } ``` ### `thumbnails::Thumbnail` One Space's latest thumbnail. ```rust pub struct Thumbnail { pub image: Vec<u8>, pub format: String, pub width: u32, pub height: u32, pub captured_at: SystemTime, } impl Thumbnail { fn is_fresh(&self, max_age: Option<Duration>, now: SystemTime) -> bool; } ``` ### `thumbnails::ThumbnailCache` The cache. ```rust pub struct ThumbnailCache { /* private fields */ } impl ThumbnailCache { fn new(dir: Option<PathBuf>, disk_bytes: u64) -> Self; fn in_home(home: &Path) -> Self; fn dir(&self) -> Option<&Path>; fn get(&self, space: &str) -> Option<Thumbnail>; fn spaces(&self) -> Vec<String>; fn put(&self, space: &str, t: Thumbnail); fn fail(&self, space: &str, now: Instant, denied: bool, message: String); fn backing_off(&self, space: &str, now: Instant) -> Option<CaptureFailure>; fn remove(&self, space: &str); fn retain(&self, keep: &HashSet<String>); fn note_interest(&self, now: Instant); fn interested(&self, now: Instant) -> bool; fn due(&self, space: &str, now: SystemTime) -> bool; } ``` ### `volume::VolumeAttachment` A mounted volume in a Space. ```rust pub struct VolumeAttachment { /* private fields */ } impl VolumeAttachment { fn volume_id(&self) -> &str; fn mount_path(&self) -> &str; fn backend(&self) -> &str; fn is_live(&self) -> bool; async fn wait_mounted(&self, timeout: Duration) -> Result<VolumeGuestStatus>; async fn detach(self) -> Result<()>; } ``` ### `volume::VolumeGuestStatus` The guest's view of its mount. ```rust pub struct VolumeGuestStatus { pub state: String, pub mount_path: String, pub backend: String, pub detail: String, pub bytes_in: u64, pub bytes_out: u64, } ``` ### `walk::Walk` The result of `walk`. ```rust pub struct Walk { pub entries: Vec<WalkEntry>, pub skipped: Vec<String>, } ``` ### `walk::WalkEntry` One file or directory to transfer. ```rust pub struct WalkEntry { pub path: PathBuf, pub rel: String, pub is_dir: bool, pub size: u64, pub mode: u32, pub modified: Option<SystemTime>, } ``` ## Functions ```rust /// Whether `image` is expected to run ... pub fn expects_spacesd(image: Option<&str>, explicit: Option<bool>) -> bool /// The key a Fleet pool is named after ... pub fn pool_key(image: &str, command: Option<&[String]>, env: &BTreeMap<String, String>, services: &BTreeMap<String, u16>) -> String /// Lowercase DNS label: `[a-z0-9-]` ... pub fn sanitize_label(value: &str) -> String /// `pool_key` plus the size ... pub fn sized_pool_key(image: &str, command: Option<&[String]>, env: &BTreeMap<String, String>, services: &BTreeMap<String, u16>, cpus: Option<u32>, memory_mb: Option<u32>) -> String // mod app_icon /// The guest script for a batch: looks ... pub fn batch_script(p: IconPlatform, requests: &[IconRequest], bundle: &str) -> String /// What the bytes are: a PNG or an SVG ... pub fn content_type(bytes: &[u8]) -> Option<&'static str> /// The app's stable identity in the icon ... pub fn icon_identity(app_name: &str, app_id: &str) -> String /// The guest script: prints the path of ... pub fn icon_script(p: IconPlatform, app_name: &str, app_id: &str, pid: u32, out: &str) -> String /// The files in a bundle `batch_script` ... pub fn parse_bundle(bytes: &[u8]) -> Vec<(usize, Vec<u8>)> /// The path the script printed: its last ... pub fn printed_path(stdout: &str) -> Option<&str> /// A name safe to splice into the script ... pub fn shell_word(s: &str) -> String // mod cloud /// Whether a relay machine is a Space in ... pub fn is_cloud_machine(m: &RelayMachine) -> bool // mod exec /// The tail of `text`, with a marker when ... pub fn cap_output(text: &str) -> String // mod extension /// The JSON a `ToolOutcome` carries (its ... pub fn outcome_json(out: ToolOutcome) -> Result<Value> // mod files /// Normalizes a `send_file` target to a ... pub fn downloads_subdir(target: Option<&str>, home: Option<&str>) -> Result<String> // mod fleet_runtime /// `resolve` with the image evidence ... pub fn check(runtime: Option<FleetRuntime>, image: &str, evidence: &ImageEvidence) -> Result<RuntimeKind> /// The default Space image: the canonical ... pub fn default_image(_runtime: FleetRuntime) -> String /// The guest OS (`linux`, `windows` ... pub fn guest_os(image: &str, resolved_os: Option<&str>) -> String /// `guest_os` with the catalog lookup ... pub fn guest_os_with(image: &str, catalog_os: Option<String>, resolved_os: Option<&str>) -> String /// The tag of an image reference (see ... pub fn image_tag(image: &str) -> Option<&str> /// Parses the wire spelling of a runtime ... pub fn parse_runtime(value: &str) -> Result<FleetRuntime> /// Validates the runtime/image pairing ... pub async fn resolve(runtime: Option<FleetRuntime>, image: &str) -> Result<RuntimeKind> /// `resolve` returning the whole ... pub async fn resolve_image(runtime: Option<FleetRuntime>, image: &str) -> Result<FleetImage> // mod groups /// Whether a Create button should be ... pub fn can_create(members: &[String]) -> bool /// The framing each member receives: the ... pub fn frame_message(text: &str, title: &str, others: &[String]) -> String // mod host_spaces /// The relay URL a Space on this host ... pub fn guest_relay_url(relay_url: &str, kind: &str) -> String /// A `tonic::Status` carrying a ... pub fn to_status(e: &Error) -> Status // mod hotspot /// The default dialer: plain TCP from this ... pub fn direct_dialer() -> Dialer // mod id /// Canonicalizes a stored id (legacy ... pub fn canonical_id(id: &str) -> String // mod install_cache /// The host cache directory. pub fn cache_dir() -> PathBuf /// `a` in the host cache under `dir` ... pub async fn fetch(dir: &Path, a: &StagedArchive, progress: &mut dyn FnMut(u64, u64) + Send) -> Result<PathBuf> /// Whether to stage through the host for a ... pub fn wanted(url: &Url) -> bool // mod presence /// The stable presence color of an agent ... pub fn color_for(id: &str) -> &'static str /// Local Unix time in milliseconds, the ... pub fn now_ms() -> f64 /// Registers the presence datagram channel ... pub fn register_presence_datagrams(channel: Arc<dyn PresenceDatagrams>) /// Black or white, whichever reads better ... pub fn text_color_on(background: &str) -> &'static str // mod presence::art /// The art for every shape, in wire order. pub fn all_cursor_art() -> &'static [CursorArt] /// The art for `shape`. pub fn cursor_art(shape: CursorShape) -> &'static CursorArt /// A standalone SVG of `shape` filled with ... pub fn cursor_art_svg(shape: CursorShape, color: &str, size: f64) -> String // mod presence::view /// Opacity of a cursor idle for `idle_ms`. pub fn idle_alpha(idle_ms: f64) -> f64 // mod reattach /// Checks a link every `policy.poll` and ... pub async fn supervise<H, HF, R, RF>(policy: Policy, health: H, reattach: R) where H: FnMut() -> HF, HF: Future<Output = Health>, R: FnMut() -> RF, RF: Future<Output = Result<Attempt, String>> // mod registry /// The cua home: `$CUA_HOME`, else ... pub fn cua_home() -> PathBuf // mod routines /// `8:05 AM`. pub fn clock_label(hour: u32, minute: u32) -> String /// A fresh id (UUID v4 form, upper case ... pub fn new_id() -> String /// The background loop: one for every ... pub fn spawn_scheduler(store: Arc<Mutex<RoutineStore>>, every: Duration) -> JoinHandle<()> /// One tick against a shared store ... pub async fn tick_shared(store: &Mutex<RoutineStore>, now: DateTime<Utc>) -> Vec<FiringRecord> // mod routines::iso pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<DateTime<Utc>, D::Error> pub fn format(d: &DateTime<Utc>) -> String pub fn parse(s: &str) -> Result<DateTime<Utc>, ParseError> pub fn serialize<S: Serializer>(d: &DateTime<Utc>, s: S) -> Result<S::Ok, S::Error> // mod routines::iso::option pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<DateTime<Utc>>, D::Error> pub fn serialize<S: Serializer>(d: &Option<DateTime<Utc>>, s: S) -> Result<S::Ok, S::Error> // mod services /// Converts a `CallToolResponse` into MCP ... pub fn tool_result(resp: CallToolResponse) -> ToolResult // mod share /// Moves `who` onto the list for `role` ... pub fn apply_share(allow: &mut Vec<String>, viewers: &mut Vec<String>, who: &str, role: Option<ShareRole>) /// Normalizes an invitee: an email ... pub fn normalize_who(who: &str) -> Result<String> // mod site_login /// The `request_site_login` tool: files a ... pub async fn request_site_login(spaces: &Spaces, a: RequestSiteLogin, agent: Option<String>) -> Result<Value> // mod stream /// Whether a streaming client is ... pub fn has_stream_client() -> bool /// The words a person sees for input the ... pub fn input_refusal_text(message: &str) -> Option<&'static str> /// Registers the streaming client for this ... pub fn register_stream_client(client: Arc<dyn StreamClient>) // mod thumbnails /// The `desktop_stream` feature of a host ... pub fn desktop_not_shared(caps: &GetCapabilitiesResponse) -> Option<&Feature> // mod walk /// A single file as a walk entry named ... pub fn single(path: &Path, name: &str) -> Result<WalkEntry> /// Enumerates everything under `root` (not ... pub fn walk(root: &Path, respect_ignore_files: bool) -> Result<Walk> ``` ## Constants ```rust /// Default time allowed for the ... pub const DEFAULT_PROBE_TIMEOUT: Duration = _; /// The spacesd pseudo-feature every ... pub const SPACESD_FEATURE: &str = "spacesd"; // mod app_icon /// How long the guest script may run. pub const ICON_SCRIPT_TIMEOUT: Duration = _; /// Largest icon file accepted. pub const MAX_ICON_BYTES: usize = _; // mod audit /// The chain's first `prev`. pub const GENESIS: &str = /* ... */; // mod cloud /// `SpaceInfo::cloud_delete`: only the ... pub const DELETE_ELSEWHERE: &str = "elsewhere"; /// `SpaceInfo::cloud_delete`: this home ... pub const DELETE_HERE: &str = "here"; // mod exec /// Default `space_bash` timeout. pub const DEFAULT_BASH_TIMEOUT: Duration = _; /// Characters of each output stream kept ... pub const MAX_OUTPUT_CHARS: usize = 16_000; // mod files /// Chunk used when the driver does not say. pub const DEFAULT_SEND_CHUNK: usize = _; /// Largest chunk sent to ... pub const MAX_SEND_CHUNK: usize = _; // mod groups pub const MAX_BOTS: usize = 6; /// The product bound: a group of one is a ... pub const MIN_BOTS: usize = 2; // mod host_spaces /// Metadata carrying the relay-verified ... pub const CALLER_METADATA: &str = "x-cua-host-caller"; /// The spacesd feature a host reports ... pub const HOST_SPACES_FEATURE: &str = "host_spaces"; // mod presence /// Roster heartbeat interval the SDK asks ... pub const HEARTBEAT_INTERVAL: Duration = _; /// The cursor palette, in the server's ... pub const PALETTE: &[&str] = _; /// Shortest interval between two cursor ... pub const SEND_INTERVAL: Duration = _; // mod presence::art /// The art JSON, verbatim. pub const CURSOR_ART_JSON: &str = /* ... */; // mod presence::view /// The conformance vectors every ... pub const CONFORMANCE_JSON: &str = /* ... */; /// Render delay on the 30 Hz datagram ... pub const DATAGRAM_DELAY_MS: f64 = 66.0; /// Longest extrapolation past the newest ... pub const EXTRAPOLATE_MS: f64 = 100.0; /// Heartbeat interval the SDKs ask for. pub const HEARTBEAT_INTERVAL_MS: f64 = 5_000.0; /// Missed heartbeats before everyone else ... pub const HEARTBEAT_MISSES: f64 = 3.0; /// A cursor idle this long starts fading. pub const IDLE_FADE_AFTER_MS: f64 = 5_000.0; /// Fade duration. pub const IDLE_FADE_MS: f64 = 300.0; /// A jump longer than this (normalized ... pub const SNAP_DISTANCE: f64 = 0.25; /// Render delay on the 20 Hz `Join` stream. pub const STREAM_DELAY_MS: f64 = 100.0; // mod registry /// File name of the 0600 credential store. pub const CREDENTIALS_FILE: &str = "spaces-credentials.json"; /// File name of the direct-host list. pub const DIRECT_HOSTS_FILE: &str = "direct-hosts.json"; /// Advisory lock file serializing writers ... pub const LOCK_FILE: &str = "spaces.lock"; /// File name of the registry. pub const SPACES_FILE: &str = "spaces.json"; // mod routines /// Marks a routine-originated turn in a ... pub const ROUTINE_PREFIX: &str = "[routine]"; // mod services /// Names that mean "the Space's own ... pub const DRIVER_ALIASES: [&str; 5] = _; /// The service name of the spacesd's own ... pub const DRIVER_SERVICE: &str = "driver"; // mod share /// The spacesd feature a Space needs to be ... pub const RELAY_ATTACH_FEATURE: &str = "relay_attach"; // mod site_login /// Default and maximum wait for the user's ... pub const DEFAULT_WAIT_SECS: u32 = 20; /// See `DEFAULT_WAIT_SECS`. pub const MAX_WAIT_SECS: u32 = 120; // mod stream /// Bound of the delivery queue. pub const DELIVERY_QUEUE: usize = 256; /// A window narrower or shorter than this ... pub const MIN_STREAMABLE_SIDE: f64 = 40.0; // mod thumbnails /// A running Space's thumbnail is captured ... pub const BACKGROUND_INTERVAL: Duration = _; /// After a refused capture (a host that ... pub const DENIED_BACKOFF: Duration = _; /// Disk cap for the whole cache. pub const DISK_BYTES: u64 = _; /// After another failed capture: the first ... pub const FAILURE_BACKOFF: Duration = _; /// How long one request keeps the ... pub const INTEREST_WINDOW: Duration = _; /// Long edge of a captured thumbnail ... pub const MAX_DIMENSION: u32 = 320; /// The longest wait after failed captures. pub const MAX_FAILURE_BACKOFF: Duration = _; /// JPEG quality of a captured thumbnail. pub const QUALITY: u32 = 70; /// How often the daemon checks which ... pub const REFRESH_TICK: Duration = _; // mod volume /// The guest feature. pub const VOLUME_FEATURE: &str = "volume.mount"; // mod walk /// Directories always skipped when ignore ... pub const ALWAYS_IGNORED_DIRS: [&str; 1] = _; /// Upper bound on entries in one transfer ... pub const MAX_ENTRIES: usize = 200_000; /// Ignore files honored in every directory. pub const NESTED_IGNORE_FILES: [&str; 2] = _; /// Ignore files honored at the transfer ... pub const ROOT_ONLY_IGNORE_FILES: [&str; 1] = _; ``` ## Type aliases ```rust /// A machine row of the relay directory. pub type RelayMachine = Machine; /// Result alias. pub type Result<T, E = Error> = Result<T, E>; // mod extension /// A boxed, `Send` future (the extension ... pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; // mod hotspot /// How this machine dials the guest's ... pub type Dialer = Arc<dyn Fn(String, u16) -> Pin<Box<dyn Future<Output = Result<TcpStream>> + Send>> + Send + Sync>; // mod presence /// An open presence datagram channel: the ... pub type DatagramChannel = (Arc<dyn CursorUplink>, Receiver<PresenceEvent>); ``` ## Re-exports `cua_sandbox_core::byoc::CloudCheck`, `cua_sandbox_core::byoc::CloudConnected`, `cua_sandbox_core::byoc::CloudCredentials`, `cua_sandbox_core::byoc::CloudDisconnected`, `cua_sandbox_core::byoc::CloudKind`, `cua_sandbox_core::byoc::CloudProvider`, `cua_sandbox_core::byoc::CloudResource`, `cua_sandbox_core::byoc::CloudStatusReport`, `cua_sandbox_core::byoc::CloudSweepItem`, `cua_sandbox_core::byoc::CloudSweepReport`, `cua_sandbox_core::byoc::CloudTarget`, `cua_sandbox_core::byoc::CloudTestReport`, `cua_spaces_contract`, `spaces::CreatePhase`, `spaces::CreateProgress`, `cua_spacesd_client`, `cua_host::relay::AccountTokens`, `cua_host::ConnectedClient`, `cua_host::DEFAULT_RELAY_URL`, `cua_host::DeviceAuth`, `cua_host::Identity`, `cua_host::relay::NoAccount`, `cua_host::relay_url_from_env`, `cua_host::relay::StaticToken`. --- # cua-spaces: client The Spaces client: transports, model, control and teleport. Part of the public API of the `cua-spaces` crate (`cua_spaces`): client. Summaries and declarations only (`cargo doc -p cua-spaces --open` in `libs/cua` has the full comments); see [Rust crates](</docs/cua-sdk/reference/rust>) for stability. More of this crate: [registry and primitives](</docs/cua-sdk/reference/rust/cua-spaces>), [agents and MCP](</docs/cua-sdk/reference/rust/cua-spaces-agents-mcp>). ## Modules - `client`: The typed Spaces control-plane client, over any MCP ... - `client::conformance`: `cua.control.session/1`; the cross-language ... - `client::control`: The control plane: every advertised tool behind a typed ... - `client::coverage`: Which typed call reaches each advertised tool. - `client::error`: One flat error type. - `client::framing`: The newline-delimited framer. - `client::metadata`: The one place a run's application metadata is smuggled ... - `client::model`: The control plane's value types. - `client::persistent`: Persistent agents, routines, notifications and ... - `client::teleport`: Session teleport: pushing a logged-in host app session ... - `client::transport`: The MCP stdio transport, and the one seam where a ... ## Types ### `client::conformance::Node` An ordered JSON value. ```rust pub enum Node { Null, Bool(bool), Int(i64), Str(String), Array(Vec<Node>), Object(Vec<(String, Node)>), } impl Node { fn object(entries: Vec<(&str, Node)>) -> Node; fn string(value: impl Into<String>) -> Node; fn optional_string(value: &Option<String>) -> Node; fn optional_int(value: Option<i64>) -> Node; fn optional_bool(value: Option<bool>) -> Node; fn strings(values: &[String]) -> Node; fn to_json(&self) -> String; } ``` ### `client::Connection` A connection to a Spaces backend. ```rust pub struct Connection { /* private fields */ } impl Connection { fn new(transport: Arc<dyn ToolTransport>) -> Self; fn transport(&self) -> Arc<dyn ToolTransport>; fn available_tools(&self) -> Result<Vec<String>>; fn call_tool_raw(&self, tool: &str, arguments_json: &str) -> Result<String>; fn spaces(&self) -> Result<Vec<SpaceInfo>>; fn attach(self: &Arc<Self>, space_id: &str, require_ready: bool) -> Result<Space>; fn create_space(self: &Arc<Self>, options: CreateSpaceOptions) -> Result<Space>; fn delete_space(&self, space_id: &str) -> Result<()>; fn add_space(&self, url: &str, token: Option<&str>, name: Option<&str>) -> Result<SpaceInfo>; fn remove_space(&self, space_id: &str) -> Result<()>; fn hotspot_start(&self, space_id: &str) -> Result<HotspotStatus>; fn hotspot_stop(&self) -> Result<HotspotStatus>; fn hotspot_status(&self) -> Result<HotspotStatus>; fn volume_ls(&self, path: &str, as_agent: Option<&str>, in_space: Option<&str>) -> Result<Map<String, Value>>; fn volume_read(&self, path: &str, version: Option<&str>, as_agent: Option<&str>, in_space: Option<&str>) -> Result<Map<String, Value>>; fn volume_write(&self, path: &str, content: &str, encoding: Option<&str>, if_etag: Option<&str>, create_only: bool, as_agent: Option<&str>) -> Result<Map<String, Value>>; fn volume_delete(&self, path: &str, if_etag: Option<&str>) -> Result<Map<String, Value>>; fn volume_history(&self, path: &str) -> Result<Map<String, Value>>; fn volume_restore(&self, path: &str, version: &str) -> Result<Map<String, Value>>; fn volume_grant(&self, principal: &str, prefix: &str, mode: &str, expires_in_secs: Option<u64>) -> Result<Map<String, Value>>; fn volume_revoke(&self, grant_id: &str) -> Result<Map<String, Value>>; fn volume_grants(&self, all: bool) -> Result<Map<String, Value>>; fn volume_request_access(&self, as_agent: &str, prefix: &str, mode: &str, reason: &str) -> Result<Map<String, Value>>; fn volume_requests(&self) -> Result<Map<String, Value>>; fn volume_approve(&self, request_id: &str) -> Result<Map<String, Value>>; fn volume_deny(&self, request_id: &str) -> Result<Map<String, Value>>; fn volume_audit(&self, limit: u32) -> Result<Map<String, Value>>; fn volume_storage(&self) -> Result<Map<String, Value>>; fn volume_storage_set(&self, update: Value) -> Result<Map<String, Value>>; fn volume_mount_status(&self) -> Result<Map<String, Value>>; fn volume_mount(&self) -> Result<Map<String, Value>>; fn volume_unmount(&self) -> Result<Map<String, Value>>; fn volume_sync_status(&self) -> Result<Map<String, Value>>; fn volume_sync_events(&self, since_seq: u64, wait_ms: u32) -> Result<Map<String, Value>>; fn volume_sync_resolve(&self, path: &str) -> Result<Map<String, Value>>; fn volume_cache_stats(&self) -> Result<Map<String, Value>>; fn volume_cache_set(&self, capacity_bytes: u64) -> Result<Map<String, Value>>; fn volume_cache_clear(&self) -> Result<Map<String, Value>>; fn cloud_status(&self, provider: Option<&str>) -> Result<Map<String, Value>>; fn cloud_connect(&self, target: Value, make_default: bool, ttl_hours: Option<u32>) -> Result<Map<String, Value>>; fn cloud_test(&self, target: Value) -> Result<Map<String, Value>>; fn cloud_disconnect(&self, provider: &str) -> Result<Map<String, Value>>; fn cloud_sweep(&self, provider: Option<&str>, dry_run: bool, all: bool) -> Result<Map<String, Value>>; fn create_persistent_agent(&self, name: &str, harness: &str, space_id: &str, model: Option<&str>, base_url: Option<&str>, env_from_host: &[String]) -> Result<Record>; fn persistent_agents(&self) -> Result<Vec<Record>>; fn remove_persistent_agent(&self, name: &str) -> Result<Record>; fn send_to_persistent_agent(&self, name: &str, text: &str) -> Result<Record>; fn save_agent_home(&self, name: &str) -> Result<Record>; fn pause_agent(&self, name: &str) -> Result<Record>; fn resume_agent(&self, name: &str, prompt: Option<&str>) -> Result<Record>; fn add_routine(&self, agent: &str, title: &str, prompt: &str, schedule: &RoutineSchedule) -> Result<Record>; fn routines(&self, agent: Option<&str>) -> Result<Vec<Record>>; fn remove_routine(&self, id: &str) -> Result<()>; fn set_routine_enabled(&self, id: &str, enabled: bool) -> Result<Record>; fn notify_user(&self, title: &str, body: &str) -> Result<String>; fn notifications(&self, unread_only: bool) -> Result<Vec<Record>>; fn mark_notifications_read(&self, ids: &[String]) -> Result<u64>; fn allow_computer(&self, agent: &str, machine: &str) -> Result<Record>; fn revoke_computer(&self, agent: &str, machine: Option<&str>) -> Result<u64>; fn computer_access(&self, agent: Option<&str>) -> Result<Vec<Record>>; } ``` ### `client::coverage::ToolCoverage` One row of the coverage table. ```rust pub struct ToolCoverage { pub tool: String, pub sdk_symbol: String, pub providers: Vec<String>, pub metering: String, } ``` ### `client::InProcessTransport` The typed client driving the Rust MCP server ... ```rust pub struct InProcessTransport { /* private fields */ } impl InProcessTransport { fn new(server: McpServer, runtime: Handle) -> Self; } ``` ### `client::LineFramer` ```rust pub struct LineFramer { /* private fields */ } impl LineFramer { fn new(maximum_frame_bytes: usize) -> Self; fn maximum_frame_bytes(&self) -> usize; fn buffered_byte_count(&self) -> usize; fn append(&mut self, chunk: &[u8]); fn take_frame(&mut self) -> Result<Option<Vec<u8>>>; fn take_frames(&mut self) -> Result<Vec<Vec<u8>>>; } ``` ### `client::model::Admission` ```rust pub struct Admission { pub accepted: Vec<TransferCandidate>, pub rejected: Vec<Rejection>, } ``` ### `client::model::AgentEvent` Agent output as typed events. ```rust pub struct AgentEvent { pub run_id: String, pub line_index: u32, pub kind: AgentEventKind, pub text: String, pub state: Option<AgentState>, pub exit_code: Option<i32>, pub is_inferred: bool, } ``` ### `client::model::AgentEventKind` ```rust pub enum AgentEventKind { Text, StateChanged, Finished, Raw, ToolUse, Artifact, Question, } impl AgentEventKind { fn as_str(self) -> &'static str; fn is_inferred_only(self) -> bool; } ``` ### `client::model::AgentKindInfo` Which agent harness to start, and whether it is real. ```rust pub struct AgentKindInfo { pub id: String, pub is_production_ready: bool, } ``` ### `client::model::AgentRunHandle` The handle `start_agent` returns. ```rust pub struct AgentRunHandle { pub id: String, pub space: String, pub agent: String, pub turn_model: TurnModel, pub notes: Vec<String>, pub approvals_are_enforced: bool, } impl AgentRunHandle { fn directory(&self) -> String; } ``` ### `client::model::AgentStartRequest` What to start. ```rust pub struct AgentStartRequest { pub agent: String, pub prompt: String, pub metadata: Vec<MetadataEntry>, pub shows_window: bool, pub timeout_seconds: Option<u32>, } ``` ### `client::model::AgentState` The status vocabulary the Spaces agent harness ... ```rust pub enum AgentState { Running, AwaitingInput, Idle, Finished, Failed, Crashed, Unknown, } impl AgentState { fn from_wire(wire: Option<&str>) -> Self; fn as_str(self) -> &'static str; fn is_live(self) -> bool; fn has_ended(self) -> bool; } ``` ### `client::model::CreateSpaceOptions` Options for `crate::client::Connection::create_space` ... ```rust pub struct CreateSpaceOptions { pub image: Option<String>, pub on: Option<String>, pub kind: Option<String>, pub runtime: Option<String>, pub name: Option<String>, pub reuse: bool, pub wait: Option<bool>, } ``` ### `client::model::Delivery` The result of a send, with **one** shape and a ... ```rust pub struct Delivery { pub run_id: String, pub accepted: bool, pub reason: String, pub state_at_refusal: Option<AgentState>, pub queued_for_ms: Option<u64>, } impl Delivery { fn decode(row: &Map<String, Value>, run_id: &str, queued_for_ms: Option<u64>) -> Self; fn required(self) -> Result<Delivery>; } ``` ### `client::model::DeliveryMode` How a message should be delivered to a run that may be ... ```rust pub enum DeliveryMode { RefuseIfBusy, AbandonCurrentTurn, QueueUntilIdle, } ``` ### `client::model::HarnessCapabilities` What the server says about its agent harnesses. ```rust pub struct HarnessCapabilities { pub statuses: Vec<String>, pub harnesses: Vec<AgentKindInfo>, pub status_classifier: String, } impl HarnessCapabilities { fn decode(row: &Map<String, Value>) -> Self; } ``` ### `client::model::HotspotStatus` Whether this Mac is sharing its network, and with which ... ```rust pub struct HotspotStatus { pub is_sharing: bool, pub space: Option<String>, pub detail: String, } impl HotspotStatus { fn decode(row: &Map<String, Value>) -> Self; } ``` ### `client::model::MetadataEntry` A metadata key/value. ```rust pub struct MetadataEntry { pub key: String, pub value: String, } ``` ### `client::model::ProviderCapabilities` What a provider can actually do, declared rather than ... ```rust pub struct ProviderCapabilities { pub provider: String, pub agents: bool, pub window_list: bool, pub upload: bool, pub download: bool, pub rcdp_streaming: bool, pub teleport: bool, pub provisioning: bool, pub server_backstop: bool, pub notes: Vec<String>, } ``` ### `client::model::Rejection` ```rust pub struct Rejection { pub candidate: TransferCandidate, pub violation: String, } ``` ### `client::model::RemoteFile` A file that now exists inside the Space, at the path ... ```rust pub struct RemoteFile { pub path: String, pub name: String, pub byte_count: Option<u64>, } ``` ### `client::model::RunSnapshot` **One** state type, returned in full by every call that ... ```rust pub struct RunSnapshot { pub id: String, pub space: String, pub agent: String, pub state: AgentState, pub reason: String, pub accepts_message: bool, pub exit_code: Option<i32>, pub summary: String, pub output_tail: Option<String>, pub output_truncated: bool, pub created_at_ms: Option<i64>, pub reason_is_carried_forward: bool, pub raw_prompt: Option<String>, } impl RunSnapshot { fn decode(row: &Map<String, Value>, run_id: &str, space: &str, requested_tail: Option<u32>, previous: Option<&RunSnapshot>) -> Self; fn differs_visibly(&self, other: &RunSnapshot) -> bool; } ``` ### `client::model::SchedulerFacts` What the core can honestly say about recurring work. ```rust pub struct SchedulerFacts { pub is_server_backed: bool, pub missed_slot_policy: String, } ``` ### `client::model::ServiceCatalog` ```rust pub struct ServiceCatalog { pub service: String, pub tools: Vec<SpaceTool>, pub other_services: Vec<String>, pub instructions: Option<String>, pub not_ready_warning: Option<String>, } impl ServiceCatalog { fn is_empty_but_reachable(&self) -> bool; } ``` ### `client::model::SpaceInfo` A Space as the account sees it. ```rust pub struct SpaceInfo { pub id: String, pub provider: SpaceProvider, pub operating_system: String, pub state: SpaceState, pub raw_phase: String, pub ip_address: Option<String>, } impl SpaceInfo { fn from_row(row: &Map<String, Value>) -> Self; fn is_ready(&self) -> bool; fn capabilities(&self) -> ProviderCapabilities; } ``` ### `client::model::SpaceProvider` Which kind of Space this is. ```rust pub enum SpaceProvider { Local, Fleet, Direct, Demo, Unknown, } impl SpaceProvider { fn from_raw(raw: &str) -> Self; fn as_str(self) -> &'static str; fn home(self) -> &'static str; fn default_upload_directory(self) -> String; fn capabilities(self) -> ProviderCapabilities; } ``` ### `client::model::SpaceState` One normalised readiness vocabulary. ```rust pub enum SpaceState { Ready, Starting, Stopped, Failed, Unknown, } impl SpaceState { fn from_phase(phase: &str) -> Self; fn is_ready(self) -> bool; fn as_str(self) -> &'static str; } ``` ### `client::model::SpaceTool` ```rust pub struct SpaceTool { pub service: String, pub name: String, pub summary: String, pub input_schema_json: String, } ``` ### `client::model::SpaceWindow` A window inside a Space, and the rcdp target that ... ```rust pub struct SpaceWindow { pub id: String, pub app: String, pub title: String, pub width_px: u32, pub height_px: u32, pub scale_factor_hundredths: u32, pub visible: bool, pub process_id: Option<i32>, } impl SpaceWindow { fn from_row(row: &Map<String, Value>) -> Self; fn looks_like_rcdp_target(&self) -> bool; } ``` ### `client::model::StopOutcome` The result of a stop, which **verifies** rather than ... ```rust pub struct StopOutcome { pub stopped: bool, pub alive: Option<bool>, pub reason: String, } impl StopOutcome { fn decode(row: &Map<String, Value>) -> Self; } ``` ### `client::model::StreamEndpoint` Where an in-product stream connects. ```rust pub struct StreamEndpoint { pub host: String, pub port: u16, pub driver_port: u16, pub token: String, } impl StreamEndpoint { fn from_row(row: &Map<String, Value>, fallback_host: Option<&str>) -> Self; } ``` ### `client::model::ToolContentPart` A content part returned by an in-Space tool. cua-driver ... ```rust pub struct ToolContentPart { pub kind: String, pub text: String, pub data_base64: String, pub mime_type: String, } ``` ### `client::model::TransferCandidate` ```rust pub struct TransferCandidate { pub name: String, pub byte_count: u64, } ``` ### `client::model::TransferLimits` Caller-declared transfer limits, checked before any I/O. ```rust pub struct TransferLimits { pub max_file_count: u32, pub max_bytes_per_file: u64, pub max_bytes_per_batch: u64, pub is_server_published: bool, } impl TransferLimits { fn conservative_default() -> Self; fn unlimited() -> Self; fn admit(&self, candidates: &[TransferCandidate]) -> Admission; fn check(&self, candidates: &[TransferCandidate]) -> Result<()>; } ``` ### `client::model::TurnModel` What the harness published about how a run takes turns ... ```rust pub struct TurnModel { pub kind: String, pub accepts_follow_ups: bool, } impl TurnModel { fn from_row(row: &Map<String, Value>) -> Self; } ``` ### `client::model::UploadPlacement` ```rust pub struct UploadPlacement { pub kind: UploadPlacementKind, pub path: String, } impl UploadPlacement { fn collision_safe_default() -> Self; } ``` ### `client::model::UploadPlacementKind` Where an upload should land. ```rust pub enum UploadPlacementKind { ExactPath, CollisionSafe, Clobbering, } ``` ### `client::persistent::RoutineSchedule` When a routine fires. ```rust pub enum RoutineSchedule { EveryMinutes(i64), DailyAt(String), WeeklyOn(String), } ``` ### `client::ScriptedTransport` An in-process backend driven by a JSON script, so the ... ```rust pub struct ScriptedTransport { /* private fields */ } impl ScriptedTransport { const SCRIPT_SCHEMA: &'static str = "cua.control.script/1";; fn from_script(script_json: &str) -> Result<Self>; fn calls(&self) -> Vec<(String, Value)>; fn call_count(&self) -> usize; } ``` ### `client::Space` A handle on one Space. ```rust pub struct Space { pub info: SpaceInfo, } impl Space { fn id(&self) -> &str; fn provider(&self) -> SpaceProvider; fn capabilities(&self) -> ProviderCapabilities; fn home(&self) -> &'static str; fn connection(&self) -> Arc<Connection>; fn delete(&self) -> Result<()>; fn stop(&self) -> Result<Map<String, Value>>; fn start(&self) -> Result<Map<String, Value>>; fn share_with(&self, who: &str, role: &str) -> Result<Map<String, Value>>; fn unshare(&self, who: Option<&str>) -> Result<Map<String, Value>>; fn shares(&self) -> Result<Map<String, Value>>; fn relay_register(&self) -> Result<Map<String, Value>>; fn relay_unregister(&self) -> Result<Map<String, Value>>; fn bash(&self, command: &str) -> Result<String>; fn write_text(&self, text: &str, path: &str) -> Result<RemoteFile>; fn upload(&self, local_path: &str, placement: &UploadPlacement, byte_count: Option<u64>) -> Result<RemoteFile>; fn send_file(&self, local_path: &str, target_directory: Option<String>, respect_ignorefiles: Option<bool>) -> Result<String>; fn download(&self, remote_path: &str, into_directory: Option<String>) -> Result<String>; fn windows(&self) -> Result<Vec<SpaceWindow>>; fn window_of_process(&self, process_id: i32) -> Result<Option<SpaceWindow>>; fn stream_endpoint(&self, force_refresh: bool) -> Result<StreamEndpoint>; fn stream_window_to_operator_desktop(&self, window_id: &str) -> Result<()>; fn pin_picture_in_picture_on_operator_desktop(&self) -> Result<()>; fn unpin_picture_in_picture_from_operator_desktop(&self) -> Result<()>; fn open_viewer_on_operator_desktop(&self) -> Result<()>; fn service_catalog(&self, service: Option<String>, matching: Option<String>) -> Result<ServiceCatalog>; fn services(&self) -> Result<Vec<String>>; fn call_service_tool(&self, tool: &str, service: Option<String>, arguments_json: &str) -> Result<Vec<ToolContentPart>>; fn start_agent(&self, request: &AgentStartRequest) -> Result<AgentRunHandle>; fn run_status(&self, run_id: &str, tail: u32) -> Result<RunSnapshot>; fn runs(&self) -> Result<Vec<RunSnapshot>>; fn send_message(&self, run_id: &str, text: &str, mode: DeliveryMode, queue_timeout_ms: Option<u64>) -> Result<Delivery>; fn stop_run(&self, run_id: &str) -> Result<StopOutcome>; fn events_after(&self, run_id: &str, cursor: u64, max: u32) -> Result<Map<String, Value>>; fn interrupt_run(&self, run_id: &str) -> Result<bool>; fn harness_capabilities(&self) -> Result<HarnessCapabilities>; fn run_events(&self, run_id: &str, tail: u32) -> Result<Vec<AgentEvent>>; fn approve_run(&self, _run_id: &str, _decision: &str) -> Result<()>; fn teleport_manifest(&self, app: &str, scope: TeleportScope) -> Result<TeleportManifest>; fn teleport_send(&self, approval: &Approval) -> Result<TeleportReceipt>; } ``` ### `client::SpacesError` Everything that can go wrong, on the language's error ... ```rust pub enum SpacesError { TransportUnavailable(String), TransportFraming(String), ToolFailed { tool: String, message: String }, RpcFailed { method: String, code: i64, message: String }, MalformedResponse { tool: String, detail: String }, UnsupportedByProvider { tool: String, provider: String, detail: String }, SpaceUnavailable(String, String), RunNotFound(String), LimitExceeded(String), WouldProvision(String), LocalFileUnavailable(String), TimedOut { waiting_for: String, after_ms: u64 }, NotImplementedYet(String), TeleportRefused(String), } impl SpacesError { fn tag(&self) -> &'static str; } ``` ### `client::teleport::Approval` Proof a human agreed, and to what. ```rust pub struct Approval { /* private fields */ } impl Approval { fn app(&self) -> &str; fn scope(&self) -> TeleportScope; fn space(&self) -> &str; fn approved_bytes(&self) -> u64; fn approved_paths(&self) -> Vec<String>; fn uses_server_default(&self) -> bool; } ``` ### `client::teleport::TeleportItem` One transferable item in an app's manifest. ```rust pub struct TeleportItem { pub relative_path: String, pub label: String, pub estimated_bytes: u64, pub is_sensitive: bool, pub is_checked_by_default: bool, pub count: Option<u32>, pub count_noun: Option<String>, pub explanation: String, } impl TeleportItem { fn from_row(row: &Map<String, Value>) -> Self; } ``` ### `client::teleport::TeleportManifest` Exactly what would leave this machine, before anything ... ```rust pub struct TeleportManifest { pub app: String, pub display_name: String, pub scope: TeleportScope, pub server_scope: String, pub items: Vec<TeleportItem>, pub total_estimated_bytes: u64, pub notes: Vec<String>, } impl TeleportManifest { fn decode(app: &str, scope: TeleportScope, payload: &Value) -> Self; fn sensitive_items(&self) -> Vec<TeleportItem>; fn default_selection(&self) -> Vec<TeleportItem>; fn login_only_selection(&self) -> Vec<TeleportItem>; fn approving(&self, space_id: &str, relative_paths: &[String], acknowledging_sensitive_items: bool) -> Result<Approval>; fn approving_server_default(&self, space_id: &str, acknowledging_sensitive_items: bool) -> Result<Approval>; } ``` ### `client::teleport::TeleportReceipt` What actually moved. ```rust pub struct TeleportReceipt { pub app: String, pub space: String, pub method: String, pub transferred_paths: Vec<String>, pub raw_result: String, } ``` ### `client::teleport::TeleportScope` How much of an app's session to consider. ```rust pub enum TeleportScope { Full, Tabs, } impl TeleportScope { fn as_str(self) -> &'static str; } ``` ### `client::ToolTransport` The one thing the control plane needs from a Spaces ... ```rust pub trait ToolTransport: Send + Sync { fn call_tool(&self, name: &str, arguments: &Value) -> Result<Value>; fn available_tools(&self) -> Result<Vec<String>>; } ``` ## Functions ```rust // mod client::conformance /// Drive the whole typed control-plane ... pub fn run_session(script_json: &str) -> Result<String> /// The scripted backend the session runs ... pub fn session_script() -> String // mod client::coverage /// The coverage table, in contract order ... pub fn coverage() -> Vec<ToolCoverage> // mod client::metadata /// The metadata carried by a prompt echo ... pub fn decode(text: &str) -> Vec<MetadataEntry> /// The prompt as it should be sent. pub fn encode(prompt: &str, metadata: &[MetadataEntry]) -> String /// The text with the marker removed, for ... pub fn strip(text: &str) -> String // mod client::model pub fn agent_kind(id: &str) -> AgentKindInfo /// Split a snapshot into events, **without ... pub fn events_from_snapshot(snapshot: &RunSnapshot) -> Vec<AgentEvent> /// The provider half of a qualified Space ... pub fn provider_prefix(space_id: &str) -> Option<&str> pub fn scheduler_facts() -> SchedulerFacts pub fn tool_content_parts(payload: &Value) -> Vec<ToolContentPart> // mod client::transport /// The **only** place `isError` is read ... pub fn decode_tool_result(tool: &str, result: &Value) -> Result<Value> /// The payload as an array, tolerating the ... pub fn expect_array(payload: &Value, unwrapping: Option<&str>) -> Vec<Value> /// The payload as an object, or a ... pub fn expect_object(tool: &str, payload: &Value) -> Result<Map<String, Value>> ``` ## Constants ```rust // mod client /// The version of this client. pub const VERSION: &str = "0.1.0"; // mod client::conformance pub const SESSION_SCHEMA: &str = "cua.control.session/1"; // mod client::framing /// 64 MiB. pub const DEFAULT_MAXIMUM_FRAME_BYTES: usize = _; // mod client::metadata pub const CLOSE_MARKER: &str = "-->"; /// Recognisable, greppable, and last: so ... pub const OPEN_MARKER: &str = "<!--cua-spaces-meta:"; // mod client::model /// `false` in every shipping backend. pub const APPROVALS_ARE_ENFORCED: bool = false; /// The two harnesses that are wired end to ... pub const PRODUCTION_READY_AGENTS: [&str; 2] = _; pub const SCHEDULER_IS_SERVER_BACKED: bool = false; // mod client::transport pub const CLIENT_NAME: &str = "cua-spaces-client"; /// The MCP revision the client announces. pub const CLIENT_PROTOCOL_VERSION: &str = "2024-11-05"; pub const CLIENT_VERSION: &str = "0.1.0"; ``` ## Type aliases ```rust // mod client pub type Result<T> = Result<T, SpacesError>; // mod client::persistent /// One JSON record. pub type Record = Map<String, Value>; ``` --- # cua-spaces: agents and MCP Agents in Spaces and the Spaces MCP server (stdio and HTTP). Part of the public API of the `cua-spaces` crate (`cua_spaces`): agents and MCP. Summaries and declarations only (`cargo doc -p cua-spaces --open` in `libs/cua` has the full comments); see [Rust crates](</docs/cua-sdk/reference/rust>) for stability. More of this crate: [registry and primitives](</docs/cua-sdk/reference/rust/cua-spaces>), [client](</docs/cua-sdk/reference/rust/cua-spaces-client>). ## Modules - `agents`: Coding agents inside a Space: `cua_agents` over the ... - `agents::telemetry`: Agent-run telemetry: the host install that starts a run ... - `mcp`: The Spaces MCP server: the contract's tools over ... - `mcp::codes`: JSON-RPC error codes. - `mcp::http`: MCP streamable HTTP: `POST /mcp` with a JSON-RPC body ... - `mcp::stdio`: MCP over stdio: newline-delimited JSON-RPC. ## Types ### `agents::CapabilitiesReport` `agent_capabilities`. ```rust pub struct CapabilitiesReport { pub protocol: &'static str, pub statuses: Vec<&'static str>, pub event_kinds: &'static [&'static str], pub harnesses: Vec<HarnessInfo>, pub forwardable_env: Vec<&'static str>, pub runner: &'static str, } ``` ### `mcp::McpServer` The server. ```rust pub struct McpServer { /* private fields */ } impl McpServer { fn new(spaces: Spaces) -> Self; fn remote(backend: Arc<dyn ToolBackend>) -> Self; fn with_session_broker(self, broker: Arc<dyn SessionBroker>) -> Self; fn with_extension(self, extension: Arc<dyn ToolExtension>) -> Self; fn with_filter(self, filter: ToolFilter) -> Self; fn with_name(self, name: &'static str) -> Self; fn with_instructions(self, instructions: &'static str) -> Self; fn spaces(&self) -> Option<&Spaces>; fn tools_list(&self) -> Value; async fn call(&self, name: &str, arguments: Value) -> ToolOutcome; async fn handle(&self, message: Value) -> Option<Value>; } ``` ### `mcp::ToolBackend` Where contract tool calls go when the server does not ... ```rust pub trait ToolBackend: Send + Sync { async fn call(&self, tool: &str, arguments: Value) -> ToolOutcome; } ``` ### `mcp::ToolExtension` Tools served next to the Spaces contract (the `cua` ... ```rust pub trait ToolExtension: Send + Sync { fn tools(&self) -> Vec<Value>; async fn call(&self, tool: &str, arguments: Value) -> Option<ToolOutcome>; } ``` ### `mcp::ToolOutcome` The result of one tool call, as MCP content. ```rust pub struct ToolOutcome { pub content: Vec<Value>, pub structured: Option<Value>, pub is_error: bool, pub meta: Option<Value>, } impl ToolOutcome { fn text(text: impl Into<String>) -> Self; fn json<T: Serialize>(value: &T) -> Self; fn error(e: &Error) -> Self; fn error_message(kind: &str, message: impl Into<String>) -> Self; fn first_text(&self) -> Option<&str>; fn to_result(&self) -> Value; } ``` ## Functions ```rust // mod agents /// What every harness is, can and cannot ... pub fn capabilities() -> CapabilitiesReport /// Resolves `names` from this process's ... pub fn env_from_host(names: &[String]) -> Result<(BTreeMap<String, String>, Vec<String>)> /// The provider key variables a host may ... pub fn forwardable_env() -> Vec<&'static str> // mod agents::telemetry /// Before stopping `run_id`: records an ... pub async fn before_stop(agents: &Agents, run_id: &str) /// The error variant of `e` (never its ... pub fn error_variant(e: &Error) -> &'static str /// A run's status was read: records its ... pub fn observe(info: &RunInfo) /// Starting a run failed. pub fn start_failed(harness: &str, space: &str, entry: &str, e: &Error) /// A run was started from `entry` in the ... pub fn started(agents: &Agents, space: &str, entry: &str, s: &Started) /// A run was stopped before its first turn ... pub fn stopped(run_id: &str) /// Follows `run_id` in the background ... pub fn watch(agents: Agents, run_id: String) // mod mcp /// Runs a Space-scoped contract tool ... pub async fn call_on(space: &Space, tool: &str, arguments: Value) -> ToolOutcome // mod mcp::http /// The `/mcp` router. pub fn router(server: McpServer, bearer: Option<String>) -> Router /// Serves `router` on `listener` until the ... pub async fn serve(server: McpServer, listener: TcpListener, bearer: Option<String>) -> Result<()> // mod mcp::stdio /// Serves until `input` reaches EOF. pub async fn serve<R, W>(server: McpServer, input: R, output: W) -> Result<()> where R: AsyncRead + Unpin, W: AsyncWrite + Unpin + Send + 'static /// Serves on the process's stdin/stdout. pub async fn serve_process_stdio(server: McpServer) -> Result<()> ``` ## Constants ```rust // mod agents::telemetry /// How long a watcher follows a run before ... pub const WATCH_FOR: Duration = _; // mod mcp /// Server instructions sent in ... pub const INSTRUCTIONS: &str = /* ... */; /// Server name in `initialize`. pub const SERVER_NAME: &str = "cua-spaces"; /// Server version in `initialize`. pub const SERVER_VERSION: &str = "0.1.0"; /// Tools `call_on` accepts. pub const SPACE_SCOPED_TOOLS: &[&str] = _; /// Protocol revisions this server accepts ... pub const SUPPORTED_PROTOCOL_VERSIONS: [&str; 3] = _; // mod mcp::codes /// `tools/call` without a tool `name`. pub const INVALID_PARAMS: i64 = -32602; /// The message is not a JSON-RPC request ... pub const INVALID_REQUEST: i64 = -32600; /// An unknown method, or `tools/call` of a ... pub const METHOD_NOT_FOUND: i64 = -32601; /// The message is not valid JSON. pub const PARSE_ERROR: i64 = -32700; // mod mcp::http /// Largest accepted request body (16 MiB). pub const MAX_BODY_BYTES: usize = _; /// Session header. pub const SESSION_HEADER: &str = "mcp-session-id"; // mod mcp::stdio /// Longest accepted message line (16 MiB). pub const MAX_LINE_BYTES: usize = _; ``` ## Type aliases ```rust // mod mcp /// Which contract tools a server exposes ... pub type ToolFilter = Arc<dyn Fn(&str) -> bool + Send + Sync>; ``` ## Re-exports `cua_agents::AgentEvent`, `cua_agents::Agents`, `cua_agents::Artifact`, `cua_agents::Attachment`, `cua_agents::Endpoint`, `cua_agents::EventPage`, `cua_agents::events`, `cua_agents::harness`, `cua_agents::installables`, `cua_agents::McpServer`, `cua_agents::quote`, `cua_agents::RunInfo`, `cua_agents::RunOptions`, `cua_agents::RunResult`, `cua_agents::runs`, `cua_agents::RunStatus`, `cua_agents::Started`, `cua_agents::transcript`, `cua_agents::Transcript`, `cua_agents::TranscriptItem`, `cua_agents::Wire`. --- # Protocol The gRPC contracts of cua-spacesd (cua.env.v1) and the cua daemon (cua.daemon.v1): ports, routes, auth and every RPC. The gRPC contracts, generated from the `.proto` files in `libs/cua/proto`. `cua.env.v1` is served by cua-spacesd inside a sandbox; `cua.daemon.v1` is served by the local `cua daemon`. Changes are additive and pass `buf breaking`. Contract revision: `cua.env.v1` protocol version 1, revision 8 (reported by `SystemService.GetCapabilities`). Clients use the SDK's [`SpacesdClient`](</docs/cua-sdk/reference/spacesd>) or `cua spacesd ...`; `SpacesdClient.call_json` reaches any RPC below. Sandboxes do not require cua-spacesd: only the computer interfaces and the Spaces primitives need it. It never injects input itself; pointer and keyboard actions go to Cua Driver. | Port | Carries | | --- | --- | | TCP 3211 | Native gRPC (HTTP/2) and gRPC-Web (HTTP/1.1), gRPC reflection, and the HTTP routes below | | UDP 3212 | QUIC media datagrams (ALPN `rcdp/2`, certificate pinned through `StreamService.OpenMedia`) | The default bind is `0.0.0.0:3211` with a token and `127.0.0.1:3211` without one; a non-loopback bind without a token is refused. Clients send the env token as `authorization: Bearer <token>` or `x-cua-env-authorization: Bearer <token>` (the Fleet gateway consumes `authorization`). The token grants full control of the machine: never put it in a URL; browsers use tickets and signed URLs. | Route | Auth | Purpose | | --- | --- | --- | | `GET /health` | None | 204 while serving, 503 while shutting down | | `GET`, `HEAD`, `PUT /files` | Signed URL from `CreateSignedUrl` | Upload and download with `Range`; 403 when expired or tampered | | `POST /mcp` | Env token | Streamable-HTTP MCP over the Cua Driver tools; 501 when disabled | | `GET /tunnel` (WebSocket) | Ticket from `TunnelService.Forward` | Port forwarding | | `GET /hotspot` (WebSocket) | Ticket from `StartHotspot` | Reverse SOCKS egress | | `GET /media` (WebSocket) | Ticket from `StreamService.OpenMedia` | Video (H.264) and audio (Opus), media wire v2 (`libs/cua/proto/MEDIA.md`) | Tickets go in `?ticket=` or the WebSocket subprotocol `cua.ticket.<ticket>`. Tickets and signed URLs are keyed from the env token, so rotating the token revokes them. ## Services | Service | Package | Page | RPCs | | --- | --- | --- | --- | | [`SystemService`](</docs/cua-sdk/reference/protocol/env-system#systemservice>) | `cua.env.v1` | [System](</docs/cua-sdk/reference/protocol/env-system>) | 10 | | [`ProcessService`](</docs/cua-sdk/reference/protocol/env-process#processservice>) | `cua.env.v1` | [Processes](</docs/cua-sdk/reference/protocol/env-process>) | 8 | | [`FilesystemService`](</docs/cua-sdk/reference/protocol/env-filesystem#filesystemservice>) | `cua.env.v1` | [Filesystem](</docs/cua-sdk/reference/protocol/env-filesystem>) | 16 | | [`ComputerService`](</docs/cua-sdk/reference/protocol/env-computer#computerservice>) | `cua.env.v1` | [Computer and driver](</docs/cua-sdk/reference/protocol/env-computer>) | 7 | | [`DriverService`](</docs/cua-sdk/reference/protocol/env-computer#driverservice>) | `cua.env.v1` | [Computer and driver](</docs/cua-sdk/reference/protocol/env-computer>) | 2 | | [`WindowsService`](</docs/cua-sdk/reference/protocol/env-windows#windowsservice>) | `cua.env.v1` | [Windows and accessibility](</docs/cua-sdk/reference/protocol/env-windows>) | 11 | | [`AccessibilityService`](</docs/cua-sdk/reference/protocol/env-windows#accessibilityservice>) | `cua.env.v1` | [Windows and accessibility](</docs/cua-sdk/reference/protocol/env-windows>) | 3 | | [`StreamService`](</docs/cua-sdk/reference/protocol/env-stream#streamservice>) | `cua.env.v1` | [Streams and presence](</docs/cua-sdk/reference/protocol/env-stream>) | 5 | | [`PresenceService`](</docs/cua-sdk/reference/protocol/env-stream#presenceservice>) | `cua.env.v1` | [Streams and presence](</docs/cua-sdk/reference/protocol/env-stream>) | 3 | | [`TeleportService`](</docs/cua-sdk/reference/protocol/env-teleport-tunnel#teleportservice>) | `cua.env.v1` | [Teleport, tunnels and the volume](</docs/cua-sdk/reference/protocol/env-teleport-tunnel>) | 7 | | [`TunnelService`](</docs/cua-sdk/reference/protocol/env-teleport-tunnel#tunnelservice>) | `cua.env.v1` | [Teleport, tunnels and the volume](</docs/cua-sdk/reference/protocol/env-teleport-tunnel>) | 6 | | [`VolumeService`](</docs/cua-sdk/reference/protocol/env-teleport-tunnel#volumeservice>) | `cua.env.v1` | [Teleport, tunnels and the volume](</docs/cua-sdk/reference/protocol/env-teleport-tunnel>) | 3 | | [`HostSpacesService`](</docs/cua-sdk/reference/protocol/env-host-spaces#hostspacesservice>) | `cua.env.v1` | [Host Spaces](</docs/cua-sdk/reference/protocol/env-host-spaces>) | 6 | | [`SandboxService`](</docs/cua-sdk/reference/protocol/daemon-sandboxes#sandboxservice>) | `cua.daemon.v1` | [Daemon: sandboxes](</docs/cua-sdk/reference/protocol/daemon-sandboxes>) | 19 | | [`RuntimeService`](</docs/cua-sdk/reference/protocol/daemon-runtimes#runtimeservice>) | `cua.daemon.v1` | [Daemon: local runtimes](</docs/cua-sdk/reference/protocol/daemon-runtimes>) | 5 | | [`SpaceService`](</docs/cua-sdk/reference/protocol/daemon-spaces#spaceservice>) | `cua.daemon.v1` | [Daemon: Spaces](</docs/cua-sdk/reference/protocol/daemon-spaces>) | 17 | | [`DaemonService`](</docs/cua-sdk/reference/protocol/daemon-service#daemonservice>) | `cua.daemon.v1` | [Daemon: service](</docs/cua-sdk/reference/protocol/daemon-service>) | 3 | ## Metadata and HTTP routes Well-known metadata keys and HTTP paths (`cua_proto::metadata`). | Constant | Value | Meaning | | --- | --- | --- | | `AUTHORIZATION` | `authorization` | Bearer token header: `authorization: Bearer <token>`. | | `ENV_AUTHORIZATION` | `x-cua-env-authorization` | Alternate spacesd token header, `x-cua-env-authorization: Bearer <token>`, for paths where `authorization` belongs to an outer hop (the Fleet gateway consumes and strips it). Servers accept either header. | | `PRINCIPAL_BIN` | `x-cua-principal-bin` | Binary metadata carrying a serialized `cua.env.v1.Principal`. | | `HEALTH_PATH` | `/health` | Plain-HTTP, unauthenticated health route (204 when healthy). | | `MEDIA_WS_PATH` | `/media` | Media WebSocket path (ticket-authenticated). | | `TUNNEL_WS_PATH` | `/tunnel` | TCP-over-WebSocket tunnel path (ticket-authenticated). | | `HOTSPOT_WS_PATH` | `/hotspot` | Reverse-SOCKS hotspot WebSocket path (ticket-authenticated). | | `VOLUME_WS_PATH` | `/volume` | Cua Volume WebSocket path (ticket-authenticated): the client serves the guest's volume mount over it. | | `FILES_PATH` | `/files` | Signed-URL file route. | | `MCP_PATH` | `/mcp` | Streamable-HTTP MCP endpoint for the cua-driver tool registry. | | `VIEWER_PATH` | `/viewer` | The web viewer (static page, unauthenticated; it reads its viewer ticket from the URL fragment). Served with and without the trailing slash; assets live under `/viewer/`. | ## gRPC-Web fallbacks gRPC-Web cannot carry client streams, so every client-streaming RPC has a unary fallback (`cua_proto::CLIENT_STREAM_FALLBACKS`). | Client stream | Unary fallback | | --- | --- | | `/cua.env.v1.ProcessService/StreamInput` | `/cua.env.v1.ProcessService/SendInput` | | `/cua.env.v1.FilesystemService/WriteFile` | `/cua.env.v1.FilesystemService/UploadChunk` | ## Errors Errors are `google.rpc.Status` with typed details: spacesd packs [`ErrorInfo`](</docs/cua-sdk/reference/protocol/env-common#errorinfo>) and the daemon packs [`DaemonErrorInfo`](</docs/cua-sdk/reference/protocol/daemon-service#daemonerrorinfo>). --- # System cua.env.v1 SystemService: capabilities, init, health, shutdown, viewer tickets and relay attach. The handshake (`GetCapabilities`), session init, health, shutdown, viewer tickets and joining a relay (`AttachRelay`). Source: `libs/cua/proto/cua/env/v1/system.proto`. ## SystemService `cua.env.v1.SystemService` Discovery, bootstrap and lifecycle of the spacesd itself. `GetCapabilities` is the probe the SDK uses to decide whether a sandbox has cua-spacesd at all (`sb.spacesd()`), and which features it may rely on. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`GetCapabilities`](#systemservicegetcapabilities) | [`GetCapabilitiesRequest`](#getcapabilitiesrequest) | [`GetCapabilitiesResponse`](#getcapabilitiesresponse) | unary | | [`Init`](#systemserviceinit) | [`InitRequest`](#initrequest) | [`InitResponse`](#initresponse) | unary | | [`Health`](#systemservicehealth) | [`HealthRequest`](#healthrequest) | [`HealthResponse`](#healthresponse) | unary | | [`Metrics`](#systemservicemetrics) | [`MetricsRequest`](#metricsrequest) | [`MetricsResponse`](#metricsresponse) | unary | | [`Shutdown`](#systemserviceshutdown) | [`ShutdownRequest`](#shutdownrequest) | [`ShutdownResponse`](#shutdownresponse) | unary | | [`Diagnose`](#systemservicediagnose) | [`DiagnoseRequest`](</docs/cua-sdk/reference/protocol/env-diagnose#diagnoserequest>) | [`DiagnoseResponse`](</docs/cua-sdk/reference/protocol/env-diagnose#diagnoseresponse>) | server stream | | [`DiagnoseOnce`](#systemservicediagnoseonce) | [`DiagnoseOnceRequest`](</docs/cua-sdk/reference/protocol/env-diagnose#diagnoseoncerequest>) | [`DiagnoseOnceResponse`](</docs/cua-sdk/reference/protocol/env-diagnose#diagnoseonceresponse>) | unary | | [`CreateViewerTicket`](#systemservicecreateviewerticket) | [`CreateViewerTicketRequest`](#createviewerticketrequest) | [`CreateViewerTicketResponse`](#createviewerticketresponse) | unary | | [`AttachRelay`](#systemserviceattachrelay) | [`AttachRelayRequest`](#attachrelayrequest) | [`AttachRelayResponse`](#attachrelayresponse) | unary | | [`DetachRelay`](#systemservicedetachrelay) | [`DetachRelayRequest`](#detachrelayrequest) | [`DetachRelayResponse`](#detachrelayresponse) | unary | ### SystemService.GetCapabilities `/cua.env.v1.SystemService/GetCapabilities`, unary: [`GetCapabilitiesRequest`](#getcapabilitiesrequest) to [`GetCapabilitiesResponse`](#getcapabilitiesresponse). Returns version, platform and the feature list. Cheap; safe to poll. ### SystemService.Init `/cua.env.v1.SystemService/Init`, unary: [`InitRequest`](#initrequest) to [`InitResponse`](#initresponse). Configures the driver after boot or claim. Idempotent: repeating the same request is a no-op, and later calls overwrite earlier values. ### SystemService.Health `/cua.env.v1.SystemService/Health`, unary: [`HealthRequest`](#healthrequest) to [`HealthResponse`](#healthresponse). Liveness and per-component health. The same information in reduced form is served unauthenticated as plain HTTP `GET /health` (204 when healthy). ### SystemService.Metrics `/cua.env.v1.SystemService/Metrics`, unary: [`MetricsRequest`](#metricsrequest) to [`MetricsResponse`](#metricsresponse). A point-in-time sample of guest resource usage. ### SystemService.Shutdown `/cua.env.v1.SystemService/Shutdown`, unary: [`ShutdownRequest`](#shutdownrequest) to [`ShutdownResponse`](#shutdownresponse). Stops or restarts the driver, or powers off or reboots the guest. ### SystemService.Diagnose `/cua.env.v1.SystemService/Diagnose`, server stream: [`DiagnoseRequest`](</docs/cua-sdk/reference/protocol/env-diagnose#diagnoserequest>) to [`DiagnoseResponse`](</docs/cua-sdk/reference/protocol/env-diagnose#diagnoseresponse>). Runs the image self-test (the checks of `cua-spacesd doctor`) and streams each check as it finishes, then the report. Requires the token even in bootstrap modes: the report names paths, versions and units. ### SystemService.DiagnoseOnce `/cua.env.v1.SystemService/DiagnoseOnce`, unary: [`DiagnoseOnceRequest`](</docs/cua-sdk/reference/protocol/env-diagnose#diagnoseoncerequest>) to [`DiagnoseOnceResponse`](</docs/cua-sdk/reference/protocol/env-diagnose#diagnoseonceresponse>). `Diagnose` as one unary call returning only the final report, for clients that cannot read server streams. ### SystemService.CreateViewerTicket `/cua.env.v1.SystemService/CreateViewerTicket`, unary: [`CreateViewerTicketRequest`](#createviewerticketrequest) to [`CreateViewerTicketResponse`](#createviewerticketresponse). Mints a viewer ticket: a scoped, expiring credential for the web viewer served at `/viewer` on this port. It authorizes only the calls a viewer makes (streaming, and clipboard and files when granted) and is safe in a URL fragment. Viewer tickets cannot mint viewer tickets. ### SystemService.AttachRelay `/cua.env.v1.SystemService/AttachRelay`, unary: [`AttachRelayRequest`](#attachrelayrequest) to [`AttachRelayResponse`](#attachrelayresponse). Makes this running driver reachable through a `cua-relay` as a machine of the owner's account, without restarting it (what `cua-spacesd join` does at start). Callers then authenticate with relay-signed assertions: the owner, accounts the relay shares the machine with, and view-only accounts that may only watch. Only the root-token holder may call it (never a relay caller or a viewer ticket). Feature `relay_attach`. ### SystemService.DetachRelay `/cua.env.v1.SystemService/DetachRelay`, unary: [`DetachRelayRequest`](#detachrelayrequest) to [`DetachRelayResponse`](#detachrelayresponse). Leaves the relay attached with `AttachRelay` and stops accepting relay assertions. Root-token holder only. ## Types in `cua/env/v1/system.proto` ### GetCapabilitiesRequest Request for `SystemService.GetCapabilities`. No fields. ### GetCapabilitiesResponse Response for `SystemService.GetCapabilities`. | Field | # | Type | Description | | --- | --- | --- | --- | | `version` | 1 | `string` | cua-spacesd release version (semver), for example "0.3.1". | | `protocol_version` | 2 | `uint32` | Major contract version. Always 1 for package `cua.env.v1`. | | `protocol_revision` | 3 | `uint32` | Additive revision within the major version. Incremented whenever an RPC, field or enum value is added, so clients can reason about optional surface without probing. | | `os` | 4 | [`OperatingSystem`](#operatingsystem) | Guest operating system. | | `runtime` | 5 | [`Runtime`](#runtime) | How the guest is hosted. | | `runtime_detail` | 6 | `string` | Free-form runtime detail (for example "runsc 20260901" or "kubevirt 1.4 / qemu 9.1"). | | `arch` | 7 | [`Architecture`](#architecture) | Guest CPU architecture. | | `display_server` | 8 | [`DisplayServer`](#displayserver) | The graphical session type, or `DISPLAY_SERVER_NONE` when headless. | | `displays` | 9 | repeated [`Display`](</docs/cua-sdk/reference/protocol/env-common#display>) | Displays attached to the guest desktop. Empty when headless. | | `features` | 10 | repeated [`Feature`](#feature) | Every feature the driver knows about, supported or not. Unsupported features carry a `limitation`. See `Feature` for canonical names. | | `hostname` | 11 | `string` | Guest hostname. | | `initialized` | 12 | `bool` | True once `Init` has succeeded at least once since the driver started. | | `boot_time` | 13 | `google.protobuf.Timestamp` | When the guest booted. | | `side_channels` | 14 | [`SideChannels`](#sidechannels) | Where the media and tunnel sockets are served. | | `limits` | 15 | [`Limits`](#limits) | Server-enforced size limits clients must respect. | | `machine_seal_public_key` | 16 | `bytes` | This guest's long-term X25519 public key for sealed delivery (S1): Keyvault site-login and teleport bundle payloads sealed to it cross a relay as ciphertext. Generated on first start, 0600. Empty from a guest image that predates this (older images): callers must refuse to deliver such secrets over a relay: path unless the user explicitly opts in per delivery. 32 bytes when present. | ### OperatingSystem Guest operating system description. | Field | # | Type | Description | | --- | --- | --- | --- | | `family` | 1 | [`OsFamily`](#osfamily) | OS family. | | `name` | 2 | `string` | Distribution or product name, for example "Ubuntu" or "macOS". | | `version` | 3 | `string` | Version string, for example "24.04" or "15.4". | | `kernel` | 4 | `string` | Kernel release, for example "6.8.0-45-generic" or "Darwin 24.4.0". | | `pretty_name` | 5 | `string` | The full product string, for example "Ubuntu 24.04.3 LTS", "macOS 26.5.2 (25F84)" or "Windows Server 2022 Datacenter 10.0.20348". Empty from older drivers: use `name` and `version`. | ### Feature One capability and whether this guest supports it. Canonical names (lowercase, dot-separated, stable forever): "a11y", "background_input", "desktop_stream", "window_stream", "h264_hw", "h264_sw", "quic_media", "pty", "fs_watch", "clipboard.text", "clipboard.files", "presence", "windows", "launch_app", "driver", "tunnel.forward", "hotspot", "teleport.<provider>" (for example "teleport.firefox"), "audio.desktop" (desktop audio capture), "audio.per_app" (per-application audio capture; when unsupported, per-app requests fall back to desktop audio and `limitation` says so), "audio.uplink" (client audio into a guest virtual source), "audio.opus", "presence.cursor_shape" (server-computed cursor shapes; attributes "hit_test", "system" and "probe" name the backends, "probe" is "off" when `PresenceSettings.cursor_probe` is disabled), and "experimental.<name>" for experimental fields. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Canonical feature name. | | `supported` | 2 | `bool` | True if the feature works on this guest. | | `limitation` | 3 | `string` | When unsupported or degraded: why, in one sentence a user can act on (for example "AT-SPI bus not running; start the session with dbus-run-session"). Empty when fully supported. | | `attributes` | 4 | map of `string` to `string` | Feature-specific details (for example {"encoder": "vaapi"} for "h264_hw", {"isolation": "runc"} for a container runtime). | ### SideChannels Where the non-gRPC sockets live, relative to the gRPC endpoint. | Field | # | Type | Description | | --- | --- | --- | --- | | `media_ws_path` | 1 | `string` | HTTP path of the media WebSocket, normally "/media". | | `media_quic_port` | 2 | `uint32` | UDP port of the direct QUIC media listener, 0 when disabled. Normally the gRPC port plus one (3212). | | `tunnel_ws_path` | 3 | `string` | HTTP path of the TCP-over-WebSocket tunnel, normally "/tunnel". | | `files_http_path` | 4 | `string` | HTTP path of the signed-URL file route, normally "/files". | | `mcp_http_path` | 5 | `string` | HTTP path of the streamable-HTTP MCP endpoint exposing the cua-driver tool registry, normally "/mcp". Empty when disabled. | ### Limits Server-enforced limits. | Field | # | Type | Description | | --- | --- | --- | --- | | `max_chunk_bytes` | 1 | `uint32` | Largest `bytes` payload accepted in one chunk message (upload chunks, stdin chunks, receive-files chunks). Normally 4 MiB. | | `max_message_bytes` | 2 | `uint32` | Largest encoded gRPC message accepted or sent. Normally 8 MiB. | | `preferred_chunk_bytes` | 3 | `uint32` | Recommended chunk size for bulk transfer. Normally 1 MiB. | | `default_scrollback_bytes` | 4 | `uint64` | Default per-process scrollback ring size in bytes. | ### InitRequest Request for `SystemService.Init`. | Field | # | Type | Description | | --- | --- | --- | --- | | `token` | 1 | `string` | Access token to install. If the driver has no token yet (first claim), the call must arrive over an already-authenticated channel such as the Fleet gateway or loopback. If a token is configured, the call must be authenticated with it, and a different value here rotates it. Empty leaves the token unchanged. | | `env` | 2 | map of `string` to `string` | Environment variables added to every process started afterwards. Keys present here replace earlier values; an empty value unsets the key. | | `default_user` | 3 | `string` | Default OS user for processes and file operations. Empty keeps the current default (the driver's own user). | | `default_workdir` | 4 | `string` | Default working directory for processes. Empty keeps the user's home. | | `ca_bundle` | 5 | `bytes` | PEM-encoded CA certificates appended to the guest trust store. | | `now` | 6 | `google.protobuf.Timestamp` | Caller's wall clock. When set and the guest clock is off by more than one second, the driver steps the guest clock (VMs resumed from snapshot drift). | | `labels` | 7 | map of `string` to `string` | Opaque labels recorded for diagnostics (for example sandbox name, claim id). | | `audio_uplink` | 8 | [`AudioUplinkAccess`](#audiouplinkaccess) | Who may open an audio uplink (`StreamService.OpenMedia` with `audio.uplink.enabled`). Unset leaves the current setting; the initial setting is disabled. | | `presence` | 9 | [`PresenceSettings`](#presencesettings) | Presence settings of this Space. Unset leaves the current settings. | ### PresenceSettings Per-Space presence settings. | Field | # | Type | Description | | --- | --- | --- | --- | | `cursor_probe` | 1 | optional `bool` | Whether the server may read the real cursor shape at a participant's position by briefly moving the guest pointer there while it is idle (no injected input or agent action recently) and restoring it exactly. Gives app-specific shapes the accessibility hit-test cannot infer; hover states may flash under the probe. Unset leaves the current setting; the initial setting is enabled. | ### AudioUplinkAccess Per-principal permission for audio uplink. | Field | # | Type | Description | | --- | --- | --- | --- | | `mode` | 1 | [`AudioUplinkMode`](#audiouplinkmode) | Access mode. | | `principal_ids` | 2 | repeated `string` | With `AUDIO_UPLINK_MODE_ALLOWLIST`: `Principal.id` values allowed to open an uplink. | ### InitResponse Response for `SystemService.Init`. | Field | # | Type | Description | | --- | --- | --- | --- | | `token_changed` | 1 | `bool` | True if this call installed or rotated the token. | | `clock_adjusted` | 2 | `bool` | True if the guest clock was stepped. | ### HealthRequest Request for `SystemService.Health`. No fields. ### HealthResponse Response for `SystemService.Health`. | Field | # | Type | Description | | --- | --- | --- | --- | | `status` | 1 | [`HealthStatus`](#healthstatus) | Overall status, the worst of all components. | | `components` | 2 | repeated [`ComponentHealth`](#componenthealth) | Per-subsystem status (for example "capture", "a11y", "encoder", "filesystem"). | | `uptime` | 3 | `google.protobuf.Duration` | Time since the driver process started. | ### ComponentHealth Health of one subsystem. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Subsystem name. | | `status` | 2 | [`HealthStatus`](#healthstatus) | Its status. | | `detail` | 3 | `string` | Why it is not serving, when applicable. | ### MetricsRequest Request for `SystemService.Metrics`. | Field | # | Type | Description | | --- | --- | --- | --- | | `disk_path` | 1 | `string` | Path whose filesystem is reported in `disk_*`. Empty means the default working directory's filesystem. | ### MetricsResponse Response for `SystemService.Metrics`. | Field | # | Type | Description | | --- | --- | --- | --- | | `sampled_at` | 1 | `google.protobuf.Timestamp` | When the sample was taken. | | `cpu_count` | 2 | `uint32` | Logical CPUs visible to the guest. | | `cpu_percent` | 3 | `double` | CPU utilisation across all CPUs, 0 to 100. | | `load_average_1m` | 4 | `double` | 1-minute load average (0 on Windows). | | `memory_total_bytes` | 5 | `uint64` | Total guest memory in bytes. | | `memory_used_bytes` | 6 | `uint64` | Used guest memory in bytes (excluding reclaimable cache). | | `disk_total_bytes` | 7 | `uint64` | Total bytes of the filesystem named by `disk_path`. | | `disk_used_bytes` | 8 | `uint64` | Used bytes of that filesystem. | | `managed_process_count` | 9 | `uint32` | Processes currently managed by `ProcessService`. | | `media_session_count` | 10 | `uint32` | Open media sessions. | | `network_rx_bytes` | 11 | `uint64` | Bytes received on all guest network interfaces since boot. | | `network_tx_bytes` | 12 | `uint64` | Bytes sent on all guest network interfaces since boot. | | `memory_limited` | 13 | `bool` | `memory_total_bytes` is the guest's own limit: a virtual machine's memory or a container's cgroup memory limit. False when it is only the host's memory (a container without a limit, bare metal). | | `disk_limited` | 14 | `bool` | `disk_total_bytes` is the guest's own disk: a virtual machine's disk or a filesystem with a quota. False when it is the host's (a container's overlay, bare metal). | ### ShutdownRequest Request for `SystemService.Shutdown`. | Field | # | Type | Description | | --- | --- | --- | --- | | `mode` | 1 | [`ShutdownMode`](#shutdownmode) | What to stop. | | `grace_period` | 2 | `google.protobuf.Duration` | How long to wait for in-flight RPCs and managed processes before forcing. Unset means 5 seconds. | | `reason` | 3 | `string` | Reason recorded in the driver log. | ### ShutdownResponse Response for `SystemService.Shutdown`. Sent before the shutdown begins. No fields. ### CreateViewerTicketRequest Request for `SystemService.CreateViewerTicket`. | Field | # | Type | Description | | --- | --- | --- | --- | | `ttl` | 1 | `google.protobuf.Duration` | Lifetime. Unset means 1 hour; the maximum is 24 hours. Rotating the root token revokes every viewer ticket. | | `policy` | 2 | [`SessionPolicy`](</docs/cua-sdk/reference/protocol/env-stream#sessionpolicy>) | Highest session policy the viewer may open media with. Unset means `SESSION_POLICY_ALLOW_ACTIVATION` (a full interactive viewer). | | `clipboard` | 3 | `bool` | Allow `ComputerService.GetClipboard` and `SetClipboard`. | | `files_root` | 4 | `string` | Guest directory the viewer may read and write through `FilesystemService` (drag-and-drop uploads and folder sharing). Empty means no file access. `~` is the desktop user's home. | | `audio_uplink` | 5 | `bool` | Allow a microphone uplink track (still subject to `InitRequest.audio_uplink`). | | `principal` | 6 | [`Principal`](</docs/cua-sdk/reference/protocol/env-common#principal>) | Who is viewing, shown to other participants. The id is prefixed with `viewer:` by the server. | ### CreateViewerTicketResponse Response for `SystemService.CreateViewerTicket`. | Field | # | Type | Description | | --- | --- | --- | --- | | `ticket` | 1 | `string` | The ticket. The viewer sends it as `x-cua-env-authorization: Bearer <ticket>` (or `authorization`). | | `expires_at` | 2 | `google.protobuf.Timestamp` | When it expires. | | `viewer_path` | 3 | `string` | Path of the viewer page with the ticket in the fragment, relative to this port: `/viewer/#ticket=<ticket>`. Fragments never reach servers or proxies. | | `files_root` | 4 | `string` | `files_root` resolved to an absolute guest path (empty without file access). | ### AttachRelayRequest Request for `SystemService.AttachRelay`. | Field | # | Type | Description | | --- | --- | --- | --- | | `relay_url` | 1 | `string` | Relay base URL (`wss://relay.cua.ai`, `ws://relay:8080`, `https://...`). | | `machine_token` | 2 | `string` | The machine token the relay issued when the owner registered this machine (`POST /v1/machines`). Kept in memory only. | | `machine_id` | 3 | `string` | The machine id the owner registered (`[a-z0-9-]{8,64}`). | | `relay_jwks_json` | 4 | `string` | The relay's signing keys (JWKS JSON) from the registration, pinned: the join handshake must present one of them. | | `owner` | 5 | `string` | The owning account id. | | `owner_email` | 6 | `string` | The owner's email (informational). | ### AttachRelayResponse Response for `SystemService.AttachRelay`. | Field | # | Type | Description | | --- | --- | --- | --- | | `machine_id` | 1 | `string` | The machine id this driver joined as. | ### DetachRelayRequest Request for `SystemService.DetachRelay`. No fields. ### DetachRelayResponse Response for `SystemService.DetachRelay`. | Field | # | Type | Description | | --- | --- | --- | --- | | `detached` | 1 | `bool` | False when no relay was attached. | ### OsFamily Operating system family. | Value | # | Description | | --- | --- | --- | | `OS_FAMILY_UNSPECIFIED` | 0 | Not reported. | | `OS_FAMILY_LINUX` | 1 | Linux. | | `OS_FAMILY_MACOS` | 2 | macOS. | | `OS_FAMILY_WINDOWS` | 3 | Windows. | ### Runtime How the guest is hosted. | Value | # | Description | | --- | --- | --- | | `RUNTIME_UNSPECIFIED` | 0 | Not reported. | | `RUNTIME_UNKNOWN` | 1 | Could not be determined. | | `RUNTIME_KUBEVIRT` | 2 | A KubeVirt virtual machine (Fleet). | | `RUNTIME_GVISOR` | 3 | A gVisor (runsc) sandboxed container. | | `RUNTIME_LUME` | 4 | A Lume (Apple Virtualization.framework) virtual machine. | | `RUNTIME_QEMU` | 5 | A QEMU virtual machine outside KubeVirt. | | `RUNTIME_CONTAINER` | 6 | A plain container (runc or equivalent). | | `RUNTIME_BARE` | 7 | Bare metal or an unmanaged host (for example a laptop joined through the relay). | | `RUNTIME_HYPERV` | 8 | A Hyper-V virtual machine. | ### Architecture CPU architecture. | Value | # | Description | | --- | --- | --- | | `ARCHITECTURE_UNSPECIFIED` | 0 | Not reported. | | `ARCHITECTURE_X86_64` | 1 | x86-64 / amd64. | | `ARCHITECTURE_ARM64` | 2 | AArch64 / arm64. | ### DisplayServer Graphical session type. | Value | # | Description | | --- | --- | --- | | `DISPLAY_SERVER_UNSPECIFIED` | 0 | Not reported. | | `DISPLAY_SERVER_NONE` | 1 | No graphical session. | | `DISPLAY_SERVER_X11` | 2 | X11 (including Xvfb). | | `DISPLAY_SERVER_WAYLAND` | 3 | Wayland. | | `DISPLAY_SERVER_QUARTZ` | 4 | macOS Quartz / WindowServer. | | `DISPLAY_SERVER_WIN32` | 5 | Windows desktop window manager. | ### AudioUplinkMode Audio uplink access mode. | Value | # | Description | | --- | --- | --- | | `AUDIO_UPLINK_MODE_UNSPECIFIED` | 0 | Not set. Keeps the current mode. | | `AUDIO_UPLINK_MODE_DISABLED` | 1 | No uplinks. The initial mode. | | `AUDIO_UPLINK_MODE_ALLOWLIST` | 2 | Only principals in `principal_ids`. | | `AUDIO_UPLINK_MODE_ANY` | 3 | Any authenticated caller. | ### HealthStatus Health status. | Value | # | Description | | --- | --- | --- | | `HEALTH_STATUS_UNSPECIFIED` | 0 | Not reported. | | `HEALTH_STATUS_SERVING` | 1 | Fully operational. | | `HEALTH_STATUS_DEGRADED` | 2 | Operational with reduced function (see component details). | | `HEALTH_STATUS_NOT_SERVING` | 3 | Not operational. | ### ShutdownMode What `SystemService.Shutdown` stops. | Value | # | Description | | --- | --- | --- | | `SHUTDOWN_MODE_UNSPECIFIED` | 0 | Not set. Rejected with INVALID_ARGUMENT, to avoid accidental power-off. | | `SHUTDOWN_MODE_DRIVER_EXIT` | 1 | Exit the driver. The service manager may restart it. | | `SHUTDOWN_MODE_DRIVER_RESTART` | 2 | Restart the driver in place (re-exec). | | `SHUTDOWN_MODE_GUEST_POWEROFF` | 3 | Power off the guest. | | `SHUTDOWN_MODE_GUEST_REBOOT` | 4 | Reboot the guest. | --- # Diagnose cua.env.v1 Diagnose: the image self-test report `SystemService.Diagnose` streams. The report of the image self-test (`SystemService.Diagnose` / `DiagnoseOnce`): checks, their evidence and the summary. Source: `libs/cua/proto/cua/env/v1/diagnose.proto`. ## Types in `cua/env/v1/diagnose.proto` ### DiagnoseOptions Options shared by `Diagnose` and `DiagnoseOnce`. | Field | # | Type | Description | | --- | --- | --- | --- | | `strict` | 1 | `bool` | Treat warnings (and skips not allowed by the manifest) as failures. | | `only` | 2 | repeated `string` | Run only checks whose id equals, or starts with `<entry>.`, one of these (for example "stream" or "auth.token_file"). Empty runs all. | | `skip` | 3 | repeated `string` | Skip checks matched the same way as `only`. | | `effects` | 4 | [`DiagnoseEffects`](#diagnoseeffects) | Which checks may change guest state. Unspecified means `NONE`. | | `expect_manifest` | 5 | `bytes` | A manifest (JSON, same schema as `/etc/cua-image/manifest.json`) to check against instead of the one baked into the image. Empty uses the image's own. | | `timeout` | 6 | `google.protobuf.Duration` | Overall budget. Unset means 120 seconds. Checks still running when it expires fail with a timeout. | | `host_time` | 7 | `google.protobuf.Timestamp` | The caller's wall clock, for the clock-skew check. Unset skips the comparison. | ### DiagnoseRequest Request for `SystemService.Diagnose`. | Field | # | Type | Description | | --- | --- | --- | --- | | `options` | 1 | [`DiagnoseOptions`](#diagnoseoptions) | What to run. | ### DiagnoseResponse One event of `SystemService.Diagnose`: `started`, then `check`, for each check, and a final `report`. | Field | # | Type | Description | | --- | --- | --- | --- | | `started` | 1 | [`DiagnoseCheckStarted`](#diagnosecheckstarted) (oneof `event`) | A check began. | | `check` | 2 | [`DiagnoseCheck`](#diagnosecheck) (oneof `event`) | A check finished. | | `report` | 3 | [`DiagnoseReport`](#diagnosereport) (oneof `event`) | The run finished. Always the last message. | ### DiagnoseCheckStarted A check began. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | Check id, for example "stream.encode.openh264". | | `group` | 2 | `string` | Check group, the id's first segment. | ### DiagnoseOnceRequest Request for `SystemService.DiagnoseOnce`. | Field | # | Type | Description | | --- | --- | --- | --- | | `options` | 1 | [`DiagnoseOptions`](#diagnoseoptions) | What to run. | ### DiagnoseOnceResponse Response for `SystemService.DiagnoseOnce`. | Field | # | Type | Description | | --- | --- | --- | --- | | `report` | 1 | [`DiagnoseReport`](#diagnosereport) | The final report. | ### DiagnoseCheck The result of one check. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | Stable id, `<group>.<name>[.<detail>]`. | | `group` | 2 | `string` | Group, the id's first segment (for example "auth"). | | `status` | 3 | [`CheckStatus`](#checkstatus) | Outcome after severity is applied. | | `severity` | 4 | [`CheckSeverity`](#checkseverity) | Severity from the manifest. | | `claimed_by` | 5 | repeated `string` | Manifest claims that make this check matter, for example "feature:desktop_stream" or "auth_mode:local". | | `message` | 6 | `string` | What happened, in one line. | | `remediation` | 7 | `string` | What to do about a warning or failure. Empty on pass. | | `duration_ms` | 8 | `uint32` | Wall time of the check in milliseconds. | | `facts` | 9 | map of `string` to `string` | Measured values (for example {"backend": "openh264", "psnr_db": "41.2"}). | | `artifacts` | 10 | repeated [`DiagnoseArtifact`](#diagnoseartifact) | Evidence files. | ### DiagnoseArtifact Evidence produced by a check. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | File name, unique within the report (for example "screenshot.display0.png"). | | `media_type` | 2 | `string` | Media type, for example "image/png". | | `data` | 3 | `bytes` | Contents, when small enough to inline (at most 1 MiB). Empty otherwise. | | `path` | 4 | `string` | Where the full file was written in the guest. Empty when not written. | ### DiagnoseReport The whole report. | Field | # | Type | Description | | --- | --- | --- | --- | | `schema_version` | 1 | `uint32` | Report schema version. Always 1 for this message. | | `spacesd` | 2 | [`DiagnoseSpacesd`](#diagnosespacesd) | The daemon that ran the checks. Unset when a shim produced the report for an image without cua-spacesd. | | `image` | 3 | [`DiagnoseImage`](#diagnoseimage) | The image under test. | | `environment` | 4 | [`DiagnoseEnvironment`](#diagnoseenvironment) | Where the guest runs. | | `summary` | 5 | [`DiagnoseSummary`](#diagnosesummary) | Totals. | | `checks` | 6 | repeated [`DiagnoseCheck`](#diagnosecheck) | Every check, in run order. | | `fidelity` | 7 | [`DiagnoseFidelity`](#diagnosefidelity) | Facts that make a task behave differently between variants of one image. Compared key by key by the eval-parity lane. | | `producer` | 8 | `string` | What produced the report: "cua-spacesd", or "cua-doctor-shim/<target>". | | `started_at` | 9 | `google.protobuf.Timestamp` | When the run started. | ### DiagnoseSpacesd The daemon that ran the checks. | Field | # | Type | Description | | --- | --- | --- | --- | | `version` | 1 | `string` | cua-spacesd version. | | `protocol_revision` | 2 | `uint32` | `cua.env.v1` protocol revision it serves. | | `git_sha` | 3 | `string` | Source revision of the build, when known. | | `cua_driver_version` | 4 | `string` | Version of the linked cua-driver core. | ### DiagnoseImage The image under test. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Image name from the manifest, for example "linux". | | `ref` | 2 | `string` | Reference the image was built as, when the manifest records it. | | `variant` | 3 | `string` | "rootfs" (container) or "containerdisk" (VM). | | `os` | 4 | `string` | "linux", "windows" or "macos". | | `manifest_sha256` | 5 | `string` | SHA-256 of the manifest the checks ran against. Empty without one. | | `manifest_source` | 6 | `string` | Where the manifest came from: a guest path, "request", or empty when there is none. | ### DiagnoseEnvironment Where the guest runs. | Field | # | Type | Description | | --- | --- | --- | --- | | `runtime` | 1 | `string` | Runtime: "container", "gvisor", "qemu", "kubevirt", "lume", "hyperv", "bare" or "unknown". | | `runtime_detail` | 2 | `string` | Free-form runtime detail, for example "runsc". | | `arch` | 3 | `string` | "x86_64" or "arm64". | | `init` | 4 | `string` | Init system: "systemd", "supervisord", "launchd", "scm" or "unknown". | | `display_server` | 5 | `string` | "x11", "wayland", "quartz", "win32" or "none". | | `os` | 6 | `string` | OS name and version, for example "Ubuntu 24.04". | ### DiagnoseSummary Totals of a report. | Field | # | Type | Description | | --- | --- | --- | --- | | `status` | 1 | [`CheckStatus`](#checkstatus) | Worst outcome: fail if any check failed, warn if any warned (fail under `strict`), else pass. | | `pass` | 2 | `uint32` | Passed checks. | | `warn` | 3 | `uint32` | Warnings. | | `fail` | 4 | `uint32` | Failures. | | `skip` | 5 | `uint32` | Skipped checks. | | `duration_ms` | 6 | `uint32` | Wall time of the whole run in milliseconds. | | `strict` | 7 | `bool` | True when the run used `strict`. | ### DiagnoseFidelity Facts that change how a task behaves. Every key is always present; an unknown value is the empty string. | Field | # | Type | Description | | --- | --- | --- | --- | | `display` | 1 | `string` | Primary display, "<width>x<height>@<scale>". | | `fonts_sha256` | 2 | `string` | SHA-256 over the sorted list of installed font files. | | `tz` | 3 | `string` | Time zone name. | | `locale` | 4 | `string` | Locale, for example "C.UTF-8". | | `clock_skew_ms` | 5 | `int64` | Guest clock minus the caller's clock, in milliseconds. 0 when the caller sent no `host_time`. | | `encoder` | 6 | `string` | Video encoder the stream would use, for example "openh264". | | `a11y_backend` | 7 | `string` | Accessibility backend, for example "atspi". | | `audio_backend` | 8 | `string` | Audio backend, for example "pipewire". | | `cpu_model` | 9 | `string` | CPU model string. | | `cpu_count` | 10 | `uint32` | Logical CPUs. | | `memory_mib` | 11 | `uint64` | Total memory in MiB. | | `gpu` | 12 | `string` | GPU description, or "none". | | `kernel` | 13 | `string` | Kernel release. | | `runtime` | 14 | `string` | Runtime, as in `DiagnoseEnvironment.runtime`. | | `init` | 15 | `string` | Init system, as in `DiagnoseEnvironment.init`. | | `packages_sha256` | 16 | `string` | SHA-256 over the installed package list (name and version). | | `app_versions` | 17 | map of `string` to `string` | Versions of the apps the manifest lists (for example {"firefox": "130.0"}). | | `tool_versions` | 18 | map of `string` to `string` | Versions of the tools the manifest lists (dev tiers, for example {"node": "v22.20.0"}). | | `simulator_runtimes` | 19 | repeated `string` | Available simulator runtimes, sorted (for example "iOS 26.0"). Empty unless the manifest claims some. | ### DiagnoseEffects Which checks may act on the guest. | Value | # | Description | | --- | --- | --- | | `DIAGNOSE_EFFECTS_UNSPECIFIED` | 0 | Not set. Same as `DIAGNOSE_EFFECTS_NONE`. | | `DIAGNOSE_EFFECTS_NONE` | 1 | Read-only checks. Input, window and fixture checks are skipped. | | `DIAGNOSE_EFFECTS_VIRTUAL_ONLY` | 2 | Effectful checks run, but only against fixture windows the doctor itself launched on the guest's virtual display. Refused on bare hosts. | ### CheckStatus Outcome of one check. | Value | # | Description | | --- | --- | --- | | `CHECK_STATUS_UNSPECIFIED` | 0 | Not reported. | | `CHECK_STATUS_PASS` | 1 | Works as claimed. | | `CHECK_STATUS_WARN` | 2 | Works with a caveat, or something unclaimed is off. | | `CHECK_STATUS_FAIL` | 3 | Broken. | | `CHECK_STATUS_SKIP` | 4 | Not run; `message` says why. | ### CheckSeverity How much a check matters for this image, derived from the manifest. | Value | # | Description | | --- | --- | --- | | `CHECK_SEVERITY_UNSPECIFIED` | 0 | Not reported. | | `CHECK_SEVERITY_REQUIRED` | 1 | The image claims it: a failure fails the report. | | `CHECK_SEVERITY_RECOMMENDED` | 2 | Optional in the manifest: a failure is reported as a warning. | | `CHECK_SEVERITY_INFO` | 3 | Not claimed: a failure is reported as a warning. | --- # Shared types and errors The cua.env.v1 types every spacesd service shares: ErrorInfo, principals and geometry. Errors (`google.rpc.Status` carrying `ErrorInfo`), principals and geometry, used by every `cua.env.v1` service. Source: `libs/cua/proto/cua/env/v1/common.proto`. ## Types in `cua/env/v1/common.proto` ### Point A point in a two-dimensional coordinate space. Which space is always stated by the enclosing message (see `CoordinateSpace`). | Field | # | Type | Description | | --- | --- | --- | --- | | `x` | 1 | `double` | Horizontal coordinate, growing to the right. | | `y` | 2 | `double` | Vertical coordinate, growing downward. | ### Rect An axis-aligned rectangle in a coordinate space stated by the enclosing message. | Field | # | Type | Description | | --- | --- | --- | --- | | `x` | 1 | `double` | Left edge. | | `y` | 2 | `double` | Top edge. | | `width` | 3 | `double` | Width. Never negative. | | `height` | 4 | `double` | Height. Never negative. | ### PixelSize A size in device (physical) pixels. | Field | # | Type | Description | | --- | --- | --- | --- | | `width` | 1 | `uint32` | Width in pixels. | | `height` | 2 | `uint32` | Height in pixels. | ### WindowRef Opaque, server-issued reference to a window. Native window identifiers (CGWindowID, HWND, X11 Window, portal tokens) are never exposed. `id` stays stable for the life of the native window. `epoch` changes whenever the server recycles an `id` or the window's identity is no longer trustworthy (for example, the owning process restarted). A request with a stale epoch fails with FAILED_PRECONDITION and `ERROR_REASON_STALE_HANDLE`. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | Opaque window identifier. Compare only for equality. | | `epoch` | 2 | `uint64` | Identity generation of `id`. | ### Display A display (monitor) attached to the guest desktop. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | Opaque, stable display identifier. The alias "primary" is accepted wherever a display id is expected. | | `name` | 2 | `string` | Human-readable name, for example "Built-in Retina Display" or "XVFB-0". | | `primary` | 3 | `bool` | True for the primary display. | | `bounds` | 4 | [`Rect`](#rect) | Display bounds in global logical points (`COORDINATE_SPACE_SCREEN`). | | `native_size` | 5 | [`PixelSize`](#pixelsize) | Size of the display's framebuffer in physical pixels. | | `scale_factor` | 6 | `double` | Physical pixels per logical point (for example 2.0 on Retina, 1.0 on most Linux framebuffers). | | `refresh_rate_hz` | 7 | `uint32` | Nominal refresh rate in hertz, 0 when unknown (for example Xvfb). | | `rotation_degrees` | 8 | `uint32` | Clockwise rotation in degrees: 0, 90, 180 or 270. | ### DeliveryReport What the server actually did for an input or action request. | Field | # | Type | Description | | --- | --- | --- | --- | | `delivery` | 1 | [`Delivery`](#delivery) | The delivery mode that was used. Never `DELIVERY_AUTO` or unspecified. | | `focus_changed` | 2 | `bool` | True if the focused window or frontmost app changed as a result. | | `pointer_moved` | 3 | `bool` | True if the user-visible pointer moved as a result. | | `detail` | 4 | `string` | Free-form, human-readable note (for example which platform path was used). Not for programmatic use. | ### Principal Who is making a call. Sent as serialized bytes in the `x-cua-principal-bin` metadata entry. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | Stable identifier chosen by the client (for example a user id or an agent run id). | | `display_name` | 2 | `string` | Name shown to other participants. | | `color` | 3 | `string` | Preferred cursor color as `#RRGGBB`. The server may substitute a different color to keep participants distinguishable. | | `kind` | 4 | [`PrincipalKind`](#principalkind) | Whether the principal is a human or an automated agent. | ### ErrorInfo Machine-readable error detail, packed as `google.protobuf.Any` inside the `google.rpc.Status` in the `grpc-status-details-bin` trailer. | Field | # | Type | Description | | --- | --- | --- | --- | | `reason` | 1 | [`ErrorReason`](#errorreason) | The specific reason. More precise than the gRPC status code. | | `message` | 2 | `string` | Human-readable description. Not for programmatic use. | | `feature` | 3 | `string` | Capability feature name involved, when `reason` is `ERROR_REASON_FEATURE_UNSUPPORTED` (for example "a11y"). | | `metadata` | 4 | map of `string` to `string` | Structured context. Well-known keys: "expected_sequence", "expected_offset", "path", "pid", "tag", "retry_after_ms". | ### KeepAlive Periodic empty message sent on long-lived server streams so proxies (the Fleet gateway, load balancers) do not time out an idle stream. Clients ignore it apart from resetting their own liveness timers. No fields. ### CoordinateSpace The coordinate space a `Point` or `Rect` is expressed in. | Value | # | Description | | --- | --- | --- | | `COORDINATE_SPACE_UNSPECIFIED` | 0 | Not set. Servers treat it as `COORDINATE_SPACE_SCREEN`. | | `COORDINATE_SPACE_SCREEN` | 1 | Global desktop space in logical points, origin at the top-left of the primary display. This is the space `Display.bounds` uses. | | `COORDINATE_SPACE_WINDOW` | 2 | Logical points relative to the top-left corner of the target window's frame. Requires a target window on the request. | | `COORDINATE_SPACE_SCREENSHOT` | 3 | Pixels of a screenshot previously returned by `ComputerService.Screenshot`, identified by `screenshot_id` on the request. The server performs the one conversion to native coordinates and fails with `ERROR_REASON_STALE_GEOMETRY` if the display or window geometry changed since the screenshot was taken. | | `COORDINATE_SPACE_NORMALIZED` | 4 | Normalized coordinates in [0, 1] relative to the target window, or the target display when no window is given. | ### Delivery How synthetic input or an action is delivered to the guest. | Value | # | Description | | --- | --- | --- | | `DELIVERY_UNSPECIFIED` | 0 | Not set. Servers treat it as `DELIVERY_AUTO`. | | `DELIVERY_AUTO` | 1 | Prefer background delivery and fall back to foreground delivery when the platform or target cannot receive background input. | | `DELIVERY_BACKGROUND` | 2 | Deliver without moving the user-visible pointer, changing the focused window or activating the target app. Fails with `ERROR_REASON_WOULD_REQUIRE_ACTIVATION` rather than falling back. | | `DELIVERY_FOREGROUND` | 3 | Activate the target (if any) and deliver through the global input stream, moving the real pointer and focus. | ### PrincipalKind Kind of principal behind a call. | Value | # | Description | | --- | --- | --- | | `PRINCIPAL_KIND_UNSPECIFIED` | 0 | Not set. Servers treat it as `PRINCIPAL_KIND_HUMAN`. | | `PRINCIPAL_KIND_HUMAN` | 1 | A person driving a client UI. | | `PRINCIPAL_KIND_AGENT` | 2 | An automated agent. | ### ErrorReason Machine-readable error reasons shared by every service. | Value | # | Description | | --- | --- | --- | | `ERROR_REASON_UNSPECIFIED` | 0 | Not set. | | `ERROR_REASON_UNAUTHENTICATED` | 1 | Missing or wrong bearer token, or an expired ticket. | | `ERROR_REASON_FEATURE_UNSUPPORTED` | 2 | The operation needs a feature the guest does not support. See `ErrorInfo.feature` and `SystemService.GetCapabilities`. | | `ERROR_REASON_PERMISSION_DENIED` | 3 | The guest OS denied permission (for example macOS TCC, UAC, AT-SPI disabled). | | `ERROR_REASON_STALE_HANDLE` | 4 | A `WindowRef` epoch no longer matches. | | `ERROR_REASON_STALE_GEOMETRY` | 5 | Geometry changed since the referenced screenshot or media frame. | | `ERROR_REASON_STALE_SNAPSHOT` | 6 | An accessibility element's snapshot is no longer current. | | `ERROR_REASON_WOULD_REQUIRE_ACTIVATION` | 7 | `DELIVERY_BACKGROUND` was requested but only foreground delivery could reach the target. | | `ERROR_REASON_TARGET_UNAVAILABLE` | 8 | The target window, app, display or element is gone. | | `ERROR_REASON_DELIVERY_FAILED` | 9 | The platform accepted the request but native delivery failed. | | `ERROR_REASON_PROCESS_NOT_FOUND` | 10 | No process matches the given pid or tag. | | `ERROR_REASON_PATH_NOT_FOUND` | 11 | The path does not exist. | | `ERROR_REASON_PATH_EXISTS` | 12 | The path already exists and overwrite was not requested. | | `ERROR_REASON_NOT_A_DIRECTORY` | 13 | A directory was required. | | `ERROR_REASON_IS_A_DIRECTORY` | 14 | A regular file was required. | | `ERROR_REASON_DISK_FULL` | 15 | The guest disk is full (HTTP 507 on the `/files` route). | | `ERROR_REASON_CHECKSUM_MISMATCH` | 16 | Received content does not match the declared SHA-256. | | `ERROR_REASON_SESSION_NOT_FOUND` | 17 | Unknown or expired upload, transfer, watcher, media session or forward. | | `ERROR_REASON_OFFSET_MISMATCH` | 18 | A chunk arrived at the wrong offset. `metadata["expected_offset"]` holds the offset the server expects next. | | `ERROR_REASON_SEQUENCE_GAP` | 19 | A sequenced input chunk skipped ahead. `metadata["expected_sequence"]` holds the sequence the server expects next. | | `ERROR_REASON_RATE_LIMITED` | 20 | Too many requests. `metadata["retry_after_ms"]` may hint a delay. | | `ERROR_REASON_LIMIT_EXCEEDED` | 21 | A request limit was exceeded (chunk size, message size, tree depth). | | `ERROR_REASON_NOT_INITIALIZED` | 22 | `SystemService.Init` has not configured a required default yet. | | `ERROR_REASON_INTERNAL` | 23 | An internal server error. Report a bug. | --- # Processes cua.env.v1 ProcessService: start, attach to, feed and signal guest processes. Detached, reattachable guest processes with PTY support and bounded scrollback. Source: `libs/cua/proto/cua/env/v1/process.proto`. ## ProcessService `cua.env.v1.ProcessService` Runs and supervises guest processes. Processes are detached from the RPC that started them: a client disconnect does not kill the process unless `StartProcessRequest.kill_on_disconnect` is set. Output goes to a bounded scrollback ring so a client can reattach with `ConnectProcess` (by pid or tag) and replay recent output. After a process exits, its exit status and scrollback stay in a retention cache (at least 10 minutes and the most recent 256 processes) so a late `ConnectProcess` still observes the `ProcessEnd`. Output offsets: every byte a process writes to stdout, stderr or its PTY is numbered in one combined, monotonically increasing byte space starting at 0. `ProcessData.offset` is the offset of the chunk's first byte, which lets a reconnecting client drop output it has already seen. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`StartProcess`](#processservicestartprocess) | [`StartProcessRequest`](#startprocessrequest) | [`StartProcessResponse`](#startprocessresponse) | server stream | | [`ConnectProcess`](#processserviceconnectprocess) | [`ConnectProcessRequest`](#connectprocessrequest) | [`ConnectProcessResponse`](#connectprocessresponse) | server stream | | [`ListProcesses`](#processservicelistprocesses) | [`ListProcessesRequest`](#listprocessesrequest) | [`ListProcessesResponse`](#listprocessesresponse) | unary | | [`SendInput`](#processservicesendinput) | [`SendInputRequest`](#sendinputrequest) | [`SendInputResponse`](#sendinputresponse) | unary | | [`StreamInput`](#processservicestreaminput) | [`StreamInputRequest`](#streaminputrequest) | [`StreamInputResponse`](#streaminputresponse) | client stream | | [`SignalProcess`](#processservicesignalprocess) | [`SignalProcessRequest`](#signalprocessrequest) | [`SignalProcessResponse`](#signalprocessresponse) | unary | | [`CloseStdin`](#processserviceclosestdin) | [`CloseStdinRequest`](#closestdinrequest) | [`CloseStdinResponse`](#closestdinresponse) | unary | | [`ResizePty`](#processserviceresizepty) | [`ResizePtyRequest`](#resizeptyrequest) | [`ResizePtyResponse`](#resizeptyresponse) | unary | ### ProcessService.StartProcess `/cua.env.v1.ProcessService/StartProcess`, server stream: [`StartProcessRequest`](#startprocessrequest) to [`StartProcessResponse`](#startprocessresponse). Starts a process and streams its lifecycle and output. The first event is always `ProcessStart`, the last is `ProcessEnd` (unless the client disconnects first). Cancelling this RPC detaches; it does not signal the process unless `kill_on_disconnect` is set. ### ProcessService.ConnectProcess `/cua.env.v1.ProcessService/ConnectProcess`, server stream: [`ConnectProcessRequest`](#connectprocessrequest) to [`ConnectProcessResponse`](#connectprocessresponse). Attaches to a running or recently exited process. The stream begins with `ProcessStart`, then up to `replay_bytes` of scrollback, then live output, then `ProcessEnd`. ### ProcessService.ListProcesses `/cua.env.v1.ProcessService/ListProcesses`, unary: [`ListProcessesRequest`](#listprocessesrequest) to [`ListProcessesResponse`](#listprocessesresponse). Lists managed processes. ### ProcessService.SendInput `/cua.env.v1.ProcessService/SendInput`, unary: [`SendInputRequest`](#sendinputrequest) to [`SendInputResponse`](#sendinputresponse). Writes one chunk to a process's stdin or PTY. The unary, gRPC-Web-safe equivalent of `StreamInput`. Chunks may carry sequence numbers for exactly-once, in-order delivery across retries. ### ProcessService.StreamInput `/cua.env.v1.ProcessService/StreamInput`, client stream: [`StreamInputRequest`](#streaminputrequest) to [`StreamInputResponse`](#streaminputresponse). Streams stdin or PTY input. Native gRPC only; gRPC-Web clients use `SendInput`. The first message must set `process`. **Note** gRPC-Web cannot carry client streams. Over gRPC-Web, call the unary [`SendInput`](#processservicesendinput) instead (`/cua.env.v1.ProcessService/SendInput`). ### ProcessService.SignalProcess `/cua.env.v1.ProcessService/SignalProcess`, unary: [`SignalProcessRequest`](#signalprocessrequest) to [`SignalProcessResponse`](#signalprocessresponse). Sends a signal to a process (or its process group). ### ProcessService.CloseStdin `/cua.env.v1.ProcessService/CloseStdin`, unary: [`CloseStdinRequest`](#closestdinrequest) to [`CloseStdinResponse`](#closestdinresponse). Closes a process's stdin (EOF). For PTY processes, sends the terminal's EOF character instead. ### ProcessService.ResizePty `/cua.env.v1.ProcessService/ResizePty`, unary: [`ResizePtyRequest`](#resizeptyrequest) to [`ResizePtyResponse`](#resizeptyresponse). Resizes a PTY process's terminal (delivers SIGWINCH on Unix). ## Types in `cua/env/v1/process.proto` ### ProcessConfig What to run. | Field | # | Type | Description | | --- | --- | --- | --- | | `command` | 1 | `string` | Executable name or path. Resolved against the process's PATH. No shell is involved; to run a shell command line, pass the shell explicitly (for example `command: "/bin/bash", args: ["-lc", "echo hi"]`). | | `args` | 2 | repeated `string` | Arguments, not including argv[0]. | | `env` | 3 | map of `string` to `string` | Environment variables layered over the user's login environment and the `InitRequest.env` defaults. | | `cwd` | 4 | `string` | Working directory. Empty means the `Init` default workdir, else the user's home directory. | | `user` | 5 | `string` | OS user to run as. Empty means the `Init` default user. Requires the driver to run with sufficient privilege to switch users. | | `timeout` | 6 | `google.protobuf.Duration` | Wall-clock limit. When it elapses the process receives SIGTERM, then SIGKILL after 5 seconds, and `ProcessEnd.timed_out` is set. Unset means no limit. | ### PtyConfig Pseudo-terminal settings. Present means "run under a PTY". | Field | # | Type | Description | | --- | --- | --- | --- | | `size` | 1 | [`PtySize`](#ptysize) | Initial terminal size. | | `term` | 2 | `string` | Value of TERM. Empty means "xterm-256color". | ### PtySize Terminal size. | Field | # | Type | Description | | --- | --- | --- | --- | | `cols` | 1 | `uint32` | Columns. Must be at least 1. | | `rows` | 2 | `uint32` | Rows. Must be at least 1. | | `pixel_width` | 3 | `uint32` | Width in pixels, 0 when unknown. | | `pixel_height` | 4 | `uint32` | Height in pixels, 0 when unknown. | ### ProcessSelector Identifies a managed process. | Field | # | Type | Description | | --- | --- | --- | --- | | `pid` | 1 | `uint32` (oneof `selector`) | Guest process id. Pids are reused by the OS; prefer `tag` for long-lived references. | | `tag` | 2 | `string` (oneof `selector`) | Client-assigned tag from `StartProcessRequest.tag`. | ### StartProcessRequest Request for `ProcessService.StartProcess`. | Field | # | Type | Description | | --- | --- | --- | --- | | `config` | 1 | [`ProcessConfig`](#processconfig) | What to run. | | `pty` | 2 | [`PtyConfig`](#ptyconfig) | Run under a PTY when set. PTY output arrives as `ProcessData.pty` and stdout and stderr are merged into it. | | `tag` | 3 | `string` | Unique, client-assigned name for reattaching. Starting a process with a tag that belongs to a running process fails with ALREADY_EXISTS. Tags of exited processes may be reused; the old entry leaves the retention cache. | | `stdin` | 4 | `bool` | Keep stdin open for `SendInput` / `StreamInput`. When false, stdin is connected to /dev/null (or NUL). | | `keepalive_interval` | 5 | `google.protobuf.Duration` | Interval between `KeepAlive` events on this stream. Unset means 30 seconds. Keep it below the idle timeout of any proxy in the path. | | `scrollback_bytes` | 6 | `uint64` | Size of this process's scrollback ring in bytes. 0 means the server default (`Limits.default_scrollback_bytes`). | | `kill_on_disconnect` | 7 | `bool` | Kill the process (SIGTERM, then SIGKILL) when this RPC ends before the process does. Default false: processes are detached. | ### StartProcessResponse One message of the `ProcessService.StartProcess` stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `event` | 1 | [`ProcessEvent`](#processevent) | The event. | ### ConnectProcessRequest Request for `ProcessService.ConnectProcess`. | Field | # | Type | Description | | --- | --- | --- | --- | | `process` | 1 | [`ProcessSelector`](#processselector) | Which process. | | `replay_bytes` | 2 | `uint64` | How many bytes of scrollback to replay before live output. 0 replays nothing. Values larger than the retained scrollback replay everything retained. Use `replay_from_offset` instead to resume precisely. | | `replay_from_offset` | 3 | optional `uint64` | When set, replay retained output starting at this combined output offset (typically the last offset the client saw plus its length; 0 replays everything retained). Takes precedence over `replay_bytes`. If the offset has already been evicted from the ring, replay starts at the oldest retained byte and the first `ProcessData.offset` reveals the gap. | | `keepalive_interval` | 4 | `google.protobuf.Duration` | Interval between `KeepAlive` events. Unset means 30 seconds. | ### ConnectProcessResponse One message of the `ProcessService.ConnectProcess` stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `event` | 1 | [`ProcessEvent`](#processevent) | The event. | ### ProcessEvent A process lifecycle or output event. | Field | # | Type | Description | | --- | --- | --- | --- | | `start` | 1 | [`ProcessStart`](#processstart) (oneof `event`) | The process started (or, on `ConnectProcess`, is known). | | `data` | 2 | [`ProcessData`](#processdata) (oneof `event`) | Output bytes. | | `end` | 3 | [`ProcessEnd`](#processend) (oneof `event`) | The process ended. | | `keepalive` | 4 | [`KeepAlive`](</docs/cua-sdk/reference/protocol/env-common#keepalive>) (oneof `event`) | Idle stream heartbeat. | ### ProcessStart Sent first on every process stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `pid` | 1 | `uint32` | Guest process id. | | `tag` | 2 | `string` | Tag, if any. | | `started_at` | 3 | `google.protobuf.Timestamp` | When the process started. | | `scrollback_start_offset` | 4 | `uint64` | Combined output offset of the oldest byte still in the scrollback ring. | | `output_end_offset` | 5 | `uint64` | Combined output offset one past the newest byte produced so far. | ### ProcessData A chunk of process output. | Field | # | Type | Description | | --- | --- | --- | --- | | `offset` | 1 | `uint64` | Combined output offset of the first byte in this chunk. | | `stdout` | 2 | `bytes` (oneof `output`) | Bytes written to stdout. | | `stderr` | 3 | `bytes` (oneof `output`) | Bytes written to stderr. | | `pty` | 4 | `bytes` (oneof `output`) | Bytes written to the PTY (stdout and stderr merged). | ### ProcessEnd Sent last on every process stream once the process has exited. | Field | # | Type | Description | | --- | --- | --- | --- | | `exit_code` | 1 | optional `int32` | Exit code, when the process exited normally. | | `signal` | 2 | [`Signal`](#signal) | Terminating signal, when the process was killed by a signal. | | `timed_out` | 3 | `bool` | True if the process was stopped because `ProcessConfig.timeout` elapsed. | | `error` | 4 | `string` | Set when the process could not be started or supervised (for example "executable not found"). `exit_code` and `signal` are unset then. | | `ended_at` | 5 | `google.protobuf.Timestamp` | When the process ended. | ### ListProcessesRequest Request for `ProcessService.ListProcesses`. | Field | # | Type | Description | | --- | --- | --- | --- | | `include_exited` | 1 | `bool` | Also list exited processes still in the retention cache. | | `tag_prefix` | 2 | `string` | Only list processes whose tag starts with this prefix. | ### ListProcessesResponse Response for `ProcessService.ListProcesses`. | Field | # | Type | Description | | --- | --- | --- | --- | | `processes` | 1 | repeated [`ProcessInfo`](#processinfo) | Matching processes, newest first. | ### ProcessInfo A managed process. | Field | # | Type | Description | | --- | --- | --- | --- | | `pid` | 1 | `uint32` | Guest process id. | | `tag` | 2 | `string` | Tag, if any. | | `config` | 3 | [`ProcessConfig`](#processconfig) | How it was started. Environment values are redacted to "" when the key looks secret (contains TOKEN, SECRET, KEY or PASSWORD). | | `pty` | 4 | `bool` | True if it runs under a PTY. | | `state` | 5 | [`ProcessState`](#processstate) | Current state. | | `end` | 6 | [`ProcessEnd`](#processend) | Exit details, once exited. | | `started_at` | 7 | `google.protobuf.Timestamp` | When it started. | | `scrollback_start_offset` | 8 | `uint64` | Combined output offset of the oldest retained byte. | | `output_end_offset` | 9 | `uint64` | Combined output offset one past the newest byte. | | `attached_clients` | 10 | `uint32` | Number of streams currently attached (StartProcess + ConnectProcess). | ### ProcessInput Bytes destined for a process. | Field | # | Type | Description | | --- | --- | --- | --- | | `stdin` | 1 | `bytes` (oneof `input`) | Bytes for stdin. Requires `StartProcessRequest.stdin`. | | `pty` | 2 | `bytes` (oneof `input`) | Bytes for the PTY (keystrokes). Requires a PTY process. | ### SendInputRequest Request for `ProcessService.SendInput`. | Field | # | Type | Description | | --- | --- | --- | --- | | `process` | 1 | [`ProcessSelector`](#processselector) | Which process. | | `input` | 2 | [`ProcessInput`](#processinput) | The bytes. At most `Limits.max_chunk_bytes`. | | `sequence` | 3 | `uint64` | Optional ordering and de-duplication. 0 means "unsequenced": the chunk is applied in arrival order with no retry protection. When non-zero, sequences are tracked per (process, `writer_id`): the first sequenced chunk may start at any value, each later chunk must be exactly one greater than the last applied one, a chunk at or below the last applied sequence is acknowledged as a duplicate without being written again, and a gap fails with FAILED_PRECONDITION / `ERROR_REASON_SEQUENCE_GAP`. The SDK owns this counter; applications never set it. | | `writer_id` | 4 | `string` | Distinguishes independent sequenced writers to the same process. Empty is a valid writer id. | ### SendInputResponse Response for `ProcessService.SendInput`. | Field | # | Type | Description | | --- | --- | --- | --- | | `applied_sequence` | 1 | `uint64` | Highest sequence applied for this writer (0 for unsequenced input). | | `duplicate` | 2 | `bool` | True if this chunk was a retry of an already-applied sequence and was not written again. | ### StreamInputRequest One message of the `ProcessService.StreamInput` client stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `process` | 1 | [`ProcessSelector`](#processselector) (oneof `message`) | First message only: which process to write to. | | `input` | 2 | [`ProcessInput`](#processinput) (oneof `message`) | Subsequent messages: bytes to write, in order. | | `keepalive` | 3 | [`KeepAlive`](</docs/cua-sdk/reference/protocol/env-common#keepalive>) (oneof `message`) | Heartbeat from the client for long idle input streams. | ### StreamInputResponse Response for `ProcessService.StreamInput`, sent when the client half-closes. | Field | # | Type | Description | | --- | --- | --- | --- | | `bytes_written` | 1 | `uint64` | Total bytes written to the process. | ### SignalProcessRequest Request for `ProcessService.SignalProcess`. | Field | # | Type | Description | | --- | --- | --- | --- | | `process` | 1 | [`ProcessSelector`](#processselector) | Which process. | | `signal` | 2 | [`Signal`](#signal) | Which signal. | | `process_group` | 3 | `bool` | Signal the whole process group instead of the leader only. | ### SignalProcessResponse Response for `ProcessService.SignalProcess`. No fields. ### CloseStdinRequest Request for `ProcessService.CloseStdin`. | Field | # | Type | Description | | --- | --- | --- | --- | | `process` | 1 | [`ProcessSelector`](#processselector) | Which process. | ### CloseStdinResponse Response for `ProcessService.CloseStdin`. No fields. ### ResizePtyRequest Request for `ProcessService.ResizePty`. | Field | # | Type | Description | | --- | --- | --- | --- | | `process` | 1 | [`ProcessSelector`](#processselector) | Which process. Must run under a PTY. | | `size` | 2 | [`PtySize`](#ptysize) | New terminal size. | ### ResizePtyResponse Response for `ProcessService.ResizePty`. No fields. ### ProcessState Lifecycle state of a managed process. | Value | # | Description | | --- | --- | --- | | `PROCESS_STATE_UNSPECIFIED` | 0 | Not reported. | | `PROCESS_STATE_RUNNING` | 1 | Running (or stopped by SIGSTOP). | | `PROCESS_STATE_EXITED` | 2 | Exited; details in `ProcessInfo.end`. | ### Signal POSIX signals. On Windows only `SIGNAL_KILL` and `SIGNAL_TERM` (mapped to a console close / terminate) and `SIGNAL_INT` (CTRL_C_EVENT) are supported; others fail with `ERROR_REASON_FEATURE_UNSUPPORTED`. Numbers follow Linux x86-64 for readability only; the server maps each value to the guest's native signal number. | Value | # | Description | | --- | --- | --- | | `SIGNAL_UNSPECIFIED` | 0 | Not set. | | `SIGNAL_HUP` | 1 | SIGHUP. | | `SIGNAL_INT` | 2 | SIGINT. | | `SIGNAL_QUIT` | 3 | SIGQUIT. | | `SIGNAL_KILL` | 9 | SIGKILL. | | `SIGNAL_USR1` | 10 | SIGUSR1. | | `SIGNAL_USR2` | 12 | SIGUSR2. | | `SIGNAL_TERM` | 15 | SIGTERM. | | `SIGNAL_CONT` | 18 | SIGCONT. | | `SIGNAL_STOP` | 19 | SIGSTOP. | | `SIGNAL_TSTP` | 20 | SIGTSTP. | | `SIGNAL_WINCH` | 28 | SIGWINCH. | --- # Filesystem cua.env.v1 FilesystemService: stat, list, watch, read, chunked upload and signed URLs. File and directory operations, watchers, chunked transfers and signed HTTP URLs. Source: `libs/cua/proto/cua/env/v1/filesystem.proto`. ## FilesystemService `cua.env.v1.FilesystemService` Guest filesystem access. Paths are absolute, or relative to the default working directory set by `SystemService.Init`. A leading "~" expands to the acting user's home. Operations run as the `Init` default user. Large payloads: there is no total size cap. Reads stream in chunks (`ReadFile`). Writes use either the native-gRPC client stream (`WriteFile`) or the gRPC-Web-safe, resumable unary chunk protocol (`BeginUpload` / `UploadChunk` / `CommitUpload`). Browsers and `<img>` tags can use signed URLs on the plain-HTTP `/files` route instead (`CreateSignedUrl`). Writes are atomic: data goes to a temporary file in the destination directory and is renamed into place on success. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`Stat`](#filesystemservicestat) | [`StatRequest`](#statrequest) | [`StatResponse`](#statresponse) | unary | | [`ListDir`](#filesystemservicelistdir) | [`ListDirRequest`](#listdirrequest) | [`ListDirResponse`](#listdirresponse) | unary | | [`MakeDir`](#filesystemservicemakedir) | [`MakeDirRequest`](#makedirrequest) | [`MakeDirResponse`](#makedirresponse) | unary | | [`Move`](#filesystemservicemove) | [`MoveRequest`](#moverequest) | [`MoveResponse`](#moveresponse) | unary | | [`Remove`](#filesystemserviceremove) | [`RemoveRequest`](#removerequest) | [`RemoveResponse`](#removeresponse) | unary | | [`WatchDir`](#filesystemservicewatchdir) | [`WatchDirRequest`](#watchdirrequest) | [`WatchDirResponse`](#watchdirresponse) | server stream | | [`CreateWatcher`](#filesystemservicecreatewatcher) | [`CreateWatcherRequest`](#createwatcherrequest) | [`CreateWatcherResponse`](#createwatcherresponse) | unary | | [`GetWatcherEvents`](#filesystemservicegetwatcherevents) | [`GetWatcherEventsRequest`](#getwatchereventsrequest) | [`GetWatcherEventsResponse`](#getwatchereventsresponse) | unary | | [`RemoveWatcher`](#filesystemserviceremovewatcher) | [`RemoveWatcherRequest`](#removewatcherrequest) | [`RemoveWatcherResponse`](#removewatcherresponse) | unary | | [`ReadFile`](#filesystemservicereadfile) | [`ReadFileRequest`](#readfilerequest) | [`ReadFileResponse`](#readfileresponse) | server stream | | [`WriteFile`](#filesystemservicewritefile) | [`WriteFileRequest`](#writefilerequest) | [`WriteFileResponse`](#writefileresponse) | client stream | | [`BeginUpload`](#filesystemservicebeginupload) | [`BeginUploadRequest`](#beginuploadrequest) | [`BeginUploadResponse`](#beginuploadresponse) | unary | | [`UploadChunk`](#filesystemserviceuploadchunk) | [`UploadChunkRequest`](#uploadchunkrequest) | [`UploadChunkResponse`](#uploadchunkresponse) | unary | | [`CommitUpload`](#filesystemservicecommitupload) | [`CommitUploadRequest`](#commituploadrequest) | [`CommitUploadResponse`](#commituploadresponse) | unary | | [`AbortUpload`](#filesystemserviceabortupload) | [`AbortUploadRequest`](#abortuploadrequest) | [`AbortUploadResponse`](#abortuploadresponse) | unary | | [`CreateSignedUrl`](#filesystemservicecreatesignedurl) | [`CreateSignedUrlRequest`](#createsignedurlrequest) | [`CreateSignedUrlResponse`](#createsignedurlresponse) | unary | ### FilesystemService.Stat `/cua.env.v1.FilesystemService/Stat`, unary: [`StatRequest`](#statrequest) to [`StatResponse`](#statresponse). Returns metadata for one path. ### FilesystemService.ListDir `/cua.env.v1.FilesystemService/ListDir`, unary: [`ListDirRequest`](#listdirrequest) to [`ListDirResponse`](#listdirresponse). Lists a directory, optionally recursively. ### FilesystemService.MakeDir `/cua.env.v1.FilesystemService/MakeDir`, unary: [`MakeDirRequest`](#makedirrequest) to [`MakeDirResponse`](#makedirresponse). Creates a directory. ### FilesystemService.Move `/cua.env.v1.FilesystemService/Move`, unary: [`MoveRequest`](#moverequest) to [`MoveResponse`](#moveresponse). Moves or renames a file or directory. ### FilesystemService.Remove `/cua.env.v1.FilesystemService/Remove`, unary: [`RemoveRequest`](#removerequest) to [`RemoveResponse`](#removeresponse). Removes a file or directory. ### FilesystemService.WatchDir `/cua.env.v1.FilesystemService/WatchDir`, server stream: [`WatchDirRequest`](#watchdirrequest) to [`WatchDirResponse`](#watchdirresponse). Streams change events under a directory. The first message is `WatchStarted`, sent once the watch is armed, so changes made after it arrives are guaranteed to be observed. ### FilesystemService.CreateWatcher `/cua.env.v1.FilesystemService/CreateWatcher`, unary: [`CreateWatcherRequest`](#createwatcherrequest) to [`CreateWatcherResponse`](#createwatcherresponse). Polling alternative to `WatchDir` for clients that cannot hold a stream: creates a server-side watcher that buffers events. ### FilesystemService.GetWatcherEvents `/cua.env.v1.FilesystemService/GetWatcherEvents`, unary: [`GetWatcherEventsRequest`](#getwatchereventsrequest) to [`GetWatcherEventsResponse`](#getwatchereventsresponse). Drains buffered events from a watcher. ### FilesystemService.RemoveWatcher `/cua.env.v1.FilesystemService/RemoveWatcher`, unary: [`RemoveWatcherRequest`](#removewatcherrequest) to [`RemoveWatcherResponse`](#removewatcherresponse). Deletes a watcher. Watchers not polled for 5 minutes are deleted automatically. ### FilesystemService.ReadFile `/cua.env.v1.FilesystemService/ReadFile`, server stream: [`ReadFileRequest`](#readfilerequest) to [`ReadFileResponse`](#readfileresponse). Streams a file (or a byte range of it) in chunks. ### FilesystemService.WriteFile `/cua.env.v1.FilesystemService/WriteFile`, client stream: [`WriteFileRequest`](#writefilerequest) to [`WriteFileResponse`](#writefileresponse). Writes a file from a client stream. Native gRPC only; gRPC-Web clients use `BeginUpload` / `UploadChunk` / `CommitUpload`. The first message must be a `WriteFileHeader`. **Note** gRPC-Web cannot carry client streams. Over gRPC-Web, call the unary [`UploadChunk`](#filesystemserviceuploadchunk) instead (`/cua.env.v1.FilesystemService/UploadChunk`). ### FilesystemService.BeginUpload `/cua.env.v1.FilesystemService/BeginUpload`, unary: [`BeginUploadRequest`](#beginuploadrequest) to [`BeginUploadResponse`](#beginuploadresponse). Starts, or resumes, a resumable chunked upload. ### FilesystemService.UploadChunk `/cua.env.v1.FilesystemService/UploadChunk`, unary: [`UploadChunkRequest`](#uploadchunkrequest) to [`UploadChunkResponse`](#uploadchunkresponse). Appends one chunk to an upload. ### FilesystemService.CommitUpload `/cua.env.v1.FilesystemService/CommitUpload`, unary: [`CommitUploadRequest`](#commituploadrequest) to [`CommitUploadResponse`](#commituploadresponse). Verifies and atomically publishes an upload at its destination path. ### FilesystemService.AbortUpload `/cua.env.v1.FilesystemService/AbortUpload`, unary: [`AbortUploadRequest`](#abortuploadrequest) to [`AbortUploadResponse`](#abortuploadresponse). Discards an upload and its partial data. ### FilesystemService.CreateSignedUrl `/cua.env.v1.FilesystemService/CreateSignedUrl`, unary: [`CreateSignedUrlRequest`](#createsignedurlrequest) to [`CreateSignedUrlResponse`](#createsignedurlresponse). Mints a time-limited URL on the plain-HTTP `/files` route for GET (download) or PUT (upload) of one path. ## Types in `cua/env/v1/filesystem.proto` ### EntryInfo Metadata for one filesystem entry. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Final path component. | | `path` | 2 | `string` | Absolute, normalized path. | | `type` | 3 | [`FileType`](#filetype) | Entry type. | | `size` | 4 | `uint64` | Size in bytes (0 for directories). | | `mode` | 5 | `uint32` | Unix permission bits (for example 0o644). Synthesized on Windows. | | `owner` | 6 | `string` | Owning user name. | | `group` | 7 | `string` | Owning group name (empty on Windows). | | `modified_at` | 8 | `google.protobuf.Timestamp` | Last modification time. | | `symlink_target` | 9 | `string` | Target of a symbolic link, when `type` is `FILE_TYPE_SYMLINK`. | ### StatRequest Request for `FilesystemService.Stat`. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Path to inspect. | | `no_follow_symlinks` | 2 | `bool` | Report the link itself rather than its target. | ### StatResponse Response for `FilesystemService.Stat`. | Field | # | Type | Description | | --- | --- | --- | --- | | `entry` | 1 | [`EntryInfo`](#entryinfo) | The entry. | ### ListDirRequest Request for `FilesystemService.ListDir`. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Directory to list. | | `depth` | 2 | `uint32` | Recursion depth. 0 and 1 both list direct children only; 2 includes grandchildren, and so on. Symbolic links are never followed during recursion. | | `include_hidden` | 3 | `bool` | Include entries whose name starts with "." (and hidden entries on Windows). | | `page_size` | 4 | `uint32` | Maximum entries per page. 0 means 1000. At most 10000. | | `page_token` | 5 | `string` | Continuation token from a previous `ListDirResponse`. | ### ListDirResponse Response for `FilesystemService.ListDir`. | Field | # | Type | Description | | --- | --- | --- | --- | | `entries` | 1 | repeated [`EntryInfo`](#entryinfo) | Entries in depth-first order, directories before their contents. | | `next_page_token` | 2 | `string` | Token for the next page, empty on the last page. | ### MakeDirRequest Request for `FilesystemService.MakeDir`. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Directory to create. | | `parents` | 2 | `bool` | Create missing parents too. | | `mode` | 3 | `uint32` | Permission bits. 0 means 0o755. | ### MakeDirResponse Response for `FilesystemService.MakeDir`. | Field | # | Type | Description | | --- | --- | --- | --- | | `entry` | 1 | [`EntryInfo`](#entryinfo) | The directory. | | `created` | 2 | `bool` | False if it already existed (not an error). | ### MoveRequest Request for `FilesystemService.Move`. | Field | # | Type | Description | | --- | --- | --- | --- | | `source` | 1 | `string` | Existing path. | | `destination` | 2 | `string` | New path. | | `overwrite` | 3 | `bool` | Replace an existing destination. Without it an existing destination fails with ALREADY_EXISTS / `ERROR_REASON_PATH_EXISTS`. | | `create_parents` | 4 | `bool` | Create missing parents of `destination`. | ### MoveResponse Response for `FilesystemService.Move`. | Field | # | Type | Description | | --- | --- | --- | --- | | `entry` | 1 | [`EntryInfo`](#entryinfo) | The entry at its new path. | ### RemoveRequest Request for `FilesystemService.Remove`. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Path to remove. | | `recursive` | 2 | `bool` | Remove a non-empty directory and its contents. | | `missing_ok` | 3 | `bool` | Succeed when the path does not exist. | ### RemoveResponse Response for `FilesystemService.Remove`. No fields. ### FsEvent One filesystem change. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Absolute path of the affected entry. | | `type` | 2 | [`FsEventType`](#fseventtype) | What happened. | | `old_path` | 3 | `string` | Previous path for renames, when the platform reports it. | | `observed_at` | 4 | `google.protobuf.Timestamp` | When the server observed the change. | ### WatchDirRequest Request for `FilesystemService.WatchDir`. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Directory to watch. | | `recursive` | 2 | `bool` | Watch the whole subtree. | | `keepalive_interval` | 3 | `google.protobuf.Duration` | Interval between `keepalive` messages. Unset means 30 seconds. | ### WatchStarted Sent once when a watch is armed. No fields. ### WatchOverflow Sent when the kernel event queue overflowed and events were lost. Clients should rescan. No fields. ### WatchDirResponse One message of the `FilesystemService.WatchDir` stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `started` | 1 | [`WatchStarted`](#watchstarted) (oneof `message`) | First message: the watch is armed. | | `event` | 2 | [`FsEvent`](#fsevent) (oneof `message`) | A change. | | `overflow` | 3 | [`WatchOverflow`](#watchoverflow) (oneof `message`) | Events were dropped. | | `keepalive` | 4 | [`KeepAlive`](</docs/cua-sdk/reference/protocol/env-common#keepalive>) (oneof `message`) | Idle stream heartbeat. | ### CreateWatcherRequest Request for `FilesystemService.CreateWatcher`. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Directory to watch. | | `recursive` | 2 | `bool` | Watch the whole subtree. | ### CreateWatcherResponse Response for `FilesystemService.CreateWatcher`. The watcher is armed when this returns. | Field | # | Type | Description | | --- | --- | --- | --- | | `watcher_id` | 1 | `string` | Watcher id for `GetWatcherEvents` and `RemoveWatcher`. | ### GetWatcherEventsRequest Request for `FilesystemService.GetWatcherEvents`. | Field | # | Type | Description | | --- | --- | --- | --- | | `watcher_id` | 1 | `string` | Which watcher. | | `max_events` | 2 | `uint32` | Maximum events to return. 0 means all buffered. | ### GetWatcherEventsResponse Response for `FilesystemService.GetWatcherEvents`. | Field | # | Type | Description | | --- | --- | --- | --- | | `events` | 1 | repeated [`FsEvent`](#fsevent) | Buffered events, oldest first. Returned events are removed from the buffer. | | `overflowed` | 2 | `bool` | True if the watcher's buffer (10000 events) overflowed since the last poll and events were dropped. Clients should rescan. | ### RemoveWatcherRequest Request for `FilesystemService.RemoveWatcher`. | Field | # | Type | Description | | --- | --- | --- | --- | | `watcher_id` | 1 | `string` | Which watcher. | ### RemoveWatcherResponse Response for `FilesystemService.RemoveWatcher`. No fields. ### ReadFileRequest Request for `FilesystemService.ReadFile`. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | File to read. | | `offset` | 2 | `uint64` | First byte to read. | | `length` | 3 | `uint64` | Maximum bytes to read. 0 means to end of file. | | `chunk_size` | 4 | `uint32` | Preferred chunk size. 0 means `Limits.preferred_chunk_bytes` (1 MiB). Clamped to `Limits.max_chunk_bytes`. | | `compute_sha256` | 5 | `bool` | Compute the SHA-256 of the bytes sent and report it in `ReadFileEnd`. | ### ReadFileResponse One message of the `FilesystemService.ReadFile` stream: `entry` first, then zero or more `chunk`s in order, then `end`. | Field | # | Type | Description | | --- | --- | --- | --- | | `entry` | 1 | [`EntryInfo`](#entryinfo) (oneof `message`) | File metadata, sent first. | | `chunk` | 2 | [`FileChunk`](#filechunk) (oneof `message`) | File content. | | `end` | 3 | [`ReadFileEnd`](#readfileend) (oneof `message`) | Sent last. | ### FileChunk A slice of file content. | Field | # | Type | Description | | --- | --- | --- | --- | | `offset` | 1 | `uint64` | Absolute file offset of `data[0]`. | | `data` | 2 | `bytes` | The bytes. | ### ReadFileEnd Final message of a `ReadFile` stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `bytes_read` | 1 | `uint64` | Bytes sent in chunks. | | `sha256` | 2 | `string` | Lowercase hex SHA-256 of the bytes sent, when requested. | ### WriteFileHeader Destination and options for a write. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Destination path. | | `mode` | 2 | [`WriteMode`](#writemode) | How to treat an existing file. | | `permissions` | 3 | `uint32` | Permission bits for a new file. 0 means 0o644. | | `create_parents` | 4 | `bool` | Create missing parent directories. | | `expected_size` | 5 | `uint64` | Expected total size in bytes, 0 if unknown. Checked on completion when non-zero. | | `expected_sha256` | 6 | `string` | Expected lowercase hex SHA-256 of the full content. Checked on completion when set; a mismatch discards the data and fails with `ERROR_REASON_CHECKSUM_MISMATCH`. | ### WriteFileRequest One message of the `FilesystemService.WriteFile` client stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `header` | 1 | [`WriteFileHeader`](#writefileheader) (oneof `message`) | First message only. | | `data` | 2 | `bytes` (oneof `message`) | File content, strictly sequential. Each chunk is at most `Limits.max_chunk_bytes`. | ### WriteFileResponse Response for `FilesystemService.WriteFile`. | Field | # | Type | Description | | --- | --- | --- | --- | | `entry` | 1 | [`EntryInfo`](#entryinfo) | The written file. | | `sha256` | 2 | `string` | Lowercase hex SHA-256 of the bytes received. | ### BeginUploadRequest Request for `FilesystemService.BeginUpload`. | Field | # | Type | Description | | --- | --- | --- | --- | | `header` | 1 | [`WriteFileHeader`](#writefileheader) | Destination and options. `expected_sha256` is strongly recommended. | | `upload_id` | 2 | `string` | Client-chosen id that makes this call idempotent and resumable. If an unexpired upload with this id exists for the same path, the call returns its current `received_bytes` instead of starting over. Empty lets the server generate an id (not resumable after the response is lost). | | `ttl` | 3 | `google.protobuf.Duration` | How long partial data is kept without progress. Unset means 1 hour. At most 24 hours. | ### BeginUploadResponse Response for `FilesystemService.BeginUpload`. | Field | # | Type | Description | | --- | --- | --- | --- | | `upload_id` | 1 | `string` | Upload id for later calls. | | `received_bytes` | 2 | `uint64` | Bytes already received. 0 for a new upload; the resume point otherwise. | | `max_chunk_bytes` | 3 | `uint32` | Largest chunk the server accepts. | | `expires_at` | 4 | `google.protobuf.Timestamp` | When the partial upload expires if no further chunks arrive. | ### UploadChunkRequest Request for `FilesystemService.UploadChunk`. | Field | # | Type | Description | | --- | --- | --- | --- | | `upload_id` | 1 | `string` | Which upload. | | `offset` | 2 | `uint64` | Offset of `data[0]`. Must equal the server's `received_bytes`; a chunk wholly below it is acknowledged as a duplicate, anything else fails with `ERROR_REASON_OFFSET_MISMATCH` and `metadata["expected_offset"]`. | | `data` | 3 | `bytes` | The bytes. At most `max_chunk_bytes`. | ### UploadChunkResponse Response for `FilesystemService.UploadChunk`. | Field | # | Type | Description | | --- | --- | --- | --- | | `received_bytes` | 1 | `uint64` | Total bytes received so far. | | `duplicate` | 2 | `bool` | True if the chunk was a retry and was not written again. | | `expires_at` | 3 | `google.protobuf.Timestamp` | New expiry of the partial upload. | ### CommitUploadRequest Request for `FilesystemService.CommitUpload`. | Field | # | Type | Description | | --- | --- | --- | --- | | `upload_id` | 1 | `string` | Which upload. | | `sha256` | 2 | `string` | Expected lowercase hex SHA-256, overriding the header's. Optional. | ### CommitUploadResponse Response for `FilesystemService.CommitUpload`. | Field | # | Type | Description | | --- | --- | --- | --- | | `entry` | 1 | [`EntryInfo`](#entryinfo) | The published file. | | `sha256` | 2 | `string` | Lowercase hex SHA-256 of the committed content. | ### AbortUploadRequest Request for `FilesystemService.AbortUpload`. | Field | # | Type | Description | | --- | --- | --- | --- | | `upload_id` | 1 | `string` | Which upload. | ### AbortUploadResponse Response for `FilesystemService.AbortUpload`. No fields. ### CreateSignedUrlRequest Request for `FilesystemService.CreateSignedUrl`. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Path the URL grants access to. Exactly this path; no traversal. | | `method` | 2 | [`SignedUrlMethod`](#signedurlmethod) | Allowed method. | | `ttl` | 3 | `google.protobuf.Duration` | Validity. Required; at most 24 hours. | | `content_type` | 4 | `string` | For GET: `Content-Type` to serve. Empty means guessed from the name. | | `download_name` | 5 | `string` | For GET: serve with `Content-Disposition: attachment` using this file name. Empty means inline. | ### CreateSignedUrlResponse Response for `FilesystemService.CreateSignedUrl`. | Field | # | Type | Description | | --- | --- | --- | --- | | `url_path` | 1 | `string` | Path and query to append to the spacesd's base URL, for example "/files?path=%2Fhome%2Fu%2Fa.png&method=GET&exp=1767225600&sig=…". The server does not know how it is addressed (direct, Fleet gateway, relay), so it never returns an absolute URL. The signature is HMAC-SHA256 over method, path and expiry with a key derived from the access token; rotating the token revokes every outstanding URL. | | `expires_at` | 2 | `google.protobuf.Timestamp` | When the URL stops working. | ### FileType Type of a filesystem entry. | Value | # | Description | | --- | --- | --- | | `FILE_TYPE_UNSPECIFIED` | 0 | Not reported. | | `FILE_TYPE_FILE` | 1 | Regular file. | | `FILE_TYPE_DIRECTORY` | 2 | Directory. | | `FILE_TYPE_SYMLINK` | 3 | Symbolic link (only when not following links). | | `FILE_TYPE_OTHER` | 4 | Anything else (socket, FIFO, device). | ### FsEventType Kind of filesystem change. | Value | # | Description | | --- | --- | --- | | `FS_EVENT_TYPE_UNSPECIFIED` | 0 | Not reported. | | `FS_EVENT_TYPE_CREATE` | 1 | Entry created. | | `FS_EVENT_TYPE_WRITE` | 2 | File content written. | | `FS_EVENT_TYPE_REMOVE` | 3 | Entry removed. | | `FS_EVENT_TYPE_RENAME` | 4 | Entry renamed or moved. `path` is the new path when known. | | `FS_EVENT_TYPE_CHMOD` | 5 | Permissions or ownership changed. | ### WriteMode How `WriteFile` and uploads treat an existing destination. | Value | # | Description | | --- | --- | --- | | `WRITE_MODE_UNSPECIFIED` | 0 | Not set. Treated as `WRITE_MODE_OVERWRITE`. | | `WRITE_MODE_OVERWRITE` | 1 | Replace any existing file atomically. | | `WRITE_MODE_CREATE_NEW` | 2 | Fail with `ERROR_REASON_PATH_EXISTS` if the destination exists. | | `WRITE_MODE_APPEND` | 3 | Append to the existing file (created if missing). Not atomic. | ### SignedUrlMethod HTTP method a signed URL is valid for. | Value | # | Description | | --- | --- | --- | | `SIGNED_URL_METHOD_UNSPECIFIED` | 0 | Not set. Rejected. | | `SIGNED_URL_METHOD_GET` | 1 | Download with `GET` (supports `Range`). | | `SIGNED_URL_METHOD_PUT` | 2 | Upload with `PUT` (the body replaces the file atomically). | --- # Computer and driver cua.env.v1 ComputerService and DriverService: screenshots, pointer, keyboard, clipboard, displays and Cua Driver tools. Desktop control. These services need the desktop provider; input goes through Cua Driver, which `DriverService` also exposes tool by tool. Source: `libs/cua/proto/cua/env/v1/computer.proto`, `libs/cua/proto/cua/env/v1/driver.proto`. ## ComputerService `cua.env.v1.ComputerService` Screen capture and synthetic input. Implemented by calling the cua-driver-core tool registry in-process (not over MCP). Every input RPC states its delivery mode explicitly and reports what actually happened in a `DeliveryReport`. Coordinates: screenshots report both their pixel size and the logical region they cover, so clients never guess a Retina scale. Input can be expressed in screen points, window points, normalized units or the pixels of a specific screenshot (see `CoordinateSpace`); the server performs the single conversion to native coordinates. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`Screenshot`](#computerservicescreenshot) | [`ScreenshotRequest`](#screenshotrequest) | [`ScreenshotResponse`](#screenshotresponse) | unary | | [`Pointer`](#computerservicepointer) | [`PointerRequest`](#pointerrequest) | [`PointerResponse`](#pointerresponse) | unary | | [`Keyboard`](#computerservicekeyboard) | [`KeyboardRequest`](#keyboardrequest) | [`KeyboardResponse`](#keyboardresponse) | unary | | [`GetClipboard`](#computerservicegetclipboard) | [`GetClipboardRequest`](#getclipboardrequest) | [`GetClipboardResponse`](#getclipboardresponse) | unary | | [`SetClipboard`](#computerservicesetclipboard) | [`SetClipboardRequest`](#setclipboardrequest) | [`SetClipboardResponse`](#setclipboardresponse) | unary | | [`GetCursorPosition`](#computerservicegetcursorposition) | [`GetCursorPositionRequest`](#getcursorpositionrequest) | [`GetCursorPositionResponse`](#getcursorpositionresponse) | unary | | [`ListDisplays`](#computerservicelistdisplays) | [`ListDisplaysRequest`](#listdisplaysrequest) | [`ListDisplaysResponse`](#listdisplaysresponse) | unary | ### ComputerService.Screenshot `/cua.env.v1.ComputerService/Screenshot`, unary: [`ScreenshotRequest`](#screenshotrequest) to [`ScreenshotResponse`](#screenshotresponse). Captures a display, a window or a region of either. ### ComputerService.Pointer `/cua.env.v1.ComputerService/Pointer`, unary: [`PointerRequest`](#pointerrequest) to [`PointerResponse`](#pointerresponse). Performs one pointer (mouse) operation. ### ComputerService.Keyboard `/cua.env.v1.ComputerService/Keyboard`, unary: [`KeyboardRequest`](#keyboardrequest) to [`KeyboardResponse`](#keyboardresponse). Performs one keyboard operation. ### ComputerService.GetClipboard `/cua.env.v1.ComputerService/GetClipboard`, unary: [`GetClipboardRequest`](#getclipboardrequest) to [`GetClipboardResponse`](#getclipboardresponse). Reads the clipboard. ### ComputerService.SetClipboard `/cua.env.v1.ComputerService/SetClipboard`, unary: [`SetClipboardRequest`](#setclipboardrequest) to [`SetClipboardResponse`](#setclipboardresponse). Replaces the clipboard. ### ComputerService.GetCursorPosition `/cua.env.v1.ComputerService/GetCursorPosition`, unary: [`GetCursorPositionRequest`](#getcursorpositionrequest) to [`GetCursorPositionResponse`](#getcursorpositionresponse). Returns the user-visible pointer position. ### ComputerService.ListDisplays `/cua.env.v1.ComputerService/ListDisplays`, unary: [`ListDisplaysRequest`](#listdisplaysrequest) to [`ListDisplaysResponse`](#listdisplaysresponse). Lists displays attached to the guest desktop. ## DriverService `cua.env.v1.DriverService` Passthrough to the full cua-driver tool registry. Every cua-driver tool is reachable here without new protos. The same registry is exposed to agents as streamable-HTTP MCP at `/mcp` on the same port. Arguments and structured results are JSON text so that tool schemas evolve with the cua-driver contract, not with this package. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`ListTools`](#driverservicelisttools) | [`ListToolsRequest`](#listtoolsrequest) | [`ListToolsResponse`](#listtoolsresponse) | unary | | [`CallTool`](#driverservicecalltool) | [`CallToolRequest`](#calltoolrequest) | [`CallToolResponse`](#calltoolresponse) | unary | ### DriverService.ListTools `/cua.env.v1.DriverService/ListTools`, unary: [`ListToolsRequest`](#listtoolsrequest) to [`ListToolsResponse`](#listtoolsresponse). Lists the tools the linked cua-driver registry exposes on this platform. ### DriverService.CallTool `/cua.env.v1.DriverService/CallTool`, unary: [`CallToolRequest`](#calltoolrequest) to [`CallToolResponse`](#calltoolresponse). Invokes one tool. ## Types in `cua/env/v1/computer.proto` ### ScreenshotRequest Request for `ComputerService.Screenshot`. | Field | # | Type | Description | | --- | --- | --- | --- | | `display_id` | 1 | `string` (oneof `source`) | A display id, or "primary". | | `window` | 2 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) (oneof `source`) | A single window. Captured without occluding windows where the platform supports it (see capability "window_stream"). | | `region` | 3 | [`Rect`](</docs/cua-sdk/reference/protocol/env-common#rect>) | Sub-region in logical points, relative to the source's top-left corner. Unset captures the whole source. | | `format` | 4 | [`ImageFormat`](#imageformat) | Output format. | | `quality` | 5 | `uint32` | Quality 1 to 100 for lossy formats. 0 means 80. | | `max_dimension` | 6 | `uint32` | Downscale so the longer edge is at most this many pixels, preserving aspect ratio. 0 means no limit (native resolution). | | `include_cursor` | 7 | `bool` | Draw the pointer into the image. | ### ScreenshotResponse Response for `ComputerService.Screenshot`. | Field | # | Type | Description | | --- | --- | --- | --- | | `image` | 1 | `bytes` | Encoded image bytes. | | `format` | 2 | [`ImageFormat`](#imageformat) | Format of `image`. | | `image_size` | 3 | [`PixelSize`](</docs/cua-sdk/reference/protocol/env-common#pixelsize>) | Pixel size of `image` (after any downscale). | | `native_size` | 4 | [`PixelSize`](</docs/cua-sdk/reference/protocol/env-common#pixelsize>) | Pixel size of the captured area at native (physical) resolution. | | `scale` | 5 | `double` | Image pixels per logical point. To convert an image pixel to a screen point: `screen = logical_bounds.origin + pixel / scale`. | | `logical_bounds` | 6 | [`Rect`](</docs/cua-sdk/reference/protocol/env-common#rect>) | The captured area in global logical points (`COORDINATE_SPACE_SCREEN`). | | `screenshot_id` | 7 | `string` | Id to reference this capture's pixel space in later input requests (`COORDINATE_SPACE_SCREENSHOT`). Valid until the geometry of the source changes; the server keeps at least the 16 most recent ids. | | `display_id` | 8 | `string` | Display the capture came from (for a window capture, the display containing most of the window). | | `captured_at` | 9 | `google.protobuf.Timestamp` | When the frame was captured. | ### InputTarget Where and how an input request is delivered. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Window to deliver to. Unset delivers to whatever is under the pointer (pointer) or focused (keyboard). Required for `DELIVERY_BACKGROUND` on most platforms and for `COORDINATE_SPACE_WINDOW`. | | `display_id` | 2 | `string` | Display for `COORDINATE_SPACE_NORMALIZED` without a window. Empty means the primary display. | | `delivery` | 3 | [`Delivery`](</docs/cua-sdk/reference/protocol/env-common#delivery>) | Delivery mode. | | `space` | 4 | [`CoordinateSpace`](</docs/cua-sdk/reference/protocol/env-common#coordinatespace>) | Coordinate space of every point in the request. | | `screenshot_id` | 5 | `string` | Required with `COORDINATE_SPACE_SCREENSHOT`: which screenshot's pixel space the points refer to. | ### PointerClick Click (press and release) one or more times. | Field | # | Type | Description | | --- | --- | --- | --- | | `position` | 1 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Where to click. Unset clicks at the current pointer position. | | `button` | 2 | [`MouseButton`](#mousebutton) | Which button. | | `count` | 3 | `uint32` | Number of clicks: 1 single, 2 double, 3 triple. 0 means 1. | | `modifiers` | 4 | repeated [`Key`](#key) | Modifier keys held during the click. | ### PointerMove Move the pointer. | Field | # | Type | Description | | --- | --- | --- | --- | | `position` | 1 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Destination. | | `duration` | 2 | `google.protobuf.Duration` | Animate the move over this duration. Unset jumps instantly. | ### PointerDown Press a button without releasing it. | Field | # | Type | Description | | --- | --- | --- | --- | | `position` | 1 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Where to press. Unset presses at the current position. | | `button` | 2 | [`MouseButton`](#mousebutton) | Which button. | ### PointerUp Release a pressed button. | Field | # | Type | Description | | --- | --- | --- | --- | | `position` | 1 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Where to release. Unset releases at the current position. | | `button` | 2 | [`MouseButton`](#mousebutton) | Which button. | ### PointerDrag Press at `from`, move along `path` to `to`, release. | Field | # | Type | Description | | --- | --- | --- | --- | | `from` | 1 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Start point. | | `to` | 2 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | End point. | | `path` | 3 | repeated [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Optional intermediate points, in order. | | `button` | 4 | [`MouseButton`](#mousebutton) | Which button. | | `duration` | 5 | `google.protobuf.Duration` | Total drag duration. Unset means 300 ms. | | `modifiers` | 6 | repeated [`Key`](#key) | Modifier keys held during the drag. | ### PointerScroll Scroll by a delta. | Field | # | Type | Description | | --- | --- | --- | --- | | `position` | 1 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Where to scroll. Unset scrolls at the current pointer position. The pointer is not moved for background delivery. | | `delta_x` | 2 | `double` | Horizontal delta; positive scrolls content right. | | `delta_y` | 3 | `double` | Vertical delta; positive scrolls content down. | | `unit` | 4 | [`ScrollUnit`](#scrollunit) | Unit of the deltas. | ### PointerRequest Request for `ComputerService.Pointer`. | Field | # | Type | Description | | --- | --- | --- | --- | | `target` | 1 | [`InputTarget`](#inputtarget) | Target, delivery and coordinate space. | | `click` | 2 | [`PointerClick`](#pointerclick) (oneof `action`) | Click. | | `move` | 3 | [`PointerMove`](#pointermove) (oneof `action`) | Move. | | `down` | 4 | [`PointerDown`](#pointerdown) (oneof `action`) | Button down. | | `up` | 5 | [`PointerUp`](#pointerup) (oneof `action`) | Button up. | | `drag` | 6 | [`PointerDrag`](#pointerdrag) (oneof `action`) | Drag. | | `scroll` | 7 | [`PointerScroll`](#pointerscroll) (oneof `action`) | Scroll. | ### PointerResponse Response for `ComputerService.Pointer`. | Field | # | Type | Description | | --- | --- | --- | --- | | `report` | 1 | [`DeliveryReport`](</docs/cua-sdk/reference/protocol/env-common#deliveryreport>) | What actually happened. | | `cursor_position` | 2 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | User-visible pointer position afterwards, in screen points. | ### KeyInput One key, either named or as a character. | Field | # | Type | Description | | --- | --- | --- | --- | | `named` | 1 | [`Key`](#key) (oneof `key`) | A named key. | | `character` | 2 | `string` (oneof `key`) | A single Unicode scalar value, mapped through the guest's current keyboard layout to the key (and Shift/AltGr) that produces it. Fails with `ERROR_REASON_DELIVERY_FAILED` if no key produces it; use `KeyboardType` for arbitrary text. | ### KeyboardType Type text. | Field | # | Type | Description | | --- | --- | --- | --- | | `text` | 1 | `string` | UTF-8 text. Newlines are typed as Enter. | | `mode` | 2 | [`TextEntryMode`](#textentrymode) | Entry mode. | | `delay` | 3 | `google.protobuf.Duration` | Delay between characters for `TEXT_ENTRY_MODE_KEYSTROKES`. Unset means no deliberate delay. | ### KeyboardPress Press and release one key, optionally with modifiers held. | Field | # | Type | Description | | --- | --- | --- | --- | | `key` | 1 | [`KeyInput`](#keyinput) | The key. | | `modifiers` | 2 | repeated [`Key`](#key) | Modifiers held while the key is pressed. | | `repeat` | 3 | `uint32` | Number of presses. 0 means 1. | ### KeyboardHotkey Press a chord: keys go down in order and up in reverse order (for example Control, Shift, T). | Field | # | Type | Description | | --- | --- | --- | --- | | `keys` | 1 | repeated [`KeyInput`](#keyinput) | Keys in press order. At least one. | ### KeyboardDown Press a key without releasing it. | Field | # | Type | Description | | --- | --- | --- | --- | | `key` | 1 | [`KeyInput`](#keyinput) | The key. | ### KeyboardUp Release a pressed key. | Field | # | Type | Description | | --- | --- | --- | --- | | `key` | 1 | [`KeyInput`](#keyinput) | The key. | ### KeyboardRequest Request for `ComputerService.Keyboard`. | Field | # | Type | Description | | --- | --- | --- | --- | | `target` | 1 | [`InputTarget`](#inputtarget) | Target and delivery. `space` and points are ignored. | | `type` | 2 | [`KeyboardType`](#keyboardtype) (oneof `action`) | Type text. | | `press` | 3 | [`KeyboardPress`](#keyboardpress) (oneof `action`) | Press one key. | | `hotkey` | 4 | [`KeyboardHotkey`](#keyboardhotkey) (oneof `action`) | Press a chord. | | `down` | 5 | [`KeyboardDown`](#keyboarddown) (oneof `action`) | Key down. | | `up` | 6 | [`KeyboardUp`](#keyboardup) (oneof `action`) | Key up. | ### KeyboardResponse Response for `ComputerService.Keyboard`. | Field | # | Type | Description | | --- | --- | --- | --- | | `report` | 1 | [`DeliveryReport`](</docs/cua-sdk/reference/protocol/env-common#deliveryreport>) | What actually happened. | ### ClipboardContent Clipboard content. | Field | # | Type | Description | | --- | --- | --- | --- | | `text` | 1 | optional `string` | Plain UTF-8 text. At most 1 MiB. Unset means no text flavor. | | `file_paths` | 2 | repeated `string` | Absolute guest paths of files on the clipboard (file copy/paste). To put host files on the guest clipboard, upload them first with `FilesystemService`, then set their paths here. Requires capability "clipboard.files". | | `image_png` | 3 | optional `bytes` | A PNG image (for example a copied screenshot). At most 16 MiB. Requires capability "clipboard.image". | ### GetClipboardRequest Request for `ComputerService.GetClipboard`. No fields. ### GetClipboardResponse Response for `ComputerService.GetClipboard`. | Field | # | Type | Description | | --- | --- | --- | --- | | `content` | 1 | [`ClipboardContent`](#clipboardcontent) | Current content. Only flavors the driver understands are reported. | | `generation` | 2 | `uint64` | Clipboard generation counter; changes whenever the content changes. | ### SetClipboardRequest Request for `ComputerService.SetClipboard`. | Field | # | Type | Description | | --- | --- | --- | --- | | `content` | 1 | [`ClipboardContent`](#clipboardcontent) | New content. Replaces every flavor. | ### SetClipboardResponse Response for `ComputerService.SetClipboard`. | Field | # | Type | Description | | --- | --- | --- | --- | | `generation` | 1 | `uint64` | Generation after the write. | ### GetCursorPositionRequest Request for `ComputerService.GetCursorPosition`. No fields. ### GetCursorPositionResponse Response for `ComputerService.GetCursorPosition`. | Field | # | Type | Description | | --- | --- | --- | --- | | `position` | 1 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Position in global logical points (`COORDINATE_SPACE_SCREEN`). | | `display_id` | 2 | `string` | Display containing the pointer. | ### ListDisplaysRequest Request for `ComputerService.ListDisplays`. No fields. ### ListDisplaysResponse Response for `ComputerService.ListDisplays`. | Field | # | Type | Description | | --- | --- | --- | --- | | `displays` | 1 | repeated [`Display`](</docs/cua-sdk/reference/protocol/env-common#display>) | Attached displays, primary first. | ### ImageFormat Encoded image format. | Value | # | Description | | --- | --- | --- | | `IMAGE_FORMAT_UNSPECIFIED` | 0 | Not set. Treated as `IMAGE_FORMAT_PNG`. | | `IMAGE_FORMAT_PNG` | 1 | PNG (lossless). | | `IMAGE_FORMAT_JPEG` | 2 | JPEG (lossy, honours `quality`). | | `IMAGE_FORMAT_WEBP` | 3 | WebP (lossy, honours `quality`). | ### MouseButton Mouse button. | Value | # | Description | | --- | --- | --- | | `MOUSE_BUTTON_UNSPECIFIED` | 0 | Not set. Treated as `MOUSE_BUTTON_LEFT`. | | `MOUSE_BUTTON_LEFT` | 1 | Primary button. | | `MOUSE_BUTTON_RIGHT` | 2 | Secondary button. | | `MOUSE_BUTTON_MIDDLE` | 3 | Middle button / wheel press. | | `MOUSE_BUTTON_BACK` | 4 | Back (button 4). | | `MOUSE_BUTTON_FORWARD` | 5 | Forward (button 5). | ### ScrollUnit Unit of scroll deltas. | Value | # | Description | | --- | --- | --- | | `SCROLL_UNIT_UNSPECIFIED` | 0 | Not set. Treated as `SCROLL_UNIT_LINE`. | | `SCROLL_UNIT_LINE` | 1 | Wheel notches / text lines. | | `SCROLL_UNIT_PIXEL` | 2 | Logical points (precise, trackpad-style scrolling). | ### Key Named keys, independent of keyboard layout. Printable characters that are not listed here are sent with `KeyInput.character`. Values are grouped in numeric blocks with gaps left for additions within each block: modifiers 1-19, editing and navigation 20-59, function keys 60-99, letters 100-129, digits 130-149, punctuation 150-179, numeric keypad 180-219, system and lock keys 220-239, media and hardware keys 240-269, browser and launcher keys 270-299. | Value | # | Description | | --- | --- | --- | | `KEY_UNSPECIFIED` | 0 | Not set. Rejected. | | `KEY_SHIFT` | 1 | Shift (either side). | | `KEY_SHIFT_LEFT` | 2 | Left Shift. | | `KEY_SHIFT_RIGHT` | 3 | Right Shift. | | `KEY_CONTROL` | 4 | Control (either side). | | `KEY_CONTROL_LEFT` | 5 | Left Control. | | `KEY_CONTROL_RIGHT` | 6 | Right Control. | | `KEY_ALT` | 7 | Alt / Option (either side). | | `KEY_ALT_LEFT` | 8 | Left Alt / Option. | | `KEY_ALT_RIGHT` | 9 | Right Alt / Option / AltGr. | | `KEY_META` | 10 | Meta: Command on macOS, Windows key on Windows, Super on Linux (either side). | | `KEY_META_LEFT` | 11 | Left Meta. | | `KEY_META_RIGHT` | 12 | Right Meta. | | `KEY_FN` | 13 | Fn (where the platform exposes it). | | `KEY_CAPS_LOCK` | 14 | Caps Lock. | | `KEY_ENTER` | 20 | Enter / Return. | | `KEY_TAB` | 21 | Tab. | | `KEY_SPACE` | 22 | Space bar. | | `KEY_BACKSPACE` | 23 | Backspace (Delete on macOS keyboards). | | `KEY_DELETE` | 24 | Forward delete. | | `KEY_ESCAPE` | 25 | Escape. | | `KEY_INSERT` | 26 | Insert. | | `KEY_HOME` | 27 | Home. | | `KEY_END` | 28 | End. | | `KEY_PAGE_UP` | 29 | Page Up. | | `KEY_PAGE_DOWN` | 30 | Page Down. | | `KEY_ARROW_UP` | 31 | Up arrow. | | `KEY_ARROW_DOWN` | 32 | Down arrow. | | `KEY_ARROW_LEFT` | 33 | Left arrow. | | `KEY_ARROW_RIGHT` | 34 | Right arrow. | | `KEY_CONTEXT_MENU` | 35 | Context menu / Application key. | | `KEY_HELP` | 36 | Help. | | `KEY_F1` | 60 | F1. | | `KEY_F2` | 61 | F2. | | `KEY_F3` | 62 | F3. | | `KEY_F4` | 63 | F4. | | `KEY_F5` | 64 | F5. | | `KEY_F6` | 65 | F6. | | `KEY_F7` | 66 | F7. | | `KEY_F8` | 67 | F8. | | `KEY_F9` | 68 | F9. | | `KEY_F10` | 69 | F10. | | `KEY_F11` | 70 | F11. | | `KEY_F12` | 71 | F12. | | `KEY_F13` | 72 | F13. | | `KEY_F14` | 73 | F14. | | `KEY_F15` | 74 | F15. | | `KEY_F16` | 75 | F16. | | `KEY_F17` | 76 | F17. | | `KEY_F18` | 77 | F18. | | `KEY_F19` | 78 | F19. | | `KEY_F20` | 79 | F20. | | `KEY_F21` | 80 | F21. | | `KEY_F22` | 81 | F22. | | `KEY_F23` | 82 | F23. | | `KEY_F24` | 83 | F24. | | `KEY_A` | 100 | Letter A (physical key position on a US layout). | | `KEY_B` | 101 | Letter B. | | `KEY_C` | 102 | Letter C. | | `KEY_D` | 103 | Letter D. | | `KEY_E` | 104 | Letter E. | | `KEY_F` | 105 | Letter F. | | `KEY_G` | 106 | Letter G. | | `KEY_H` | 107 | Letter H. | | `KEY_I` | 108 | Letter I. | | `KEY_J` | 109 | Letter J. | | `KEY_K` | 110 | Letter K. | | `KEY_L` | 111 | Letter L. | | `KEY_M` | 112 | Letter M. | | `KEY_N` | 113 | Letter N. | | `KEY_O` | 114 | Letter O. | | `KEY_P` | 115 | Letter P. | | `KEY_Q` | 116 | Letter Q. | | `KEY_R` | 117 | Letter R. | | `KEY_S` | 118 | Letter S. | | `KEY_T` | 119 | Letter T. | | `KEY_U` | 120 | Letter U. | | `KEY_V` | 121 | Letter V. | | `KEY_W` | 122 | Letter W. | | `KEY_X` | 123 | Letter X. | | `KEY_Y` | 124 | Letter Y. | | `KEY_Z` | 125 | Letter Z. | | `KEY_DIGIT_0` | 130 | Digit 0 (top row). | | `KEY_DIGIT_1` | 131 | Digit 1. | | `KEY_DIGIT_2` | 132 | Digit 2. | | `KEY_DIGIT_3` | 133 | Digit 3. | | `KEY_DIGIT_4` | 134 | Digit 4. | | `KEY_DIGIT_5` | 135 | Digit 5. | | `KEY_DIGIT_6` | 136 | Digit 6. | | `KEY_DIGIT_7` | 137 | Digit 7. | | `KEY_DIGIT_8` | 138 | Digit 8. | | `KEY_DIGIT_9` | 139 | Digit 9. | | `KEY_MINUS` | 150 | Minus / underscore. | | `KEY_EQUAL` | 151 | Equal / plus. | | `KEY_BRACKET_LEFT` | 152 | Left bracket / brace. | | `KEY_BRACKET_RIGHT` | 153 | Right bracket / brace. | | `KEY_BACKSLASH` | 154 | Backslash / pipe. | | `KEY_SEMICOLON` | 155 | Semicolon / colon. | | `KEY_QUOTE` | 156 | Quote / double quote. | | `KEY_BACKQUOTE` | 157 | Backquote / tilde. | | `KEY_COMMA` | 158 | Comma / less-than. | | `KEY_PERIOD` | 159 | Period / greater-than. | | `KEY_SLASH` | 160 | Slash / question mark. | | `KEY_INTL_BACKSLASH` | 161 | The extra key left of Z on ISO keyboards. | | `KEY_NUMPAD_0` | 180 | Keypad 0. | | `KEY_NUMPAD_1` | 181 | Keypad 1. | | `KEY_NUMPAD_2` | 182 | Keypad 2. | | `KEY_NUMPAD_3` | 183 | Keypad 3. | | `KEY_NUMPAD_4` | 184 | Keypad 4. | | `KEY_NUMPAD_5` | 185 | Keypad 5. | | `KEY_NUMPAD_6` | 186 | Keypad 6. | | `KEY_NUMPAD_7` | 187 | Keypad 7. | | `KEY_NUMPAD_8` | 188 | Keypad 8. | | `KEY_NUMPAD_9` | 189 | Keypad 9. | | `KEY_NUMPAD_ADD` | 190 | Keypad +. | | `KEY_NUMPAD_SUBTRACT` | 191 | Keypad -. | | `KEY_NUMPAD_MULTIPLY` | 192 | Keypad *. | | `KEY_NUMPAD_DIVIDE` | 193 | Keypad /. | | `KEY_NUMPAD_DECIMAL` | 194 | Keypad decimal point. | | `KEY_NUMPAD_ENTER` | 195 | Keypad Enter. | | `KEY_NUMPAD_EQUAL` | 196 | Keypad =. | | `KEY_NUM_LOCK` | 197 | Num Lock / Clear. | | `KEY_PRINT_SCREEN` | 220 | Print Screen / SysRq. | | `KEY_SCROLL_LOCK` | 221 | Scroll Lock. | | `KEY_PAUSE` | 222 | Pause / Break. | | `KEY_MEDIA_PLAY_PAUSE` | 240 | Play / pause toggle. | | `KEY_MEDIA_STOP` | 241 | Stop. | | `KEY_MEDIA_NEXT` | 242 | Next track. | | `KEY_MEDIA_PREVIOUS` | 243 | Previous track. | | `KEY_VOLUME_UP` | 244 | Volume up. | | `KEY_VOLUME_DOWN` | 245 | Volume down. | | `KEY_VOLUME_MUTE` | 246 | Mute toggle. | | `KEY_BRIGHTNESS_UP` | 247 | Brightness up. | | `KEY_BRIGHTNESS_DOWN` | 248 | Brightness down. | | `KEY_EJECT` | 249 | Eject. | | `KEY_POWER` | 250 | Power. | | `KEY_SLEEP` | 251 | Sleep. | | `KEY_BROWSER_BACK` | 270 | Browser back. | | `KEY_BROWSER_FORWARD` | 271 | Browser forward. | | `KEY_BROWSER_REFRESH` | 272 | Browser refresh. | | `KEY_BROWSER_HOME` | 273 | Browser home. | | `KEY_BROWSER_SEARCH` | 274 | Browser search. | | `KEY_LAUNCH_MAIL` | 275 | Launch mail client. | | `KEY_LAUNCH_APP1` | 276 | Launch application 1 (often "My Computer" / Mission Control). | | `KEY_LAUNCH_APP2` | 277 | Launch application 2 (often calculator / Launchpad). | ### TextEntryMode How `KeyboardType` enters text. | Value | # | Description | | --- | --- | --- | | `TEXT_ENTRY_MODE_UNSPECIFIED` | 0 | Not set. Treated as `TEXT_ENTRY_MODE_KEYSTROKES`. | | `TEXT_ENTRY_MODE_KEYSTROKES` | 1 | Synthesize a key press per character (Unicode injection for characters without a key). Honors the caret position and triggers key handlers. | | `TEXT_ENTRY_MODE_INSERT` | 2 | Insert the text in one operation (accessibility value insertion or paste). Fast, but inserts relative to the element's own insertion point and may not fire per-key handlers. | ## Types in `cua/env/v1/driver.proto` ### ListToolsRequest Request for `DriverService.ListTools`. No fields. ### ToolInfo A tool description, mirroring the MCP tool shape. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Tool name, for example "click" or "get_window_state". | | `description` | 2 | `string` | Human-readable description. | | `input_schema_json` | 3 | `string` | JSON Schema of the arguments object, as JSON text. | | `output_schema_json` | 4 | `string` | JSON Schema of the structured result, as JSON text. Empty when the tool declares none. | | `read_only` | 5 | `bool` | True if the tool does not modify guest state. | | `destructive` | 6 | `bool` | True if the tool may perform destructive changes. | ### ListToolsResponse Response for `DriverService.ListTools`. | Field | # | Type | Description | | --- | --- | --- | --- | | `tools` | 1 | repeated [`ToolInfo`](#toolinfo) | Available tools. | | `contract_version` | 2 | `string` | cua-driver contract version the registry implements. | ### CallToolRequest Request for `DriverService.CallTool`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Tool name. | | `arguments_json` | 2 | `string` | Arguments object as JSON text. Empty means `{}`. | | `timeout` | 3 | `google.protobuf.Duration` | Abort the call after this long. Unset means the tool's own default. | ### ToolImage An image produced by a tool. | Field | # | Type | Description | | --- | --- | --- | --- | | `data` | 1 | `bytes` | Encoded bytes. | | `mime_type` | 2 | `string` | Media type, for example "image/png". | ### ToolContent One part of a tool result, mirroring MCP content parts. | Field | # | Type | Description | | --- | --- | --- | --- | | `text` | 1 | `string` (oneof `content`) | Text. | | `image` | 2 | [`ToolImage`](#toolimage) (oneof `content`) | An image. | | `json` | 3 | `string` (oneof `content`) | JSON text. | ### CallToolResponse Response for `DriverService.CallTool`. Tool-level failures are reported with `is_error`, not as gRPC errors; gRPC errors mean the call could not be dispatched (unknown tool, invalid JSON, timeout). | Field | # | Type | Description | | --- | --- | --- | --- | | `content` | 1 | repeated [`ToolContent`](#toolcontent) | Result parts in order. | | `structured_json` | 2 | `string` | Structured result as JSON text, when the tool declares an output schema. | | `is_error` | 3 | `bool` | True if the tool reported a failure. | --- # Windows and accessibility cua.env.v1 WindowsService and AccessibilityService: list, watch and arrange windows, launch apps, and the accessibility tree. Windows, app launch and the accessibility tree. These services need the desktop provider. Source: `libs/cua/proto/cua/env/v1/windows.proto`, `libs/cua/proto/cua/env/v1/accessibility.proto`. ## WindowsService `cua.env.v1.WindowsService` Window enumeration, window management and app launching. Windows are addressed with opaque `WindowRef` handles (id + epoch). Native handles never cross the wire. Nothing here goes through a shell: apps are launched by identifier with an explicit argument vector. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`ListWindows`](#windowsservicelistwindows) | [`ListWindowsRequest`](#listwindowsrequest) | [`ListWindowsResponse`](#listwindowsresponse) | unary | | [`WatchWindows`](#windowsservicewatchwindows) | [`WatchWindowsRequest`](#watchwindowsrequest) | [`WatchWindowsResponse`](#watchwindowsresponse) | server stream | | [`GetWindow`](#windowsservicegetwindow) | [`GetWindowRequest`](#getwindowrequest) | [`GetWindowResponse`](#getwindowresponse) | unary | | [`ActivateWindow`](#windowsserviceactivatewindow) | [`ActivateWindowRequest`](#activatewindowrequest) | [`ActivateWindowResponse`](#activatewindowresponse) | unary | | [`SetWindowBounds`](#windowsservicesetwindowbounds) | [`SetWindowBoundsRequest`](#setwindowboundsrequest) | [`SetWindowBoundsResponse`](#setwindowboundsresponse) | unary | | [`MinimizeWindow`](#windowsserviceminimizewindow) | [`MinimizeWindowRequest`](#minimizewindowrequest) | [`MinimizeWindowResponse`](#minimizewindowresponse) | unary | | [`MaximizeWindow`](#windowsservicemaximizewindow) | [`MaximizeWindowRequest`](#maximizewindowrequest) | [`MaximizeWindowResponse`](#maximizewindowresponse) | unary | | [`RestoreWindow`](#windowsservicerestorewindow) | [`RestoreWindowRequest`](#restorewindowrequest) | [`RestoreWindowResponse`](#restorewindowresponse) | unary | | [`CloseWindow`](#windowsserviceclosewindow) | [`CloseWindowRequest`](#closewindowrequest) | [`CloseWindowResponse`](#closewindowresponse) | unary | | [`LaunchApp`](#windowsservicelaunchapp) | [`LaunchAppRequest`](#launchapprequest) | [`LaunchAppResponse`](#launchappresponse) | unary | | [`Open`](#windowsserviceopen) | [`OpenRequest`](#openrequest) | [`OpenResponse`](#openresponse) | unary | ### WindowsService.ListWindows `/cua.env.v1.WindowsService/ListWindows`, unary: [`ListWindowsRequest`](#listwindowsrequest) to [`ListWindowsResponse`](#listwindowsresponse). Lists windows matching a filter. ### WindowsService.WatchWindows `/cua.env.v1.WindowsService/WatchWindows`, server stream: [`WatchWindowsRequest`](#watchwindowsrequest) to [`WatchWindowsResponse`](#watchwindowsresponse). Streams window lifecycle changes. Starts with a snapshot of all matching windows so clients never race between a list and a watch. ### WindowsService.GetWindow `/cua.env.v1.WindowsService/GetWindow`, unary: [`GetWindowRequest`](#getwindowrequest) to [`GetWindowResponse`](#getwindowresponse). Returns one window. ### WindowsService.ActivateWindow `/cua.env.v1.WindowsService/ActivateWindow`, unary: [`ActivateWindowRequest`](#activatewindowrequest) to [`ActivateWindowResponse`](#activatewindowresponse). Brings a window to the front and gives it keyboard focus. ### WindowsService.SetWindowBounds `/cua.env.v1.WindowsService/SetWindowBounds`, unary: [`SetWindowBoundsRequest`](#setwindowboundsrequest) to [`SetWindowBoundsResponse`](#setwindowboundsresponse). Moves and/or resizes a window. ### WindowsService.MinimizeWindow `/cua.env.v1.WindowsService/MinimizeWindow`, unary: [`MinimizeWindowRequest`](#minimizewindowrequest) to [`MinimizeWindowResponse`](#minimizewindowresponse). Minimizes (iconifies) a window. ### WindowsService.MaximizeWindow `/cua.env.v1.WindowsService/MaximizeWindow`, unary: [`MaximizeWindowRequest`](#maximizewindowrequest) to [`MaximizeWindowResponse`](#maximizewindowresponse). Maximizes (zooms) a window. ### WindowsService.RestoreWindow `/cua.env.v1.WindowsService/RestoreWindow`, unary: [`RestoreWindowRequest`](#restorewindowrequest) to [`RestoreWindowResponse`](#restorewindowresponse). Restores a minimized or maximized window to its normal state. ### WindowsService.CloseWindow `/cua.env.v1.WindowsService/CloseWindow`, unary: [`CloseWindowRequest`](#closewindowrequest) to [`CloseWindowResponse`](#closewindowresponse). Asks a window to close (like clicking its close button), or force-closes it. ### WindowsService.LaunchApp `/cua.env.v1.WindowsService/LaunchApp`, unary: [`LaunchAppRequest`](#launchapprequest) to [`LaunchAppResponse`](#launchappresponse). Launches an application. ### WindowsService.Open `/cua.env.v1.WindowsService/Open`, unary: [`OpenRequest`](#openrequest) to [`OpenResponse`](#openresponse). Opens a URL or path with its default (or a given) application. ## AccessibilityService `cua.env.v1.AccessibilityService` The guest accessibility tree (macOS AX, Windows UI Automation, Linux AT-SPI via cua-driver platform-linux). Every read returns a snapshot id. Element ids are only meaningful together with the snapshot id they came from; acting on an element from an older snapshot fails with `ERROR_REASON_STALE_SNAPSHOT` unless the server can prove the element is unchanged. Requires capability "a11y". | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`GetTree`](#accessibilityservicegettree) | [`GetTreeRequest`](#gettreerequest) | [`GetTreeResponse`](#gettreeresponse) | unary | | [`Find`](#accessibilityservicefind) | [`FindRequest`](#findrequest) | [`FindResponse`](#findresponse) | unary | | [`Act`](#accessibilityserviceact) | [`ActRequest`](#actrequest) | [`ActResponse`](#actresponse) | unary | ### AccessibilityService.GetTree `/cua.env.v1.AccessibilityService/GetTree`, unary: [`GetTreeRequest`](#gettreerequest) to [`GetTreeResponse`](#gettreeresponse). Returns the accessibility tree of a window (or the focused window). ### AccessibilityService.Find `/cua.env.v1.AccessibilityService/Find`, unary: [`FindRequest`](#findrequest) to [`FindResponse`](#findresponse). Finds elements matching a query. ### AccessibilityService.Act `/cua.env.v1.AccessibilityService/Act`, unary: [`ActRequest`](#actrequest) to [`ActResponse`](#actresponse). Performs an accessibility action on an element. ## Types in `cua/env/v1/windows.proto` ### AppInfo Application that owns a window. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Display name, for example "Firefox". | | `app_id` | 2 | `string` | Platform app identifier: bundle id on macOS, .desktop id on Linux, AppUserModelID or executable name on Windows. Empty when unknown. | | `pid` | 3 | `uint32` | Guest process id of the owning process. Guest-local; never a host id. | ### WindowInfo Description of a window. | Field | # | Type | Description | | --- | --- | --- | --- | | `ref` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Handle. | | `title` | 2 | `string` | Title. | | `app` | 3 | [`AppInfo`](#appinfo) | Owning application. | | `bounds` | 4 | [`Rect`](</docs/cua-sdk/reference/protocol/env-common#rect>) | Frame in global logical points. Advisory: it is a snapshot and may be stale by the time it is used. Media sessions and screenshots report their own authoritative geometry. | | `display_id` | 5 | `string` | Display containing most of the window. | | `state` | 6 | [`WindowState`](#windowstate) | State. | | `kind` | 7 | [`WindowKind`](#windowkind) | Role. | | `focused` | 8 | `bool` | True if this window has keyboard focus. | | `on_screen` | 9 | `bool` | True if any part is on screen (not minimized, hidden or off-desktop). | | `z_order` | 10 | `uint32` | Front-to-back stacking order among listed windows, 0 is frontmost. | ### WindowFilter Filter for window listing and watching. All set fields must match. | Field | # | Type | Description | | --- | --- | --- | --- | | `title_contains` | 1 | `string` | Case-insensitive substring of the title. | | `app_name_contains` | 2 | `string` | Case-insensitive substring of the app name. | | `app_id` | 3 | `string` | Exact `AppInfo.app_id`. | | `pid` | 4 | `uint32` | Owning process id. | | `display_id` | 5 | `string` | Only windows on this display. | | `on_screen_only` | 6 | `bool` | Only on-screen windows. | | `include_system` | 7 | `bool` | Include `WINDOW_KIND_SYSTEM` and `WINDOW_KIND_PHANTOM` windows, which are omitted by default. | ### ListWindowsRequest Request for `WindowsService.ListWindows`. | Field | # | Type | Description | | --- | --- | --- | --- | | `filter` | 1 | [`WindowFilter`](#windowfilter) | Filter. | ### ListWindowsResponse Response for `WindowsService.ListWindows`. | Field | # | Type | Description | | --- | --- | --- | --- | | `windows` | 1 | repeated [`WindowInfo`](#windowinfo) | Matching windows, front to back. | ### WatchWindowsRequest Request for `WindowsService.WatchWindows`. | Field | # | Type | Description | | --- | --- | --- | --- | | `filter` | 1 | [`WindowFilter`](#windowfilter) | Filter applied to every event. | | `keepalive_interval` | 2 | `google.protobuf.Duration` | Interval between `keepalive` messages. Unset means 30 seconds. | ### WindowSnapshot Initial state of a window watch. | Field | # | Type | Description | | --- | --- | --- | --- | | `windows` | 1 | repeated [`WindowInfo`](#windowinfo) | All matching windows, front to back. | ### WindowClosed A window was closed or no longer matches the filter. | Field | # | Type | Description | | --- | --- | --- | --- | | `ref` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | The window. | ### WatchWindowsResponse One message of the `WindowsService.WatchWindows` stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `snapshot` | 1 | [`WindowSnapshot`](#windowsnapshot) (oneof `event`) | First message: current matching windows. | | `created` | 2 | [`WindowInfo`](#windowinfo) (oneof `event`) | A new window appeared (or started matching). | | `updated` | 3 | [`WindowInfo`](#windowinfo) (oneof `event`) | A window changed title, bounds, state, focus or z-order. Carries the full new description. | | `closed` | 4 | [`WindowClosed`](#windowclosed) (oneof `event`) | A window closed. | | `keepalive` | 5 | [`KeepAlive`](</docs/cua-sdk/reference/protocol/env-common#keepalive>) (oneof `event`) | Idle stream heartbeat. | ### GetWindowRequest Request for `WindowsService.GetWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Which window. | ### GetWindowResponse Response for `WindowsService.GetWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowInfo`](#windowinfo) | The window. | ### ActivateWindowRequest Request for `WindowsService.ActivateWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Which window. | ### ActivateWindowResponse Response for `WindowsService.ActivateWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowInfo`](#windowinfo) | The window afterwards. | ### SetWindowBoundsRequest Request for `WindowsService.SetWindowBounds`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Which window. | | `position` | 2 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | New top-left corner in global logical points. Unset keeps the position. | | `width` | 3 | optional `double` | New width in logical points. Unset keeps the width. | | `height` | 4 | optional `double` | New height in logical points. Unset keeps the height. | ### SetWindowBoundsResponse Response for `WindowsService.SetWindowBounds`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowInfo`](#windowinfo) | The window afterwards. Its bounds may differ from the request when the window manager or app constrains them. | ### MinimizeWindowRequest Request for `WindowsService.MinimizeWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Which window. | ### MinimizeWindowResponse Response for `WindowsService.MinimizeWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowInfo`](#windowinfo) | The window afterwards. | ### MaximizeWindowRequest Request for `WindowsService.MaximizeWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Which window. | ### MaximizeWindowResponse Response for `WindowsService.MaximizeWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowInfo`](#windowinfo) | The window afterwards. | ### RestoreWindowRequest Request for `WindowsService.RestoreWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Which window. | ### RestoreWindowResponse Response for `WindowsService.RestoreWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowInfo`](#windowinfo) | The window afterwards. | ### CloseWindowRequest Request for `WindowsService.CloseWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Which window. | | `force` | 2 | `bool` | Destroy the window without giving the app a chance to object (for example to an unsaved-changes prompt). On most platforms this kills the owning process. | ### CloseWindowResponse Response for `WindowsService.CloseWindow`. | Field | # | Type | Description | | --- | --- | --- | --- | | `closed` | 1 | `bool` | True if the window is gone. False when the app kept it open (for example it showed a save prompt). | ### AppSpec Identifies an application to launch. | Field | # | Type | Description | | --- | --- | --- | --- | | `app_id` | 1 | `string` (oneof `app`) | Platform app identifier: macOS bundle id ("org.mozilla.firefox"), Linux .desktop id ("firefox.desktop"), Windows AppUserModelID. | | `executable` | 2 | `string` (oneof `app`) | Executable name (resolved on PATH) or absolute path. | | `name` | 3 | `string` (oneof `app`) | Display name, resolved by the platform (for example "Firefox"). The least precise option. | ### LaunchAppRequest Request for `WindowsService.LaunchApp`. | Field | # | Type | Description | | --- | --- | --- | --- | | `app` | 1 | [`AppSpec`](#appspec) | What to launch. | | `args` | 2 | repeated `string` | Arguments passed verbatim, never through a shell. | | `env` | 3 | map of `string` to `string` | Extra environment variables. | | `cwd` | 4 | `string` | Working directory. Empty means the user's home. | | `delivery` | 5 | [`Delivery`](</docs/cua-sdk/reference/protocol/env-common#delivery>) | `DELIVERY_BACKGROUND` launches without activating the app or stealing focus (where the platform allows). `DELIVERY_FOREGROUND` activates it. | | `wait_for_window` | 6 | `google.protobuf.Duration` | Wait up to this long for the app's first window and return it. Unset returns immediately after the process starts. | ### LaunchAppResponse Response for `WindowsService.LaunchApp`. | Field | # | Type | Description | | --- | --- | --- | --- | | `pid` | 1 | `uint32` | Guest process id of the launched (or already running, for single- instance apps) process. 0 when the platform does not report it. | | `windows` | 2 | repeated [`WindowInfo`](#windowinfo) | Windows that appeared within `wait_for_window`. | ### OpenRequest Request for `WindowsService.Open`. | Field | # | Type | Description | | --- | --- | --- | --- | | `url` | 1 | `string` (oneof `target`) | A URL, for example "https://example.com" or "mailto:a@b". | | `path` | 2 | `string` (oneof `target`) | A guest file or directory path. | | `with_app` | 3 | [`AppSpec`](#appspec) | Open with this app instead of the default handler. | | `delivery` | 4 | [`Delivery`](</docs/cua-sdk/reference/protocol/env-common#delivery>) | Delivery, as for `LaunchAppRequest.delivery`. | ### OpenResponse Response for `WindowsService.Open`. | Field | # | Type | Description | | --- | --- | --- | --- | | `pid` | 1 | `uint32` | Guest process id of the handling app, 0 when unknown. | ### WindowState Window state. | Value | # | Description | | --- | --- | --- | | `WINDOW_STATE_UNSPECIFIED` | 0 | Not reported. | | `WINDOW_STATE_NORMAL` | 1 | Normal (neither minimized, maximized nor fullscreen). | | `WINDOW_STATE_MINIMIZED` | 2 | Minimized / iconified. | | `WINDOW_STATE_MAXIMIZED` | 3 | Maximized / zoomed. | | `WINDOW_STATE_FULLSCREEN` | 4 | Fullscreen. | | `WINDOW_STATE_HIDDEN` | 5 | Hidden (app hidden, withdrawn, or on another virtual desktop). | ### WindowKind Broad window role. | Value | # | Description | | --- | --- | --- | | `WINDOW_KIND_UNSPECIFIED` | 0 | Not reported. | | `WINDOW_KIND_STANDARD` | 1 | A normal application window. | | `WINDOW_KIND_DIALOG` | 2 | A dialog or sheet. | | `WINDOW_KIND_PANEL` | 3 | A floating panel or utility window. | | `WINDOW_KIND_MENU` | 4 | A menu or popup. | | `WINDOW_KIND_TOOLTIP` | 5 | A tooltip. | | `WINDOW_KIND_SYSTEM` | 6 | Desktop chrome: dock, menu bar strip, panel, wallpaper. | | `WINDOW_KIND_PHANTOM` | 7 | Invisible or indicator surfaces (for example the macOS screen-recording indicator, 1x1 helper windows). Hidden unless `include_system` is set. | ## Types in `cua/env/v1/accessibility.proto` ### AccessibilityNode One accessibility element. Trees are returned flattened in pre-order (parents before children) to avoid unbounded message nesting. | Field | # | Type | Description | | --- | --- | --- | --- | | `element_id` | 1 | `string` | Element id, unique within its snapshot. | | `parent_id` | 2 | `string` | Parent element id, empty for the root. | | `depth` | 3 | `uint32` | Depth below the root (root is 0). | | `role` | 4 | `string` | Normalized role, for example "button", "text_field", "menu_item", "window". Platform roles are mapped to one vocabulary. | | `native_role` | 5 | `string` | The platform's native role string (for example "AXButton", "ControlType.Button", "push button"), for debugging. | | `name` | 6 | `string` | Accessible name / label. | | `value` | 7 | `string` | Current value (text content, slider value), when it has one. | | `description` | 8 | `string` | Accessible description / help text. | | `bounds` | 9 | [`Rect`](</docs/cua-sdk/reference/protocol/env-common#rect>) | Frame in global logical points, when known. | | `states` | 10 | repeated `string` | Normalized states, for example "focused", "enabled", "selected", "checked", "expanded", "editable", "offscreen". | | `actions` | 11 | repeated [`AccessibilityAction`](#accessibilityaction) | Actions the element supports (see `AccessibilityAction`). | | `attributes` | 12 | map of `string` to `string` | Other attributes the platform exposes, stringified. | ### GetTreeRequest Request for `AccessibilityService.GetTree`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Window whose tree to read. Unset means the focused window. | | `max_depth` | 2 | `uint32` | Maximum depth below the root. 0 means unlimited (subject to the server's node limit). | | `include_hidden` | 3 | `bool` | Include elements that are offscreen or not visible. | | `max_nodes` | 4 | `uint32` | Stop after this many nodes. 0 means the server limit (normally 5000). | ### GetTreeResponse Response for `AccessibilityService.GetTree`. | Field | # | Type | Description | | --- | --- | --- | --- | | `snapshot_id` | 1 | `string` | Snapshot id for later `Act` calls. | | `window` | 2 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | The window the tree belongs to. | | `nodes` | 3 | repeated [`AccessibilityNode`](#accessibilitynode) | Nodes in pre-order. | | `truncated` | 4 | `bool` | True if `max_depth` or `max_nodes` cut the tree short. | ### AccessibilityQuery Query for `AccessibilityService.Find`. All set fields must match. | Field | # | Type | Description | | --- | --- | --- | --- | | `role` | 1 | `string` | Normalized role. | | `name` | 2 | `string` | Exact accessible name. | | `name_contains` | 3 | `string` | Case-insensitive substring of the accessible name. | | `value_contains` | 4 | `string` | Case-insensitive substring of the value. | | `states` | 5 | repeated `string` | Required states. | ### FindRequest Request for `AccessibilityService.Find`. | Field | # | Type | Description | | --- | --- | --- | --- | | `window` | 1 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Window to search. Unset means the focused window. | | `query` | 2 | [`AccessibilityQuery`](#accessibilityquery) | What to look for. | | `max_results` | 3 | `uint32` | Maximum matches. 0 means 50. | ### FindResponse Response for `AccessibilityService.Find`. | Field | # | Type | Description | | --- | --- | --- | --- | | `snapshot_id` | 1 | `string` | Snapshot id for later `Act` calls. | | `nodes` | 2 | repeated [`AccessibilityNode`](#accessibilitynode) | Matching elements (their `parent_id` refers to elements not necessarily included). | ### ElementRef Reference to an element within a snapshot. | Field | # | Type | Description | | --- | --- | --- | --- | | `snapshot_id` | 1 | `string` | Snapshot the element id came from. | | `element_id` | 2 | `string` | Element id. | ### ActRequest Request for `AccessibilityService.Act`. | Field | # | Type | Description | | --- | --- | --- | --- | | `element` | 1 | [`ElementRef`](#elementref) | Which element. | | `action` | 2 | [`AccessibilityAction`](#accessibilityaction) | Which action. | | `value` | 3 | `string` | New value for `ACCESSIBILITY_ACTION_SET_VALUE`. | | `custom_action` | 4 | `string` | Native action name for `ACCESSIBILITY_ACTION_CUSTOM`. | | `delivery` | 5 | [`Delivery`](</docs/cua-sdk/reference/protocol/env-common#delivery>) | Delivery mode. Accessibility actions are background-safe on most platforms; `DELIVERY_FOREGROUND` activates the window first. | ### ActResponse Response for `AccessibilityService.Act`. | Field | # | Type | Description | | --- | --- | --- | --- | | `report` | 1 | [`DeliveryReport`](</docs/cua-sdk/reference/protocol/env-common#deliveryreport>) | What actually happened. | ### AccessibilityAction Accessibility action. | Value | # | Description | | --- | --- | --- | | `ACCESSIBILITY_ACTION_UNSPECIFIED` | 0 | Not set. Rejected. | | `ACCESSIBILITY_ACTION_PRESS` | 1 | Press / invoke / click. | | `ACCESSIBILITY_ACTION_FOCUS` | 2 | Move keyboard focus to the element. | | `ACCESSIBILITY_ACTION_SET_VALUE` | 3 | Replace the element's value with `ActRequest.value`. | | `ACCESSIBILITY_ACTION_INCREMENT` | 4 | Increment (sliders, steppers). | | `ACCESSIBILITY_ACTION_DECREMENT` | 5 | Decrement. | | `ACCESSIBILITY_ACTION_SHOW_MENU` | 6 | Open the element's context menu. | | `ACCESSIBILITY_ACTION_EXPAND` | 7 | Expand (tree items, disclosure triangles, combo boxes). | | `ACCESSIBILITY_ACTION_COLLAPSE` | 8 | Collapse. | | `ACCESSIBILITY_ACTION_SELECT` | 9 | Select (list items, tabs). | | `ACCESSIBILITY_ACTION_SCROLL_INTO_VIEW` | 10 | Scroll the element into view. | | `ACCESSIBILITY_ACTION_CUSTOM` | 11 | A platform-specific action named by `ActRequest.custom_action`. | --- # Streams and presence cua.env.v1 StreamService and PresenceService: media sessions and multiplayer cursors. Media sessions (video and audio over the media wire, see `libs/cua/proto/MEDIA.md`) and presence for shared sessions. Source: `libs/cua/proto/cua/env/v1/stream.proto`, `libs/cua/proto/cua/env/v1/presence.proto`. ## StreamService `cua.env.v1.StreamService` Control plane for video streaming. Video never travels over gRPC. This service discovers targets, negotiates codec and limits, and mints a short-lived media ticket. The client then opens the media plane (rcdp wire v2, see `libs/cua/proto/MEDIA.md`) on the `/media` WebSocket of the same port, or on direct QUIC, authenticating with the ticket. The ticket is bound to one media session and target and is safe to put in a URL; it is not the root token. Audio is a first-class track next to video: a media session may carry zero or one video track plus any number of downlink audio tracks (desktop mix or per-app), and optionally one uplink audio track (client microphone into a guest virtual source). All tracks share one media clock; see MEDIA.md §12. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`ListTargets`](#streamservicelisttargets) | [`ListTargetsRequest`](#listtargetsrequest) | [`ListTargetsResponse`](#listtargetsresponse) | unary | | [`OpenMedia`](#streamserviceopenmedia) | [`OpenMediaRequest`](#openmediarequest) | [`OpenMediaResponse`](#openmediaresponse) | unary | | [`SetPreferences`](#streamservicesetpreferences) | [`SetPreferencesRequest`](#setpreferencesrequest) | [`SetPreferencesResponse`](#setpreferencesresponse) | unary | | [`RequestKeyframe`](#streamservicerequestkeyframe) | [`RequestKeyframeRequest`](#requestkeyframerequest) | [`RequestKeyframeResponse`](#requestkeyframeresponse) | unary | | [`CloseMedia`](#streamserviceclosemedia) | [`CloseMediaRequest`](#closemediarequest) | [`CloseMediaResponse`](#closemediaresponse) | unary | ### StreamService.ListTargets `/cua.env.v1.StreamService/ListTargets`, unary: [`ListTargetsRequest`](#listtargetsrequest) to [`ListTargetsResponse`](#listtargetsresponse). Lists streamable targets: one desktop target per display, plus windows. ### StreamService.OpenMedia `/cua.env.v1.StreamService/OpenMedia`, unary: [`OpenMediaRequest`](#openmediarequest) to [`OpenMediaResponse`](#openmediaresponse). Creates a media session for a target and returns how to attach to it. ### StreamService.SetPreferences `/cua.env.v1.StreamService/SetPreferences`, unary: [`SetPreferencesRequest`](#setpreferencesrequest) to [`SetPreferencesResponse`](#setpreferencesresponse). Changes frame rate, size or bitrate of an open media session. ### StreamService.RequestKeyframe `/cua.env.v1.StreamService/RequestKeyframe`, unary: [`RequestKeyframeRequest`](#requestkeyframerequest) to [`RequestKeyframeResponse`](#requestkeyframeresponse). Asks the encoder for a keyframe (IDR) on the next frame. ### StreamService.CloseMedia `/cua.env.v1.StreamService/CloseMedia`, unary: [`CloseMediaRequest`](#closemediarequest) to [`CloseMediaResponse`](#closemediaresponse). Closes a media session and revokes its ticket. ## PresenceService `cua.env.v1.PresenceService` Multi-user presence: who is looking at the guest and where their cursors are. Agent cursors are published automatically when an agent principal drives `ComputerService` or cua-driver tools. Cursor updates are unary (`UpdateCursor`) so browsers on gRPC-Web can send them; events arrive on the `Join` server stream. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`Join`](#presenceservicejoin) | [`JoinRequest`](#joinrequest) | [`JoinResponse`](#joinresponse) | server stream | | [`UpdateCursor`](#presenceserviceupdatecursor) | [`UpdateCursorRequest`](#updatecursorrequest) | [`UpdateCursorResponse`](#updatecursorresponse) | unary | | [`Leave`](#presenceserviceleave) | [`LeaveRequest`](#leaverequest) | [`LeaveResponse`](#leaveresponse) | unary | ### PresenceService.Join `/cua.env.v1.PresenceService/Join`, server stream: [`JoinRequest`](#joinrequest) to [`JoinResponse`](#joinresponse). Joins presence and streams events until `Leave` or disconnect. The first event is `PresenceJoined` with the caller's participant id and the current roster. ### PresenceService.UpdateCursor `/cua.env.v1.PresenceService/UpdateCursor`, unary: [`UpdateCursorRequest`](#updatecursorrequest) to [`UpdateCursorResponse`](#updatecursorresponse). Publishes the caller's cursor. ### PresenceService.Leave `/cua.env.v1.PresenceService/Leave`, unary: [`LeaveRequest`](#leaverequest) to [`LeaveResponse`](#leaveresponse). Leaves presence and ends the caller's `Join` stream. ## Types in `cua/env/v1/stream.proto` ### MediaTarget A streamable target. | Field | # | Type | Description | | --- | --- | --- | --- | | `display_id` | 1 | `string` (oneof `target`) | A whole display ("desktop" target), by display id or "primary". | | `window` | 2 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) (oneof `target`) | A single window. | ### StreamTarget One entry of `ListTargetsResponse`. | Field | # | Type | Description | | --- | --- | --- | --- | | `display` | 1 | [`Display`](</docs/cua-sdk/reference/protocol/env-common#display>) (oneof `target`) | A desktop target. | | `window` | 2 | [`WindowInfo`](</docs/cua-sdk/reference/protocol/env-windows#windowinfo>) (oneof `target`) | A window target. | | `available` | 3 | `bool` | False when the target is listed but cannot be streamed right now. | | `limitation` | 4 | `string` | Why not, when `available` is false (for example "screen recording permission not granted"). | | `audio` | 5 | [`AudioSource`](#audiosource) | The audio source that pairs with this target: the desktop mix for a display target, the owning app's audio for a window target (or the desktop mix, with `AudioSource.limitation` set, where per-app capture is unsupported). Unset when the guest has no audio capture. | ### AudioSource A capturable audio source. | Field | # | Type | Description | | --- | --- | --- | --- | | `source_id` | 1 | `string` | Opaque id, stable while the source exists. "desktop" always names the desktop mix. | | `kind` | 2 | [`AudioSourceKind`](#audiosourcekind) | What the source captures. | | `name` | 3 | `string` | Human-readable name, for example "Desktop audio" or "Firefox". | | `app` | 4 | [`AppInfo`](</docs/cua-sdk/reference/protocol/env-windows#appinfo>) | Owning application, for `AUDIO_SOURCE_KIND_APPLICATION`. | | `windows` | 5 | repeated [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Windows of that application, for pairing with window video targets. | | `available` | 6 | `bool` | False when listed but not capturable right now. | | `limitation` | 7 | `string` | Why a source is unavailable, or degraded (for example "per-app capture needs Windows 10 2004+; falls back to desktop audio"). | | `desktop_fallback` | 8 | `bool` | True if capturing this source actually captures the desktop mix because per-app capture is unsupported. | ### ListTargetsRequest Request for `StreamService.ListTargets`. | Field | # | Type | Description | | --- | --- | --- | --- | | `include_windows` | 1 | `bool` | Include window targets. Desktop targets are always included. | | `window_filter` | 2 | [`WindowFilter`](</docs/cua-sdk/reference/protocol/env-windows#windowfilter>) | Filter for window targets. | ### ListTargetsResponse Response for `StreamService.ListTargets`. | Field | # | Type | Description | | --- | --- | --- | --- | | `targets` | 1 | repeated [`StreamTarget`](#streamtarget) | Desktop targets first (primary display first), then windows front to back. Phantom and system windows are never listed. | | `codecs` | 2 | repeated [`MediaCodec`](#mediacodec) | Codecs this driver can produce, in its preference order. | | `audio_sources` | 3 | repeated [`AudioSource`](#audiosource) | Every capturable audio source: the desktop mix first, then per-app sources. Empty when the guest has no audio capture. | | `audio_codecs` | 4 | repeated [`AudioCodec`](#audiocodec) | Audio codecs this driver can produce, in its preference order. | | `audio_uplink_allowed` | 5 | `bool` | True if this caller may open an audio uplink (feature "audio.uplink" is supported and the caller's principal is allowed). | | `audio_uplink_limitation` | 6 | `string` | Why the uplink is not allowed, when it is not. | ### AudioEncoding Audio encoding parameters shared by downlink and uplink requests. Zero and unset values select the defaults noted per field. | Field | # | Type | Description | | --- | --- | --- | --- | | `codecs` | 1 | repeated [`AudioCodec`](#audiocodec) | Acceptable codecs in preference order. Empty means Opus, then PCM. | | `sample_rate_hz` | 2 | `uint32` | Sample rate in Hz. 0 means 48000. Opus accepts 8000, 12000, 16000, 24000 and 48000. | | `channels` | 3 | `uint32` | Channel count, 1 or 2. 0 means 2 (downlink) or 1 (uplink). | | `bitrate_kbps` | 4 | `uint32` | Opus target bitrate in kbps (6 to 510). 0 means 64 for stereo, 32 for mono. Ignored for PCM. | | `fec` | 5 | optional `bool` | Opus in-band forward error correction. Unset means on. | | `dtx` | 6 | optional `bool` | Opus discontinuous transmission (send almost nothing during silence). Unset means on. | | `frame_ms` | 7 | `uint32` | Frame duration in milliseconds: 10, 20, 40 or 60. 0 means 20. Shorter frames lower latency at a higher packet rate. | | `expected_loss_percent` | 8 | `uint32` | Expected packet loss in percent (0 to 100), tuning the Opus FEC redundancy. 0 means 10 when `fec` is on. | ### AudioOptions Audio options for `OpenMediaRequest`. | Field | # | Type | Description | | --- | --- | --- | --- | | `enabled` | 1 | `bool` | Enable downlink audio. When false, no audio tracks are sent and the other fields except `uplink` are ignored. | | `source_ids` | 2 | repeated `string` | Sources to send, one track each, by `AudioSource.source_id`. Empty means the source that pairs with the video target (`StreamTarget.audio`). | | `encoding` | 3 | [`AudioEncoding`](#audioencoding) | Downlink encoding. | | `uplink` | 4 | [`AudioUplinkOptions`](#audiouplinkoptions) | Optional microphone uplink. | ### AudioUplinkOptions Client-to-guest audio (for example the user's microphone), played into a guest virtual audio source so apps inside can "hear" the user. Off by default and gated separately from video and input: it requires the "audio.uplink" feature, a session policy other than `SESSION_POLICY_VIEW_ONLY`, and the caller's principal to be allowed by `InitRequest.audio_uplink`. A denied uplink does not fail `OpenMedia`; the response reports why in `AudioUplinkGrant`. | Field | # | Type | Description | | --- | --- | --- | --- | | `enabled` | 1 | `bool` | Request an uplink track. | | `encoding` | 2 | [`AudioEncoding`](#audioencoding) | Encoding the client will send. | | `virtual_source_name` | 3 | `string` | Name of the guest virtual source (microphone) to create or reuse. Empty means "cua-uplink". | | `set_default_input` | 4 | `bool` | Make the virtual source the guest's default input device while the uplink is active. | ### OpenMediaRequest Request for `StreamService.OpenMedia`. | Field | # | Type | Description | | --- | --- | --- | --- | | `target` | 1 | [`MediaTarget`](#mediatarget) | What to stream. | | `codecs` | 2 | repeated [`MediaCodec`](#mediacodec) | Acceptable codecs in client preference order. Empty means any. | | `max_fps` | 3 | `uint32` | Maximum frames per second. 0 means 30. | | `max_dimension` | 4 | `uint32` | Maximum long edge in pixels. 0 means native. | | `bitrate_kbps` | 5 | `uint32` | Target H.264 bitrate in kbps (250 to 100000). 0 means encoder default. | | `policy` | 6 | [`SessionPolicy`](#sessionpolicy) | Input policy for clients attached to this session. | | `geometry_control` | 7 | [`GeometryControl`](#geometrycontrol) | Geometry control. | | `ticket_ttl` | 8 | `google.protobuf.Duration` | Ticket lifetime for attaching. Unset means 60 seconds; at most 10 minutes. The ticket only has to be valid when the socket attaches; an attached socket stays open past expiry. | | `prefer_quic` | 9 | `bool` | Include `OpenMediaResponse.quic` when direct QUIC media is available. | | `audio` | 10 | [`AudioOptions`](#audiooptions) | Audio tracks. Unset means no audio (the v2.0 behavior). | | `disable_video` | 11 | `bool` | Open an audio-only session: no video track is produced. `target` still selects the paired audio source when `audio.source_ids` is empty. | | `presence_participant_id` | 12 | `string` | The viewer's presence participant, its input's owner (PRESENCE.md §7). | ### AudioTrack One negotiated downlink audio track. | Field | # | Type | Description | | --- | --- | --- | --- | | `track_id` | 1 | `uint32` | Track id carried in every audio packet header (MEDIA.md §12). Unique within the media session; never 0. | | `source` | 2 | [`AudioSource`](#audiosource) | The source it captures. | ### NegotiatedAudioEncoding Negotiated audio encoding. | Field | # | Type | Description | | --- | --- | --- | --- | | `codec` | 1 | [`AudioCodec`](#audiocodec) | Selected codec. | | `sample_rate_hz` | 2 | `uint32` | Sample rate in Hz. | | `channels` | 3 | `uint32` | Channel count. | | `bitrate_kbps` | 4 | `uint32` | Target bitrate in kbps (0 for PCM). | | `fec` | 5 | `bool` | In-band FEC on. | | `dtx` | 6 | `bool` | DTX on. | | `frame_ms` | 7 | `uint32` | Frame duration in milliseconds. | ### AudioUplinkGrant Outcome of an uplink request. | Field | # | Type | Description | | --- | --- | --- | --- | | `granted` | 1 | `bool` | True if the uplink is open. | | `denied_reason` | 2 | `string` | Why not, when denied (for example "principal not allowed", "view_only session", "no virtual audio device"). | | `track_id` | 3 | `uint32` | Track id the client must put in uplink packet headers. | | `encoding` | 4 | [`NegotiatedAudioEncoding`](#negotiatedaudioencoding) | Encoding the server expects. | | `virtual_source_name` | 5 | `string` | Name of the guest virtual source the audio is played into. | ### NegotiatedAudio Negotiated audio for a media session. | Field | # | Type | Description | | --- | --- | --- | --- | | `tracks` | 1 | repeated [`AudioTrack`](#audiotrack) | Downlink tracks, in the order requested. | | `encoding` | 2 | [`NegotiatedAudioEncoding`](#negotiatedaudioencoding) | Downlink encoding, shared by every downlink track. | | `uplink` | 3 | [`AudioUplinkGrant`](#audiouplinkgrant) | Uplink outcome, when an uplink was requested. | ### QuicEndpoint Direct QUIC media endpoint. Only reachable when the client can reach the guest's UDP port directly (not through the Fleet gateway or relay). | Field | # | Type | Description | | --- | --- | --- | --- | | `port` | 1 | `uint32` | UDP port (normally 3212). | | `certificate_sha256` | 2 | `string` | Lowercase hex SHA-256 of the server's self-signed certificate (DER), to pin. | | `alpn` | 3 | `string` | ALPN protocol id, "rcdp/2". | ### MediaGeometry Geometry of a media session's frames. | Field | # | Type | Description | | --- | --- | --- | --- | | `frame_size` | 1 | [`PixelSize`](</docs/cua-sdk/reference/protocol/env-common#pixelsize>) | Frame size in pixels. | | `scale` | 2 | `double` | Frame pixels per logical point of the target. | | `logical_bounds` | 3 | [`Rect`](</docs/cua-sdk/reference/protocol/env-common#rect>) | Target bounds in global logical points when the session opened. | | `geometry_epoch` | 4 | `uint64` | Geometry generation; matches `geometry_epoch` on the media plane. | ### OpenMediaResponse Response for `StreamService.OpenMedia`. | Field | # | Type | Description | | --- | --- | --- | --- | | `media_session_id` | 1 | `string` | Media session id. | | `ticket` | 2 | `string` | Bearer ticket for the media socket. | | `ticket_expires_at` | 3 | `google.protobuf.Timestamp` | When the ticket stops being accepted for new attaches. | | `ws_path` | 4 | `string` | WebSocket path relative to the spacesd base URL, including the ticket, for example "/media?ticket=…". Works through the Fleet gateway and the relay. | | `quic` | 5 | [`QuicEndpoint`](#quicendpoint) | Direct QUIC endpoint, when requested and available. | | `codec` | 6 | [`MediaCodec`](#mediacodec) | Negotiated codec. | | `max_fps` | 7 | `uint32` | Effective frame rate cap. | | `max_dimension` | 8 | `uint32` | Effective long-edge cap. | | `bitrate_kbps` | 9 | `uint32` | Effective bitrate in kbps, 0 when not applicable. | | `policy` | 10 | [`SessionPolicy`](#sessionpolicy) | Effective policy (may be lower than requested). | | `geometry_control` | 11 | [`GeometryControl`](#geometrycontrol) | Effective geometry control. | | `geometry` | 12 | [`MediaGeometry`](#mediageometry) | Initial geometry. | | `wire_version` | 13 | `uint32` | Media wire protocol version. 2 for this contract. | | `audio` | 14 | [`NegotiatedAudio`](#negotiatedaudio) | Negotiated audio. Unset when audio was not requested or is not available (see the "audio.*" capabilities). | ### SetPreferencesRequest Request for `StreamService.SetPreferences`. | Field | # | Type | Description | | --- | --- | --- | --- | | `media_session_id` | 1 | `string` | Which session. | | `max_fps` | 2 | `uint32` | New frame rate cap. 0 keeps the current value. | | `max_dimension` | 3 | `uint32` | New long-edge cap. 0 keeps the current value. | | `bitrate_kbps` | 4 | `uint32` | New bitrate in kbps. 0 keeps the current value. | | `audio_enabled` | 5 | optional `bool` | Pause (false) or resume (true) all downlink audio tracks. Unset keeps the current state. Paused tracks send nothing; the session keeps its track ids. | | `audio_bitrate_kbps` | 6 | `uint32` | New Opus bitrate in kbps. 0 keeps the current value. | | `audio_fec` | 7 | optional `bool` | Turn Opus FEC on or off. Unset keeps the current value. | | `audio_dtx` | 8 | optional `bool` | Turn Opus DTX on or off. Unset keeps the current value. | | `audio_frame_ms` | 9 | `uint32` | New frame duration in milliseconds (10, 20, 40 or 60). 0 keeps the current value. Takes effect with a new audio config epoch. | | `audio_expected_loss_percent` | 10 | optional `uint32` | New expected loss percentage for FEC tuning. Unset keeps the current value. | | `audio_uplink_muted` | 11 | optional `bool` | Mute (true) or unmute (false) the uplink's virtual source. Unset keeps the current state. | ### SetPreferencesResponse Response for `StreamService.SetPreferences`. When this returns, capture has been reconfigured; frames from the old configuration may still be in flight and are distinguished by `codec_epoch` on the media plane. | Field | # | Type | Description | | --- | --- | --- | --- | | `max_fps` | 1 | `uint32` | Effective frame rate cap. | | `max_dimension` | 2 | `uint32` | Effective long-edge cap. | | `bitrate_kbps` | 3 | `uint32` | Effective bitrate in kbps. | | `audio_enabled` | 4 | `bool` | True if downlink audio is flowing (not paused). | | `audio_encoding` | 5 | [`NegotiatedAudioEncoding`](#negotiatedaudioencoding) | Effective downlink audio encoding. Unset when the session has no audio. | | `audio_uplink_muted` | 6 | `bool` | True if the uplink's virtual source is muted. | ### RequestKeyframeRequest Request for `StreamService.RequestKeyframe`. | Field | # | Type | Description | | --- | --- | --- | --- | | `media_session_id` | 1 | `string` | Which session. | ### RequestKeyframeResponse Response for `StreamService.RequestKeyframe`. No fields. ### CloseMediaRequest Request for `StreamService.CloseMedia`. | Field | # | Type | Description | | --- | --- | --- | --- | | `media_session_id` | 1 | `string` | Which session. | ### CloseMediaResponse Response for `StreamService.CloseMedia`. No fields. ### MediaCodec Video codec on the media plane. | Value | # | Description | | --- | --- | --- | | `MEDIA_CODEC_UNSPECIFIED` | 0 | Not set. | | `MEDIA_CODEC_H264` | 1 | H.264, one Annex B access unit per packet; keyframes carry SPS/PPS. | | `MEDIA_CODEC_BGRA` | 2 | Raw top-down BGRA, tightly packed. Loopback / debugging only. | | `MEDIA_CODEC_PNG` | 3 | One PNG per frame. Fallback when no encoder is available. | ### SessionPolicy What a media session's attached clients may do to the target. | Value | # | Description | | --- | --- | --- | | `SESSION_POLICY_UNSPECIFIED` | 0 | Not set. Treated as `SESSION_POLICY_VIEW_ONLY`. | | `SESSION_POLICY_VIEW_ONLY` | 1 | Video only; interactive input is refused. | | `SESSION_POLICY_BACKGROUND_ONLY` | 2 | Input allowed, but only background delivery (never activates or moves the real pointer). | | `SESSION_POLICY_ALLOW_ACTIVATION` | 3 | Input allowed and may activate the target. | ### GeometryControl Whether a session may resize its target window to fit the viewer. | Value | # | Description | | --- | --- | --- | | `GEOMETRY_CONTROL_UNSPECIFIED` | 0 | Not set. Treated as `GEOMETRY_CONTROL_OBSERVE_ONLY`. | | `GEOMETRY_CONTROL_OBSERVE_ONLY` | 1 | Never resize the target. | | `GEOMETRY_CONTROL_BIDIRECTIONAL` | 2 | Viewer may resize the target. Exclusive per target: a second bidirectional session fails with FAILED_PRECONDITION instead of stealing the lease. Window targets only. | ### AudioCodec Audio codec on the media plane. | Value | # | Description | | --- | --- | --- | | `AUDIO_CODEC_UNSPECIFIED` | 0 | Not set. | | `AUDIO_CODEC_OPUS` | 1 | Opus (RFC 6716), one Opus packet per media packet. The default. | | `AUDIO_CODEC_PCM_S16LE` | 2 | Raw interleaved signed 16-bit little-endian PCM. Lossless, for debugging and tests; roughly 1.5 Mbit/s for 48 kHz stereo. | ### AudioSourceKind What an audio source captures. | Value | # | Description | | --- | --- | --- | | `AUDIO_SOURCE_KIND_UNSPECIFIED` | 0 | Not reported. | | `AUDIO_SOURCE_KIND_DESKTOP` | 1 | Everything the guest plays (the default output's mix). | | `AUDIO_SOURCE_KIND_APPLICATION` | 2 | Audio of one application only. | ## Types in `cua/env/v1/presence.proto` ### Participant A participant. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant_id` | 1 | `string` | Server-assigned participant id, unique per `Join`. | | `principal` | 2 | [`Principal`](</docs/cua-sdk/reference/protocol/env-common#principal>) | Who it is. `color` is the server-assigned color, which may differ from the requested one. | | `joined_at` | 3 | `google.protobuf.Timestamp` | When it joined. | ### CursorPosition Where a cursor is. | Field | # | Type | Description | | --- | --- | --- | --- | | `display_id` | 1 | `string` | Target the cursor is over. Unset means the primary desktop. | | `window` | 2 | [`WindowRef`](</docs/cua-sdk/reference/protocol/env-common#windowref>) | Window the cursor is over, when the participant is viewing a window stream. | | `position` | 3 | [`Point`](</docs/cua-sdk/reference/protocol/env-common#point>) | Position normalized to [0, 1] within the window (if set) or the display, so viewers at different scales agree. | | `visible` | 4 | `bool` | False hides the cursor (for example the pointer left the viewer). | | `shape` | 5 | [`CursorShape`](#cursorshape) | The pointer shape the guest would show at `position`, computed by the server for participants that joined with `JoinRequest.cursor_shapes`. Ignored in `UpdateCursorRequest`. `CURSOR_SHAPE_UNSPECIFIED` (older daemons, or not probed yet) is drawn as the arrow. | | `shape_source` | 6 | [`CursorShapeSource`](#cursorshapesource) | How `shape` was determined. | ### JoinRequest Request for `PresenceService.Join`. | Field | # | Type | Description | | --- | --- | --- | --- | | `principal` | 1 | [`Principal`](</docs/cua-sdk/reference/protocol/env-common#principal>) | Who is joining. Overrides the `x-cua-principal-bin` metadata. | | `keepalive_interval` | 2 | `google.protobuf.Duration` | Interval between `keepalive` events. Unset means 30 seconds. | | `cursor_shapes` | 3 | `bool` | Asks the server to compute cursor shapes (`CursorPosition.shape`) and send `CursorShapeChanged`. Honored only when the capability "presence.cursor_shape" is supported; otherwise shapes stay unspecified. | | `roster_interval` | 4 | `google.protobuf.Duration` | Interval between `RosterHeartbeat` events, which list everyone present so a client can drop a participant whose leave it missed. Unset or zero means no heartbeats. Clamped to at least one second. | | `cursor_batches` | 5 | `bool` | Asks for one `CursorBatch` per server tick, carrying every cursor that moved in it, instead of one `CursorMoved` per cursor. | | `cursor_datagrams` | 6 | `bool` | Asks for a presence datagram channel (`PresenceJoined.datagrams`): cursors as unreliable, latest-wins QUIC datagrams in both directions, with this stream kept for everything else. Ignored when the server has no QUIC listener; the stream then carries cursors as before. | ### PresenceJoined First event of a `Join` stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant` | 1 | [`Participant`](#participant) | The caller. | | `roster` | 2 | repeated [`ParticipantCursor`](#participantcursor) | Everyone else currently present, with their last known cursors. | | `datagrams` | 3 | [`PresenceDatagrams`](#presencedatagrams) | The presence datagram channel, when requested and available. See `libs/cua/proto/PRESENCE.md`. | ### PresenceDatagrams Where and how to open the presence datagram channel. | Field | # | Type | Description | | --- | --- | --- | --- | | `endpoint` | 1 | [`QuicEndpoint`](#quicendpoint) | The QUIC listener (normally UDP 3212); `alpn` is "cua-presence/1". | | `ticket` | 2 | `string` | Single-use credential binding the QUIC connection to this participant. | | `tick` | 3 | `google.protobuf.Duration` | Server cursor tick on the channel (normally 33 ms). | ### ParticipantCursor A participant and their cursor. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant` | 1 | [`Participant`](#participant) | The participant. | | `cursor` | 2 | [`CursorPosition`](#cursorposition) | Their last cursor, unset if never published. | ### ParticipantLeft A participant left. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant_id` | 1 | `string` | Their participant id. | | `reason` | 2 | [`LeaveReason`](#leavereason) | Why. | ### RosterHeartbeat Everyone present, sent every `JoinRequest.roster_interval`. A participant missing from it has left. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant_ids` | 1 | repeated `string` | Participant ids present, the recipient included. | | `at` | 2 | `google.protobuf.Timestamp` | When the server sent it. | ### CursorBatch Every cursor that moved in one server tick. | Field | # | Type | Description | | --- | --- | --- | --- | | `moves` | 1 | repeated [`CursorMoved`](#cursormoved) | The moves, at most one per participant. | | `tick` | 2 | `uint64` | Server tick number. Ticks are the server's cursor interval apart; a gap means ticks without moves. | | `at` | 3 | `google.protobuf.Timestamp` | When the server sent it. | ### CursorMoved A participant's cursor moved. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant_id` | 1 | `string` | Whose cursor. | | `cursor` | 2 | [`CursorPosition`](#cursorposition) | New position. | | `at` | 3 | `google.protobuf.Timestamp` | When the server received it. | ### CursorShapeChanged A participant's cursor shape changed. Sent to every subscriber that asked for shapes, including the participant itself, so each client can draw its own cursor with the guest's shape. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant_id` | 1 | `string` | Whose cursor. | | `shape` | 2 | [`CursorShape`](#cursorshape) | New shape. | | `source` | 3 | [`CursorShapeSource`](#cursorshapesource) | How it was determined. | | `at` | 4 | `google.protobuf.Timestamp` | When the server determined it. | ### JoinResponse One message of the `PresenceService.Join` stream. | Field | # | Type | Description | | --- | --- | --- | --- | | `joined` | 1 | [`PresenceJoined`](#presencejoined) (oneof `event`) | First event. | | `participant_joined` | 2 | [`Participant`](#participant) (oneof `event`) | Someone joined. | | `participant_left` | 3 | [`ParticipantLeft`](#participantleft) (oneof `event`) | Someone left. | | `cursor_moved` | 4 | [`CursorMoved`](#cursormoved) (oneof `event`) | Someone's cursor moved. Coalesced to at most 30 per second per participant. | | `keepalive` | 5 | [`KeepAlive`](</docs/cua-sdk/reference/protocol/env-common#keepalive>) (oneof `event`) | Idle stream heartbeat. | | `cursor_shape_changed` | 6 | [`CursorShapeChanged`](#cursorshapechanged) (oneof `event`) | Someone's cursor shape changed (only with `JoinRequest.cursor_shapes`). | | `roster_heartbeat` | 7 | [`RosterHeartbeat`](#rosterheartbeat) (oneof `event`) | Everyone present (only with `JoinRequest.roster_interval`). | | `cursor_batch` | 8 | [`CursorBatch`](#cursorbatch) (oneof `event`) | Cursors that moved in one tick (only with `JoinRequest.cursor_batches`). | ### UpdateCursorRequest Request for `PresenceService.UpdateCursor`. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant_id` | 1 | `string` | The caller's participant id from `PresenceJoined.participant`. | | `cursor` | 2 | [`CursorPosition`](#cursorposition) | New position. | ### UpdateCursorResponse Response for `PresenceService.UpdateCursor`. No fields. ### LeaveRequest Request for `PresenceService.Leave`. | Field | # | Type | Description | | --- | --- | --- | --- | | `participant_id` | 1 | `string` | The caller's participant id. | ### LeaveResponse Response for `PresenceService.Leave`. No fields. ### CursorShape A pointer shape, in a small portable vocabulary. Clients draw their own art for each value in the participant's color. | Value | # | Description | | --- | --- | --- | | `CURSOR_SHAPE_UNSPECIFIED` | 0 | Unknown or not probed. Draw the arrow. | | `CURSOR_SHAPE_ARROW` | 1 | The ordinary arrow. | | `CURSOR_SHAPE_TEXT` | 2 | Text insertion (I-beam). | | `CURSOR_SHAPE_POINTER` | 3 | A link or other clickable control (pointing hand). | | `CURSOR_SHAPE_RESIZE_NS` | 4 | Resize along the vertical axis (top or bottom edge). | | `CURSOR_SHAPE_RESIZE_EW` | 5 | Resize along the horizontal axis (left or right edge). | | `CURSOR_SHAPE_RESIZE_NESW` | 6 | Resize along the rising diagonal (top-right or bottom-left corner). | | `CURSOR_SHAPE_RESIZE_NWSE` | 7 | Resize along the falling diagonal (top-left or bottom-right corner). | | `CURSOR_SHAPE_WAIT` | 8 | Busy; input is not accepted. | | `CURSOR_SHAPE_PROGRESS` | 9 | Working in the background; input is still accepted. | | `CURSOR_SHAPE_NOT_ALLOWED` | 10 | The action is not permitted here. | | `CURSOR_SHAPE_CROSSHAIR` | 11 | Precise selection. | | `CURSOR_SHAPE_GRAB` | 12 | Open hand over something draggable. | | `CURSOR_SHAPE_GRABBING` | 13 | Closed hand while dragging. | | `CURSOR_SHAPE_MOVE` | 14 | Move in any direction. | ### CursorShapeSource How a cursor shape was determined. | Value | # | Description | | --- | --- | --- | | `CURSOR_SHAPE_SOURCE_UNSPECIFIED` | 0 | Not reported. | | `CURSOR_SHAPE_SOURCE_HIT_TEST` | 1 | Accessibility hit-test at the participant's position (role of the element under the point, window edges). Never moves the guest pointer. | | `CURSOR_SHAPE_SOURCE_SYSTEM` | 2 | The guest's real cursor, read while this participant's input is what positioned the pointer. | | `CURSOR_SHAPE_SOURCE_PROBE` | 3 | The guest's real cursor, read by briefly moving the idle pointer to the participant's position and restoring it (see `PresenceSettings`). | ### LeaveReason Why a participant left. | Value | # | Description | | --- | --- | --- | | `LEAVE_REASON_UNSPECIFIED` | 0 | Not reported. | | `LEAVE_REASON_LEFT` | 1 | It called `Leave`. | | `LEAVE_REASON_DISCONNECTED` | 2 | Its `Join` stream ended. | | `LEAVE_REASON_TIMEOUT` | 3 | No activity within the server's timeout (agents without a stream). | | `LEAVE_REASON_RUN_ENDED` | 4 | The agent run or driver session that owned the cursor ended. | --- # Teleport, tunnels and the volume cua.env.v1 TeleportService, TunnelService and VolumeService: receiving teleported sessions and files, port forwards, hotspots and the guest mount of Cua Volume. The receiving side of teleport (sending lives in the cua SDK), TCP port forwards, the reverse-SOCKS hotspot, and the guest mount of Cua Volume served by the client. Source: `libs/cua/proto/cua/env/v1/teleport.proto`, `libs/cua/proto/cua/env/v1/tunnel.proto`, `libs/cua/proto/cua/env/v1/volume.proto`. ## TeleportService `cua.env.v1.TeleportService` Receiving side of "teleport": moving an app session (browser profile cookies and storage, editor state) or a set of files from a user's machine into the guest. The approval gate (biometric or explicit user consent for sensitive items) lives on the sending side, in the SDK; the guest never pulls. All uploads are unary chunks so browsers on gRPC-Web can send them. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`GetManifest`](#teleportservicegetmanifest) | [`GetManifestRequest`](#getmanifestrequest) | [`GetManifestResponse`](#getmanifestresponse) | unary | | [`ImportSession`](#teleportserviceimportsession) | [`ImportSessionRequest`](#importsessionrequest) | [`ImportSessionResponse`](#importsessionresponse) | unary | | [`BeginReceiveFiles`](#teleportservicebeginreceivefiles) | [`BeginReceiveFilesRequest`](#beginreceivefilesrequest) | [`BeginReceiveFilesResponse`](#beginreceivefilesresponse) | unary | | [`ReceiveFilesChunk`](#teleportservicereceivefileschunk) | [`ReceiveFilesChunkRequest`](#receivefileschunkrequest) | [`ReceiveFilesChunkResponse`](#receivefileschunkresponse) | unary | | [`CommitReceiveFiles`](#teleportservicecommitreceivefiles) | [`CommitReceiveFilesRequest`](#commitreceivefilesrequest) | [`CommitReceiveFilesResponse`](#commitreceivefilesresponse) | unary | | [`AbortReceiveFiles`](#teleportserviceabortreceivefiles) | [`AbortReceiveFilesRequest`](#abortreceivefilesrequest) | [`AbortReceiveFilesResponse`](#abortreceivefilesresponse) | unary | | [`WipeImport`](#teleportservicewipeimport) | [`WipeImportRequest`](#wipeimportrequest) | [`WipeImportResponse`](#wipeimportresponse) | unary | ### TeleportService.GetManifest `/cua.env.v1.TeleportService/GetManifest`, unary: [`GetManifestRequest`](#getmanifestrequest) to [`GetManifestResponse`](#getmanifestresponse). Describes what the guest can accept for an app: whether the provider exists, whether the app is installed or running, and which bundle format version to send. ### TeleportService.ImportSession `/cua.env.v1.TeleportService/ImportSession`, unary: [`ImportSessionRequest`](#importsessionrequest) to [`ImportSessionResponse`](#importsessionresponse). Uploads an rcdp-app-session bundle in offset-addressed chunks and, on the final chunk, verifies and imports it. Resumable: resend from the returned `received_bytes`. ### TeleportService.BeginReceiveFiles `/cua.env.v1.TeleportService/BeginReceiveFiles`, unary: [`BeginReceiveFilesRequest`](#beginreceivefilesrequest) to [`BeginReceiveFilesResponse`](#beginreceivefilesresponse). Starts a file transfer into the guest's Downloads folder by declaring its manifest. Entries matching ignore rules are dropped here. ### TeleportService.ReceiveFilesChunk `/cua.env.v1.TeleportService/ReceiveFilesChunk`, unary: [`ReceiveFilesChunkRequest`](#receivefileschunkrequest) to [`ReceiveFilesChunkResponse`](#receivefileschunkresponse). Uploads one chunk of one declared file. ### TeleportService.CommitReceiveFiles `/cua.env.v1.TeleportService/CommitReceiveFiles`, unary: [`CommitReceiveFilesRequest`](#commitreceivefilesrequest) to [`CommitReceiveFilesResponse`](#commitreceivefilesresponse). Verifies every file's SHA-256 and publishes the transfer atomically. ### TeleportService.AbortReceiveFiles `/cua.env.v1.TeleportService/AbortReceiveFiles`, unary: [`AbortReceiveFilesRequest`](#abortreceivefilesrequest) to [`AbortReceiveFilesResponse`](#abortreceivefilesresponse). Discards a transfer. ### TeleportService.WipeImport `/cua.env.v1.TeleportService/WipeImport`, unary: [`WipeImportRequest`](#wipeimportrequest) to [`WipeImportResponse`](#wipeimportresponse). Removes what a committed import left in the guest: every file and directory it created and every Keychain item it installed, as recorded in the import's ledger. Guests that support it advertise the feature "teleport.wipe". An unknown `import_id` is `NOT_FOUND`. ## TunnelService `cua.env.v1.TunnelService` Network tunnels between a client and the guest, carried over WebSocket upgrades on the spacesd port so they work through the Fleet gateway, the relay and gRPC-Web-only networks. - `Forward`: client-to-guest TCP. Each WebSocket connection to `ws_path` becomes one TCP connection to the guest port; binary frames carry the byte stream in both directions. - Hotspot: guest-to-client egress (reverse SOCKS5). A SOCKS5 listener on guest loopback relays each outbound connection over the client's WebSocket, and the client dials it from its own network. Replaces the unauthenticated rcdp-socks. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`Forward`](#tunnelserviceforward) | [`ForwardRequest`](#forwardrequest) | [`ForwardResponse`](#forwardresponse) | unary | | [`ListForwards`](#tunnelservicelistforwards) | [`ListForwardsRequest`](#listforwardsrequest) | [`ListForwardsResponse`](#listforwardsresponse) | unary | | [`CloseForward`](#tunnelservicecloseforward) | [`CloseForwardRequest`](#closeforwardrequest) | [`CloseForwardResponse`](#closeforwardresponse) | unary | | [`StartHotspot`](#tunnelservicestarthotspot) | [`StartHotspotRequest`](#starthotspotrequest) | [`StartHotspotResponse`](#starthotspotresponse) | unary | | [`StopHotspot`](#tunnelservicestophotspot) | [`StopHotspotRequest`](#stophotspotrequest) | [`StopHotspotResponse`](#stophotspotresponse) | unary | | [`GetHotspotStatus`](#tunnelservicegethotspotstatus) | [`GetHotspotStatusRequest`](#gethotspotstatusrequest) | [`GetHotspotStatusResponse`](#gethotspotstatusresponse) | unary | ### TunnelService.Forward `/cua.env.v1.TunnelService/Forward`, unary: [`ForwardRequest`](#forwardrequest) to [`ForwardResponse`](#forwardresponse). Authorizes TCP forwarding to a guest port and returns the WebSocket to connect to. ### TunnelService.ListForwards `/cua.env.v1.TunnelService/ListForwards`, unary: [`ListForwardsRequest`](#listforwardsrequest) to [`ListForwardsResponse`](#listforwardsresponse). Lists active forwards. ### TunnelService.CloseForward `/cua.env.v1.TunnelService/CloseForward`, unary: [`CloseForwardRequest`](#closeforwardrequest) to [`CloseForwardResponse`](#closeforwardresponse). Revokes a forward and closes its connections. ### TunnelService.StartHotspot `/cua.env.v1.TunnelService/StartHotspot`, unary: [`StartHotspotRequest`](#starthotspotrequest) to [`StartHotspotResponse`](#starthotspotresponse). Starts the reverse-SOCKS hotspot for the calling client. ### TunnelService.StopHotspot `/cua.env.v1.TunnelService/StopHotspot`, unary: [`StopHotspotRequest`](#stophotspotrequest) to [`StopHotspotResponse`](#stophotspotresponse). Stops the hotspot. ### TunnelService.GetHotspotStatus `/cua.env.v1.TunnelService/GetHotspotStatus`, unary: [`GetHotspotStatusRequest`](#gethotspotstatusrequest) to [`GetHotspotStatusResponse`](#gethotspotstatusresponse). Reports hotspot state and counters. ## VolumeService `cua.env.v1.VolumeService` Cua Volume in the guest: the Space's view of the user's volume, mounted at a fixed path and served by the client (the host that runs the volume). The client calls `AttachVolume` and opens the returned WebSocket (`/volume`, ticket-authenticated), which it keeps open. Once it is attached, cua-spacesd mounts the volume in the guest; the mount's traffic travels as tunnel streams over that socket (the frame format of the hotspot, `OPEN` carrying the target `nfs` or `fs`), and the client answers them from its own copy of the volume, scoped to this Space. The guest never holds storage keys and never has a network path to the client. The mount goes away when the socket closes or on `DetachVolume`. | Guest | Backend | Mount path | |---|---|---| | macOS | `nfs`: the system NFS client | `~/Cua Volume` | | Linux | `fs`: FUSE (the file operations protocol) | `/volume`, else `~/Cua Volume` | | Windows | not yet: a preview (`nfs`, Client for NFS, on `V:\`) is off unless the guest sets `CUA_VOLUME_WINDOWS_PREVIEW=1` | | On Windows a `mount_path` that is not a drive (for example `C:\Users\<user>\Cua Volume`) becomes a directory symbolic link to the drive the volume mounts on. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`AttachVolume`](#volumeserviceattachvolume) | [`AttachVolumeRequest`](#attachvolumerequest) | [`AttachVolumeResponse`](#attachvolumeresponse) | unary | | [`DetachVolume`](#volumeservicedetachvolume) | [`DetachVolumeRequest`](#detachvolumerequest) | [`DetachVolumeResponse`](#detachvolumeresponse) | unary | | [`GetVolumeStatus`](#volumeservicegetvolumestatus) | [`GetVolumeStatusRequest`](#getvolumestatusrequest) | [`GetVolumeStatusResponse`](#getvolumestatusresponse) | unary | ### VolumeService.AttachVolume `/cua.env.v1.VolumeService/AttachVolume`, unary: [`AttachVolumeRequest`](#attachvolumerequest) to [`AttachVolumeResponse`](#attachvolumeresponse). Prepares a mount and returns the WebSocket the client attaches. ### VolumeService.DetachVolume `/cua.env.v1.VolumeService/DetachVolume`, unary: [`DetachVolumeRequest`](#detachvolumerequest) to [`DetachVolumeResponse`](#detachvolumeresponse). Unmounts and closes the socket. ### VolumeService.GetVolumeStatus `/cua.env.v1.VolumeService/GetVolumeStatus`, unary: [`GetVolumeStatusRequest`](#getvolumestatusrequest) to [`GetVolumeStatusResponse`](#getvolumestatusresponse). Reports the mount. ## Types in `cua/env/v1/teleport.proto` ### GetManifestRequest Request for `TeleportService.GetManifest`. | Field | # | Type | Description | | --- | --- | --- | --- | | `app` | 1 | `string` | Provider / app id, for example "firefox", "chromium", "chrome", "vscode". Advertised as capability "teleport.<app>". | | `scope` | 2 | [`TeleportScope`](#teleportscope) | Requested scope. | ### GetManifestResponse Response for `TeleportService.GetManifest`. | Field | # | Type | Description | | --- | --- | --- | --- | | `supported` | 1 | `bool` | True if the guest has a provider for this app. | | `limitation` | 2 | `string` | Why not, when unsupported. | | `app_installed` | 3 | `bool` | True if the app is installed in the guest. | | `app_running` | 4 | `bool` | True if the app is running. Importing may require closing it. | | `bundle_version` | 5 | `uint32` | Bundle format version the sender must produce. | | `scopes` | 6 | repeated [`TeleportScope`](#teleportscope) | Scopes this provider can import. | | `supported_apps` | 7 | repeated `string` | Every app id the guest can import, for discovery. | ### ImportOptions Options applied when an imported session is committed. | Field | # | Type | Description | | --- | --- | --- | --- | | `replace_existing` | 1 | `bool` | Replace existing state instead of merging. | | `close_running_app` | 2 | `bool` | Close the app (gracefully, then forcefully after 5 seconds) if it is running. | | `launch_after` | 3 | `bool` | Launch the app after importing. | | `expires_at_ms` | 4 | `uint64` | Unix time in milliseconds at which the guest wipes the import on its own, as `WipeImport` would. 0 means no expiry. Checked every 60 seconds and when the guest daemon starts, so an expiry survives restarts. | | `broker_grant` | 5 | `string` | Set by the Cua Keyvault broker when it delivers a session it authorized (a consent grant, an unattended rule or a first-party approval), to `keyvault`; empty for a direct SDK send. Not a secret and not verified by the guest. cua-spacesd counts deliveries with and without it (present or absent) in its opt-in aggregate telemetry; nothing else reads it. | ### ImportSessionRequest Request for `TeleportService.ImportSession`. | Field | # | Type | Description | | --- | --- | --- | --- | | `import_id` | 1 | `string` | Client-chosen import id, stable across retries. The first chunk (offset 0) creates the import. | | `app` | 2 | `string` | App id as in `GetManifestRequest.app`. | | `scope` | 3 | [`TeleportScope`](#teleportscope) | Scope of the bundle. | | `offset` | 4 | `uint64` | Offset of `data[0]` in the bundle. Must equal `received_bytes`; a chunk wholly below it is acknowledged as a duplicate. | | `data` | 5 | `bytes` | Bundle bytes. At most `Limits.max_chunk_bytes`. | | `commit` | 6 | `bool` | True on the last chunk: verify and import. May carry data. | | `sha256` | 7 | `string` | Lowercase hex SHA-256 of the whole bundle. Required when `commit`. | | `options` | 8 | [`ImportOptions`](#importoptions) | Import options, read when `commit`. | ### ImportResult Outcome of an import, returned on the final chunk. | Field | # | Type | Description | | --- | --- | --- | --- | | `imported` | 1 | repeated `string` | Items imported (for example "cookies", "localStorage"). | | `skipped` | 2 | repeated `string` | Items skipped, with reasons, as "item: reason". | | `launched` | 3 | `bool` | True if the app was launched. | ### ImportSessionResponse Response for `TeleportService.ImportSession`. | Field | # | Type | Description | | --- | --- | --- | --- | | `received_bytes` | 1 | `uint64` | Bundle bytes received so far. | | `duplicate` | 2 | `bool` | True if the chunk was a retry and was not written again. | | `result` | 3 | [`ImportResult`](#importresult) | Set on the final chunk once the import completed. | ### TransferEntry One entry of a file transfer manifest. | Field | # | Type | Description | | --- | --- | --- | --- | | `relative_path` | 1 | `string` | Path relative to the transfer root, "/"-separated. No "..", no absolute paths. | | `directory` | 2 | `bool` | True for a directory (no content). | | `size` | 3 | `uint64` | File size in bytes. | | `sha256` | 4 | `string` | Lowercase hex SHA-256 of the file content. | | `mode` | 5 | `uint32` | Unix permission bits. 0 means 0o644 (files) or 0o755 (directories). | | `modified_at` | 6 | `google.protobuf.Timestamp` | Modification time to preserve. | ### BeginReceiveFilesRequest Request for `TeleportService.BeginReceiveFiles`. | Field | # | Type | Description | | --- | --- | --- | --- | | `transfer_id` | 1 | `string` | Client-chosen transfer id, stable across retries. Repeating the call with the same id returns the current progress. | | `destination_subdir` | 2 | `string` | Subdirectory of the guest user's Downloads folder to place the transfer in. Empty places entries directly in Downloads. | | `entries` | 3 | repeated [`TransferEntry`](#transferentry) | All entries. Directories may be listed explicitly or implied by file paths. | | `ignore_patterns` | 4 | repeated `string` | gitignore-syntax patterns applied relative to the transfer root. | | `honor_gitignore` | 5 | `bool` | Also honor `.gitignore` files that are part of the transfer (their content must be uploaded first; the server evaluates them at commit). | | `conflict_policy` | 6 | [`ConflictPolicy`](#conflictpolicy) | What to do with existing files. | | `ttl` | 7 | `google.protobuf.Duration` | How long partial data is kept without progress. Unset means 1 hour. | ### TransferFileProgress Progress of one file in a transfer. | Field | # | Type | Description | | --- | --- | --- | --- | | `index` | 1 | `uint32` | Index into the accepted entries. | | `received_bytes` | 2 | `uint64` | Bytes received. | ### BeginReceiveFilesResponse Response for `TeleportService.BeginReceiveFiles`. | Field | # | Type | Description | | --- | --- | --- | --- | | `transfer_id` | 1 | `string` | The transfer id. | | `accepted` | 2 | repeated [`TransferEntry`](#transferentry) | Entries that will be received, in the order indices refer to. | | `ignored` | 3 | repeated `string` | Relative paths dropped by ignore patterns. | | `progress` | 4 | repeated [`TransferFileProgress`](#transferfileprogress) | Progress per accepted file, for resuming. | | `max_chunk_bytes` | 5 | `uint32` | Largest chunk accepted. | ### ReceiveFilesChunkRequest Request for `TeleportService.ReceiveFilesChunk`. | Field | # | Type | Description | | --- | --- | --- | --- | | `transfer_id` | 1 | `string` | Which transfer. | | `index` | 2 | `uint32` | Index into `BeginReceiveFilesResponse.accepted`. | | `offset` | 3 | `uint64` | Offset of `data[0]` in that file. Must equal its received bytes; a chunk wholly below is a duplicate. | | `data` | 4 | `bytes` | File bytes. At most `max_chunk_bytes`. | ### ReceiveFilesChunkResponse Response for `TeleportService.ReceiveFilesChunk`. | Field | # | Type | Description | | --- | --- | --- | --- | | `received_bytes` | 1 | `uint64` | Bytes received for that file. | | `duplicate` | 2 | `bool` | True if the chunk was a retry. | ### CommitReceiveFilesRequest Request for `TeleportService.CommitReceiveFiles`. | Field | # | Type | Description | | --- | --- | --- | --- | | `transfer_id` | 1 | `string` | Which transfer. | ### ReceivedFile A file placed by a committed transfer. | Field | # | Type | Description | | --- | --- | --- | --- | | `path` | 1 | `string` | Final absolute guest path (after conflict renaming). | | `size` | 2 | `uint64` | Size in bytes. | | `sha256` | 3 | `string` | Verified lowercase hex SHA-256. | ### CommitReceiveFilesResponse Response for `TeleportService.CommitReceiveFiles`. | Field | # | Type | Description | | --- | --- | --- | --- | | `destination` | 1 | `string` | Absolute guest path of the transfer root. | | `files` | 2 | repeated [`ReceivedFile`](#receivedfile) | Files placed. | | `skipped` | 3 | repeated `string` | Relative paths skipped by `CONFLICT_POLICY_SKIP` or by `.gitignore` rules evaluated at commit. | ### AbortReceiveFilesRequest Request for `TeleportService.AbortReceiveFiles`. | Field | # | Type | Description | | --- | --- | --- | --- | | `transfer_id` | 1 | `string` | Which transfer. | ### AbortReceiveFilesResponse Response for `TeleportService.AbortReceiveFiles`. No fields. ### WipeImportRequest Request for `TeleportService.WipeImport`. | Field | # | Type | Description | | --- | --- | --- | --- | | `import_id` | 1 | `string` | Import to wipe, as sent in `ImportSessionRequest.import_id`. Ignored when `all` is set. | | `all` | 2 | `bool` | Wipe every ledgered import in the guest. | ### WipeImportResponse Response for `TeleportService.WipeImport`. | Field | # | Type | Description | | --- | --- | --- | --- | | `wiped_import_ids` | 1 | repeated `string` | Import ids whose ledgers were wiped and removed. | | `removed_paths` | 2 | repeated `string` | Absolute guest paths removed (files, and directories the import created). | | `keychain_items_removed` | 3 | `uint32` | Keychain items removed (macOS guests; 0 elsewhere). | | `cookie_rows_removed` | 4 | `uint32` | Cookie rows deleted out of an existing Cookies database the import merged into (not itself in removed_paths: it was not the import's file to remove). | ### TeleportScope Which part of an app's state to teleport. | Value | # | Description | | --- | --- | --- | | `TELEPORT_SCOPE_UNSPECIFIED` | 0 | Not set. Treated as `TELEPORT_SCOPE_SESSION`. | | `TELEPORT_SCOPE_SESSION` | 1 | Sign-in state only: cookies, local storage, session storage. | | `TELEPORT_SCOPE_PROFILE` | 2 | The whole profile: session plus history, bookmarks, extensions, settings. | ### ConflictPolicy What to do when a received file already exists. | Value | # | Description | | --- | --- | --- | | `CONFLICT_POLICY_UNSPECIFIED` | 0 | Not set. Treated as `CONFLICT_POLICY_RENAME`. | | `CONFLICT_POLICY_RENAME` | 1 | Keep both: the new file becomes "name (1).ext". | | `CONFLICT_POLICY_OVERWRITE` | 2 | Replace the existing file. | | `CONFLICT_POLICY_SKIP` | 3 | Keep the existing file and drop the new one. | ## Types in `cua/env/v1/tunnel.proto` ### ForwardRequest Request for `TunnelService.Forward`. | Field | # | Type | Description | | --- | --- | --- | --- | | `port` | 1 | `uint32` | Guest TCP port to reach. | | `host` | 2 | `string` | Guest host to connect to. Empty means "127.0.0.1". Only loopback and the guest's own addresses are allowed. | | `ttl` | 3 | `google.protobuf.Duration` | How long the ticket accepts new connections. Unset means 10 minutes; at most 24 hours. Open connections survive expiry. | ### ForwardResponse Response for `TunnelService.Forward`. | Field | # | Type | Description | | --- | --- | --- | --- | | `forward_id` | 1 | `string` | Forward id. | | `ticket` | 2 | `string` | Ticket accepted by the tunnel WebSocket. Reusable for many connections until expiry. | | `ws_path` | 3 | `string` | WebSocket path relative to the spacesd base URL, including the ticket, for example "/tunnel?ticket=…". | | `expires_at` | 4 | `google.protobuf.Timestamp` | When the ticket expires. | ### ForwardInfo An active forward. | Field | # | Type | Description | | --- | --- | --- | --- | | `forward_id` | 1 | `string` | Forward id. | | `host` | 2 | `string` | Guest host. | | `port` | 3 | `uint32` | Guest port. | | `active_connections` | 4 | `uint32` | Open connections. | | `bytes_in` | 5 | `uint64` | Bytes sent to the guest port. | | `bytes_out` | 6 | `uint64` | Bytes received from the guest port. | | `expires_at` | 7 | `google.protobuf.Timestamp` | Ticket expiry. | ### ListForwardsRequest Request for `TunnelService.ListForwards`. No fields. ### ListForwardsResponse Response for `TunnelService.ListForwards`. | Field | # | Type | Description | | --- | --- | --- | --- | | `forwards` | 1 | repeated [`ForwardInfo`](#forwardinfo) | Active forwards. | ### CloseForwardRequest Request for `TunnelService.CloseForward`. | Field | # | Type | Description | | --- | --- | --- | --- | | `forward_id` | 1 | `string` | Which forward. | ### CloseForwardResponse Response for `TunnelService.CloseForward`. No fields. ### StartHotspotRequest Request for `TunnelService.StartHotspot`. | Field | # | Type | Description | | --- | --- | --- | --- | | `socks_port` | 1 | `uint32` | Guest loopback port for the SOCKS5 listener. 0 means 1080. | | `set_system_proxy` | 2 | `bool` | Point the guest's system proxy settings (and HTTP(S)_PROXY for new processes) at the hotspot. | | `bypass` | 3 | repeated `string` | Destinations (CIDRs or host suffixes) that bypass the hotspot and use the guest's own network. | | `ticket_ttl` | 4 | `google.protobuf.Duration` | How long the ticket accepts the client's WebSocket. Unset means 60 seconds. | ### StartHotspotResponse Response for `TunnelService.StartHotspot`. | Field | # | Type | Description | | --- | --- | --- | --- | | `hotspot_id` | 1 | `string` | Hotspot id. | | `ticket` | 2 | `string` | Ticket for the hotspot WebSocket. | | `ws_path` | 3 | `string` | WebSocket path relative to the spacesd base URL, including the ticket, for example "/hotspot?ticket=…". The client keeps this socket open; the hotspot stops when it closes. | | `socks_address` | 4 | `string` | Guest-side SOCKS5 address, for example "127.0.0.1:1080". | ### StopHotspotRequest Request for `TunnelService.StopHotspot`. | Field | # | Type | Description | | --- | --- | --- | --- | | `hotspot_id` | 1 | `string` | Which hotspot. | ### StopHotspotResponse Response for `TunnelService.StopHotspot`. No fields. ### GetHotspotStatusRequest Request for `TunnelService.GetHotspotStatus`. No fields. ### GetHotspotStatusResponse Response for `TunnelService.GetHotspotStatus`. | Field | # | Type | Description | | --- | --- | --- | --- | | `state` | 1 | [`HotspotState`](#hotspotstate) | State. | | `hotspot_id` | 2 | `string` | Hotspot id, when not stopped. | | `peer_principal_id` | 3 | `string` | Id of the principal whose client provides egress. | | `socks_address` | 4 | `string` | Guest-side SOCKS5 address. | | `active_connections` | 5 | `uint32` | Open relayed connections. | | `bytes_out` | 6 | `uint64` | Bytes sent from the guest through the hotspot. | | `bytes_in` | 7 | `uint64` | Bytes received into the guest through the hotspot. | ### HotspotState Hotspot state. | Value | # | Description | | --- | --- | --- | | `HOTSPOT_STATE_UNSPECIFIED` | 0 | Not reported. | | `HOTSPOT_STATE_STOPPED` | 1 | No hotspot. | | `HOTSPOT_STATE_WAITING_FOR_PEER` | 2 | Started, waiting for the client WebSocket. | | `HOTSPOT_STATE_ACTIVE` | 3 | Relaying. | ## Types in `cua/env/v1/volume.proto` ### AttachVolumeRequest Request for `VolumeService.AttachVolume`. | Field | # | Type | Description | | --- | --- | --- | --- | | `mount_path` | 1 | `string` | Where to mount. Empty means the guest's default (see the table above). | | `ticket_ttl` | 2 | `google.protobuf.Duration` | How long the ticket accepts the client's WebSocket. Unset means 60 seconds. | ### AttachVolumeResponse Response for `VolumeService.AttachVolume`. | Field | # | Type | Description | | --- | --- | --- | --- | | `volume_id` | 1 | `string` | Volume session id. | | `ticket` | 2 | `string` | Ticket for the volume WebSocket. | | `ws_path` | 3 | `string` | WebSocket path relative to the spacesd base URL, including the ticket, for example "/volume?ticket=…". | | `mount_path` | 4 | `string` | Where the volume will be mounted. | | `backend` | 5 | `string` | `nfs` or `fs`: the target name the guest's streams carry, which the client answers. | ### DetachVolumeRequest Request for `VolumeService.DetachVolume`. | Field | # | Type | Description | | --- | --- | --- | --- | | `volume_id` | 1 | `string` | Which session. Empty means the current one. | ### DetachVolumeResponse Response for `VolumeService.DetachVolume`. No fields. ### GetVolumeStatusRequest Request for `VolumeService.GetVolumeStatus`. No fields. ### GetVolumeStatusResponse Response for `VolumeService.GetVolumeStatus`. | Field | # | Type | Description | | --- | --- | --- | --- | | `state` | 1 | [`VolumeState`](#volumestate) | State. | | `volume_id` | 2 | `string` | Volume session id (empty when detached). | | `mount_path` | 3 | `string` | Mount path. | | `backend` | 4 | `string` | `nfs` or `fs`. | | `detail` | 5 | `string` | Why, when `state` is `VOLUME_STATE_ERROR`. | | `active_streams` | 6 | `uint32` | Open streams to the client. | | `bytes_out` | 7 | `uint64` | Bytes sent from the guest to the client. | | `bytes_in` | 8 | `uint64` | Bytes received from the client. | ### VolumeState State of the guest mount. | Value | # | Description | | --- | --- | --- | | `VOLUME_STATE_UNSPECIFIED` | 0 | Not reported. | | `VOLUME_STATE_DETACHED` | 1 | No volume. | | `VOLUME_STATE_WAITING_FOR_CLIENT` | 2 | Attached by RPC, waiting for the client WebSocket. | | `VOLUME_STATE_MOUNTING` | 3 | Mounting. | | `VOLUME_STATE_MOUNTED` | 4 | Mounted and served. | | `VOLUME_STATE_ERROR` | 5 | The mount failed; see `detail`. | --- # Host Spaces cua.env.v1 HostSpacesService: Spaces a host creates for its owner's devices through the relay. Spaces a machine set up with `cua host setup --provide-spaces` creates for its owner's enrolled devices: settings, capacity, create and delete. The host's cua-spacesd forwards each call to its cua daemon. Source: `libs/cua/proto/cua/env/v1/host.proto`. ## HostSpacesService `cua.env.v1.HostSpacesService` Spaces a host provides: a machine set up with `cua host setup --provide-spaces` creates Spaces (macOS VMs with Lume on a Mac, Linux containers with Docker, whatever else runs locally) for the owner's enrolled devices and the accounts it is shared with, through the relay. The host's cua-spacesd serves this service only in `join` mode with a host policy, and only to relay-asserted editors (the owner, or an account on the machine's allowlist) and the local token holder: a view-only share is refused. It forwards each call to the host's cua daemon, which creates and deletes the Spaces with its own runtimes, attaches each new Space to the relay as its own machine, and records every create and delete in the host's hash-chained audit log (`~/.cua/host/spaces-audit.jsonl`). Providing Spaces never exposes the host's own desktop or files: with the host setting `share_desktop` off, a relayed caller reaches only this service and `SystemService` `GetCapabilities` / `Health`. A host set up with `cua host setup --direct <ip:port> --provide-spaces` serves this service on its direct listener too, without the relay: the caller is the holder of the host's env token (the owner), and by default only loopback, Tailscale (100.64.0.0/10, fd7a:115c:a1e0::/48) and private LAN addresses may call it. A Space created with `direct` set is not attached to the relay: the host forwards a TCP port on its own address (`HostSpace.direct_port`) to the Space's cua-spacesd. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`GetHostSpaces`](#hostspacesservicegethostspaces) | [`GetHostSpacesRequest`](#gethostspacesrequest) | [`GetHostSpacesResponse`](#gethostspacesresponse) | unary | | [`CreateHostSpace`](#hostspacesservicecreatehostspace) | [`CreateHostSpaceRequest`](#createhostspacerequest) | [`CreateHostSpaceResponse`](#createhostspaceresponse) | unary | | [`DeleteHostSpace`](#hostspacesservicedeletehostspace) | [`DeleteHostSpaceRequest`](#deletehostspacerequest) | [`DeleteHostSpaceResponse`](#deletehostspaceresponse) | unary | | [`CancelHostSpace`](#hostspacesservicecancelhostspace) | [`CancelHostSpaceRequest`](#cancelhostspacerequest) | [`CancelHostSpaceResponse`](#cancelhostspaceresponse) | unary | | [`SetHostSpacePower`](#hostspacesservicesethostspacepower) | [`SetHostSpacePowerRequest`](#sethostspacepowerrequest) | [`SetHostSpacePowerResponse`](#sethostspacepowerresponse) | unary | | [`DeleteCloudSpace`](#hostspacesservicedeletecloudspace) | [`DeleteCloudSpaceRequest`](#deletecloudspacerequest) | [`DeleteCloudSpaceResponse`](#deletecloudspaceresponse) | unary | ### HostSpacesService.GetHostSpaces `/cua.env.v1.HostSpacesService/GetHostSpaces`, unary: [`GetHostSpacesRequest`](#gethostspacesrequest) to [`GetHostSpacesResponse`](#gethostspacesresponse). This host's settings, capacity, the Spaces it provides to the caller (every one for the owner) and, for the owner, its recent audit. ### HostSpacesService.CreateHostSpace `/cua.env.v1.HostSpacesService/CreateHostSpace`, unary: [`CreateHostSpaceRequest`](#createhostspacerequest) to [`CreateHostSpaceResponse`](#createhostspaceresponse). Creates one Space on this host and attaches it to the relay as the machine the caller registered for it (`attach`). Fails with `RESOURCE_EXHAUSTED` when the host is at capacity (two macOS VMs per Mac, by Apple's license, or the host's `max_spaces`). ### HostSpacesService.DeleteHostSpace `/cua.env.v1.HostSpacesService/DeleteHostSpace`, unary: [`DeleteHostSpaceRequest`](#deletehostspacerequest) to [`DeleteHostSpaceResponse`](#deletehostspaceresponse). Deletes a Space this host provides (its sandbox and its relay machine). The owner deletes any; another account only the ones it created. ### HostSpacesService.CancelHostSpace `/cua.env.v1.HostSpacesService/CancelHostSpace`, unary: [`CancelHostSpaceRequest`](#cancelhostspacerequest) to [`CancelHostSpaceResponse`](#cancelhostspaceresponse). Cancels a `CreateHostSpace` the caller started, by the relay machine it registered for it (`attach.machine_id`): the create stops (an image download, a boot) and what it made is removed; one that already finished is deleted, since the caller gave up on it. Idempotent. ### HostSpacesService.SetHostSpacePower `/cua.env.v1.HostSpacesService/SetHostSpacePower`, unary: [`SetHostSpacePowerRequest`](#sethostspacepowerrequest) to [`SetHostSpacePowerResponse`](#sethostspacepowerresponse). Turns a Space this host provides off or on. Off stops it with its disk kept, freeing the host's memory (a runtime that cannot start it again, QEMU, suspends it instead); on boots it again and re-attaches it to its relay machine. The owner powers any; another account only the ones it created. ### HostSpacesService.DeleteCloudSpace `/cua.env.v1.HostSpacesService/DeleteCloudSpace`, unary: [`DeleteCloudSpaceRequest`](#deletecloudspacerequest) to [`DeleteCloudSpaceResponse`](#deletecloudspaceresponse). Deletes a Space in the owner's own cloud that this machine created (an AWS, Google Cloud or Modal sandbox, by the relay machine it joined as): its cloud resources, through this machine's ownership records, and its relay machine. Only the owner. Lets another of the owner's devices delete it permanently without the cloud's credentials. Works whether or not this machine provides Spaces. ## Types in `cua/env/v1/host.proto` ### HostSpacesSettings The host's two independent settings and its limits. | Field | # | Type | Description | | --- | --- | --- | --- | | `share_desktop` | 1 | `bool` | The host's own desktop is a Space too (screen, input, shell, files). | | `provide_spaces` | 2 | `bool` | The host accepts Space create, list and delete requests. | | `max_spaces` | 3 | `uint32` | At most this many provided Spaces at once (0: no limit besides the runtimes' own). | | `max_macos_vms` | 4 | `uint32` | At most this many macOS VMs at once on this Mac (Apple's license allows two; 0 on a host that cannot run macOS VMs). | ### HostSpacesCapacity How much of one limit is in use. | Field | # | Type | Description | | --- | --- | --- | --- | | `resource` | 1 | `string` | `spaces` (every provided Space) or `macos_vms` (macOS VMs on this Mac, provided or not). | | `used` | 2 | `uint32` | In use now. | | `limit` | 3 | `uint32` | The limit (0: none). | | `reason` | 4 | `string` | Why the limit exists, for people ("Apple's macOS license allows two macOS VMs per Mac"). | ### HostSpace A Space this host provides. | Field | # | Type | Description | | --- | --- | --- | --- | | `relay_machine` | 1 | `string` | The relay machine the Space is reached as (`relay:<machine>`). | | `local_space` | 2 | `string` | The Space on the host (`local:<name>`). | | `name` | 3 | `string` | Display name. | | `image` | 4 | `string` | The image it runs, as requested. | | `os` | 5 | `string` | Guest OS family: `linux`, `macos` or `windows` (empty when unknown). | | `kind` | 6 | `string` | `container` or `vm` (empty when unknown). | | `runtime` | 7 | `string` | Engine: `lume`, `runc`, `gvisor`, `qemu` (empty when unknown). | | `created_by` | 8 | `string` | The account that created it (id, or email when the relay shares it). | | `created_at_ms` | 9 | `int64` | When it was created (Unix milliseconds). | | `direct_port` | 10 | `uint32` | A Space created with `CreateHostSpaceRequest.direct`: the TCP port on the host's own address that the host forwards to the Space's cua-spacesd (`direct:<host address>:<direct_port>`). 0 for a Space reached through the relay. | | `direct_token` | 11 | `string` | The Space's cua-spacesd token, set only in the `CreateHostSpace` response of a direct Space (the caller stores it like any direct Space's token). Empty otherwise. | ### HostSpacesAuditEvent One line of the host's Spaces audit log. | Field | # | Type | Description | | --- | --- | --- | --- | | `ts_ms` | 1 | `int64` | Unix milliseconds. | | `action` | 2 | `string` | `create`, `delete`, `refused`, or `config`. | | `who` | 3 | `string` | Who: an account (`name <email> (id)`), or `local` for this machine. | | `space` | 4 | `string` | The relay machine or Space the event is about. | | `detail` | 5 | `string` | Detail for people. | ### GetHostSpacesRequest Request for `HostSpacesService.GetHostSpaces`. No fields. ### GetHostSpacesResponse Response for `HostSpacesService.GetHostSpaces`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | This host's display name. | | `os` | 2 | `string` | The host's operating system (`macos`, `linux`, `windows`). | | `settings` | 3 | [`HostSpacesSettings`](#hostspacessettings) | Settings and limits. | | `capacity` | 4 | repeated [`HostSpacesCapacity`](#hostspacescapacity) | Current use of each limit. | | `spaces` | 5 | repeated [`HostSpace`](#hostspace) | Spaces this host provides: every one for the owner, the caller's own otherwise. | | `audit` | 6 | repeated [`HostSpacesAuditEvent`](#hostspacesauditevent) | The most recent audit events, newest first (the owner only). | | `images` | 7 | repeated `string` | Image aliases this host resolves (`macos`, `linux`, ...), for clients to offer. | ### CreateHostSpaceRequest Request for `HostSpacesService.CreateHostSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `image` | 1 | `string` | Image: a registry reference or an alias (`macos`, `macos:26`, `linux`). Empty uses the canonical Linux image. | | `kind` | 2 | `string` | "auto" (empty), "container" or "vm". | | `runtime` | 3 | `string` | "auto" (empty) or a runtime this host offers ("lume", "runc", ...). | | `name` | 4 | `string` | Name on the host. Empty generates `space-<hex>`. | | `cpus` | 5 | `uint32` | vCPUs (0: the default). | | `memory_mb` | 6 | `uint64` | Memory in MiB (0: the default). | | `disk_gb` | 7 | `uint32` | Disk in GiB for a VM (0: the image's). | | `attach` | 8 | [`AttachRelayRequest`](</docs/cua-sdk/reference/protocol/env-system#attachrelayrequest>) | The relay machine the caller registered for this Space: the host attaches the new Space's driver to it (`SystemService.AttachRelay`), so the Space is reached as `relay:<machine_id>` and the host never holds an account credential. Empty when `direct` is set. | | `direct` | 9 | `bool` | Create the Space without the relay (a host set up with `--direct`): it is reached at the host's address on `HostSpace.direct_port`, with `HostSpace.direct_token`. `attach` must be empty. | ### CreateHostSpaceResponse Response for `HostSpacesService.CreateHostSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | [`HostSpace`](#hostspace) | The new Space. | ### DeleteHostSpaceRequest Request for `HostSpacesService.DeleteHostSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | The relay machine of the Space (or its `local:<name>` on the host). | ### DeleteHostSpaceResponse Response for `HostSpacesService.DeleteHostSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `message` | 1 | `string` | What happened, for people. | ### DeleteCloudSpaceRequest Request for `HostSpacesService.DeleteCloudSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | The relay machine the cloud Space joined as. | ### DeleteCloudSpaceResponse Response for `HostSpacesService.DeleteCloudSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `message` | 1 | `string` | What was deleted, for people. | ### CancelHostSpaceRequest Request for `HostSpacesService.CancelHostSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | The relay machine the caller registered for the Space. | ### CancelHostSpaceResponse Response for `HostSpacesService.CancelHostSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `message` | 1 | `string` | What happened, for people: what was removed and what stays. | ### SetHostSpacePowerRequest Request for `HostSpacesService.SetHostSpacePower`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | The relay machine of the Space (or its `local:<name>` on the host). | | `on` | 2 | `bool` | True turns it on, false off. | ### SetHostSpacePowerResponse Response for `HostSpacesService.SetHostSpacePower`. | Field | # | Type | Description | | --- | --- | --- | --- | | `state` | 1 | `string` | "running", "suspended" or "stopped": the state it is in now. | | `power` | 2 | `string` | How it turns off: "stop" or "suspend". | | `message` | 3 | `string` | What happened, for people. | --- # Daemon: sandboxes cua.daemon.v1 SandboxService: the local cua daemon API for sandboxes in every location. The local `cua daemon` API (a Unix socket on the host, not cua-spacesd): sandbox lifecycle across locations. Source: `libs/cua/proto/cua/daemon/v1/sandboxes.proto`. ## SandboxService `cua.daemon.v1.SandboxService` Sandbox lifecycle across providers (Fleet, local VMM, direct). Sandboxes are daemon-agnostic: nothing here assumes cua-spacesd is installed in the guest. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`ListSandboxes`](#sandboxservicelistsandboxes) | [`ListSandboxesRequest`](#listsandboxesrequest) | [`ListSandboxesResponse`](#listsandboxesresponse) | unary | | [`CreateSandbox`](#sandboxservicecreatesandbox) | [`CreateSandboxRequest`](#createsandboxrequest) | [`CreateSandboxResponse`](#createsandboxresponse) | unary | | [`CancelCreateSandbox`](#sandboxservicecancelcreatesandbox) | [`CancelCreateSandboxRequest`](#cancelcreatesandboxrequest) | [`CancelCreateSandboxResponse`](#cancelcreatesandboxresponse) | unary | | [`GetSandbox`](#sandboxservicegetsandbox) | [`GetSandboxRequest`](#getsandboxrequest) | [`GetSandboxResponse`](#getsandboxresponse) | unary | | [`DeleteSandbox`](#sandboxservicedeletesandbox) | [`DeleteSandboxRequest`](#deletesandboxrequest) | [`DeleteSandboxResponse`](#deletesandboxresponse) | unary | | [`ConnectSandbox`](#sandboxserviceconnectsandbox) | [`ConnectSandboxRequest`](#connectsandboxrequest) | [`ConnectSandboxResponse`](#connectsandboxresponse) | unary | | [`SuspendSandbox`](#sandboxservicesuspendsandbox) | [`SuspendSandboxRequest`](#suspendsandboxrequest) | [`SuspendSandboxResponse`](#suspendsandboxresponse) | unary | | [`ResumeSandbox`](#sandboxserviceresumesandbox) | [`ResumeSandboxRequest`](#resumesandboxrequest) | [`ResumeSandboxResponse`](#resumesandboxresponse) | unary | | [`RestartSandbox`](#sandboxservicerestartsandbox) | [`RestartSandboxRequest`](#restartsandboxrequest) | [`RestartSandboxResponse`](#restartsandboxresponse) | unary | | [`KeepAlive`](#sandboxservicekeepalive) | [`KeepAliveRequest`](#keepaliverequest) | [`KeepAliveResponse`](#keepaliveresponse) | unary | | [`WaitReady`](#sandboxservicewaitready) | [`WaitReadyRequest`](#waitreadyrequest) | [`WaitReadyResponse`](#waitreadyresponse) | unary | | [`ServiceRequest`](#sandboxserviceservicerequest) | [`ServiceRequestRequest`](#servicerequestrequest) | [`ServiceRequestResponse`](#servicerequestresponse) | unary | | [`ForwardPort`](#sandboxserviceforwardport) | [`ForwardPortRequest`](#forwardportrequest) | [`ForwardPortResponse`](#forwardportresponse) | unary | | [`CloseForward`](#sandboxservicecloseforward) | [`CloseForwardRequest`](#closeforwardrequest) | [`CloseForwardResponse`](#closeforwardresponse) | unary | | [`GetEnvEndpoint`](#sandboxservicegetenvendpoint) | [`GetEnvEndpointRequest`](#getenvendpointrequest) | [`GetEnvEndpointResponse`](#getenvendpointresponse) | unary | | [`GetServiceEndpoint`](#sandboxservicegetserviceendpoint) | [`GetServiceEndpointRequest`](#getserviceendpointrequest) | [`GetServiceEndpointResponse`](#getserviceendpointresponse) | unary | | [`GetServiceUrl`](#sandboxservicegetserviceurl) | [`GetServiceUrlRequest`](#getserviceurlrequest) | [`GetServiceUrlResponse`](#getserviceurlresponse) | unary | | [`CreatePublicUrl`](#sandboxservicecreatepublicurl) | [`CreatePublicUrlRequest`](#createpublicurlrequest) | [`CreatePublicUrlResponse`](#createpublicurlresponse) | unary | | [`RevokePublicUrl`](#sandboxservicerevokepublicurl) | [`RevokePublicUrlRequest`](#revokepublicurlrequest) | [`RevokePublicUrlResponse`](#revokepublicurlresponse) | unary | ### SandboxService.ListSandboxes `/cua.daemon.v1.SandboxService/ListSandboxes`, unary: [`ListSandboxesRequest`](#listsandboxesrequest) to [`ListSandboxesResponse`](#listsandboxesresponse). Lists sandboxes known to the daemon. ### SandboxService.CreateSandbox `/cua.daemon.v1.SandboxService/CreateSandbox`, unary: [`CreateSandboxRequest`](#createsandboxrequest) to [`CreateSandboxResponse`](#createsandboxresponse). Creates a sandbox where `location` says (local by default), or connects to an existing machine (`direct:<addr>`). ### SandboxService.CancelCreateSandbox `/cua.daemon.v1.SandboxService/CancelCreateSandbox`, unary: [`CancelCreateSandboxRequest`](#cancelcreatesandboxrequest) to [`CancelCreateSandboxResponse`](#cancelcreatesandboxresponse). Cancels a `CreateSandbox` of a named sandbox still running: the work in flight stops (an image download, a boot, a claim) and what the create made is deleted; a sandbox of that name that existed before is never touched. Finished image downloads stay cached. Returns once the clean-up is done. Idempotent: `cancelled` is false when no create of that name was running. A `CreateSandbox` whose caller goes away (a closed connection) is cleaned up the same way. ### SandboxService.GetSandbox `/cua.daemon.v1.SandboxService/GetSandbox`, unary: [`GetSandboxRequest`](#getsandboxrequest) to [`GetSandboxResponse`](#getsandboxresponse). Returns one sandbox. ### SandboxService.DeleteSandbox `/cua.daemon.v1.SandboxService/DeleteSandbox`, unary: [`DeleteSandboxRequest`](#deletesandboxrequest) to [`DeleteSandboxResponse`](#deletesandboxresponse). Deletes (or releases, or detaches from) a sandbox. ### SandboxService.ConnectSandbox `/cua.daemon.v1.SandboxService/ConnectSandbox`, unary: [`ConnectSandboxRequest`](#connectsandboxrequest) to [`ConnectSandboxResponse`](#connectsandboxresponse). Reattaches to a sandbox by ref (a state file, a live cloud claim or an address) and keeps the handle in the daemon. ### SandboxService.SuspendSandbox `/cua.daemon.v1.SandboxService/SuspendSandbox`, unary: [`SuspendSandboxRequest`](#suspendsandboxrequest) to [`SuspendSandboxResponse`](#suspendsandboxresponse). Suspends a sandbox (local: pause; Fleet: scale to zero). ### SandboxService.ResumeSandbox `/cua.daemon.v1.SandboxService/ResumeSandbox`, unary: [`ResumeSandboxRequest`](#resumesandboxrequest) to [`ResumeSandboxResponse`](#resumesandboxresponse). Resumes a suspended sandbox. ### SandboxService.RestartSandbox `/cua.daemon.v1.SandboxService/RestartSandbox`, unary: [`RestartSandboxRequest`](#restartsandboxrequest) to [`RestartSandboxResponse`](#restartsandboxresponse). Restarts a sandbox. ### SandboxService.KeepAlive `/cua.daemon.v1.SandboxService/KeepAlive`, unary: [`KeepAliveRequest`](#keepaliverequest) to [`KeepAliveResponse`](#keepaliveresponse). Extends a Fleet claim lease. ### SandboxService.WaitReady `/cua.daemon.v1.SandboxService/WaitReady`, unary: [`WaitReadyRequest`](#waitreadyrequest) to [`WaitReadyResponse`](#waitreadyresponse). Waits until user-declared readiness probes pass. ### SandboxService.ServiceRequest `/cua.daemon.v1.SandboxService/ServiceRequest`, unary: [`ServiceRequestRequest`](#servicerequestrequest) to [`ServiceRequestResponse`](#servicerequestresponse). Sends one HTTP request to a named service of a sandbox. Credentials (for example the Fleet bearer) stay in the daemon. ### SandboxService.ForwardPort `/cua.daemon.v1.SandboxService/ForwardPort`, unary: [`ForwardPortRequest`](#forwardportrequest) to [`ForwardPortResponse`](#forwardportresponse). Forwards a daemon loopback port to a guest port. ### SandboxService.CloseForward `/cua.daemon.v1.SandboxService/CloseForward`, unary: [`CloseForwardRequest`](#closeforwardrequest) to [`CloseForwardResponse`](#closeforwardresponse). Stops a forward started by `ForwardPort`. ### SandboxService.GetEnvEndpoint `/cua.daemon.v1.SandboxService/GetEnvEndpoint`, unary: [`GetEnvEndpointRequest`](#getenvendpointrequest) to [`GetEnvEndpointResponse`](#getenvendpointresponse). Probes cua-spacesd in the sandbox and returns the daemon's loopback passthrough endpoint for it. `cua.env.v1` calls sent there are proxied with the sandbox credentials attached by the daemon. ### SandboxService.GetServiceEndpoint `/cua.daemon.v1.SandboxService/GetServiceEndpoint`, unary: [`GetServiceEndpointRequest`](#getserviceendpointrequest) to [`GetServiceEndpointResponse`](#getserviceendpointresponse). The daemon's loopback passthrough for a named service of a sandbox: any HTTP request (any method, streamed both ways, headers passed through) sent to `url` + path reaches that path on the service with the sandbox's route and credentials attached by the daemon. For MCP clients, SSE and anything else served over HTTP. ### SandboxService.GetServiceUrl `/cua.daemon.v1.SandboxService/GetServiceUrl`, unary: [`GetServiceUrlRequest`](#getserviceurlrequest) to [`GetServiceUrlResponse`](#getserviceurlresponse). Returns a URL for a named service usable from this machine without credentials: the published loopback port locally, a signed service URL in the cloud. ### SandboxService.CreatePublicUrl `/cua.daemon.v1.SandboxService/CreatePublicUrl`, unary: [`CreatePublicUrlRequest`](#createpublicurlrequest) to [`CreatePublicUrlResponse`](#createpublicurlresponse). Creates a shareable URL for a named service that expires: a signed service URL in the cloud; locally a loopback proxy URL with its own token, served by the daemon. ### SandboxService.RevokePublicUrl `/cua.daemon.v1.SandboxService/RevokePublicUrl`, unary: [`RevokePublicUrlRequest`](#revokepublicurlrequest) to [`RevokePublicUrlResponse`](#revokepublicurlresponse). Revokes a URL created by `CreatePublicUrl`. ## Types in `cua/daemon/v1/sandboxes.proto` ### Sandbox A sandbox. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Name (unique within its location; see `id`). | | `provider` | 2 | [`SandboxProvider`](#sandboxprovider) | Provider. | | `state` | 3 | [`SandboxState`](#sandboxstate) | State. | | `image` | 4 | `string` | Image reference, for example "ghcr.io/trycua/linux:24.04". | | `endpoints` | 5 | map of `string` to `string` | Reachable endpoints by service name, for example {"env": "http://127.0.0.1:52011"}. | | `labels` | 6 | map of `string` to `string` | User labels. | | `created_at` | 7 | `google.protobuf.Timestamp` | Creation time. | | `error` | 8 | `string` | Failure reason when `state` is `SANDBOX_STATE_FAILED`. | | `runtime_type` | 9 | `string` | Runtime identifier as persisted in the state file (`fleet`, `direct`, `docker`, `qemu`, `lume`, ...). | | `ephemeral` | 10 | `bool` | True when the sandbox has no state file and is torn down on delete. | | `services` | 11 | map of `string` to `uint32` | Declared services: logical name to guest port (0 when only the name is known, for example on Fleet). | | `location` | 12 | `string` | Where the sandbox runs: "local", "cloud", "direct" (a machine reached by address) or "relay". | | `expires_at` | 13 | `google.protobuf.Timestamp` | When the sandbox expires unless kept alive. Unset when unknown or never (cloud sandboxes held by the daemon are renewed while held). | | `provider_details` | 14 | map of `string` to `string` | Provider internals for debugging (cloud: pool, namespace, claim; local: backend, container_id). Not part of the portable API. | | `id` | 15 | `string` | Qualified ref: "local:<name>", "cloud:<name>" or "direct:<host:port>". Every request's `name` accepts it (and a bare name when that name is unique across locations). | | `kind` | 16 | `string` | What kind of machine it runs as: "container" or "vm"; empty when not known (an existing machine, or a cloud sandbox this daemon did not create). | | `runtime` | 17 | `string` | The engine that runs it: "gvisor", "runc", "qemu", "lume" or "kubevirt"; empty when not known. | ### ReadinessProbe A user-declared readiness probe. With no probes a sandbox is ready as soon as its provider reports it running. | Field | # | Type | Description | | --- | --- | --- | --- | | `port` | 1 | `uint32` | Guest port to probe. | | `http_path` | 2 | `string` | HTTP path. Empty means a TCP connect probe. | | `http_status` | 3 | `uint32` | Exact HTTP status to wait for. 0 accepts any 2xx. | ### Sidecar An extra container sharing a sandbox's network namespace: the sandbox reaches its ports on `localhost`, and `services` may name them. Local containers and cloud container (gVisor) sandboxes only. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Name, unique within the sandbox (a DNS label other than `main`). Empty derives it from the image. | | `image` | 2 | `string` | Image reference. | | `command` | 3 | repeated `string` | argv replacing the image's entrypoint. Empty lets the image decide. | | `env` | 4 | map of `string` to `string` | Environment (plain values, not secrets). | | `ports` | 5 | repeated `uint32` | Ports it listens on. | ### RegistrySecret Credentials for a private registry. Write-only: never logged, stored in state files or returned. | Field | # | Type | Description | | --- | --- | --- | --- | | `registry` | 1 | `string` | Registry host (`ghcr.io`, `localhost:5000`). Empty means the image's. | | `username` | 2 | `string` | User name (`AWS` for ECR tokens). | | `password` | 3 | `string` | Password or token. | ### ImageLayer One image build step, as in `images.cua.ai/v1alpha1` recipes. | Field | # | Type | Description | | --- | --- | --- | --- | | `type` | 1 | `string` | `apt_install`, `pip_install`, `uv_install`, `run` or `app_install`. | | `packages` | 2 | repeated `string` | Packages (the install types). | | `command` | 3 | `string` | Shell command (`run`). | | `app_id` | 4 | `string` | App id (`app_install`). | ### BuildFile A local file copied into a built image. | Field | # | Type | Description | | --- | --- | --- | --- | | `source` | 1 | `string` | Path on the daemon's machine. | | `destination` | 2 | `string` | Absolute path in the image. | ### ImageBuild Image layers built on top of the sandbox's image. Cloud: a remote build through the images API, cached by content. | Field | # | Type | Description | | --- | --- | --- | --- | | `layers` | 1 | repeated [`ImageLayer`](#imagelayer) | Steps, in order. | | `env` | 2 | map of `string` to `string` | Image environment. | | `ports` | 3 | repeated `uint32` | Ports the image exposes. | | `files` | 4 | repeated [`BuildFile`](#buildfile) | Files copied into the image. | | `timeout` | 5 | `google.protobuf.Duration` | Build budget. Unset uses one hour. | ### ListSandboxesRequest Request for `SandboxService.ListSandboxes`. | Field | # | Type | Description | | --- | --- | --- | --- | | `provider` | 1 | [`SandboxProvider`](#sandboxprovider) | Deprecated. Deprecated: use `location`. Only sandboxes of this provider. Unspecified lists all. | | `include_cloud` | 2 | optional `bool` | Also list the account's live Fleet claims (not only the ones this machine knows) when the provider filter allows cloud sandboxes. Unset means true. Without Fleet credentials nothing is added. | | `location` | 3 | `string` | Only sandboxes in this location: "local", "cloud" or "direct". Empty lists every location. Wins over `provider`. | ### ListSandboxesResponse Response for `SandboxService.ListSandboxes`. | Field | # | Type | Description | | --- | --- | --- | --- | | `sandboxes` | 1 | repeated [`Sandbox`](#sandbox) | Sandboxes. | | `warnings` | 2 | repeated `string` | What the listing left out, for example "cloud sandboxes not listed: <reason>". The listing still succeeds. | ### CreateSandboxRequest Request for `SandboxService.CreateSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Desired name. Empty lets the daemon generate one. | | `provider` | 2 | [`SandboxProvider`](#sandboxprovider) | Deprecated. Deprecated: use `location`. Used only when `location` is empty. | | `image` | 3 | `string` | Image reference (local and cloud sandboxes). | | `pool` | 4 | `string` | Cloud: claim from this existing pool (Fleets advanced API). | | `url` | 5 | `string` | Deprecated: use `location` "direct:<addr>". The address of an existing machine. | | `token` | 6 | `string` | Access token for the direct provider's spacesd, if any. | | `labels` | 7 | map of `string` to `string` | User labels. | | `os` | 8 | `string` | Guest OS: `linux` (default), `macos` or `windows`. | | `cpus` | 9 | `uint32` | vCPUs. 0 uses the provider default. | | `memory_mb` | 10 | `uint64` | Memory in MiB. 0 uses the provider default. | | `ports` | 11 | repeated `uint32` | Extra guest ports to publish (local) or expose as `port-<n>` services (Fleet). | | `services` | 12 | map of `string` to `uint32` | Named services: logical name to guest port. `env` defaults to 3211. | | `wait_for` | 13 | repeated [`ReadinessProbe`](#readinessprobe) | Readiness probes. | | `ready_timeout` | 14 | `google.protobuf.Duration` | Total readiness budget. Unset uses the provider default. | | `env` | 15 | map of `string` to `string` | Guest environment. Local: container environment or cloud-init; cloud: the template environment (hashed into the managed pool key). | | `fleet_runtime` | 16 | `string` | Deprecated. Deprecated: use `runtime`. Cloud runtime (`kubevirt`, `gvisor`), used when `runtime` is empty. | | `fleet_replicas` | 17 | `uint32` | Deprecated: initial replicas of a new managed Fleet pool (> 0 means warm). 0 leaves it to `fleet_warm`. | | `fleet_ttl_seconds` | 18 | `uint32` | Fleet claim TTL in seconds after creation. 0 uses the default (managed pools: `CUA_FLEET_CLAIM_TTL`, renewed by the daemon while the sandbox is held). | | `fleet_warm` | 19 | optional `bool` | Managed Fleet pools (no `pool`): start a new pool with one warm replica. Unset uses `CUA_FLEET_WARM`. Existing pools are never resized. | | `fleet_max_pool_size` | 20 | `uint32` | Managed Fleet pools: autoscaling ceiling. 0 uses the default (`CUA_FLEET_MAX_POOL_SIZE`, 10). | | `command` | 21 | repeated `string` | The sandbox's command (argv), replacing the image's entrypoint. Empty lets the image decide. Local containers: ENTRYPOINT; local Linux VMs: run by cloud-init; cloud pod runtimes: the template command. Cloud VMs (KubeVirt) and macOS guests reject it. | | `sidecars` | 22 | repeated [`Sidecar`](#sidecar) | Sidecar containers sharing the sandbox's network namespace. Local containers and cloud container sandboxes; VMs reject them. | | `registry_secret` | 23 | [`RegistrySecret`](#registrysecret) | Credentials for a private `image` (and sidecar images on its registry): local pulls use them; the cloud stores them as the sandbox's registry pull Secret. Never logged or returned. | | `build` | 24 | [`ImageBuild`](#imagebuild) | Image layers built on top of `image` before the sandbox starts. | | `container_runtime` | 25 | `string` | Deprecated. Deprecated: use `runtime`. Local container sandboxes: `runc` or `gvisor`, used when `runtime` is empty. Cloud sandboxes ignore it. | | `fleet_apply` | 26 | `bool` | With `pool`: update that pool's template to the sandbox fields given here (image, command, env, services, sidecars, cpus, memory) instead of failing with POOL_SPEC_MISMATCH when they differ. Managed pools refuse. | | `owner_pid` | 27 | `uint32` | The client process that owns an ephemeral sandbox (no `name`). The daemon records it in the sandbox's lease, so a local ephemeral sandbox is reaped after its client dies even while the daemon runs. 0: the daemon itself owns it. | | `network` | 28 | `string` | Guest network: empty or `default` gives outbound network (like a Docker container); `none` cuts egress while published ports still work. Only local QEMU VMs honour `none`; containers, Lume and cloud sandboxes reject it. | | `location` | 29 | `string` | Where it runs: "local", "cloud", "direct:<addr>" (an existing machine running cua-spacesd), or a registered provider (SANDBOX_PROVIDER_CONTRIB, e.g. "e2b"). Empty uses the default (`default.on` in the config, `CUA_DEFAULT_ON`, else "local"); clients resolve the default on their side and send it explicitly. | | `kind` | 30 | `string` | What kind of machine: "auto" (empty), "container" or "vm". Auto decides from the image: macOS and Windows images are VMs, an image with a container rootfs is a container, a disk-only image is a VM. | | `runtime` | 31 | `string` | Which engine: "auto" (empty) or one the location offers for the kind. Local: "gvisor", "runc" (containers), "qemu", "lume" (VMs). Cloud: "gvisor" (containers), "kubevirt" (VMs). A registered provider offers its own engines. Anything else is DAEMON_ERROR_REASON_INVALID_PLACEMENT, listing the valid values. | | `keep_on_failure` | 32 | `bool` | Keep a named sandbox whose readiness check fails (its cloud claim, VM or container, and its record) for debugging. False: a failed create deletes what it made and releases the claim. Ephemeral sandboxes are always deleted. | | `gpu` | 33 | `string` | A GPU option of the runtime it runs on (`GetGpuSupport`): "paravirtual" (a macOS VM on Lume: GPU acceleration, experimental), "virgl" (QEMU on Linux), "nvidia" (a runc container on Linux), a contrib provider's GPU type; "auto" picks the runtime's own. Empty: no GPU. An option the runtime does not offer here is DAEMON_ERROR_REASON_INVALID_ARGUMENT or _UNSUPPORTED, saying why. | ### CreateSandboxResponse Response for `SandboxService.CreateSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `sandbox` | 1 | [`Sandbox`](#sandbox) | The sandbox. | ### GetSandboxRequest Request for `SandboxService.GetSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | ### GetSandboxResponse Response for `SandboxService.GetSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `sandbox` | 1 | [`Sandbox`](#sandbox) | The sandbox. | ### CancelCreateSandboxRequest Request for `SandboxService.CancelCreateSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | The sandbox's name. | ### CancelCreateSandboxResponse Response for `SandboxService.CancelCreateSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `cancelled` | 1 | `bool` | A create of that name was running and was stopped. | | `message` | 2 | `string` | What was removed and what stays, for people. | ### DeleteSandboxRequest Request for `SandboxService.DeleteSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | ### DeleteSandboxResponse Response for `SandboxService.DeleteSandbox`. No fields. ### ConnectSandboxRequest Request for `SandboxService.ConnectSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | ### ConnectSandboxResponse Response for `SandboxService.ConnectSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `sandbox` | 1 | [`Sandbox`](#sandbox) | The sandbox. | ### SuspendSandboxRequest Request for `SandboxService.SuspendSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | ### SuspendSandboxResponse Response for `SandboxService.SuspendSandbox`. No fields. ### ResumeSandboxRequest Request for `SandboxService.ResumeSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | ### ResumeSandboxResponse Response for `SandboxService.ResumeSandbox`. No fields. ### RestartSandboxRequest Request for `SandboxService.RestartSandbox`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | ### RestartSandboxResponse Response for `SandboxService.RestartSandbox`. No fields. ### KeepAliveRequest Request for `SandboxService.KeepAlive`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | | `duration` | 2 | `google.protobuf.Duration` | How long to extend the lease by. | ### KeepAliveResponse Response for `SandboxService.KeepAlive`. No fields. ### WaitReadyRequest Request for `SandboxService.WaitReady`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | | `probes` | 2 | repeated [`ReadinessProbe`](#readinessprobe) | Probes to wait for. | | `timeout` | 3 | `google.protobuf.Duration` | Total budget. | ### WaitReadyResponse Response for `SandboxService.WaitReady`. No fields. ### HttpHeader One HTTP header. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Header name. | | `value` | 2 | `string` | Header value. | ### ServiceRequestRequest Request for `SandboxService.ServiceRequest`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | | `service` | 2 | `string` | Logical service name, for example "env" or "server". | | `method` | 3 | `string` | HTTP method. Empty means GET. | | `path` | 4 | `string` | Path (and query) under the service. | | `body` | 5 | `bytes` | Request body. | | `timeout` | 6 | `google.protobuf.Duration` | Timeout. Unset means 30 seconds. | | `headers` | 7 | repeated [`HttpHeader`](#httpheader) | Request headers (for example content-type, or accept and mcp-session-id for MCP). On Fleet the gateway bearer and claim header are added by the daemon and may not be set here. | ### ServiceRequestResponse Response for `SandboxService.ServiceRequest`. | Field | # | Type | Description | | --- | --- | --- | --- | | `status` | 1 | `uint32` | HTTP status. | | `headers` | 2 | repeated [`HttpHeader`](#httpheader) | Response headers. | | `body` | 3 | `bytes` | Response body. | ### ForwardPortRequest Request for `SandboxService.ForwardPort`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | | `port` | 2 | `uint32` | Guest port. | ### ForwardPortResponse Response for `SandboxService.ForwardPort`. | Field | # | Type | Description | | --- | --- | --- | --- | | `forward_id` | 1 | `string` | Forward id for `CloseForward`. Empty when no listener was started (the port is only reachable as `url`). | | `guest_port` | 2 | `uint32` | Guest port. | | `local_addr` | 3 | `string` | Loopback `host:port` accepting connections, when a TCP forward runs. | | `url` | 4 | `string` | URL to use (a loopback URL, or the Fleet gateway URL of the port's service). | ### CloseForwardRequest Request for `SandboxService.CloseForward`. | Field | # | Type | Description | | --- | --- | --- | --- | | `forward_id` | 1 | `string` | Forward id. | ### CloseForwardResponse Response for `SandboxService.CloseForward`. No fields. ### GetEnvEndpointRequest Request for `SandboxService.GetEnvEndpoint`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | | `probe_timeout` | 2 | `google.protobuf.Duration` | Probe timeout. Unset uses the daemon default. | ### GetServiceEndpointRequest Request for `SandboxService.GetServiceEndpoint`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | | `service` | 2 | `string` | Service name, for example "mcp". | ### GetServiceEndpointResponse Response for `SandboxService.GetServiceEndpoint`. | Field | # | Type | Description | | --- | --- | --- | --- | | `url` | 1 | `string` | Base URL, for example "http://127.0.0.1:52011/v1/sandboxes/dev/svc/mcp". Append the path. | | `headers` | 2 | repeated [`HttpHeader`](#httpheader) | Headers every request needs (the daemon's loopback bearer). | ### GetEnvEndpointResponse Response for `SandboxService.GetEnvEndpoint`. | Field | # | Type | Description | | --- | --- | --- | --- | | `url` | 1 | `string` | Loopback passthrough base URL, for example "http://127.0.0.1:52011/v1/sandboxes/dev/env". Append the gRPC path. | | `token` | 2 | `string` | Bearer token for the passthrough (the daemon's loopback token). | | `spacesd_version` | 3 | `string` | spacesd version reported by the probe. | ### GetServiceUrlRequest Request for `SandboxService.GetServiceUrl`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | | `service` | 2 | `string` | Logical service name. | ### GetServiceUrlResponse Response for `SandboxService.GetServiceUrl`. | Field | # | Type | Description | | --- | --- | --- | --- | | `url` | 1 | `string` | The URL (no trailing slash). | ### PublicUrl A shareable, expiring URL of a sandbox service. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | Id for `RevokePublicUrl`. | | `url` | 2 | `string` | The URL. | | `expires_at` | 3 | `google.protobuf.Timestamp` | When it stops working. | | `sandbox` | 4 | `string` | Sandbox name. | | `service` | 5 | `string` | Logical service name. | | `provider_details` | 6 | map of `string` to `string` | Provider internals (cloud: namespace, claim; local: upstream). | ### CreatePublicUrlRequest Request for `SandboxService.CreatePublicUrl`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref or unique name (with `upstream_url`, only recorded). | | `service` | 2 | `string` | Logical service name. | | `ttl` | 3 | `google.protobuf.Duration` | Lifetime, 60 s to 24 h. Unset means 1 h. | | `label` | 4 | `string` | Label shown in listings (cloud). | | `upstream_url` | 5 | `string` | Share this loopback `http://` URL instead of resolving `name` and `service` (an embedded SDK runtime asking the daemon to host its URL). | ### CreatePublicUrlResponse Response for `SandboxService.CreatePublicUrl`. | Field | # | Type | Description | | --- | --- | --- | --- | | `public_url` | 1 | [`PublicUrl`](#publicurl) | The URL. | ### RevokePublicUrlRequest Request for `SandboxService.RevokePublicUrl`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox ref ("local:<name>", "cloud:<name>", "direct:<host:port>", a legacy id) or a name that is unique across locations. | | `id` | 2 | `string` | `PublicUrl.id`. | ### RevokePublicUrlResponse Response for `SandboxService.RevokePublicUrl`. No fields. ### SandboxProvider Where a sandbox comes from. Deprecated: requests use the `location` string (`local`, `cloud`, `direct:<addr>`), which also names third-party providers; responses carry `Sandbox.location`. | Value | # | Description | | --- | --- | --- | | `SANDBOX_PROVIDER_UNSPECIFIED` | 0 | Not set. | | `SANDBOX_PROVIDER_FLEET` | 1 | Claimed from a Fleet pool. | | `SANDBOX_PROVIDER_LOCAL` | 2 | Run locally through cua-vmm (Lume, QEMU, container). | | `SANDBOX_PROVIDER_DIRECT` | 3 | An existing machine reached by URL and token. | | `SANDBOX_PROVIDER_CONTRIB` | 4 | A contrib provider (third-party platform). | ### SandboxState Sandbox lifecycle state. | Value | # | Description | | --- | --- | --- | | `SANDBOX_STATE_UNSPECIFIED` | 0 | Not reported. | | `SANDBOX_STATE_PROVISIONING` | 1 | Being created or booted. | | `SANDBOX_STATE_RUNNING` | 2 | Running and ready (per its readiness probes). | | `SANDBOX_STATE_STOPPED` | 3 | Suspended or stopped; can resume. | | `SANDBOX_STATE_DELETING` | 4 | Being deleted. | | `SANDBOX_STATE_FAILED` | 5 | Failed; see `Sandbox.error`. | --- # Daemon: local runtimes cua.daemon.v1 RuntimeService: the local engines and images the cua daemon orchestrates. The local runtimes (gVisor, runc, QEMU, Lume) and images the `cua daemon` orchestrates. Source: `libs/cua/proto/cua/daemon/v1/runtime.proto`. ## RuntimeService `cua.daemon.v1.RuntimeService` Local runtime provisioning (`cua runtime doctor|setup`). | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`Doctor`](#runtimeservicedoctor) | [`DoctorRequest`](#doctorrequest) | [`DoctorResponse`](#doctorresponse) | unary | | [`Setup`](#runtimeservicesetup) | [`SetupRequest`](#setuprequest) | [`SetupResponse`](#setupresponse) | unary | | [`PullImage`](#runtimeservicepullimage) | [`PullImageRequest`](#pullimagerequest) | [`PullImageResponse`](#pullimageresponse) | unary | | [`BuildImage`](#runtimeservicebuildimage) | [`BuildImageRequest`](#buildimagerequest) | [`BuildImageResponse`](#buildimageresponse) | unary | | [`PushImage`](#runtimeservicepushimage) | [`PushImageRequest`](#pushimagerequest) | [`PushImageResponse`](#pushimageresponse) | unary | ### RuntimeService.Doctor `/cua.daemon.v1.RuntimeService/Doctor`, unary: [`DoctorRequest`](#doctorrequest) to [`DoctorResponse`](#doctorresponse). Inspects local runtimes (Lume, QEMU, container engine, runsc) without changing anything. ### RuntimeService.Setup `/cua.daemon.v1.RuntimeService/Setup`, unary: [`SetupRequest`](#setuprequest) to [`SetupResponse`](#setupresponse). Installs or starts missing local runtime components. Every step is explicit and logged in the response. ### RuntimeService.PullImage `/cua.daemon.v1.RuntimeService/PullImage`, unary: [`PullImageRequest`](#pullimagerequest) to [`PullImageResponse`](#pullimageresponse). Pulls an image into the local cache or container engine. ### RuntimeService.BuildImage `/cua.daemon.v1.RuntimeService/BuildImage`, unary: [`BuildImageRequest`](#buildimagerequest) to [`BuildImageResponse`](#buildimageresponse). Builds an image from an `images.cua.ai/v1alpha1` Image resource. ### RuntimeService.PushImage `/cua.daemon.v1.RuntimeService/PushImage`, unary: [`PushImageRequest`](#pushimagerequest) to [`PushImageResponse`](#pushimageresponse). Copies an image (host architecture) to another registry reference. ## Types in `cua/daemon/v1/runtime.proto` ### RuntimeCheck One doctor check. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Component, for example "lume", "qemu", "docker", "runsc". | | `status` | 2 | [`CheckStatus`](#checkstatus) | Status. | | `version` | 3 | `string` | Detected version, when present. | | `detail` | 4 | `string` | Explanation or remediation. | ### DoctorRequest Request for `RuntimeService.Doctor`. No fields. ### DoctorResponse Response for `RuntimeService.Doctor`. | Field | # | Type | Description | | --- | --- | --- | --- | | `checks` | 1 | repeated [`RuntimeCheck`](#runtimecheck) | Checks, in a stable order. | | `report_json` | 2 | `string` | The full host report (cua-vmm `DoctorReport`) as JSON. | ### SetupRequest Request for `RuntimeService.Setup`. | Field | # | Type | Description | | --- | --- | --- | --- | | `components` | 1 | repeated `string` | Components to set up. Empty means every installable component. | | `dry_run` | 2 | `bool` | Report the plan without changing anything. | ### SetupStep One setup step. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Component. | | `status` | 2 | [`CheckStatus`](#checkstatus) | Status after the step. | | `detail` | 3 | `string` | What was done, or would be done on a dry run. | ### SetupResponse Response for `RuntimeService.Setup`. | Field | # | Type | Description | | --- | --- | --- | --- | | `steps` | 1 | repeated [`SetupStep`](#setupstep) | Steps, in execution order. | ### LocalImage A local image. | Field | # | Type | Description | | --- | --- | --- | --- | | `reference` | 1 | `string` | Reference. | | `kind` | 2 | `string` | `container` or `vm`. | | `location` | 3 | `string` | Local disk path (VM images) or engine image reference (containers). | | `size_bytes` | 4 | `uint64` | Size in bytes, when known. | ### PullImageRequest Request for `RuntimeService.PullImage`. | Field | # | Type | Description | | --- | --- | --- | --- | | `reference` | 1 | `string` | Reference, with an optional backend prefix (`container:`, `vm:`). | ### PullImageResponse Response for `RuntimeService.PullImage`. | Field | # | Type | Description | | --- | --- | --- | --- | | `image` | 1 | [`LocalImage`](#localimage) | The pulled image. | ### BuildImageRequest Request for `RuntimeService.BuildImage`. | Field | # | Type | Description | | --- | --- | --- | --- | | `spec_json` | 1 | `string` | `images.cua.ai/v1alpha1` Image resource as JSON. | | `base` | 2 | `string` | Base image, with an optional backend prefix (`container:`, `vm:`, `disk:`). | | `push` | 3 | `string` | Push the result to this reference. Empty keeps it local. | ### BuildImageResponse Response for `RuntimeService.BuildImage`. | Field | # | Type | Description | | --- | --- | --- | --- | | `result` | 1 | `string` | `reference@digest` when pushed, else the local output directory. | ### PushImageRequest Request for `RuntimeService.PushImage`. | Field | # | Type | Description | | --- | --- | --- | --- | | `reference` | 1 | `string` | Source reference. | | `destination` | 2 | `string` | Destination reference. | ### PushImageResponse Response for `RuntimeService.PushImage`. | Field | # | Type | Description | | --- | --- | --- | --- | | `digest` | 1 | `string` | Pushed manifest digest. | ### CheckStatus Status of one local runtime component. | Value | # | Description | | --- | --- | --- | | `CHECK_STATUS_UNSPECIFIED` | 0 | Not reported. | | `CHECK_STATUS_OK` | 1 | Present and usable. | | `CHECK_STATUS_INSTALLABLE` | 2 | Missing, but `cua runtime setup` can install it. | | `CHECK_STATUS_ERROR` | 3 | Missing or broken and needs user action. | | `CHECK_STATUS_NOT_APPLICABLE` | 4 | Not applicable on this host. | --- # Daemon: Spaces cua.daemon.v1 SpaceService: Spaces and the hosts that provide them, through the local cua daemon. Spaces through the local `cua daemon`: create, list, power, delete, and the machines that provide Spaces. Source: `libs/cua/proto/cua/daemon/v1/spaces.proto`. ## SpaceService `cua.daemon.v1.SpaceService` Spaces: sandboxes with Spaces features (cua-spacesd, streams, agents, teleport) that the user works in interactively. The daemon hosts one `cua_spaces::Spaces` runtime: the registry in `~/.cua/spaces.json` (tokens in the 0600 `spaces-credentials.json`), Space creation in every location, hotspots, and the Spaces MCP server (`CallSpaceTool`, `cua daemon mcp`, `/mcp`). Verbs: `CreateSpace` / `DeleteSpace` create and delete the sandbox; `AddSpace` / `RemoveSpace` register an existing machine and forget it without touching it. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`AddSpace`](#spaceserviceaddspace) | [`AddSpaceRequest`](#addspacerequest) | [`AddSpaceResponse`](#addspaceresponse) | unary | | [`ListSpaces`](#spaceservicelistspaces) | [`ListSpacesRequest`](#listspacesrequest) | [`ListSpacesResponse`](#listspacesresponse) | unary | | [`RemoveSpace`](#spaceserviceremovespace) | [`RemoveSpaceRequest`](#removespacerequest) | [`RemoveSpaceResponse`](#removespaceresponse) | unary | | [`ResolveSpace`](#spaceserviceresolvespace) | [`ResolveSpaceRequest`](#resolvespacerequest) | [`ResolveSpaceResponse`](#resolvespaceresponse) | unary | | [`ClaimFleetSpace`](#spaceserviceclaimfleetspace) | [`ClaimFleetSpaceRequest`](#claimfleetspacerequest) | [`ClaimFleetSpaceResponse`](#claimfleetspaceresponse) | unary | | [`ProvisionLocalSpace`](#spaceserviceprovisionlocalspace) | [`ProvisionLocalSpaceRequest`](#provisionlocalspacerequest) | [`ProvisionLocalSpaceResponse`](#provisionlocalspaceresponse) | unary | | [`ReleaseSpace`](#spaceservicereleasespace) | [`ReleaseSpaceRequest`](#releasespacerequest) | [`ReleaseSpaceResponse`](#releasespaceresponse) | unary | | [`CreateSpace`](#spaceservicecreatespace) | [`CreateSpaceRequest`](#createspacerequest) | [`CreateSpaceResponse`](#createspaceresponse) | unary | | [`CreateSpaceStream`](#spaceservicecreatespacestream) | [`CreateSpaceStreamRequest`](#createspacestreamrequest) | [`CreateSpaceStreamResponse`](#createspacestreamresponse) | server stream | | [`CancelCreateSpace`](#spaceservicecancelcreatespace) | [`CancelCreateSpaceRequest`](#cancelcreatespacerequest) | [`CancelCreateSpaceResponse`](#cancelcreatespaceresponse) | unary | | [`GetGpuSupport`](#spaceservicegetgpusupport) | [`GetGpuSupportRequest`](#getgpusupportrequest) | [`GetGpuSupportResponse`](#getgpusupportresponse) | unary | | [`DeleteSpace`](#spaceservicedeletespace) | [`DeleteSpaceRequest`](#deletespacerequest) | [`DeleteSpaceResponse`](#deletespaceresponse) | unary | | [`ConnectSpace`](#spaceserviceconnectspace) | [`ConnectSpaceRequest`](#connectspacerequest) | [`ConnectSpaceResponse`](#connectspaceresponse) | unary | | [`ListSpaceTools`](#spaceservicelistspacetools) | [`ListSpaceToolsRequest`](#listspacetoolsrequest) | [`ListSpaceToolsResponse`](#listspacetoolsresponse) | unary | | [`CallSpaceTool`](#spaceservicecallspacetool) | [`CallSpaceToolRequest`](#callspacetoolrequest) | [`CallSpaceToolResponse`](#callspacetoolresponse) | unary | | [`ListHosts`](#spaceservicelisthosts) | [`ListHostsRequest`](#listhostsrequest) | [`ListHostsResponse`](#listhostsresponse) | unary | | [`GetSpaceThumbnail`](#spaceservicegetspacethumbnail) | [`GetSpaceThumbnailRequest`](#getspacethumbnailrequest) | [`GetSpaceThumbnailResponse`](#getspacethumbnailresponse) | unary | ### SpaceService.AddSpace `/cua.daemon.v1.SpaceService/AddSpace`, unary: [`AddSpaceRequest`](#addspacerequest) to [`AddSpaceResponse`](#addspaceresponse). Adds an existing machine as a Space by address (`host:port`, a direct `http(s)://host:port`, or a sandbox ref such as `cloud:<name>`) after a `GetCapabilities` handshake. ### SpaceService.ListSpaces `/cua.daemon.v1.SpaceService/ListSpaces`, unary: [`ListSpacesRequest`](#listspacesrequest) to [`ListSpacesResponse`](#listspacesresponse). Lists registered Spaces. ### SpaceService.RemoveSpace `/cua.daemon.v1.SpaceService/RemoveSpace`, unary: [`RemoveSpaceRequest`](#removespacerequest) to [`RemoveSpaceResponse`](#removespaceresponse). Forgets a Space. Does not delete the underlying sandbox. ### SpaceService.ResolveSpace `/cua.daemon.v1.SpaceService/ResolveSpace`, unary: [`ResolveSpaceRequest`](#resolvespacerequest) to [`ResolveSpaceResponse`](#resolvespaceresponse). Resolves any accepted spelling (id, legacy id, URL, name) to a Space. ### SpaceService.ClaimFleetSpace `/cua.daemon.v1.SpaceService/ClaimFleetSpace`, unary: [`ClaimFleetSpaceRequest`](#claimfleetspacerequest) to [`ClaimFleetSpaceResponse`](#claimfleetspaceresponse). **Deprecated.** Deprecated: use `CreateSpace` with location "cloud". ### SpaceService.ProvisionLocalSpace `/cua.daemon.v1.SpaceService/ProvisionLocalSpace`, unary: [`ProvisionLocalSpaceRequest`](#provisionlocalspacerequest) to [`ProvisionLocalSpaceResponse`](#provisionlocalspaceresponse). **Deprecated.** Deprecated: use `CreateSpace` with location "local". ### SpaceService.ReleaseSpace `/cua.daemon.v1.SpaceService/ReleaseSpace`, unary: [`ReleaseSpaceRequest`](#releasespacerequest) to [`ReleaseSpaceResponse`](#releasespaceresponse). **Deprecated.** Deprecated: use `DeleteSpace`. ### SpaceService.CreateSpace `/cua.daemon.v1.SpaceService/CreateSpace`, unary: [`CreateSpaceRequest`](#createspacerequest) to [`CreateSpaceResponse`](#createspaceresponse). Creates a Space (a sandbox, registered as a Space) where `location` says, with the same location / kind / runtime model as `SandboxService.CreateSandbox`. ### SpaceService.CreateSpaceStream `/cua.daemon.v1.SpaceService/CreateSpaceStream`, server stream: [`CreateSpaceStreamRequest`](#createspacestreamrequest) to [`CreateSpaceStreamResponse`](#createspacestreamresponse). `CreateSpace`, streaming what the create is doing: progress events (pulling the image, booting, waiting for cua-spacesd, connecting) in order, then exactly one `result`. A failed create ends the stream with its status after the progress it made. ### SpaceService.CancelCreateSpace `/cua.daemon.v1.SpaceService/CancelCreateSpace`, unary: [`CancelCreateSpaceRequest`](#cancelcreatespacerequest) to [`CancelCreateSpaceResponse`](#cancelcreatespaceresponse). Cancels a create that is still running, found by the caller's `create_id`, the id the Space will have (every progress event carries it in `space`) or its name: the work in flight stops (an image download, a boot, a claim, a relay registration) and what the create made is removed (its VM or container and disks, its cloud claim, its relay machine, its registry entry). Nothing that existed before is touched; finished image downloads stay cached so the next create resumes. Returns once the clean-up is done; the create itself ends with DAEMON_ERROR_REASON_CANCELLED. Idempotent, and works for a create another process runs or one a daemon restart cut off. ### SpaceService.GetGpuSupport `/cua.daemon.v1.SpaceService/GetGpuSupport`, unary: [`GetGpuSupportRequest`](#getgpusupportrequest) to [`GetGpuSupportResponse`](#getgpusupportresponse). The GPU options each runtime of a location offers on this host (the wizard's GPU row; `gpu` of `CreateSpace`). ### SpaceService.DeleteSpace `/cua.daemon.v1.SpaceService/DeleteSpace`, unary: [`DeleteSpaceRequest`](#deletespacerequest) to [`DeleteSpaceResponse`](#deletespaceresponse). Deletes a Space's sandbox and forgets it. A Space added by address (`direct:`, `relay:`) is only forgotten: cua did not create it. ### SpaceService.ConnectSpace `/cua.daemon.v1.SpaceService/ConnectSpace`, unary: [`ConnectSpaceRequest`](#connectspacerequest) to [`ConnectSpaceResponse`](#connectspaceresponse). Connects to a registered Space and returns a loopback env passthrough (`<loopback>/v1/spaces/<key>/env`) that carries the Space's credentials, so a client runs every `cua.env.v1` call and media WebSocket through the daemon without seeing the Space's token. ### SpaceService.ListSpaceTools `/cua.daemon.v1.SpaceService/ListSpaceTools`, unary: [`ListSpaceToolsRequest`](#listspacetoolsrequest) to [`ListSpaceToolsResponse`](#listspacetoolsresponse). Lists the Spaces MCP tools (`tools/list` result as JSON). ### SpaceService.CallSpaceTool `/cua.daemon.v1.SpaceService/CallSpaceTool`, unary: [`CallSpaceToolRequest`](#callspacetoolrequest) to [`CallSpaceToolResponse`](#callspacetoolresponse). Calls one Spaces MCP tool (the same implementation `cua daemon mcp` and `/mcp` serve). ### SpaceService.ListHosts `/cua.daemon.v1.SpaceService/ListHosts`, unary: [`ListHostsRequest`](#listhostsrequest) to [`ListHostsResponse`](#listhostsresponse). Your machines that provide Spaces (the account's relay hosts and the hosts added by their Tailscale or LAN address), each asked for its limits: what `CreateSpace` takes as `on="host:<id>"` (the New Space wizard's Run on menu). One that does not answer is listed offline when it provided a Space before. ### SpaceService.GetSpaceThumbnail `/cua.daemon.v1.SpaceService/GetSpaceThumbnail`, unary: [`GetSpaceThumbnailRequest`](#getspacethumbnailrequest) to [`GetSpaceThumbnailResponse`](#getspacethumbnailresponse). The Space's latest thumbnail (a small JPEG of its primary display) from the daemon's cache, which every client on this machine shares: the cached image when it is younger than `max_age`, else a fresh capture through cua-spacesd (the cached one again, with its older `captured_at`, when the capture fails). Asking keeps the daemon refreshing running Spaces' thumbnails in the background for a while. ## Types in `cua/daemon/v1/spaces.proto` ### Space A registered Space. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | Space id, for example "space://direct/10.0.0.5:3211". | | `name` | 2 | `string` | Display name. | | `spacesd_version` | 3 | `string` | spacesd version reported at the last handshake. | | `features` | 4 | repeated `string` | Supported feature names reported at the last handshake. | | `added_at` | 5 | `google.protobuf.Timestamp` | When it was added. | | `os` | 6 | `string` | Guest OS family at the last handshake (GetCapabilities): "linux", "macos" or "windows"; empty when not reported. | | `services` | 7 | repeated `string` | Services the Space declares (never "env"), for example "mcp". A Space without cua-spacesd has an empty feature list and reaches these through generic MCP (`list_tools` / `call_tool` with `service`). | | `os_name` | 8 | `string` | Guest OS product or distribution at the last handshake (GetCapabilities `os.name`), for example "Ubuntu" or "macOS"; empty when not reported. The Spaces app picks the OS icon from it. | | `os_pretty_name` | 9 | `string` | The guest's full OS string at the last handshake (GetCapabilities `os.pretty_name`), for example "Ubuntu 24.04.3 LTS"; empty when the driver does not report one. | | `image` | 10 | `string` | The image the sandbox runs, as requested (local and cloud Spaces), for example "ghcr.io/trycua/linux:24.04"; empty when unknown (a Space added by address). | | `image_digest` | 11 | `string` | The digest of the variant that runs ("sha256:..."), when known. | | `kind` | 12 | `string` | Container or virtual machine: "container" or "vm" (the guest's runtime at the last handshake, else the local sandbox's record); empty when unknown. | | `arch` | 13 | `string` | The guest's CPU architecture: "arm64" or "amd64" (GetCapabilities `arch` at the last handshake, else the local sandbox's record); empty when unknown. | | `host` | 14 | `string` | For a Space a host provides (`relay:<machine>` created with `on="host:<machine>"`): the relay machine id of that host; for one a host added by its direct address created (`direct:<addr>:<port>`): that host's Space id (`direct:<addr>:<port>`); empty otherwise. Lists group these Spaces under their host. | | `host_name` | 15 | `string` | The display name of `host`, when known. | | `power` | 16 | `string` | How the Space turns off and on again (`stop_space` / `start_space`): "suspend" (its memory is kept), "stop" (its disk is kept), or empty when it cannot. Filled in when listed, not stored. | | `power_state` | 17 | `string` | "running", "suspended" or "stopped" as cua last recorded it; empty when unknown. Filled in when listed, not stored. | | `cloud` | 18 | `string` | A Space in the owner's own cloud: the provider ("aws", "gcp", "modal"); empty otherwise. | | `cloud_place` | 19 | `string` | Where it runs, for people ("AWS · us-west-2"); empty otherwise. | | `cloud_delete` | 20 | `string` | How this device would delete it permanently: "here" (this cua home created it and records it), "host:<machine>" (ask the device that created it, through the relay) or "elsewhere" (only from the device that created it). Empty for other Spaces. | ### AddSpaceRequest Request for `SpaceService.AddSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `url` | 1 | `string` | Space URL. | | `token` | 2 | `string` | spacesd token (direct Spaces). | | `name` | 3 | `string` | Display name. Empty derives one. | | `service` | 4 | `string` | When `url` is not a cua-spacesd but an MCP endpoint: the service name it is registered under. Empty means "mcp". | ### AddSpaceResponse Response for `SpaceService.AddSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | [`Space`](#space) | The Space. | ### ListSpacesRequest Request for `SpaceService.ListSpaces`. No fields. ### ListSpacesResponse Response for `SpaceService.ListSpaces`. | Field | # | Type | Description | | --- | --- | --- | --- | | `spaces` | 1 | repeated [`Space`](#space) | Spaces. | ### RemoveSpaceRequest Request for `SpaceService.RemoveSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | Space id. | ### RemoveSpaceResponse Response for `SpaceService.RemoveSpace`. No fields. ### ResolveSpaceRequest Request for `SpaceService.ResolveSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | Id, legacy id, URL or display name. | ### ResolveSpaceResponse Response for `SpaceService.ResolveSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | [`Space`](#space) | The registered Space. | ### ClaimFleetSpaceRequest Request for `SpaceService.ClaimFleetSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `image` | 1 | `string` | Image. Empty uses the default Spaces image for the runtime. | | `runtime` | 2 | `string` | Fleet runtime (`kubevirt`, `gvisor`). Empty picks from the image. | | `name` | 3 | `string` | Display name. | | `wait` | 4 | optional `bool` | Wait for the claim to bind (default true when unset). | | `reuse` | 5 | `bool` | Reuse a reachable registered Fleet Space for the same image first. | | `command` | 6 | repeated `string` | Entrypoint override (pod runtimes). Empty keeps the image's. | | `env` | 7 | map of `string` to `string` | Guest environment. | | `services` | 8 | map of `string` to `uint32` | Named services (name -> guest port), for example {"mcp": 8765}. | | `spacesd` | 9 | optional `bool` | Whether the image runs cua-spacesd. Unset: yes for the Spaces images, no for any other image. | ### ClaimFleetSpaceResponse Response for `SpaceService.ClaimFleetSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | [`Space`](#space) | The Space, once bound. | | `pending_json` | 2 | `string` | With `wait = false`: the pending claim as JSON (`{namespace, claim, pool, ...}`). | | `reused` | 3 | `bool` | A registered Space was reused. | ### ProvisionLocalSpaceRequest Request for `SpaceService.ProvisionLocalSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `image` | 1 | `string` | Image. Empty uses the default Spaces image. | | `name` | 2 | `string` | Name. Empty generates one. | | `cpus` | 3 | `uint32` | vCPUs (0 = 2). | | `memory_mb` | 4 | `uint64` | Memory in MiB (0 = 4096). | | `timeout` | 5 | `google.protobuf.Duration` | Provisioning budget (default 600 s). | | `command` | 6 | repeated `string` | Entrypoint override. Empty keeps the image's. | | `env` | 7 | map of `string` to `string` | Guest environment. | | `services` | 8 | map of `string` to `uint32` | Named services (name -> guest port), for example {"mcp": 8765}. | | `spacesd` | 9 | optional `bool` | Whether the image runs cua-spacesd. Unset: yes for the Spaces images, no for any other image. | ### ProvisionLocalSpaceResponse Response for `SpaceService.ProvisionLocalSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | [`Space`](#space) | The Space. | ### ReleaseSpaceRequest Request for `SpaceService.ReleaseSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | Space. | ### ReleaseSpaceResponse Response for `SpaceService.ReleaseSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `message` | 1 | `string` | What happened, for humans. | ### ConnectSpaceRequest Request for `SpaceService.ConnectSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | Space. | ### ConnectSpaceResponse Response for `SpaceService.ConnectSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | [`Space`](#space) | The Space. | | `env_url` | 2 | `string` | env passthrough base URL (speaks `cua.env.v1`, gRPC and gRPC-Web, and relays the `/media` and `/tunnel` WebSockets). | | `token` | 3 | `string` | Bearer for `env_url` (the daemon's loopback token). | | `capabilities` | 4 | `bytes` | `cua.env.v1.GetCapabilitiesResponse` of the Space, protobuf-encoded. | | `services` | 5 | repeated [`SpaceServiceEndpoint`](#spaceserviceendpoint) | The Space's declared services through the daemon (same bearer as `env_url`). `env_url` is empty when the Space runs no cua-spacesd. | ### SpaceServiceEndpoint A declared service of a Space, reachable through the daemon. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Service name, for example "mcp". | | `url` | 2 | `string` | Base URL: requests to `<url><path>` reach `<path>` on the service. | | `mcp_path` | 3 | `string` | Its MCP endpoint path, for example "/mcp". | ### ListSpaceToolsRequest Request for `SpaceService.ListSpaceTools`. No fields. ### ListSpaceToolsResponse Response for `SpaceService.ListSpaceTools`. | Field | # | Type | Description | | --- | --- | --- | --- | | `tools_json` | 1 | `string` | MCP `tools/list` result as JSON. | ### CallSpaceToolRequest Request for `SpaceService.CallSpaceTool`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Tool name from the Spaces contract. | | `arguments_json` | 2 | `string` | Arguments as a JSON object. Empty means `{}`. | ### CallSpaceToolResponse Response for `SpaceService.CallSpaceTool`. | Field | # | Type | Description | | --- | --- | --- | --- | | `content_json` | 1 | `string` | MCP content parts as a JSON array. | | `structured_json` | 2 | `string` | MCP structured content as JSON, when present. | | `is_error` | 3 | `bool` | The call failed as a tool error. | | `meta_json` | 4 | `string` | The result's `_meta` as JSON, when present (forwarded verbatim from an in-Space MCP service). | ### CreateSpaceRequest Request for `SpaceService.CreateSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `image` | 1 | `string` | Image. Empty uses the canonical Linux image. | | `location` | 2 | `string` | Where it runs: "local", "cloud" or a registered provider. Empty uses the default (`default.on` in the config, `CUA_DEFAULT_ON`, else "local"); clients resolve the default on their side. | | `kind` | 3 | `string` | What kind of machine: "auto" (empty), "container" or "vm". | | `runtime` | 4 | `string` | Which engine: "auto" (empty), or one the location offers for the kind ("gvisor", "runc", "qemu", "lume" locally; "gvisor", "kubevirt" in the cloud). | | `name` | 5 | `string` | Name. Empty generates `space-<hex>`. | | `cpus` | 6 | `uint32` | vCPUs (0 = the default, 2). | | `memory_mb` | 7 | `uint64` | Memory in MiB (0 = the default, 4096). | | `timeout` | 8 | `google.protobuf.Duration` | Readiness budget (default 600 s). | | `wait` | 9 | optional `bool` | Wait until the Space is ready (default true when unset). With false the response carries only its id and phase "starting". | | `reuse` | 10 | `bool` | Return a reachable registered Space with the same image, location, kind and runtime instead of creating one (get-or-create). | | `command` | 11 | repeated `string` | Entrypoint override. Empty keeps the image's. | | `env` | 12 | map of `string` to `string` | Guest environment. | | `services` | 13 | map of `string` to `uint32` | Named services (name -> guest port), for example {"mcp": 8765}. | | `spacesd` | 14 | optional `bool` | Whether the image runs cua-spacesd. Unset: yes for the canonical images, no for any other image. | | `disk_gb` | 15 | `uint32` | Grow the VM's disk to this many GiB (local VMs only; 0 keeps the image's size). Lume grows the macOS APFS container before the first boot; Linux images grow their root partition at boot (cloud-init growpart). | | `gpu` | 16 | `string` | A GPU option of the runtime the Space runs on (`GetGpuSupport`): "paravirtual" (a macOS VM on Lume: GPU acceleration, experimental), "virgl", "nvidia", a provider's GPU type; "auto" picks the runtime's own. Empty: no GPU. Kept with the Space. | | `create_id` | 17 | `string` | The caller's own key for this create (an app's pending row id), for `CancelCreateSpace` before the Space's id is known. | ### CreateSpaceResponse Response for `SpaceService.CreateSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | [`Space`](#space) | The Space (with `wait = false`: only its id). | | `phase` | 2 | `string` | "ready", or "starting" with `wait = false`. | | `reused` | 3 | `bool` | `reuse` returned an existing Space. | ### CreateSpaceStreamRequest Request for `SpaceService.CreateSpaceStream`. | Field | # | Type | Description | | --- | --- | --- | --- | | `create` | 1 | [`CreateSpaceRequest`](#createspacerequest) | The create, as for `CreateSpace`. `wait` is ignored: the stream always runs until the Space is ready or the create fails. | ### CreateSpaceProgress What a create is doing. | Field | # | Type | Description | | --- | --- | --- | --- | | `phase` | 1 | `string` | "preparing", "pulling", "creating", "booting", "waiting_for_services", "connecting" or "ready", in that order. | | `fraction` | 2 | optional `double` | How far through the phase, 0.0 to 1.0, when its size is known (an image pull). | | `detail` | 3 | `string` | One short line, for example the image being pulled. | | `bytes_done` | 4 | optional `uint64` | Bytes downloaded so far, when the step counts them (an image pull). | | `bytes_total` | 5 | optional `uint64` | Bytes the download has in all. | | `bytes_per_second` | 6 | optional `double` | Smoothed download rate, bytes per second. | | `space` | 7 | `string` | The id the Space will have (`local:<name>`), for `CancelCreateSpace`. | ### CreateSpaceStreamResponse One message of `SpaceService.CreateSpaceStream`. | Field | # | Type | Description | | --- | --- | --- | --- | | `progress` | 1 | [`CreateSpaceProgress`](#createspaceprogress) (oneof `event`) | What the create is doing now. | | `result` | 2 | [`CreateSpaceResponse`](#createspaceresponse) (oneof `event`) | The created Space (the last message). | ### CancelCreateSpaceRequest Request for `SpaceService.CancelCreateSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | The create's `create_id`, the id the Space will have, or its name. | ### CancelCreateSpaceResponse Response for `SpaceService.CancelCreateSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | The Space id the create had, when known. | | `state` | 2 | `string` | "cancelled", "not_creating" (nothing by that key is running) or "already_created" (it finished; delete it instead). | | `message` | 3 | `string` | What was removed and what stays, for people. | ### GetGpuSupportRequest Request for `SpaceService.GetGpuSupport`. | Field | # | Type | Description | | --- | --- | --- | --- | | `location` | 1 | `string` | "local" (empty) or "cloud". | ### GpuOption One GPU option of a runtime. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | What to pass as `gpu`. | | `label` | 2 | `string` | How a person names it ("GPU acceleration"). | | `experimental` | 3 | `bool` | Experimental. | | `supported` | 4 | `bool` | Works on this host (and account) now. | | `reason` | 5 | `string` | Why not, one short line. | | `learn_more` | 6 | `string` | A page that explains it. | | `usd_per_hour` | 7 | optional `double` | Estimated cost per hour while it runs (cloud GPU types). | ### GpuSupport The GPU options of one runtime. | Field | # | Type | Description | | --- | --- | --- | --- | | `runtime` | 1 | `string` | "lume", "qemu", "container", "gvisor", "fleet", a provider's name. | | `options` | 2 | repeated [`GpuOption`](#gpuoption) | Its options; empty: none. | | `reason` | 3 | `string` | Why it has none. | ### GetGpuSupportResponse Response for `SpaceService.GetGpuSupport`. | Field | # | Type | Description | | --- | --- | --- | --- | | `runtimes` | 1 | repeated [`GpuSupport`](#gpusupport) | One entry per runtime of the location. | ### DeleteSpaceRequest Request for `SpaceService.DeleteSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | Space. | ### DeleteSpaceResponse Response for `SpaceService.DeleteSpace`. | Field | # | Type | Description | | --- | --- | --- | --- | | `message` | 1 | `string` | What happened, for humans. | ### ListHostsRequest Request for `SpaceService.ListHosts`. No fields. ### SpacesHostLimit One limit of a host and how much of it is used. | Field | # | Type | Description | | --- | --- | --- | --- | | `resource` | 1 | `string` | `spaces` (every Space it provides) or `macos_vms` (macOS VMs on that Mac). | | `used` | 2 | `uint32` | In use now. | | `limit` | 3 | `uint32` | The limit (0: none). | | `reason` | 4 | `string` | Why the limit exists, for people. | ### SpacesHost One of your machines that provides Spaces. | Field | # | Type | Description | | --- | --- | --- | --- | | `id` | 1 | `string` | What `on="host:<id>"` takes: the relay machine id, or the name a direct host was added as. | | `name` | 2 | `string` | Its name. | | `via` | 3 | `string` | `relay` or `direct`. | | `online` | 4 | `bool` | It answered. | | `os` | 5 | `string` | Its operating system (`macos`, `linux`, `windows`), when it answered. | | `limits` | 6 | repeated [`SpacesHostLimit`](#spaceshostlimit) | Its limits, when it answered. | ### ListHostsResponse Response for `SpaceService.ListHosts`. | Field | # | Type | Description | | --- | --- | --- | --- | | `hosts` | 1 | repeated [`SpacesHost`](#spaceshost) | In order: relay hosts, then direct ones. | ### GetSpaceThumbnailRequest Request for `SpaceService.GetSpaceThumbnail`. | Field | # | Type | Description | | --- | --- | --- | --- | | `space` | 1 | `string` | Space. | | `max_age` | 2 | `google.protobuf.Duration` | How old a cached thumbnail may be (unset: any age; zero: always capture). | ### GetSpaceThumbnailResponse Response for `SpaceService.GetSpaceThumbnail`. | Field | # | Type | Description | | --- | --- | --- | --- | | `image` | 1 | `bytes` | Encoded image. | | `format` | 2 | `string` | "jpeg". | | `width` | 3 | `uint32` | Pixel width. | | `height` | 4 | `uint32` | Pixel height. | | `captured_at` | 5 | `google.protobuf.Timestamp` | When it was captured. | --- # Daemon: service cua.daemon.v1 DaemonService: the local cua daemon identity and the loopback media bridge for webviews. The `cua daemon` identity (`GetInfo`) and the loopback media bridge for webviews. Source: `libs/cua/proto/cua/daemon/v1/daemon.proto`. ## DaemonService `cua.daemon.v1.DaemonService` The daemon itself: identity, the loopback bridge for webviews, shutdown. | RPC | Request | Response | Kind | | --- | --- | --- | --- | | [`GetInfo`](#daemonservicegetinfo) | [`GetInfoRequest`](#getinforequest) | [`GetInfoResponse`](#getinforesponse) | unary | | [`OpenMediaBridge`](#daemonserviceopenmediabridge) | [`OpenMediaBridgeRequest`](#openmediabridgerequest) | [`OpenMediaBridgeResponse`](#openmediabridgeresponse) | unary | | [`Shutdown`](#daemonserviceshutdown) | [`ShutdownRequest`](#shutdownrequest) | [`ShutdownResponse`](#shutdownresponse) | unary | ### DaemonService.GetInfo `/cua.daemon.v1.DaemonService/GetInfo`, unary: [`GetInfoRequest`](#getinforequest) to [`GetInfoResponse`](#getinforesponse). Returns the daemon identity and its loopback endpoint. ### DaemonService.OpenMediaBridge `/cua.daemon.v1.DaemonService/OpenMediaBridge`, unary: [`OpenMediaBridgeRequest`](#openmediabridgerequest) to [`OpenMediaBridgeResponse`](#openmediabridgeresponse). Opens a media session in a sandbox and returns a loopback WebSocket URL that relays it. The URL carries a short-lived bridge ticket; the browser never sees the Fleet bearer or the spacesd token. ### DaemonService.Shutdown `/cua.daemon.v1.DaemonService/Shutdown`, unary: [`ShutdownRequest`](#shutdownrequest) to [`ShutdownResponse`](#shutdownresponse). Stops the daemon after the response is sent. ## Types in `cua/daemon/v1/daemon.proto` ### DaemonErrorInfo Structured error detail of daemon RPCs. | Field | # | Type | Description | | --- | --- | --- | --- | | `reason` | 1 | [`DaemonErrorReason`](#daemonerrorreason) | Reason. | | `message` | 2 | `string` | Human-readable message. | | `metadata` | 3 | map of `string` to `string` | Extra context. | ### GetInfoRequest Request for `DaemonService.GetInfo`. No fields. ### GetInfoResponse Response for `DaemonService.GetInfo`. | Field | # | Type | Description | | --- | --- | --- | --- | | `version` | 1 | `string` | cua-daemon version. | | `pid` | 2 | `uint32` | Process id. | | `socket_path` | 3 | `string` | Unix socket path, when listening on one. | | `loopback_url` | 4 | `string` | Loopback base URL (`http://127.0.0.1:<port>`) serving the same services, the env passthrough and the media bridge. | | `loopback_token` | 5 | `string` | Bearer token of the loopback listener. | | `features` | 6 | repeated `string` | Compiled-in SDK features, for example "env", "fleet", "local". | | `started_at` | 7 | `google.protobuf.Timestamp` | When the daemon started. | | `executable` | 8 | `string` | The daemon's executable (symlinks resolved), so a client bound to an app bundle can tell its own daemon from another app's. Empty from daemons that predate it. | | `build_id` | 9 | `string` | The executable's build as it was when the daemon started (version, size and modification time), so a client can tell a daemon whose executable was rebuilt or updated since. Empty from daemons that predate it. | ### OpenMediaBridgeRequest Request for `DaemonService.OpenMediaBridge`. | Field | # | Type | Description | | --- | --- | --- | --- | | `name` | 1 | `string` | Sandbox name. | | `open_media_json` | 2 | `string` | `cua.env.v1.OpenMediaRequest` in canonical proto3 JSON. Empty opens the primary display with defaults. | ### OpenMediaBridgeResponse Response for `DaemonService.OpenMediaBridge`. | Field | # | Type | Description | | --- | --- | --- | --- | | `ws_url` | 1 | `string` | Loopback WebSocket URL, for example "ws://127.0.0.1:52011/v1/bridge/media?ticket=...". Speaks rcdp wire v2 unchanged. | | `ticket` | 2 | `string` | Bridge ticket (also in `ws_url`). | | `expires_at` | 3 | `google.protobuf.Timestamp` | When the bridge ticket stops accepting new attaches. | | `open_media_response_json` | 4 | `string` | `cua.env.v1.OpenMediaResponse` in canonical proto3 JSON, with the upstream ticket removed. | ### ShutdownRequest Request for `DaemonService.Shutdown`. No fields. ### ShutdownResponse Response for `DaemonService.Shutdown`. No fields. ### DaemonErrorReason Why a daemon call failed. Packed as `DaemonErrorInfo` in the `google.rpc.Status` details of every daemon error. | Value | # | Description | | --- | --- | --- | | `DAEMON_ERROR_REASON_UNSPECIFIED` | 0 | Unknown. | | `DAEMON_ERROR_REASON_INVALID_ARGUMENT` | 1 | Bad input. | | `DAEMON_ERROR_REASON_NOT_FOUND` | 2 | No such sandbox, forward or bridge. | | `DAEMON_ERROR_REASON_PROVIDER_NOT_CONFIGURED` | 3 | The provider is not configured in the daemon (for example no Fleet credentials). | | `DAEMON_ERROR_REASON_UNSUPPORTED` | 4 | The provider or build cannot do this. | | `DAEMON_ERROR_REASON_SPACESD_NOT_AVAILABLE` | 5 | No cua-spacesd answered in the sandbox. | | `DAEMON_ERROR_REASON_TIMEOUT` | 6 | A deadline elapsed. | | `DAEMON_ERROR_REASON_FLEET` | 7 | Fleet API failure. | | `DAEMON_ERROR_REASON_RUNTIME` | 8 | Local runtime failure. | | `DAEMON_ERROR_REASON_ENV` | 9 | spacesd call failure. | | `DAEMON_ERROR_REASON_HTTP` | 10 | HTTP failure talking to a sandbox service. | | `DAEMON_ERROR_REASON_INTERNAL` | 11 | Daemon bug or I/O failure. | | `DAEMON_ERROR_REASON_UNAUTHENTICATED` | 12 | Missing or wrong daemon token. | | `DAEMON_ERROR_REASON_CAPABILITY_MISSING` | 13 | The Space's spacesd reports a feature the call needs as unsupported. | | `DAEMON_ERROR_REASON_HOST_CAPABILITY_MISSING` | 14 | A host-side prerequisite (app-session providers, an operator display, a local runtime) is not available. | | `DAEMON_ERROR_REASON_TELEPORT_REFUSED` | 15 | The teleport consent gate refused. | | `DAEMON_ERROR_REASON_POOL_SPEC_MISMATCH` | 16 | A named cloud pool's template differs from the requested sandbox fields (pass apply to update it). | | `DAEMON_ERROR_REASON_CLAIM_SECRETS_NOT_DELIVERED` | 17 | A claim bound but its per-claim secrets never reached the sandbox; the claim was released. | | `DAEMON_ERROR_REASON_AMBIGUOUS_SANDBOX` | 18 | A bare sandbox name matches sandboxes in more than one location. The qualified candidates are in `DaemonErrorInfo.metadata["candidates"]` (comma-separated, for example "local:box,cloud:box"). | | `DAEMON_ERROR_REASON_INSUFFICIENT_DISK` | 19 | A pull, build or VM create would leave less than the configured minimum free disk space; nothing was written (`cua cache prune` frees space). | | `DAEMON_ERROR_REASON_INVALID_PLACEMENT` | 20 | A location, kind or runtime that does not exist, or a combination of them (or with the image) that does not. The accepted values are in `DaemonErrorInfo.metadata["valid"]` (comma-separated, for example "auto,gvisor,runc") and the axis in `metadata["axis"]` ("on", "kind", "runtime" or "image"). | | `DAEMON_ERROR_REASON_FLEET_ADMISSION_DENIED` | 21 | Fleet's admission refused a write, for example a sandbox size over the account's limits. The message is Fleet's and names the limit. | | `DAEMON_ERROR_REASON_CLOUD_CREDIT_EXHAUSTED` | 22 | The account is out of Cua Cloud credit (no credit left, and no card or plan): new cloud sandboxes are refused, running ones keep running. The message ends with the website billing page's URL. | | `DAEMON_ERROR_REASON_CANCELLED` | 23 | The create was cancelled (`CancelCreateSpace`, or its caller went away); what it made is gone. The message says what was removed and what stays (finished image downloads stay cached). | | `DAEMON_ERROR_REASON_CLOUD` | 24 | Your own cloud account (AWS, Google Cloud, Modal) refused or failed a call Cua made there: a missing permission, a quota, a region without the machine type. The message names the cloud's own error. | --- # Cua Driver Drive native apps on macOS, Windows and Linux in the background. Cua Driver gives an agent the apps on a real computer. It reads each window's accessibility tree and screenshot, then clicks, types, scrolls, and drives browser tabs, in the background where the operating system allows it. The person at the keyboard keeps their focus and cursor; the agent gets its own cursor overlay. Use it from any agent through **MCP** (`cua-driver mcp`), from a shell through the **CLI** (`cua-driver call <tool>`), or inside your own app through the Python and TypeScript **SDK** (`CuaDriver.create()`). [Real desktop workflows with GPT-6 Astra + Cua Driver](<https://github.com/user-attachments/assets/e9344780-e780-46e7-bcce-91f61eda9599>) Selected interactions from QGIS, Preview, GIMP, and Inkscape, with the recorded agent cursor and action captions. Recorded on macOS with GPT-6 Astra + Cua Driver; selected gestures at 1x, with setup and waits cut. Try them in [Recipes](</docs/cua-driver/guides/recipes>). ## Platforms | Platform | Requirements | Automation APIs | Status | | ------------- | -------------------------------------------------------------------- | ------------------------------------------------- | -------------------------------- | | macOS | macOS 14+, Apple Silicon or Intel; Accessibility and Screen Recording | Accessibility (AX), ScreenCaptureKit, Quartz | Supported | | Windows | Windows 10/11 or Server, interactive desktop session | UI Automation, Win32 input and window messages | Supported | | Linux X11 | x86_64 desktop session, AT-SPI 2 | AT-SPI, X11/EWMH, XTest | Supported with toolkit limits | | Linux Wayland | x86_64 desktop session, AT-SPI 2 | AT-SPI, compositor-specific capture and input | Supported with compositor limits | See [Platform support](</docs/cua-driver/concepts/platform-support>) for proven surfaces and limits. ## Choose an integration Pick by who owns the runtime: the process that holds the OS permissions, sessions, and browser connections. | You are building | Use | Runtime owner | | --------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------- | | An agent or harness (Claude Code, Codex, Cursor, …) | [MCP](</docs/cua-driver/guides/connect-your-agent>): `cua-driver mcp` | The MCP process; on macOS, the `CuaDriver.app` daemon | | An agent that can only run shell commands | [CLI](</docs/cua-driver/reference/cli>): `cua-driver call <tool>` | The `cua-driver serve` daemon | | An app with built-in computer use | [SDK](</docs/cua-driver/guides/use-the-sdk>): `CuaDriver.create()` | Your app process | | A signed desktop app that serves an external agent | [App-hosted service](</docs/cua-driver/guides/use-the-sdk#expose-mcp-from-a-desktop-app>) | The permission-owning desktop app | | Several clients sharing one desktop | `cua-driver serve --socket <path>` plus `cua-driver mcp --socket <path>` | The daemon | Inside a [Cua Sandbox](</docs/cua-sdk>) you do not install Cua Driver: the sandbox's guest service bundles its tools. ## Next - [Quickstart](</docs/cua-driver/quickstart>): install and drive your first app. - [Connect your agent](</docs/cua-driver/guides/connect-your-agent>): every supported harness. - [How Cua Driver works](</docs/cua-driver/concepts/how-cua-driver-works>): background delivery, targets, and sessions. --- # Quickstart Install Cua Driver, connect your agent, and have it compute 6 × 7 in a desktop calculator. Install Cua Driver on your computer and let your agent operate the calculator app, in the background. ## Prerequisites - macOS 14+, Windows 10/11, or an x86_64 Linux desktop (X11 or XWayland, with AT-SPI 2) - An MCP-capable agent, such as Claude Code or Codex - A desktop calculator app ## Run **1. Install and start the daemon.** No administrator access needed. **macOS** ```bash test="cli-shape" id="cua-driver-permissions-grant" /bin/bash -c "$(curl -fsSL https://cua.ai/driver/install.sh)" open -n -g -a CuaDriver --args serve cua-driver permissions grant ``` Starting through the app bundle makes macOS attribute the prompts to CuaDriver. Choose **Open System Settings** and switch CuaDriver on under **Accessibility** and **Screen & System Audio Recording**; accept the relaunch. Rerun `cua-driver permissions grant` if only one prompt appeared. ![CuaDriver listed under Accessibility in System Settings with its toggle switched off](https://github.com/user-attachments/assets/d149fe5a-28cb-460c-8868-4eda80d540a4) **Windows** ```powershell test="cli-shape" id="cua-driver-autostart-kick" irm https://cua.ai/driver/install.ps1 | iex cua-driver autostart kick ``` Run it from your desktop session. Over SSH, see [Windows over SSH](</docs/cua-driver/guides/vms-and-remote#windows-over-ssh>). **Linux** ```bash test="cli-shape" id="cua-driver-serve" /bin/bash -c "$(curl -fsSL https://cua.ai/driver/install.sh)" cua-driver serve ``` Run it in a terminal inside the desktop session and leave it open. On a minimal image, first `sudo apt install libxi6 at-spi2-core`. On GNOME, also install the bundled WinRects helper (no `sudo` needed), then log out and back in once so Shell loads it: ```bash skip="host-install" ~/.cua-driver/packages/current/wayland-helper/install.sh gnome-extensions info winrects@cua # State: ACTIVE ``` **2. Check that it sees your apps** (in a second terminal): ```bash test="cli-shape" id="cua-driver-call" cua-driver call list_apps ``` You should see the GUI apps you have open. An empty list means the driver cannot reach the desktop; run `cua-driver doctor`. **3. Connect your agent and ask.** To set up every agent it finds at once (skill and MCP server), run `curl -fsSL https://cua.ai/install.sh | sh -s -- --select cua-driver` instead; it also installs the `cua` CLI. **Claude Code** ```bash test="cli-shape" id="cua-driver-skills-install" cua-driver skills install ``` **Codex** ```bash test="cli-shape" id="cua-driver-mcp-config-codex" cua-driver mcp-config --client codex ``` Run the `codex mcp add …` command it prints. **Other** See [Connect your agent](</docs/cua-driver/guides/connect-your-agent>). Start a fresh agent session and send: ```text output Using Cua Driver, open the installed calculator app, compute 6 × 7, and read the displayed result back from a fresh snapshot. ``` ## Result The calculator shows **42** and the agent reports it from the app. Your own window and pointer stay where they were: the moving cursor is the agent's overlay. Some apps only accept foreground input; see [How Cua Driver works](</docs/cua-driver/concepts/how-cua-driver-works>). ## Clean up ```bash test="cli-shape" id="cua-driver-stop" cua-driver stop ``` ## Next - [Connect your agent](</docs/cua-driver/guides/connect-your-agent>): every supported harness and local models. - [Recipes](</docs/cua-driver/guides/recipes>): real workflows with prompts and checks. - [Use the SDK](</docs/cua-driver/guides/use-the-sdk>): drive apps from Python or TypeScript. --- # Connect your agent Register Cua Driver with Claude Code, Codex, Cursor, Hermes, OpenClaw, local models, and agent SDKs. An agent reaches Cua Driver in one of two ways: an MCP client launches `cua-driver mcp`, or a skill teaches a shell-capable agent to run `cua-driver call <tool>`. The harness owns the model and the loop; Cua Driver never calls a model API. ## Register the MCP server To add the skill and MCP server to every agent on the machine in one step, use the cua installer: `curl -fsSL https://cua.ai/install.sh | sh -s -- --select cua-driver`. `mcp-config` prints the exact command or JSON for your client, with your installed binary's absolute path. Run what it prints, then restart the client: ```bash test="cli-shape" id="cua-driver-mcp-config" cua-driver mcp-config --client <client> ``` | Harness | Setup | Notes | | --- | --- | --- | | Claude Code | `claude mcp add --transport stdio cua-driver -- cua-driver mcp`, or `--client claude` for the computer-use compatibility profile | Install the skill for native skill activation. | | Codex (CLI, IDE, app) | `--client codex`, which prints `codex mcp add cua-driver -- <path> mcp` | Codex's own approval policy also applies. | | Cursor | `--client cursor`; paste into `~/.cursor/mcp.json` or `.cursor/mcp.json` | | | Antigravity CLI (Gemini) | `--client antigravity`; merge into `~/.gemini/config/mcp_config.json` | `--client gemini` is a legacy alias. | | OpenClaw | `--client openclaw`, which prints `openclaw mcp set cua-driver '{…}'` | On macOS, a gateway-spawned server does not inherit OpenClaw.app's grants. | | Hermes | Built in: `hermes tools enable computer_use --platform cli`, then `hermes -t computer_use chat` | Repair with `hermes computer-use install` and `doctor`. Do not add a second raw MCP server. | | Prime Agent | `cua-driver skills install`, then `/reload` and `/skill:cua-driver` | Calls the CLI; no MCP. | | OpenCode, Factory Droid, ZCode | `--client opencode`, `--client droid`, `--client zcode` | | | Qwen Code | `--client qwen`, which prints `qwen mcp add cua-driver <path> mcp` | | | Pi | `--client pi` | No MCP: runs one-shot `cua-driver call …` commands. | | Kimi Code | `kimi mcp add cua-driver -- <path> mcp`, then `kimi mcp test cua-driver` | No preset. | | T3 Code | Register Cua Driver in the harness that T3 Code launches | | Find `<path>` with `command -v cua-driver`. The full roster is in the [`mcp-config` reference](</docs/cua-driver/reference/cli#cua-driver-mcp-config>). Any other client that accepts the standard shape can use this (`cua-driver mcp-config` with no flag): ```json test="config" id="json-config" schema="mcp-client-config" { "mcpServers": { "cua-driver": { "command": "cua-driver", "args": ["mcp"] } } } ``` Model providers map to harnesses: Claude through Claude Code, OpenAI through Codex, Gemini through Antigravity, Kimi through Kimi Code, Qwen through Qwen Code. MiniMax documents setups for [Codex](https://platform.minimax.io/docs/token-plan/codex) and [Claude Code](https://platform.minimax.io/docs/token-plan/claude-code). **Warning** Registering a client does not choose a [permission mode](</docs/cua-driver/guides/permissions>). On macOS, `cua-driver mcp` proxies to the `CuaDriver.app` daemon, whose launch flags decide. On Windows and Linux it owns its runtime: set `CUA_DRIVER_PERMISSION_MODE` in the client's `env` block, or use `cua-driver mcp --socket <endpoint>` to reach a configured daemon. The default, `standard`, allows input to every app. ## Install the agent skill The skill teaches an agent to pick tools, address elements, keep focus, and verify each action. It links into Claude Code, Codex, Prime Agent, OpenClaw, OpenCode, Antigravity, Hermes, and Pi: ```bash test="cli-shape" id="cua-driver-skills-install" cua-driver skills install cua-driver skills status ``` If `status` reports a client's skill directory as absent, create it (`~/.agents/skills` for Codex, `~/.claude/skills` for Claude Code) and rerun. Add `--all-platforms` to keep the macOS, Windows, and Linux guides together. OpenClaw can use ClawHub instead: `clawhub install @cua/driver`. Cua Driver 0.28.0+ also serves the skill as MCP resources; whether that activates it as a skill is up to the client. ## Agent SDKs Examples in `libs/cua-driver/examples/agent-sdks` (Python and TypeScript): | SDK | Route | Run | | --- | --- | --- | | Claude Agent SDK | Native callbacks calling `CuaDriver.create()` in process | `claude_agent.py --route native` / `npm run claude -- --route native` | | Claude Agent SDK | MCP | `claude_agent.py --route mcp` / `npm run claude -- --route mcp` | | Codex SDK | MCP (it has no custom tool callbacks) | `codex_agent.py` / `npm run codex` | They remove interactive approval prompts: run only trusted tasks. ## Local models Tested: Meta's Muse Glimmer 30B with Claude Code as the harness, served by Ollama or by llama.cpp with Unsloth's `UD-Q4_K_XL` GGUF. Every tool schema costs context, so hide unused tools with the filter: ```bash skip="host-install" curl -fsSL https://cua.ai/docs/examples/local-models/cua-mcp-filter.py \ -o "$HOME/.local/bin/cua-mcp-filter" && chmod +x "$HOME/.local/bin/cua-mcp-filter" ``` Save as `muse-cua-mcp.json` (the filter hides tools; it does not authorize): ```json test="config" id="json-config-local-model" schema="mcp-client-config" { "mcpServers": { "cua-computer-use": { "command": "cua-mcp-filter", "args": [ "--allow", "start_session,end_session,launch_app,list_apps,list_windows,get_window_state,get_accessibility_tree,move_cursor,click,type_text,press_key,hotkey,invoke_menu" ] } } } ``` **Ollama** ```bash skip="third-party-cli" ollama pull muse-glimmer:30b-mlx CLAUDE_CODE_MAX_CONTEXT_TOKENS=131072 \ ollama launch claude --model muse-glimmer:30b-mlx --yes -- \ --bare --strict-mcp-config --mcp-config ./muse-cua-mcp.json --tools "" ``` **llama.cpp** ```bash skip="third-party-cli" llama-server --hf-repo "unsloth/Muse-Glimmer-30B-GGUF:UD-Q4_K_XL" \ --alias "muse-glimmer-local" --host 127.0.0.1 --port 8001 \ --ctx-size 131072 --parallel 1 --temp 1.0 --top-p 0.95 --top-k 64 \ --jinja --mmproj-auto --fit on --no-webui # In another terminal, once http://127.0.0.1:8001/health responds: ANTHROPIC_BASE_URL=http://127.0.0.1:8001 \ ANTHROPIC_API_KEY=local-no-key-required \ CLAUDE_CODE_MAX_CONTEXT_TOKENS=131072 \ claude --bare --strict-mcp-config --mcp-config ./muse-cua-mcp.json \ --tools "" --model muse-glimmer-local ``` Keep context small: bound `get_window_state` with `max_elements` and `max_depth`, reuse `pid` and `window_id`, and batch text into one `type_text`. In the tested Calculator runs this cut uncached input from 71,088 to 12,251 tokens. Use at least a 64K context window. To target a macOS VM, pass the guest's SSH MCP command ([Run in a VM](</docs/cua-driver/guides/vms-and-remote#macos-lume-vm>)) to `cua-mcp-filter --driver`. ### Text-only models For a model that cannot accept images, such as iFlytek Spark-X2.5 on the Astron Token Plan, add `--text-only` to the filter's `args`. The filter then requests `get_window_state` without a screenshot and replaces any remaining image content with a short text note, so the model works through the accessibility tree and its `element_token` values. Leave pixel-only tools such as `screenshot` and `zoom` out of the allowlist. Without `--text-only`, a provider that rejects images fails every request once a screenshot reaches the conversation. Apps that expose no usable accessibility elements (canvas, video, custom-drawn surfaces) need a vision model instead. [Create a checklist in Apple Notes](<https://cuademo06261324.blob.core.windows.net/artifacts/demos/muse-glimmer/notes-editorial.mp4>) Muse Glimmer runs locally and completes the task through Cua Driver. [Schedule an Apple Reminder](<https://cuademo06261324.blob.core.windows.net/artifacts/demos/muse-glimmer/reminders-editorial.mp4>) Muse Glimmer sets the reminder and verifies the final state. --- # Use the SDK Call Cua Driver from Python or TypeScript in process, verify each action, and expose MCP from a signed desktop app. `CuaDriver.create()` loads the Rust runtime into your process: no daemon, socket, or executable. Your app owns the OS permissions, sessions, and shutdown. On macOS, the process that imports the SDK needs the Accessibility and Screen Recording grants. **Python** ```bash skip="host-install" python -m pip install cua-driver ``` **TypeScript** ```bash skip="host-install" npm install @trycua/cua-driver ``` ## Capture the desktop The first call creates an implicit session for this runtime; later unnamed calls reuse it. Keep one `CuaDriver` for the app's lifetime. **Python** ```python skip="no-runner" import asyncio from cua_driver import CuaDriver, EndSessionInput, GetDesktopStateInput async def main() -> None: driver = CuaDriver.create() try: result = await driver.get_desktop_state( GetDesktopStateInput(session=None, screenshot_out_file=None) ) if result.is_error: raise RuntimeError(result.text) print(result.images[0].mime_type) finally: await driver.end_session(EndSessionInput(session=None)) await driver.shutdown() asyncio.run(main()) ``` **TypeScript** ```ts skip="no-runner" import { CuaDriver, EndSessionInput, GetDesktopStateInput } from '@trycua/cua-driver'; const driver = CuaDriver.create(undefined); try { const result = await driver.getDesktopState(GetDesktopStateInput.new({})); if (result.isError) throw new Error(result.text); console.log(result.images[0]?.mimeType); } finally { await driver.endSession(EndSessionInput.new({})); await driver.shutdown(); driver.uniffiDestroy(); } ``` In TypeScript, `shutdown()` stops admitting work and awaits admitted calls; `uniffiDestroy()` then frees the native handle. Call both, in that order. ## Act on a window and verify it Find an exact window, snapshot it, click an element by its token, and check the change in a second snapshot. The example expects an app `Window Demo` with a `Counter` window, an `Increment` button, and a `Count` value; replace them with your own. **Python** ```python skip="no-runner" import asyncio from cua_driver import ( ActionTarget, ClickButton, ClickInput, ClickPosition, CuaDriver, GetWindowStateInput, InputDeliveryMode, ListAppsInput, ListWindowsInput, ) def unique(items, what): if len(items) != 1: raise RuntimeError(f"Expected one {what}, found {len(items)}") return items[0] async def main() -> None: driver = CuaDriver.create() try: apps = await driver.list_apps(ListAppsInput()) app = unique([a for a in apps.apps if a.name == "Window Demo" and a.running], "app") windows = await driver.list_windows(ListWindowsInput(pid=app.pid, on_screen_only=True)) window = unique([w for w in windows.windows if w.title == "Counter"], "window") capture = GetWindowStateInput( pid=app.pid, window_id=window.window_id, session=None, query=None, include_accessibility_tree=True, include_screenshot=True, screenshot_out_file=None, max_elements=None, max_depth=None, max_dimension=None, ) async def snapshot(): state = await driver.get_window_state(capture) if state.degraded or state.truncated: raise RuntimeError("Snapshot is degraded or truncated") return state before = await snapshot() button = unique([e for e in before.elements or [] if e.label == "Increment"], "button") count = unique([e for e in before.elements or [] if e.label == "Count"], "counter") await driver.click(ClickInput( target=ActionTarget.WINDOW(pid=app.pid, window_id=window.window_id), position=ClickPosition.ELEMENT(element_token=button.element_token), delivery_mode=InputDeliveryMode.BACKGROUND, session=None, button=ClickButton.LEFT, count=1, )) after = await snapshot() updated = unique([e for e in after.elements or [] if e.label == "Count"], "counter") if updated.value == count.value: raise RuntimeError("Click returned, but the counter did not change") print("Verified:", count.value, "->", updated.value) finally: await driver.shutdown() asyncio.run(main()) ``` **TypeScript** ```ts skip="no-runner" import { ActionTarget, ClickButton, ClickInput, ClickPosition, CuaDriver, GetWindowStateInput, InputDeliveryMode, ListAppsInput, ListWindowsInput, } from '@trycua/cua-driver'; function unique<T>(items: T[], what: string): T { if (items.length !== 1) throw new Error(`Expected one ${what}, found ${items.length}`); return items[0]!; } const driver = CuaDriver.create(undefined); try { const apps = await driver.listApps(ListAppsInput.new({})); const app = unique(apps.apps.filter(a => a.name === 'Window Demo' && a.running), 'app'); const windows = await driver.listWindows(ListWindowsInput.new({ pid: app.pid, onScreenOnly: true })); const window = unique(windows.windows.filter(w => w.title === 'Counter'), 'window'); // Keep windowId as bigint; converting to number can lose precision. const capture = GetWindowStateInput.new({ pid: app.pid, windowId: window.windowId, includeAccessibilityTree: true, includeScreenshot: true, }); const snapshot = async () => { const state = await driver.getWindowState(capture); if (state.degraded || state.truncated) throw new Error('Snapshot is degraded or truncated'); return state; }; const before = await snapshot(); const button = unique((before.elements ?? []).filter(e => e.label === 'Increment'), 'button'); const count = unique((before.elements ?? []).filter(e => e.label === 'Count'), 'counter'); await driver.click(ClickInput.new({ target: new ActionTarget.Window({ pid: app.pid, windowId: window.windowId }), position: new ClickPosition.Element({ elementToken: button.elementToken! }), deliveryMode: InputDeliveryMode.Background, button: ClickButton.Left, count: 1, })); const after = await snapshot(); const updated = unique((after.elements ?? []).filter(e => e.label === 'Count'), 'counter'); if (updated.value === count.value) throw new Error('Click returned, but the counter did not change'); console.log('Verified:', count.value, '->', updated.value); } finally { await driver.shutdown(); driver.uniffiDestroy(); } ``` Version 0.25 does not expose these typed window methods (`ActionTarget`, `ClickPosition`); upgrade the bindings and native library together. Rules the example follows: - `click` returns `ActionResult` and raises `DriverError.Tool` on refusal. A returned action does not prove the UI changed; the second snapshot does. - Tokens belong to one snapshot. After a stale-token refusal, snapshot again and resolve the element again. - `background` delivery never retries in the foreground on its own. - Window coordinates come from that window's screenshot; do not add the screen origin. See the [native-window migration guide](https://github.com/trycua/cua/blob/2c4f19d2aa455b6ae2f79d2bf982e9b3c2356fda/libs/cua-driver/docs/native-window-sdk-migration.md). - An action can reach the OS even if your call times out. Check an independent postcondition before any retry. `DriverError.ActionInterrupted` reports `NotStarted`, `Completed`, or `Unknown`; never replay `Unknown` blindly. A complete runnable version that types into a local browser fixture and confirms the value through a separate `/state` endpoint lives in `libs/cua-driver/examples/agent-sdks` (`fixture_server.py`, then `native_driver.py` or `npm run native`). ## Constructors | Python / TypeScript | Use | | --- | --- | | `create()` | Same-process runtime. The default. | | `create_configured(options)` / `createConfigured` | Trusted host sets an immutable permission ceiling and session TTLs. | | `create_configured_with_authorization_host(options, host)` | Adds a callback that approves residual boundaries, such as attaching to a logged-in browser profile. | | `create_configured_with_activity_observer(options, observer)` | Adds content-free activity events (tool, risk class, refusal code; never arguments or images). | | `create_private_worker(options)` / `createPrivateWorker` | One supervised child runtime over inherited pipes; no listener, closes with its channel. | | `connect(socket_path)` / `connect(socketPath)` | Connects to a running daemon. | Python uses `snake_case`, TypeScript `camelCase`. Desktop methods mirror the [MCP tools](</docs/cua-driver/reference/mcp-tools>) (`get_window_state`, `click`, `type_text`, …); `list_tools_json()` and `call_tool()` reach the full generic tool surface. Results are `ToolResult` (`text`, `images`, `structured_json`, `is_error`, `error_code`, `verified`, `degraded`). On macOS, a direct runtime cannot draw the agent cursor unless the host provides an AppKit main-thread adapter; it returns `facility_unavailable`. Use a private worker when you need the overlay. ## Expose MCP from a desktop app When a signed desktop app must let an external agent use its permissions, host a private daemon from the app process and hand its MCP connection to the agent. If only your app calls the driver, use `create()` instead. ```ts skip="no-runner" import { CuaDriver, EmbeddedCuaDriverHost } from '@trycua/cua-driver'; const host = new EmbeddedCuaDriverHost( '/path/inside/YourApp.app/Contents/Resources/cua-driver', 'com.example.your-app', ); const connection = await host.start(); const driver = CuaDriver.connect(connection.socketPath); // Your app calls the SDK on `driver`. An external agent launches // connection.mcp.command with connection.mcp.args and connection.mcp.environment. // Shutdown, in order: await driver.shutdown(); driver.uniffiDestroy(); await host.stop(); host.uniffiDestroy(); ``` Python exposes the same objects: `EmbeddedCuaDriverHost(str(get_binary_path()), "com.yourco.yourapp")`. - **Call `start()` from the permission-owning app process.** On macOS, a gateway, terminal, `open`, or `NSWorkspace` launch changes the TCC responsibility chain and cannot lend the app's grants. A separate backend may launch the returned `connection.mcp` unchanged, but must not start a second host. - **Ship the executable** as a signed app resource, outside Electron's ASAR archive, with its executable bit. The npm package does not bundle it; the Python wheel does. - **Restart on grant changes.** `restart()` gives a new generation, PID, and endpoint: discard old clients and MCP proxies. `start()` coalesces callers, `stop()` is idempotent, and `waitForExit(generation)` reports an unexpected exit. - **Check it.** Call `check_permissions`; on macOS embedded mode returns `source.attribution: "host"` and never raises its own prompts. Call `health_report` with `{"include":["bundle_identity"]}` to confirm the parent app's bundle ID. - **Bound the post-action window wait.** After each input action the driver watches the window list to report a menu, dialog, or new window the action opened. For an action that opens nothing, the wait lasts 1000 ms on macOS and 800 ms for Linux X11 foreground delivery. Set `CUA_DRIVER_WINDOW_CHANGE_TIMEOUT_MS` (0 to 10000; `0` skips it) and `CUA_DRIVER_WINDOW_CHANGE_POLL_MS` (5 to 1000) in the daemon's launch environment to shorten it; `EmbeddedCuaDriverHost` admits both in its `environment` option. On macOS the wait also holds the focus-restore lease, so a shorter wait shortens that protection. Windows ignores both. - **Shorten macOS keystroke pacing.** The macOS adapter waits 8 ms between synthesized key events. Set `CUA_DRIVER_KEY_GAP_MS` (2 to 100) in the same launch environment to change it; `EmbeddedCuaDriverHost` admits it. Only macOS reads it. Some targets drop key events that arrive too close together, so read the field back before relying on a low value. Without the SDK, the equivalent low-level launch is: ```sh test="cli-shape" id="cua-driver-serve" CUA_DRIVER_EMBEDDED=1 \ CUA_DRIVER_HOST_BUNDLE_ID=com.yourco.yourapp \ cua-driver serve --socket /tmp/yourapp-cua.sock ``` ```sh test="cli-shape" id="cua-driver-mcp" cua-driver mcp --embedded --socket /tmp/yourapp-cua.sock \ --host-bundle-id com.yourco.yourapp ``` Spawn `serve` directly (for example with `posix_spawn` or `NSTask`), never through `open(1)`. A reference macOS host lives at `libs/cua-driver/rust/examples/embedded-host-macos`. --- # Drive a browser Bind a browser window, snapshot its tab, and navigate, click, and type without raising it; attach to a logged-in Chrome or Edge profile. The `browser_*` tools act inside Chromium and Electron pages through the Chrome DevTools Protocol (CDP), bound to the exact native window you picked. They reach an occluded tab without moving the real cursor or taking keyboard focus. Safari, Firefox, Tauri, WKWebView, WebKitGTK, and split-process WebView2 hosts return `browser_route_unavailable`; use the native [accessibility and pixel actions](</docs/cua-driver/concepts/how-cua-driver-works>) there. [Chrome remains behind the agent terminal](<https://github.com/user-attachments/assets/46f4be37-92c2-4360-bc20-bde35401bc71>) Claude Code starts a YouTube video in Chrome without raising the browser window. ## Bind and snapshot Browser capabilities belong to a named session. Pick the window with `list_apps` and `list_windows`, then bind it: ```jsonc skip="pseudo" start_session({"session": "research-1"}) get_browser_state({"pid": 844, "window_id": 10725, "session": "research-1"}) ``` Keep the returned opaque `target_id` and the `tab_id` whose `active` is `true`. `active: null` means the window title cannot tell tabs apart: choose a tab explicitly. Mutation needs `binding_quality: "exact"`. Snapshot the tab, then act only on returned refs: ```jsonc skip="pseudo" get_browser_state({ "target_id": "<target_id>", "tab_id": "<tab_id>", "session": "research-1", "snapshot_format": "semantic_v2" }) ``` A `semantic_v2` snapshot returns an `outline` of visible content, action `refs` (each lists `click`, `type`, `upload`, or `pointer` in `actions`), and read-only `content_refs` for `scope_ref`. Narrow it with `query` or `scope_ref`; follow `snapshot.continuation` for the next segment. Refs die on a newer snapshot, navigation, or reconnect: snapshot again after `browser_ref_stale`. ## Act ```jsonc skip="pseudo" browser_click({"target_id": "<target_id>", "tab_id": "<tab_id>", "ref": "p1:7", "session": "research-1"}) browser_type({"target_id": "<target_id>", "tab_id": "<tab_id>", "ref": "p2:3", "text": "status: ready", "replace": true, "session": "research-1"}) browser_navigate({"target_id": "<target_id>", "tab_id": "<tab_id>", "url": "https://example.com", "session": "research-1"}) end_session({"session": "research-1"}) ``` | Tool | Notes | | --- | --- | | `browser_click` | Trusted CDP input by default. Works fully in the background for Chrome and Edge on Windows; macOS and Linux return `browser_input_trust_unavailable` because the browser would activate. Pass `input_route: "dom_event"` for a synthetic DOM click that stays in the background but returns `effect: "unverifiable"`. | | `browser_type` | `insert_text` by default; `mode: "keystrokes"` for per-key events; `replace: true` to overwrite. | | `browser_pointer` | `hover`, `right_click`, `double_click`, `scroll`, `drag` (with `destination_ref` in the same frame). | | `browser_dialog` | `inspect`, then `accept` or `dismiss` the returned `dialog_id`. Linux needs `delivery_mode: "foreground"`. | | `browser_set_input_files` | Absolute paths to regular files (max 32), on a ref with `upload`. | | `browser_download` | Into an existing absolute `destination_root` that your policy admits. Returns only a byte count and id. | | `browser_navigate` | Invalidates every ref on the tab. | Verify the result with a fresh snapshot. The driver never falls back from trusted input to a DOM event, or foregrounds the browser, to make a call look successful. ## Use an isolated browser When `get_browser_state` returns `browser_requires_setup`, start a separate driver-owned Chromium profile. It never touches the person's profile: ```jsonc skip="pseudo" browser_prepare({ "pid": 844, "session": "research-1", "allow_launch": true, "profile": {"mode": "isolated_new"} }) ``` Bind the window of the returned `prepared_pid`. An `isolated_new` profile is deleted when the session ends; use `{"mode": "isolated_named", "name": "research"}` to keep it. Do not pass remote-debugging flags through `launch_app`: they are refused. ## Attach to a logged-in profile Use an existing Chrome or Edge profile only when the task needs its signed-in session, on a trusted machine. CDP exposes that profile's pages, cookies, and storage, and loopback is not authentication: other processes running as the same user can reach an open endpoint. Relaunching the browser yourself with `--remote-debugging-port` does not reach that profile. Chrome and Edge ignore the flag on their default user-data directory: no listener opens and no `DevToolsActivePort` file is written. The flag works only with a different `--user-data-dir`, which is a different, signed-out profile. The browser's own remote-debugging switch, which `browser_prepare` drives below, is the route into the real profile. **Warning** Attachment is an explicit boundary. It needs `--grant existing-profile` at launch (standard), a manifest entry `kind: existing_profile` (bounded), an embedding host's authorization callback, or unrestricted mode. A tool argument or MCP approval cannot grant it. A bounded manifest for this (on Windows and Linux, use Chrome's absolute `executable` path instead of `bundle_id`): ```yaml test="config" id="yaml-config" version: 3 expires_after: 2h idle_timeout: 20m resources: apps: - bundle_id: com.google.Chrome launch: false windows: all terminate: deny browser: profiles: - kind: existing_profile origins: - https://app.example.com allow: tools: - start_session - end_session - get_browser_state - browser_prepare - browser_navigate - browser_click - browser_type ``` ```bash test="cli-shape" id="cua-driver-serve-bounded" cua-driver serve \ --permission-mode bounded \ --capability-manifest ./browser-session.yaml \ --approve-capability-manifest ``` For a standalone standard-mode launch instead, use `cua-driver mcp --grant existing-profile` or `cua-driver serve --grant existing-profile`. Then request the exact window: ```jsonc skip="pseudo" browser_prepare({ "pid": 844, "window_id": 10725, "session": "research-1", "strategy": {"kind": "existing_profile"} }) ``` If the browser has no DevTools endpoint, the driver opens a temporary tab at `chrome://inspect/#remote-debugging` (or `edge://…`), turns on **Allow remote debugging for this browser instance**, proves the new listener is loopback and owned by that process, and closes the tab. It presses only the browser's own consent button. On Chrome 144 and later it connects through the running profile's auto-connect endpoint. It never restarts, copies, or edits the profile. The result's `side_effects` reports what it did (for example `enabled_remote_debugging`, `foregrounded_window`). On macOS, if Chrome hides that page's accessibility tree, a bounded pixel click on the setup page may briefly foreground the approved window and then restore focus. After `attached_existing_profile`, bind again with `get_browser_state`; old ids are invalid. The grant lives in memory: it expires after 30 minutes idle or 8 hours, and ends with the session or runtime. Ending the session does not turn off Chrome's remote-debugging setting; do that in Chrome. Supported: Chrome and Edge on macOS, Windows, and Linux X11; Chrome on GNOME Wayland; Chrome, Edge, and Chromium on Sway. On Linux, start the browser with `--force-renderer-accessibility`. Setup recognizes English UI labels only. ## Refusals | Code | Meaning | | --- | --- | | `browser_requires_setup` | No approved endpoint: call `browser_prepare`. | | `browser_consent_required` | Existing profile without a grant, host approval, or matching manifest. | | `browser_consent_revoked` | The host denied, or the browser's prompt was dismissed. | | `browser_ref_stale`, `browser_binding_stale` | Snapshot or bind again. | | `browser_wrong_target_refused`, `browser_endpoint_owner_mismatch` | Window, prompt, or endpoint identity is not exact. | | `browser_input_trust_unavailable` | Trusted input would activate the browser; use `input_route: "dom_event"` if acceptable. | | `browser_route_unavailable` | No typed route for this engine or platform. | | `browser_origin_outside_scope` | The tab left the manifest's origins; mutation pauses. | | `browser_action_unavailable` | The ref does not declare the needed action. | The legacy `page` tool keeps its read-only `get_text` and `query_dom` actions. Its mutation actions need `CUA_DRIVER_ENABLE_LEGACY_PAGE_MUTATIONS=1` at daemon start and skip the typed tools' binding and consent checks; migrate to `browser_*`. --- # Parse visual regions Parse a screenshot into text and icon regions with the optional perception extension, and let an external chooser such as jev-use pick bounded actions. When a window's accessibility tree and browser state do not identify the target, `parse_visual_regions` turns one retained screenshot into text and icon regions. Cua Driver keeps capture and input authority; the regions are observations, not action recommendations. **Warning: Developer preview** `cua-perception` is a separate extension, not part of the default install. Its verified catalog and initial OCR/model artifact are pending release verification. Its OmniParser detector is AGPL-3.0-only and its PP-OCR models are Apache-2.0; see the [perception third-party notices](https://github.com/trycua/cua/blob/18259fc2ee5d37a133edd91f762057daf9d9f791/libs/cua-driver/docs/perception-third-party-notices.md). The default Driver stays MIT licensed and works without it. ## Install the extension Inspect the catalog entry (version, hashes, licenses, provenance) before installing. Nothing is ever installed as a side effect: a parse on a default install returns `not_installed`. ```bash test="cli-shape" id="cua-driver-extension-install-cua-percept" cua-driver describe parse_visual_regions cua-driver extension inspect cua-perception --catalog <catalog.json> cua-driver extension install cua-perception --catalog <catalog.json> cua-driver extension status cua-perception ``` Update with `cua-driver extension update cua-perception --catalog <catalog.json>` and remove with: ```bash test="cli-shape" id="cua-driver-extension-remove-cua-percepti" cua-driver extension remove cua-perception ``` ## Capture, parse, act once, reobserve Use one persistent MCP connection or SDK runtime for the whole loop. A one-shot `cua-driver call` process cannot resolve another process's `capture_id`. ```text output state = get_window_state({"pid":844,"window_id":10725}) regions = parse_visual_regions({ "capture_id": state.capture_id, "options": {"kinds":["text","icon"], "min_confidence":0.5, "max_regions":100} }) click({..., "x": <point in a region>, "y": ..., "capture_id": state.capture_id}) state = get_window_state({"pid":844,"window_id":10725}) ``` - Regions are half-open rectangles in the source PNG's pixels, origin top left. Map a point through the returned action-coordinate mapping; do not rescale yourself. - Build the `click` locally with `x`, `y`, the exact target, delivery mode, and the same `capture_id`. At most one action per capture. - Never strip `capture_id` to retry a stale capture as a plain coordinate click. - Reobserve after every attempt, including timeouts and refusals, and verify the real postcondition. `background_unavailable` does not authorize a foreground retry. - Prefer an accessibility element or browser ref when one names the same control. | Error | Response | | --- | --- | | `not_installed` | Continue without it, or inspect and install explicitly. | | `capture_not_found`, `capture_expired`, `capture_stale`, `capture_generation_mismatch` | Capture again and parse the new id. | | `unsupported_target`, `unsupported_platform` | Use accessibility, browser state, or your own visual reasoning. | | `incompatible_protocol`, `artifact_invalid` | Stop and inspect the installed version and provenance. | | `worker_*`, `timeout`, `inference_failed` | Do not act on partial results; reobserve. | | `invalid_frame`, `resource_limit_exceeded` | Use a native-resolution capture or narrow the request. | The worker only receives the screenshot and parse options, never input, accessibility, browser, or credential access. ## Let an external chooser pick the action To put a model such as [TypeSafe Jev](https://typesafe.ai/) above this loop, build a small candidate table locally: opaque IDs, each mapped to one complete Driver action, plus `reobserve` and `abstain`. Send the chooser only the goal, the `capture_id`, compact regions, and candidate descriptions. Accept exactly one known ID and dispatch the prebuilt action unchanged. The chooser never sees tool names, arguments, screenshots, or credentials. The `jev-use` example (public preview) does this for a local browser form. It needs Cua Driver 0.23.2 or later, a system-installed Chrome, Edge, or Chromium, [uv](https://docs.astral.sh/uv/getting-started/installation/), and, for the live run, a key from the [TypeSafe console](https://console.typesafe.ai/). On macOS, point it at the app binary with `export CUA_DRIVER_BIN="/Applications/CuaDriver.app/Contents/MacOS/cua-driver"`. On Windows, run Driver and the terminal without elevation. ```bash skip="third-party-cli" git clone --depth 1 https://github.com/trycua/cua.git cua-jev-use cd cua-jev-use/libs/cua-driver/examples/jev-use uv sync --frozen --python 3.12 # Fixed choices, no API key: uv run --frozen python verify_setup.py --output-dir runs/connection-check # Live Jev choices (prompts for TYPESAFE_API_KEY): uv run --frozen python verify_setup.py --live --output-dir runs/jev-example ``` Each run ends with a line like this; the live run reports `"checks": 2`: ```json test="config" id="json-config" {"event": "setup_complete", "checks": 1, "fixture_closed": true} ``` In `runs/jev-example/summary.json`, look for `"complete": true` and `"observed": {"submitted": "jev-guide-live"}`, which comes from the test server, not the model. Add `--typescript` to run the TypeScript agent too (after `npm ci`; `"checks": 4`). To call only the chooser from your own loop: **Mock** ```bash skip="third-party-cli" uv run python/choose_action.py --mock < fixtures/jev-choice-request-v1.json ``` **Live** ```bash skip="third-party-cli" uv run python/choose_action.py < fixtures/jev-choice-request-v1.json ``` To adapt it, change the candidates (`python/core.py`), the goal and observations (`python/jev_adapter.py`), and the completion check (`python/run.py`). A failed or incomplete run may still have acted: read the action log before retrying. ## Native desktop apps The same chooser loop works in native desktop apps. Candidates then come from the accessibility tree that Cua Driver reports through macOS Accessibility, Windows UI Automation, or Linux AT-SPI, not from a web page: - An element becomes a candidate only when it is enabled, on screen, labeled, and part of the native app rather than embedded web content. Window chrome is skipped. - Platform roles map to one vocabulary (`button`, `toggle`, `checkbox`, `radio`, `popup`, `menu_item`, `link`, `text_input`), and candidate IDs come from the role, the label, and the path of actionable ancestors, so the same task yields the same IDs on every platform. - At most 24 actions are offered, plus `reobserve` and `abstain`. In a window with more controls, the ones that perform the task's declared steps are kept first, and the offered actions keep their accessibility-tree order. Risky labels (delete, send, purchase, close) are left out unless the task allows them, and typed text comes only from the task's parameters. - When the tree offers nothing to act through (empty, missing a declared control, or only window chrome), the agent falls back to visual regions from the same capture. A visual target needs an exact, unique text match with OCR confidence of at least 0.8 unless the task lowers it. Native requests use the `cua.jev_choice_request_v2` contract, which adds a `source` (`page`, `ax`, or `visual`) to each candidate. The built-in tasks also send `progress`: each required step and how many times this run has performed it. The runner counts its own successful actions and never reads the count from the app. The example includes AppKit, WPF, WinUI3, GTK3, and custom-painted canvas test apps; each task passes only when the app's own state file shows the expected result: ```bash skip="third-party-cli" bash ../../tests/fixtures/build/macos.sh --only appkit uv run --frozen python verify_native.py --harness appkit --typescript --output-dir runs/native-mock # Add --live for Jev choices, or --task counter to run one task. ``` Use `--harness wpf` (build with `..\..\tests\fixtures\build\windows.ps1 -Targets wpf`) or `--harness winui3` (build with `-Targets winui3`) on Windows and `--harness gtk3` (build with `bash ../../tests/fixtures/build/linux.sh --only gtk3`, run with `.venv/bin/python`) on an X11 Linux session. `--harness canvas` needs the perception extension and proves the visual fallback. Tasks live in `python/native_tasks.py` and `typescript/native_tasks.ts`; the design is recorded in [RFC 4268](https://github.com/trycua/cua/blob/main/rfcs/4268-jev-use-native-candidates.md). --- # Record trajectories and history Record an agent's actions, render them as a zoom-on-click video, and keep an encrypted local Computer History. ## Record a trajectory A recording saves every action with its arguments and before-and-after screenshots, plus an optional display video. Rendering needs `ffmpeg` on every platform; capture also needs it on Windows and Linux (macOS 15+ uses ScreenCaptureKit). [Turn an agent trajectory into a zoom-on-click demo](<https://github.com/user-attachments/assets/9eafceca-1738-4e34-949a-2fb0dbe1832e>) The agent clicks cells in a numbered grid, then Cua Driver renders the recorded actions. Have your agent call `start_recording` before the task: ```json test="config" id="json-config" { "output_dir": "~/cua-trajectories/calendar-demo", "record_video": true } ``` Actions such as `click`, `type_text`, `press_key`, and `scroll` are added automatically, one `turn-NNNNN` folder each. Call `stop_recording` with `{}` and wait for it: it finalizes `recording.mp4`. Then render: ```bash test="cli-shape" id="cua-driver-recording-render" cua-driver recording render \ ~/cua-trajectories/calendar-demo \ ~/cua-trajectories/calendar-demo.mp4 \ --scale 2.5 ``` The renderer zooms around each recorded action; `--scale` sets the strength and `--no-zoom` gives a plain render. All options: [`cua-driver recording`](</docs/cua-driver/reference/cli>) and the [recording tools](</docs/cua-driver/reference/mcp-tools>). ## Computer History Computer History keeps an encrypted local record of actions performed through Cua Driver, so an agent can pick up where a previous run stopped. It is an opt-in preview in nightly builds on macOS, Windows, and Linux, and records nothing outside Cua-mediated actions. ```bash test="cli-shape" id="cua-driver-history-enable" cua-driver channel set nightly cua-driver update --apply cua-driver history enable cua-driver history status ``` `enable` creates a key in the macOS Keychain, Windows Credential Manager, or Linux Secret Service (which must be unlocked; there is no plaintext fallback) and starts capture. Defaults: 7-day retention, 100 MiB quota, no network I/O. | Command | Effect | | --- | --- | | `cua-driver history list 50` | Newest 50 events. Add `--json` for scripts. | | `cua-driver history show 42` | One event by sequence number. | | `cua-driver history pause` / `resume` / `disable` | Stop or restart capture; existing history is kept. | | `cua-driver history delete --yes` | Destroy the key and the encrypted store. | ```bash test="cli-shape" id="cua-driver-history-list" cua-driver history list 50 cua-driver history show 42 cua-driver history pause cua-driver history resume cua-driver history delete --yes ``` History stores timestamps, opaque IDs, capability names, app identity, and fixed outcome categories. It never stores screenshots, typed text, keystrokes, clipboard, tool arguments or results, accessibility trees, file paths, window titles, or URLs. Deleting destroys the key; it cannot erase backups or snapshots of the ciphertext. ### Let an agent read history A runtime that admits the preview exposes two read-only tools: `history_status` (capability `history.status`) and `history_query` (`history.query`, at most 200 events, filters `limit`, `session_id`, `since_sequence`, `until_sequence`). Every call passes the normal [permission stack](</docs/cua-driver/guides/permissions>); a bounded manifest must name both: ```yaml test="config" id="yaml-config" version: 3 expires_after: 1h idle_timeout: 10m resources: computer_history: operations: - status - query allow: tools: - history_status - history_query ``` Having the tools does not make a model use them. For "continue" or "what did I do" requests, give the agent a trusted instruction (or run the status-then-query preflight in your host) to check history first, treat events as metadata, and verify current state before acting. A query that returns events appends an encrypted access record. Agents cannot enable, pause, delete, or export history. | Error | Fix | | --- | --- | | `history_preview_not_admitted` | Restart a development daemon with `cua-driver serve --experimental-history`. | | `history_key_locked`, `history_key_unavailable` | Unlock the login keychain or start a Secret Service. | | `history_quota_reached` | Delete history; the action that hit the limit still ran. | | `history_storage_corrupt` | `cua-driver history delete --yes`, then enable again. | The event schema (CloudEvents 1.0, `urn:cua-driver:schema:history-event:v0`) and storage profile are specified in the [agent integration RFC](https://github.com/trycua/cua/blob/main/libs/cua-driver/docs/computer-history-agent-integration-rfc.md). **Note** To return to stable: `cua-driver history disable`, `cua-driver channel set stable`, `cua-driver update --apply`. History is kept unless you delete it. --- # Set permissions Choose a permission mode, narrow it with a capability manifest, and restrict tools with YAML or Rego policies. Every call, from the CLI, MCP, the SDK, or a daemon, passes the same authorization check inside the runtime before it touches the desktop. The trusted launcher configures it at startup; an agent cannot change it from a tool call, and a running daemon cannot be reconfigured (restart it). Every call must pass all of these layers, and each can only narrow: 1. Hard invariants, such as self-targeting and protected-host checks. 2. The built-in tool and risk map. 3. Administrator policy, `CUA_DRIVER_MANAGED_POLICY_FILE`. 4. User policy, `CUA_DRIVER_POLICY_FILE`. 5. The permission mode. 6. The capability manifest, when configured. 7. A launch grant or host decision for a residual boundary. ## Choose a mode | Mode | Use it for | Behavior | | --- | --- | --- | | `standard` (default) | Local CLI and MCP use | Observation, input, isolated browsers, recording, and validated file transfer run without prompts. Attaching to a logged-in browser profile still needs a grant. | | `bounded` | Unattended agents, gateways, embedded apps | A capability manifest is required. In-scope work is silent; everything else is denied. | | `unrestricted` | Disposable or fully trusted machines | Cua approval checks are bypassed. No protection against prompt injection. | ```bash test="cli-shape" id="cua-driver-serve-bounded" cua-driver serve \ --permission-mode bounded \ --capability-manifest ~/cua-session.yaml \ --approve-capability-manifest ``` ```bash test="cli-shape" id="cua-driver-serve-unrestricted" cua-driver serve --dangerously-bypass-approvals ``` `--permission-mode unrestricted` alone fails closed. Add the two manifest flags to `standard` or `unrestricted` to narrow them too. On macOS, pass the flags after `serve` in `open -n -g -a CuaDriver --args serve …` and use an absolute manifest path. What `standard` allows: | Operation | Standard | | --- | --- | | Observe windows, apps, and the desktop; click, type, scroll, drag | Allow | | Driver-owned isolated browser; read page content | Allow | | Upload, download, screenshot, record to validated paths | Allow | | Terminate a process this runtime launched | Allow (fingerprint rechecked) | | Attach to a logged-in Chromium profile | Needs `--grant existing-profile` or host approval | | Terminate a foreign process; raise an OS permission prompt from a tool; legacy page mutations; unknown risky operations | Deny | ### Without CLI flags `cua-driver mcp` on Windows and Linux, the Windows autostart task, and embedding hosts read the same settings from the environment: | Flag | Environment variable | | --- | --- | | `--permission-mode <mode>` | `CUA_DRIVER_PERMISSION_MODE=<mode>` | | `--capability-manifest <path>` | `CUA_DRIVER_CAPABILITY_MANIFEST_FILE=<path>` | | `--approve-capability-manifest` | `CUA_DRIVER_CAPABILITY_MANIFEST_APPROVED=1` | | `--dangerously-bypass-approvals` | `CUA_DRIVER_DANGEROUSLY_BYPASS_APPROVALS=1` | `unrestricted` needs both the mode and the bypass variable; `bounded` needs the manifest and its approval. A missing half fails startup instead of falling back to `standard`. Anyone who can set these variables sets the mode: protect them like the unit file or task that carries them. To keep a mode across reboots, see [Pin a permission mode](</docs/cua-driver/guides/operate#pin-a-permission-mode>). ## Write a capability manifest A manifest is deny by default: a tool must be in `allow.tools` and every resource it touches must match. It narrows any mode and never widens one. ```yaml test="config" id="yaml-config-manifest" version: 3 expires_after: 8h idle_timeout: 30m allow: tools: - start_session - end_session - launch_app - get_window_state - click - type_text - press_key - kill_app resources: apps: - executable: /usr/bin/example-editor # macOS: bundle_id: com.example.Editor launch: true windows: all terminate: driver_launched files: read: - dir: /data/input recursive: true write: - dir: /data/output recursive: true desktop: display: false ``` - Apps: `bundle_id` on macOS, an absolute `executable` on Windows and Linux. `terminate: driver_launched` only kills a process this runtime started. - Files: roots are compared by canonical path component; `recursive: false` allows direct children only. - `desktop.display: true` allows full-display capture and screen-coordinate input. Keep it `false` unless needed. - `expires_after` and `idle_timeout` are optional, but required in `bounded`. - Version 1 and 2 manifests still load; their `ask.tools` entries are denied. **Warning** A manifest that lists `resources.browser.origins` may only use the typed `browser_*` tools. Generic input and observation (`click`, `type_text`, `get_window_state`, `get_desktop_state`, `page`, and similar) see whatever tab is open, so the runtime refuses to start with both. Run browser work and desktop work in two runtimes, and scope the desktop one away from the browser with `desktop.display: false`. A standard runtime without a manifest can type into any window, which defeats another runtime's origin list. A browser-only manifest: ```yaml test="config" id="yaml-config-browser" version: 3 expires_after: 8h idle_timeout: 30m allow: tools: - start_session - end_session - launch_app - list_windows - browser_prepare - get_browser_state - browser_navigate - browser_click - browser_type - browser_download resources: browser: profiles: - kind: isolated # or existing_profile for a logged-in profile origins: - https://app.example.com desktop: display: false ``` Test it before unattended use: an allowed call succeeds silently, the same tool against another app, origin, or path returns `bounded_resource_outside_manifest`, and an unlisted tool returns `permission_denied`. To point an MCP client at the bounded daemon: ```json test="config" id="json-config" schema="mcp-client-config" { "mcpServers": { "cua": { "command": "cua-driver", "args": ["mcp", "--socket", "/path/to/cua-driver.sock"] } } } ``` ## Revoke ```bash test="cli-shape" id="cua-driver-revoke" cua-driver revoke --session research-1 cua-driver revoke --all ``` Ending a session removes its grants, browser bindings, recordings, and cursor; its label stays retired until `start_session` declares it again. `revoke --all` suspends the whole runtime (calls return `authorization_suspended`) until you restart it. ## Restrict tools with a policy A policy file limits which tools an agent may call and with which arguments. It is deny by default once set, loaded once at startup, and a missing or invalid file stops the daemon from starting. ```yaml test="config" id="yaml-config-policy" allow: tools: # allowed with any arguments - screenshot - get_window_state - list_apps - list_windows - click - press_key - wait rules: # allowed when every constraint passes - tool: type_text constraints: text: max_length: 500 pattern: "^[\\x20-\\x7E\\n\\t]*$" - tool: scroll constraints: amount: min: -20 max: 20 - tool: launch_app constraints: bundle_id: allowed: ["com.apple.Safari", "com.google.Chrome"] deny: tools: # checked first - shell_execute ``` Evaluation: `deny.tools` → `allow.tools` → any matching `allow.rules` entry → deny. Constraint checks are `min`, `max`, `max_length`, `pattern` (RE2 syntax), and `allowed`. `allow` and `deny` also accept a flat list of names. `type_text_chars` is normalized to `type_text`. ```bash test="cli-shape" id="cua-driver-serve" CUA_DRIVER_POLICY_FILE=~/.cua-driver/policy.yaml cua-driver serve ``` ```bash test="cli-shape" id="cua-driver-call" cua-driver call shell_execute '{"command":"echo hello"}' # Error: tool 'shell_execute' is explicitly denied ``` For programmable rules, point the variable at a `.rego` file, or a directory of `.rego` files loaded in filename order. The rule `data.cua.policy.allow` must be a boolean; `undefined` denies. The input is `{"server": "cua-driver", "tool": "<name>", "arguments": {…}}`: ```rego skip="pseudo" package cua.policy import rego.v1 safe_tools := {"screenshot", "get_window_state", "list_apps", "list_windows", "click", "press_key", "wait"} allow if { input.tool in safe_tools } allow if { input.tool == "type_text" count(input.arguments.text) <= 500 } ``` A managed policy (`CUA_DRIVER_MANAGED_POLICY_FILE`) uses the same formats; a call must pass both. `cua-driver status` shows a hash of each loaded policy. Policies decide which calls run. They do not filter responses, rate-limit, or authenticate callers, and code running inside the runtime's process can bypass them. A browser `browser_navigate` host allowlist also cannot stop a page from redirecting after a click. ## Host authorization An embedding app can install a `DriverAuthorizationHost` callback to decide residual `standard` boundaries itself, and a `DriverActivityObserver` for content-free events (see [Use the SDK](</docs/cua-driver/guides/use-the-sdk#constructors>)). Without a grant or callback, a residual boundary returns `authorization_required`; Cua never shows its own modal. Same-user code and malware on the same desktop are outside this boundary. --- # Run in a VM or over SSH Run Cua Driver inside a macOS Lume VM or drive a Windows desktop over SSH. Cua Driver always runs on the machine whose desktop it drives. To reach a VM or a remote computer, run the driver there and carry MCP over SSH. ## macOS Lume VM On an Apple Silicon host with [Lume](</docs/lume/quickstart>) and `jq`, pull the published Tahoe image (150 GB sparse disk; login and password `lume`): ```bash test="cli-shape" id="lume-pull" IMAGE=macos-tahoe-cua:26.5.2 VM=cua-driver-dev-26.5.2 lume pull "$IMAGE" "$VM" lume run "$VM" ``` The image has SIP disabled to match the maintainer test base; that grants no permissions, and Cua Driver works with SIP on too. In Terminal inside the VM display, install and grant consent: ```bash skip="macos-vm" /bin/bash -c "$(curl -fsSL https://cua.ai/driver/install.sh)" ``` ```bash test="cli-shape" id="cua-driver-permissions-grant" /Users/lume/.local/bin/cua-driver permissions grant /Users/lume/.local/bin/cua-driver list_apps '{}' /Users/lume/.local/bin/cua-driver call get_desktop_state '{}' > /tmp/desktop.json ``` Approve three kinds of prompt: Accessibility and Screen Recording for CuaDriver, CuaDriver controlling System Events (from `list_apps`), and Tahoe's separate direct screen capture (from `get_desktop_state`). Rerun the last two commands; they must finish without prompts. Then confirm the daemon's grants: ```bash test="cli-shape" id="cua-driver-permissions-status" /Users/lume/.local/bin/cua-driver permissions status --json ``` Expect `accessibility: true`, `screen_recording: true`, and `source.attribution: "driver-daemon"`. `unknown` means no app-owned daemon answered: start it with `open -n -g -a CuaDriver --args serve`. Run an agent inside the VM with the usual [`mcp-config`](</docs/cua-driver/guides/connect-your-agent>), or from the host over SSH. Copy only your public key into the guest, then register the SSH command as a stdio server: ```bash skip="macos-vm" VM_IP="$(lume get "$VM" --format json | jq -r '.[0].ipAddress')" cat ~/.ssh/id_ed25519.pub | ssh "lume@${VM_IP}" \ 'umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys' codex mcp add cua-driver-vm -- \ ssh -T -o BatchMode=yes "lume@${VM_IP}" /Users/lume/.local/bin/cua-driver mcp ``` The remote `cua-driver mcp` proxies to the daemon in the logged-in guest session, so screenshots and input keep the guest app's permission identity. Do initial consent and GUI checks in the VM display, not over SSH. Save a reusable copy while stopped. Keep it local, with no tokens, private keys, or personal files inside: ```bash test="cli-shape" id="lume-stop" lume stop "$VM" lume clone "$VM" "${VM}-backup" lume ls ``` A clone keeps its grants only while `/Applications/CuaDriver.app` keeps the release signature; an ad-hoc source build gets a new permission identity. Maintainers running the driver's macOS E2E suite in Lume follow `libs/cua-driver/tests/runners/macos-lume/README.md` in the repository. ## Windows over SSH Windows OpenSSH runs in Session 0, which has no desktop, so window tools return empty results there. `cua-driver doctor` reports `running in Session 0 (services)`. Run the daemon in your interactive session and let the SSH side proxy to it over the named pipe: ```text output Session 1+ (RDP or console) cua-driver serve (autostart task) ▲ \\.\pipe\cua-driver Session 0 (SSH) cua-driver call … / cua-driver mcp --socket \\.\pipe\cua-driver ``` **1. From RDP or the local console**, register and start the task: ```powershell test="cli-shape" id="cua-driver-autostart-enable" cua-driver autostart enable cua-driver autostart kick ``` `query session` must show your user as `Active` or `Disc`. If you have never signed in with RDP, do it once. **Warning** Sign in through SSH with the same Windows account that owns the interactive daemon. The named pipe is private to that account; another account can see the pipe but gets `Access is denied`. Run `whoami /user` in both sessions and compare the SIDs, which must match exactly. **2. From SSH**, check the daemon and call tools: ```powershell test="cli-shape" id="cua-driver-status" cua-driver status # Cua Driver daemon is running # socket: \\.\pipe\cua-driver cua-driver call list_apps ``` If the endpoint exists but belongs to another Windows account, `status` reports it as not reachable and tells you to compare the two account SIDs. **3. Register the agent with an explicit socket:** ```powershell skip="third-party-cli" claude mcp add --transport stdio cua-driver -- cua-driver.exe mcp --socket \\.\pipe\cua-driver ``` **Warning** Bare `cua-driver mcp` owns its own runtime on Windows and fails in Session 0. Always pass `--socket`. If the interactive daemon is gone, MCP startup fails instead of acting from the wrong session. Still empty? Check that `whoami /user` shows the same SID over SSH and on the desktop, that `cua-driver --version` matches over SSH, that `cua-driver autostart status` shows the task, and that `cua-driver doctor` from RDP reports `session N has an attached interactive desktop`. --- # Test cua-driver in a sandbox Run a cua-driver build against a disposable Linux desktop, locally or in CI, and prove the build under test is the one that runs. A sandbox gives cua-driver a real desktop (Xvfb, XFCE, AT-SPI) that you can throw away, on your machine or on a CI runner. Put your build into it with `--overlay`, then check that it is the one running. ## On your machine Build the driver for Linux, then create a sandbox with it: ```bash test="cli-shape" id="driver-sandbox-create" cua sb create linux --name drv --wait desktop \ --overlay cua-driver=./target/release/cua-driver ``` The overlay replaces `/usr/local/bin/cua-driver` in the guest, records its sha256 and stops any older copy still running. Check it: ```bash test="cli-shape" id="driver-sandbox-doctor" cua doctor local:drv --no-host --only build --expect cua-driver=git:<commit sha> cua sb exec local:drv cua-driver doctor --json ``` `cua-driver doctor --json` and the `health_report` tool report the running build under `build`: - `git_sha`: the commit it was built from; - `exe_sha256`: the executable that actually runs. A process started before the file was replaced still reports its old hash. Run your tests against the desktop, then delete the sandbox: ```bash test="cli-shape" id="driver-sandbox-run" cua sb cp ./tests/smoke.sh drv:/tmp/smoke.sh cua sb shell drv "set -a; . /run/cua-desktop/desktop.env; set +a; bash /tmp/smoke.sh" cua sb rm local:drv --force ``` ## The driver inside cua-spacesd cua-spacesd links the driver core and serves its tools on `/mcp`. To test driver changes through spacesd, inject a cua-spacesd built from the same commit. The overlay restarts the daemon and waits until the new process runs it: ```bash test="cli-shape" id="driver-sandbox-spacesd" cua sb overlay drv cua-spacesd=./target/release/cua-spacesd cua doctor local:drv --no-host --only build \ --expect cua-spacesd=git:<commit sha> --expect cua-driver=git:<commit sha> ``` With both injected, `cua-driver=git:` must hold for the linked driver and the standalone binary. ## In GitHub Actions ```yaml test="config" id="driver-sandbox-ci" - uses: trycua/cua/.github/actions/cua-sandbox@main with: image: linux overlay: cua-driver=libs/cua-driver/rust/target/release/cua-driver expect: cua-driver=git:${{ github.sha }} guest-setup: apt-get update -qq && apt-get install -y -qq jq ffmpeg run: | cua sb cp scripts/ci/linux/preflight-rust-e2e.sh "$CUA_SANDBOX:/tmp/preflight.sh" cua sb shell "$CUA_SANDBOX" "set -a; . /run/cua-desktop/desktop.env; set +a; export CUA_E2E_SOURCE_SHA=${{ github.sha }} CUA_TEST_DRIVER_BIN=/usr/local/bin/cua-driver; bash /tmp/preflight.sh" ``` This is how the repository's own Linux driver preflight runs. The preflight fails with `stale driver` when the driver's embedded commit differs from `CUA_E2E_SOURCE_SHA`. **Note** On the runner, the action puts a `PATH` first where `cua-driver` is the injected build, and `cua-spacesd` is a stub that fails. A copy installed on the runner is never picked up. See [Test in CI with Cua sandboxes](</docs/cua-sdk/guides/test-in-ci>) for every input and output. --- # Keep Cua Driver running and updated Keep the Cua Driver daemon running, update it, choose a release channel, personalize the agent cursor, and control telemetry. ## Keep it running MCP clients on macOS and all one-shot `cua-driver call` commands need a running daemon. Apps that use `CuaDriver.create()` do not. **macOS** Save a LaunchAgent at `~/Library/LaunchAgents/com.trycua.cua-driver.plist` (from a repo checkout, `bash libs/cua-driver/scripts/install-local.sh --autostart` writes it for you): ```xml skip="host-install" <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.trycua.cua-driver</string> <key>ProgramArguments</key> <array> <string>/Applications/CuaDriver.app/Contents/MacOS/cua-driver</string> <string>serve</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> </dict> </plist> ``` ```bash skip="host-install" launchctl load ~/Library/LaunchAgents/com.trycua.cua-driver.plist ``` The LaunchAgent daemon is attributed to `com.trycua.driver`, so its grants persist across reboots. **Windows** Run once from RDP or the local console (not SSH or `SYSTEM`): ```powershell test="cli-shape" id="cua-driver-autostart-enable" cua-driver autostart enable cua-driver autostart kick cua-driver autostart status ``` `enable` registers the `cua-driver-serve` Scheduled Task with `LogonType: Interactive`, so the daemon lives in your desktop session. It is idempotent and survives RDP disconnects. Only `not-registered` from `status` means the task is absent. Remove it with `cua-driver autostart disable`. **Linux** Save `~/.config/systemd/user/cua-driver.service`: ```ini skip="host-install" [Unit] Description=cua-driver background daemon After=graphical-session.target PartOf=graphical-session.target [Service] Type=simple ExecStart=%h/.local/bin/cua-driver serve Restart=on-failure RestartSec=2 [Install] WantedBy=graphical-session.target ``` ```bash skip="host-install" systemctl --user daemon-reload systemctl --user enable --now cua-driver.service ``` On a headless box, use `WantedBy=default.target` and run `loginctl enable-linger $USER`. ```bash test="cli-shape" id="cua-driver-status" cua-driver status cua-driver stop ``` `stop` ends the daemon; the autostart entry brings it back at the next logon. ### Pin a permission mode Mode flags are read once at start, so put them in the autostart entry. **macOS** Add each flag as its own `<string>` after `serve`, with an absolute path (`launchd` does not expand `~`), then `launchctl unload` and `load` the plist: ```xml skip="host-install" <string>serve</string> <string>--permission-mode</string> <string>bounded</string> <string>--capability-manifest</string> <string>/Users/you/cua-session.yaml</string> <string>--approve-capability-manifest</string> ``` **Windows** The task always runs a bare `serve`, so set User environment variables and restart: ```powershell test="cli-shape" id="cua-driver-stop" setx CUA_DRIVER_PERMISSION_MODE bounded setx CUA_DRIVER_CAPABILITY_MANIFEST_FILE "$env:USERPROFILE\cua-capabilities.yaml" setx CUA_DRIVER_CAPABILITY_MANIFEST_APPROVED 1 cua-driver stop cua-driver autostart kick ``` **Linux** ```ini skip="host-install" ExecStart=%h/.local/bin/cua-driver serve --permission-mode bounded --capability-manifest %h/cua-session.yaml --approve-capability-manifest ``` Then `systemctl --user daemon-reload` and `systemctl --user restart cua-driver.service`. A bad configuration fails startup instead of falling back to `standard`. A user policy file works the same way: set `CUA_DRIVER_POLICY_FILE` in the plist's `EnvironmentVariables`, the unit, or the User environment. ## Update ```bash test="cli-shape" id="cua-driver-check-update" cua-driver check-update cua-driver update --apply ``` `check-update` only checks (`--json` for scripts, `--no-cache` to skip the 1-hour cache); `update --apply` runs the canonical installer. Then restart the daemon with your usual start command (`open -n -g -a CuaDriver --args serve`, `cua-driver autostart kick`, or `cua-driver serve`), keeping any mode flags. The same check is the `check_for_update` MCP tool. The check reads the version the installer itself would install, so it agrees with `curl -fsSL https://cua.ai/driver/install.sh | bash`, and it does not spend GitHub's REST API quota for stable releases. When GitHub's API does answer with a rate limit (nightly lookups and fallbacks), the message says so and shows when the limit resets; set `GITHUB_TOKEN` or `GH_TOKEN` to use your own quota. ```bash test="cli-shape" id="cua-driver-update-2" if cua-driver check-update --json | jq -e '.update_available' > /dev/null; then cua-driver update --apply fi ``` `mcp`, `serve`, and `doctor` print a banner when a newer release exists. Turn the check off for one run with `CUA_DRIVER_RS_UPDATE_CHECK=false`, or always: ```bash test="cli-shape" id="cua-driver-config-set" cua-driver config set update_check_enabled false ``` If pacman owns the executable, update with `sudo pacman -Syu`; Driver's own update and channel commands refuse with pacman guidance (builds with [#3636](https://github.com/trycua/cua/pull/3636)). ### Before updating to token-only element targeting Releases that include [#3873](https://github.com/trycua/cua/pull/3873) address accessibility elements only by `element_token`. Update scripts, agents, and wrappers that act on elements before you update: 1. Pass `element_token` instead of `element_index` or `snapshot_id`. Read it from `structuredContent.elements[].element_token` in the latest `get_window_state` result and send it with the window's `pid`. The token names its window, so `window_id` is optional; a different `window_id` is refused with `conflicting_element_target`. `get_window_state` still returns `element_index` and `snapshot_id` for display and correlation. 2. Remove arguments that a tool does not advertise. Tools with closed input schemas now refuse unknown argument names with `invalid_arguments` (`<tool>: unknown argument <name>`) instead of ignoring them. This includes `element_index` and `snapshot_id` on action tools, and `from_zoom` on the Windows `type_text`, `press_key`, and `hotkey` tools, which Windows read without advertising. Pass window coordinates or an `element_token` there. 3. Read a window before you use window-relative pixels. Pixel actions and `zoom` against a window target return `screenshot_context_missing` until the same session has read that window with a screenshot; Driver no longer assumes native pixels. `from_zoom` coordinates return `zoom_context_missing` once a newer read replaces the zoomed snapshot. Coordinates for a desktop target are unaffected. 4. Re-read after a stale token. Each `get_window_state` call replaces the previous snapshot of that window and lists the ids it replaced in `invalidated_snapshot_ids`. An older token, or a token from another Driver runtime, is refused with `stale_element_token`, which lists the process's current snapshots. Earlier releases reported a token from another runtime as `generation_mismatch`. Each one-shot `cua-driver call` without a `session` label runs in its own disposable session, and that session's snapshots are retired when the call returns. Pass the same `session` label to the read and to the actions that use its tokens or pixels. ### Channels Stable is the default. Nightlies are immutable builds of `main`. ```bash test="cli-shape" id="cua-driver-channel-set-nightly" cua-driver channel status cua-driver channel set nightly cua-driver update --apply ``` `channel set` only saves the preference; `update --apply` installs. Fresh installs can choose the channel directly: **macOS and Linux** ```bash skip="host-install" curl -fsSL https://cua.ai/driver/install.sh | bash -s -- --channel nightly ``` **Windows** ```powershell skip="host-install" & ([scriptblock]::Create((irm https://cua.ai/driver/install.ps1))) -Channel nightly ``` On Windows, the installer adds its directory to your User `Path`; to skip that: ```powershell test="windows" id="install-no-path-update" & ([scriptblock]::Create((irm https://cua.ai/driver/install.ps1))) -NoPathUpdate ``` To pin one exact nightly for a single install, set `CUA_DRIVER_RS_VERSION` to its full `nightly-cua-driver-rs-v…` tag from the GitHub release before running the installer. The pin does not change the saved channel and never falls back to another release. ## Personalize the cursor The agent's cursor is an overlay, not the real pointer: a session-colored pointer (`cua.default`, the only built-in theme) with a badge showing the session name, delivery (`background` or `foreground`), and target (`ax`, `pixel`, `browser`, or `desktop`). It is a visual aid, not an authorization signal. ![All session badge combinations for background and foreground delivery across accessibility, pixel, browser, and desktop targets](https://github.com/user-attachments/assets/071dbfd0-b0d4-442f-9654-994c84db3029) ```bash test="cli-shape" id="cua-driver" cua-driver start_session '{"session":"research"}' cua-driver set_agent_cursor_theme \ '{"session":"research","theme_id":"cua.default","reduced_motion":"auto"}' cua-driver get_agent_cursor_state '{"session":"research"}' ``` `set_agent_cursor_enabled` hides or shows it; `set_agent_cursor_motion` tunes movement: `style` picks the trajectory (`signature_arc` by default, or `spring_settle`, `magnetic`, `comet_swoop`, `adaptive`, `classic`), `timing` picks `native`, `fitts`, or `fixed`, and `effects` turns the trail, glow, magnet, ripple, and squish on or off. The shell shortcut is `cua-driver cursor motion --session research --style comet_swoop`. Labels longer than 28 characters are shortened. A custom theme is a dotLottie archive with a `cua/theme.json` manifest (`"schema": "cua.cursor-theme/2"`), 128×128 30 fps animations for all twelve actions (`idle`, `observe`, `click`, `drag`, `scroll`, `text`, `key`, `navigate`, `app`, `transfer`, `record`, `system`), a still frame each, and an author and license. Compile and install it locally; agents can select an installed ID but never install one: ```bash test="cli-shape" id="cua-driver-cursor-theme-validate" cua-driver cursor-theme validate theme.lottie cua-driver cursor-theme build theme.lottie --output theme.cua-theme cua-driver cursor-theme preview theme.cua-theme --output preview cua-driver cursor-theme install theme.cua-theme cua-driver cursor-theme list cua-driver cursor-theme uninstall com.example.cursor.studio ``` ## Choose your cursor Six motion styles are built in. `signature_arc` is the default. | Style | Feel | | --- | --- | | `signature_arc` | One confident arc with a small follow-through. | | `spring_settle` | An arc that lands with one soft bounce. | | `magnetic` | Slows near the target, then is pulled in. | | `comet_swoop` | A wide arc with a short trail. | | `adaptive` | Careful for small targets, a swoop for long moves. | | `classic` | The original glide. | Pick one for every new session by saving it as the default: ```bash test="cli-shape" id="cua-driver-choose-cursor-default" cua-driver config set cursor.motion.style magnetic cua-driver config set cursor.motion.timing fitts cua-driver config set cursor.motion.effects.trail true cua-driver config get cursor.motion.style ``` The default is saved in `~/.cua-driver/config.json` and applies to sessions started afterwards. Use `null` as the value to clear a key, or `cua-driver config set cursor.motion null` to clear them all. A bad value is rejected with the list of allowed styles. Or choose per run, before the cursor first appears, in `start_session`: ```bash test="cli-shape" id="cua-driver-choose-cursor-session" cua-driver start_session '{"session":"research","cursor_motion":{"style":"spring_settle","timing":"fitts"}}' ``` `cursor_motion` takes the same fields as `set_agent_cursor_motion`, and `start_session` echoes it back. When several apply, the most specific wins: 1. `set_agent_cursor_motion` on the session 2. `cursor_motion` in `start_session` 3. the saved default (`cursor.motion.*`) 4. the built-in `signature_arc` Reduced motion always wins: when the theme or host asks for it, every move is a short straight glide with no effects, whatever you chose. Cua Driver has no settings window, so there is no cursor picker in the app. Use `cua-driver config set` or `start_session`. ## Telemetry Cua Driver sends content-free, pseudonymous usage events to PostHog's EU endpoint, on by default. The installer shows a notice before the first event. ```bash test="cli-shape" id="cua-driver-telemetry-status" cua-driver telemetry status --json cua-driver telemetry disable cua-driver telemetry inspect cua_driver_mcp_tool_completed --json cua-driver telemetry reset-id ``` `CUA_DRIVER_RS_TELEMETRY_ENABLED=false` overrides the saved setting for one process. The switches every Cua program shares also turn it off: `DO_NOT_TRACK=1`, `CUA_TELEMETRY=0` and `cua telemetry off` (see [Telemetry and privacy](</docs/cua-sdk/concepts/telemetry>)). `inspect` builds an event locally without sending it; `reset-id` creates a new installation ID (`uninstall --purge` removes local state). Events carry the product version, OS family and major version, architecture, transport, built-in tool names (any other tool is `other`), and bucketed durations and outcomes. GeoIP lookup is disabled on every event. Never collected: prompts, tool arguments or results, typed text, screenshots, accessibility trees, window titles, app names, file paths, URLs, or error messages. The update check is separate and not controlled by this setting. See the [privacy policy](https://cua.ai/privacy-policy). --- # Design cursor motions Cua Cursor Motion, the open-source library behind Cua Driver's agent cursor. Plan the six motions, design your own, and play them on a web page. Cua Driver moves its agent cursor with **Cua Cursor Motion**, an MIT-licensed library you can use on its own. It comes in two packages that give the same trajectories: - `cua-cursor-motion` (`libs/cua-driver/rust/crates/cua-cursor-motion`), the Rust crate the driver plans every move with; - `@trycua/cursor-motion` (`libs/typescript/cursor-motion`), a TypeScript port with a canvas player that draws the Cua cursor. Both are tested against the same golden trajectories, so a motion you tune on a web page plays the same way in the driver. ## Use it from the repository Cua Cursor Motion is not published to crates.io or npm. Use it straight from [trycua/cua](https://github.com/trycua/cua). In Rust, add a git dependency. Cargo finds the crate inside the repository: ```toml skip="no-runner" [dependencies] cua-cursor-motion = { git = "https://github.com/trycua/cua" } ``` On the web, build the single-file ES module once and copy it with its types into your project: ```bash skip="host-install" git clone --depth 1 https://github.com/trycua/cua cd cua/libs/typescript pnpm install pnpm --filter @trycua/cursor-motion build cp cursor-motion/dist/index.js ../../../my-app/src/vendor/cua-cursor-motion.js cp cursor-motion/dist/index.d.ts ../../../my-app/src/vendor/cua-cursor-motion.d.ts ``` The file has no imports, so it also loads from a plain `<script type="module">`. In a TypeScript project with a bundler you can copy `libs/typescript/cursor-motion/src/` instead. ## From the Cua Driver SDKs The Cua Driver SDKs export the planner through the same UniFFI boundary as the rest of the driver, so Python and TypeScript hosts plan moves with the Rust code itself. A planned move is a `CursorTrajectory`: read its samples once with `samples()` and interpolate locally each frame. ```python skip="no-runner" import cua_driver as cd params = cd.default_cursor_motion_params() params.style = cd.CursorMotionStyle.SPRING_SETTLE trajectory = cd.plan_cursor_move( params, cd.CursorMoveRequest( from_point=cd.CursorMotionPoint(x=100, y=100), to_point=cd.CursorMotionPoint(x=700, y=400), from_heading=None, end_heading=None, target=cd.CursorMotionRect(x=660, y=380, width=80, height=40), seed="demo", reduced_motion=False, ), ) samples = trajectory.samples() ``` ```ts skip="no-runner" import { CursorMotionStyle, defaultCursorMotionParams, planCursorMove } from '@trycua/cua-driver'; const trajectory = planCursorMove( { ...defaultCursorMotionParams(), style: CursorMotionStyle.SpringSettle }, { fromPoint: { x: 100, y: 100 }, toPoint: { x: 700, y: 400 }, target: { x: 660, y: 380, width: 80, height: 40 }, seed: 'demo', reducedMotion: false, } ); const samples = trajectory.samples(); ``` `plan_cursor_spec` takes a custom `CursorMotionSpec`, and `CursorTrajectory.effect_frame(t, blend)` returns the trail, glow and magnet to paint. For a web page without the native library, use the single-file web port (see [Use it from the repository](#use-it-from-the-repository)). ## Plan a move A move is planned once, as samples at 120 Hz, then played back by time. It takes a style (`signature_arc`, `spring_settle`, `magnetic`, `comet_swoop`, `adaptive` or `classic`), a timing mode (`native`, `fitts` or `fixed`) and the shape knobs of `set_agent_cursor_motion`. ```ts skip="no-runner" import { planMove } from './vendor/cua-cursor-motion.js'; const trajectory = planMove( { style: 'spring_settle', timing: 'fitts' }, { from: { x: 100, y: 100 }, to: { x: 700, y: 400 }, target: [660, 380, 80, 40] } ); const { x, y, heading } = trajectory.sampleAt(0.3); ``` ```rust skip="no-runner" use cua_cursor_motion::{plan_move, MotionParams, MotionStyle, MotionTiming, MoveRequest, Pt}; let params = MotionParams { style: MotionStyle::SpringSettle, timing: MotionTiming::Fitts, ..Default::default() }; let request = MoveRequest { target: Some([660.0, 380.0, 80.0, 40.0]), ..MoveRequest::new(Pt::new(100.0, 100.0), Pt::new(700.0, 400.0)) }; let sample = plan_move(¶ms, &request).sample_at(0.3); ``` `arrivalT` (`arrival_t` in Rust) is when the tip first reaches the target. Cua Driver clicks then and lets any follow-through or bounce finish during the click. The target rect sets the Fitts width: smaller targets take longer. ## Play it on a web page `MotionPlayer` draws the Cua cursor, its glow, the comet trail and the click ripple inside a `<canvas>`. It never moves, hides or restyles the page's own mouse pointer. ```ts skip="no-runner" import { MotionPlayer } from './vendor/cua-cursor-motion.js'; const player = new MotionPlayer(document.querySelector('canvas')!); player.place({ x: 80, y: 300 }); await player.moveTo( { x: 640, y: 120 }, { params: { style: 'comet_swoop' }, target: [600, 104, 80, 32], click: true } ); ``` ## Design your own motion A custom motion is a `MotionSpec`: a path shape (`straight`, `arc`, `bow`), a speed curve (named easings or `cubic_bezier`), an overshoot or settle (`follow_through`, `spring`), a duration model (`fitts`, `fixed`, `distance`), a heading mode, effects and a trail. `signature_arc`, `spring_settle` and `comet_swoop` are specs themselves, so start from one: ```ts skip="no-runner" import { planSpec, specForStyle } from './vendor/cua-cursor-motion.js'; const spec = specForStyle('signature_arc')!; spec.ease = { type: 'cubic_bezier', x1: 0.3, y1: 0, x2: 0.1, y2: 1 }; spec.settle = { type: 'follow_through', amount: 0.04, maxPt: 14, at: 0.75 }; spec.effects.trail = true; const trajectory = planSpec(spec, { from: { x: 100, y: 500 }, to: { x: 900, y: 200 } }); ``` Custom specs play in your own renderer. Cua Driver itself takes one of the six styles and its knobs. ## The playground The playground lets you pick a style, drag the start and end points, tune the curve with a live speed plot, and copy the result as TypeScript or as Cua Driver config. Run it from a checkout: ```bash skip="host-install" cd libs/typescript pnpm install pnpm --filter @trycua/cursor-motion playground ``` It opens at `http://127.0.0.1:4173/playground/`. For a built-in style, the exported Cua Driver config looks like this: ```bash test="cli-shape" id="cua-driver-design-cursor-motion" cua-driver config set cursor.motion.style comet_swoop cua-driver config set cursor.motion.timing fitts cua-driver cursor motion --session demo --style comet_swoop --timing fitts --effects trail=on ``` The arc knobs (`arc_size`, `arc_flow`, `start_handle`, `end_handle`) and the classic glide's `spring` and `turn_radius` travel only through `set_agent_cursor_motion`. See [Choose your cursor](</docs/cua-driver/guides/operate#choose-your-cursor>) for how saved defaults, `start_session` and per-call settings combine. --- # Troubleshoot Fix stale macOS permissions and empty results, and work around known per-app and per-platform limits. Start with the built-in checks: ```bash test="cli-shape" id="cua-driver-diagnose" cua-driver status cua-driver doctor cua-driver diagnose ``` `diagnose` output can contain usernames, paths, and process IDs: sanitize it before sharing. | Symptom | Fix | | --- | --- | | `list_apps` or `list_windows` returns `[]` | The daemon cannot see the desktop. On Windows over SSH, see [Windows over SSH](</docs/cua-driver/guides/vms-and-remote#windows-over-ssh>); on Linux, start `serve` inside the desktop session. | | No screenshot in `get_window_state` on macOS | Screen Recording is missing: without this grant it returns the tree only (no PNG). Accessibility is needed for every tree read and input. | | `permissions status` says `unknown` | No app-owned daemon answered. Run `open -n -g -a CuaDriver --args serve`. | | Permission granted in System Settings but reported false | Stale grant: [reset it](#reset-stale-macos-permissions). | | `libXi.so.6: cannot open shared object file` | `sudo apt install libxi6 at-spi2-core`. | | `doctor` warns AT-SPI is unreachable | `get_window_state` needs the accessibility bus; start the app inside a full desktop session. | | Action returned success but nothing changed | Check `effect` and follow `escalation`; see [the action ladder](</docs/cua-driver/concepts/how-cua-driver-works#the-action-ladder>). | | `stale_element_token` | Call `get_window_state` again and use a fresh `element_token`. The refusal lists the process's current snapshots, so you can tell whether you already hold a newer one. The accessibility route still works. | | `screenshot_context_missing` | A window-relative pixel action came before a screenshot read of that window in the same session. Call `get_window_state` (with its screenshot) first, using the same `session` label for one-shot CLI calls. | | `invalid_arguments` (`unknown argument element_index`) | Action tools take `element_token` only; read it from the latest `get_window_state` row. | | `desktop_scope_disabled` | A legacy window-less call: pass a window target, or `target: {kind: "desktop", display_id: "primary"}`. | ## Reset stale macOS permissions Use this when CuaDriver is enabled in **System Settings → Privacy & Security** but `permissions status` or `diagnose` reports `accessibility: false` or `screen_recording: false`. A grant for an older bundle ID (`com.trycua.cuadriver`, `com.trycua.cuadriverrs`) or signature does not cover the current `com.trycua.driver`. Quit every MCP client that starts Cua Driver, then stop the daemon and confirm it is not running: ```bash test="cli-shape" id="cua-driver-stop" cua-driver stop cua-driver status ``` Reset only Cua Driver's grants (repeat for the two older IDs if this Mac ran them; `No such bundle identifier` for those is fine): ```bash skip="destructive" tccutil reset Accessibility com.trycua.driver tccutil reset ScreenCapture com.trycua.driver tccutil reset AppleEvents com.trycua.driver ``` Never run an unscoped reset or edit `TCC.db`. If `tccutil` cannot find `com.trycua.driver`, re-register the app and retry: ```bash skip="host-macos-permissions" /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister \ -f /Applications/CuaDriver.app ``` Grant again, turning CuaDriver on under **Accessibility** and **Screen Recording** (add `/Applications/CuaDriver.app` with **+** if it is missing), then check: ```bash test="cli-shape" id="cua-driver-permissions-grant" cua-driver permissions grant cua-driver permissions status --json ``` Expect `accessibility: true`, `screen_recording: true`, and `source.attribution: "driver-daemon"`. If not, check that `diagnose` points at `/Applications/CuaDriver.app` and not an older copy or a terminal-owned process. ## Known limits Cua Driver refuses a route it cannot deliver safely instead of reporting a silent success. These are the cases callers hit most. ### Canvas apps and games need the foreground Blender, Unity, most native games, and some WebGL-heavy Electron apps ignore per-process routed input. Pixel clicks silently no-op. Use element actions where the app exposes them; otherwise retry that action with `delivery_mode: "foreground"` while nobody is using the machine. ### Chromium turns pixel right-clicks into left-clicks On macOS, a pixel `right_click` on Chrome, Edge, Brave, or Arc web content fires a left click. Use `right_click` with `element_token` on accessible targets; for pure web content, activate the browser briefly for that one right-click. ### Off-Space SwiftUI windows lose their tree macOS 14+ strips accessibility detail from SwiftUI windows on another Space; `get_window_state` returns `off_space: true`. Switch to that Space, or use an AppKit app. AppKit windows are not affected. ### Minimized windows drop keyboard commits On macOS, Return, Space, or Tab sent to a field in a minimized window does nothing. Use `set_value` to write the field, or click the commit button by element. ### Native Wayland keyboard input is focus-bound On Sway, GNOME, and KDE, background `press_key`, `hotkey`, or `type_text` into a field that is not AT-SPI-editable returns `background_unavailable`. Type into accessible fields with `type_text`, drive controls by `element_token`, use `delivery_mode: "foreground"` where the desktop supports it, or run the app under XWayland (`GDK_BACKEND=x11`, `QT_QPA_PLATFORM=xcb`). ### Cinnamon leaves accessibility off Cua Driver does not change Cinnamon's accessibility flags (doing so launches Orca or fights the settings schemas), so some apps return an empty AT-SPI tree. Screenshots and browser routes still work. ### Element frames and pixel coordinates use different spaces `elements[].frame` is screen-absolute (points on macOS, pixels elsewhere). Window-targeted `x, y` are pixels in that window's screenshot. Prefer `element_token`. If you must convert: ```text output scale_x = screenshot_width / bounds.width # bounds from list_windows scale_y = screenshot_height / bounds.height x = (frame.x + frame.w / 2 - bounds.x) * scale_x y = (frame.y + frame.h / 2 - bounds.y) * scale_y ``` Recompute the scale from every snapshot: it is normally 2 on Retina, and less than 1 when a large window is downscaled to `max_image_dimension`. **Note** Browser-specific limits (trusted input, Safari and Firefox, embedded webviews) are listed in [Drive a browser](</docs/cua-driver/guides/browsers>). Platform coverage is in [Platform support](</docs/cua-driver/concepts/platform-support>). --- # Recipes Real desktop workflows with Cua Driver, each with the prompt to give your agent and how to check the result. Each recipe assumes Cua Driver is installed and connected to your agent ([Quickstart](</docs/cua-driver/quickstart>)). The recorded ones ran on macOS with GPT-6 Astra + Cua Driver: selected gestures play at 1x; setup, waits, retries, and intervening edits are cut. They use foreground desktop input and fictional data, so run them on a desktop you are not using, or in a [VM](</docs/cua-driver/guides/vms-and-remote>). Cua Driver has no file or HTTP tools: an agent reads and writes files by driving apps (an editor, Terminal, a PDF viewer). To run many jobs in parallel, give each its own [Cua Sandbox](</docs/cua-sdk>). ## Background examples on Windows and Linux Agents operating desktop apps while the user's active window stays in place, recorded by the Cua team on the named platform. [Build and check a WPF app on Windows](<https://github.com/user-attachments/assets/4159f98d-9623-40d0-a85f-37c913b1403b>) Claude Code builds a WPF CRM, runs it, patches the code, and checks the app again. [Fill a Linux spreadsheet over SSH](<https://github.com/user-attachments/assets/7096c772-aa24-4286-a453-6aedfbeb3e23>) An agent enters a monthly budget in Gnumeric on a remote machine with no attached display or GPU. ## Build and inspect a map QGIS map and LibreOffice Writer handoff from eight fictional station observations. Inputs: [stations.csv](</docs-assets/media/workflow-demos/map/stations.csv>) and [campus.geojson](</docs-assets/media/workflow-demos/map/campus.geojson>). [Build a planning map in QGIS and inspect the exported PDF](<https://github.com/user-attachments/assets/e133cb0a-9b70-4812-9d97-76fadd67317b>) Selected layout gestures in QGIS and inspection of the exported map in Chrome. ```text output Use Cua Driver to build a fictional Morrow Field campus planning map in QGIS. Preserve stations.csv and campus.geojson. Save new outputs in an output folder. Import stations.csv as points: X = longitude, Y = latitude, source CRS EPSG:4326. Import campus.geojson with the same source CRS. Set the project CRS to EPSG:3857 for a layout with distances in meters; do not relabel the source coordinates. Use the field calculator to create status: READY when flow_lpm >= 2.0 AND days_since_check <= 30; otherwise REVIEW. Compare the original numeric values without rounding or recalculating elapsed days. Save an editable GeoPackage. Style READY green and REVIEW amber, label all eight station IDs, and include category names so the result does not rely on color alone. Select the REVIEW features and confirm their IDs and count from the data. Create one A4 landscape layout with a title, legend, north arrow, and meter scale bar. Save a QGZ project with relative paths and export the map as a PDF. In LibreOffice Writer, create a one-page handoff with the map, verified category counts and REVIEW IDs, the classification rule, and the fictional-planning caveat. Save the handoff as ODT and PDF. Reopen the project and inspect both exported PDFs in Chrome or Preview. Report the saved files, completed checks, and any unresolved issue. Keep the outputs local; do not send or publish them. ``` **Check:** R01 sits at longitude `0.00150`, latitude `0.00440` (swapped axes look plausible here). `READY` is R01–R04 and `REVIEW` is R05–R08; R08 fails on flow `1.99`. The map PDF is A4 landscape with all eight labels, legend, north arrow, and scale bar. ## Redact and annotate a PDF A review copy of a fictional two-page proof in Preview, plus a Writer handoff. Input: [willowfen-vendor-proof-SOURCE.pdf](</docs-assets/media/workflow-demos/pdf/willowfen-vendor-proof-SOURCE.pdf>). [Redact and annotate a vendor proof in Preview](<https://github.com/user-attachments/assets/5ef3bec8-70a5-45f5-8eb3-930a345b9019>) Selected redaction and annotation steps in Preview. ```text output Use Cua Driver to prepare a local review copy of willowfen-vendor-proof-SOURCE.pdf in Preview. Preserve the original. Duplicate it before editing, and confirm that the active document is the copy before applying any irreversible redaction. Use Preview's actual Redact tool to remove the complete line below each marker: - Page 1, R1: Supplier cost. - Page 1, R2: Internal reference. - Page 2, R3: Supplier contact. - Page 2, R4: Internal contingency. The R1-R4 markers may remain. Do not substitute shapes or highlights for Redact. If Redact is unavailable, stop and report the limitation. Keep the title, date, time, location, participant count, public fee, materials, booking address, schedule, access notes, and complete room diagram. Add REVIEW COPY in page 1's outlined annotation area without covering other text. Save as willowfen-vendor-proof-REVIEW.pdf, then close and reopen it in Preview. In LibreOffice Writer, create a one-page handoff with the workshop title, review-only status, source and review filenames, the four removed categories without their values, retained public facts, page count, and verification results. Save willowfen-release-handoff.odt and export willowfen-release-handoff.pdf. Inspect both review PDF pages and the handoff PDF in Preview. Report the files and the checks that passed. If extraction has not been checked, state that it remains unverified. Keep all outputs local; do not send or publish them. ``` **Check:** the source hash is unchanged; the four target lines are gone from the rendered pages and from `pdftotext` output. Extraction alone is not proof: a black box can hide text that is still recoverable. ## Prepare an exhibition panel Crop a fictional archive ticket in GIMP, build two editable A4 panels in Inkscape, and a two-page installation packet in Writer. Inputs: [archive-contact-sheet.png](</docs-assets/media/workflow-demos/archive/archive-contact-sheet.png>) and [panel-starter.svg](</docs-assets/media/workflow-demos/archive/panel-starter.svg>). [Prepare archive artwork in GIMP and an exhibition panel in Inkscape](<https://github.com/user-attachments/assets/89d570f7-e895-4b66-8c20-ded5da2926a4>) Selected image and panel edits in GIMP and Inkscape. Give the agent this caption table with the prompt: | Station | Object ID | Year | Caption | | ------- | ---------- | ---- | ----------------------------------------------------------------- | | A | RA-1932-01 | 1932 | A three-pence ticket links Ferry Steps and Market Square. | | B | RA-1956-02 | 1956 | Route 4 connects Ferry Steps, Market Square, and Hill Library. | | C | RA-1974-03 | 1974 | Three morning departures make the same journey in twenty minutes. | ```text output Use Cua Driver to prepare a fictional Riverline Archive / Everyday Journeys exhibition. Preserve both source files and save new work in an output folder. In GIMP, crop the ticket from archive-contact-sheet.png: top-left origin, X = 200, Y = 300, width = 1800, height = 1100 pixels. Retain all ticket text; do not scale or retouch it. Save riverline-hero.xcf and export riverline-hero.png. In Inkscape, use panel-starter.svg to create two A4 landscape panels. Keep text editable, content inside the 15 mm safe margin, body text at least 24 pt, and headings at least 36 pt. Use teal #164E52, oxblood #702F3B, and ivory #F5EEDC. The entry panel must say Riverline Archive, Everyday Journeys, START HERE, A > B > C, and Fictional collection. The object key must include all three stations, object IDs, years, and exact captions from the supplied table. Save riverline-entry.svg and riverline-object-key.svg. Export each as PDF and as a 1782 × 1260 PNG. In LibreOffice Writer, create an A4 portrait installation packet with exactly two pages and an explicit page break. Page 1 needs the exhibition title, proportional ticket image, caption table, and fictional-collection notice. Page 2 needs both panel images and this illustrative plan: a 3600 mm wall; A, B, and C centered at 600, 1800, and 3000 mm from its left end; object centers 1400 mm high; label centers 1200 mm high; and 1500 mm of clear approach depth. Place the entry panel left of A and the object key right of C. Include a checklist to confirm object order, measure centers, keep the approach clear, proof labels, and check glare. Label these as illustrative targets, not measured venue conditions or formal accessibility compliance. Save riverline-installation.odt and export riverline-installation.pdf. Reopen the editable files in their apps and inspect the exported PDFs in Preview. Report output files, completed checks, and unresolved issues. Keep the outputs local; do not send or publish them. ``` **Check:** the XCF canvas is 1800 × 1100 with all ticket text; SVG text is still editable; the packet PDF has exactly two pages with every measurement. ## Build a report in Numbers macOS only (Numbers has no Linux or Windows version). ```text output Using cua-driver, fetch the last 30 days of AAPL daily closing prices. Open Numbers, create a new spreadsheet, enter Date and Close columns, add a 2D line chart, then export the document as a PDF to ~/reports/aapl-analysis.pdf. ``` The agent fetches data by driving Terminal (`curl`) or a browser, then drives Numbers with `launch_app`, `click`, `type_text`, and `set_value`. **Check:** open `~/reports/aapl-analysis.pdf` and confirm the table and chart. ## Automate a legacy Windows app behind a VPN Run Cua Driver on the Windows machine inside the VPN, so the desktop session, app traffic, and files stay there. [Drive a legacy postal app with Cua Driver](<https://github.com/user-attachments/assets/11b248e5-d505-4349-aaae-ad9f95a1162f>) Claude Code fills shipment details and prints receipts in a Windows desktop app with no API. Confirm the driver sees the app first: ```powershell test="cli-shape" id="cua-driver-call" cua-driver call list_apps cua-driver call list_windows ``` An empty `[]` means the driver is outside the interactive desktop; see [Windows over SSH](</docs/cua-driver/guides/vms-and-remote#windows-over-ssh>). Stage input files on that machine, then: ```text output Open the payroll client, log in with the credentials in C:\creds.txt, create a new employee from new_hire.xlsx, generate the onboarding report, and export it as PDF to the Desktop. ``` ## Export contacts overnight Log into LinkedIn or X yourself in the browser the agent will drive, keep the machine awake, and cap the run while testing: ```text output Using cua-driver, open the browser to https://www.linkedin.com/mynetwork/invite-connect/connections/ . For each of the first 50 connections: 1. Open the profile. 2. Read first name, last name, role, company, and profile URL. 3. Append one row to ~/contacts.csv. 4. Set met_at to "Google Devfest Toronto" for every row. Use this CSV header: first,last,role,company,met_at,linkedin If a value is missing, leave that field empty and keep going. ``` Spot-check `~/contacts.csv` in the morning before importing it anywhere. ## Fill a form from a local file The data stays on your machine: the agent opens the file in a viewer, reads it on screen, and types it into the form. Name the exact fields you want. ```text output Open ~/Downloads/resume.pdf in the PDF viewer. Read the name, email, phone, and work history. Then open https://form.jotform.com/<your-form-id> in the browser, fill each matching field from the PDF, and submit the form. ``` Watch the field mapping and correct it in plain language ("use the resume email for Work Email"). --- # How Cua Driver works Observation, targets, the background-first action ladder, verification, and who owns the runtime. Cua Driver is the UI layer of a computer-use agent. The harness runs the model and the loop; Cua Driver turns each proposed step into an observation or an action on a real desktop, and reports what happened. ```text output model -> agent harness -> Cua Driver (MCP, CLI, or SDK) -> OS accessibility + input -> apps ``` ## Observe: tree and pixels together `get_window_state(pid, window_id)` returns the window's accessibility tree and a screenshot in one call. The tree says what is actionable (roles, labels, an `element_token` per element); the screenshot says which one, and shows what the tree omits or gets wrong. Pass `include_screenshot: false` to skip the image when you only need to re-index. `get_desktop_state` captures the whole primary display. When neither the tree nor typed browser state exposes the target, the optional [perception extension](</docs/cua-driver/concepts/perception-extension>) can parse that same screenshot into labelled regions. ## Act: one exact target per call Every input tool (`click`, `type_text`, `press_key`, `hotkey`, `scroll`, `drag`, `move_cursor`) names its target: | Target | Coordinates | Delivery | | --- | --- | --- | | `{kind: "window", pid, window_id}` | Elements from that window's snapshot, or pixels in its screenshot | `background` (default) or `foreground` | | `{kind: "desktop", display_id: "primary"}` | Screen pixels from `get_desktop_state` | Foreground only | The target is chosen per call; nothing is locked into a session. A malformed or ambiguous target fails before anything is sent. ## The action ladder Background delivery means the driver does not raise the window, move the real pointer, or switch the frontmost app. The agent gets its own cursor overlay. 1. **Element, background.** Act by `element_token`: UI Automation Invoke on Windows, `AXPerformAction` on macOS, AT-SPI actions on Linux. This is the only rung the driver can verify itself. 2. **Pixel, background.** Act by `x, y` read from the same screenshot. For keyboard tools, `x, y` first clicks to give the field renderer focus, which is how to type into Chromium and Electron inputs. 3. **Page.** For browser tabs, switch to the [browser tools](</docs/cua-driver/guides/browsers>), which act on the DOM over CDP without focus. 4. **Foreground.** Retry only that action with `delivery_mode: "foreground"`: the window is raised, the input lands, and the previous app is restored. Needed for games, canvas apps such as Blender, and focus-polling apps. Each response says whether to climb: | Field | Values | | --- | --- | | `effect` | `confirmed` (read back through accessibility), `unverifiable`, `suspected_noop`, `partial`, `refused` | | `escalation` | `{recommended: "px" \| "page" \| "foreground", reason}` when a next rung exists | | `verified` | `true` only for accessibility read-back | A delivered event is not an applied change: Electron, Catalyst, and web content can echo a write they did not apply, so the driver reports those as `unverifiable` and recommends the next rung. After anything short of `confirmed`, observe again and check. Refusals carry a `code` and a recovery hint; `stale_element_token` means snapshot again, not that the element route is broken. ## Platform mechanisms | | Semantic route | Background input and capture | Main limit | | --- | --- | --- | --- | | macOS | Accessibility API | Scoped CoreGraphics and SkyLight events; ScreenCaptureKit per window | Off-Space SwiftUI windows lose their tree; needs TCC grants | | Windows | UI Automation | Window messages to the target HWND | The daemon must run in the interactive session, not Session 0 | | Linux X11 | AT-SPI | Window-addressed X11 input and capture | Some toolkits reject synthetic background events | | Linux Wayland | AT-SPI | Compositor-specific; no general raw input to other windows | Raw background keys refuse; use AT-SPI, XWayland, or foreground | On Wayland, knowing where a window is does not change which window the compositor's seat sends input to, so the driver refuses rather than risk typing into the wrong app. A structured `background_unavailable` is part of the contract, not a failure to hide. ## Processes and sessions The runtime owns permissions, sessions, element caches, recordings, and browser connections. Where it lives depends on how you connect: | Entry point | Runtime | | --- | --- | | `CuaDriver.create()` | Inside your process | | `create_private_worker()` | A supervised child over inherited pipes | | `cua-driver mcp` on Windows and Linux | The MCP process itself (exits on stdin EOF) | | `cua-driver mcp` on macOS | Proxies to the `CuaDriver.app` daemon (`--direct` to own it instead) | | `cua-driver serve` + `mcp --socket` / `call` / `connect()` | A long-lived daemon | The daemon exists for identity. On macOS, Accessibility and Screen Recording grants belong to `CuaDriver.app` (`com.trycua.driver`), not to whatever terminal spawned a `cua-driver` process. On Windows, an SSH process in Session 0 cannot see the desktop, so a daemon in the user's session does the work. Each MCP connection or SDK runtime gets one implicit session, reused by unnamed calls and cleaned up when the connection closes, on `end_session`, or after five idle minutes. After idle expiry, the next unnamed call starts a new implicit session, and element tokens and snapshots from the ended one stay invalid, so observe again before acting. An ended named session refuses calls with `session_ended` until `start_session` restarts it. Pass a `session` label on each call for a readable name and a cursor badge; the label is not a credential. One-shot `cua-driver call` commands each get a disposable session. All sessions still share one screen, keyboard, and focus, and native input is admitted one action at a time. The optional HTTP MCP listener is off unless `CUA_DRIVER_RS_MCP_HTTP_PORT` is set, and then requires `CUA_DRIVER_RS_MCP_HTTP_TOKEN` (32 to 4096 characters, sent as a bearer token). Windows named pipes accept only the daemon owner's user. ## Permissions sit below every transport CLI, MCP, SDK, and daemon calls all pass the same authorization check inside the runtime. The launcher fixes the mode, manifest, and grants at startup; an agent cannot widen them. See [Set permissions](</docs/cua-driver/guides/permissions>). ## How it is validated A tool returning `ok` is not evidence. Each supported behavior is a cell in one Rust catalog (action × element/pixel × background/foreground × window/desktop × surface), run against source-built fixture apps in a real desktop session. A cell passes only when state the app or desktop owns changed, and, for background cells, when focus, z-order, the real cursor, and the foreground app stayed untouched. An exact refusal also passes; a silent success does not. Runs keep per-cell video and logs tied to the source commit. Current results are in [Platform support](</docs/cua-driver/concepts/platform-support>). --- # Perception extension How the optional cua-perception extension turns one screenshot into regions an agent can act on, and how Cua Driver binds every resulting action to that exact screenshot. The perception extension lets an agent act on what it can see in a window when the application exposes no usable target. It turns one screenshot into labelled regions (text, icons, controls), and Cua Driver acts on exactly the region the agent chose, bound to the screenshot it came from. It is optional and runs entirely on the local machine. Cua Driver works without it: the accessibility tree and typed browser state remain the first choice for finding a target. ## When it helps Many interfaces hide their targets. Canvas apps, custom-drawn views, games, remote desktops, and some web pages have no useful accessibility tree or page structure, so an agent has nothing to address by role or label. The extension gives those surfaces a structure without sending screenshots to a remote model. Every [`get_window_state`](</docs/cua-driver/concepts/how-cua-driver-works#observe-tree-and-pixels-together>) call already returns the accessibility tree and a screenshot together. The extension adds one step on top of that observation: it parses the screenshot into regions. It does not replace the tree, and it never captures on its own. ## The loop 1. **Capture.** `get_window_state` captures the exact window and returns a `capture_id` with the screenshot. 2. **Parse.** `parse_visual_regions` turns that screenshot into typed regions with bounds and, for text, the recognized words. 3. **Choose.** The agent, or an external chooser such as a decision model, selects one region. 4. **Act.** Cua Driver clicks the region using the same `capture_id`, trying background delivery first. 5. **Reobserve.** The agent takes a fresh capture to confirm the result before the next step. Without the extension installed, `parse_visual_regions` returns `not_installed`, and agents continue with the accessibility tree or page structure. Nothing is downloaded automatically. ## Every action is bound to one screenshot A region only means something on the screen it was found on. Cua Driver therefore ties each perception-based action to its capture: | Situation | Result | | --- | --- | | The click carries the `capture_id` of the screenshot the region came from | Delivered | | The same capture is reused for another action | Refused with `capture_not_found` | | The capture is older than 60 seconds | Refused with `capture_expired` | Agents receive region bounds and text, never screenshot bytes, and Cua Driver keeps the authority to capture and to act. A stale or reused screen is refused instead of guessed at. ## What the extension contains Each release packs local models and a CPU runtime, so parsing needs no GPU and no network access. | Component | Role | License | | --- | --- | --- | | OmniParser icon detector | Finds icons and controls | AGPL-3.0-only | | PP-OCR detector and recognizer | Finds and reads on-screen text | Apache-2.0 | | ONNX Runtime (CPU) | Runs the models locally | Shipped with its own notices | Releases are published as `cua-perception-v<version>` GitHub releases for macOS arm64, Linux x64, and Windows x64. Each target has a signed catalog, the archive, an SBOM, provenance, a runtime contract, and an install check, plus one `SHA256SUMS`. Cua Driver verifies the signed catalog before it installs anything, and a verified install reports `publisher-verified`. **Warning: License precautions** The extension is not MIT licensed. Its OmniParser icon detector is AGPL-3.0-only, and Cua does not relicense it. Installing the extension does not change Cua Driver's MIT license, but redistributing the extension, or offering it to users over a network (for example, inside a hosted product), can require you to provide the AGPL corresponding source. If your organization does not accept AGPL components, skip the extension: Cua Driver keeps working, and `parse_visual_regions` returns `not_installed`. This is not legal advice. See the [third-party notices and precautions](https://github.com/trycua/cua/blob/main/libs/cua-driver/docs/perception-third-party-notices.md#before-you-install-redistribute-or-host-the-extension). ## Install is always explicit Capture, parsing, Cua Driver startup, and update checks never install the extension. You inspect a catalog first and then install, check, update, or remove the extension with `cua-driver extension` commands. A running Driver picks up an install or removal without a restart. ## Performance Parsing runs on the CPU. Measured per screenshot on the canonical harnesses: about 2–2.5 seconds on macOS, 3.5–4 seconds on Linux, and 8–9 seconds on Windows. Prefer accessibility or page targets when they exist, and reserve perception for surfaces that expose nothing addressable. ## Related pages - [Parse visual regions](</docs/cua-driver/guides/visual-regions>): install the extension, run the loop step by step, and let a decision model such as jev-use choose among regions. - [How Cua Driver works](</docs/cua-driver/concepts/how-cua-driver-works>): how observation, action rungs, and delivery fit together. --- # Platform support Proven operating systems, Linux window systems, and browser routes, with known boundaries and remaining work. A capability counts as supported only when a Rust harness test observes the result in application- or desktop-owned state (see [How Cua Driver works](</docs/cua-driver/concepts/how-cua-driver-works#how-it-is-validated>)). Unsupported routes return a structured refusal. | Level | Meaning | | --- | --- | | Supported | Proven against app-owned or desktop-owned state. | | Supported with limits | Common paths proven; some delivery shapes refuse. | | Experimental | Backend exists; coverage incomplete. Assume nothing unlisted works. | ## Operating systems | Platform | APIs | State | | --- | --- | --- | | Windows | Win32, UI Automation, native input, window messages | Supported. Coverage: Electron, Tauri, WPF, WinUI 3, WebView2. Some background Chromium gestures and elevated-integrity targets refuse or are unproven. | | macOS | AppKit, Accessibility, Quartz/HID, ScreenCaptureKit | Supported. Coverage: Electron, Tauri, AppKit, SwiftUI, WKWebView. Needs Accessibility and Screen Recording. Some background scroll and drag shapes refuse. | | Linux X11 | X11/EWMH, XTest, AT-SPI | Supported with toolkit limits. Toolkits that reject synthetic background events get an explicit refusal. | | Linux Wayland | AT-SPI plus compositor-specific discovery, capture, activation, and portal input | Supported with compositor limits. Semantic background actions work where the app exposes them; raw input to an occluded window generally does not. | ## Linux window systems "Wayland" is not one API: each compositor exposes different discovery, capture, activation, and input. Native Wayland is opt-in with `CUA_DRIVER_RS_ENABLE_WAYLAND=1`; without it, Wayland sessions use XWayland routes where the app has a real X11 window. | Environment | State | Proven | Main limits | | --- | --- | --- | --- | | X11/Xorg | Supported | Discovery, capture, AT-SPI, foreground input, semantic background actions, desktop scope | Raw background delivery depends on the toolkit. | | Sway (wlroots) | Supported with limits | The full Electron, Tauri, GTK, and capture catalog | Focus-bound raw background input refuses. Other wlroots compositors unproven. | | GNOME/Mutter | Supported with limits | AT-SPI, GTK controls, compositor geometry and capture, foreground activation, portal/libei input | Needs the bundled [WinRects Shell helper](</docs/cua-driver/quickstart>) and one Shell restart. Portal video recording incomplete. | | KDE/KWin | Experimental | Plasma 6 startup, GTK AT-SPI discovery, portal availability | The KWin helper is read-only; raw target-addressed input refuses. | | Hyprland / Omarchy | Experimental | [Qualified isolated input](#hyprland-and-omarchy) since 0.24.0 | Does not inherit Sway coverage. | | `cua-compositor` (nested) | Experimental | Native GTK behavior, capture, per-surface raw input | Owns its own compositor; says nothing about stock Wayland. | | XWayland | Supported with limits | X11 routes for real X11 windows; AT-SPI fallback | Depends on whether the app actually uses X11. | X11 window capture supports packed 16-, 24-, and 32-bit TrueColor and DirectColor images. DirectColor capture reads the window's current colormap for each frame, including palette changes that do not repaint the window. Other indexed visual classes remain outside the native capture path. Distribution names (Ubuntu, Arch) do not matter much; what matters is the display session and the app's backend. A portal grant belongs to the runtime that received it and may prompt again after a restart. ### Hyprland and Omarchy Omarchy is an Arch setup built on Hyprland. Modern Hyprland is not wlroots, so Sway results do not carry over. An optional Hyprland plugin, off by default and outside the normal installer, adds two independent agent seats (`Cua-Agent`, `Cua-Agent-2`) for isolated background input. Cua Driver 0.24.0 shipped it with a narrow qualification: - Apps: Calc from `libreoffice-fresh 26.2.5-3` and Inkscape `1.4.4-6`, checked against package metadata before every action. - The plugin's Hyprland ABI and compiler toolchain must match the compositor. - Not covered: Chromium, Electron, and XWayland raw input; Unicode and IME; multiple layouts; modified pointer gestures. - Explicit foreground actions change focus and pointer and do not restore them. Evidence for this feature is recorded in PRs [#3547](https://github.com/trycua/cua/pull/3547), [#3557](https://github.com/trycua/cua/pull/3557), and [#3572](https://github.com/trycua/cua/pull/3572). General Hyprland support still needs an accepted input and permission design, packaged install and upgrade proof, and the full native catalog on an integrated candidate. ## Browser routes Browser mutation always starts from an exact native `(pid, window_id)` binding. | Surface | Proven | Limit | | --- | --- | --- | | Chrome and Edge on Windows | Snapshot, navigate, type, trusted background click, DOM pointer actions, dialogs, file assignment, downloads, frames, multi-tab | Elevated-integrity targets refuse. | | Chrome on macOS; Chrome and Edge on Linux X11 | The same, except trusted pointer input | Trusted pointer returns `browser_input_trust_unavailable`; Linux dialogs need foreground. | | Chrome on Sway | Exact binding when compositor identity and geometry agree | Generic GNOME or KDE Wayland is read-only. | | Electron (Windows, macOS, X11, Sway) | Typed mutation while one native window maps to one page | A second page or window invalidates the route. | | Brave, Vivaldi, Opera, Arc | Detected, not release-accepted | Use at your own risk. | | Tauri, WKWebView, WebKitGTK, split-process WebView2, Safari, Firefox | Browser identity and a clean refusal | Use native accessibility and pixel actions. | Logged-in profile attachment is covered in [Browsers](</docs/cua-driver/guides/browsers#attach-to-a-logged-in-profile>). ## Boundaries and remaining work These are operating-system boundaries, not bugs; the driver refuses instead of working around them: - **Windows integrity levels:** a lower-integrity process cannot inject input into a higher-integrity (elevated) app. - **Standard Wayland:** an ordinary client cannot send raw input to an unfocused, occluded window. AT-SPI actions still work in the background. - **macOS:** off-Space SwiftUI trees, keyboard commits to minimized windows, and apps that accept only HID-tap input. Workarounds are in [Troubleshoot](</docs/cua-driver/guides/troubleshoot#known-limits>). ### Off-Space windows on macOS macOS omits windows on other Spaces from an app's `AXWindows` list. For AppKit windows, the driver recovers the requested window by its exact CGWindowID, so `get_window_state`, element actions, and background pixel actions keep working while the window stays on its Space. The user's current Space, frontmost app, and keyboard focus do not change. Recovery probes the first 2,000 accessibility element ids of the app; a window beyond that limit reports `ax_window_unresolved`. The agent cursor does not paint while an action targets a window on another Space. Windows virtual desktops and Linux workspaces do not yet suppress the cursor for off-workspace targets. ### Desktop captures leave out the agent cursor `get_desktop_state` keeps the driver's own agent cursor and session pill out of the PNG it returns. Every response reports the outcome in `structuredContent.agent_overlay_capture`: `excluded` with the native `method`, `not_present` when no overlay pixels were on screen, or `not_excluded` with a `reason`. | Platform | How the cursor is left out | | --- | --- | | macOS 14 or later | ScreenCaptureKit display filter; the `screencapture` fallback reports `not_excluded`. | | Windows 10 2004 or later | `WDA_EXCLUDEFROMCAPTURE` for the length of the capture; older builds report `not_excluded`. | | Linux X11 | The overlay hides for the root grab (`method` ends in `+x11_save_under` without a compositor). | | Linux Wayland | The compositor draws the cursor into the output; captures report `not_excluded`. | Open evidence gaps, with no dates promised: broader WPF and WinUI 3 gestures, WebView2 native input, a controlled `background_uipi_blocked` fixture, more AppKit actions and SwiftUI popovers, real-Xorg multi-pointer, GNOME and KDE renderer catalogs, and the full nested-compositor catalog. A row changes only when an unchanged catalog cell produces new app-owned evidence at an exact source commit. **Note** Contributors: source layout, harnesses, and runner commands are in the repository's `libs/cua-driver/README.md` and `libs/cua-driver/docs/test-harnesses-guide.md`. --- # Overview Every cua-driver command and every MCP tool on macOS, Linux and Windows, generated from the driver. ## Commands | Command group | Description | | --- | --- | | [`cua-driver`](</docs/cua-driver/reference/cli>) | Synopsis, global options, exit codes and every command. | | [Tools and MCP](</docs/cua-driver/reference/cli/mcp>) | Serve MCP, print client config, and list, describe or call tools from the shell. | | [Daemon](</docs/cua-driver/reference/cli/daemon>) | Run, stop and inspect the daemon, its sessions and autostart. | | [Permissions and diagnostics](</docs/cua-driver/reference/cli/permissions>) | macOS permission grants, diagnostic probes and install reports. | | [Trajectory recording](</docs/cua-driver/reference/cli/recording>) | Record, inspect and render action trajectories. | | [Computer History](</docs/cua-driver/reference/cli/history>) | The encrypted, metadata-only Computer History preview. | | [Configuration](</docs/cua-driver/reference/cli/config>) | Driver configuration, telemetry, cursor themes and cursor motion. | | [Skills and extensions](</docs/cua-driver/reference/cli/extensions>) | The agent skill pack, signed extensions and the perception parser. | | [Updates](</docs/cua-driver/reference/cli/updates>) | Check for and apply updates, and choose the release channel. | ## MCP tools | Tools | Description | | --- | --- | | [All tools](</docs/cua-driver/reference/mcp-tools>) | Every `cua-driver mcp` tool with a one-line summary. | | [App and window tools](</docs/cua-driver/reference/mcp-tools/apps-and-windows>) | List, launch, quit, front and arrange apps and windows. 8 tools. | | [Window state tools](</docs/cua-driver/reference/mcp-tools/window-state>) | Screenshots, accessibility trees, verification and visual regions of a window. 4 tools. | | [Screen tools](</docs/cua-driver/reference/mcp-tools/screen>) | The desktop, screen size, cursor position and zoomed crops. 4 tools. | | [Click tool](</docs/cua-driver/reference/mcp-tools/click>) | Click by element or pixel. 1 tools. | | [Double-click and right-click tools](</docs/cua-driver/reference/mcp-tools/double-and-right-click>) | Double-click and right-click by element or pixel. 2 tools. | | [Pointer tools](</docs/cua-driver/reference/mcp-tools/pointer>) | Drag, scroll, move the pointer, and press or release buttons. 7 tools. | | [Typing tools](</docs/cua-driver/reference/mcp-tools/keyboard>) | Type text into a field or window. 1 tools. | | [Key and shortcut tools](</docs/cua-driver/reference/mcp-tools/keys-and-shortcuts>) | Press keys and keyboard shortcuts. 2 tools. | | [Value and clipboard tools](</docs/cua-driver/reference/mcp-tools/values-and-clipboard>) | Set element values and read or write the clipboard. 3 tools. | | [Batch tool](</docs/cua-driver/reference/mcp-tools/batch>) | Run several action tools in one call. 1 tools. | | [Page tools](</docs/cua-driver/reference/mcp-tools/page>) | Read and act on web pages, and prepare and navigate browsers. 4 tools. | | [Browser input tools](</docs/cua-driver/reference/mcp-tools/browser-input>) | Exact browser input over CDP: clicks, typing, pointer, dialogs, files and downloads. 6 tools. | | [Session tools](</docs/cua-driver/reference/mcp-tools/sessions>) | Start, escalate, inspect and end lifecycle sessions. 6 tools. | | [Agent cursor tools](</docs/cua-driver/reference/mcp-tools/agent-cursor>) | Show, animate and style the agent cursor. 4 tools. | | [Recording tools](</docs/cua-driver/reference/mcp-tools/recording>) | Record and replay trajectories. 5 tools. | | [Configuration and maintenance tools](</docs/cua-driver/reference/mcp-tools/maintenance>) | Configuration, permissions, health, updates and extensions. 6 tools. | --- # cua-driver Cross-platform computer-use automation driver. ```text output cua-driver [OPTIONS] <COMMAND> ``` Run `cua-driver <command> --help` for the build you have. ## Commands ### Tools and MCP Serve MCP, print client config, and list, describe or call tools from the shell. [Reference](</docs/cua-driver/reference/cli/mcp>). | Command | Description | | --- | --- | | [`cua-driver mcp`](</docs/cua-driver/reference/cli/mcp#cua-driver-mcp>) | Run the stdio MCP server. | | [`cua-driver mcp-config`](</docs/cua-driver/reference/cli/mcp#cua-driver-mcp-config>) | Print client-specific connection guidance (MCP config where supported). | | [`cua-driver list-tools`](</docs/cua-driver/reference/cli/mcp#cua-driver-list-tools>) | List every registered MCP tool with a one-line description. | | [`cua-driver describe`](</docs/cua-driver/reference/cli/mcp#cua-driver-describe>) | Print a tool's full description and JSON input schema. | | [`cua-driver call`](</docs/cua-driver/reference/cli/mcp#cua-driver-call>) | Invoke an MCP tool through the running daemon. | | [`cua-driver manifest`](</docs/cua-driver/reference/cli/mcp#cua-driver-manifest>) | Emit a stable JSON description of the CLI surface. | | [`cua-driver dump-docs`](</docs/cua-driver/reference/cli/mcp#cua-driver-dump-docs>) | Output machine-readable CLI and MCP documentation JSON. | ### Daemon Run, stop and inspect the daemon, its sessions and autostart. [Reference](</docs/cua-driver/reference/cli/daemon>). | Command | Description | | --- | --- | | [`cua-driver serve`](</docs/cua-driver/reference/cli/daemon#cua-driver-serve>) | Run Cua Driver as a long-running daemon. | | [`cua-driver stop`](</docs/cua-driver/reference/cli/daemon#cua-driver-stop>) | Ask the running daemon to exit gracefully. | | [`cua-driver status`](</docs/cua-driver/reference/cli/daemon#cua-driver-status>) | Report whether a Cua Driver daemon is running. | | [`cua-driver sessions`](</docs/cua-driver/reference/cli/daemon#cua-driver-sessions>) | List content-free lifecycle summaries for sessions owned by the daemon runtime. | | [`cua-driver sessions list`](</docs/cua-driver/reference/cli/daemon#cua-driver-sessions-list>) | List live sessions. | | [`cua-driver revoke`](</docs/cua-driver/reference/cli/daemon#cua-driver-revoke>) | Revoke one or all live authorization/session scopes. | | [`cua-driver autostart`](</docs/cua-driver/reference/cli/daemon#cua-driver-autostart>) | Manage platform-native daemon autostart. | | [`cua-driver autostart enable`](</docs/cua-driver/reference/cli/daemon#cua-driver-autostart-enable>) | Register the autostart entry. | | [`cua-driver autostart disable`](</docs/cua-driver/reference/cli/daemon#cua-driver-autostart-disable>) | Remove the autostart entry. | | [`cua-driver autostart status`](</docs/cua-driver/reference/cli/daemon#cua-driver-autostart-status>) | Print whether autostart is registered and running. | | [`cua-driver autostart kick`](</docs/cua-driver/reference/cli/daemon#cua-driver-autostart-kick>) | Start the autostart entry now without re-logging. | ### Permissions and diagnostics macOS permission grants, diagnostic probes and install reports. [Reference](</docs/cua-driver/reference/cli/permissions>). | Command | Description | | --- | --- | | [`cua-driver permissions`](</docs/cua-driver/reference/cli/permissions#cua-driver-permissions>) | Inspect or raise macOS TCC permission grants (macOS only). | | [`cua-driver permissions status`](</docs/cua-driver/reference/cli/permissions#cua-driver-permissions-status>) | Report Accessibility and Screen Recording status through a running daemon (read-only, no prompt). | | [`cua-driver permissions grant`](</docs/cua-driver/reference/cli/permissions#cua-driver-permissions-grant>) | Launch CuaDriver through LaunchServices so the permission prompts are attributed to the app. | | [`cua-driver doctor`](</docs/cua-driver/reference/cli/permissions#cua-driver-doctor>) | Run platform-aware diagnostic probes. | | [`cua-driver diagnose`](</docs/cua-driver/reference/cli/permissions#cua-driver-diagnose>) | Print a pasteable install-layout and permission-attribution report. | ### Trajectory recording Record, inspect and render action trajectories. [Reference](</docs/cua-driver/reference/cli/recording>). | Command | Description | | --- | --- | | [`cua-driver recording`](</docs/cua-driver/reference/cli/recording#cua-driver-recording>) | Control trajectory recording on a running daemon. | | [`cua-driver recording start`](</docs/cua-driver/reference/cli/recording#cua-driver-recording-start>) | Start trajectory recording to a directory. | | [`cua-driver recording stop`](</docs/cua-driver/reference/cli/recording#cua-driver-recording-stop>) | Stop trajectory recording. | | [`cua-driver recording status`](</docs/cua-driver/reference/cli/recording#cua-driver-recording-status>) | Print the current recording state. | | [`cua-driver recording render`](</docs/cua-driver/reference/cli/recording#cua-driver-recording-render>) | Render a recorded trajectory directory to an MP4. | ### Computer History The encrypted, metadata-only Computer History preview. [Reference](</docs/cua-driver/reference/cli/history>). | Command | Description | | --- | --- | | [`cua-driver history`](</docs/cua-driver/reference/cli/history#cua-driver-history>) | Encrypted, metadata-only Computer History early preview: lifecycle and local inspection. | | [`cua-driver history enable`](</docs/cua-driver/reference/cli/history#cua-driver-history-enable>) | Turn on the Computer History preview (admits it on a local daemon if needed). | | [`cua-driver history disable`](</docs/cua-driver/reference/cli/history#cua-driver-history-disable>) | Turn Computer History off; recorded chunks stay until deleted. | | [`cua-driver history pause`](</docs/cua-driver/reference/cli/history#cua-driver-history-pause>) | Pause recording without turning the preview off. | | [`cua-driver history resume`](</docs/cua-driver/reference/cli/history#cua-driver-history-resume>) | Resume recording after pause. | | [`cua-driver history status`](</docs/cua-driver/reference/cli/history#cua-driver-history-status>) | Print the Computer History state. | | [`cua-driver history flush`](</docs/cua-driver/reference/cli/history#cua-driver-history-flush>) | Seal the current chunk to disk now. | | [`cua-driver history list`](</docs/cua-driver/reference/cli/history#cua-driver-history-list>) | List recent metadata-only events. | | [`cua-driver history show`](</docs/cua-driver/reference/cli/history#cua-driver-history-show>) | Print one event by its sequence number. | | [`cua-driver history delete`](</docs/cua-driver/reference/cli/history#cua-driver-history-delete>) | Delete the encrypted chunks and their native credential-store key. | ### Configuration Driver configuration, telemetry, cursor themes and cursor motion. [Reference](</docs/cua-driver/reference/cli/config>). | Command | Description | | --- | --- | | [`cua-driver config`](</docs/cua-driver/reference/cli/config#cua-driver-config>) | Read or mutate persistent driver configuration. | | [`cua-driver config show`](</docs/cua-driver/reference/cli/config#cua-driver-config-show>) | Print the full config. | | [`cua-driver config get`](</docs/cua-driver/reference/cli/config#cua-driver-config-get>) | Print one config key. | | [`cua-driver config set`](</docs/cua-driver/reference/cli/config#cua-driver-config-set>) | Set one config key. | | [`cua-driver config reset`](</docs/cua-driver/reference/cli/config#cua-driver-config-reset>) | Reset config to defaults. | | [`cua-driver telemetry`](</docs/cua-driver/reference/cli/config#cua-driver-telemetry>) | Inspect or change content-free product telemetry. | | [`cua-driver telemetry enable`](</docs/cua-driver/reference/cli/config#cua-driver-telemetry-enable>) | Persistently enable telemetry. | | [`cua-driver telemetry disable`](</docs/cua-driver/reference/cli/config#cua-driver-telemetry-disable>) | Persistently disable every telemetry request. | | [`cua-driver telemetry status`](</docs/cua-driver/reference/cli/config#cua-driver-telemetry-status>) | Show the effective setting and redacted identity state. | | [`cua-driver telemetry reset-id`](</docs/cua-driver/reference/cli/config#cua-driver-telemetry-reset-id>) | Erase the installation ID and event markers. | | [`cua-driver telemetry inspect`](</docs/cua-driver/reference/cli/config#cua-driver-telemetry-inspect>) | Build a fixed event payload without sending it. | | [`cua-driver cursor-theme`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-theme>) | Validate, compile, inspect, preview, install, or remove a local cursor theme. | | [`cua-driver cursor-theme validate`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-theme-validate>) | Validate a bounded dotLottie source archive. | | [`cua-driver cursor-theme build`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-theme-build>) | Compile a validated dotLottie archive into a bounded .cua-theme artifact. | | [`cua-driver cursor-theme inspect`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-theme-inspect>) | Inspect metadata in a compiled .cua-theme artifact. | | [`cua-driver cursor-theme preview`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-theme-preview>) | Render a compiled theme's representative still frames to a directory. | | [`cua-driver cursor-theme install`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-theme-install>) | Install a compiled theme into the current user's theme store. | | [`cua-driver cursor-theme list`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-theme-list>) | List the built-in and installed cursor themes. | | [`cua-driver cursor-theme uninstall`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-theme-uninstall>) | Remove a custom theme from the current user's theme store. | | [`cua-driver cursor`](</docs/cua-driver/reference/cli/config#cua-driver-cursor>) | Configure a session's agent cursor from the command line. | | [`cua-driver cursor motion`](</docs/cua-driver/reference/cli/config#cua-driver-cursor-motion>) | Set the cursor's motion style, timing and effects. | ### Skills and extensions The agent skill pack, signed extensions and the perception parser. [Reference](</docs/cua-driver/reference/cli/extensions>). | Command | Description | | --- | --- | | [`cua-driver skills`](</docs/cua-driver/reference/cli/extensions#cua-driver-skills>) | Install, update, inspect, or remove the optional agent skill pack. | | [`cua-driver skills install`](</docs/cua-driver/reference/cli/extensions#cua-driver-skills-install>) | Fetch the versioned skill pack and link detected agents. | | [`cua-driver skills update`](</docs/cua-driver/reference/cli/extensions#cua-driver-skills-update>) | Refresh the local skill pack and links. | | [`cua-driver skills uninstall`](</docs/cua-driver/reference/cli/extensions#cua-driver-skills-uninstall>) | Remove agent skill links. | | [`cua-driver skills status`](</docs/cua-driver/reference/cli/extensions#cua-driver-skills-status>) | Report local skill-pack and per-agent link state. | | [`cua-driver skills path`](</docs/cua-driver/reference/cli/extensions#cua-driver-skills-path>) | Print the local skill-pack path. | | [`cua-driver extension`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension>) | Inspect and manage signed, target-specific optional extensions. | | [`cua-driver extension list`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension-list>) | List registry-known extensions and local state. | | [`cua-driver extension info`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension-info>) | Compatibility alias for installed extension status. | | [`cua-driver extension inspect`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension-inspect>) | Verify and preview license, source, provenance, and hashes without mutation. | | [`cua-driver extension status`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension-status>) | Verify installed integrity and optionally run the self-test hook. | | [`cua-driver extension install`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension-install>) | Verify, stage, and durably activate an extension. | | [`cua-driver extension update`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension-update>) | Verify and atomically activate a newer extension version. | | [`cua-driver extension remove`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension-remove>) | Remove only a fully validated, manager-owned extension tree. | | [`cua-driver extension path`](</docs/cua-driver/reference/cli/extensions#cua-driver-extension-path>) | Compatibility command that prints the exact active version directory. | | [`cua-driver perception`](</docs/cua-driver/reference/cli/extensions#cua-driver-perception>) | Parse a local PNG with the installed optional perception extension. | | [`cua-driver perception parse`](</docs/cua-driver/reference/cli/extensions#cua-driver-perception-parse>) | Parse one local PNG into canonical visual regions. | ### Updates Check for and apply updates, and choose the release channel. [Reference](</docs/cua-driver/reference/cli/updates>). | Command | Description | | --- | --- | | [`cua-driver check-update`](</docs/cua-driver/reference/cli/updates#cua-driver-check-update>) | Check whether a newer cua-driver release is available. | | [`cua-driver update`](</docs/cua-driver/reference/cli/updates#cua-driver-update>) | Check for an update and optionally apply it. | | [`cua-driver channel`](</docs/cua-driver/reference/cli/updates#cua-driver-channel>) | Inspect or change the stable/nightly update channel. | | [`cua-driver channel status`](</docs/cua-driver/reference/cli/updates#cua-driver-channel-status>) | Show selected and current release channels. | | [`cua-driver channel set`](</docs/cua-driver/reference/cli/updates#cua-driver-channel-set>) | Save stable or nightly as the update channel. | ## Global options `--help` prints help for any command; `--version` prints the version. ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # Tools and MCP Serve MCP, print client config, and list, describe or call tools from the shell. | Command | Description | | --- | --- | | [`cua-driver mcp`](#cua-driver-mcp) | Run the stdio MCP server. | | [`cua-driver mcp-config`](#cua-driver-mcp-config) | Print client-specific connection guidance (MCP config where supported). | | [`cua-driver list-tools`](#cua-driver-list-tools) | List every registered MCP tool with a one-line description. | | [`cua-driver describe`](#cua-driver-describe) | Print a tool's full description and JSON input schema. | | [`cua-driver call`](#cua-driver-call) | Invoke an MCP tool through the running daemon. | | [`cua-driver manifest`](#cua-driver-manifest) | Emit a stable JSON description of the CLI surface. | | [`cua-driver dump-docs`](#cua-driver-dump-docs) | Output machine-readable CLI and MCP documentation JSON. | Every command also accepts the [global options](</docs/cua-driver/reference/cli#global-options>). ## `cua-driver mcp` Run the stdio MCP server. On Windows and Linux, bare cua-driver mcp owns its runtime directly and shuts it down on stdin EOF. On macOS it proxies to CuaDriver.app so desktop permissions retain the app identity. Pass --direct to make the macOS MCP process own the runtime and TCC attribution, or --socket to select an explicit daemon endpoint. ```text output cua-driver mcp [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | string | | Select an explicit daemon socket or named-pipe endpoint. | | `--host-bundle-id` | string | | Advisory host bundle id label echoed in check_permissions output (embedded mode). | | `--cursor-theme` | string | `cua.default` | Select an installed cursor theme id. | | `--cursor-reduced-motion` | string | `auto` | Follow the OS setting, force still frames, or allow animation: auto, on, or off. | | `--grant` | string | | Pre-authorize a residual standard-mode boundary for a newly launched runtime. Repeatable; supported value: existing-profile. | | `--direct` | boolean | `false` | Own the runtime in this MCP process; mutually exclusive with --socket. | | `--claude-code-computer-use-compat` | boolean | `false` | Accepted for older Claude Code setup snippets; there is no standalone screenshot tool; use get_window_state for window screenshots. | | `--embedded` | boolean | `false` | Declare embedding-host mode. Without --direct, require the host's private service through --socket instead of auto-launching the standalone app. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-mcp" # Run the stdio MCP server (what MCP clients launch) cua-driver mcp # Connect to a daemon on an explicit socket cua-driver mcp --socket /tmp/cua-driver.sock ``` ## `cua-driver mcp-config` Print client-specific connection guidance (MCP config where supported). Supported clients include claude, codex, cursor, antigravity, openclaw, opencode, hermes, pi, prime-agent, qwen, droid, and zcode. ```text output cua-driver mcp-config [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--client` | string | | Client name to print configuration for. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-mcp-config" # Print a generic mcpServers entry cua-driver mcp-config # Print setup for Codex cua-driver mcp-config --client codex ``` ## `cua-driver list-tools` List every registered MCP tool with a one-line description. ```text output cua-driver list-tools ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-list-tools" # List every tool with a one-line description cua-driver list-tools ``` ## `cua-driver describe` Print a tool's full description and JSON input schema. ```text output cua-driver describe <tool-name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<tool-name>` | string | required | Name of the MCP tool to describe. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-describe" # Print a tool's description and input schema cua-driver describe click ``` ## `cua-driver call` Invoke an MCP tool through the running daemon. Requires a Cua Driver daemon. JSON arguments may be passed as a positional JSON object or through stdin. ```text output cua-driver call [OPTIONS] <tool-name> [<json-args>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<tool-name>` | string | required | Name of the MCP tool to invoke. | | `<json-args>` | string | optional | JSON object for the tool input schema. If omitted, stdin is read when piped. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--screenshot-out-file` | path | | Write the first image content block from the response to this path. | | `--socket` | string | | Override the daemon socket or named-pipe path. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-call" # Call a tool with no arguments cua-driver call list_apps # Click at window coordinates cua-driver call click '{"pid":844,"x":100,"y":200}' # Save the screenshot from a tool response cua-driver call get_window_state '{"pid":844,"window_id":10725}' --screenshot-out-file state.png # Read only what changed since an earlier snapshot_id cua-driver call get_window_state '{"pid":844,"window_id":10725,"since":"s0000002a"}' # Return the structured elements instead of the markdown tree cua-driver call get_window_state '{"pid":844,"window_id":10725,"tree_format":"elements"}' ``` ## `cua-driver manifest` Emit a stable JSON description of the CLI surface. Consumers can use this instead of hardcoding launch arguments such as the MCP invocation. ```text output cua-driver manifest [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--pretty` | `-p` | boolean | `false` | Pretty-print JSON. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-manifest" # Print the CLI surface as JSON cua-driver manifest --pretty ``` ## `cua-driver dump-docs` Output machine-readable CLI and MCP documentation JSON. Used by the docs generator to keep reference pages in sync with the live binary. ```text output cua-driver dump-docs [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--type` | | string | `all` | Which docs to emit: all, cli, or mcp. | | `--pretty` | `-p` | boolean | `false` | Pretty-print JSON. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-dump-docs" # Print the CLI documentation JSON cua-driver dump-docs --type cli --pretty ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # Daemon Run, stop and inspect the daemon, its sessions and autostart. | Command | Description | | --- | --- | | [`cua-driver serve`](#cua-driver-serve) | Run Cua Driver as a long-running daemon. | | [`cua-driver stop`](#cua-driver-stop) | Ask the running daemon to exit gracefully. | | [`cua-driver status`](#cua-driver-status) | Report whether a Cua Driver daemon is running. | | [`cua-driver sessions`](#cua-driver-sessions) | List content-free lifecycle summaries for sessions owned by the daemon runtime. | | [`cua-driver sessions list`](#cua-driver-sessions-list) | List live sessions. | | [`cua-driver revoke`](#cua-driver-revoke) | Revoke one or all live authorization/session scopes. | | [`cua-driver autostart`](#cua-driver-autostart) | Manage platform-native daemon autostart. | | [`cua-driver autostart enable`](#cua-driver-autostart-enable) | Register the autostart entry. | | [`cua-driver autostart disable`](#cua-driver-autostart-disable) | Remove the autostart entry. | | [`cua-driver autostart status`](#cua-driver-autostart-status) | Print whether autostart is registered and running. | | [`cua-driver autostart kick`](#cua-driver-autostart-kick) | Start the autostart entry now without re-logging. | Every command also accepts the [global options](</docs/cua-driver/reference/cli#global-options>). ## `cua-driver serve` Run Cua Driver as a long-running daemon. The daemon owns per-process state such as accessibility snapshots, recording state, and cursor overlay state. ```text output cua-driver serve [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | string | | Override the daemon socket or named-pipe path. | | `--pid-file` | path | | Override the pid-file path on Unix targets. | | `--permission-mode` | string | `standard` | Immutable agent authorization mode: standard, bounded, or unrestricted. | | `--grant` | string | | Pre-authorize a residual standard-mode boundary. Repeatable; supported value: existing-profile. | | `--capability-manifest` | path | | Optional narrow-only tool/resource ceiling; required in bounded mode. | | `--session-policy` | path | | Deprecated alias for capability-manifest. | | `--host-bundle-id` | string | | Advisory host bundle id label echoed in check_permissions output (embedded mode). | | `--cursor-theme` | string | `cua.default` | Installed cursor theme id for the agent cursor overlay. | | `--cursor-reduced-motion` | string | `auto` | Agent cursor motion: auto follows the OS setting, on forces still frames, off allows animation. | | `--glide-ms` | number | | Override the agent cursor glide duration, in milliseconds. | | `--dwell-ms` | number | | Override how long the agent cursor dwells after a click, in milliseconds. | | `--idle-hide-ms` | number | | Override how long an idle agent cursor stays visible, in milliseconds. | | `--experimental-pip-geometry` | string | | Size and optional top-left origin of the experimental preview window: WxH or WxH+X+Y (default 480x360, top-right of the main display). | | `--claude-code-computer-use-compat` | boolean | `false` | Select the Claude Code computer-use compatibility surface (forwarded by `mcp` when it launches the daemon). | | `--experimental-pip` | boolean | `false` | Show a small always-on-top window with the latest post-action screenshot and a one-line label (macOS only). | | `--dangerously-bypass-approvals` | boolean | `false` | Select unrestricted mode and acknowledge its risk. | | `--approve-capability-manifest` | boolean | `false` | Trusted-launcher confirmation that the exact capability manifest was reviewed. | | `--approve-session-policy` | boolean | `false` | Deprecated alias for approve-capability-manifest. | | `--no-permissions-gate` | boolean | `false` | Skip the macOS first-launch permissions gate. | | `--embedded` | boolean | `false` | Run embedded inside a host app: inherit the host's TCC grants, never prompt or relaunch. Also CUA_DRIVER_EMBEDDED=1. | | `--no-overlay` | boolean | `false` | Disable the agent cursor overlay for this daemon. | | `--experimental-history` | boolean | `false` | Admit the encrypted local Computer History early preview for this daemon launch. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-serve" # Run the daemon in the foreground cua-driver serve # Start a bounded daemon with a reviewed capability manifest cua-driver serve --permission-mode bounded --capability-manifest manifest.yaml --approve-capability-manifest # Run without the agent cursor overlay cua-driver serve --no-overlay ``` ## `cua-driver stop` Ask the running daemon to exit gracefully. ```text output cua-driver stop [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | string | | Override the daemon socket or named-pipe path. | | `--expected-pid` | number | | Stop only if the daemon's process id is this one (a positive integer). | **Examples** ```bash test="cli-shape" id="ref-cua-driver-stop" # Ask the running daemon to exit cua-driver stop ``` ## `cua-driver status` Report whether a Cua Driver daemon is running. ```text output cua-driver status [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | string | | Override the daemon socket or named-pipe path. | | `--pid-file` | path | | Override the pid-file path on Unix targets. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-status" # Check whether the daemon is running cua-driver status ``` ## `cua-driver sessions` List content-free lifecycle summaries for sessions owned by the daemon runtime. ```text output cua-driver sessions [OPTIONS] [COMMAND] ``` Without a subcommand, runs `cua-driver sessions list`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | string | | Override the daemon socket or named-pipe path. | | `--json` | boolean | `false` | Emit the machine-readable session summary. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-sessions" # List live sessions as JSON cua-driver sessions --json ``` ### `cua-driver sessions list` List live sessions. The default subcommand. ```text output cua-driver sessions list ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-sessions-list" # List live sessions cua-driver sessions list ``` ## `cua-driver revoke` Revoke one or all live authorization/session scopes. Revocation is deny-only and never accepts an approval token. ```text output cua-driver revoke [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--session` | string | | Exact session id to stop and revoke. | | `--socket` | string | | Override the daemon socket or named-pipe path. | | `--all` | boolean | `false` | Stop and revoke every live session. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-revoke" # Stop and revoke one session cua-driver revoke --session 3f9c2a # Stop and revoke every live session cua-driver revoke --all ``` ## `cua-driver autostart` Manage platform-native daemon autostart. Windows registers a logon Scheduled Task. macOS and Linux currently print manual-recipe guidance. ```text output cua-driver autostart <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-autostart" # Check the autostart entry cua-driver autostart status ``` ### `cua-driver autostart enable` Register the autostart entry. ```text output cua-driver autostart enable ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-autostart-enable" # Start the daemon at every logon cua-driver autostart enable ``` ### `cua-driver autostart disable` Remove the autostart entry. ```text output cua-driver autostart disable ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-autostart-disable" # Remove the autostart entry cua-driver autostart disable ``` ### `cua-driver autostart status` Print whether autostart is registered and running. `not-registered` is emitted only when Task Scheduler explicitly reports that the named task does not exist. If the task cannot be inspected, the command exits non-zero and reports `permission-denied` or `unknown` together with the original diagnostic. ```text output cua-driver autostart status ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-autostart-status" # Check the autostart entry cua-driver autostart status ``` ### `cua-driver autostart kick` Start the autostart entry now without re-logging. ```text output cua-driver autostart kick ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-autostart-kick" # Start the entry now cua-driver autostart kick ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # Permissions and diagnostics macOS permission grants, diagnostic probes and install reports. | Command | Description | | --- | --- | | [`cua-driver permissions`](#cua-driver-permissions) | Inspect or raise macOS TCC permission grants (macOS only). | | [`cua-driver permissions status`](#cua-driver-permissions-status) | Report Accessibility and Screen Recording status through a running daemon (read-only, no prompt). | | [`cua-driver permissions grant`](#cua-driver-permissions-grant) | Launch CuaDriver through LaunchServices so the permission prompts are attributed to the app. | | [`cua-driver doctor`](#cua-driver-doctor) | Run platform-aware diagnostic probes. | | [`cua-driver diagnose`](#cua-driver-diagnose) | Print a pasteable install-layout and permission-attribution report. | `cua-driver permissions` exists on macOS only; Windows and Linux have no TCC equivalent. - `permissions status` is read-only and never prompts. It does not run the ScreenCaptureKit probe (macOS Tahoe can show a separate dialog for it), so it reports `screen_recording_capturable: null` and `direct_capture_status: "not_checked"`, plus the last successful `permissions grant` verification when one is recorded for the running driver identity. - `permissions grant` launches the installed `CuaDriver.app` through LaunchServices so the prompts attribute to the app, then requires a successful live capture probe. It never sends a prompt-capable request over the daemon socket. - Embedded hosts, in-process `CuaDriver.create()` runtimes and `cua-driver mcp --direct` own their own prompts: there, `check_permissions` stays read-only even with `{"prompt": true}`. - After a grant changes, quit and relaunch the responsible app (`CuaDriver.app`, or the host of a direct runtime). See [Permissions](</docs/cua-driver/guides/permissions>) for the grant flow and recovery. Every command also accepts the [global options](</docs/cua-driver/reference/cli#global-options>). ## `cua-driver permissions` Inspect or raise macOS TCC permission grants (macOS only). macOS only: Accessibility and Screen Recording are macOS TCC grants. See the macOS permissions reference. ```text output cua-driver permissions [COMMAND] ``` Without a subcommand, runs `cua-driver permissions status`. **Examples** ```bash test="cli-shape" id="ref-cua-driver-permissions" # Report Accessibility and Screen Recording status cua-driver permissions ``` ### `cua-driver permissions status` Report Accessibility and Screen Recording status through a running daemon (read-only, no prompt). The default subcommand. ```text output cua-driver permissions status [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-permissions-status" # Report the grants as JSON cua-driver permissions status --json ``` ### `cua-driver permissions grant` Launch CuaDriver through LaunchServices so the permission prompts are attributed to the app. ```text output cua-driver permissions grant ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-permissions-grant" # Request the grants for CuaDriver.app cua-driver permissions grant ``` ## `cua-driver doctor` Run platform-aware diagnostic probes. Exit code is non-zero when any probe is an error. ```text output cua-driver doctor [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit the probe report as JSON. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-doctor" # Run the diagnostic probes cua-driver doctor # Emit the probe report as JSON cua-driver doctor --json ``` ## `cua-driver diagnose` Print a pasteable install-layout and permission-attribution report. ```text output cua-driver diagnose ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-diagnose" # Print an install and permission report to paste into an issue cua-driver diagnose ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # Trajectory recording Record, inspect and render action trajectories. | Command | Description | | --- | --- | | [`cua-driver recording start`](#cua-driver-recording-start) | Start trajectory recording to a directory. | | [`cua-driver recording stop`](#cua-driver-recording-stop) | Stop trajectory recording. | | [`cua-driver recording status`](#cua-driver-recording-status) | Print the current recording state. | | [`cua-driver recording render`](#cua-driver-recording-render) | Render a recorded trajectory directory to an MP4. | Every command also accepts the [global options](</docs/cua-driver/reference/cli#global-options>). Control trajectory recording on a running daemon. Recording state lives in the required daemon and survives client reconnects. ```text output cua-driver recording [OPTIONS] [COMMAND] ``` Without a subcommand, runs `cua-driver recording status`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | string | | Override the daemon socket or named-pipe path. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-recording" # Print the current recording state cua-driver recording ``` ## `cua-driver recording start` Start trajectory recording to a directory. ```text output cua-driver recording start <output-dir> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<output-dir>` | path | required | Directory to write turn folders into. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-recording-start" # Record turns into a directory cua-driver recording start ~/cua-trajectories/demo1 ``` ## `cua-driver recording stop` Stop trajectory recording. ```text output cua-driver recording stop ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-recording-stop" # Stop recording cua-driver recording stop ``` ## `cua-driver recording status` Print the current recording state. ```text output cua-driver recording status ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-recording-status" # Print the recording state cua-driver recording status ``` ## `cua-driver recording render` Render a recorded trajectory directory to an MP4. This pure file-to-file path does not require a running daemon. ```text output cua-driver recording render [OPTIONS] <input-dir> <out-mp4> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<input-dir>` | path | required | Trajectory directory containing recorded turn folders. | | `<out-mp4>` | path | required | Output MP4 path. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--scale` | number | | Scale factor for rendered frames. | | `--no-zoom` | boolean | `false` | Disable cursor/action zoom effects in the rendered video. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-recording-render" # Render a recorded trajectory to MP4 cua-driver recording render ~/cua-trajectories/demo1 demo1.mp4 ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # Computer History The encrypted, metadata-only Computer History preview. | Command | Description | | --- | --- | | [`cua-driver history enable`](#cua-driver-history-enable) | Turn on the Computer History preview (admits it on a local daemon if needed). | | [`cua-driver history disable`](#cua-driver-history-disable) | Turn Computer History off; recorded chunks stay until deleted. | | [`cua-driver history pause`](#cua-driver-history-pause) | Pause recording without turning the preview off. | | [`cua-driver history resume`](#cua-driver-history-resume) | Resume recording after pause. | | [`cua-driver history status`](#cua-driver-history-status) | Print the Computer History state. | | [`cua-driver history flush`](#cua-driver-history-flush) | Seal the current chunk to disk now. | | [`cua-driver history list`](#cua-driver-history-list) | List recent metadata-only events. | | [`cua-driver history show`](#cua-driver-history-show) | Print one event by its sequence number. | | [`cua-driver history delete`](#cua-driver-history-delete) | Delete the encrypted chunks and their native credential-store key. | Every command also accepts the [global options](</docs/cua-driver/reference/cli#global-options>). Encrypted, metadata-only Computer History early preview: lifecycle and local inspection. The daemon must be admitted for the preview (`cua-driver serve --experimental-history`); `enable` restarts a local daemon with it when needed. ```text output cua-driver history [OPTIONS] [COMMAND] ``` Without a subcommand, runs `cua-driver history status`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | string | | Override the daemon socket or named-pipe path. | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-history" # Print the Computer History state cua-driver history ``` ## `cua-driver history enable` Turn on the Computer History preview (admits it on a local daemon if needed). ```text output cua-driver history enable ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-enable" # Turn on the Computer History preview cua-driver history enable ``` ## `cua-driver history disable` Turn Computer History off; recorded chunks stay until deleted. ```text output cua-driver history disable ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-disable" # Turn Computer History off cua-driver history disable ``` ## `cua-driver history pause` Pause recording without turning the preview off. ```text output cua-driver history pause ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-pause" # Pause recording cua-driver history pause ``` ## `cua-driver history resume` Resume recording after pause. ```text output cua-driver history resume ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-resume" # Resume recording cua-driver history resume ``` ## `cua-driver history status` Print the Computer History state. The default subcommand. ```text output cua-driver history status [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-status" # Print the state as JSON cua-driver history status --json ``` ## `cua-driver history flush` Seal the current chunk to disk now. ```text output cua-driver history flush ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-flush" # Seal the current chunk now cua-driver history flush ``` ## `cua-driver history list` List recent metadata-only events. ```text output cua-driver history list [OPTIONS] [<limit>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<limit>` | number | optional | Most events to print. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-list" # List the 20 most recent events cua-driver history list 20 ``` ## `cua-driver history show` Print one event by its sequence number. ```text output cua-driver history show [OPTIONS] <sequence> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<sequence>` | number | required | Event sequence number. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-show" # Print event 42 as JSON cua-driver history show 42 --json ``` ## `cua-driver history delete` Delete the encrypted chunks and their native credential-store key. Needs --yes. ```text output cua-driver history delete [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--yes` | boolean | `false` | Confirm irreversible deletion of encrypted chunks and their native credential-store key. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-history-delete" # Delete every recorded chunk and its key cua-driver history delete --yes ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # Configuration Driver configuration, telemetry, cursor themes and cursor motion. | Command | Description | | --- | --- | | [`cua-driver config`](#cua-driver-config) | Read or mutate persistent driver configuration. | | [`cua-driver config show`](#cua-driver-config-show) | Print the full config. | | [`cua-driver config get`](#cua-driver-config-get) | Print one config key. | | [`cua-driver config set`](#cua-driver-config-set) | Set one config key. | | [`cua-driver config reset`](#cua-driver-config-reset) | Reset config to defaults. | | [`cua-driver telemetry`](#cua-driver-telemetry) | Inspect or change content-free product telemetry. | | [`cua-driver telemetry enable`](#cua-driver-telemetry-enable) | Persistently enable telemetry. | | [`cua-driver telemetry disable`](#cua-driver-telemetry-disable) | Persistently disable every telemetry request. | | [`cua-driver telemetry status`](#cua-driver-telemetry-status) | Show the effective setting and redacted identity state. | | [`cua-driver telemetry reset-id`](#cua-driver-telemetry-reset-id) | Erase the installation ID and event markers. | | [`cua-driver telemetry inspect`](#cua-driver-telemetry-inspect) | Build a fixed event payload without sending it. | | [`cua-driver cursor-theme`](#cua-driver-cursor-theme) | Validate, compile, inspect, preview, install, or remove a local cursor theme. | | [`cua-driver cursor-theme validate`](#cua-driver-cursor-theme-validate) | Validate a bounded dotLottie source archive. | | [`cua-driver cursor-theme build`](#cua-driver-cursor-theme-build) | Compile a validated dotLottie archive into a bounded .cua-theme artifact. | | [`cua-driver cursor-theme inspect`](#cua-driver-cursor-theme-inspect) | Inspect metadata in a compiled .cua-theme artifact. | | [`cua-driver cursor-theme preview`](#cua-driver-cursor-theme-preview) | Render a compiled theme's representative still frames to a directory. | | [`cua-driver cursor-theme install`](#cua-driver-cursor-theme-install) | Install a compiled theme into the current user's theme store. | | [`cua-driver cursor-theme list`](#cua-driver-cursor-theme-list) | List the built-in and installed cursor themes. | | [`cua-driver cursor-theme uninstall`](#cua-driver-cursor-theme-uninstall) | Remove a custom theme from the current user's theme store. | | [`cua-driver cursor`](#cua-driver-cursor) | Configure a session's agent cursor from the command line. | | [`cua-driver cursor motion`](#cua-driver-cursor-motion) | Set the cursor's motion style, timing and effects. | Every command also accepts the [global options](</docs/cua-driver/reference/cli#global-options>). ## `cua-driver config` Read or mutate persistent driver configuration. Without a subcommand, prints the full config. ```text output cua-driver config [OPTIONS] [COMMAND] ``` Without a subcommand, runs `cua-driver config show`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | string | | Override the daemon socket or named-pipe path. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-config" # Print the full config cua-driver config ``` ### `cua-driver config show` Print the full config. ```text output cua-driver config show ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-config-show" # Print the full config cua-driver config show ``` ### `cua-driver config get` Print one config key. ```text output cua-driver config get <key> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<key>` | string | required | Config key to read. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-config-get" # Print one key cua-driver config get max_image_dimension ``` ### `cua-driver config set` Set one config key. Cursor motion defaults are saved keys that apply to sessions started afterwards: cursor.motion.style (signature_arc, spring_settle, magnetic, comet_swoop, adaptive, classic), cursor.motion.timing (native, fitts, fixed) and cursor.motion.effects.<trail|glow|magnet|ripple|squish> (true, false). Use null for a key to clear it, or cursor.motion null to clear all. A start_session cursor_motion or a set_agent_cursor_motion call overrides the saved default. ```text output cua-driver config set <key> <value> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<key>` | string | required | Config key to write. | | `<value>` | string | required | Value to store. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-config-set" # Downscale screenshots to at most 1568 px cua-driver config set max_image_dimension 1568 # Make magnetic the default cursor motion for new sessions cua-driver config set cursor.motion.style magnetic # Scale cursor move time with distance and target size cua-driver config set cursor.motion.timing fitts # Turn the cursor trail on by default cua-driver config set cursor.motion.effects.trail true ``` ### `cua-driver config reset` Reset config to defaults. ```text output cua-driver config reset ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-config-reset" # Restore the defaults cua-driver config reset ``` ## `cua-driver telemetry` Inspect or change content-free product telemetry. Telemetry is default-on. Disable retains the pseudonymous installation ID; reset-id erases the ID and event markers while preserving the preference. ```text output cua-driver telemetry <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-telemetry" # Show the effective telemetry setting cua-driver telemetry status ``` ### `cua-driver telemetry enable` Persistently enable telemetry. ```text output cua-driver telemetry enable ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-telemetry-enable" # Enable telemetry cua-driver telemetry enable ``` ### `cua-driver telemetry disable` Persistently disable every telemetry request. Retains the local installation ID. ```text output cua-driver telemetry disable ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-telemetry-disable" # Disable every telemetry request cua-driver telemetry disable ``` ### `cua-driver telemetry status` Show the effective setting and redacted identity state. ```text output cua-driver telemetry status [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit JSON. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-telemetry-status" # Show the setting as JSON cua-driver telemetry status --json ``` ### `cua-driver telemetry reset-id` Erase the installation ID and event markers. The persisted enabled/disabled preference is retained. ```text output cua-driver telemetry reset-id ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-telemetry-reset-id" # Erase the installation ID cua-driver telemetry reset-id ``` ### `cua-driver telemetry inspect` Build a fixed event payload without sending it. The distinct ID is replaced with a redacted placeholder. ```text output cua-driver telemetry inspect [OPTIONS] <event> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<event>` | string | required | Fixed telemetry event name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit JSON (the only output format; pass it for forward compatibility). | **Examples** ```bash test="cli-shape" id="ref-cua-driver-telemetry-inspect" # Show the payload of one event without sending it cua-driver telemetry inspect cua_driver_cli_completed --json ``` ## `cua-driver cursor-theme` Validate, compile, inspect, preview, install, or remove a local cursor theme. This is a trusted local authoring workflow. Agent-facing tools may select an installed theme id, but cannot install source or compiled theme data. ```text output cua-driver cursor-theme <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-theme" # List cursor themes cua-driver cursor-theme list ``` ### `cua-driver cursor-theme validate` Validate a bounded dotLottie source archive. ```text output cua-driver cursor-theme validate [OPTIONS] <source> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<source>` | path | required | Path to the source .lottie archive. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--development` | boolean | `false` | Allow the reserved com.example development namespace. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-theme-validate" # Validate a source archive cua-driver cursor-theme validate my-cursor.lottie ``` ### `cua-driver cursor-theme build` Compile a validated dotLottie archive into a bounded .cua-theme artifact. ```text output cua-driver cursor-theme build [OPTIONS] <source> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<source>` | path | required | Path to the source .lottie archive. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--output` | path | required | Output .cua-theme path. | | `--development` | boolean | `false` | Allow the reserved com.example development namespace. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-theme-build" # Compile a theme cua-driver cursor-theme build my-cursor.lottie --output my-cursor.cua-theme ``` ### `cua-driver cursor-theme inspect` Inspect metadata in a compiled .cua-theme artifact. ```text output cua-driver cursor-theme inspect [OPTIONS] <theme> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<theme>` | path | required | Path to the compiled .cua-theme artifact. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable JSON. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-theme-inspect" # Print a theme's metadata cua-driver cursor-theme inspect my-cursor.cua-theme --json ``` ### `cua-driver cursor-theme preview` Render a compiled theme's representative still frames to a directory. ```text output cua-driver cursor-theme preview [OPTIONS] <theme> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<theme>` | path | required | Path to the compiled .cua-theme artifact. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--output` | path | required | Preview output directory. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-theme-preview" # Render still frames cua-driver cursor-theme preview my-cursor.cua-theme --output preview ``` ### `cua-driver cursor-theme install` Install a compiled theme into the current user's theme store. ```text output cua-driver cursor-theme install <theme> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<theme>` | path | required | Path to the compiled .cua-theme artifact. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-theme-install" # Install a theme cua-driver cursor-theme install my-cursor.cua-theme ``` ### `cua-driver cursor-theme list` List the built-in and installed cursor themes. ```text output cua-driver cursor-theme list [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable JSON. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-theme-list" # List themes as JSON cua-driver cursor-theme list --json ``` ### `cua-driver cursor-theme uninstall` Remove a custom theme from the current user's theme store. The built-in cua.default theme cannot be removed. ```text output cua-driver cursor-theme uninstall <theme-id> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<theme-id>` | string | required | Installed custom theme id. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-theme-uninstall" # Remove an installed theme cua-driver cursor-theme uninstall com.example.my-cursor ``` ## `cua-driver cursor` Configure a session's agent cursor from the command line. Forwards to the set_agent_cursor_motion tool on the running daemon. Fields you leave out keep their current value. ```text output cua-driver cursor <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor" # Use the magnetic cursor motion cua-driver cursor motion --session demo --style magnetic ``` ### `cua-driver cursor motion` Set the cursor's motion style, timing and effects. Effects take on, off, or default (the style's own choice). ```text output cua-driver cursor motion [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--session` | string | required | Public label of the session that owns the cursor. | | `--style` | string | | signature_arc, spring_settle, magnetic, comet_swoop, adaptive, or classic. | | `--timing` | string | | native, fitts, or fixed. | | `--effects` | string | | Comma-separated name=on\|off\|default for trail, glow, magnet, ripple, squish. | | `--glide-ms` | number | | Move duration in milliseconds for fixed timing (1430 when 0). | | `--socket` | path | | Override the daemon socket path. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-cursor-motion" # Use the comet swoop cursor motion cua-driver cursor motion --session demo --style comet_swoop # Make every move take 900 ms cua-driver cursor motion --session demo --timing fixed --glide-ms 900 # Turn off the trail and reset the click ripple cua-driver cursor motion --session demo --effects trail=off,ripple=default ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # Skills and extensions The agent skill pack, signed extensions and the perception parser. | Command | Description | | --- | --- | | [`cua-driver skills`](#cua-driver-skills) | Install, update, inspect, or remove the optional agent skill pack. | | [`cua-driver skills install`](#cua-driver-skills-install) | Fetch the versioned skill pack and link detected agents. | | [`cua-driver skills update`](#cua-driver-skills-update) | Refresh the local skill pack and links. | | [`cua-driver skills uninstall`](#cua-driver-skills-uninstall) | Remove agent skill links. | | [`cua-driver skills status`](#cua-driver-skills-status) | Report local skill-pack and per-agent link state. | | [`cua-driver skills path`](#cua-driver-skills-path) | Print the local skill-pack path. | | [`cua-driver extension`](#cua-driver-extension) | Inspect and manage signed, target-specific optional extensions. | | [`cua-driver extension list`](#cua-driver-extension-list) | List registry-known extensions and local state. | | [`cua-driver extension info`](#cua-driver-extension-info) | Compatibility alias for installed extension status. | | [`cua-driver extension inspect`](#cua-driver-extension-inspect) | Verify and preview license, source, provenance, and hashes without mutation. | | [`cua-driver extension status`](#cua-driver-extension-status) | Verify installed integrity and optionally run the self-test hook. | | [`cua-driver extension install`](#cua-driver-extension-install) | Verify, stage, and durably activate an extension. | | [`cua-driver extension update`](#cua-driver-extension-update) | Verify and atomically activate a newer extension version. | | [`cua-driver extension remove`](#cua-driver-extension-remove) | Remove only a fully validated, manager-owned extension tree. | | [`cua-driver extension path`](#cua-driver-extension-path) | Compatibility command that prints the exact active version directory. | | [`cua-driver perception`](#cua-driver-perception) | Parse a local PNG with the installed optional perception extension. | | [`cua-driver perception parse`](#cua-driver-perception-parse) | Parse one local PNG into canonical visual regions. | Every command also accepts the [global options](</docs/cua-driver/reference/cli#global-options>). ## `cua-driver skills` Install, update, inspect, or remove the optional agent skill pack. The install script never touches agent skill directories automatically. ```text output cua-driver skills [COMMAND] ``` Without a subcommand, runs `cua-driver skills status`. **Examples** ```bash test="cli-shape" id="ref-cua-driver-skills" # Report skill-pack state cua-driver skills ``` ### `cua-driver skills install` Fetch the versioned skill pack and link detected agents. ```text output cua-driver skills install [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--from` | string | | Fetch from the main branch instead of the tagged release. Only `main` is accepted; write it as `--from=main`. | | `--all-platforms` | boolean | `false` | Keep platform-specific skill files for every platform. | | `--force` | boolean | `false` | Re-fetch the skill pack even when the local copy is current. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-skills-install" # Install the skill pack and link detected agents cua-driver skills install # Install the latest skill pack from main cua-driver skills install --from=main ``` ### `cua-driver skills update` Refresh the local skill pack and links. Always re-fetches; takes the same options as install. ```text output cua-driver skills update [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--from` | string | | Fetch from the main branch instead of the tagged release. Only `main` is accepted; write it as `--from=main`. | | `--all-platforms` | boolean | `false` | Keep platform-specific skill files for every platform. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-skills-update" # Refresh the skill pack and links cua-driver skills update ``` ### `cua-driver skills uninstall` Remove agent skill links. ```text output cua-driver skills uninstall [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--all` | boolean | `false` | Also delete the local skill-pack copy. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-skills-uninstall" # Remove the links and the local copy cua-driver skills uninstall --all ``` ### `cua-driver skills status` Report local skill-pack and per-agent link state. ```text output cua-driver skills status ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-skills-status" # Report skill-pack and link state cua-driver skills status ``` ### `cua-driver skills path` Print the local skill-pack path. ```text output cua-driver skills path ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-skills-path" # Print the skill-pack path cua-driver skills path ``` ## `cua-driver extension` Inspect and manage signed, target-specific optional extensions. Verified installs bind a pinned publisher identity and Ed25519 key validity/rotation state to exact catalog, archive, manifest, file, original-model, and converted-model hashes plus component licenses/notices and corresponding-source revisions. Inspect previews license, source, and provenance before mutation. Explicit developer-only unsigned installs remain visibly unverified. Install, update, and remove use platform-specific ownership and link/reparse-point checks on macOS, Linux, and Windows. ```text output cua-driver extension [COMMAND] ``` Without a subcommand, runs `cua-driver extension list`. **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension" # List known extensions cua-driver extension list ``` ### `cua-driver extension list` List registry-known extensions and local state. ```text output cua-driver extension list [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension-list" # List extensions as JSON cua-driver extension list --json ``` ### `cua-driver extension info` Compatibility alias for installed extension status. ```text output cua-driver extension info [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Registry extension name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension-info" # Show an installed extension cua-driver extension info cua-perception ``` ### `cua-driver extension inspect` Verify and preview license, source, provenance, and hashes without mutation. ```text output cua-driver extension inspect [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Registry extension name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--catalog` | path | | Signed local catalog. | | `--archive` | path | | Developer-only unsigned local archive. | | `--allow-unsigned-local` | boolean | `false` | Explicitly select developer-only unverified mode. | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension-inspect" # Preview license, source and provenance cua-driver extension inspect cua-perception --catalog catalog.json ``` ### `cua-driver extension status` Verify installed integrity and optionally run the self-test hook. ```text output cua-driver extension status [OPTIONS] [<name>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | optional | Optional registry extension name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--self-test` | boolean | `false` | Run the extension self-test hook. | | `--json` | boolean | `false` | Emit machine-readable output. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension-status" # Verify an extension and run its self-test cua-driver extension status cua-perception --self-test ``` ### `cua-driver extension install` Verify, stage, and durably activate an extension. Supported on macOS, Linux, and Windows with platform ownership and link/reparse-point checks. ```text output cua-driver extension install [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Registry extension name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--catalog` | path | | Signed local catalog. | | `--archive` | path | | Developer-only unsigned local archive. | | `--allow-unsigned-local` | boolean | `false` | Explicitly select developer-only unverified mode. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension-install" # Install from a signed catalog cua-driver extension install cua-perception --catalog catalog.json ``` ### `cua-driver extension update` Verify and atomically activate a newer extension version. Supported on macOS, Linux, and Windows with platform ownership and link/reparse-point checks. ```text output cua-driver extension update [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Registry extension name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--catalog` | path | | Signed local catalog. | | `--archive` | path | | Developer-only unsigned local archive. | | `--allow-unsigned-local` | boolean | `false` | Explicitly select developer-only unverified mode. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension-update" # Activate a newer version cua-driver extension update cua-perception --catalog catalog.json ``` ### `cua-driver extension remove` Remove only a fully validated, manager-owned extension tree. Supported on macOS, Linux, and Windows with platform ownership and link/reparse-point checks. ```text output cua-driver extension remove <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Registry extension name. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension-remove" # Remove an extension cua-driver extension remove cua-perception ``` ### `cua-driver extension path` Compatibility command that prints the exact active version directory. ```text output cua-driver extension path <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Registry extension name. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-extension-path" # Print the active version directory cua-driver extension path cua-perception ``` ## `cua-driver perception` Parse a local PNG with the installed optional perception extension. This read-only local-input mode does not create a Driver capture. Its visual-regions JSON marks the result ineligible for Driver action authority. ```text output cua-driver perception <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-driver-perception" # Parse a PNG into visual regions cua-driver perception parse --image screen.png --capture capture.json --json ``` ### `cua-driver perception parse` Parse one local PNG into canonical visual regions. Requires --json. The capture file supplies source metadata only. ```text output cua-driver perception parse [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--image` | path | required | Local PNG path. | | `--capture` | path | required | Local capture source metadata JSON path. | | `--json` | boolean | `false` | Emit canonical visual-regions JSON with local-input provenance. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-perception-parse" # Parse a PNG into visual regions cua-driver perception parse --image screen.png --capture capture.json --json ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # Updates Check for and apply updates, and choose the release channel. | Command | Description | | --- | --- | | [`cua-driver check-update`](#cua-driver-check-update) | Check whether a newer cua-driver release is available. | | [`cua-driver update`](#cua-driver-update) | Check for an update and optionally apply it. | | [`cua-driver channel`](#cua-driver-channel) | Inspect or change the stable/nightly update channel. | | [`cua-driver channel status`](#cua-driver-channel-status) | Show selected and current release channels. | | [`cua-driver channel set`](#cua-driver-channel-set) | Save stable or nightly as the update channel. | Every command also accepts the [global options](</docs/cua-driver/reference/cli#global-options>). ## `cua-driver check-update` Check whether a newer cua-driver release is available. Read-only. Uses the same update-state payload as the check_for_update MCP tool. ```text output cua-driver check-update [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit a machine-readable JSON payload. | | `--no-cache` | boolean | `false` | Skip the 1-hour on-disk cache and look up the latest release again. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-check-update" # Check for a newer release cua-driver check-update # Check now, as JSON cua-driver check-update --json --no-cache ``` ## `cua-driver update` Check for an update and optionally apply it. The apply path delegates to the canonical platform installer scripts. ```text output cua-driver update [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--apply` | boolean | `false` | Download and install the latest release when one is available. | | `--json` | boolean | `false` | Emit the structured update-state payload. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-update" # Check and print the install command cua-driver update # Install the latest release cua-driver update --apply ``` ## `cua-driver channel` Inspect or change the stable/nightly update channel. Selection is persistent but never installs by itself; use cua-driver update --apply after changing it. ```text output cua-driver channel [COMMAND] ``` Without a subcommand, runs `cua-driver channel status`. **Examples** ```bash test="cli-shape" id="ref-cua-driver-channel" # Show the selected release channel cua-driver channel ``` ### `cua-driver channel status` Show selected and current release channels. ```text output cua-driver channel status [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable channel state. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-channel-status" # Show the channels as JSON cua-driver channel status --json ``` ### `cua-driver channel set` Save stable or nightly as the update channel. ```text output cua-driver channel set [OPTIONS] <channel> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<channel>` | string | required | Release channel: stable or nightly. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable channel state. | **Examples** ```bash test="cli-shape" id="ref-cua-driver-channel-set" # Follow nightly builds (then run update --apply) cua-driver channel set nightly ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure: the daemon is not running or is incompatible, a tool call returned an error, or the operation failed. `call` and direct tool invocations exit with the daemon's result code (1 on a tool error); `update --apply` passes through the installer's status. | | `2` | Invalid input: tool arguments that are not valid JSON, an unknown `mcp-config` client, or an `update --apply` refused on a local development install. | | `64` | Usage error: an unknown or missing subcommand or argument, or conflicting flags (for example `revoke` without exactly one of `--session` or `--all`, or an authorization flag passed to `mcp`). | --- # All tools Every tool cua-driver serves over MCP on macOS, Linux and Windows, with its parameters. `cua-driver mcp` serves these tools over stdio. Every tool also runs from the shell as `cua-driver <tool> '<json-args>'` (or `cua-driver call <tool>` through the daemon), with the same code path. Where a tool's text or schema differs by platform, its entry has one tab per platform; the choice persists across pages. On Windows and Linux, bare `cua-driver mcp` owns its runtime and stops it on stdin EOF. On macOS it proxies to the installed `CuaDriver.app` daemon so Accessibility and Screen Recording grants keep the app identity; `--socket` selects an explicit daemon endpoint on every platform. See [the process model](</docs/cua-driver/concepts/how-cua-driver-works#processes-and-sessions>) for the lifecycle. ## Common parameters These parameters have the same shape on macOS, Windows and Linux (composed from shared schema fragments and held together by a CI consistency gate). | Parameter | Tools | Meaning | | --- | --- | --- | | `session` | every action and cursor tool | Optional public run label. Repeat it on every call of multi-call work; it is not sticky. Without it, the call uses the connection's private implicit session. A label is never caller identity or authorization. | | `target` | `move_cursor`, `click`, `drag`, `scroll`, `type_text`, `press_key`, `hotkey` | The preferred per-call target: `{kind:"window", pid, window_id}` or `{kind:"desktop", display_id:"primary"}`. Cannot be combined with the legacy `scope`, `pid` or `window_id`. | | `delivery_mode` | `click`, `double_click`, `right_click`, `drag`, `scroll`, `type_text`, `press_key`, `hotkey` | `"background"` (the default) acts without fronting or raising the target. `"foreground"` briefly fronts it, acts, and restores the previous front window: use it only after a background attempt reports it did not land. Omitted or unknown values fall back to `"background"`. | | `include_screenshot` | `get_window_state` | Default `true` (tree and screenshot). `false` returns the tree only, for re-indexing before an element action. | | `capture_mode` | `get_window_state` | Deprecated and ignored; still accepted so older callers do not fail. | | `modifier`, `button`, `element_token` | pointer and element tools | Held modifier keys, the mouse button, and the element handle from the latest `get_window_state` row. Action tools refuse `element_index` and `snapshot_id`. | The `required` sets are the same on every platform: `click` requires nothing, `scroll` requires `direction`, and `zoom` requires `window_id` plus `x1`, `y1`, `x2`, `y2`. Legacy flat calls still check `pid`: a window action needs it, a desktop action omits it. The first stateful call on a connection creates its implicit lifecycle session; later unnamed calls on that connection share it. The session's idle TTL is five minutes. Closing the connection, `end_session` or the idle timeout run the same cleanup. Each `get_window_state` read replaces the previous snapshot of that window and lists the replaced ids in `invalidated_snapshot_ids`; a token from a replaced snapshot is refused with `stale_element_token`, which names the current snapshots. Window-relative `x, y` and `zoom` need a screenshot read of that window in the same session first (`screenshot_context_missing` otherwise), so pass one `session` label to the read and the actions of a one-shot `cua-driver call` sequence. A window action may omit `window_id` only when its `pid` owns exactly one eligible top-level window. With several, the driver sends no input and returns `code: "ambiguous_window_target"`, `effect: "refused"` and the candidates; retry with an exact `window_id` from that result or from `list_windows`. A `pid` with no eligible window returns `window_target_not_found`. ## Action results `click`, `double_click`, `right_click`, `drag`, `scroll`, `type_text`, `press_key`, `hotkey` and `set_value` return these structured fields: | Field | Type | Meaning | | --- | --- | --- | | `path` | string | The delivery rung that ran: `ax`, `cgevent`, `cgevent_fg`, `key_events`, `key_events_fg`, `pixel`, `x11_atspi`, `x11_pixel`, `x11_pixel_fg` or `msaa`. | | `verified` | boolean or absent | `true`: the effect was read back through accessibility. `false`: the action ran but is unconfirmed. Absent: the tool does not verify. | | `effect` | string | `confirmed`, `unverifiable` or `suspected_noop`. | | `escalation` | object or absent | Present when the driver recommends the next rung: `{recommended: "px" \| "foreground" \| "page", reason}`. | `get_window_state` may return `degraded: true` with a `degraded_reason` when the accessibility walk found no actionable elements (bridge not up, not on the session bus, or a non-accessible surface). Treat `elements: []` as incomplete then, and act by pixel on the screenshot in the same response. ## App and window tools List, launch, quit, front and arrange apps and windows. [Reference](</docs/cua-driver/reference/mcp-tools/apps-and-windows>). | Tool | Description | Platforms | | --- | --- | --- | | [`list_apps`](</docs/cua-driver/reference/mcp-tools/apps-and-windows#list_apps>) | List macOS apps, both currently running and installed-but-not-running, with per-app state flags: - running: is a process for this app live? | macOS, Linux, Windows | | [`list_windows`](</docs/cua-driver/reference/mcp-tools/apps-and-windows#list_windows>) | List layer-0 top-level windows known to WindowServer, including off-screen ones (minimized, other Space, hidden). | macOS, Linux, Windows | | [`launch_app`](</docs/cua-driver/reference/mcp-tools/apps-and-windows#launch_app>) | Launch a macOS app in the background: the target does NOT come to the foreground. | macOS, Linux, Windows | | [`kill_app`](</docs/cua-driver/reference/mcp-tools/apps-and-windows#kill_app>) | Force-terminate a process by pid (kill -9 equivalent on macOS / Linux; taskkill /F equivalent on Windows). | macOS, Linux, Windows | | [`bring_to_front`](</docs/cua-driver/reference/mcp-tools/apps-and-windows#bring_to_front>) | Persistently activate an app and leave it in the foreground. | macOS, Linux, Windows | | [`set_window_frame`](</docs/cua-driver/reference/mcp-tools/apps-and-windows#set_window_frame>) | Set one exact top-level window's frame in the desktop-coordinate space reported by list_windows and verify the resulting geometry through an independent readback. | macOS, Linux, Windows | | [`invoke_menu`](</docs/cua-driver/reference/mcp-tools/apps-and-windows#invoke_menu>) | Resolve an exact application-menu path one live native level at a time and invoke its final item through accessibility APIs. | macOS, Linux, Windows | | [`debug_window_info`](</docs/cua-driver/reference/mcp-tools/apps-and-windows#debug_window_info>) | Diagnostic: dump everything cua-driver sees about a pid's top-level windows from the daemon's session perspective. | Windows | ## Window state tools Screenshots, accessibility trees, verification and visual regions of a window. [Reference](</docs/cua-driver/reference/mcp-tools/window-state>). | Tool | Description | Platforms | | --- | --- | --- | | [`get_window_state`](</docs/cua-driver/reference/mcp-tools/window-state#get_window_state>) | Snapshot a window: the accessibility tree plus a screenshot. | macOS, Linux, Windows | | [`get_accessibility_tree`](</docs/cua-driver/reference/mcp-tools/window-state#get_accessibility_tree>) | Return a lightweight snapshot of the desktop: running regular apps and on-screen visible windows with their bounds, z-order, and owner pid. | macOS, Linux, Windows | | [`verify_state`](</docs/cua-driver/reference/mcp-tools/window-state#verify_state>) | Deterministically verify bounded predicates against one exact window. | macOS, Linux, Windows | | [`parse_visual_regions`](</docs/cua-driver/reference/mcp-tools/window-state#parse_visual_regions>) | Parse one immutable registered capture into model-neutral text and icon regions. | macOS, Linux, Windows | ## Screen tools The desktop, screen size, cursor position and zoomed crops. [Reference](</docs/cua-driver/reference/mcp-tools/screen>). | Tool | Description | Platforms | | --- | --- | --- | | [`get_desktop_state`](</docs/cua-driver/reference/mcp-tools/screen#get_desktop_state>) | Capture the full display in true screen pixels, full size unless `max_image_dimension` caps it. | macOS, Linux, Windows | | [`get_screen_size`](</docs/cua-driver/reference/mcp-tools/screen#get_screen_size>) | Return the logical size of the main display in points plus its backing scale factor. | macOS, Linux, Windows | | [`get_cursor_position`](</docs/cua-driver/reference/mcp-tools/screen#get_cursor_position>) | Return the current mouse cursor position in screen points (origin top-left). | macOS, Linux, Windows | | [`zoom`](</docs/cua-driver/reference/mcp-tools/screen#zoom>) | Capture a cropped JPEG of a window region (x1,y1)–(x2,y2) in screenshot pixel coordinates, with 20% padding added on each side. | macOS, Linux, Windows | ## Click tool Click by element or pixel. [Reference](</docs/cua-driver/reference/mcp-tools/click>). | Tool | Description | Platforms | | --- | --- | --- | | [`click`](</docs/cua-driver/reference/mcp-tools/click#click>) | Click a target pid. | macOS, Linux, Windows | ## Double-click and right-click tools Double-click and right-click by element or pixel. [Reference](</docs/cua-driver/reference/mcp-tools/double-and-right-click>). | Tool | Description | Platforms | | --- | --- | --- | | [`double_click`](</docs/cua-driver/reference/mcp-tools/double-and-right-click#double_click>) | Double-click at (x, y) or on an AX element identified by element_token. | macOS, Linux, Windows | | [`right_click`](</docs/cua-driver/reference/mcp-tools/double-and-right-click#right_click>) | Right-click against a target pid. | macOS, Linux, Windows | ## Pointer tools Drag, scroll, move the pointer, and press or release buttons. [Reference](</docs/cua-driver/reference/mcp-tools/pointer>). | Tool | Description | Platforms | | --- | --- | --- | | [`drag`](</docs/cua-driver/reference/mcp-tools/pointer#drag>) | Press-drag-release gesture from (from_x, from_y) to (to_x, to_y) in window-local screenshot pixels: the same space get_window_state returns. | macOS, Linux, Windows | | [`scroll`](</docs/cua-driver/reference/mcp-tools/pointer#scroll>) | Scroll the target pid. | macOS, Linux, Windows | | [`move_cursor`](</docs/cua-driver/reference/mcp-tools/pointer#move_cursor>) | Move a cursor to (x, y). | macOS, Linux, Windows | | [`mouse_button_down`](</docs/cua-driver/reference/mcp-tools/pointer#mouse_button_down>) | Press and hold a mouse button at (x,y) via background X11 delivery. | Linux | | [`mouse_button_up`](</docs/cua-driver/reference/mcp-tools/pointer#mouse_button_up>) | Release a previously-held mouse button via background X11 delivery. | Linux | | [`mouse_drag`](</docs/cua-driver/reference/mcp-tools/pointer#mouse_drag>) | Move a previously-held mouse button to a new point via background X11 delivery. | Linux | | [`parallel_mouse_drag`](</docs/cua-driver/reference/mcp-tools/pointer#parallel_mouse_drag>) | Run multiple mouse drag gestures concurrently via Linux MPX/XI2 virtual master pointers. | Linux | ## Typing tools Type text into a field or window. [Reference](</docs/cua-driver/reference/mcp-tools/keyboard>). | Tool | Description | Platforms | | --- | --- | --- | | [`type_text`](</docs/cua-driver/reference/mcp-tools/keyboard#type_text>) | Insert text via `AXSetAttribute(kAXSelectedText)` into the element given by `element_token`, or the pid's focused element. | macOS, Linux, Windows | ## Key and shortcut tools Press keys and keyboard shortcuts. [Reference](</docs/cua-driver/reference/mcp-tools/keys-and-shortcuts>). | Tool | Description | Platforms | | --- | --- | --- | | [`press_key`](</docs/cua-driver/reference/mcp-tools/keys-and-shortcuts#press_key>) | Press and release one key. | macOS, Linux, Windows | | [`hotkey`](</docs/cua-driver/reference/mcp-tools/keys-and-shortcuts#hotkey>) | Press a key combination: e.g. | macOS, Linux, Windows | ## Value and clipboard tools Set element values and read or write the clipboard. [Reference](</docs/cua-driver/reference/mcp-tools/values-and-clipboard>). | Tool | Description | Platforms | | --- | --- | --- | | [`set_value`](</docs/cua-driver/reference/mcp-tools/values-and-clipboard#set_value>) | Set an element's value by `element_token`. | macOS, Linux, Windows | | [`clipboard_read`](</docs/cua-driver/reference/mcp-tools/values-and-clipboard#clipboard_read>) | List available system clipboard types and optionally return privacy-sensitive plain text. | macOS, Linux, Windows | | [`clipboard_write`](</docs/cua-driver/reference/mcp-tools/values-and-clipboard#clipboard_write>) | Replace the system clipboard with exactly one value: plain text, an image from an absolute local path, or a file URL from an absolute local path. | macOS, Linux, Windows | ## Batch tool Run several action tools in one call. [Reference](</docs/cua-driver/reference/mcp-tools/batch>). | Tool | Description | Platforms | | --- | --- | --- | | [`run_actions`](</docs/cua-driver/reference/mcp-tools/batch#run_actions>) | The default way to act: run one or more action tools in ONE call, in order, stop at the first failure, and (with `observe`) get what changed in the same response. | macOS, Linux, Windows | ## Page tools Read and act on web pages, and prepare and navigate browsers. [Reference](</docs/cua-driver/reference/mcp-tools/page>). | Tool | Description | Platforms | | --- | --- | --- | | [`page`](</docs/cua-driver/reference/mcp-tools/page#page>) | Legacy browser compatibility tool. | macOS, Linux, Windows | | [`get_browser_state`](</docs/cua-driver/reference/mcp-tools/page#get_browser_state>) | Read-only browser inspection. | macOS, Linux, Windows | | [`browser_prepare`](</docs/cua-driver/reference/mcp-tools/page#browser_prepare>) | Explicitly prepare an owned DevTools endpoint for a browser. | macOS, Linux, Windows | | [`browser_navigate`](</docs/cua-driver/reference/mcp-tools/page#browser_navigate>) | Navigate one tab of an exactly-bound browser target to a new URL (http/https/about only). | macOS, Linux, Windows | ## Browser input tools Exact browser input over CDP: clicks, typing, pointer, dialogs, files and downloads. [Reference](</docs/cua-driver/reference/mcp-tools/browser-input>). | Tool | Description | Platforms | | --- | --- | --- | | [`browser_click`](</docs/cua-driver/reference/mcp-tools/browser-input#browser_click>) | Click a page element (by ref) or viewport coordinates in an exactly-bound tab. | macOS, Linux, Windows | | [`browser_type`](</docs/cua-driver/reference/mcp-tools/browser-input#browser_type>) | Type text into an exactly-bound tab via the Input domain. | macOS, Linux, Windows | | [`browser_pointer`](</docs/cua-driver/reference/mcp-tools/browser-input#browser_pointer>) | Perform hover, right-click, double-click, scroll, or drag in an exactly-bound browser tab. | macOS, Linux, Windows | | [`browser_dialog`](</docs/cua-driver/reference/mcp-tools/browser-input#browser_dialog>) | Inspect or resolve a page-owned JavaScript alert, confirm, prompt, or beforeunload dialog on one exactly-bound tab. | macOS, Linux, Windows | | [`browser_set_input_files`](</docs/cua-driver/reference/mcp-tools/browser-input#browser_set_input_files>) | Assign one or more explicit absolute local files to an exact live <input type=file> ref through CDP. | macOS, Linux, Windows | | [`browser_download`](</docs/cua-driver/reference/mcp-tools/browser-input#browser_download>) | Trigger one download through an exact live browser ref and save it inside an explicitly approved directory. | macOS, Linux, Windows | ## Session tools Start, escalate, inspect and end lifecycle sessions. [Reference](</docs/cua-driver/reference/mcp-tools/sessions>). | Tool | Description | Platforms | | --- | --- | --- | | [`start_session`](</docs/cua-driver/reference/mcp-tools/sessions#start_session>) | Optionally create or return a lifecycle session before acting. | macOS, Linux, Windows | | [`escalate_session`](</docs/cua-driver/reference/mcp-tools/sessions#escalate_session>) | Deprecated compatibility tool for legacy capture-scope sessions. | macOS, Linux, Windows | | [`get_session`](</docs/cua-driver/reference/mcp-tools/sessions#get_session>) | Read content-free lifecycle, cursor, recording, and idle status for one session visible to this authenticated transport. | macOS, Linux, Windows | | [`list_sessions`](</docs/cua-driver/reference/mcp-tools/sessions#list_sessions>) | List content-free lifecycle summaries attached to this authenticated transport lease. | macOS, Linux, Windows | | [`get_session_state`](</docs/cua-driver/reference/mcp-tools/sessions#get_session_state>) | Deprecated compatibility alias that reads a live legacy session's capture policy. | macOS, Linux, Windows | | [`end_session`](</docs/cua-driver/reference/mcp-tools/sessions#end_session>) | End one visible lifecycle session and run its cursor, recording, configuration, and other cleanup hooks exactly once. | macOS, Linux, Windows | ## Agent cursor tools Show, animate and style the agent cursor. [Reference](</docs/cua-driver/reference/mcp-tools/agent-cursor>). | Tool | Description | Platforms | | --- | --- | --- | | [`set_agent_cursor_enabled`](</docs/cua-driver/reference/mcp-tools/agent-cursor#set_agent_cursor_enabled>) | Show or hide the agent cursor owned by a session. | macOS, Linux, Windows | | [`set_agent_cursor_motion`](</docs/cua-driver/reference/mcp-tools/agent-cursor#set_agent_cursor_motion>) | Configure the movement style, timing, effects and visibility timing for a session cursor. | macOS, Linux, Windows | | [`set_agent_cursor_theme`](</docs/cua-driver/reference/mcp-tools/agent-cursor#set_agent_cursor_theme>) | Select an already-installed cursor theme for a session. | macOS, Linux, Windows | | [`get_agent_cursor_state`](</docs/cua-driver/reference/mcp-tools/agent-cursor#get_agent_cursor_state>) | Return the session cursor's theme, semantic playback, position, visibility, and motion. | macOS, Linux, Windows | ## Recording tools Record and replay trajectories. [Reference](</docs/cua-driver/reference/mcp-tools/recording>). | Tool | Description | Platforms | | --- | --- | --- | | [`start_recording`](</docs/cua-driver/reference/mcp-tools/recording#start_recording>) | Start trajectory recording for the calling session. | macOS, Linux, Windows | | [`stop_recording`](</docs/cua-driver/reference/mcp-tools/recording#stop_recording>) | Stop trajectory recording. | macOS, Linux, Windows | | [`get_recording_state`](</docs/cua-driver/reference/mcp-tools/recording#get_recording_state>) | Report the current trajectory recorder state: whether recording is enabled, the output directory (when enabled), and the 1-based counter for the next turn folder that will be written. | macOS, Linux, Windows | | [`replay_trajectory`](</docs/cua-driver/reference/mcp-tools/recording#replay_trajectory>) | Replay a recorded trajectory by re-invoking every turn's tool call in lexical order. | macOS, Linux, Windows | | [`install_ffmpeg`](</docs/cua-driver/reference/mcp-tools/recording#install_ffmpeg>) | Install the ffmpeg binary used by start_recording's video capture (Linux/Windows; macOS records natively and needs no ffmpeg). | macOS, Linux, Windows | ## Configuration and maintenance tools Configuration, permissions, health, updates and extensions. [Reference](</docs/cua-driver/reference/mcp-tools/maintenance>). | Tool | Description | Platforms | | --- | --- | --- | | [`get_config`](</docs/cua-driver/reference/mcp-tools/maintenance#get_config>) | Return the current cua-driver-rs configuration. | macOS, Linux, Windows | | [`set_config`](</docs/cua-driver/reference/mcp-tools/maintenance#set_config>) | Update cua-driver-rs configuration. | macOS, Linux, Windows | | [`check_permissions`](</docs/cua-driver/reference/mcp-tools/maintenance#check_permissions>) | Report TCC permission status for Accessibility and Screen Recording. | macOS, Linux, Windows | | [`health_report`](</docs/cua-driver/reference/mcp-tools/maintenance#health_report>) | Single-call end-to-end driver diagnostics. | macOS, Linux, Windows | | [`check_for_update`](</docs/cua-driver/reference/mcp-tools/maintenance#check_for_update>) | Check the saved stable/nightly Cua Driver channel for a release on GitHub. | macOS, Linux, Windows | | [`install_extension`](</docs/cua-driver/reference/mcp-tools/maintenance#install_extension>) | Preview or install one Driver-managed optional extension. | macOS, Linux, Windows | --- # App and window tools List, launch, quit, front and arrange apps and windows. | Tool | Description | Platforms | | --- | --- | --- | | [`list_apps`](#list_apps) | List macOS apps, both currently running and installed-but-not-running, with per-app state flags: - running: is a process for this app live? | macOS, Linux, Windows | | [`list_windows`](#list_windows) | List layer-0 top-level windows known to WindowServer, including off-screen ones (minimized, other Space, hidden). | macOS, Linux, Windows | | [`launch_app`](#launch_app) | Launch a macOS app in the background: the target does NOT come to the foreground. | macOS, Linux, Windows | | [`kill_app`](#kill_app) | Force-terminate a process by pid (kill -9 equivalent on macOS / Linux; taskkill /F equivalent on Windows). | macOS, Linux, Windows | | [`bring_to_front`](#bring_to_front) | Persistently activate an app and leave it in the foreground. | macOS, Linux, Windows | | [`set_window_frame`](#set_window_frame) | Set one exact top-level window's frame in the desktop-coordinate space reported by list_windows and verify the resulting geometry through an independent readback. | macOS, Linux, Windows | | [`invoke_menu`](#invoke_menu) | Resolve an exact application-menu path one live native level at a time and invoke its final item through accessibility APIs. | macOS, Linux, Windows | | [`debug_window_info`](#debug_window_info) | Diagnostic: dump everything cua-driver sees about a pid's top-level windows from the daemon's session perspective. | Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `list_apps` Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. **macOS** List macOS apps, both currently running and installed-but-not-running, with per-app state flags: - running: is a process for this app live? (pid is 0 when false) - active: is it the system-frontmost app? (implies running) - launch_path: filesystem path to the `.app` bundle, when known. Pass this to `launch_app` to start the app cold. - kind: `"desktop"` for `.app` bundles on macOS. - last_used: RFC3339 timestamp from the bundle's filesystem mtime, when readable; otherwise null. Standalone running entries include only apps with NSApplicationActivationPolicyRegular: background helpers and system UI agents are filtered out. Installed apps resolve their running/pid state against all live processes by bundle identifier, so an installed app whose process runs as an accessory (LSUIElement / menu-bar apps, e.g. Cua Driver itself) still reports its live pid. Installed apps come from scanning /Applications, /Applications/Utilities, ~/Applications, /System/Applications, and /System/Applications/Utilities. Use this for "is X installed?" as well as "is X running?". For per-window state, on-screen, on-current-Space, minimized, window titles, call list_windows instead. For just opening an app, running or not, call launch_app({bundle_id: ...}) directly; list_apps is not a prerequisite. **Linux** List Linux apps, both currently running and installed-but-not-running, with per-app state flags: - running: is a process for this app live? (pid is 0 when false) - active: reserved (Linux X11/Wayland focus model differs from frontmost-app); always false. - kind: `"desktop"` for XDG `.desktop` launcher entries. - launch_path: the launcher command from `Exec=` (field codes stripped). Pass to `launch_app(launch_path=...)`. - bundle_id: the XDG "desktop file id"; the `.desktop` file's path relative to its XDG `applications/` root with the `.desktop` suffix stripped and path separators replaced with `-` (e.g. `kde4/konqbrowser.desktop` → `kde4-konqbrowser`). - last_used: RFC3339 mtime of the `.desktop` file, when readable. - windows: the app's current top-level windows (same records as list_windows); empty for a process without one. Running apps come from `/proc`. Installed apps come from XDG Desktop Entry files in $XDG_DATA_HOME/applications and each $XDG_DATA_DIRS entry's applications/ subdir. Entries with `NoDisplay=true` or `Hidden=true` are filtered. A `.desktop` file whose launcher matches a running process (by basename), or whose WM_CLASS a top-level window carries, is merged into a single entry with `running: true`. Use this for "is X installed?" as well as "is X running?". For per-window state, visibility, geometry, titles, call list_windows instead. **Windows** List Windows apps, both currently running and installed-but-not-running, with per-app state flags: - running: is a process for this app live? (pid is 0 when false) - active: is it the system-frontmost app? (implies running) - kind: `"desktop"` for `.exe`-backed apps (resolved from Start-Menu shortcuts) or `"uwp"` for packaged store apps. - launch_path: what `launch_app` would consume. * desktop: full `.exe` commandline (path + any arguments preserved from the source `.lnk`; the exe is quoted when it contains whitespace). * uwp: `shell:appsFolder\{PackageFamilyName}!{AppId}` where `{AppId}` falls back to `App` when the package manifest does not expose a specific Application.Id. - last_used: RFC3339 mtime of the launcher (`.lnk` for desktop, package install location for UWP), when readable. Running apps are derived from visible top-level windows + the foreground pid (mirrors NSApplicationActivationPolicyRegular's intent: background services and console processes are excluded). Installed apps are merged from Start-Menu `.lnk` enumeration and the WinRT PackageManager: running processes whose executable matches a Start-Menu target are folded into a single entry with `running: true`. Use this for "is X installed?" as well as "is X running?". For per-window state, on-screen, minimized, window titles, call list_windows instead. For just opening an app, running or not, call launch_app({path: ...}) directly; list_apps is not a prerequisite. **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `list_windows` Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. **macOS** List layer-0 top-level windows known to WindowServer, including off-screen ones (minimized, other Space, hidden). Use it to find a `window_id` for get_window_state. AppKit-internal helper windows are omitted. Per record: window_id, pid, app_name, title, bounds, z_index, is_on_screen, space_ids, current_space_id, on_current_space. For the frontmost window take the maximum integer z_index; null means stacking is unavailable, so do not infer it from array order. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `on_screen_only` | `boolean` | | Drop windows not on the current Space. | | `pid` | `integer` | | Only this pid's windows. | **Linux** List top-level windows. Each record includes z_index (integer or null; higher values are closer to the front; null means stacking order is unavailable and callers must not infer one). To select a frontmost candidate, take the maximum integer z_index; if every value is null, use an explicit fallback instead of relying on array order. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `on_screen_only` | `boolean` | | When true, filter to visible windows only. Default false. | | `pid` | `integer` | | Only list windows owned by this process ID. | **Windows** List every top-level window currently known to the window manager. Each record self-contains its owning app identity so the caller never has to join back against list_apps. Use this, not list_apps, for any window-level reasoning: "does this app have a visible window right now?", "which of this pid's windows is the main one?". Per-record fields: window_id (HWND), pid + app_name, title, bounds {x, y, width, height}, layer (always 0), z_index (integer or null; higher values are closer to the front; null means stacking order is unavailable and callers must not infer one), is_on_screen, minimized. To select a frontmost candidate, take the maximum integer z_index; if every value is null, use an explicit fallback instead of relying on array order. The macOS-specific on_current_space / space_ids fields are omitted on Windows; current_space_id is null. Inputs: pid (optional pid filter), on_screen_only (bool, default false). **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `on_screen_only` | `boolean` | | When true, drop windows that aren't currently on-screen. Default false. | | `pid` | `integer` | | Optional pid filter. When set, only this pid's windows are returned. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `launch_app` Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. **macOS** Launch a macOS app in the background: the target does NOT come to the foreground. Provide either `bundle_id` (preferred: unambiguous, e.g. `com.apple.calculator`) or `name` (e.g. "Calculator"). If both are given, bundle_id wins. Optional `urls` are handed to the app as open targets: for Finder, pass a folder path to open a backgrounded Finder window there. Browser DevTools setup belongs to `browser_prepare`, which can prove that a separate isolated profile is driver-owned before enabling CDP. Optional `webkit_inspector_port`: opens a WebKit inspector server on the specified port (sets WEBKIT_INSPECTOR_SERVER=127.0.0.1:N + TAURI_WEBVIEW_AUTOMATION=1). Use this for Tauri/WebKit-based apps. Optional `creates_new_application_instance`: when true, forces a new app instance even if one is already running (passes -n to open). Reach for this when another agent or session may drive the SAME app concurrently: it returns a fresh pid + window so each session acts on its own isolated window instead of clobbering one shared instance. Without it, single-instance apps (Calculator, many utilities) hand every caller the same window, so two sessions fight over it. Optional `additional_arguments`: extra argv strings appended after --args. Returns the launched app's pid, bundle_id, name, and a `windows` array (same shape as `list_windows`) so callers can skip an extra round-trip before `get_window_state(pid, window_id)`. `launch_state` distinguishes whether the request was sent, the process is running, and a window is ready. When the focus-steal belt-and-braces demotion check ran (target pid ≠ prior frontmost), the response also includes `self_activation_suppressed: bool`; true if focus stayed with the prior frontmost, false if the launched app held focus despite the re-demote attempt. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `additional_arguments` | `string[]` | | Extra arguments appended after --args when launching. | | `bundle_id` | `string` | | App bundle identifier, e.g. com.apple.calculator. Preferred over name. | | `creates_new_application_instance` | `boolean` | | When true, force a new app instance even if already running (open -n). Use for concurrent multi-agent/multi-session work so each session gets an isolated instance + window instead of sharing one: on single-instance apps (e.g. Calculator) every caller otherwise gets the same window and the sessions clobber each other. | | `name` | `string` | | App display name. Used only when bundle_id is absent. | | `urls` | `string[]` | | Optional file paths or URLs to open with the app (e.g. a folder path for Finder). | | `webkit_inspector_port` | `integer` | | Open a WebKit inspector server on this port (sets WEBKIT_INSPECTOR_SERVER env var). | **Linux** Launch a Linux app in the background. Provide launch_path (preferred: round-trip the value from list_apps), name (tried as a direct command, then matched against installed .desktop applications, then handed to xdg-open if it is a URL or existing file path), bundle_id (ignored on Linux), or urls (list of URLs to open). Resolution precedence: launch_path > name > bundle_id. Errors when the name resolves to nothing launchable. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `additional_arguments` | `string[]` | | Extra command-line arguments passed to the launched process. | | `bundle_id` | `string` | | Ignored on Linux (macOS/Windows concept). | | `launch_path` | `string` | | Round-trip the `launch_path` returned by `list_apps`: the Exec= command from the .desktop file with XDG field codes already stripped. Highest precedence on Linux; spawned directly via the system shell. | | `name` | `string` | | App name or command to launch. Tried as a direct command first, then matched against installed .desktop applications (exact display name, desktop-file id, or Exec basename; else an unambiguous display-name substring). | | `urls` | `string[]` | | URLs to open via xdg-open. | **Windows** Launch a Windows app hidden: the driver never brings the target to the foreground, the target's window is launched with SW_SHOWNOACTIVATE so it does not steal focus from whatever is currently frontmost. Provide either `bundle_id` / `name` / `aumid` (resolved as below) or `path` (full path to an executable). If both `name` and `path` are given, `path` wins. `urls` opens each URL in the default browser without activating it. Routing order: explicit `aumid` (or a `bundle_id` containing `!`) activates the packaged app via `IApplicationActivationManager::ActivateApplication`, returning the real packaged-process pid; required on Win11 for built-in apps like Notepad, Calculator, and Paint, which now ship as Microsoft Store packages where the legacy .exe in System32 is a ~7 KB stub that exits immediately. A plain `name` first attempts a `shell:AppsFolder` lookup; packaged matches use the activation manager while desktop registrations are launched through their shell parsing path. A miss falls back to ShellExecuteEx's PATH search. Returns the launched app's pid, name, active flag, AND a `windows` array: same per-window shape `list_windows` returns. When the launch settles but no window has materialized yet (transient; rare), `windows` comes back empty: call `list_windows(pid)` explicitly a moment later. `bundle_id` in the response is set to the AUMID actually used for packaged-app launches and `null` for ShellExecuteEx launches. Windows-only field: `path` (Swift uses `bundle_id` since macOS apps resolve via LaunchServices; Windows has no LaunchServices, so `path` is the canonical form). The macOS-specific `webkit_inspector_port`, `creates_new_application_instance`, and `additional_arguments` fields are accepted; `additional_arguments` is honored (forwarded as ShellExecuteEx parameters or as the AUMID activation arguments string). The remaining macOS-only fields currently no-op on Windows. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `additional_arguments` | `string[]` | | Extra command-line arguments passed to the launched process (or activation arguments for packaged apps). | | `aumid` | `string` | | Explicit AUMID for a packaged app. Cleaner alternative to overloading `bundle_id`. Takes precedence over `bundle_id` / `name` when present. | | `bundle_id` | `string` | | App User Model ID (AUMID) for a packaged app: pattern `{PackageFamilyName}!{ApplicationId}`, e.g. `Microsoft.WindowsNotepad_8wekyb3d8bbwe!App`. Falls back to a `name` alias if no `!` is present. Either bundle_id, name, aumid, path, or launch_path must be provided. | | `creates_new_application_instance` | `boolean` | | Accepted for parity; no-op on Windows (ShellExecuteEx always creates a new process). | | `launch_path` | `string` | | Round-trip the `launch_path` returned by `list_apps`. Highest precedence: when set, this exact string is handed to ShellExecuteEx unchanged. For Windows desktop apps it's the full `.exe` commandline (path + arguments preserved from the source shortcut); for UWP apps it's `shell:appsFolder\{PackageFamilyName}!{AppId}`. For precise UWP pid capture, prefer `aumid` over `launch_path`. | | `name` | `string` | | App display name. Tried against the `shell:AppsFolder` index first for packaged-app lookup; on a miss, passed to ShellExecuteEx's PATH search. | | `path` | `string` | | Full path to executable. Windows-only; takes precedence over name/bundle_id/aumid (but not over `launch_path`). | | `start_minimized` | `boolean` | | When true, launch the app's window minimized to the taskbar instead of restored-but-not-activated. Use this when the agent wants to drive the app entirely in the background: the user's previously-frontmost window (e.g. terminal) stays visually on top. Desktop launches hold the foreground lock through startup and use SW_SHOWMINNOACTIVE; packaged-app activation remains broker-controlled and receives a best-effort SW_SHOWMINNOACTIVE post-pass. UIA / background dispatch still work on a minimized window; only `screenshot` and `delivery_mode:"foreground"` need it restored. | | `urls` | `string[]` | | URLs to open in the default browser via ShellExecuteEx (no activation). | | `webkit_inspector_port` | `integer` | | Accepted for cross-platform parity; no-op on Windows. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `kill_app` Effect: destructive, idempotent. Platforms: macOS, Linux, Windows. **macOS** Force-terminate a process by pid (kill -9 equivalent on macOS / Linux; taskkill /F equivalent on Windows). Use as escalation when the cooperative close path (hotkey cmd+q on macOS, click-the-X on Windows) failed to make the process exit. Unsaved state is lost: prefer the cooperative path first. **Linux** Force-terminate a process by pid (kill -9 equivalent on Linux). Use as escalation when the cooperative close path failed to make the process exit. Unsaved state is lost: prefer the cooperative path first. **Windows** Force-terminate a process by pid. Use when the standard close path (Alt+F4 / WM_CLOSE via `click` on the X button) fails to make the process exit: typical for UWP / WinUI3 apps (Calculator, Photos, modern Notepad) that route WM_CLOSE into a suspended-but-resident state. Equivalent to `taskkill /F /PID <pid>`. Unsaved state is lost. Prefer the click-the-X path first; only escalate to `kill_app` when polite close didn't terminate. **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `integer` | required | PID of the process to terminate. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"pid":1} ``` ## `bring_to_front` Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. **macOS** Persistently activate an app and leave it in the foreground. Most input does not need this; use it only for a focus-proxy surface that must remain foreground across interactions. With window_id, success means the exact ordinary macOS window was independently verified as the frontmost process's focused window and the front window of that process on the display it sits on. `exact_window_effect.frontmost_ordinary` additionally reports whether it is first in the global WindowServer layer-0 order; another application (an always-raised utility window, another display's front window) can hold that spot without the requested window losing keyboard focus, so it is reported and not required. Request acceptance alone is reported as a partial result, never as activation. This DOES steal foreground and does NOT restore the previously frontmost application. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `integer` | required | Process ID of the app to activate. | | `window_id` | `integer` | | CGWindowID to verify as the focused, frontmost window. Omit to activate the app only. | **Linux** Persistently activate a window so subsequent input lands on it. This deliberately breaks the no-foreground contract and is not part of the normal input ladder. For an ordinary `background_unavailable` response, retry only the refused action with `delivery_mode:"foreground"`; the input tool performs its own activate, act, and restore sequence. Use `bring_to_front` only for a focus-proxy surface that must remain foreground across multiple calls, such as a remote desktop session, or when repeated action-scoped activation prevents the remote surface from accepting input. X11: EWMH _NET_ACTIVE_WINDOW activation (the `wmctrl -a` equivalent, with proper timestamp handling to beat focus-stealing prevention). Wayland: activates through a target-addressable compositor adapter (wlroots foreign-toplevel or the GNOME Shell helper) and refuses when the compositor offers no safe adapter. Matches the macOS / Windows bring_to_front rung. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `integer` | required | Process ID of the app to activate. | | `window_id` | `integer` | | X11 window id (xid) to activate. If omitted, the first window of `pid` is used. | **Windows** Activate `pid`'s window (or `window_id` if specified) -- bring it to the OS foreground. **This deliberately breaks the no-foreground contract.** It is not part of the normal input ladder. For an ordinary `background_unavailable` response, retry only the refused action with `delivery_mode:"foreground"`; the input tool performs its own activate, act, and restore sequence. Use `bring_to_front` only for a focus-proxy surface that must remain foreground across multiple calls, such as an RDP or Windows App session, or when repeated action-scoped activation prevents the remote surface from accepting input. Implementation uses the `AttachThreadInput` trick to bypass Windows' foreground-lock when the daemon is not at UIAccess integrity. Returns structured `{previous_fg_hwnd, now_fg_hwnd}` so callers can later restore. Windows only; macOS / Linux return an error pointing at platform-native alternatives. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `integer` | required | Target process ID. | | `window_id` | `integer` | | Optional HWND. Defaults to pid's first visible top-level window. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"pid":1} ``` ## `set_window_frame` Set one exact top-level window's frame in the desktop-coordinate space reported by list_windows and verify the resulting geometry through an independent readback. Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `height` | `number` | required | New height, in the same units as list_windows bounds. Minimum: 1. | | `pid` | `integer` | required | Process ID that owns the window. Minimum: 1. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `width` | `number` | required | New width, in the same units as list_windows bounds. Minimum: 1. | | `window_id` | `integer` | required | Window ID from list_windows. Minimum: 1. | | `x` | `number` | required | New left edge in the desktop coordinate space reported by list_windows. | | `y` | `number` | required | New top edge in the desktop coordinate space reported by list_windows. | **Example arguments** ```json {"pid":1,"window_id":1,"x":100,"y":200,"width":1,"height":1} ``` ## `invoke_menu` Resolve an exact application-menu path one live native level at a time and invoke its final item through accessibility APIs. Missing, ambiguous, disabled, or structurally mismatched segments fail closed; this tool never falls back to pixels. Effect: destructive. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `path` | `string[]` | required | Menu labels from the top-level menu to the item, e.g. `["File", "Save As..."]` (1 to 16 labels). Items: 1 to 16. | | `pid` | `integer` | required | Process ID of the application that owns the menu. Minimum: 1. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | required | Window ID from list_windows whose menu is invoked. Minimum: 1. | **Example arguments** ```json {"pid":1,"window_id":1,"path":["<path>"]} ``` ## `debug_window_info` Diagnostic: dump everything cua-driver sees about a pid's top-level windows from the daemon's session perspective. Returns window class names, owning .exe basename + path, and, when CUIAutomation succeeds, the focused UIA element with the list of patterns it supports (ValuePattern, InvokePattern, TextPattern, TogglePattern, etc.). Used to design / debug input routing for XAML / UWP / WinUI3 targets; see CUA-543. Effect: read-only, idempotent. Platforms: Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `integer` | required | PID of the process to inspect. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"pid":1} ``` --- # Window state tools Screenshots, accessibility trees, verification and visual regions of a window. | Tool | Description | Platforms | | --- | --- | --- | | [`get_window_state`](#get_window_state) | Snapshot a window: the accessibility tree plus a screenshot. | macOS, Linux, Windows | | [`get_accessibility_tree`](#get_accessibility_tree) | Return a lightweight snapshot of the desktop: running regular apps and on-screen visible windows with their bounds, z-order, and owner pid. | macOS, Linux, Windows | | [`verify_state`](#verify_state) | Deterministically verify bounded predicates against one exact window. | macOS, Linux, Windows | | [`parse_visual_regions`](#parse_visual_regions) | Parse one immutable registered capture into model-neutral text and icon regions. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `get_window_state` Effect: read-only. Platforms: macOS, Linux, Windows. **macOS** Snapshot a window: the accessibility tree plus a screenshot. By default the tree is ONE compact Markdown rendering (`tree_format:"markdown"`): a row `[N]` is addressed with `element_token` `<snapshot_id>:N`, where `snapshot_id` is in the response header and in structuredContent. `tree_format:"elements"` returns the structured `elements` array instead (`element_index`, `element_token`, `role`, `label`, `value`, `actions`, `frame`, `parent_index`, `depth`); `"both"` returns both (about twice the size). Pass the token to click, type_text, press_key, etc. START NARROW: a read walks at most 250 nodes by default and says `Tree truncated at max_elements=…` (`truncated:true`) when it stops; raise `max_elements`, or use `query` (case-insensitive substring; matching rows plus ancestors) and `max_depth`. Indices and tokens stay valid for the whole snapshot. `verbose:true` adds `_note` and the full `background_input` report; `full_output:true` restores the previous full response (both representations, all metadata, ≤2 000 nodes). DIFF READS (use them for every re-read after acting): `since:"latest"`, or `since:<snapshot_id>` from an earlier read of the same window, returns only what changed: `+` added, `~` changed, `-` removed rows (removed ids are the old snapshot's), a `reindexed:` line if indices shifted, or `no change since …; focused element is …`. The response carries a NEW snapshot_id: use it in tokens. An unknown, expired, other-window or differently-scoped (query/max_elements/max_depth) `since` falls back to a full read; `since_status` says why. A new snapshot of the same (pid, window_id) replaces the previous one and stales its tokens (`invalidated_snapshot_ids`). `include_screenshot:false` returns the tree only and keeps the last screenshot's pixel frame while the window keeps its size; `include_accessibility_tree:false` returns only the screenshot and keeps the current snapshot's rows and tokens valid (use it, not `max_elements:1`, when you just need to look). The tree can lie on some surfaces (Electron, Catalyst, virtualized rows with `h:1` frames): cross-check the screenshot, and use pixel x,y at action time only for elements missing from the tree. Refusals: `window_id_not_found`; `window_owner_pid_mismatch` names the real `owner_pid` (sandboxed Open/Save panels belong to a panel service). `degraded_reason` `ax_window_unresolved` or `ax_app_launching` (the latter with `truncation_reason: app_lookup_timeout`) means an empty tree: re-snapshot, or act with `delivery_mode:"foreground"`. `px_frame_mismatch` / `px_capture_unavailable` omit the unprovable screenshot; the AX payload stays valid. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `include_accessibility_tree` | `boolean` | | Default true. False skips the AX walk and returns the screenshot plus window metadata. Both this and include_screenshot false is an error. | | `include_screenshot` | `boolean` | | Default true. False returns the tree only (saves image tokens and latency); screenshot_out_file still forces a capture. | | `max_depth` | `integer` | | Cap on walk depth. Default 25. Minimum: 1. | | `max_dimension` | `integer` | | Optional cap on the returned screenshot's long edge, in pixels (aspect ratio preserved): the cheap path for a small preview / thumbnail. Applied on top of the session/global max_image_dimension ceiling; the tighter of the two wins. Omit for the configured default. Minimum: 1. | | `max_elements` | `integer` | | Cap on AX nodes walked (depth-first; markdown and elements truncate together). Default 250 (2 000 with full_output:true); the response states when the tree was cut. Minimum: 1. | | `max_image_dimension` | `integer` | | Per-call override for the returned screenshot's long edge in pixels. An explicit value wins over the session/global setting; 0 returns native resolution. Omit to preserve configured behavior. Minimum: 0. | | `pid` | `integer` | required | Target process ID. | | `query` | `string` | | Case-insensitive substring filter: returns matching rows plus ancestors; element_index values are not renumbered. Try this before a full read. | | `screenshot_out_file` | `string` | | When set, write the PNG to this file path (~ expanded) instead of embedding base64 in the response. The structured output will contain screenshot_file_path instead. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. | | `window_id` | `integer` | required | Target window ID from list_windows. | **Linux** Walk a running app's AT-SPI tree and return it ONCE, as compact Markdown by default (`tree_format:"markdown"`). Every actionable element is tagged `[N]` (an element_index); its element_token is `<snapshot_id>:N`, where `snapshot_id` is printed in the response header and returned in structuredContent. Pass `tree_format:"elements"` for the structured `elements` array instead (explicit `element_token`, `role`, `label`, `value`, `enabled`, `selected`, `actions`, `frame: {x,y,w,h}` when AT-SPI reports usable bounds, `parent_index`, `depth`), or `"both"` for both (about twice the size). Set `query` to project the tree to matching rows plus their ancestor chain while preserving original indices; `total_element_count` reports the complete snapshot. Size: a read is bounded by default (`max_elements` 250 actionable nodes). When the walk stops at that budget the response says `Tree truncated at max_elements=…` and `truncated: true`; pass a larger `max_elements`, or `query` / `max_depth`, to reach the rest. `_note` is omitted unless `verbose:true`. `full_output:true` restores the previous full response: both representations, all metadata, and the walker's own limits (≤5 000 nodes). DIFF READS: pass `since: <snapshot_id>` from an earlier read of the same window to get only what changed: `+` added rows, `~` changed rows, `-` removed rows (their ids are the old snapshot's), or `no change since …`. Rows not listed keep their previous `[N]` unless a `reindexed:` line says otherwise. The response carries a NEW snapshot_id: use it in element_tokens. An unknown, expired, other-window or differently-scoped (query/max_elements/max_depth) `since` falls back to a full read and `since_status` says why. The focused element is not reported on Linux yet. Always returns BOTH the element tree AND a screenshot: ground on both and cross-check (the tree lies on some surfaces). Choose the modality at ACTION time: an element ax action (element_token → accessibility rung) or an element px action (x,y → pixel rung off this screenshot). capture_mode is deprecated and ignored. On Wayland, where output capture cannot prove the requested surface's identity, the truthful tree is returned without a screenshot and `screenshot_error.code` is `surface_identity_unproven`. The mirror image: pass `include_accessibility_tree:false` to SKIP the AT-SPI walk entirely and return just the screenshot plus window metadata (window_bounds, app_name, window_title); the capture-only path for a live window preview / picture-in-picture. Setting BOTH `include_accessibility_tree:false` and `include_screenshot:false` is an error. Optional `max_dimension` caps the returned screenshot's long edge in pixels for a cheap thumbnail. Optional `max_elements` / `max_depth` bound the AT-SPI walk (Electron / large web apps produce 10k+ element trees). Markdown and structured elements are truncated identically. Default: `max_elements` 250. TIME BUDGET: `timeout_ms` (default 1000) bounds the whole AT-SPI walk. When omitted, a window's first snapshot gets a 2000 ms cold-start budget so Chromium/Electron can finish publishing its initial tree. Explicit values are always honored. Large apps (LibreOffice, GIMP, file managers) can exceed the budget; the call then returns the PARTIAL tree with `truncated: true`, `truncation_reason`, `nodes_visited`/`nodes_pending` and `elements_complete: false`. Every element listed is real and clickable; elements after the cut are simply missing. Retry with a larger `timeout_ms` (e.g. 5000) or narrow with `query`/`max_depth` when the element you need is absent. SCREENSHOT SCALE: the screenshot is delivered at or below 1.15 megapixels (long edge <= max_image_dimension, 1568 by default), because larger images are downsized before a model reads them and its pixel coordinates would then be uniformly short. Element `screenshot_frame`s and x/y for the pointer tools are pixels of the delivered screenshot (`frame` stays in screen coordinates, as on every platform); `frame_scale` < 1 reports the downsizing. An explicit per-call `max_image_dimension` (0 = native) replaces this cap. POPUP MENUS: a context menu / popover / combo list is an override-redirect window that list_windows never shows. A click or right_click that opened one names it in its result (`popup: {window_id, bounds, title}`); pass that window_id here to walk the popup's own AT-SPI toplevel so its menu items get element indices (then click them by element_token). Omitting window_id while a popup of this pid is open walks that popup. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `include_accessibility_tree` | `boolean` | | Default true: walk the AT-SPI tree and return it (per `tree_format`) alongside the screenshot. Set false to SKIP the AT-SPI walk entirely and return just the screenshot plus window metadata (window_bounds, app_name, window_title): the capture-only path for a live window preview / picture-in-picture. Mirrors include_screenshot. Setting BOTH include_accessibility_tree:false AND include_screenshot:false is an error (nothing to return). | | `include_screenshot` | `boolean` | | Default true: returns a grounding screenshot alongside the tree. Set false to skip the grab and return tree only (the cheap path for re-indexing before an element ax action). | | `max_depth` | `integer` | | Cap on the AT-SPI tree walk depth. Omit for the default (uncapped). Lower for deeply nested apps. Minimum: 1. | | `max_dimension` | `integer` | | Legacy optional cap on the returned screenshot's long edge. Applied on top of the configured max_image_dimension ceiling when max_image_dimension is omitted. Minimum: 1. | | `max_elements` | `integer` | | Cap on total AT-SPI nodes walked. Default 250 (5 000 with full_output:true); the response states when the tree was cut. Raise it to read further into a large window. Minimum: 1. | | `max_image_dimension` | `integer` | | Per-call long-edge override. This value wins over configured and legacy limits; 0 returns native-resolution PNG bytes. Omit to preserve configured behavior. Minimum: 0. | | `pid` | `integer` | required | Process ID that owns the window. | | `query` | `string` | | Optional case-insensitive substring. Projects both tree_markdown and structured elements to matches plus ancestors while preserving original indices. Compare total_element_count with returned_element_count. | | `screenshot_out_file` | `string` | | When set, write the PNG to this file path (~ expanded) instead of embedding base64 in the response. The structured output carries screenshot_file_path instead. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | | Native window identifier from list_windows, or the `popup.window_id` a click / right_click result named (an open context menu / popover; its menu items then get element indices). Omitted: the pid's open popup menu when one is mapped, else its focused / active / largest window. | **Windows** Walk a running app's UIA tree and return it ONCE, as compact Markdown by default (`tree_format:"markdown"`). Every actionable element is tagged `[N]` (an element_index); its element_token is `<snapshot_id>:N`, where `snapshot_id` is printed in the response header and returned in structuredContent. Pass `tree_format:"elements"` for the structured `elements` array instead (explicit `element_token`, `frame`, `parent_index`, `value`, `actions` per row), or `"both"` for both (about twice the size). Size: a read is bounded by default (`max_elements` 250). When the walk stops at that budget the response says `Tree truncated at max_elements=…` and `truncated: true`; pass a larger `max_elements`, or `query` / `max_depth`, to reach the rest. `_note` is omitted unless `verbose:true`. `full_output:true` restores the previous full response: both representations, all metadata, and the platform's walk limits (≤5 000 elements, depth ≤25). DIFF READS: pass `since: <snapshot_id>` from an earlier read of the same window to get only what changed: `+` added rows, `~` changed rows, `-` removed rows (their ids are the old snapshot's), or `no change since …`. Rows not listed keep their previous `[N]` unless a `reindexed:` line says otherwise. The response carries a NEW snapshot_id: use it in element_tokens. An unknown, expired, other-window or differently-scoped (query/max_elements/max_depth) `since` falls back to a full read and `since_status` says why. The focused element is not reported on Windows yet. INVARIANT: call `get_window_state` once per turn per (pid, window_id) before any element action against that window. The next snapshot of the same (pid, window_id) replaces this one, stales its element tokens, and lists the replaced ids in `invalidated_snapshot_ids`. The UIA tree walked is the window's tree (HWND-scoped); the screenshot and window bounds reported come from the same `window_id`. This is the source of truth for which window the caller intends to reason about: the driver never picks a window implicitly. `window_id` MUST belong to `pid`; the call returns `isError: true` otherwise. The driver does not auto-fall-back to a different window. Set `query` to a case-insensitive substring to project the tree to matching rows plus their ancestor chain. Original element indices are preserved. `total_element_count` reports the complete snapshot; `returned_element_count` reports the projection. Always returns BOTH the element tree AND a screenshot: ground on both and cross-check (the tree lies on some surfaces). Choose the modality at ACTION time: an element ax action (element_token → accessibility rung) or an element px action (x,y → pixel rung off this screenshot). capture_mode is deprecated and ignored. The mirror image: pass `include_accessibility_tree:false` to SKIP the UIA walk entirely and return just the screenshot plus window metadata (window_bounds, app_name, window_title); the capture-only path for a live window preview / picture-in-picture. Setting BOTH `include_accessibility_tree:false` and `include_screenshot:false` is an error. Optional `max_image_dimension` overrides the configured screenshot long-edge limit for this call; pass 0 for native resolution. Legacy `max_dimension` remains a cap on the configured limit. Uses `IUIAutomationCacheRequest` to batch-fetch all element properties in a single COM call (Chrome's ~5000-element tree returns in ~2-3s instead of timing out at 4s with per-property RPCs). Optional `max_elements` / `max_depth` bound the UIA walk (Electron / large web apps produce 10k+ element trees). Markdown and structured elements are truncated identically. Default: `max_elements` 250, depth ≤25. CHROMIUM COVERAGE: a browser-owned permission bubble can be composited outside the requested native window. Chromium-family snapshots therefore describe this limit in structuredContent.capture_coverage. After a verified ineffective window action, call escalate_session, take a fresh get_desktop_state snapshot, act explicitly in desktop scope if needed, then verify with another fresh desktop snapshot. This is separate from page JavaScript dialogs, which remain on browser_dialog. Windows requires no special permissions. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `include_accessibility_tree` | `boolean` | | Default true: walk the UIA tree and return it (per `tree_format`) alongside the screenshot. Set false to SKIP the UIA walk entirely and return just the screenshot plus window metadata (window_bounds, app_name, window_title): the capture-only path for a live window preview / picture-in-picture. Mirrors include_screenshot. Setting BOTH include_accessibility_tree:false AND include_screenshot:false is an error (nothing to return). | | `include_screenshot` | `boolean` | | Default true: returns a grounding screenshot alongside the tree. Set false to skip the grab and return tree only (the cheap path for re-indexing before an element ax action). | | `max_depth` | `integer` | | Cap on the UIA-tree walk depth. Nodes whose rendered indent would exceed this are omitted. Omit for the default (25). Lower for deep menu / Electron trees. Minimum: 1. | | `max_dimension` | `integer` | | Legacy optional cap on the returned screenshot's long edge, in pixels (aspect ratio preserved). Applied on top of the configured max_image_dimension ceiling when max_image_dimension is omitted; the tighter wins. Minimum: 1. | | `max_elements` | `integer` | | Cap on the total number of UIA nodes walked. Truncates depth-first; markdown and structured elements truncate together. Default 250 (5 000 with full_output:true); the response states when the tree was cut. Raise it to read further into a large window. Minimum: 1. | | `max_image_dimension` | `integer` | | Per-call long-edge limit for the returned screenshot, in pixels (aspect ratio preserved). Explicit values override configured behavior; 0 returns native resolution. Omit to preserve the configured default. Minimum: 0. | | `pid` | `integer` | required | Process ID from `list_apps`. | | `query` | `string` | | Optional case-insensitive substring. Projects both tree_markdown and structured elements to matches plus ancestors while preserving original indices. Compare total_element_count with returned_element_count. | | `screenshot_out_file` | `string` | | When set, write the PNG to this file path instead of embedding base64 in the response. The structured output will contain `screenshot_file_path` instead. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | required | HWND of the target window. Must belong to `pid`. Enumerate via `list_windows` or read from `launch_app`'s `windows` array. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `capture_mode` | `"ax" \| "vision"` | | DEPRECATED and ignored. get_window_state always returns BOTH the element tree and a screenshot: ground on both. The modality is chosen at action time by how you address the target: an element ax action (element_token) or an element px action (x,y). Any value (including the old "som"/"screenshot" aliases) is accepted but has no effect. | | `full_output` | `boolean` | | Default false. Set true to restore the previous full response: both `elements` and `tree_markdown`, all metadata, and the platform's own walk limits unless max_elements / max_depth are passed. Does not combine with `since`. | | `since` | `string` | | A `snapshot_id` from an earlier get_window_state of the same window, or "latest" for this window's most recent read with the same query/max_elements/max_depth. Use it on every re-read after acting. Returns only what changed against that snapshot: `+` added rows, `~` changed rows, `-` removed rows (removed ids are the old snapshot's), or `no change`. Falls back to a full read when the snapshot is unknown, expired, belongs to another window, or was read with a different query/max_elements/max_depth; `since_status` says which. The response still carries a NEW snapshot_id and its element_tokens; rows not listed keep their previous [N] unless a `reindexed:` line says otherwise. | | `timeout_ms` | `integer` | `1000` | Wall-clock budget in milliseconds for the accessibility-tree walk (default 1000, min 100, max 120000). Bounds the WHOLE walk. When the budget runs out the tool returns the PARTIAL tree it has, flagged with `truncated: true`, `truncation_reason`, `nodes_visited`, `nodes_pending` and `elements_complete: false`; retry with a larger value (e.g. 5000) or narrow with `query` / `max_depth`. Range: 100 to 120000. | | `tree_format` | `"markdown" \| "elements" \| "both"` | | Which tree representation to return. Default "markdown": the compact `tree_markdown` only, where a row `[N]` is addressed with element_token `<snapshot_id>:N`. "elements": the structured `elements` array only (per-element element_token, frame, parent_index, value). "both": the two together, as before; roughly doubles the size. | | `verbose` | `boolean` | | Default false. Set true to include the `_note` string and the full `background_input` capability report (macOS), which are omitted by default to save context. | **Example arguments** ```json {"pid":1,"window_id":1} ``` ## `get_accessibility_tree` Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. **macOS** Return a lightweight snapshot of the desktop: running regular apps and on-screen visible windows with their bounds, z-order, and owner pid. For the full AX subtree of a single window (with interactive element indices you can click by), use `get_window_state` instead: that's the heavy per-window tool. This one is a fast discovery read that needs no TCC grants. **Linux** Return a lightweight snapshot of the desktop: running processes and on-screen visible X11 windows with their bounds and owner pid. For the full AT-SPI subtree of a single window (with interactive element indices you can click by), use get_window_state instead: this is a fast discovery read. **Windows** Return a lightweight snapshot of the desktop: running processes and on-screen visible windows with their bounds and owner pid. For the full UIA subtree of a single window (with interactive element indices you can click by), use get_window_state instead: this is a fast discovery read. **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `verify_state` Deterministically verify bounded predicates against one exact window. The driver evaluates structured window/accessibility state and may return the final screenshot as uninterpreted visual evidence for a multimodal caller. Predicate results are satisfied, unsatisfied, or unknown; unknown never implies success. Accessibility projections are conservative: absence remains unknown unless the observed search domain is proven exhaustive. Effect: read-only. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `expect` | `object[]` | required | One to eight predicates, combined with logical AND. Items: 1 to 8. | | `include_screenshot` | `boolean` | | Return the final window screenshot as image content for a multimodal caller. The driver does not interpret that image. | | `pid` | `integer` | required | Exact process whose window may be observed. Minimum: 1. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. This field never selects capture modality or authorization. | | `stable_samples` | `integer` | `2` | Consecutive satisfied samples required before returning success. Range: 1 to 5. | | `timeout_ms` | `integer` | `5000` | Bounded wait. Zero performs one sample. Range: 0 to 10000. | | `window_id` | `integer` | required | Exact native window identifier. | **Example arguments** ```json {"pid":1,"window_id":1,"expect":[{}]} ``` ## `parse_visual_regions` Parse one immutable registered capture into model-neutral text and icon regions. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `capture_id` | `string` | required | `capture_id` of a screenshot returned by get_window_state or get_desktop_state. | | `options` | `object` | `{}` | Optional, model-neutral controls for one bounded parse. | | `options.kinds` | `"text" \| "icon"[]` | | Region kinds to return (`text`, `icon`). Omit for all kinds. | | `options.max_regions` | `integer` | | Return at most this many regions. Minimum: 1. | | `options.min_confidence` | `number` | | Drop regions below this confidence (0 to 1). Range: 0 to 1. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"capture_id":"<capture_id>"} ``` --- # Screen tools The desktop, screen size, cursor position and zoomed crops. | Tool | Description | Platforms | | --- | --- | --- | | [`get_desktop_state`](#get_desktop_state) | Capture the full display in true screen pixels, full size unless `max_image_dimension` caps it. | macOS, Linux, Windows | | [`get_screen_size`](#get_screen_size) | Return the logical size of the main display in points plus its backing scale factor. | macOS, Linux, Windows | | [`get_cursor_position`](#get_cursor_position) | Return the current mouse cursor position in screen points (origin top-left). | macOS, Linux, Windows | | [`zoom`](#zoom) | Capture a cropped JPEG of a window region (x1,y1)–(x2,y2) in screenshot pixel coordinates, with 20% padding added on each side. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `get_desktop_state` Effect: read-only. Platforms: macOS, Linux, Windows. **macOS** Capture the full display in true screen pixels, full size unless `max_image_dimension` caps it. Use its PNG as the coordinate source for actions whose target is {kind:"desktop",display_id:"primary"}. Returns the true screen size and backing scale factor. Vision-only: no AX tree walk. **Linux** Capture the full display in the desktop action coordinate frame. Use the returned PNG directly as the coordinate source for actions whose target is {kind:"desktop",display_id:"primary"}. No AT-SPI walk. **Windows** Capture the full display in true screen pixels, full size unless `max_image_dimension` caps it. Use its PNG as the coordinate source for actions whose target is {kind:"desktop",display_id:"primary"}. Returns the true screen size. Vision-only: no UIA tree walk. **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `max_image_dimension` | `integer` | | Optional long-edge cap for the returned PNG, in pixels (aspect ratio preserved). Omitted or 0 returns the full-size capture. When the cap downsizes the image, the response reports `screenshot_original_width/height`, and x/y read off it for this session's later scope:"desktop" actions (or passed with its `capture_id`) are mapped back to the full-size frame automatically. Minimum: 0. | | `screenshot_out_file` | `string` | | Write PNG here instead of base64. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `get_screen_size` Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. **macOS** Return the logical size of the main display in points plus its backing scale factor. Agents click in points; Retina displays have scale_factor 2.0. Requires no TCC permissions. **Linux** Return the logical size of the main display in points plus its backing scale factor. Agents click in points; Retina displays have scale_factor 2.0. Requires no TCC permissions. **Windows** Return the size of the main display in physical pixels plus its display scale factor. On Windows, screenshots and pixel clicks use this same physical-pixel coordinate space. Requires no special permissions. **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `get_cursor_position` Return the current mouse cursor position in screen points (origin top-left). Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `zoom` Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. **macOS** Capture a cropped JPEG of a window region (x1,y1)–(x2,y2) in screenshot pixel coordinates, with 20% padding added on each side. The output image is at most 500 px wide. Corners, not a size: x1,y1 is the top-left and x2,y2 the bottom-right (x,y,width,height is also accepted and converted). After a zoom, pass `from_zoom=true` to click/type_text to auto-translate coordinates back to full-window space. Coordinate actions return `screenshot_context_missing` when no current snapshot contains a screenshot owned by this session. `from_zoom` actions return `zoom_context_missing` when the zoom was never created or was replaced; call `get_window_state`, then `zoom`, again on the same connection. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `integer` | | Optional target pid. When omitted, the driver resolves the unique current snapshot for this session and window. | | `window_id` | `integer` | required | CGWindowID from list_windows. | | `x1` | `number` | required | Left edge of region in screenshot pixels. | | `x2` | `number` | required | Right edge of region in screenshot pixels. | | `y1` | `number` | required | Top edge of region in screenshot pixels. | | `y2` | `number` | required | Bottom edge of region in screenshot pixels. | **Linux** Capture a cropped JPEG of a window region (x1,y1)–(x2,y2) in screenshot pixels, with 20% padding. Output is at most 500 px wide. Corners, not a size: x1,y1 is the top-left and x2,y2 the bottom-right (x,y,width,height is also accepted and converted). After a zoom, pass from_zoom=true to click/type_text to auto-translate coordinates back to full-window space. Coordinate actions return `screenshot_context_missing` when no current snapshot contains a screenshot owned by this session. `from_zoom` actions return `zoom_context_missing` when the zoom was never created or was replaced; call `get_window_state`, then `zoom`, again on the same connection. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `integer` | | Optional target pid. When omitted, the driver resolves the unique current snapshot for this session and window. | | `window_id` | `integer` | required | Window id of the window captured by get_window_state. | | `x1` | `number` | required | Left edge of the region in window-local screenshot pixels. | | `x2` | `number` | required | Right edge of the region in window-local screenshot pixels. | | `y1` | `number` | required | Top edge of the region in window-local screenshot pixels. | | `y2` | `number` | required | Bottom edge of the region in window-local screenshot pixels. | **Windows** Zoom into a rectangular region of a window screenshot at full (native) resolution. Use this when `get_window_state` returned a resized image and you need to read small text, identify icons, or verify UI details. Corners, not a size: x1,y1 is the top-left and x2,y2 the bottom-right (x,y,width,height is also accepted and converted). Coordinates `x1, y1, x2, y2` are in the same pixel space as the screenshot returned by `get_window_state` (i.e. the resized image if `max_image_dimension` is active). The maximum zoom region width is 500 px in scaled-image coordinates. A 20% padding is automatically added on every side of the requested region so the target remains visible even if the caller's coordinates are slightly off. After a zoom, pass `from_zoom=true` to click/type_text to auto-translate coordinates back to full-window space. Coordinate actions return `screenshot_context_missing` when no current snapshot contains a screenshot owned by this session. `from_zoom` actions return `zoom_context_missing` when the zoom was never created or was replaced; call `get_window_state`, then `zoom`, again on the same connection. Windows-specific: `window_id` is the canonical addressing field (HWND). `pid` is preferred when available; otherwise the driver resolves the unique current snapshot owned by this session and window. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `pid` | `integer` | | Optional target process ID. When omitted, the unique current snapshot for this session and HWND is used. | | `window_id` | `integer` | required | HWND of the target window. | | `x1` | `number` | required | Left edge of the region (resized-image pixels). | | `x2` | `number` | required | Right edge of the region (resized-image pixels). | | `y1` | `number` | required | Top edge of the region (resized-image pixels). | | `y2` | `number` | required | Bottom edge of the region (resized-image pixels). | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"window_id":1,"x1":100,"y1":200,"x2":100,"y2":200} ``` --- # Click tool Click by element or pixel. Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `click` Effect: destructive. Platforms: macOS, Linux, Windows. **macOS** Click a target pid. Prefer `element_token` (from get_window_state): it identifies one snapshot element, works on backgrounded, minimized or off-Space windows, and moves no cursor or focus. Use `x, y` only for canvas / video / WebGL / custom-drawn surfaces missing from the AX tree. - element_token: AX action path. Tokens are scoped to (pid, window_id) and stale once the window is snapshotted again. - x, y: window-local pixels of the get_window_state screenshot, sent as CGEvent mouse events. Needs a visible window. `modifier` takes cmd/shift/option/ctrl. `button` defaults to left; on the AX path "right" maps to AXShowMenu and "middle" falls back to a pixel click at the element center. `action`: press (default), show_menu, pick, confirm, cancel, open. `from_zoom:true` translates zoom-image pixels after a `zoom` call. Pressing a popup button opens its menu and leaves it open (holding key focus) until dismissed; to choose an option use `set_value`. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `action` | `string` | | AX action. | | `button` | `"left" \| "right" \| "middle"` | | Mouse button. Default left. | | `capture_id` | `string` | | Optional capture ID from get_window_state or get_desktop_state. With x,y it is consumed atomically; stale or mismatched captures are refused. | | `count` | `integer` | | Click count (pixel path only). Default 1. | | `debug_image_out` | `string` | | Write a screenshot with a crosshair at (x, y) to this path. Needs window_id; not with from_zoom. | | `delivery_mode` | `"background" \| "foreground"` | | Default "background" (no fronting). "foreground" briefly fronts the window, acts, then restores the prior app; needs window_id and is required for modified clicks. Escalate in order: AX element_token, background pixel, foreground, checking state between rungs; a click has no read-back except list-row selection. | | `from_zoom` | `boolean` | | x and y are in the last zoom image for this pid. | | `modifier` | `string[]` | | Modifier keys: cmd, shift, option/alt, ctrl. | | `pid` | `integer` | | Target process ID. Optional with element_token (it carries the pid). | | `scope` | `"window" \| "desktop"` | | Default "window". "desktop": x,y with no pid/window_id are true screen pixels from get_desktop_state. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. | | `window_id` | `integer` | | Target window ID. Omit with element_token. | | `x` | `number` | | X in screenshot pixels (get_window_state PNG, or get_desktop_state PNG for a desktop target). | | `y` | `number` | | Y in screenshot pixels from the image selected by target. | **Linux** Click against a target pid. **Prefer `element_token` over pixel coordinates**: element_token works on backgrounded / hidden windows, surfaces a stable handle, and tells you what you're clicking via the cached AT-SPI element's role + label. Reach for `x, y` only when the target is a canvas / custom-drawn surface that doesn't appear in the AT-SPI tree. Provide either (window_id + x/y) or (pid + element_token). x/y are WINDOW-LOCAL pixels of window_id (the get_window_state screenshot frame); a point outside the window is refused (point_outside_window) and every pixel result reports the resolved window_point / screen_point. Background delivery first resolves the accessible under the point and fires its action (the result says `hit: <role> "<name>" action=...`; a file / list item is selected through its container: `selected: ...`), else presses the real virtual pointer there. A click that opens a popup menu names it (`popup: {window_id, bounds}`): pass that window_id to get_window_state to index its items. The token is stale once the next get_window_state of the same window replaces its snapshot: re-snapshot every turn before clicking. After a zoom call, pass from_zoom=true to auto-translate zoom-image coords back to full-window space. `count`: 1 (default), 2 for a double-click, 3 for a triple-click (selects a line / paragraph in editors); one press train with real double-click cadence, in background and foreground alike; there is no separate triple_click tool. button: "left" (default), "right", or "middle". Defaults to left so the field is fully back-compat. X11: routes through XSendEvent ButtonPress/Release with the matching button code. Native Wayland: only left-button is supported via the virtual-pointer protocol; right/middle return an error rather than silently degrading to left. `modifier` holds ctrl/shift/alt/super for the click on X11. Native Wayland refuses modified pointer clicks until its input protocol can carry keyboard modifier state. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `button` | `"left" \| "right" \| "middle"` | | Mouse button. Default: "left" (legacy back-compat). X11: routed via ButtonPress/Release with the matching evdev code. Native Wayland: only left-button is supported via the virtual-pointer protocol; right/middle return an error. | | `capture_id` | `string` | | Optional one-shot binding to the exact PNG returned by get_window_state or get_desktop_state. When present, x/y are interpreted in that capture and admitted before native dispatch. | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `count` | `integer` | | Number of clicks in one press train: 1 (default), 2 = double-click, 3 = triple-click (line/paragraph selection). There is no separate triple_click tool: pass count: 3 here. Minimum: 1. | | `cursor_id` | `string` | | Optional multi-cursor instance id. Default: 'default'. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `from_zoom` | `boolean` | | Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space. | | `modifier` | `string[]` | | Modifier keys held during the action: cmd, shift, option/alt, ctrl. | | `pid` | `integer` | | Target process ID. Required unless scope is "desktop" or element_token is supplied (the token carries the pid). | | `scope` | `"window" \| "desktop"` | `"window"` | Coordinate frame (default "window"). Pass "desktop" with x,y and no pid/window_id for a screen-absolute click in get_desktop_state coordinates. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | | Window id from list_windows. Required with x/y or element_index; optional with element_token (the token carries it). | | `x` | `number` | | Window-local pixel X of the target window's own get_window_state screenshot (0..screenshot_width). For get_desktop_state pixels pass scope:"desktop" (or coordinate_frame:"desktop"). | | `y` | `number` | | Window-local pixel Y of the target window's own get_window_state screenshot (0..screenshot_height); see x. | **Windows** Left-click against a target pid. **Prefer `element_token` over pixel coordinates**: element_token works on backgrounded / minimized / hidden / off-desktop windows, surfaces a stable handle that survives rebuilds, and tells you what you're clicking via the cached element's role + label. Reach for `x, y` only when the target is a canvas / video / WebGL / custom-drawn surface that doesn't appear in the UIA tree. Two addressing modes: - `element_token` (from the last `get_window_state` snapshot of that window): performs the UIA Invoke pattern on the cached element via PostMessage. No cursor move, no focus steal. Requires a prior `get_window_state(pid, window_id)` in this turn; the token is stale once the next snapshot of the same window replaces it. - `x`, `y` (window-local screenshot pixels, top-left origin of the PNG returned by `get_window_state`). On `delivery_mode:"background"` (the default), cua-driver FIRST does a UIA hit-test at the resolved screen position: if the deepest invokable element under that point exposes `InvokePattern`, it's invoked through the accessibility channel; same background-safe path as the `element_token` mode, no foreground swap, no visible flash. **This makes pixel clicks on UWP / WinUI3 / Win11 packaged apps work flash-free out of the box**: agents should default to `delivery_mode:"background"` even on XAML hosts whose CoreInput dispatcher drops raw PostMessage. The PostMessage(WM_LBUTTONDOWN/UP) fallback only runs when the UIA hit-test misses (canvas / video / WebGL / custom-drawn surfaces with no UIA peer), and on those targets `delivery_mode:"background"` returns a structured `background_unavailable` error if PostMessage is known to drop too: at which point you switch to `delivery_mode:"foreground"`. **Always try the default `background` click first and let that error be your signal: do NOT pass `foreground` preemptively because the target 'looks like' GTK/Chromium/etc. The driver decides when background is impossible; a guessed foreground click needlessly steals the user's focus.** `count: 2` posts two down/up pairs for a double-click. Pixel clicks need a visible on-screen window to anchor the coordinate conversion (errors with `pid X has no on-screen window` otherwise). Exactly one of `element_token` or (`x` AND `y`) must be provided. `pid` is required for window scope and omitted for desktop scope. After a `zoom` call, pass `from_zoom=true` to auto-translate zoom-image coords back to full-window space. Windows-only convenience: `button: "left"|"right"|"middle"` switches the mouse button (Swift exposes right-click as a separate `right_click` tool). `action: "expand"` on an `element_token` opens the element through UIA ExpandCollapsePattern (the dropdown half of an MSAA split button); other actions use the default invoke. `debug_image_out` isn't supported yet. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `action` | `string` | | Accessibility action for an element_token target. "expand" opens the element through UIA ExpandCollapsePattern, or clicks the dropdown half of an MSAA split button. Omit for the default invoke. | | `button` | `"left" \| "right" \| "middle"` | | Mouse button. Default "left". | | `capture_id` | `string` | | Optional ID from get_window_state or get_desktop_state. When present, x and y are admitted against that exact capture and the ID is consumed before native dispatch. | | `count` | `integer` | | Click count: 1 (single), 2 (double), 3 (triple). Default 1. Range: 1 to 3. | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `from_zoom` | `boolean` | | When true, x and y are pixel coordinates in the last `zoom` image for this pid. The driver maps them back to window coords. | | `modifier` | `string[]` | | Modifier keys held during the action: cmd, shift, option/alt, ctrl. | | `pid` | `integer` | | Target process ID for window scope. Optional with element_token (the token carries the pid). Omit with scope=desktop for screen-absolute coordinates from get_desktop_state. | | `scope` | `"window" \| "desktop"` | `"window"` | Coordinate frame (default "window"). Pass "desktop" with x,y and no pid/window_id for a screen-absolute click in get_desktop_state coordinates. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | | HWND of the target window. Omit when element_token is supplied (the token carries it). | | `x` | `number` | | X in window-local screenshot pixels: same space as the PNG get_window_state returns. Must be provided together with y. | | `y` | `number` | | Y in window-local screenshot pixels. Must be provided together with x. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `target` | `window target \| desktop target` | | Exact capture/input target selected independently for each action. `display_id="primary"` is the portable desktop target in this release. Platforms that cannot address another display reject it explicitly rather than silently changing coordinate spaces. | --- # Double-click and right-click tools Double-click and right-click by element or pixel. | Tool | Description | Platforms | | --- | --- | --- | | [`double_click`](#double_click) | Double-click at (x, y) or on an AX element identified by element_token. | macOS, Linux, Windows | | [`right_click`](#right_click) | Right-click against a target pid. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `double_click` Effect: destructive. Platforms: macOS, Linux, Windows. **macOS** Double-click at (x, y) or on an AX element identified by element_token. AX path (element_token provided): performs `AXOpen` when the element advertises it (Finder items, openable list rows/cells); otherwise resolves the element's on-screen center and falls back to a pixel double-click there. Pixel path (x, y provided): two down/up pairs ~80 ms apart at the given coordinates. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Best-effort-background ladder rung (default "background"). "background": inject without fronting or raising the target; no focus steal. "foreground": briefly front the target, act, then restore the prior frontmost; the explicit last resort when a background attempt didn't land. Re-call with "foreground" only for the action that needs it. | | `window_id` | `integer` | | CGWindowID. Omit when element_token is supplied (the token carries it). | | `x` | `number` | | Screen X coordinate (pixel path). | | `y` | `number` | | Screen Y coordinate (pixel path). | **Linux** Double-click at (x,y) or an element_token (AT-SPI bounds). Background delivery on X11 sends real button events from the session's virtual master pointer (path mpx_pointer); a point covered by another application's window is refused (target_occluded), one covered by this app's own dialog is retargeted to it (retargeted_to). No focus steal. Provide either (window_id + x/y) or (pid + element_token). After a zoom call, pass from_zoom=true to auto-translate zoom-image coords. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `cursor_id` | `string` | | Optional multi-cursor instance id. Default: 'default'. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `from_zoom` | `boolean` | | Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space. | | `window_id` | `integer` | | Window id from list_windows. Required with x/y or element_index; optional with element_token (the token carries it). | | `x` | `number` | | Window-local pixel X of the target window's own get_window_state screenshot (0..screenshot_width). For get_desktop_state pixels pass scope:"desktop" (or coordinate_frame:"desktop"). | | `y` | `number` | | Window-local pixel Y of the target window's own get_window_state screenshot (0..screenshot_height); see x. | **Windows** Double-click against a target pid. Two addressing modes: - `element_token` (from the last `get_window_state` snapshot of that window): synthesizes a stamped pixel double-click at the element's cached on-screen center via PostMessage. (Windows has no AXOpen analogue; Swift's AXOpen-first path falls through to the same pixel recipe when the element doesn't advertise AXOpen, so the user-visible behavior matches.) - `x`, `y` (window-local screenshot pixels, top-left origin of the PNG returned by `get_window_state`): posts two WM_LBUTTONDOWN/UP pairs in quick succession to the deepest child window at that point. Exactly one of `element_token` or (`x` AND `y`) must be provided. `pid` is required in both modes. The macOS-only `modifier` field is accepted for parity (no-op on Windows: PostMessage doesn't propagate modifier-key state). **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `from_zoom` | `boolean` | | After a zoom call, pass true to translate zoom-image coords back to window space. | | `modifier` | `string[]` | | Modifier keys held during the action: cmd, shift, option/alt, ctrl. | | `window_id` | `integer` | | HWND for the target window. Omit when element_token is supplied (the token carries it). | | `x` | `number` | | X in window-local screenshot pixels. Must be provided together with y. | | `y` | `number` | | Y in window-local screenshot pixels. Must be provided together with x. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `pid` | `integer` | required | Target process ID. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"pid":1} ``` ## `right_click` Effect: destructive. Platforms: macOS, Linux, Windows. **macOS** Right-click against a target pid. Two addressing modes: - `element_token` (from the last `get_window_state` snapshot): performs `AXShowMenu` on the cached element. Pure AX RPC, works on backgrounded / hidden windows, no cursor move or focus steal. Requires a prior `get_window_state(pid, window_id)` in this turn. - `x`, `y`: synthesizes `rightMouseDown` / `rightMouseUp` CGEvent pair posted to the pid. Driver converts image-pixel → screen-point internally. `modifier` forces the CGEvent path (AX actions don't propagate modifier keys). Exactly one of `element_token` or (`x` AND `y`) must be provided. `pid` always required. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Best-effort-background ladder rung (default "background"). "background": inject without fronting or raising the target; no focus steal. "foreground": briefly front the target, act, then restore the prior frontmost; the explicit last resort when a background attempt didn't land. Re-call with "foreground" only for the action that needs it. | | `modifier` | `string[]` | | Modifier keys held during the right-click: cmd/shift/option/ctrl. Pixel path only. | | `window_id` | `integer` | | CGWindowID. Omit when element_token is supplied (the token carries it). | | `x` | `number` | | X in window-local screenshot pixels. Must be provided together with y. | | `y` | `number` | | Y in window-local screenshot pixels. Must be provided together with x. | **Linux** Right-click at (x,y) or an element_token (AT-SPI bounds). Background delivery on X11 sends real button events from the session's virtual master pointer (path mpx_pointer); a point covered by another application's window is refused (target_occluded), one covered by this app's own dialog is retargeted to it (retargeted_to), one outside the window is refused (point_outside_window; x/y are window-local pixels, the result reports window_point / screen_point). No focus steal. When a context menu opens, the result names it (`popup: {window_id, bounds, title}`): call get_window_state(pid, window_id=<that id>) to index its menu items and click them by element_token. Provide either (window_id + x/y) or (pid + element_token). After a zoom call, pass from_zoom=true to auto-translate zoom-image coords. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `cursor_id` | `string` | | Optional multi-cursor instance id. Default: 'default'. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `from_zoom` | `boolean` | | Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space. | | `modifier` | `string[]` | | Modifier keys held during the action: cmd, shift, option/alt, ctrl. | | `window_id` | `integer` | | Window id from list_windows. Required with x/y or element_index; optional with element_token (the token carries it). | | `x` | `number` | | Window-local pixel X of the target window's own get_window_state screenshot (0..screenshot_width). For get_desktop_state pixels pass scope:"desktop" (or coordinate_frame:"desktop"). | | `y` | `number` | | Window-local pixel Y of the target window's own get_window_state screenshot (0..screenshot_height); see x. | **Windows** Right-click against a target pid. Two addressing modes: - `element_token` (from the last `get_window_state` snapshot of that window): posts WM_RBUTTONDOWN/UP at the element's cached on-screen center via PostMessage. (Windows has no AXShowMenu analogue wired up yet; Swift's AX-action path falls through to the same pixel recipe on non-advertising elements, so user-visible behavior matches.) - `x`, `y` (window-local screenshot pixels, top-left origin of the PNG returned by `get_window_state`): posts WM_RBUTTONDOWN/UP to the deepest child window at that point. Exactly one of `element_token` or (`x` AND `y`) must be provided. `pid` is required in both modes. `modifier` is accepted for parity (no-op on Windows: PostMessage doesn't propagate modifier-key state). **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `from_zoom` | `boolean` | | After a zoom call, pass true to translate zoom-image coords back to window space. | | `modifier` | `string[]` | | Modifier keys held during the action: cmd, shift, option/alt, ctrl. | | `window_id` | `integer` | | HWND for the target window. Omit when element_token is supplied (the token carries it). | | `x` | `number` | | X in window-local screenshot pixels. Must be provided together with y. | | `y` | `number` | | Y in window-local screenshot pixels. Must be provided together with x. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `pid` | `integer` | required | Target process ID. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"pid":1} ``` --- # Pointer tools Drag, scroll, move the pointer, and press or release buttons. | Tool | Description | Platforms | | --- | --- | --- | | [`drag`](#drag) | Press-drag-release gesture from (from_x, from_y) to (to_x, to_y) in window-local screenshot pixels: the same space get_window_state returns. | macOS, Linux, Windows | | [`scroll`](#scroll) | Scroll the target pid. | macOS, Linux, Windows | | [`move_cursor`](#move_cursor) | Move a cursor to (x, y). | macOS, Linux, Windows | | [`mouse_button_down`](#mouse_button_down) | Press and hold a mouse button at (x,y) via background X11 delivery. | Linux | | [`mouse_button_up`](#mouse_button_up) | Release a previously-held mouse button via background X11 delivery. | Linux | | [`mouse_drag`](#mouse_drag) | Move a previously-held mouse button to a new point via background X11 delivery. | Linux | | [`parallel_mouse_drag`](#parallel_mouse_drag) | Run multiple mouse drag gestures concurrently via Linux MPX/XI2 virtual master pointers. | Linux | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `drag` Effect: destructive. Platforms: macOS, Linux, Windows. **macOS** Press-drag-release gesture from (from_x, from_y) to (to_x, to_y) in window-local screenshot pixels: the same space get_window_state returns. Top-left origin of the target's window. Use for: marquee/lasso selection, drag-and-drop, resizing via a handle, scrubbing a slider, repositioning a panel. `duration_ms` (default 500) is the wall-clock budget for the path between mouse-down and mouse-up; `steps` (default 20) is the number of intermediate mouseDragged events linearly interpolated along the path. Increase both for slower, more human drags; decrease for snap gestures. `modifier` keys (cmd/shift/option/ctrl) are held across the entire gesture. When `from_zoom` is true, coordinates are in the last zoom image for this pid; the driver maps them back to window coordinates before dispatching. macOS has no background drag: a window-scoped drag needs delivery_mode:"foreground" and window_id. It briefly fronts the exact window, moves the physical pointer along the path, then restores the prior frontmost app. Without delivery_mode:"foreground" the call is refused with background_unavailable and nothing is sent. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `button` | `"left" \| "right" \| "middle"` | | Mouse button used for the drag. Default: left. | | `delivery_mode` | `"background" \| "foreground"` | | Window-scoped drag on macOS supports only "foreground": briefly front the exact window (window_id required), drive the physical pointer through the gesture, then restore the prior frontmost app. The default "background" is refused with background_unavailable because macOS has no background drag route. Desktop scope ignores this field. | | `duration_ms` | `integer` | | Wall-clock duration of the drag path between mouseDown and mouseUp. Default: 500. Range: 0 to 10000. | | `from_x` | `number` | required | Drag-start X in window-local screenshot pixels. Top-left origin. | | `from_y` | `number` | required | Drag-start Y in window-local screenshot pixels. Top-left origin. | | `from_zoom` | `boolean` | | When true, coordinates are in the last zoom image for this pid; driver maps back to window coordinates. | | `modifier` | `string[]` | | Modifier keys held across the entire gesture: cmd/shift/option/ctrl. | | `pid` | `integer` | | Target process ID. | | `scope` | `"window" \| "desktop"` | `"window"` | Use desktop with no pid/window_id for native get_desktop_state screenshot coordinates. | | `steps` | `integer` | | Number of intermediate mouseDragged events linearly interpolated along the path. Default: 20. Range: 1 to 200. | | `to_x` | `number` | required | Drag-end X in window-local screenshot pixels. | | `to_y` | `number` | required | Drag-end Y in window-local screenshot pixels. | | `window_id` | `integer` | | CGWindowID for the window the pixel coordinates were measured against. Optional only when pid owns exactly one eligible top-level window; otherwise the action refuses with ambiguous_window_target. | **Linux** Press-drag-release gesture from (from_x, from_y) to (to_x, to_y) in window-local screenshot pixels. Background delivery on X11 is one real held gesture on the session's virtual master pointer (path mpx_pointer: press, interpolated motion, release); it lands on GTK/VCL/Qt/Chromium; a point covered by another application's window is refused (target_occluded), one covered by this app's own dialog is retargeted to it. Headless servers fall back to XSendEvent. duration_ms (default 500), steps (default 20). No focus steal. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `button` | `"left" \| "right" \| "middle"` | | Mouse button. Default "left". | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `cursor_id` | `string` | | Optional multi-cursor instance id. Default: 'default'. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `duration_ms` | `integer` | | Total drag duration. Default: 500. Range: 0 to 10000. | | `from_x` | `number` | required | Drag start X: window-local pixels of the target window's own get_window_state screenshot; pass scope:"desktop" for get_desktop_state pixels. | | `from_y` | `number` | required | Drag start Y (same frame as from_x). | | `from_zoom` | `boolean` | | Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space. | | `modifier` | `string[]` | | Modifier keys held during the action: cmd, shift, option/alt, ctrl. | | `pid` | `integer` | | Target process ID. Required unless scope is "desktop". | | `scope` | `"window" \| "desktop"` | `"window"` | Use "desktop" with no pid/window_id for get_desktop_state screen coordinates. Default "window". | | `steps` | `integer` | | Intermediate MotionNotify events. Default: 20. Range: 1 to 200. | | `to_x` | `number` | required | Drag end X (same frame as from_x). | | `to_y` | `number` | required | Drag end Y (same frame as from_x). | | `window_id` | `integer` | | Target window XID. Required. | **Windows** Press-drag-release gesture from (from_x, from_y) to (to_x, to_y) in window-local screenshot pixels. duration_ms (default 500) is the wall-clock budget; steps (default 20) interpolates intermediate WM_MOUSEMOVE events along the path. No focus steal. After a zoom call, pass from_zoom=true. Drags that start on a window caption / title bar or resize border (i.e. moving or resizing the window itself) cannot be delivered in the background, the OS move/resize loop needs real pointer input, so they return background_unavailable; re-issue those with delivery_mode:"foreground". **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `button` | `"left" \| "right" \| "middle"` | | Mouse button. Default "left". | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `duration_ms` | `integer` | | Wall-clock duration of drag path. Default: 500. Range: 0 to 10000. | | `from_x` | `number` | required | Drag-start X in window-local screenshot pixels. | | `from_y` | `number` | required | Drag-start Y in window-local screenshot pixels. | | `from_zoom` | `boolean` | | When true, coordinates are in the last zoom image for this pid. | | `modifier` | `string[]` | | Modifier keys held during the action: cmd, shift, option/alt, ctrl. | | `pid` | `integer` | | Target process ID. | | `scope` | `"window" \| "desktop"` | | Use desktop with no pid/window_id for screen-absolute coordinates. | | `steps` | `integer` | | Number of intermediate WM_MOUSEMOVE events. Default: 20. Range: 1 to 200. | | `to_x` | `number` | required | Drag-end X in window-local screenshot pixels. | | `to_y` | `number` | required | Drag-end Y in window-local screenshot pixels. | | `window_id` | `integer` | | Target window handle (HWND). Optional: driver picks frontmost window of pid when omitted. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `target` | `window target \| desktop target` | | Preferred per-call target: an exact window (`kind="window"`, `pid`, `window_id`) or the primary desktop (`kind="desktop"`, `display_id="primary"`). | **Example arguments** ```json {"from_x":100,"from_y":200,"to_x":100,"to_y":200} ``` ## `scroll` Effect: mutating. Platforms: macOS, Linux, Windows. **macOS** Scroll the target pid. - Targeted wheel: pass `element_token` (preferred) or window-local `x, y`. Sends a real wheel event at that point, so it scrolls whatever is under it. The only way to scroll nested `overflow:auto` regions in web views. - Keystroke: no target, just pid + direction. PageDown/PageUp (by='page') or arrows (by='line') on the focused scroller. Electron/Chromium windows refuse background delivery; pass `delivery_mode:"foreground"`. `amount` is wheel notches (targeted) or key repetitions (keystroke). **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `amount` | `integer` | | Wheel notches or key repetitions, clamped to the maximum. Default 3. Range: 1 to 50. | | `delivery_mode` | `"background" \| "foreground"` | | Default "background": no fronting or focus steal. "foreground": briefly front the window, act, restore the prior app; last resort when background did not land. | | `pid` | `integer` | | Target process ID. Required unless scope is "desktop" or element_token is supplied. | | `scope` | `"window" \| "desktop"` | `"window"` | "desktop" with x,y and no pid/window_id uses get_desktop_state coordinates. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. | | `window_id` | `integer` | | Window ID. Required with x/y; optional with element_token. | | `x` | `number` | | Window-local screenshot X (get_window_state PNG). With y, wheel-scrolls at that point; needs window_id. | | `y` | `number` | | Window-local screenshot Y. | **Linux** Scroll the target pid's focused region via XSendEvent Button4/5. direction required; by defaults to line, amount defaults to 3. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `amount` | `integer` | | Number of scroll steps. Default: 3. Range: 1 to 50. | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `cursor_id` | `string` | | Optional multi-cursor instance id. Default: 'default'. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `pid` | `integer` | | Target process ID. Required unless scope is "desktop" or element_token is supplied (the token carries the pid). | | `scope` | `"window" \| "desktop"` | `"window"` | Use "desktop" with x,y and no pid/window_id for get_desktop_state screen coordinates. Default "window". | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | | Window id from list_windows. Required with x/y; optional with element_token (the token carries it). | | `x` | `number` | | Window-local screenshot-pixel X of the scroll target. Pass with y and without element_token. | | `y` | `number` | | Window-local screenshot-pixel Y of the scroll target. Pass with x and without element_token. | **Windows** Scroll the target pid's focused region. Windows transport: WM_VSCROLL / WM_HSCROLL posted to the window; the same events the OS sends when scrollbars or trackpads scroll, so any window that handles scrollbars correctly responds to this. Swift uses synthesized keystrokes (PageDown / arrow keys) via auth-signed SLEventPostToPid; both approaches reach backgrounded windows. Mapping: `by: "page"` → SB_PAGEDOWN/UP/LEFT/RIGHT × amount; `by: "line"` → SB_LINEDOWN/UP/LEFT/RIGHT × amount. Note: `element_token` is accepted for cross-platform parity but currently no-op on Windows (UIA SetFocus not wired up yet; same caveat as `press_key`). **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `amount` | `integer` | | Number of scroll ticks. Default 3. Range: 1 to 50. | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `pid` | `integer` | | Target process ID for window scope. Optional with element_token (the token carries the pid). Omit with scope=desktop for screen-absolute coordinates from get_desktop_state. | | `scope` | `"window" \| "desktop"` | `"window"` | Use "desktop" with x,y and no pid/window_id for screen-absolute coordinates from get_desktop_state. Default "window". | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | | HWND of the target window. Omit when element_token is supplied; otherwise auto-resolves the pid's first visible window. | | `x` | `number` | | With pid/window_id: window-local screenshot X used to target a nested scroll surface in foreground mode. Without pid/window_id: screen-absolute X for desktop scope. Must be paired with y. | | `y` | `number` | | With pid/window_id: window-local screenshot Y used to target a nested scroll surface in foreground mode. Without pid/window_id: screen-absolute Y for desktop scope. Must be paired with x. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `by` | `"line" \| "page"` | | Scroll granularity. Default: line. | | `direction` | `"up" \| "down" \| "left" \| "right"` | required | Scroll direction. | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `target` | `window target \| desktop target` | | Preferred per-call target: an exact window (`kind="window"`, `pid`, `window_id`) or the primary desktop (`kind="desktop"`, `display_id="primary"`). | **Example arguments** ```json {"direction":"up"} ``` ## `move_cursor` Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. **macOS** Move a cursor to (x, y). In window scope (default), moves only the agent cursor overlay. With scope=desktop, moves the real OS pointer in native get_desktop_state screenshot coordinates. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `scope` | `"window" \| "desktop"` | `"window"` | "window" (default) moves only the agent cursor overlay; "desktop" moves the real OS pointer. | | `x` | `number` | required | Destination X. Window scope: screen points for the agent cursor overlay. Desktop scope: native get_desktop_state screenshot pixels. | **Linux** Move the synthetic agent cursor without changing the user's pointer. Only an explicit scope=desktop request moves the real OS pointer in get_desktop_state coordinates. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `scope` | `"window" \| "desktop"` | `"window"` | "window" (default) moves only the agent cursor overlay; "desktop" moves the real OS pointer. | | `x` | `number` | required | Destination X. Window scope: screen coordinates of the agent cursor overlay. Desktop scope: get_desktop_state screenshot pixels. | **Windows** Move the agent cursor overlay to (x, y). Does NOT move the real mouse cursor. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `scope` | `"window" \| "desktop"` | | desktop moves the real OS pointer; window moves only the agent overlay. | | `x` | `number` | required | Destination X. Window scope: screen coordinates of the agent cursor overlay. Desktop scope: get_desktop_state screenshot pixels. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `cursor_id` | `string` | | Cursor instance to move. Default: 'default'. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `target` | `window target \| desktop target` | | Preferred per-call target: an exact window (`kind="window"`, `pid`, `window_id`) or the primary desktop (`kind="desktop"`, `display_id="primary"`). | | `y` | `number` | required | Destination Y, in the same space as x. | **Example arguments** ```json {"x":100,"y":200} ``` ## `mouse_button_down` Press and hold a mouse button at (x,y) via background X11 delivery. Does not release the button; pair with mouse_drag / mouse_button_up. Returns the current held-button state. Effect: destructive. Platforms: Linux. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `button` | `"left" \| "right" \| "middle"` | | Mouse button. Default "left". | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `cursor_id` | `string` | | Optional multi-cursor instance id. Default: 'default'. | | `from_zoom` | `boolean` | | Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space. | | `pid` | `integer` | required | Target process ID. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. When both are present, session takes precedence over cursor_id. | | `window_id` | `integer` | required | Window id from list_windows. | | `x` | `number` | required | Window-local pixel X of the target window's own get_window_state screenshot (0..screenshot_width). For get_desktop_state pixels pass scope:"desktop" (or coordinate_frame:"desktop"). | | `y` | `number` | required | Window-local pixel Y of the target window's own get_window_state screenshot (0..screenshot_height); see x. | **Example arguments** ```json {"pid":1,"window_id":1,"x":100,"y":200} ``` ## `mouse_button_up` Release a previously-held mouse button via background X11 delivery. If x/y are omitted, releases at the last held position. Returns the current held-button state. Effect: destructive. Platforms: Linux. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `cursor_id` | `string` | | Optional multi-cursor instance id. Default: 'default'. | | `from_zoom` | `boolean` | | Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space. | | `pid` | `integer` | | Optional process ID; must match the held button's target. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. When both are present, session takes precedence over cursor_id. | | `window_id` | `integer` | | Optional window id; must match the held button's target. | | `x` | `number` | | Window-local pixel X of the target window's own get_window_state screenshot (0..screenshot_width). For get_desktop_state pixels pass scope:"desktop" (or coordinate_frame:"desktop"). | | `y` | `number` | | Window-local pixel Y of the target window's own get_window_state screenshot (0..screenshot_height); see x. | ## `mouse_drag` Move a previously-held mouse button to a new point via background X11 delivery. Requires an active mouse_button_down state; does not release the button. Returns the updated held-button state. Effect: destructive. Platforms: Linux. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `cursor_id` | `string` | | Optional multi-cursor instance id. Default: 'default'. | | `duration_ms` | `integer` | | Total drag duration. Default: 500. Range: 0 to 10000. | | `from_zoom` | `boolean` | | Set true after a zoom call to auto-translate zoom-image pixel coordinates back to full-window space. | | `pid` | `integer` | | Optional process ID; must match the held button's target. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. When both are present, session takes precedence over cursor_id. | | `steps` | `integer` | | Intermediate MotionNotify events. Default: 20. Range: 1 to 200. | | `window_id` | `integer` | | Optional window id; must match the held button's target. | | `x` | `number` | required | Window-local pixel X of the target window's own get_window_state screenshot (0..screenshot_width). For get_desktop_state pixels pass scope:"desktop" (or coordinate_frame:"desktop"). | | `y` | `number` | required | Window-local pixel Y of the target window's own get_window_state screenshot (0..screenshot_height); see x. | **Example arguments** ```json {"x":100,"y":200} ``` ## `parallel_mouse_drag` Run multiple mouse drag gestures concurrently via Linux MPX/XI2 virtual master pointers. Each drag item runs on its own session-scoped master pointer (true same-window concurrent draws on X11). Each item presses once, glides continuously through its whole path, and releases once: one smooth held drag, not a chain of clicks. A path is given either as a straight segment (from_x/from_y → to_x/to_y) or as a function `fn` = y(x) sampled over [x_from, x_to] in window-local pixels (e.g. fn:"x" is a diagonal, fn:"300+120*sin(x/40)" a sine wave). Functions support + - * / ^, sin/cos/tan, sqrt, abs, exp, ln, pi, e. Effect: destructive. Platforms: Linux. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `drags` | `object[]` | required | Two or more drag gestures to run concurrently, each on its own virtual master pointer. Items: 2 to any. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"drags":[{"session":"<session>","window_id":1},{"session":"<session>","window_id":1}]} ``` --- # Typing tools Type text into a field or window. Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `type_text` Effect: destructive. Platforms: macOS, Linux, Windows. **macOS** Insert text via `AXSetAttribute(kAXSelectedText)` into the element given by `element_token`, or the pid's focused element. Does not press keys: use `press_key` / `hotkey` for Return, Escape, arrows. If AX insertion is unsupported (Chromium/Electron), it falls back to CGEvent typing when the route fits the transport budget; longer routes are refused with a safe chunk size. Web content (browser tabs, Slack, VS Code): AXValue is not proof the DOM saw the text, so the result is `effect:"unverifiable"` and Electron web content refuses background delivery. For a browser tab use the `page` tool. For an embedded web view pass `x, y` (no element_token) to pixel-click the field then type in one call; a pixel click will not reliably open a closed control, so AX-press it open first. Confirm from the screenshot, and use `delivery_mode:"foreground"` only if background still drops the text. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delay_ms` | `integer` | | Ms between characters in the CGEvent fallback. Default 30. Range: 0 to 200. | | `delivery_mode` | `"background" \| "foreground"` | | Default "background": AX insert, then CGEvent keys, no focus steal. "foreground": front the window, type, restore; last resort for surfaces (WhatsApp/Catalyst) where background keys do not land. | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `pid` | `integer` | | Target process ID. | | `scope` | `"window" \| "desktop"` | `"window"` | "desktop" with no pid/window_id types into the frontmost app. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. | | `text` | `string` | required | Text to insert. | | `window_id` | `integer` | | Window ID. Omit with element_token. | | `x` | `number` | | Screenshot-pixel X of the field (get_window_state PNG, as in click). With y and no element_token, pixel-clicks to focus, then types. | | `y` | `number` | | Screenshot-pixel Y (see x). | **Linux** Type text to a window via XSendEvent (KeyPress/KeyRelease). No focus steal. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `pid` | `integer` | | Target process ID. Omit with scope "desktop" to type into the focused application. | | `scope` | `"window" \| "desktop"` | `"window"` | Use "desktop" with no pid to type into the focused application. Default "window". | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `text` | `string` | required | Text to type. | | `window_id` | `integer` | | Window id from list_windows. Required with x/y; optional with element_token (the token carries it). | | `x` | `number` | | Pixel X of the field to type into: the element px action form. Pass x,y (no element_token) and the tool pixel-clicks there to establish real renderer focus, then types. Use for Chromium/Electron inputs the AX path can't reach. Window-local pixels of the target window's own get_window_state screenshot by default (same convention as click); for get_desktop_state pixels pass scope:"desktop" (or coordinate_frame:"desktop"): a bare pid+x/y is otherwise misinterpreted as window-local and can focus the wrong widget. | | `y` | `number` | | Pixel Y of the field (see x). | **Windows** Insert text into the target pid via character-by-character PostMessage(WM_CHAR) to the focused window. No focus steal. Special keys (Return, Escape, arrows, Tab) go through `press_key` / `hotkey`: they are not text. **Routing on Windows.** When the target's owning EXE or top-level window class identifies it as a XAML / WinUI3 / UWP host (modern Notepad, Calculator, Photos, Settings, etc.), the tool requires `element_token` and routes through UI Automation's `ValuePattern.SetValue`: same backend as the `set_value` tool. PostMessage WM_CHAR doesn't reach those hosts (their CoreInput dispatcher only consumes events from the system input queue), so the fallback path silently dropped chars. If you call `type_text(pid, text)` on a XAML host without `element_token`, the tool returns an actionable error pointing you at `get_window_state` first. Native ConsoleHost on Windows ARM64 is hard-refused because it can accept synthesized Unicode events without delivering them; use a process or PTY setup channel instead. Legacy Win32 apps still use the PostMessage path, preserving the no-focus-steal property. `delay_ms` (0–200, default 30) spaces successive characters on the PostMessage path so autocomplete and IME can keep up. Ignored on the UIA path (SetValue is atomic). **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delay_ms` | `integer` | | Milliseconds between characters. Default 30. Range: 0 to 200. | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `element_token` | `string` | | Opaque element handle from get_window_state. When supplied, type_text writes through UIA ValuePattern and reads that exact element back by handle. The shared ActionResult is confirmed only when the complete expected value is synchronously visible. If SetValue succeeds but read-back is stale or unavailable, the result is unverifiable with no escalation; take a fresh snapshot before retrying because deferred providers may publish only after this call returns. | | `pid` | `integer` | | Target process ID. | | `scope` | `"window" \| "desktop"` | | Use desktop with no pid/window_id to type into the current foreground application. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `text` | `string` | required | Text to insert at the focused element's cursor. | | `window_id` | `integer` | | HWND of the target window. Omit when element_token is supplied (the token carries it). | | `x` | `number` | | Window-local screenshot-pixel X of the field to type into: the element px action form. Pass x,y (no element_token) and the tool pixel-clicks there to establish real renderer focus, then types. Use for Chromium/Electron inputs the UIA/WM_CHAR path can't reach. Read straight off the get_window_state PNG, same convention as click. | | `y` | `number` | | Window-local screenshot-pixel Y of the field (see x). | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `target` | `window target \| desktop target` | | Preferred per-call target: an exact window (`kind="window"`, `pid`, `window_id`) or the primary desktop (`kind="desktop"`, `display_id="primary"`). | **Example arguments** ```json {"text":"<text>"} ``` --- # Key and shortcut tools Press keys and keyboard shortcuts. | Tool | Description | Platforms | | --- | --- | --- | | [`press_key`](#press_key) | Press and release one key. | macOS, Linux, Windows | | [`hotkey`](#hotkey) | Press a key combination: e.g. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `press_key` Effect: destructive. Platforms: macOS, Linux, Windows. **macOS** Press and release one key. `delivery_mode` as in click/type_text. - `background` (default): posts to the pid without raising the window; with `element_token` it focuses that element first. - `foreground`: fronts the exact window (needs `window_id`), sends a real HID key transition (Chromium content, inline editors, menu equivalents), then restores the prior app. Confirmed only when a native AX value/selection read-back changes; otherwise `effect:"unverifiable"` (not a delivery failure). Keys: return, tab, escape, up/down/left/right, space, delete, home, end, pageup, pagedown, f1-f12, any letter or digit. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Default "background": no fronting or focus steal. "foreground": briefly front the window, act, restore the prior app; last resort when background did not land. | | `key` | `string` | required | Key name. | | `modifiers` | `string[]` | | cmd, shift, option/alt, ctrl, fn. | | `pid` | `integer` | | Target process ID. | | `scope` | `"window" \| "desktop"` | `"window"` | "desktop" with no pid/window_id sends to the frontmost app. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. | | `window_id` | `integer` | | Target window; required for foreground. Does not raise by itself. | | `x` | `number` | | Screenshot-pixel X: pixel-click to focus, then send the key. With y, no element_token. | | `y` | `number` | | Screenshot-pixel Y (see x). | **Linux** Press a key via XSendEvent to a window. No focus steal. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `coordinate_frame` | `"window" \| "desktop"` | | Frame of x/y (and from_x/from_y/to_x/to_y). Default "window": window-local screenshot pixels as returned by get_window_state. "desktop": full-screen pixels as returned by get_desktop_state, translated to the target window. Passing scope:"desktop" together with pid/window_id means the same thing. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `key` | `string` | required | Key name: enter/return, tab, escape, space, backspace, delete, insert, home, end, pageup, pagedown, up, down, left, right, f1-f12, or any single ASCII character. | | `modifiers` | `string[]` | | Modifier keys held while the key is pressed: ctrl, shift, alt, super. | | `pid` | `integer` | | Target process ID. Omit with scope "desktop" to send the key to the focused application. | | `scope` | `"window" \| "desktop"` | `"window"` | Use "desktop" with no pid to send the key to the focused application. Default "window". | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | | Window id from list_windows. Required with x/y or element_index; optional with element_token (the token carries it). | | `x` | `number` | | Pixel X: the element px action form: pixel-click there to focus, then send the key. Use when the key must go to a Chromium/Electron surface the AX path can't focus. Pass with y, no element_token. Window-local pixels by default (same convention as click); for get_desktop_state pixels pass scope:"desktop" (or coordinate_frame:"desktop"). | | `y` | `number` | | Pixel Y (see x). | **Windows** Press and release a single key, delivered directly to the target pid's top-level window via PostMessage(WM_KEYDOWN/WM_KEYUP). The target does NOT need to be frontmost: no focus steal. Optional `window_id` selects a specific HWND when the pid owns more than one; without it the first visible top-level window for the pid is used. Key vocabulary: return, tab, escape, up/down/left/right, space, delete, home, end, pageup, pagedown, f1-f12, plus any letter or digit. Optional `modifiers` array takes ctrl/shift/alt/win. For true combinations (ctrl+c), `hotkey` is a cleaner surface. `element_token` focuses the cached UIA element before sending the key; the top-level window remains backgrounded when delivery_mode is background. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `key` | `string` | required | Key name (return, tab, escape, up, down, left, right, space, delete, home, end, pageup, pagedown, f1-f12, letter, digit). | | `modifiers` | `string[]` | | Optional modifier names held while the key is pressed (ctrl/shift/alt/win). | | `pid` | `integer` | | Target process ID. | | `scope` | `"window" \| "desktop"` | | Use desktop with no pid/window_id to send the key to the current foreground application. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `window_id` | `integer` | | HWND for the target window. Omit when element_token is supplied; otherwise auto-resolves the pid's first visible window. | | `x` | `number` | | Window-local screenshot-pixel X: the element px action form: pixel-click there to focus, then send the key. Use when the key must go to a Chromium/Electron surface the UIA path can't focus. Pass with y, no element_token. | | `y` | `number` | | Window-local screenshot-pixel Y (see x). | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `target` | `window target \| desktop target` | | Preferred per-call target: an exact window (`kind="window"`, `pid`, `window_id`) or the primary desktop (`kind="desktop"`, `display_id="primary"`). | **Example arguments** ```json {"key":"<key>"} ``` ## `hotkey` Effect: destructive. Platforms: macOS, Linux, Windows. **macOS** Press a key combination: e.g. `["cmd", "c"]` for Copy, `["cmd", "shift", "4"]` for screenshot selection. Follows the same `delivery_mode` ladder as click/type_text: it does NOT raise the window by default: • `background` (default): post the combo to the target pid WITHOUT fronting or raising it; uses the macOS 14+ auth-message envelope so Chromium/Electron accept it as trusted live input. With an AX target, focus that exact element first. No top-level focus steal. `window_id` here only targets the combo; it does not raise. • `foreground`: briefly front the window (NSMenu path, < 1 ms via SLPSSetFrontProcessWithOptions) so native menu key-equivalents (Cmd+Z, Cmd+W) dispatch, then restore the prior frontmost; the explicit escalation for menu-bar shortcuts on non-Chromium apps that ignore a background combo. With an AX target or x,y, the focused field receives the chord through the foreground HID queue (needed by native Chromium fields such as the omnibox). Requires window_id. A combo is never driver-verifiable (no read-back) → effect:"unverifiable"; confirm via screenshot. NOTE: a keyboard combo does NOT focus a text field; to type into a backgrounded Electron input, establish real renderer focus with a PIXEL click first, then `type_text`. If an app only accepts paste, call `clipboard_write`, then `clipboard_read` and verify its types (and text when applicable) before selecting or replacing editor content; only then send Cmd+V. Recognized modifiers: cmd/command, shift, option/alt, ctrl/control, fn. Non-modifier keys use the same vocabulary as `press_key`. Order: modifiers first, one non-modifier last. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Best-effort-background ladder rung (default "background"). "background": inject without fronting or raising the target; no focus steal. "foreground": briefly front the target, act, then restore the prior frontmost; the explicit last resort when a background attempt didn't land. Re-call with "foreground" only for the action that needs it. | | `keys` | `string[]` | required | Modifier(s) and one non-modifier key, e.g. ["cmd", "c"]. Items: 2 to any. | | `pid` | `integer` | | Target process ID. | | `scope` | `"window" \| "desktop"` | `"window"` | Use desktop with no pid/window_id to send the chord to the frontmost application. | | `window_id` | `integer` | | Target window. Required for delivery_mode:"foreground" (the NSMenu activation needs a window). Does NOT itself raise the window: raising is gated on delivery_mode. | | `x` | `number` | | Screenshot-pixel X: the element px action form: pixel-click there to focus, then send the combo (so e.g. Cmd+V pastes into that field). Pass with y. Use for Chromium/Electron surfaces the background combo can't reach. | | `y` | `number` | | Screenshot-pixel Y (see x). | **Linux** Press a combination of keys simultaneously, e.g. ["ctrl","c"] for Copy. Delivered to the target window without focus steal (real key events from a virtual master keyboard on X11); target does NOT need to be frontmost. Window-manager chords cannot reach the WM that way: alt+F4 on a window target closes that window through the WM (_NET_CLOSE_WINDOW, effect confirmed when it unmaps); super+*, alt+tab, ctrl+alt+* and other WM bindings are refused with code wm_chord_unavailable; use bring_to_front / list_windows / kill_app or delivery_mode:"foreground" instead. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `keys` | `string[]` | required | Modifier(s) + one non-modifier key, e.g. ["ctrl","c"]. Items: 2 to any. | | `pid` | `integer` | | Target process ID. Omit with scope "desktop" to send the chord to the focused application. | | `scope` | `"window" \| "desktop"` | `"window"` | Use "desktop" with no pid to send the chord to the focused application. Default "window". | | `window_id` | `integer` | | Window id from list_windows. Required with x/y or element_index; optional with element_token (the token carries it). | | `x` | `number` | | Screenshot-pixel X: the element px action form: pixel-click there to focus, then send the combo (so e.g. Ctrl+V pastes into that field). Pass with y. Use for Chromium/Electron surfaces the background combo can't reach. | | `y` | `number` | | Screenshot-pixel Y (see x). | **Windows** Press a combination of keys simultaneously: e.g. `["ctrl", "c"]` for Copy, `["ctrl", "shift", "t"]` for reopen-closed-tab. Dispatch is target-aware: - **Modern XAML / WinUI / UWP targets** route through UI Automation: the driver walks the target's accessibility subtree, finds a descendant whose AcceleratorKey matches the combo, and invokes it. No focus steal, no system-queue input. - **Legacy Win32 targets with modifiers** (Ctrl+S, Alt+Tab, etc.) route through `SendInput` against the system input queue, with a brief `SetForegroundWindow` swap so the events land on the target. This path is necessary because PostMessage(WM_KEYDOWN, VK_CONTROL) does NOT update the OS-wide modifier state visible to `GetKeyState` / `TranslateAccelerator`, so Win32 apps that bind accelerators via `TranslateAccelerator` (LibreOffice, FAR, classic Notepad, etc.) never see the combo as a real accelerator on the PostMessage-only path. Trade-off: brief foreground swap (mitigated by restoring the previous foreground after the keystrokes flush). Requires the daemon to have UIAccess integrity so `SetForegroundWindow` is permitted: the MCP proxy auto-prefers the `cua-driver-uia.exe` worker pipe when both daemons are running. - **Legacy Win32 targets without modifiers** (plain `enter`, `tab`, `f5`, etc.) route through `PostMessage(WM_KEYDOWN/UP)`: no focus steal, no need to update modifier state. The target does NOT need to be frontmost in any branch. **`window_id`** (optional): explicit HWND when the pid owns more than one window; otherwise the pid's first visible window is used. Recognized modifiers: ctrl/control, shift, alt, win/windows. Non-modifier keys use the same vocabulary as `press_key` (return, tab, escape, up/down/left/right, space, delete, home, end, pageup, pagedown, f1-f12, letters, digits). Order: modifiers first, one non-modifier last. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `keys` | `string[]` | required | Modifier(s) and one non-modifier key, e.g. ["ctrl", "c"]. Items: 2 to any. | | `pid` | `integer` | | Target process ID. | | `scope` | `"window" \| "desktop"` | | Use desktop with no pid/window_id to send the hotkey to the current foreground application. | | `window_id` | `integer` | | Explicit HWND when the pid owns multiple windows. | | `x` | `number` | | Window-local screenshot-pixel X: the element px action form: pixel-click there to focus, then send the combo (so e.g. Ctrl+V pastes into that field). Pass with y. Use for Chromium/Electron surfaces the background combo can't reach. | | `y` | `number` | | Window-local screenshot-pixel Y (see x). | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `target` | `window target \| desktop target` | | Preferred per-call target: an exact window (`kind="window"`, `pid`, `window_id`) or the primary desktop (`kind="desktop"`, `display_id="primary"`). | **Example arguments** ```json {"keys":["<key>","<key>"]} ``` --- # Value and clipboard tools Set element values and read or write the clipboard. | Tool | Description | Platforms | | --- | --- | --- | | [`set_value`](#set_value) | Set an element's value by `element_token`. | macOS, Linux, Windows | | [`clipboard_read`](#clipboard_read) | List available system clipboard types and optionally return privacy-sensitive plain text. | macOS, Linux, Windows | | [`clipboard_write`](#clipboard_write) | Replace the system clipboard with exactly one value: plain text, an image from an absolute local path, or a file URL from an absolute local path. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `set_value` Effect: destructive, idempotent. Platforms: macOS, Linux, Windows. **macOS** Set an element's value by `element_token`. A popup button / select dropdown: selects the menu item whose title or value matches `value` (case-insensitive). Safari `<select>` is set through the DOM; AppKit and Chromium popups list items only while open, so the menu is opened, the item pressed and the menu closed again (the front window loses key focus for ~0.4 s). An unknown value lists the options. Any other element: writes AXValue (sliders, steppers, date pickers, native text fields). A Finder file name (list row or Get Info `Name` field, not being edited) is refused with `file_name_needs_rename`; the refusal names the keyboard route. For free-form text in web inputs use `type_text_chars`: WebKit ignores AXValue writes. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. | | `value` | `string` | required | New value, coerced to the element's type. | | `window_id` | `integer` | | Window ID. Omit with element_token. | **Linux** Set value of an AT-SPI element via SetValue action. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Input delivery mode. 'background' (default) never activates or raises the target window. On X11 it injects via XTEST / the XInput2 master pointer (no focus steal). On Wayland it goes through libei + xdg-desktop-portal, which injects to the compositor's input focus: Wayland's security model has no per-window background targeting, so a specific non-focused window cannot be aimed at; when no libei backend is available the tool returns a structured background_unavailable error. 'foreground' is the explicit escalation: activate the target (X11 _NET_ACTIVE_WINDOW; Wayland compositor activate), inject, then restore the prior active window; a brief focus swap unless the target was already active. Matches the macOS / Windows delivery_mode surface. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `value` | `string \| number` | required | New value. Written through AT-SPI EditableText/Value when the element exposes them; otherwise (GTK2 spin scales, VCL spin buttons) the field is clicked with the session's real pointer, its text selected and replaced through the virtual keyboard and committed with Tab, then read back. | | `window_id` | `integer` | | Omit when element_token is supplied (the token carries it). | **Windows** Set a value on a UIA element via the ValuePattern interface. Two semantic modes (matching Swift's split): - **Standard input** (text fields, sliders, combo box edit): writes the value directly through UIA `IUIAutomationValuePattern::SetValue`. This is the canonical Windows write path, equivalent to Swift's `AXValue` write. - **ComboBox**: on a native Win32 combo box (WinForms, MFC, dialogs) selects the option whose text matches `value` and sends the app the notifications a user's choice sends (CBN_SELENDOK, CBN_SELCHANGE), without focusing the window; an editable combo gets the text typed into its edit field. A drop-down list without that option is refused with the list of options. Other combo boxes get a UIA value write, which the app may not treat as a selection. For free-form text entry that the target accepts via keystrokes only, prefer `type_text`: UIA ValuePattern writes are ignored by some web inputs (same caveat as Swift's `AXValue`-vs-WebKit). **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | | Input delivery mode. 'background' (default) never swaps foreground: it routes through UIA Invoke / PostMessage and the window is never raised. For targets whose input stack silently drops posted events (Chromium/Electron content, GTK buttons, VCL/LibreOffice accelerators) the tool returns a structured background_unavailable error rather than fronting. 'foreground' is the explicit escalation: a brief SetForegroundWindow swap + SendInput, restoring the prior foreground afterward. IMPORTANT: 'background' is not a hint to weigh; it is the mandatory first attempt. Do NOT pass 'foreground' preemptively because a target 'looks like' GTK/Chromium/Electron; the DRIVER decides when background is impossible and tells you so via a background_unavailable error (or a verified no-op). Only THEN re-issue the same action with 'foreground'. The lists above are the driver's detectors, not a checklist for you to front on a guess: fronting up-front needlessly steals the user's focus and is a bug, not a shortcut. Matches the macOS delivery_mode surface. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `value` | `string` | required | New value. UIA will coerce to the element's native type. | | `window_id` | `integer` | | HWND of the window. Omit when element_token is supplied (the token carries it). | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `element_token` | `string` | | Element handle `<snapshot_id>:<row>` from get_window_state: row `[11]` of a read whose snapshot_id is s0000002a is "s0000002a:11" (also in `structuredContent.elements[].element_token`). Returns an explicit stale error naming the current snapshots once a newer read supersedes it. | | `pid` | `integer` | required | Target process ID. | **Example arguments** ```json {"pid":1,"value":"<value>"} ``` ## `clipboard_read` List available system clipboard types and optionally return privacy-sensitive plain text. Clipboard content is never retained in telemetry. Effect: read-only. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `include_text` | `boolean` | `false` | Return plain-text clipboard content in addition to the available types. Clipboard content is privacy-sensitive and is never retained in telemetry. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `clipboard_write` Replace the system clipboard with exactly one value: plain text, an image from an absolute local path, or a file URL from an absolute local path. Returns the available types for read-back before paste. Effect: destructive, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `file_path` | `string` | | Absolute path to a local file to place on the clipboard as a file URL. | | `image_path` | `string` | | Absolute path to a local image to place on the clipboard. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `text` | `string` | | Plain text to place on the clipboard. | --- # Batch tool Run several action tools in one call. Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `run_actions` The default way to act: run one or more action tools in ONE call, in order, stop at the first failure, and (with `observe`) get what changed in the same response. Use it whenever you know the next action(s) and would otherwise re-read the window after them: fill three fields, click, and see the result in one call instead of five. Even a single step plus `observe:true` replaces an action call followed by a get_window_state call. Each step is `{tool, args}` where `tool` is one of click, double_click, right_click, set_value, type_text, press_key, hotkey, scroll, drag and `args` are exactly that tool's arguments. Every step goes through the same session, permission and approval checks as a direct call; a batch grants nothing extra. All steps are validated before the first one runs, so a malformed step 4 changes nothing. Element tokens and element_index values come from a get_window_state read before the batch; a step that changes the UI can invalidate later element targets, so put element-targeted actions before the actions that reshuffle the window, or use pixel targets after them. Returns per-step status (`ok` or the error message) and, when `observe` is given, ONE bounded get_window_state read after the last executed step. That read is a `since:"latest"` diff by default: only the rows that changed since your last read of the window, plus a new snapshot_id. Without `observe` nothing is read. The batch uses one session: steps may omit `session` or repeat the batch's. `delay_ms` pauses between steps (max 2000). At most 32 steps. TARGET BY NAME (no earlier read needed, so one batch can cross screens): a step can be written `{"click": {...args}}` as well as `{tool, args}`, and its args may name the target instead of carrying a token: `role` (button, textfield, link, checkbox, menuitem, ...; AX/UIA/AT-SPI names also work), `name` (the element's label; exact match wins over substring, case-insensitive), `nth` (0-based, when several match), and `app` (app name or bundle id) and/or `window` (title substring) instead of pid/window_id. The batch reads the window fresh, waits up to 3 s (step `timeout_ms`, max 10000) for the element to appear, and acts on it; with only `app` it uses that app's frontmost window, so a dialog that opened on top is found. Later steps inherit the window, so name `app` once. The first lookup in a window also reads it once for `observe`, so the end diff shows what the batch changed even in a window you never read. Several matches fail with the candidates listed; add `nth` or a fuller name. WAITS AND CHECKS: a step may add `wait_for` (checked before its action) and `expect` (one check or up to 4, checked after it); a step may also be only a check. A check is {role?, name?, text?, gone?, value?, value_contains?, enabled?, selected?, app?, window?, timeout_ms?}: `text` matches any row of the tree, static text included; `gone:true` waits for it to disappear. Checks poll until they hold or time out (wait_for 5 s, expect 2 s, max 10 s), and a check that does not hold fails the step with what was found instead. The report says which step failed, in which phase (wait_for, find, action, expect) and why. Whole batch: at most 60 s. Example (fill a form, submit, confirm the dialog, check the result): {"steps":[{"set_value":{"app":"Safari","role":"textfield","name":"Email","value":"ada@example.com"}},{"click":{"role":"button","name":"Submit"},"expect":{"role":"button","name":"Confirm"}},{"click":{"role":"button","name":"Confirm"},"expect":[{"name":"Confirm","gone":true},{"text":"Thanks"}]}],"observe":true}. Effect: destructive. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delay_ms` | `integer` | | Pause between steps in milliseconds (not after the last). Default 0. Range: 0 to 2000. | | `observe` | `object \| boolean` | | Optional end-of-batch observation: `true`, or arguments for ONE get_window_state call. `pid` and `window_id` default to those of the last step that names both. Defaults to since="latest" (only what changed since your last read of that window with the same query/max_elements/max_depth; a full read when there is none), include_screenshot=false and max_elements=250; pass include_screenshot=true to see the window, or since=null for a full read. Omit to read nothing. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `steps` | `object[]` | required | Ordered actions. Execution stops at the first failing step. Each step is {tool, args} or {<tool>: args}, plus optional wait_for / expect / timeout_ms; a step may also be only a wait_for or expect check. Items: 1 to 32. | **Example arguments** ```json {"steps":[{"tool":"click"}]} ``` --- # Page tools Read and act on web pages, and prepare and navigate browsers. | Tool | Description | Platforms | | --- | --- | --- | | [`page`](#page) | Legacy browser compatibility tool. | macOS, Linux, Windows | | [`get_browser_state`](#get_browser_state) | Read-only browser inspection. | macOS, Linux, Windows | | [`browser_prepare`](#browser_prepare) | Explicitly prepare an owned DevTools endpoint for a browser. | macOS, Linux, Windows | | [`browser_navigate`](#browser_navigate) | Navigate one tab of an exactly-bound browser target to a new URL (http/https/about only). | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `page` Legacy browser compatibility tool. Prefer get_browser_state and the typed browser_* tools for exact targeting, endpoint ownership, and consent. Read-only get_text and query_dom remain available by default. Mutating actions require the daemon operator to set CUA_DRIVER_ENABLE_LEGACY_PAGE_MUTATIONS=1 before daemon startup (restart the daemon after changing it); this escape hatch does not provide the typed browser surface's exact binding or existing-profile grant guarantees. Supports Chrome, Brave, Edge, Safari (via AppleScript on macOS), Electron apps (via CDP), Chromium/Firefox on Windows (via UIA for read; CDP for execute_javascript when --remote-debugging-port is set), and WKWebView/Tauri/AT-SPI fallbacks. Actions: - execute_javascript: Run JS and return the result. - get_text: Extract visible text from the page. - query_dom: Find elements matching a CSS selector. - click_element: Click a CSS-selected element AND animate the agent cursor to its on-screen center first (so the user sees what the agent is doing). Prefer over `execute_javascript('el.click()')` whenever you want visible cursor feedback. - insert_text: Insert `text` at whatever currently holds DOM focus in one native operation (CDP Input.insertText); no synthesized key events, but more durable than a one-shot execute_javascript write since rich-text editors already have to treat it like an IME commit. Try this before type_keystrokes on a contenteditable that discarded an execute_javascript write. Click/focus the target field first. - type_keystrokes: Type `text` via real per-character keystroke events into whatever currently holds DOM focus. Slower than insert_text but the most durable rung: use it when insert_text also gets discarded, or the editor's own keydown/keyup handlers need to see real keys. Click/focus the target field first. - enable_javascript_apple_events: macOS-only; patch the browser's Preferences to allow JS from Apple Events (Chrome/Brave/Edge, requires user confirmation and a browser restart). Effect: mutating. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `action` | `"execute_javascript" \| "get_text" \| "query_dom" \| "click_element" \| "insert_text" \| "type_keystrokes" \| "enable_javascript_apple_events"` | required | Action to perform. | | `attributes` | `string[]` | | Element attributes to include in query_dom results. | | `bundle_id` | `string` | | Bundle ID of the browser. Required for enable_javascript_apple_events (macOS only). | | `cdp_port` | `integer` | | Optional, for execute_javascript/insert_text/type_keystrokes: use this exact CDP port instead of auto-discovering one from pid. Needed when the port was opened via the browser's own remote-debugging toggle rather than a launch-time flag, since that path may not answer the auto-discovery probe. Range: 1 to 65535. | | `css_selector` | `string` | | CSS selector for query_dom (e.g. 'a', 'button', 'input', 'h1'-'h6', 'p', 'img', 'select', '*'). | | `javascript` | `string` | | JavaScript to execute. Required for execute_javascript. | | `pid` | `integer` | | Target process ID. | | `selector` | `string` | | CSS selector for click_element (e.g. 'button.submit', '#login a'). | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `target_url_contains` | `string` | | Optional, for execute_javascript/insert_text/type_keystrokes: require exactly one browser tab whose URL contains this substring. Use this on a multi-tab browser: there's no built-in link between window_id and which tab a CDP call reaches. | | `text` | `string` | | Text to insert or type. Required for insert_text and type_keystrokes. The target field must already have DOM focus (click/focus it first). | | `user_has_confirmed_enabling` | `boolean` | | Must be true to proceed with enable_javascript_apple_events. This will quit and relaunch the browser. | | `window_id` | `integer` | | Target window ID from list_windows. | **Example arguments** ```json {"action":"execute_javascript"} ``` ## `get_browser_state` Read-only browser inspection. Mode 1 (bind): pass pid + window_id of a native browser window to classify it, correlate it to a CDP target (exact-or-refuse), and mint a session-scoped target id plus tab ids. Mode 2 (snapshot): pass target_id + tab_id. The dom_refs_v1 compatibility format returns composed DOM refs. semantic_v2 joins accessibility, DOM, layout, and viewport state; ranks visible content before retained/offscreen state; and returns a semantic outline, typed action refs, content refs, scoped reads, and opaque continuation. Never performs setup: a missing endpoint is a structured browser_requires_setup refusal pointing at browser_prepare. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `continuation` | `string` | | Opaque continuation minted by an earlier semantic_v2 response. | | `include_screenshot` | `boolean` | `false` | Capture the exact tab viewport as PNG through CDP without selecting the tab or foregrounding its native window. The request refuses if capture cannot be completed. | | `pid` | `integer` | | Native browser process id (bind mode). | | `query` | `string` | | Read-only semantic match over role, accessible name, and visible text. | | `scope_ref` | `string` | | Current semantic/content ref whose subtree should be observed. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session. | | `snapshot_format` | `"dom_refs_v1" \| "semantic_v2"` | | Versioned snapshot contract. dom_refs_v1 remains the compatibility default. | | `tab_id` | `string` | | Opaque tab id from get_browser_state (session-scoped). | | `target_id` | `string` | | Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id). | | `window_id` | `integer` | | Native window id owned by pid (bind mode). | ## `browser_prepare` Explicitly prepare an owned DevTools endpoint for a browser. pid is required for an existing process or existing-profile attachment, and optional only for allow_launch=true with an isolated profile. Existing endpoints are detected without side effects. Acting setup for an isolated profile follows the runtime permission mode and optional capability manifest. It requires allow_launch=true, launches a separate browser, and never copies, modifies, or terminates the requested user profile. Without pid, only a platform-attested system Chrome/Edge installation (or a root-owned package payload on Linux) is eligible; redirects and user-controlled locations fail closed. Existing-profile attachment is explicit and follows the runtime's immutable permission mode: standard requires an explicit --grant existing-profile launch grant or an embedding authorization host, bounded requires a launch-approved exact resource manifest, and unrestricted requires explicit trusted startup risk acceptance. Ordinary MCP transport approval never proves profile authorization. On proven platforms, an authorized request also permits one bounded exact-window setup: open the recognized browser product's fixed remote-debugging page, toggle its uniquely matched per-instance checkbox, prove the PID-owned loopback endpoint, and close the temporary tab. Every visible effect is reported; ambiguity is refused. Effect: destructive. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `allow_launch` | `boolean` | | Allow a separate driver-owned isolated Chromium process to be launched (default false). | | `pid` | `integer` | | Browser process id to prepare. Required except for a driver-owned isolated_new/isolated_named launch with allow_launch=true. | | `profile` | `object` | | Driver-owned isolated Chromium profile to launch with allow_launch=true. mode=isolated_new creates a fresh throwaway profile; mode=isolated_named reuses the named driver-owned profile. Never an existing user profile. | | `profile.mode` | `"isolated_new" \| "isolated_named"` | | | | `profile.name` | `string` | | Required only for isolated_named; 1-64 path-safe ASCII characters. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session. | | `strategy` | `object` | | Attach to an already-running browser instead of launching one. kind=existing_profile attaches to the user's running profile at pid/window_id and requires explicit profile authorization. | | `strategy.kind` | `"existing_profile"` | | | | `window_id` | `integer` | | Exact native window approval anchor; required for strategy.kind=existing_profile. | ## `browser_navigate` Navigate one tab of an exactly-bound browser target to a new URL (http/https/about only). Refused for heuristic bindings. Navigation invalidates all p<snapshot>:<index> refs for the tab. Effect: mutating. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session. | | `tab_id` | `string` | required | Opaque tab id from get_browser_state (session-scoped). | | `target_id` | `string` | required | Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id). | | `url` | `string` | required | Destination URL (http:, https:, or about:). | **Example arguments** ```json {"target_id":"<target_id>","tab_id":"<tab_id>","url":"<url>"} ``` --- # Browser input tools Exact browser input over CDP: clicks, typing, pointer, dialogs, files and downloads. | Tool | Description | Platforms | | --- | --- | --- | | [`browser_click`](#browser_click) | Click a page element (by ref) or viewport coordinates in an exactly-bound tab. | macOS, Linux, Windows | | [`browser_type`](#browser_type) | Type text into an exactly-bound tab via the Input domain. | macOS, Linux, Windows | | [`browser_pointer`](#browser_pointer) | Perform hover, right-click, double-click, scroll, or drag in an exactly-bound browser tab. | macOS, Linux, Windows | | [`browser_dialog`](#browser_dialog) | Inspect or resolve a page-owned JavaScript alert, confirm, prompt, or beforeunload dialog on one exactly-bound tab. | macOS, Linux, Windows | | [`browser_set_input_files`](#browser_set_input_files) | Assign one or more explicit absolute local files to an exact live <input type=file> ref through CDP. | macOS, Linux, Windows | | [`browser_download`](#browser_download) | Trigger one download through an exact live browser ref and save it inside an explicitly approved directory. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `browser_click` Click a page element (by ref) or viewport coordinates in an exactly-bound tab. Default route is trusted hardware-like input (Input.dispatchMouseEvent), and refuses where that route cannot preserve standalone-browser background posture unless delivery_mode="foreground" accepts that the browser window may activate (Linux Chromium; for example a browser inside a sandbox). input_route="dom_event" (synthetic el.click(), ref required) is used only when explicitly requested; it proves dispatch, not control activation, because trust-gated controls may ignore synthetic events. Refused for heuristic bindings. Effect: mutating. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | background (default) refuses trusted input where it would activate the browser window (Linux Chromium). foreground accepts that activation, for a browser whose window nobody else is using (for example inside a sandbox). | | `input_route` | `"trusted" \| "dom_event"` | | "trusted" (default): Input.dispatchMouseEvent. It refuses rather than foregrounding a standalone browser. "dom_event": synthetic full-background DOM click, only when explicitly requested. Dispatch does not prove the control activated; refresh page state and verify the expected postcondition. | | `ref` | `string` | | Page element ref in the p<snapshot>:<index> namespace from get_browser_state. Refs are invalidated by navigation and by newer snapshots of the same tab. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session. | | `tab_id` | `string` | required | Opaque tab id from get_browser_state (session-scoped). | | `target_id` | `string` | required | Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id). | | `x` | `number` | | Viewport x (CSS px): alternative to ref. | | `y` | `number` | | Viewport y (CSS px): alternative to ref. | **Example arguments** ```json {"target_id":"<target_id>","tab_id":"<tab_id>"} ``` ## `browser_type` Type text into an exactly-bound tab via the Input domain. mode="insert_text" (default) uses Input.insertText; mode="keystrokes" dispatches per-character key events. Both insert at the caret, so typing into a field that already holds text appends to it; pass replace=true to set the field instead, or to clear it by typing an empty string. Pass a ref to an editable element from the latest snapshot. A ref is required; heuristic bindings are refused. Effect: mutating. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `mode` | `"insert_text" \| "keystrokes"` | | insert_text (default): bulk Input.insertText. keystrokes: per-character Input.dispatchKeyEvent. | | `ref` | `string` | required | Page element ref in the p<snapshot>:<index> namespace from get_browser_state. Refs are invalidated by navigation and by newer snapshots of the same tab. | | `replace` | `boolean` | | false (default): insert at the caret, appending to whatever the field already holds. true: select the element's whole content first so the text replaces it; with an empty text this clears the field. Replacement goes through the selection, so beforeinput/input still fire and framework state stays consistent. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session. | | `tab_id` | `string` | required | Opaque tab id from get_browser_state (session-scoped). | | `target_id` | `string` | required | Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id). | | `text` | `string` | required | Text to type. | **Example arguments** ```json {"target_id":"<target_id>","tab_id":"<tab_id>","ref":"<ref>","text":"<text>"} ``` ## `browser_pointer` Perform hover, right-click, double-click, scroll, or drag in an exactly-bound browser tab. Semantic refs must declare pointer for hover, right-click, double-click, and drag; scroll accepts a scroll or pointer capability. The trusted route uses CDP Input events and refuses if standalone background posture cannot be preserved. The explicit dom_event route requires a page ref and synthesizes full-background DOM events. Never activates or brings a tab to the foreground. Effect: mutating. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `action` | `"hover" \| "right_click" \| "double_click" \| "scroll" \| "drag"` | required | Pointer gesture. scroll needs delta_x or delta_y; drag needs destination_ref or to_x/to_y. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | background (default) refuses trusted input where it would activate the browser window (Linux Chromium). foreground accepts that activation, for a browser whose window nobody else is using (for example inside a sandbox). | | `delta_x` | `number` | | Horizontal scroll delta in CSS pixels. | | `delta_y` | `number` | | Vertical scroll delta in CSS pixels. | | `destination_ref` | `string` | | Drag destination page ref in the exact same frame. | | `input_route` | `"trusted" \| "dom_event"` | `"trusted"` | trusted sends CDP Input events; dom_event synthesizes DOM events in the page and requires ref. | | `ref` | `string` | | Origin page ref. Alternative to x/y. | | `session` | `string` | required | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. This tool requires the label that owns its browser target, tab, and refs. | | `tab_id` | `string` | required | Opaque tab id minted by get_browser_state. | | `target_id` | `string` | required | Opaque target id minted by get_browser_state. | | `to_x` | `number` | | Drag destination viewport x in CSS pixels. | | `to_y` | `number` | | Drag destination viewport y in CSS pixels. | | `x` | `number` | | Origin viewport x in CSS pixels. | | `y` | `number` | | Origin viewport y in CSS pixels. | **Example arguments** ```json {"target_id":"<target_id>","tab_id":"<tab_id>","session":"<session>","action":"hover"} ``` ## `browser_dialog` Inspect or resolve a page-owned JavaScript alert, confirm, prompt, or beforeunload dialog on one exactly-bound tab. This never handles browser permission UI, extension UI, native dialogs, or file pickers. Inspect returns an opaque dialog_id; accept/dismiss require that exact current id. Resolution defaults to background delivery; Linux callers must explicitly request foreground delivery because Chromium's native modal cannot be resolved there without changing foreground posture. Effect: mutating. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `action` | `"inspect" \| "accept" \| "dismiss"` | required | inspect returns the current dialog and its dialog_id; accept or dismiss resolves that exact dialog. | | `delivery_mode` | `"background" \| "foreground"` | `"background"` | Requested foreground posture for accept/dismiss. Linux Chromium requires foreground; inspect is read-only. | | `dialog_id` | `string` | | Opaque current dialog generation returned by action=inspect. | | `prompt_text` | `string` | | Sensitive response text, valid only when accepting a prompt dialog. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session. | | `tab_id` | `string` | required | Opaque tab id from get_browser_state (session-scoped). | | `target_id` | `string` | required | Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id). | **Example arguments** ```json {"target_id":"<target_id>","tab_id":"<tab_id>","action":"inspect"} ``` ## `browser_set_input_files` Assign one or more explicit absolute local files to an exact live <input type=file> ref through CDP. This bypasses native file pickers, rejects symlinks and non-regular files, and never returns local paths. Effect: mutating. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `files` | `string[]` | required | Absolute paths of 1 to 32 local regular files to assign to the input. Items: 1 to 32. | | `ref` | `string` | required | Page element ref in the p<snapshot>:<index> namespace from get_browser_state. Refs are invalidated by navigation and by newer snapshots of the same tab. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. Browser targets, tabs, and refs belong to the resolved lifecycle session. | | `tab_id` | `string` | required | Opaque tab id from get_browser_state (session-scoped). | | `target_id` | `string` | required | Opaque browser target id minted by get_browser_state (session-scoped; never a CDP id). | **Example arguments** ```json {"target_id":"<target_id>","tab_id":"<tab_id>","ref":"<ref>","files":["<file>"]} ``` ## `browser_download` Trigger one download through an exact live browser ref and save it inside an explicitly approved directory. Requires MCP-host destructive-tool approval, refuses ambiguous or stale capabilities, and never returns the source URL, filename, or destination path. Effect: destructive. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `destination_root` | `string` | required | Absolute, existing, canonical directory approved to receive the download. | | `ref` | `string` | required | Live page ref whose activation initiates the download. | | `session` | `string` | required | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. This tool requires the label that owns its browser target, tab, and refs. | | `tab_id` | `string` | required | Opaque exact tab id from get_browser_state. | | `target_id` | `string` | required | Opaque exact browser target id from get_browser_state. | **Example arguments** ```json {"session":"<session>","target_id":"<target_id>","tab_id":"<tab_id>","ref":"<ref>","destination_root":"<destination_root>"} ``` --- # Session tools Start, escalate, inspect and end lifecycle sessions. | Tool | Description | Platforms | | --- | --- | --- | | [`start_session`](#start_session) | Optionally create or return a lifecycle session before acting. | macOS, Linux, Windows | | [`escalate_session`](#escalate_session) | Deprecated compatibility tool for legacy capture-scope sessions. | macOS, Linux, Windows | | [`get_session`](#get_session) | Read content-free lifecycle, cursor, recording, and idle status for one session visible to this authenticated transport. | macOS, Linux, Windows | | [`list_sessions`](#list_sessions) | List content-free lifecycle summaries attached to this authenticated transport lease. | macOS, Linux, Windows | | [`get_session_state`](#get_session_state) | Deprecated compatibility alias that reads a live legacy session's capture policy. | macOS, Linux, Windows | | [`end_session`](#end_session) | End one visible lifecycle session and run its cursor, recording, configuration, and other cleanup hooks exactly once. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `start_session` Optionally create or return a lifecycle session before acting. For multi-call work, prefer a short public `session` label and repeat it on every call that accepts it; an omitted value uses the authenticated transport lease's implicit session instead. This tool is optional because an ordinary action can create or reuse a named run directly. Use it to set the initial cursor theme before acting or to revive a public name after it has ended; ordinary actions never revive ended names. `capture_scope` is deprecated compatibility input; new callers select window or desktop modality per action. Idempotent. Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `capture_scope` | `"auto" \| "window" \| "desktop"` | | Deprecated compatibility policy. New callers select window or desktop modality on each action instead of storing it on the session. | | `cursor_motion` | `object` | | Optional initial cursor motion (style, timing, effects and tuning). The host applies it before the cursor is first made visible. A later `set_agent_cursor_motion` call wins; this wins over the saved default (`cursor.motion.*` in the driver config) and the built-in `signature_arc`. Reduced motion always wins. | | `cursor_motion.arc_flow` | `number` | | Added to the arc asymmetry of `signature_arc`, `spring_settle` and `comet_swoop`: positive moves the apex toward the destination. Clamped to -1..1 (default 0). | | `cursor_motion.arc_size` | `number` | | Scales the arc of `signature_arc`, `spring_settle` and `comet_swoop`: 0.25 (default) keeps the style's arc, 0 is a straight line, 0.5 doubles it. Clamped to 0..1. | | `cursor_motion.dwell_after_click_ms` | `number` | | Pause after a click animation, in milliseconds. Clamped to 0..5000 (default 80). | | `cursor_motion.effects` | `object` | | Turn single effects on or off. An omitted effect keeps its current setting; null restores the style's default. | | `cursor_motion.end_handle` | `number` | | Arc control-point offset from the end, as a fraction of the distance, for `signature_arc`, `spring_settle` and `comet_swoop`. Clamped to 0..1 (default 0.3). | | `cursor_motion.glide_duration_ms` | `number` | | Move duration in milliseconds for `fixed` timing (1430 ms when 0, the default). A nonzero value with `native` timing also fixes the duration. Clamped to 0..5000. | | `cursor_motion.idle_hide_ms` | `number` | | Hide the cursor after this many idle milliseconds; 0 never hides it. Clamped to 0..60000 (default 15000). | | `cursor_motion.spring` | `number` | | Arrival spring damping for `classic`: 1 is critically damped, 0.3 is bouncy. Clamped to 0.3..1 (default 0.72). | | `cursor_motion.start_handle` | `number` | | Arc control-point offset from the start, as a fraction of the distance, for `signature_arc`, `spring_settle` and `comet_swoop`. Clamped to 0..1 (default 0.3). | | `cursor_motion.style` | `"signature_arc" \| "spring_settle" \| "magnetic" \| "comet_swoop" \| "adaptive" \| "classic"` | | Trajectory style. `signature_arc` (default) is one arc with a small follow-through; `spring_settle` lands with one soft bounce; `magnetic` is pulled into the target; `comet_swoop` is a wide arc with a short trail; `adaptive` picks a careful approach for small targets and a swoop for long moves; `classic` is the previous Dubins glide. When the theme's reduced motion is on, every move is a short straight glide with no effects. | | `cursor_motion.timing` | `"native" \| "fitts" \| "fixed"` | | `native` uses the style's own timing; `fitts` scales the move time with distance and target size; `fixed` uses glide_duration_ms (1430 ms when 0). | | `cursor_motion.turn_radius` | `number` | | Minimum turning radius of the `classic` glide path, in points; smaller turns tighter. Clamped to 1..1000 (default 80). | | `cursor_theme` | `object` | | Optional initial cursor theme. The host applies it before the cursor is first made visible, avoiding a flash of the default theme. | | `cursor_theme.reduced_motion` | `"auto" \| "on" \| "off"` | `"auto"` | | | `cursor_theme.theme_id` | `string` | | | | `session` | `string` | | Optional stable public label for this run (e.g. "research-run-1"). When omitted, the authenticated transport lease's implicit session is created or returned. | ## `escalate_session` Deprecated compatibility tool for legacy capture-scope sessions. New callers select window or desktop modality on each action. No deescalate_session tool exists. Effect: mutating. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `detail` | `string` | | Optional bounded diagnostic detail. Never use secrets or page content. | | `reason` | `"ax_tree_pixel_mismatch" \| "background_delivery_failed" \| "foreground_ineffective" \| "no_window_target" \| "other"` | required | Why the window-scoped attempt failed and desktop capture is needed. | | `session` | `string` | required | Public label of the legacy capture-scope session to escalate. | **Example arguments** ```json {"session":"<session>","reason":"ax_tree_pixel_mismatch"} ``` ## `get_session` Read content-free lifecycle, cursor, recording, and idle status for one session visible to this authenticated transport. Omit `session` to inspect its implicit session. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | Optional public label. When omitted, inspect the caller's attached implicit session. | ## `list_sessions` List content-free lifecycle summaries attached to this authenticated transport lease. It does not enumerate other callers' sessions. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `cursor` | `string` | | Opaque continuation cursor returned by a previous call. | | `limit` | `integer` | | Maximum number of content-free summaries to return (default 50, max 100). Ordinary agent transports are scoped to their own lease. Minimum: 0. | ## `get_session_state` Deprecated compatibility alias that reads a live legacy session's capture policy. Use get_session for lifecycle state. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | Optional public label. When omitted, inspect the caller's attached implicit session. | ## `end_session` End one visible lifecycle session and run its cursor, recording, configuration, and other cleanup hooks exactly once. Omit `session` to end the authenticated transport's implicit session. Idempotent. Effect: destructive, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | Optional public label to end. When omitted, end the caller's attached implicit session. | --- # Agent cursor tools Show, animate and style the agent cursor. | Tool | Description | Platforms | | --- | --- | --- | | [`set_agent_cursor_enabled`](#set_agent_cursor_enabled) | Show or hide the agent cursor owned by a session. | macOS, Linux, Windows | | [`set_agent_cursor_motion`](#set_agent_cursor_motion) | Configure the movement style, timing, effects and visibility timing for a session cursor. | macOS, Linux, Windows | | [`set_agent_cursor_theme`](#set_agent_cursor_theme) | Select an already-installed cursor theme for a session. | macOS, Linux, Windows | | [`get_agent_cursor_state`](#get_agent_cursor_state) | Return the session cursor's theme, semantic playback, position, visibility, and motion. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `set_agent_cursor_enabled` Show or hide the agent cursor owned by a session. Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | `boolean` | required | `true` shows the session's agent cursor overlay; `false` hides it. | | `session` | `string` | required | Public label of the session that owns the cursor. | **Example arguments** ```json {"session":"<session>","enabled":false} ``` ## `set_agent_cursor_motion` Configure the movement style, timing, effects and visibility timing for a session cursor. Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `arc_flow` | `number` | | Added to the arc asymmetry of `signature_arc`, `spring_settle` and `comet_swoop`: positive moves the apex toward the destination. Clamped to -1..1 (default 0). | | `arc_size` | `number` | | Scales the arc of `signature_arc`, `spring_settle` and `comet_swoop`: 0.25 (default) keeps the style's arc, 0 is a straight line, 0.5 doubles it. Clamped to 0..1. | | `dwell_after_click_ms` | `number` | | Pause after a click animation, in milliseconds. Clamped to 0..5000 (default 80). | | `effects` | `object` | | Turn single effects on or off. An omitted effect keeps its current setting; null restores the style's default. | | `effects.glow` | `boolean` | | Soft glow around the cursor that grows with speed. | | `effects.magnet` | `boolean` | | Target glow when the `magnetic` style locks on. | | `effects.ripple` | `boolean` | | Ring that expands from the hotspot on click. | | `effects.squish` | `boolean` | | Brief scale-down of the cursor on click. | | `effects.trail` | `boolean` | | Short fading trail behind the cursor. | | `end_handle` | `number` | | Arc control-point offset from the end, as a fraction of the distance, for `signature_arc`, `spring_settle` and `comet_swoop`. Clamped to 0..1 (default 0.3). | | `glide_duration_ms` | `number` | | Move duration in milliseconds for `fixed` timing (1430 ms when 0, the default). A nonzero value with `native` timing also fixes the duration. Clamped to 0..5000. | | `idle_hide_ms` | `number` | | Hide the cursor after this many idle milliseconds; 0 never hides it. Clamped to 0..60000 (default 15000). | | `session` | `string` | required | Public label of the session that owns the cursor. Omitted or null motion fields keep their current value. | | `spring` | `number` | | Arrival spring damping for `classic`: 1 is critically damped, 0.3 is bouncy. Clamped to 0.3..1 (default 0.72). | | `start_handle` | `number` | | Arc control-point offset from the start, as a fraction of the distance, for `signature_arc`, `spring_settle` and `comet_swoop`. Clamped to 0..1 (default 0.3). | | `style` | `"signature_arc" \| "spring_settle" \| "magnetic" \| "comet_swoop" \| "adaptive" \| "classic"` | | Trajectory style. `signature_arc` (default) is one arc with a small follow-through; `spring_settle` lands with one soft bounce; `magnetic` is pulled into the target; `comet_swoop` is a wide arc with a short trail; `adaptive` picks a careful approach for small targets and a swoop for long moves; `classic` is the previous Dubins glide. When the theme's reduced motion is on, every move is a short straight glide with no effects. | | `timing` | `"native" \| "fitts" \| "fixed"` | | `native` uses the style's own timing; `fitts` scales the move time with distance and target size; `fixed` uses glide_duration_ms (1430 ms when 0). | | `turn_radius` | `number` | | Minimum turning radius of the `classic` glide path, in points; smaller turns tighter. Clamped to 1..1000 (default 80). | **Example arguments** ```json {"session":"<session>"} ``` ## `set_agent_cursor_theme` Select an already-installed cursor theme for a session. Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `reduced_motion` | `"auto" \| "on" \| "off"` | `"auto"` | Theme animation policy: `on` uses the theme's reduced-motion frames, `off` always animates, `auto` (default) leaves the choice to the host. | | `session` | `string` | required | Public label of the session that owns the cursor. | | `theme_id` | `string` | required | Id of an installed cursor theme (see `cua-driver cursor-theme list`). | **Example arguments** ```json {"session":"<session>","theme_id":"<theme_id>"} ``` ## `get_agent_cursor_state` Return the session cursor's theme, semantic playback, position, visibility, and motion. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | required | Public label of the session whose cursor to inspect. | **Example arguments** ```json {"session":"<session>"} ``` --- # Recording tools Record and replay trajectories. | Tool | Description | Platforms | | --- | --- | --- | | [`start_recording`](#start_recording) | Start trajectory recording for the calling session. | macOS, Linux, Windows | | [`stop_recording`](#stop_recording) | Stop trajectory recording. | macOS, Linux, Windows | | [`get_recording_state`](#get_recording_state) | Report the current trajectory recorder state: whether recording is enabled, the output directory (when enabled), and the 1-based counter for the next turn folder that will be written. | macOS, Linux, Windows | | [`replay_trajectory`](#replay_trajectory) | Replay a recorded trajectory by re-invoking every turn's tool call in lexical order. | macOS, Linux, Windows | | [`install_ffmpeg`](#install_ffmpeg) | Install the ffmpeg binary used by start_recording's video capture (Linux/Windows; macOS records natively and needs no ffmpeg). | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `start_recording` Start trajectory recording for the calling session. Each action-tool invocation (click, right_click, scroll, type_text, press_key, hotkey, set_value) from that session writes a turn folder under `output_dir`. Without `session`, every call on the same connection is recorded, including named-session calls. Other connections and session lifecycle calls (`start_session` / `end_session`) are not recorded. CLI recordings started with `cua-driver recording start` are daemon-wide. Each turn folder holds: - `before_state.json` / `after_state.json`: application AX/UIA/AT-SPI state immediately before and after the action. - `before.png` / `after.png`: target-window screenshots immediately before and after the action. - `evidence.json`: capture status and a stable classification when an expected artifact could not be captured. - `app_state.json`: post-action AX/UIA snapshot for the target pid. - `screenshot.png`: compatibility alias of `after.png`. - `action.json`: tool name, full input arguments, result summary, result-error flag, pid, click point (when applicable), ISO-8601 timestamp. - `click.png`: for dispatched click-family actions only, `before.png` with a red marker at the click point. A call refused before target resolution is explicitly not applicable instead. The per-turn accessibility walk is bounded like `get_window_state`: `state_timeout_ms` (default 1000) caps each before/after walk, a walk that runs out of budget records the PARTIAL tree, and `evidence.json` carries `truncated`, `truncation_reason`, `nodes_visited`, `nodes_pending` and `timeout_ms` for that phase. A provider that stops answering is abandoned after the budget plus a short grace and the phase is classified `state_capture_timeout`. Actions refused before dispatch (for example an unknown or expired `capture_id`) skip the state walk; their state is classified `not_applicable` / `action_refused_before_dispatch`. Pass `include_accessibility_tree: false` to record screenshots and actions without state. Turn folders are named `turn-00001/`, `turn-00002/`, etc. Turn numbering restarts at 1 each time recording is (re-)started. **Video is off by default.** Pass `record_video: true` to also capture the main display to `<output_dir>/recording.mp4` (H.264 / 30 fps) for the lifetime of the session. The recording is torn down automatically when the MCP client disconnects. **macOS uses native ScreenCaptureKit** (daemon-owned SCStream + SCRecordingOutput) under the daemon's Screen Recording grant, with no ffmpeg subprocess. Requires macOS 15.0+. On macOS 26 (Tahoe), the first direct capture can also show a one-time consent asking to let Cua Driver bypass the system private window picker and directly access your screen and audio; choose Allow, or run `cua-driver permissions grant` beforehand to answer it up front. The recorder captures screen video only and does not enable system-audio capture. **Windows + Linux use an ffmpeg subprocess** (`gdigrab` / `x11grab` + libx264). Requires ffmpeg on PATH (winget install Gyan.FFmpeg / apt install ffmpeg); when ffmpeg is missing or fails on startup the per-turn capture (screenshots + action.json) still runs and the session's `last_error` field carries the diagnostic. State persists for the life of the daemon; a restart resets to disabled with no on-disk state. Call `stop_recording` to disable + finalize the mp4. Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `include_accessibility_tree` | `boolean` | `true` | Default true. Set false to skip the per-turn before/after accessibility walks entirely; screenshots, click markers and action.json are still recorded and state is classified `state_capture_disabled`. | | `output_dir` | `string` | required | Absolute or ~-rooted directory where turn folders and (when enabled) the video file are written. | | `record_video` | `boolean` | | Capture the main display to <output_dir>/recording.mp4. Default: false. Set to true to also capture the main display to recording.mp4 (otherwise only the per-turn screenshots + JSON are recorded). On macOS this uses native ScreenCaptureKit (macOS 15.0+); macOS 26 can show a one-time direct screen-capture consent on first use (see `cua-driver permissions grant`). On Windows + Linux it requires ffmpeg on PATH. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `state_timeout_ms` | `integer` | `1000` | Wall-clock budget in milliseconds for EACH per-turn before/after accessibility walk (same default and bounds as get_window_state's timeout_ms). A walk that runs out records the partial tree and marks it truncated in evidence.json. Range: 100 to 120000. | **Example arguments** ```json {"output_dir":"<output_dir>"} ``` ## `stop_recording` Stop trajectory recording. Disables further per-turn capture and, when video was enabled, gracefully terminates the ffmpeg subprocess so the mp4's moov atom is finalized (the file is playable). Calling stop on an already-stopped session is a no-op. The response carries `last_video_path` pointing at the finalized mp4 (when video was on). A manual `stop_recording` is **unconditional**: it stops whatever recording is active regardless of which session started it. Ownership-scoped teardown (so one client disconnecting can't stop a recording a later client started) is handled by the registry's `session_end` lifecycle hook, not by this tool. Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `get_recording_state` Report the current trajectory recorder state: whether recording is enabled, the output directory (when enabled), and the 1-based counter for the next turn folder that will be written. Counter increments on every recorded action tool call and resets to 1 each time recording is (re-)enabled. Pure read-only. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `replay_trajectory` Replay a recorded trajectory by re-invoking every turn's tool call in lexical order. `dir` must point at a directory previously written by `start_recording`. Each `turn-NNNNN/` is parsed for `action.json`, and the recorded tool is called with its recorded `arguments` via the same dispatch path an MCP / CLI call uses. Caveats: - Element-token actions (`click({pid, element_token})` etc.) will fail because element tokens are per-snapshot and don't survive across sessions. Pixel clicks (`click({pid, x, y})`) and all keyboard tools replay cleanly. Failures are reported but don't stop replay unless `stop_on_error` is true. - `get_window_state` and other read-only tools are NOT currently recorded, so replays do not re-populate the per-(pid, window_id) element cache. - If recording is ENABLED while replay runs, the replay itself is recorded into the currently configured output directory. That's deliberate: recording a replay against a new build and diffing the two trajectories is the regression-test workflow. Effect: destructive. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `delay_ms` | `integer` | | Milliseconds to sleep between turns, for human-observable pacing. Default 500. Range: 0 to 10000. | | `dir` | `string` | required | Trajectory directory previously written by `start_recording`. Absolute or ~-rooted. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `stop_on_error` | `boolean` | | Stop replay on the first tool-call error. Default true: set false to best-effort through the full trajectory. | **Example arguments** ```json {"dir":"<dir>"} ``` ## `install_ffmpeg` Install the ffmpeg binary used by start_recording's video capture (Linux/Windows; macOS records natively and needs no ffmpeg). Two-step and confirmed: called without `confirm` it only REPORTS the exact install command for this platform's package manager; pass `confirm: true` to actually run it. No-op if ffmpeg is already on PATH. ffmpeg is run as a separate process, never linked into the driver. Effect: destructive. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `confirm` | `boolean` | | Run the install command. Without it, only the planned command is reported. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | --- # Configuration and maintenance tools Configuration, permissions, health, updates and extensions. | Tool | Description | Platforms | | --- | --- | --- | | [`get_config`](#get_config) | Return the current cua-driver-rs configuration. | macOS, Linux, Windows | | [`set_config`](#set_config) | Update cua-driver-rs configuration. | macOS, Linux, Windows | | [`check_permissions`](#check_permissions) | Report TCC permission status for Accessibility and Screen Recording. | macOS, Linux, Windows | | [`health_report`](#health_report) | Single-call end-to-end driver diagnostics. | macOS, Linux, Windows | | [`check_for_update`](#check_for_update) | Check the saved stable/nightly Cua Driver channel for a release on GitHub. | macOS, Linux, Windows | | [`install_extension`](#install_extension) | Preview or install one Driver-managed optional extension. | macOS, Linux, Windows | Served by `cua-driver mcp`; see [MCP tools](</docs/cua-driver/reference/mcp-tools>) for every tool. ## `get_config` Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. **macOS** Return the current cua-driver-rs configuration. **Linux** Return current cua-driver-rs configuration. **Windows** Report the current persistent driver config. Pure read-only. Returns defaults when the underlying state is unset: same fallback the daemon uses at startup. Sibling to `set_config`. Current schema (Windows): { "schema_version": 1, "version": "<crate version>", "source_sha": "<maintainer build commit or null>", "platform": "windows", "capture_mode": "ax" | "vision" (DEPRECATED, ignored), "max_image_dimension": 0 } `capture_mode` is deprecated and no longer affects behavior: `get_window_state` always returns both the UIA tree and a screenshot. **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `set_config` Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. **macOS** Update cua-driver-rs configuration. Changes to max_image_dimension take effect immediately. The experimental_pip keys are persisted to ~/.cua-driver/config.json and take effect on the next daemon restart (the PiP backend is initialised once at startup). Note: capture_mode is a per-call param (on get_window_state / click), not a stored setting. Capture modality is selected by each action's target; the old capture_scope config key is retired. Cursor motion defaults: cursor.motion.style, cursor.motion.timing and cursor.motion.effects.<trail|glow|magnet|ripple|squish> are saved to ~/.cua-driver/config.json and seed sessions started afterwards. start_session cursor_motion and set_agent_cursor_motion override them; reduced motion always wins. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `experimental_pip` | `boolean` | | Enable the experimental picture-in-picture preview window. Applies on next daemon restart. | | `experimental_pip_geometry` | `string` | | PiP window size + optional position in `WxH` or `WxH+X+Y` form (e.g. `320x200+24+24`). Applies on next daemon restart. | | `key` | `string` | | Name of a single config field to write ({key, value} shape, matching the CLI `config set` and the Windows/Linux tools). Pair with `value`. Equivalent to passing the field directly. | | `max_image_dimension` | `integer` | | Max dimension for screenshot resizing (0 = no limit). | **Linux** Update cua-driver-rs configuration. capture_mode / max_image_dimension take effect immediately. Two input shapes (both accepted, matching Windows/Swift): - **{key, value}** (preferred): `{"key": "max_image_dimension", "value": 800}`; single leaf write. - **Legacy per-field**: `{"capture_mode": "som", "max_image_dimension": 0}`. The experimental_pip keys persist to ~/.cua-driver/config.json and apply on next daemon restart (the PiP backend is initialised once at startup; Linux ships only the trait stub today: see issue #1729). `cursor.motion.style`, `cursor.motion.timing` and `cursor.motion.effects.<name>` save the default cursor motion for sessions started afterwards. **Linux parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `capture_mode` | `"ax" \| "vision"` | | Legacy per-field shape. Default capture mode for get_window_state. ("som"/"screenshot" still decode as deprecated aliases.). | | `experimental_pip` | `boolean` | | Enable the experimental PiP preview window (applies next restart; Linux backend stubbed). | | `experimental_pip_geometry` | `string` | | PiP window size + optional position in `WxH` or `WxH+X+Y` form. | | `key` | `string` | | Name of a single config field to write ({key, value} shape). Pair with `value`. | | `max_image_dimension` | `integer` | | Legacy per-field shape. Max dimension for screenshot resizing (0 = no limit). | **Windows** Write a setting into the persistent driver config. Values take effect immediately. Two input shapes: - **Swift-compatible** (preferred): `{"key": "max_image_dimension", "value": 1568}`; single dotted-path leaf write. - **Legacy per-field** (Rust-only): `{"max_image_dimension": 0}`; bulk write of named fields. Known keys: - `capture_mode` (string: `vision` | `ax` | `som`); DEPRECATED and ignored; `get_window_state` always returns both the UIA tree and a screenshot. Still accepted/persisted for back-compat but has no effect. - `max_image_dimension` (integer) - `experimental_pip` (boolean; persisted to config.json, applies on next daemon restart: Windows backend stubbed today, see issue #1729) - `experimental_pip_geometry` (string `WxH` or `WxH+X+Y`; persisted; applies on next daemon restart) - `cursor.motion.style`, `cursor.motion.timing`, `cursor.motion.effects.<name>`: saved default cursor motion for sessions started afterwards (see the cursor docs). Returns the full updated config in the same shape as `get_config`. **Windows parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `capture_mode` | `"ax" \| "vision"` | | DEPRECATED and ignored: get_window_state always returns both the UIA tree and a screenshot. Still accepted/persisted for back-compat but has no effect. ("som"/"screenshot" still decode as deprecated aliases.). | | `experimental_pip` | `boolean` | | Legacy per-field shape. Enables PiP preview (applies next restart). | | `experimental_pip_geometry` | `string` | | Legacy per-field shape. PiP window size + optional position. | | `key` | `string` | | Dotted snake_case path to a leaf config field (Swift-compatible shape). Pair with `value`. | | `max_image_dimension` | `integer` | | Legacy per-field shape. | **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `cursor.motion` | `any` | | Pass null (or `default`) to clear every saved cursor motion default. | | `cursor.motion.effects.glow` | `boolean` | | Saved default for the `glow` cursor effect. Unset follows the style. | | `cursor.motion.effects.magnet` | `boolean` | | Saved default for the `magnet` cursor effect. Unset follows the style. | | `cursor.motion.effects.ripple` | `boolean` | | Saved default for the `ripple` cursor effect. Unset follows the style. | | `cursor.motion.effects.squish` | `boolean` | | Saved default for the `squish` cursor effect. Unset follows the style. | | `cursor.motion.effects.trail` | `boolean` | | Saved default for the `trail` cursor effect. Unset follows the style. | | `cursor.motion.style` | `"signature_arc" \| "spring_settle" \| "magnetic" \| "comet_swoop" \| "adaptive" \| "classic"` | | Saved default cursor motion style for new sessions. Overridden by start_session cursor_motion and set_agent_cursor_motion; reduced motion always wins. Built-in default: signature_arc. | | `cursor.motion.timing` | `"native" \| "fitts" \| "fixed"` | | Saved default cursor move timing for new sessions. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `value` | `any` | | New value for `key`. JSON type depends on the key. | ## `check_permissions` Effect: mutating, idempotent. Platforms: macOS, Linux, Windows. **macOS** Report TCC permission status for Accessibility and Screen Recording. By default also raises the system permission dialogs for any missing grants: Apple's request APIs are no-ops when the grant is already active, so this is safe to call repeatedly. Pass {"prompt": false} for a purely read-only status check. Returns: `accessibility` + `screen_recording` (booleans from the TCC preflight APIs), `screen_recording_capturable` (a live ScreenCaptureKit probe when `prompt` is true; null on read-only calls), `direct_capture_status` (`ready`, `unavailable`, `timed_out`, `probe_failed`, `blocked_by_screen_recording`, or `not_checked`), `direct_capture_error` (a structured timeout/probe failure when applicable), `direct_capture_verification` (validated source, UTC time, and bundle identity from an explicit grant probe), and `source` (which TCC identity the booleans reflect: the CuaDriver daemon vs the launching terminal/IDE). macOS attributes grants to the responsible process, so a standalone call from a terminal reports the terminal's grants, not the driver's. The prompt-capable ScreenCaptureKit probe never runs when `prompt` is false. Pass `probe_direct_capture:false` with `prompt:true` to register/request only the two required TCC grants before separately explaining Tahoe's direct-capture consent. **macOS parameters** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `probe_direct_capture` | `boolean` | | When prompting and Screen Recording is granted, also run the live ScreenCaptureKit probe that may raise Tahoe's direct-capture consent. Default true. Set false for a staged Accessibility/Screen Recording request. | | `prompt` | `boolean` | `false` | Raise the system permission prompts for missing grants. Default false; only a trusted host setup route may set true. | **Linux** Check required permissions for cua-driver-rs on Linux. **Windows** Check required permissions for cua-driver-rs on Windows. **Parameters on every platform** | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `health_report` Single-call end-to-end driver diagnostics. Designed to let downstream consumers ship one stable call instead of stitching together check_permissions, doctor, version, bundle attribution, and platform capability status. On macOS, prompt-capable direct capture is deliberately skipped. For standalone CuaDriver, use `cua-driver permissions grant` to verify it explicitly. For an embedded or linked driver, use the host's permission flow for the app or executable macOS attributes this process's permission request to; the standalone command does not verify the host's capture readiness. cua-driver owns the health model; consumers stay thin. Input: all optional: { "include": ["<check_name>", ...], // run only these "skip": ["<check_name>", ...] // skip these } If both are given, `include` wins. Canonical check names: macOS : binary_version, platform_supported, session_active, bundle_identity, tcc_accessibility, tcc_screen_recording, ax_capability, screen_capture_capability Windows: binary_version, platform_supported, session_active, ax_capability (via UIA), screen_capture_capability (via DXGI) Linux : binary_version, platform_supported, session_active, ax_capability (via AT-SPI), screen_capture_capability (via X11) Output: stable contract, schema_version="1": { "schema_version": "1", "platform": "darwin" | "win32" | "linux", "driver_version": "<semver>", "overall": "ok" | "degraded" | "failed", "checks": [ { "name": "<one of the canonical names above>", "status": "pass" | "fail" | "skip", "message": "<one-line summary, always present>", "hint": "<remediation step, present when status=fail>", "data": { /* check-specific structured fields */ } }, ... ] } `overall` rules: - `ok` : every non-skipped check passes - `degraded`: at least one non-core check fails (binary is still usable) - `failed` : any core check fails (binary_version, platform_supported, session_active) Stability: schema_version="1" is the contract. Future breaking changes will be `"2"`. Adding new check names under the same schema_version is non-breaking; consumers must tolerate unknown check names. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `include` | `string[]` | | Only run these checks (canonical names). Wins over `skip`. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | | `skip` | `string[]` | | Skip these checks (canonical names). Ignored when `include` is set. | ## `check_for_update` Check the saved stable/nightly Cua Driver channel for a release on GitHub. Returns current and selected channels, current and latest versions, an `update_available` boolean, the install one-liner, and the release notes URL. Read-only: never installs. Pacman-owned Linux executables return package-manager guidance without checking GitHub. Mirror of `cua-driver check-update --json`. Effect: read-only, idempotent. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | ## `install_extension` Preview or install one Driver-managed optional extension. The first call without confirm returns the exact signed artifact, destination, license, source, and trust plan without mutation. Re-call with confirm=true to perform that exact verified installation. Effect: destructive. Platforms: macOS, Linux, Windows. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `confirm` | `boolean` | | Install the previewed extension. Omit or false for a read-only plan. | | `name` | `"perception"` | required | Extension to preview or install. Only perception (the local visual-region parser used by parse_visual_regions) is available. | | `session` | `string` | | For multi-call work, prefer a short public session label and repeat it on every call that accepts it. Omit it to use the authenticated transport's implicit lifecycle session. | **Example arguments** ```json {"name":"perception"} ``` --- # Cua Spaces The Cua Spaces app and SDK: register any sandbox or machine once, then stream it, run agents in it and teleport apps into it. A Space is a desktop your agents can use and you can watch: a container or VM on your own Mac, one of your team's machines, or [your own cloud](</docs/cua-sdk/guides/your-cloud>) (AWS, Google Cloud or Modal). Each runs cua-spacesd, which streams the screen, takes input through Cua Driver, and receives teleported apps and files. Cua Spaces is source-available under the FSL-1.1-MIT. For teams, join the [waitlist](https://cua.ai/teams). The **Cua Spaces app** puts your Spaces in the menu bar and notch, so you can watch them, open them and teleport apps in with a drag. It launches at login (**Settings > General > Launch Cua Spaces at login**, on after setup unless you untick it), so the Spaces this machine provides, your persistent agents and Cua Volume come back after a restart. The **Cua SDK** gives your own apps the same Spaces: create them, stream them and run agents in them from Python, TypeScript, Swift or Rust. | Kind | Id | Comes from | |------|----|------------| | Local | `local:<name>` | `spaces.create(options)`: a new Space, on this machine by default | | Direct | `direct:<host:port>` | `spaces.add(url, token)`: an existing machine | | Relay | `relay:<machine-id>` | Your machines set up with `cua host setup`, listed automatically | The app, the `cua` CLI, the SDKs and `cua daemon` share one registry (`~/.cua/spaces.json`). ## What you can do with a Space | Task | Guide | |------|-------| | Create or add a Space, from any image | [Add a Space](</docs/spaces/guides/add-a-space>) | | Reach this machine from your other devices | [Unattended access](</docs/spaces/guides/unattended-access>) | | Watch a desktop or one window, with audio | [Stream a desktop](</docs/spaces/guides/stream-a-desktop>) | | Send a file or folder into a Space | [Send files](</docs/spaces/guides/send-files>) | | Move a signed-in app session into a Space | [Teleport an app](</docs/spaces/guides/teleport-an-app>) | | Give an AI agent Spaces tools, or run agents inside a Space | [Use from an agent](</docs/spaces/guides/use-from-an-agent>) | | See an app built on the Spaces SDK | [Cua Bots](</docs/spaces/examples/cua-bots>), [infinite canvas](</docs/spaces/examples/infinite-canvas>), [examples in other languages](</docs/spaces/examples/other-languages>) | Start with the [Quickstart](</docs/spaces/quickstart>): install the app and CLI, then add your first Space. --- # Quickstart Install the Cua Spaces app and the cua CLI, run a local Space in Docker, and register it from code. Install Cua, run a Linux desktop locally, and register it as your first Space. ## Prerequisites - macOS, Linux or Windows. The Cua Spaces app for macOS needs macOS 26 or later. - Docker, for the local Space. ## Run **macOS and Linux** ```bash skip="host-install" curl -fsSL https://cua.ai/install.sh | sh ``` **Windows** ```powershell skip="host-install" irm https://cua.ai/install.ps1 | iex ``` In a terminal it first shows a checklist: the `cua` CLI, the Cua Spaces app (on by default on macOS; pass `--select spaces` on Linux, `-Select spaces` on Windows), the cua-driver MCP and skill for your agents, and hosting this machine. Then it signs you in and offers to set up your AI coding agents. Flags: `--select ITEMS`, `--only ITEMS`, `--cli-only`, `--app-only`, `--mode host|client`, `--version X`, `--no-onboarding`, `--dry-run`, `--yes` (`-Select`, `-CliOnly`, ... on Windows). GUI installers are on [GitHub releases](https://github.com/trycua/cua/releases). Start the canonical Linux image with a token: ```bash skip="no-runner" TOKEN=$(openssl rand -hex 24) docker run -d --name my-space -e CUA_ENV_TOKEN=$TOKEN -p 127.0.0.1:3211:3211 \ ghcr.io/trycua/linux:24.04 ``` Register it and run a command in it (use `http://127.0.0.1:3211` and your `$TOKEN`): **Python** ```python test="docs" id="add-direct" session="add-direct" prelude="space-url" import asyncio import cua async def main(): spaces = cua.embedded().spaces() # or cua.connect().spaces() info = await spaces.add("http://10.0.0.5:3211", "TOKEN", "lab") space = await spaces.space(info.id) out = await space.bash("uname -a", None) print(info.id, out.stdout) asyncio.run(main()) ``` **TypeScript** ```ts test="docs" id="add-direct-ts" prelude="space-url" import { embedded } from "@trycua/cua" const spaces = embedded().spaces() // or connect().spaces() const info = await spaces.add("http://10.0.0.5:3211", "TOKEN", "lab") const space = await spaces.space(info.id) console.log(info.id, (await space.bash("uname -a", undefined)).stdout) ``` **Swift** ```swift test="swift" id="add-direct-space" import Cua let spaces = try Cua.embedded().spaces() // or Cua.connect(address: nil, token: nil) let info = try await spaces.add(url: "http://10.0.0.5:3211", token: "TOKEN", name: "lab") let space = try await spaces.space(space: info.id) print(try await space.bash(command: "uname -a", timeoutMs: nil).stdout) ``` ## Result The program prints the Space id (`direct:127.0.0.1:3211`) and the guest's `uname -a`. The Space is now in the shared registry, so the app and the CLI see it too: ```bash test="cli-shape" id="cua" cua --version cua auth status cua spaces ls ``` ## Clean up ```bash skip="no-runner" docker rm -f my-space ``` ## Next - [Add a Space](</docs/spaces/guides/add-a-space>): create one locally or in the cloud, from any image. - [Stream a desktop](</docs/spaces/guides/stream-a-desktop>). - [Use Spaces from an agent](</docs/spaces/guides/use-from-an-agent>). - [Cua Bots](</docs/spaces/examples/cua-bots>): an example app of persistent bots, each with its own Space. --- # Add a Space Create a Space on your machine, from any image, follow or cancel the create, add a GPU, add an existing machine, then list, delete or remove it. The [Quickstart](</docs/spaces/quickstart#run>) adds an existing machine by URL. The same `spaces` object also creates Spaces. Ids are the sandbox refs `cua sb ls` prints (`local:<name>`, `cloud:<name>`, `direct:<host:port>`, `relay:<machine-id>`); a bare name works when it matches one Space. ## Create a Space ```python skip="pseudo" created = await spaces.create(cua.SpaceCreateOptions(cpus=2, memory_mb=4096)) space = await spaces.space(created.space.id) ``` Runs `ghcr.io/trycua/linux:24.04` (or `image`) with a fresh env token. `on`, `kind` and `runtime` work as for [sandboxes](</docs/cua-sdk/concepts/how-sandboxes-work#where-what-kind-which-engine>); without `on`, the Space runs where `cua config get default.on` says (local unless you changed it; the Spaces app sets it in Settings, Default location). `cua runtime doctor` shows what this host has. `reuse=True` returns a registered, reachable Space in the same location first (`created.reused` says so). With `wait=False`, `created.pending_id` names a Space that is not ready yet. ## A Space from any image `create` takes `image`, `command`, `env` and `services`. An image that serves MCP becomes a Space of that service, with no cua-spacesd: ```python skip="placeholder" created = await spaces.create(cua.SpaceCreateOptions( image="<your-mcp-server-image>", command=["python", "-m", "acme_mcp", "--port", "8765"], services={"mcp": 8765}, )) space = await spaces.space(created.space.id) tools = await space.list_tools("mcp") result = await space.call_tool("add", '{"a": 2, "b": 3}', "mcp", None) ``` | Works on every Space | Needs cua-spacesd in the image | |---|---| | Lifecycle, declared services, tunnels, public URLs, `list_tools` / `call_tool` with `service` | `bash`, files, streaming, presence, teleport, agent runs, the `driver` tool registry | Spacesd features on an image without it fail with `CapabilityMissing`. The canonical images and machines set up with `cua host setup` run cua-spacesd. **Note** In the cloud, `command` and `env` work on container and VM images alike. Keep secrets out of `env`. ## Progress ![A macOS Space downloading its image: bytes, rate and time left](</docs-assets/img/spaces/spaces-create-progress.png>) The app shows a progress bar, and under it while the image downloads `4.2 of 22.1 GB · 85 MB/s · about 4 min`. `cua spaces create` prints the same on one updating line. In code, pass a listener: ```python skip="pseudo" class Show(cua.SpaceCreateListener): def on_progress(self, p): print(p.space, p.phase, p.bytes_done, p.bytes_total, p.bytes_per_second) created = await spaces.create_with_progress(cua.SpaceCreateOptions(image="macos"), Show()) ``` | Field | Value | |---|---| | `phase` | `preparing`, `pulling`, `creating`, `booting`, `waiting_for_services`, `connecting` | | `fraction` | 0 to 1, when the step has one | | `bytes_done`, `bytes_total`, `bytes_per_second` | while an image downloads | | `space` | the id the Space will have (`local:<name>`) | macOS images report bytes with a Lume whose pull API reports them (`downloadedBytes`); an older Lume reports only a percent. ## Cancel a create ![Cancel pressed mid-download: the Space is being cleaned up](</docs-assets/img/spaces/spaces-create-cancel.png>) - App: **Cancel** in the toolbar of a Space that is being created. - CLI: Ctrl-C during `cua spaces create` or `cua sb create` (exit code 130), or from any terminal: ```bash test="cli-shape" id="cua-spaces-cancel" cua spaces cancel local:space-1a2b3c ``` - SDK: `cancel_create` takes the create's `create_id`, the id the Space will have (`space` in every progress report) or its name, and returns once the cleanup is done. The create fails with `CuaError.Cancelled`. ```python skip="pseudo" outcome = await spaces.cancel_create("local:space-1a2b3c") print(outcome.state, outcome.message) # cancelled, not_creating or already_created ``` Sandboxes: `await sandboxes.cancel_create(name)` returns what was removed, or `None`. A cancel works across processes and after a daemon restart. | Removed | Kept | |---|---| | The VM or container and its disks, the cloud claim, the relay machine, the registry entry, the create's journal | Anything that existed before the create | | Partial downloads | Finished image layers in the engine's cache (Docker's; Lume's when its cache is on), so the next create resumes | A Lume without `POST /lume/pull/cancel` cannot stop a pull: the create still cancels, and the image finishes downloading in the background as a reusable base. ## GPU acceleration ```bash test="cli-shape" id="cua-spaces-gpu" cua spaces gpus cua spaces create macos --gpu paravirtual ``` `--gpu` alone (or `gpu="auto"`) picks the runtime's own option; the SDK takes `SpaceCreateOptions(gpu=...)` and `SandboxCreateOptions(gpu=...)`, and `spaces.gpu_support(on)` lists what exists here. | Runtime | Option | Where | |---|---|---| | Lume (macOS VMs) | `paravirtual`, GPU acceleration (experimental) | Apple silicon | | QEMU | `virgl` | Linux hosts whose QEMU has virglrenderer, egl-headless and a render node; none on macOS | | Containers | `nvidia` | Linux with the nvidia runtime, runc (not gVisor); none on macOS (Docker runs Linux in a VM without GPU access) | | Modal | `T4`, `L4`, `A10G`, `A100`, `H100` | | | E2B, Daytona | none | | ![New Space, Resources: the GPU acceleration (Experimental) checkbox for a macOS Space](</docs-assets/img/spaces/spaces-new-space-gpu.png>) In the app it is the GPU checkbox on the Resources step of New Space. For `paravirtual`, cua applies the unrestricted paravirtualized GPU feature level before each start of the VM and removes it when the last GPU VM is deleted, if cua set it. Metal apps in the guest may still need the per-workload shim: [Unlock Metal for a workload](</docs/lume/guides/gpu-passthrough>). ## List, delete, remove ```bash test="cli-shape" id="cua-spaces-ls" cua spaces ls ``` Lists registered Spaces and, when you are signed in, your machines on the relay (`relay:<machine-id>`, no `add` needed). Register or forget one from a shell: ```bash test="cli-shape" id="cua-spaces-add-rm" cua spaces add 10.0.0.5:3211 --token "$CUA_ENV_TOKEN" --name studio cua spaces rm studio ``` A [Tailscale](https://tailscale.com) address works here too (its IP or MagicDNS name as the host:port): `cua spaces add` reaches it like any other address and the Space registers as `direct:<addr>`. That only attaches one already-running Space; hosting Spaces for `on="host:<name>"` still goes through the cua.ai relay, set up with [`cua host setup`](</docs/start-here/host-spaces-on-your-spare-mac>). `spaces.delete(id)` deletes a Space's sandbox and forgets it (a Space added by address is only forgotten); `spaces.remove(id)` forgets a Space without touching it. In TypeScript the methods are `create`, `delete_`, `add` and `remove`, with options from `@trycua/cua/spaces`. Agents use the MCP tools `create_space`, `delete_space`, `add_space` and `remove_space` ([Use from an agent](</docs/spaces/guides/use-from-an-agent>)). --- # Set up unattended access Make this machine a Space you reach from your other devices: cua host setup installs cua-spacesd as a service that joins the cua.ai relay, or serves on ip:port. `cua host setup` installs cua-spacesd as a service. By default it joins the cua.ai relay over outbound WSS as your account: no port forwarding, no token to copy. Your other devices, once [enrolled](#reach-it-from-your-devices), then see this machine as `relay:<machine-id>` in `cua spaces ls` and the app. The app's **Set up for access** button runs the same setup. ```bash test="cli-shape" id="cua-auth-login" cua host setup # signs in to cua.ai for this command if needed ``` The host keeps only its own machine token. When the machine is not signed in, setup signs you in for that one command and stores no session, so a dedicated host holds no account credentials. The machine token lets the host serve itself and stop sharing; it cannot list or reach your other machines, change who the machine is shared with, or enroll devices. | Option | Effect | |--------|--------| | `--remote` | When sign-in is needed, print a device code to approve in a browser on another machine (for example over SSH). The account session is not saved. Cannot be combined with `--direct`. | | `--direct ip:port` | Serve on your LAN (or a forwarded port) with a local env token instead of the relay. No account needed. | | `--relay URL` | Another relay (`CUA_RELAY_URL`, default `https://relay.cua.ai`); any self-hosted `cua-relay` works. | | `--name NAME` | Display name (default: host name). | | `--allow ACCOUNT` | Also allow this account (id or email). Repeatable. | | `--runner KIND` | `auto`, `systemd`, `launchd`, `windows-task` or `process`. | | `--profile spare` | A spare machine: do not share its desktop, create Spaces for your devices (`--no-desktop --provide-spaces`). See [Host Spaces on your spare Mac](</docs/start-here/host-spaces-on-your-spare-mac>). | | `--no-desktop` / `--provide-spaces` | Each of the two settings on its own. `cua host config` changes them later. | Running `setup` again updates the configuration and rotates the machine token. The host checks the relay's signed identity assertion against the owner and the allowlist; the env token never leaves the machine. | OS | Service | |----|---------| | macOS | LaunchAgent `com.trycua.spacesd.host` in your GUI session. Grant cua-spacesd **Screen Recording** and **Accessibility**, and [Cua Driver](</docs/cua-driver/quickstart>) its own permissions. | | Linux | systemd unit `cua-spacesd-host.service` (user unit; system unit as root). | | Windows | Scheduled task `Cua\SpacesdHost` at logon, in your interactive session. | ## Check, pause, remove ```bash test="cli-shape" id="cua-host-status" cua host status # setup, service, who is connected; missing permissions cua host stop # disconnect everyone and refuse new connections cua host start # share again cua host remove # unregister, uninstall the service, delete ~/.cua/host ``` `cua host stop` turns sharing off locally first, so it holds even when the relay is unreachable. In direct mode `cua host status` prints the URL and the env token's path; add the machine elsewhere with [`spaces.add(url, token)`](</docs/spaces/quickstart#run>). **Warning** Everyone on the allowlist gets full control of this desktop. Keep the list short and run `cua host stop` when you do not need it. To let someone only watch, [share it as a viewer](</docs/spaces/guides/share-a-space>). ## Share with other accounts `--allow` at setup, or later from an enrolled device, shares the machine with another account (id or verified email). Sharing asks for confirmation; unsharing cuts that account's open connections. ```bash test="cli-shape" id="cua-host-share" cua host share friend@example.com cua host unshare friend@example.com ``` ## Reach it from your devices A device that lists or opens your machines through the relay is enrolled once with a second factor. A stolen account session alone is not enough. Hosting never enrolls a machine as a client. - **Sign in:** `cua devices enroll` right after `cua auth login` enrolls the device at once. A device that enrolled before re-enrolls on its own when you sign in again (in the CLI or the app), so an expired or replaced key never needs another device. - **Approve:** on a long-lived session, `cua devices enroll` shows a one-time code instead. Approve it on an enrolled device with `cua devices approve <code>` or by its id (`cua devices approve dev_...`), or in the app, which asks for Touch ID. Each device reports a hash of its machine's identity. When a new key of the same machine enrolls (another build of the app, a lost key), it replaces the old record, so one machine is one device. Builds of cua that store the key differently (the OS keychain for signed builds, a `0600` file otherwise) share one key: the signed build moves the file's key into the keychain. ```bash test="cli-shape" id="cua-devices" cua devices enroll cua devices approve K7QX-M2RP cua devices approve dev_0123456789abcdef01234567 cua devices ls cua devices audit # who opened which machine, from which device, and when cua devices revoke dev_0123456789abcdef01234567 ``` The device key stays in the OS keychain (or a `0600` file where there is none). Enrollment lasts 30 days. After that, a fresh sign-in or one approval re-verifies the device. Until then, unattended agents on an enrolled device keep working without prompts. **Re-signing in is enough only for your own machine**: the same machine enrolling again with a new key (another build, a lost key), or your account's first device. A brand-new device on an account that already has an enrolled device needs either an approval from an enrolled device, or a sign-in whose token itself proves a second factor (MFA) with your identity provider -- a verified email alone no longer enrolls it silently. Either way, every enrolled device is notified (the app's notification feed and `cua devices audit`), so a device you do not recognize does not slip in unnoticed. Relays can run a **migration grace period** right after enrollment is turned on, during which a signed-in session alone is let through (flagged and audited) while apps catch up. It is off by default and, on `relay.cua.ai`, only ever runs for a short, announced window right after a rollout; check `cua devices ls` (it reports `enforce_after`) or `cua host status` if you are unsure whether it is currently open. In the Cua Spaces app, **Settings > Devices** shows the same: this device's enrollment (enrolled until a date, or when a grace period ends), your devices with Rename and Revoke, and Recent Access (which device opened which machine, and when). **Enroll** offers a fresh sign-in or a one-time code to approve elsewhere. When another device asks to join and needs an approval, the app shows a notification and an approval sheet; Approve asks for Touch ID or your login password first, and Deny revokes that device. A device enrolled by a sign-in that proved MFA shows up already enrolled, with no approval step, the same as your own machine re-signing in. A machine registered without an enrolled device's signature or a sign-in that proved MFA is recorded as unconfirmed on the relay (`GET /v1/machines`' `confirmed` field) and can be confirmed from an enrolled device (`POST /v1/machines/{id}/confirm`); surfacing this as a "New machine" badge in the apps is tracked separately. --- # Share a Space Let another cua.ai account watch or use one of your Spaces through the relay, and reach a Space from your other devices. Share a Space with another cua.ai account by email or account ID. A **viewer** joins with their own named cursor and watches; an **editor** can also use the Space. They open it as `relay:<machine>` from `cua spaces ls` or the app. ```bash test="cli-shape" id="cua-spaces-share" cua spaces share local:studio bob@example.com # viewer cua spaces share local:studio bob@example.com --role editor # change the role cua spaces shares local:studio # who, and who is connected cua spaces unshare local:studio bob@example.com # at once cua spaces unshare local:studio --all # nobody; leaves the relay ``` In the app, **Share** in a Space's toolbar opens the same list. ## What each role can do | | Viewer | Editor | |---|---|---| | Presence, with their own named cursor | yes | yes | | Watch the desktop stream | view only | yes | | Pointer and keyboard input | refused | yes, as a human | | Shell, files, clipboard, MCP | refused | yes | The Space's own cua-spacesd enforces the role, not the caller. A refused call fails with `PermissionDenied` (`view-only share: <who> cannot call <method>`). ## How it works Sharing uses the relay and allowlist that [unattended access](</docs/spaces/guides/unattended-access>) uses: - A host (`relay:<id>` from `cua host setup`) is shared directly. - Any other Space is published on the relay on its first share. The owner's account registers a machine for it, and the Space's own driver dials out over WSS, so no inbound port opens. - The relay keeps two lists per machine: `allow` (editors) and `viewers`. - Invitees need a verified email or account ID and an [enrolled device](</docs/spaces/guides/unattended-access#reach-it-from-your-devices>). - Removing someone, or changing their role, cuts their open streams at once. ```rust test="excerpt" id="rs-share-space" source="libs/cua/crates/cua-spaces/tests/share_space_e2e.rs#rs-share-space" let shares = ada .share_space(&info.id, "bob@example.com", ShareRole::Viewer) .await .unwrap(); ``` The SDK call in other languages is `space.share(who, role)`, with `space.unshare(who)` and `space.shares()`. The MCP tools are `share_space`, `unshare_space` and `space_shares`. ## Confirmation Sharing asks you first: - Through `cua daemon` (the app, `cua mcp`, agents), with Touch ID or your login password. An agent can ask to share a Space; only you can allow it. - The `cua` CLI without a daemon asks on the terminal. Pass `--yes` to confirm up front. Removing someone needs no confirmation. ## Reach a Space from your other devices To reach a Space from your phone or another computer without sharing it with anyone, publish it on the relay: ```bash test="cli-shape" id="cua-spaces-relay-register" cua spaces relay-register local:studio # prints relay:<machine> cua spaces relay-unregister local:studio ``` ```rust test="excerpt" id="rs-relay-register" source="libs/cua/crates/cua-spaces/tests/share_space_e2e.rs#rs-relay-register" let reg = ada.relay_register(&info.id).await.unwrap(); ``` Only your account's enrolled devices reach it until you share it. The MCP tools are `relay_register_space` and `relay_unregister_space`. ## Audit - `cua spaces shares` lists who has access now. - The owner's share log (`~/.cua/shares-audit.jsonl`, hash-chained) records every share, removal, attach and detach. - The relay's audit log (`cua devices audit`) records `viewer_added`, `share_added`, `share_removed` and each shared access. - The Space's access log records every relay caller, including refused view-only calls (`refused ProcessService (view-only share)`). ## Limits | Space | Shareable | Why | |---|---|---| | Host (`relay:<id>`) | yes | Already on the relay. | | Local container or VM | yes, while this machine runs it | Its driver dials the relay. Suspending it disconnects everyone. | | Cloud | yes | Its driver dials the relay from the cloud. | | Added by address (`direct:`) | yes, if its driver supports `relay_attach` | Older drivers report `capability_missing`. | | A Space shared with you | no | Only the owner can share (`permission_denied`). | - The relay shares with accounts, not teams: share with each member. - Presence cursors travel on the relay stream, not QUIC datagrams, so remote cursors update a little less often than on your own network. - A cloud Space that is released (for example a paused persistent agent) leaves the relay. Share it again after it is created again. **Warning** An editor gets full control of the Space, including anything signed in there. Share as a viewer unless they need to act. --- # Stream a desktop Stream a Space desktop or one window with audio: open a media ticket, pick a codec and transport, and start from the streaming examples. `OpenMedia` on the Space's cua-spacesd returns a short-lived **ticket** bound to one target, codec and policy; the media then flows over a WebSocket or QUIC. ```bash test="cli-shape" id="cua-spacesd-targets" cua spacesd targets http://10.0.0.5:3211 --windows # displays and windows ``` ```python skip="no-runner" import cua opts = cua.SpaceStreamOptions(app_name="Firefox", audio=True, max_fps=30, codecs=["h264"]) ticket = await space.open_stream(opts) # hand ticket.ws_url to a player media = await space.attach_stream(ticket, frame_sink, audio_sink) # encoded frames, as they arrive await space.close_stream(ticket.media_session_id) ``` `attach_stream` delivers the media socket's encoded frames and audio packets as they arrive; keyframe gating, loss recovery and decoding are yours. The streaming client that does them for you, `space.stream_session(...)` with decoded frames, ships with Cua Spaces (source-available, FSL-1.1-MIT): it runs in the Spaces apps and in processes that register it, and raises `HostCapabilityMissing` elsewhere. The wire contract (`StreamService`, the RCDP messages and framing) is open source, so any client can attach to a ticket. The same calls in TypeScript, Rust and Swift, from [the examples in other languages](</docs/spaces/examples/other-languages>). The first frame is a keyframe. **TypeScript** ```ts test="excerpt" id="ts-stream" source="samples/openkoalabot-example-ts/src/core/runtime.ts#ts-stream" const ticket = await space.openStream( SpaceStreamOptions.create({ maxFps: opts.maxFps, maxDimension: opts.maxDimension }), ) const session = await space.attachStream(ticket, counter, undefined) ``` ```ts test="excerpt" id="ts-ticket" source="samples/openkoalabot-example-ts/src/core/runtime.ts#ts-ticket" const ticket = await space.openStream( SpaceStreamOptions.create({ maxFps: opts.maxFps, maxDimension: opts.maxDimension, codecs: ["h264"] }), ) ``` **Rust** ```rust test="excerpt" id="rs-stream" source="samples/openkoalabot-example-tauri/src-tauri/core/src/stream.rs#rs-stream" pub async fn open_ticket(space: &Space, max_fps: u32, max_dimension: u32) -> Result<StreamTicket> { Ok(space .open_stream( StreamTarget::Display(None), StreamOptions { max_fps, max_dimension, ..Default::default() }, ) .await?) } ``` **Swift** ```swift test="excerpt" id="sw-stream" source="samples/openkoalabot-example-swift/Sources/OpenKoalaBotExample/Scenario/ScenarioRunner.swift#sw-stream" let provider = SpaceStreamProvider(space: space) provider.maxFPS = UInt32(step["maxFps"] as? Int ?? 5) let counter = FrameCounter() let session = try await provider.openSession(.desktop, frames: counter, audio: nil) ``` | Option | Meaning | |--------|---------| | `window_id`, `app_name`, `display` | Target (none: the primary display). | | `codecs` | Preference order: `h264`, `bgra`, `png`. | | `max_fps`, `max_dimension` | Limits (`max_fps` 0 means 30). | | `audio` | Add an Opus track. | | `policy` | `view_only` (default), `background_only`, or `allow_activation` for input over the media socket. | | `ticket_ttl_ms` | Ticket lifetime (default 60 s, at most 10 min). | Without the SDK: `cua spacesd call <url> StreamService/OpenMedia '{"maxFps": 30}'`. Agents use the MCP tool `stream_endpoint`. A Space without `desktop_stream`, `window_stream` or `audio.desktop` raises `CapabilityMissing`. ## Transport and codecs | | Details | |---|---| | WebSocket `/media?ticket=...` on 3211 | Default. Works through the Fleet gateway, the relay and browsers. | | QUIC (ALPN `rcdp/2`) on UDP 3212 | Lower latency when you reach the guest directly; certificate pinned from `OpenMedia`. | | Video | H.264 (Annex B, SPS/PPS on every keyframe). Encode: VideoToolbox (hardware) on macOS, OpenH264 (software) in Linux Spaces. SDK decode: VideoToolbox (hardware) on macOS, OpenH264 elsewhere. Browser viewers decode with WebCodecs. | | Audio | Opus 48 kHz stereo, 20 ms frames, same clock as video (A/V skew within ±40 ms). | **Note** NVENC, VA-API, QSV, AMF and Media Foundation are detected but not used yet. Per-app audio and microphone uplink need PipeWire (the Linux Spaces image). The wire format is in [`libs/cua/proto/MEDIA.md`](https://github.com/trycua/cua/blob/main/libs/cua/proto/MEDIA.md). ## Presence The operator and each agent join a Space with an identity and move a shared cursor on its desktop. ```ts test="excerpt" id="ts-presence" source="samples/openkoalabot-example-ts/src/server/app.ts#ts-presence" const session = await space.joinPresence( { id: `openkoalabots-${randomBytes(4).toString("hex")}`, displayName: b.displayName || "Operator", color: "", agent: false }, 10_000n, ) ``` ```ts test="excerpt" id="ts-cursor" source="samples/openkoalabot-example-ts/src/server/app.ts#ts-cursor" await s.presence.session.updateCursor({ displayId: "", windowId: undefined, x: b.x ?? 0, y: b.y ?? 0, visible: true, pressed: false, shape: "arrow", shapeSource: "unspecified", atMs: 0, receivedMs: 0 }) ``` Python: `space.join_presence(identity, timeout_ms)`. Rust: `space.join_presence(Identity { .. }, timeout)`. Swift: `native.joinPresence(identity:timeoutMs:)`. ## Examples Each example streams the desktop and one window with audio, clicks, and verifies, against `CUA_ENV_URL` and `CUA_ENV_TOKEN`: [Rust](https://github.com/trycua/cua/tree/main/examples/streaming/rust), [Python](https://github.com/trycua/cua/tree/main/examples/streaming/python), [TypeScript (Node)](https://github.com/trycua/cua/tree/main/examples/streaming/typescript-node), [TypeScript (browser)](https://github.com/trycua/cua/tree/main/examples/streaming/typescript-web), [Swift](https://github.com/trycua/cua/tree/main/examples/streaming/swift), [Kotlin](https://github.com/trycua/cua/tree/main/examples/streaming/kotlin). ```bash skip="no-runner" libs/cua/bench/streaming/scripts/space.sh start # a throwaway local Space eval "$(libs/cua/bench/streaming/scripts/space.sh env)" ``` [`libs/cua/bench/streaming`](https://github.com/trycua/cua/tree/main/libs/cua/bench/streaming) benchmarks time to first frame, fps, bandwidth and latency per codec, transport and language against `budgets.json`. --- # Send files Send a host file or folder into a Space: it lands in ~/Downloads, verified by SHA-256. `send_file` copies a host file or folder into the Space user's `~/Downloads` (or a subfolder). Every file is verified by SHA-256 before it is published; a transfer that does not land is an error. For a folder, `.gitignore`, `.ignore` and a root `.dockerignore` are honored (`respect_ignore_files`). Nothing already in the Space is replaced by default: a file of the same name is kept and the new copy lands as `name (1).ext` (`conflict: "rename"`). Pass `conflict: "skip"` to keep only the existing file, or `conflict: "overwrite"` to replace it. From [the examples in other languages](</docs/spaces/examples/other-languages>): **TypeScript** ```ts test="excerpt" id="ts-send-file" source="samples/openkoalabot-example-ts/src/core/app.ts#ts-send-file" const report = await this.space.sendFile(localPath, { targetDirectory: subdir, respectIgnoreFiles: true, conflict: "rename", }) if (!report.verified) throw new Error(`send_file of ${localPath} was not verified`) ``` **Rust** ```rust test="excerpt" id="rs-send-file" source="samples/openkoalabot-example-tauri/src-tauri/core/src/files.rs#rs-send-file" let report: SendFileReport = space .send_file( path, SendFileOptions { subdir: subdir.to_string(), ..Default::default() }, ) .await?; ``` **Swift** ```swift test="excerpt" id="sw-send-file" source="samples/openkoalabot-example-swift/Sources/OpenKoalaBotExample/Scenario/ScenarioRunner.swift#sw-send-file" let report = try await handle().sendFile( localPath: file.path, options: SpaceSendFileOptions(targetDirectory: step["subdir"] as? String, respectIgnoreFiles: true, conflict: "overwrite")) ``` Python: `await space.send_file(path, cua.SpaceSendFileOptions(target_directory="inbox"))`. Agents use the MCP tool `send_file` ([Use from an agent](</docs/spaces/guides/use-from-an-agent>)); options are in the [`send_file` reference](</docs/spaces/reference/commands-and-files#send_file>). --- # Teleport an app Bring an app from this machine into a Space: install it there, open your files in it, or move its signed-in state, with a consent screen for everything that leaves. **Teleport an app** lists every app installed on this machine and says what teleport can do with each one in a Space: | Level | What happens in the Space | Moves | | --- | --- | --- | | Full | the app is there, and a provider can import its signed-in state | the app; the app with files; the app with its state | | Install only | the app is installed from the pinned, checksum-verified install manifest, and opens empty or with your files | the app; the app with files | | Unsupported | shown disabled, with the reason (for example "not published for aarch64 Linux") | nothing | Full today: `firefox`, `chrome`, `slack`, `discord`, `vscode`, `notion`, `unity-hub`, `steam`, `whatsapp` (macOS) and `claude-code`. Install only: Blender (x86_64). Apps come from `/Applications` and `~/Applications` on macOS, `.desktop` entries on Linux and the Start Menu on Windows. Before anything runs, the consent screen lists each install (with its pinned version and checksum), each host file or folder, and each state item. Secrets (cookies, tokens, logins) need an explicit acknowledgement, and on macOS the OS asks for Touch ID or your password before they are read. For a browser (Chrome, Firefox), the app with its signed-in state moves the tabs, preferences and site data, but no credentials. Three separate opt-ins, each off until you tick it, add them: **Keep me signed in** (the session cookies), **Saved passwords** and **Browsing history**. In the SDK these are the entry's `sensitive_groups` and `TeleportPlanOptions.sensitive_groups`. Chrome (and Chromium) encrypts cookies and saved passwords at rest with a per-machine **Safe Storage** key held in this Mac's login Keychain, so they cannot simply be copied to another machine. When **Keep me signed in** is checked, teleport decrypts the cookies here (the OS asks you to allow that Keychain read) and carries the decrypted values, not the source machine's key; the Space re-encrypts them under its own Safe Storage key before Chrome ever reads them. The source's key never leaves this Mac. **Warning** For Chrome, **Saved passwords** here still moves `Login Data` as an encrypted file, which only a Chrome that already shares this Mac's Safe Storage key can read -- a Space's Chrome does not, so a password moved this way does not yet decrypt there. (For Firefox, **Saved passwords** decrypts here with the profile's own `key4.db` master key and re-encrypts under the destination profile's own key, the same way as **Keep me signed in**, so it does decrypt there -- unless a master password is set, which is refused rather than guessed at.) To actually sign in with a saved Chrome password, use the [Keyvault](</docs/spaces/guides/keyvault>) (`cua keyvault import-passwords`) instead: it decrypts on import and signs in through [site login](</docs/spaces/guides/site-login>), without ever exposing the password to an agent. **Keep me signed in** (cookies) does not have this limitation on either browser. ### Choosing sites and sending from the Keyvault In Cua Spaces, **Keep me signed in** lists the browser's sites with counts (for example `github.com, 12 cookies, 3 storage values`) so you send the sites you mean to, not the whole profile. It starts from what you sent to that Space last time, or from the sites that look like they keep a sign-in; an identity provider (Google, Microsoft, Apple, Okta) is never picked for you. Other lines of the consent screen can be turned off one by one. Cookies are read once to build the list's counts from the plaintext columns of Chrome's store; nothing is decrypted until you teleport. If the Keyvault already holds items for the app (from **Save to Keyvault**), the review can **send from the Keyvault** instead: you pick from the saved items, nothing is read from this Mac, and macOS does not ask for Keychain access. See [Keyvault](</docs/spaces/guides/keyvault>). ## In Cua Spaces - **Teleport an app…** in a Space's window or viewer opens the picker: search, recent apps first, then what to move, then the consent screen and progress. - **Drag an app** from Finder or the Dock onto a Space tile or window: the picker opens with that app. Dropped files and folders still go to the Space's `~/Downloads`. - **Drag a window** of any supported app: the notch shows **Teleport to Cua** with a preview of that window; drop it on a Space to teleport the app. This needs the Accessibility permission (macOS). Files land in `~/Downloads/Teleported` in the Space and open in the app. ![Teleport an app: the apps on this Mac, narrowed to an aarch64 Linux Space](</docs-assets/img/spaces/spaces-teleport-picker.png>) ![The consent screen: the pinned install and the folder that leaves this Mac](</docs-assets/img/spaces/spaces-teleport-consent.png>) ![Visual Studio Code open in the Space with the teleported folder](</docs-assets/img/spaces/spaces-teleport-in-space.png>) ## Where it runs App teleport runs in the Cua Spaces apps and in the `cua` that ships with them (`cua teleport`, below). It is part of Cua Spaces, source-available under FSL-1.1-MIT, and not in the open source SDK packages: SDK programs in Python, TypeScript, Swift or Kotlin reach session teleport through `Space.teleport` against the Cua Spaces daemon ([below](#session-teleport)). To build your own Spaces UI in Swift, the Cua Spaces app export (`CuaSpacesFFI`, [reference](</docs/cua-sdk/reference/spaces/teleport-apps>)) has the calls the apps use: `Teleport.catalog`, `plan` and `run`. ```swift skip="no-runner" import CuaSDK import CuaSpacesFFI let tp = cua.teleport() let options = try await tp.spaceHint(space: space) // the Space's OS and CPU narrow the catalog let apps = try await tp.catalog(options: options) let firefox = apps.first { $0.id == "firefox" }! let plan = try await tp.plan(app: firefox, space: space, options: TeleportPlanOptions( moves: .appWithFiles, files: [notes], launch: false)) for item in plan.consent { print(item.label, item.detail) } // show these to the user let report = try await tp.run(plan: plan, space: space, consent: TeleportConsent(approved: true), listener: nil) print(report.sent) ``` `run` refuses a plan without `approved`, and a plan with secrets without `acknowledgeSensitive`. Pass a `TeleportRunListener` for progress events. `TeleportAppPicker` from `CuaSpacesTeleport` is the picker the macOS app uses. Drops go through `Teleport.parseDrop`, window drags through `Teleport.startWindowDrag` (macOS; Linux and Windows return `Unsupported`). For a web UI, `@trycua/cua/teleport` has the headless picker state machine, `<cua-teleport-picker>` and `<cua-teleport-drop>` web components and drag-payload helpers over a `TeleportHost` you provide. ## The `cua` keychain on macOS Spaces Chrome and other Chromium browsers keep their cookie key ("Safe Storage") in the Keychain. Session teleport installs that key into a keychain that cua-spacesd owns, `~/Library/Keychains/cua.keychain-db`, and pre-authorizes the browser there. It never reads, writes or deletes anything in the Space's login keychain, so no password dialog ever appears in a Space nobody is watching, whether or not the browser was opened in the Space first: - The login keychain of a Lume image can have a password nobody knows (the image's autologin file is padded wrong, so macOS re-keys the keychain at every boot), and an item a browser made there trusts only that browser. Reading it with `/usr/bin/security` raises "security wants to use your confidential information stored in 'Chrome Safe Storage'" with nobody to answer it. - The `cua` keychain is created with a password the Space knows and is the **first** entry on the keychain search list. Browsers look their key up through the search list and take the first match, so Chrome finds the key teleport installed before any key it made itself. That key is left alone, not read and not deleted. Cookies Chrome stored in the Space under its own key before the teleport do not survive it; the teleport replaces the session. - It is not the default keychain, so new items are not written there by other apps. - cua-spacesd unlocks it when it starts and again every 20 seconds, so it is open after a reboot and within seconds of a wake, and puts it back first on the search list if something reset it. The password comes from the Space's protected configuration (`CUA_ENV_LOGIN_KEYCHAIN_PW`, `~/.cua/spacesd/keychain-password`, `/etc/cua/keychain-password`, then the image's account password). - It never auto-locks: no lock after a timeout and none on sleep (`security set-keychain-settings` without `-l` or `-u`). - Every `security` call has a 20 second deadline. A call that is stuck behind a dialog is stopped, the dialog is dismissed, and the teleport fails with an error that says so instead of hanging. - A running Chrome in the Space is quit before its profile is replaced and opened again afterwards, so it reads the key teleport installed. - A Space that was never teleported into has no `cua` keychain. ## Session teleport To move only an app's signed-in state (scope `tabs` or `full`) into a Space, with an approval callback. `Space.teleport` asks the Cua Spaces daemon, which runs teleport; an SDK runtime without Cua Spaces raises `HostCapabilityMissing`. **Python** ```python skip="cua-spaces" import cua class AskUser(cua.TeleportApprover): def approve(self, manifest): print([(i.relative_path, i.is_sensitive) for i in manifest.items]) # include=None: the default selection. A list: exactly those items. return cua.TeleportDecision(include=None, acknowledge_sensitive=False) receipt = await space.teleport("firefox", "tabs", AskUser()) print(receipt.transferred_paths, receipt.launched) ``` **TypeScript** ```ts skip="cua-spaces" import { approve } from "@trycua/cua/spaces" const receipt = await space.teleport("firefox", "tabs", approve((manifest) => askUser(manifest))) // a decision, or undefined to refuse ``` **Rust** ```rust test="excerpt" id="rs-teleport" source="samples/openkoalabot-example-tauri/src-tauri/core/src/teleport.rs#rs-teleport" let v = core .spaces() .call_extension( "teleport", "teleport.send", json!({ "space": space.id().to_string(), "app": m.app, "scope": m.scope, "include": decision.include, "acknowledge_sensitive": decision.acknowledge_sensitive, }), ) .await?; Ok(serde_json::from_value(v)?) ``` **Swift** ```swift test="excerpt" id="sw-teleport" source="samples/cua-bots-macos/Sources/CuaBotsCua/Keyvault.swift#sw-teleport" let receipt = try await space.teleport(app: item.providerId, scope: nil, approver: SelectItems(label: item.label)) ``` The Rust tab is from [the examples in other languages](</docs/spaces/examples/other-languages>); the Swift tab is from [Cua Bots](</docs/spaces/examples/cua-bots>), which teleports a saved sign-in the user picked. Returning nothing raises `TeleportRefused`. Sensitive items need `acknowledge_sensitive=True`. MCP tools: `teleport_manifest`, `teleport_app`. From the CLI. `cua teleport` runs in the `cua` that ships with the Cua Spaces apps; a `cua` installed on its own hands the command to that one, or fails with an error that says where it ships. ```bash test="cli-shape" id="cua-teleport-providers" cua teleport providers # apps this machine can teleport cua teleport manifest --app firefox --scope tabs ``` ```bash test="cli-shape" id="cua-teleport-push" cua teleport push --app firefox --sandbox dev cua teleport push --app com.google.Chrome --url http://10.0.0.5:3211 --token "$TOKEN" \ --profile "Profile 1" --progress ``` `push` is your approval and prints what it sends and leaves behind. `--include PATH` picks items, `--all` sends everything, `--no-launch` imports only. `--sandbox NAME` routes through your own [Cua Keyvault](</docs/spaces/guides/keyvault>) (one combined Touch ID prompt for the whole capture-and-deliver, and nothing raw ever leaves it); `--url` uploads a bundle straight to a spacesd endpoint the Keyvault does not manage as a Space, so it never goes through the vault. **Warning** A teleported session is a live sign-in that anyone controlling the Space can use. Teleport only into Spaces you own, and prefer `tabs`. --- # Persistent agents A named agent whose memory outlives its runs and its Space: a home in the Cua Volume, routines the daemon fires, notifications, pause and resume, and access to your computers by name. A persistent agent is a harness with a name, a Space to work in, and a home in the [Cua Volume](</docs/spaces/guides/cua-volume>) at `agents/<name>/`. The harness keeps its memory in that home, so the agent remembers across runs, across a Space being deleted and created again, and across machines. ```bash test="cli-shape" id="persistent-create" cua agent create ada --harness hermes --in local:dev --env-from-host OPENAI_API_KEY cua agent tell ada "Every morning, summarize what changed in the repo" ``` ## How it works | Step | What happens | |------|--------------| | Start | The daemon takes the home's lease (one writer), restores `agents/<name>/` into `~/cua-volume/agents/<name>` in the Space, and starts the harness with its memory there. | | Each turn | When the turn ends the daemon saves the files that changed back to the drive and posts a notification with the agent's answer. | | Follow-ups | `cua agent tell` sends to the idle run, or starts a new run with the home restored. A turn in progress is never interrupted. | | Stop, pause | The home is saved and the lease released. | ## Memory Only memory moves into the home; each harness's credentials stay where they were. | Harness | Memory in the home | Setting | |---------|--------------------|---------| | Claude Code | `claude-memory/` | `autoMemoryDirectory` in a per-run `CLAUDE_CONFIG_DIR` | | Codex | `codex/memories/` | `CODEX_HOME=<home>/codex`, `[features] memories = true` | | Hermes | `hermes/memories/` (`MEMORY.md`, `USER.md`) | `HERMES_HOME=<home>/hermes` | | OpenClaw | `workspace/` (`MEMORY.md`, `memory/`) | `OPENCLAW_WORKSPACE_DIR=<home>/workspace` | Other harnesses work in `<home>/work`. Never saved: `.env`, `auth.json`, `credentials/`, `vault/`, `sessions/`, logs and caches. A file that looks like it holds a secret (an API key, a token, a private key) is kept out of the drive and you get a notification; secrets belong in the [Keyvault](</docs/spaces/guides/keyvault>). ## Routines The daemon fires routines whether or not an app is open. A routine is a turn of the agent (`[routine] <title>: <prompt>`); a busy or paused agent skips that slot. ```bash test="cli-shape" id="persistent-routine" cua agent routine add ada --title "Morning" --prompt "Plan the day" --daily 08:00 cua agent routine ls ada ``` ## Notifications A turn's end, a `notify_user` call from the agent, an access request and a failure each post a notification. The Cua app shows them as system notifications; the feed persists while no app is open. ```bash test="cli-shape" id="persistent-notifications" cua agent notifications --unread ``` ## Pause and resume ```bash test="cli-shape" id="persistent-pause" cua agent pause ada cua agent resume ada ``` | Space | Pause | Resume | |-------|-------|--------| | Local | Suspends the Space. | Resumes it. | | Your own machine or an added address | Only the agent pauses. | Only the agent resumes. | ## What the agent gets Inside its Space the agent has the MCP server `cua`, answered by your daemon over the Space's own connection (no token enters the Space). | Tool | What | |------|------| | `notify_user` | A notification in the Cua app. | | `volume_ls`, `volume_read`, `volume_write` | The drive, as `agent:<name>`: its home, `public/` (read) and its Space's folder. | | `volume_request_access` | Asks you for more; you approve in the app. | | `computer_list`, `computer_list_tools`, `computer_call` | Your computers you allowed this agent to use. | | `request_site_login` | [Sign in to a site](</docs/spaces/guides/site-login>) with a password from your Keyvault, after you approve. The agent never sees it. | ## Access to your computers `cua host setup` makes a machine a Space for your whole account. A persistent agent reaches it only if you allow that agent by name. Allowing asks for Touch ID or your passphrase; revoking takes effect on the agent's next call; every grant, use and refusal is audited. ```bash test="cli-shape" id="persistent-computer" cua agent allow-computer ada relay:0123456789abcdef cua agent computer-access --audit 20 cua agent revoke-computer ada ``` **Warning** Agents that share one Space share its user account. Give an agent its own Space when it must not see another agent's files. ## In the Cua app | Page | What it shows | |------|---------------| | Agents | Each persistent agent on one line with Pause or Resume; the selected agent's Memory (its home, each file's text and history, Restore), Routines (add, on or off, remove) and Access to this computer (allow, revoke, recent uses and refusals). | | Drive | The Cua Volume by folder, access requests from agents (Approve asks for Touch ID, Deny) and grants (Revoke). | | Notifications | The daemon's feed, newest first, with Mark all read. New entries are posted as system notifications once each, while the app runs. | ## From an agent or the SDK `agent_start` with `home` runs a persistent agent (created on first use), and the `persistent_agent_*`, `agent_pause`, `agent_resume`, `routine_*`, `notifications_*` and `computer_access_*` tools cover the rest; see [Persistent agents](</docs/spaces/reference/persistent-agents>) in the Spaces reference and the SDK's [`Spaces`](</docs/cua-sdk/reference/spaces/persistent-agents>) methods. --- # Keyvault The Cua Keyvault is a password manager for what teleport moves: cookies, localStorage values, passwords and files, grouped by the app they came from. Every item is locked until you allow unattended access. Search, select several, lock, unlock or delete them, approve requests and turn it off from the Keyvault page. The Keyvault is where Cua keeps what teleport moves into Spaces and the saved passwords agents sign in with. It works like a password manager, grouped by the app each secret came from (Chrome, Slack, Arc). Inside an app, every secret is its own item: a cookie, a localStorage value, a password, or a file for everything else (bookmarks, session files, app state). It lives in the Cua daemon, encrypted on disk. Apps and agents never read it: they ask, you approve, and the daemon delivers into the Space. Open **Keyvault** in the Cua Spaces sidebar. ![The Keyvault page: a request waiting for approval, items per site and account, live access and recent decisions](</docs-assets/img/spaces/spaces-keyvault.png>) | Section | What it shows | |---------|---------------| | Waiting for approval | Requests from apps and agents: the verified caller, what moves, where and for how long. **Approve** asks for Touch ID or your password. | | All Items | Items grouped by app, then by site, with the files of an app together. Each item shows its type, its key, its domain and when it was last saved in grey, and a **lock**. Select items, a site or a whole app to lock, unlock or delete them together. Search matches a domain, a key, an app or a type. Names stay hidden until you confirm with Touch ID (**Show Items**). | | Live access | Grants, unattended rules and copies delivered to Spaces, with **Revoke**, **Remove** and **Wipe**. | | Recent | Decisions from the audit log, and whether its hash chain verifies. | | Settings, Keyvault | Who confirms (the Cua daemon, with Touch ID), how the vault unlocks, whether this app and the daemon are verified as Cua, the prompt you can turn back on after **Never ask again**, and whether site icons may load from Google. | ## Every use asks By default, every use of a saved session or password asks you first: - An app or agent asks, and the request waits on the Keyvault page. Nothing is used until you approve it with Touch ID or your password. - One approval covers one use. Teleporting again, or signing in again, asks again unless you widen the approval. - Every item is **locked** until you unlock it, and no unattended rule exists until you write one. Unlocking asks for Touch ID. - The Cua app and CLI themselves also ask for Touch ID before each teleport or sign-in. ## Items, keys and saving again An item is keyed within its app: a file by its path, a cookie by its host, name and path, a localStorage value by its origin and key, a password by its origin and username. Saving the same app again (teleporting Chrome twice with **Save to Keyvault**) updates the items it already holds by that key and never duplicates them. A saved item keeps its lock and its place; only its value and its last-saved time change. A big file is kept as an encrypted blob rather than inside the item. ## Icons Apps show their own icon, looked up once per app version and kept in a cache. A site shows the icon your browser already had for it: Cua reads it from the browser's local favicon store when it saves the site's items (the file is copied first, so the browser is never touched) and keeps it in the vault as non-secret metadata, visible only while the list is open. When the browser had none, Cua can ask Google's favicon service for it, sending only the site's registrable domain (never a path, an item or a name), a few at a time as rows appear. Answers, including "none", are cached on disk so a domain is asked at most once a week. Turn this off with **Load site icons from Google** in Settings, Keyvault; the globe stands in for any site without a local icon. ## Locks A **locked** item needs your approval for every use. Clicking the lock **unlocks** it, which allows unattended access: any agent with access to the Cua Spaces MCP can have that key written into your connected Spaces without asking each time. The secrets are not sent to the agent; they are delivered into the Space. Before Touch ID, a prompt explains this with **Deny**, **Allow** and **Never ask again**. **Never ask again** skips the explanation from then on (Touch ID is still asked) and can be turned back on in Settings, Keyvault. Select several items, a site or a whole app and unlock them with one prompt and one Touch ID. Locking an item again, or deleting it, ends the access it gave. Deleting items also wipes any copy delivered to a Space. Identity providers (Google, Microsoft, Apple, Okta) always ask and cannot be unlocked. ## Choosing what to send The teleport review is the same list. For a browser you choose which sites to send, with counts per site, instead of the whole profile; it starts from what you sent last time to that Space, or from the sites that look like they keep a sign-in. When the app has saved items, the review can send those instead of reading the app again, so macOS does not ask for Keychain access. ## Saved passwords Import your browser's saved passwords, one item per login: ```bash test="cli-shape" id="kv-import-passwords" cua keyvault import-passwords --browser chrome ``` They stay sealed in the vault and are never delivered to a Space as files. An agent uses one only through [site login](</docs/spaces/guides/site-login>): you approve the sign-in, and the Keyvault types it into the page on the saved login's own origin. Chrome on macOS and Linux is supported. Passwords go into a Space's browser only when you tick **Also send saved passwords** in the teleport review (it is off unless you do, and never remembered). Each one is re-encrypted for the browser in the Space with that browser's own key and written beside the logins it already has, so the browser must have been opened once there. An agent can never have a password delivered, only used through site login. ## What is an item and what stays a file For Chrome and the other Chromium browsers, for Firefox, and for Chromium-based desktop apps (Slack, Discord, Visual Studio Code, Notion) each of these is its own item: - every cookie, with every attribute the browser keeps (creation and update times, priority, source scheme and port, same-site, a partitioned cookie's top-level site). A partitioned cookie stays partitioned and is a different item from the same name without a partition; - every localStorage value, read from the browser's own store and written back into the Space's while its browser is not running; - saved passwords, only when you tick them in the review (never by default). A Chromium app's cookies and passwords decrypt the same way on macOS, Linux and Windows: macOS through the app's own Keychain item, Linux through its fixed or keyring-backed key, Windows through the key `Local State` keeps, itself protected by Windows DPAPI for your account. Firefox's cookies and localStorage are never encrypted at rest; its saved passwords decrypt with the profile's own `key4.db` master key, and a profile protected by a Firefox master password is refused rather than guessed at. Chrome's Windows app-bound encryption (`v20`, Chrome 127+) keeps a cookie or password readable only by Chrome itself. Those are listed in the review, greyed, with that reason, and are never sent; the rest of the site still is. Everything else is saved as a file, by its path: preferences, bookmarks, the open tabs and session state, session storage, extensions, history and, for Firefox, everything under its profile that is not cookies, localStorage or logins. A cookie or value that cannot be read as an item is not silently dropped: it stays a file or is listed as unavailable. ## Saved sessions (cookies) The Keyvault holds a browser's cookies one item each, decrypted from Chrome's Safe Storage-encrypted store and kept sealed like any other item. Unlike a saved password, a delivered session's cookies are meant to be used: when an agent's grant or unattended rule delivers one into a Space, the Space's own Chrome gets its own freshly created (or existing) Safe Storage key, and the cookies are re-encrypted under THAT key before they land in its `Cookies` database -- the source Mac's key is never copied anywhere. This capture path is reached today by the `teleport_app` and `teleport_browser_session` MCP tools (an agent's request, with your approval); the plain **Teleport an app** picker still moves a session directly, without the Keyvault, and a first-party `cua keyvault import-session` command for a human to use directly is tracked as follow-up work. ## Audit Every request, approval, denial, delivery and sign-in is in the Keyvault's hash-chained audit log (**Recent** on the Keyvault page). Site login records `login.request`, `login.authorize`, `login.fill` and `login.denied`, with the agent, the site's origin, the Space and a masked username. Passwords are never logged. ## Turn it off **Disable Keyvault** stops every teleport, import and approval at once and invalidates outstanding grants. A `teleport_app` request from an agent is refused with `disabled`. Turning it back on asks for Touch ID. **Note** The Keyvault only shows items to apps signed by Cua. A build signed by anyone else sees why, not the items. The vault key is wrapped by the macOS Keychain or a passphrase; this build does not use a Secure Enclave key, which needs a provisioning-profile-signed app. ## Set up and unlock First run: **Set up Keyvault** creates the vault and shows a recovery key once. Store it somewhere safe; Cua does not keep a copy. How the vault key is protected depends on the Cua daemon: | Daemon | Setup | Unlock | |--------|-------|--------| | The signed Cua daemon on macOS | Touch ID; the key stays in your login keychain | Automatic, or **Unlock** | | Any daemon that cannot use the OS key store | A passphrase of at least 12 characters, typed twice | The passphrase | The page asks the daemon which one applies before anything else, so a setup it cannot do never asks for Touch ID. A passphrase goes only to the daemon, over the verified Keyvault socket; the apps never log or keep it. The recovery key also unlocks the vault. The same from a terminal: ```bash test="cli-shape" id="cua-keyvault" cua keyvault status cua keyvault init # the OS key store (signed daemon only) cua keyvault init --passphrase # prompts twice; never an argument cua keyvault unlock --passphrase cua keyvault lock ``` Scripts can pass the passphrase on stdin with `--passphrase-stdin`. See the [`cua keyvault` reference](</docs/cua-cli/reference/cli/keyvault>). ## Development builds A debug `cua daemon` can trust a development build of the app instead of Cua's signature. It never touches the login keychain, so its vault is passphrase-only: ```bash skip="no-runner" # The app build (and, for the CLI, a debug cua) by cdhash export CUA_KEYVAULT_TEST_REQUIREMENT="$(apps/cua-spaces-macos/scripts/dev-keyvault.sh \ "<path>/Cua Spaces.app" libs/cua/target/debug/cua)" cua daemon start --foreground ``` Launch the app built with a debug `libcua_sdk.dylib` (`CUA_SDK_DYLIB` in `apps/cua-spaces-macos/scripts/build-app.sh`) and `CUA_KEYVAULT_ALLOW_UNVERIFIED_DAEMON=1`, which lets a debug client talk to the unsigned daemon. Set the same variable for a debug `cua keyvault`. Release builds ignore both variables. See [Teleport an app](</docs/spaces/guides/teleport-an-app>) for what moves, and the [teleport reference](</docs/spaces/reference/teleport>) for the `teleport_app` consent flow. --- # Log in to a site Let an agent sign in to a website in a Space with a password you saved in the Cua Keyvault. You approve each sign-in; the agent never sees the password. Ask an agent "log me into github.com on a new Space" and it can sign in without you typing the password and without the agent ever reading it. The password stays in the [Keyvault](</docs/spaces/guides/keyvault>): the agent asks, you approve in Cua, and the Keyvault types the login into the page through the Space's Cua Driver. ## Before you start 1. Set up the Keyvault (**Keyvault** in the Cua sidebar, or `cua keyvault init`). 2. Import your saved passwords from Chrome. The daemon asks for Touch ID or your login password, and the passwords are stored sealed, one item per site: ```bash test="cli-shape" id="import-passwords" cua keyvault import-passwords --browser chrome cua keyvault import-passwords --browser chrome --site github.com ``` 3. Give your agent the Cua tools: `cua agents` installs them into Claude Code, Codex, Hermes and OpenClaw. ## What happens 1. The agent creates a Space and opens the site's sign-in page in its browser. 2. It calls `request_site_login` with the Space and the page URL. The Keyvault files a request and the tool answers `pending` with a `request_id`. 3. Cua shows the request: which agent, which site, which Space. Approving it asks for Touch ID or your login password. One approval covers one sign-in. 4. The agent calls `request_site_login` again with the `request_id`. The Keyvault checks the page is on the saved login's exact origin, types the username and password with Cua Driver, submits the form and answers `filled` with a masked username (`a***@example.com`). If you decline, the tool answers with a `login_refused` error and nothing is typed. If the page is on another origin (a look-alike domain, a redirect), the Keyvault refuses before typing anything. ## The tool | Argument | Meaning | |----------|---------| | `space` | The Space to sign in. | | `url` | The site's sign-in page (`http` or `https`). | | `agent` | The agent asking; shown to you in the approval. | | `session`, `target_id`, `tab_id` | The browser tab to sign in, from the agent's own `get_browser_state` on the same Space. Without them the Keyvault opens its own browser in the Space on `url` and returns its tab. | | `username` | Which saved account, when the site has several. | | `request_id` | From the first call; present on the retry. | | `wait_secs` | How long the retry waits for your answer (default 20, at most 120). | From the SDK the same call is `space.request_site_login(url, options)` (`requestSiteLogin` in Swift, TypeScript and Kotlin). See the [Keyvault tool reference](</docs/spaces/reference/keyvault>) for the full result shape. ## Approve from a terminal Requests also show up in the CLI, for a machine without the app open: ```bash test="cli-shape" id="approve-login" cua keyvault requests cua keyvault approve 5f2c9a0e cua keyvault deny 5f2c9a0e ``` ## What the agent sees Only what happened: `pending`, `filled` with the site, the origin, a masked username and the tab it signed in, or `login_refused`. The password is never in a tool result, an error, an event, a log line or the audit log. **Warning** Once a page is signed in, its session lives in the Space's browser. An agent working in that Space can use the session like you could. Delete the Space when the work is done. ## Unattended sign-in By default every sign-in asks. To let one agent sign in to one site on its own (for a routine), turn on **Unattended** for that site's saved password in the Keyvault page and add an unattended rule for the agent and the Space. Both steps ask for Touch ID, and the rule expires. ## Limits - Saved passwords import from Chrome on macOS and Linux. Windows (DPAPI) is not supported yet. - The Keyvault fills a standard form: a password field, the username field before it and a submit button after it. Multi-step sign-ins (username page, then password page) need one `request_site_login` per page. - Two-factor prompts are yours to answer: open the Space's viewer and take over. --- # What the relay can see A plain accounting of what relay.cua.ai (or any self-hosted cua-relay) can and cannot see, and what we have done and are still doing about it. `relay.cua.ai` (and any self-hosted `cua-relay`) is a **trusted, TLS-terminating proxy, not a blind relay**. This page says plainly what that means, kept current as the relay changes. If anything here reads as a surprise, it is a bug in this page: tell us. ## The short version - TLS ends at the relay's ingress. The relay process parses every request it forwards (gRPC, gRPC-Web, WebSocket upgrades, `/files` transfers) to route and authorize it. **It can read the plaintext of anything it proxies while the request is in flight**: shell output, file contents, the desktop media stream, keystrokes and clipboard data sent as input, and, unless sealed (below), Keyvault passwords and teleport session bundles. - It does not **store** traffic, env tokens or account tokens: it strips a client's credentials and forwards a short-lived signed assertion instead (account mode), or forwards a self-hosted relay's env token unmodified without persisting it (static mode). See the relay README (`libs/cua-spacesd/crates/cua-relay/README.md` in the cua repository) for the exact claims and how they are enforced in code. - A compromised relay process, relay host, or a memory dump of either, is equivalent to a compromised session for every machine currently routed through it: it can mint assertions the machines still trust, and read whatever crosses it meanwhile. ## What crosses the relay | | Path | Relay sees plaintext? | |---|---|---| | gRPC (shell, files, input) | Parsed and forwarded | Yes | | Desktop media stream | Parsed and forwarded (TCP over the relay; QUIC only on a direct/LAN path) | Yes | | `/files` transfers | Parsed and forwarded | Yes | | Env / account tokens | Stripped and replaced (account mode); forwarded unmodified, not stored (static mode) | In transit only (static mode); not at all (account mode) | | Keyvault site-login password | `browser_type` call argument | **Gated**: refused over a `relay:` Space by default; sending anyway needs your explicit, per-sign-in acknowledgement (no end-to-end sealing for this path yet; see below) | | Teleport session bundle (cookies, Safe Storage keys) | `TeleportService.ImportSession` | **Sealed** once the Space's guest key is pinned (every image built from this point on); **gated** otherwise: refused unless you explicitly acknowledge the risk for that one delivery | ## Sealed delivery: where it stands We seal teleport secrets end to end to the destination machine's own key, so the relay forwards only ciphertext for a sealed delivery. The construction: an ephemeral X25519 key per delivery, HKDF-SHA256, XChaCha20Poly1305, bound to the Space id, a purpose string and a timestamp, with replay rejection (`cua-machine-seal`; no hand-rolled crypto, every primitive is a vetted RustCrypto / dalek crate). A guest's spacesd generates its own keypair on first start (0600 on disk) and reports the public half through its capabilities; the host pins it on first use (trust-on-first-use) and seals every subsequent teleport delivery to it. **Warning** **Current state, honestly:** teleport's sealing and the gate are both live. A Space whose image was built before this shipped reports no guest key, so the teleport `ImportSession` path (which the Keyvault broker's combined prompt also uses for cookies and session data) refuses to send over a `relay:` Space from such an image unless you explicitly acknowledge the risk for that one delivery -- republishing the guest image is what makes the refusal go away, by making the delivery sealed instead. The **Keyvault site-login password path** (typed directly into a page via `browser_type`) is **gated the same way, but not yet sealed**: a saved password still crosses the relay as a plain request argument when you acknowledge the risk, since there is no practical way yet to seal a single driver-call argument the way a whole bundle upload is sealed. Treat an acknowledged site-login sign-in into a `relay:` Space the same as you would treat typing that password into any other network request that crosses a proxy you do not control, until this page says otherwise. **Local and direct Spaces are unaffected either way**: no relay is in the path, so there is nothing to seal against. ## Other hardening already shipped - **Device enrollment with MFA for new devices.** Re-signing in enrolls only your own machine (a re-key, or your account's first device). A brand-new device on an account that already has an enrolled device needs an approval from an enrolled device, or a sign-in whose token itself proves a second factor -- a verified email alone is not enough. - **New machines are flagged.** A machine registered without an enrolled device's signature or MFA is marked unconfirmed until an enrolled device vouches for it. - **A separate origin for machine content.** Machines no longer render as HTML, register a Service Worker, or set cookies on the relay's own origin outside the one known viewer path; a fully separate registrable domain for machine content is designed and partly implemented in code, pending a DNS/TLS rollout. - **Bounded relay memory.** A per-machine cap on buffered bytes and streams, plus a relay-wide budget with backpressure, so one machine (or one account's worth of them) with slow-reading clients cannot take down every other machine's connection. - **A tamper-evident, exportable audit log**, hash-chained like the Keyvault's own, with `GET /v1/audit?format=jsonl` for export to your own SIEM or backup. - **The migration grace period (a stolen session alone reaching every machine) is off by default**, and time-boxed to an announced rollout window when it does run. ## Reporting a concern See [`SECURITY.md`](https://github.com/trycua/cua/blob/main/SECURITY.md) for how to report a vulnerability privately, our response targets, and our safe harbor for good-faith security research. ## Related - [Set up unattended access](</docs/spaces/guides/unattended-access>) -- device enrollment in full. - [Share a Space](</docs/spaces/guides/share-a-space>) -- the allowlist and audit trail for sharing. - [Keyvault](</docs/spaces/guides/keyvault>) and [Teleport an app](</docs/spaces/guides/teleport-an-app>) -- what these deliver and how consent works. --- # Cua Volume One versioned drive per account, shared by every Space and agent: agent memory and outputs that outlive any Space, with default access rules, grants you approve, and an audit log. Cua Volume keeps what outlives a Space: a persistent agent's memory, its outputs, and files the user shares with every Space. A cloud Space's disk goes back to the pool on release; the drive does not. ## Layout | Folder | What it holds | |--------|---------------| | `public/` | Shared reference: docs, datasets, house rules, skills | | `agents/<agent>/` | One [persistent agent](</docs/spaces/guides/persistent-agents>)'s home: memory, `outputs/`, `inbox/` | | `spaces/<space>/` | Per-Space scratch and outputs (`local:work` is `spaces/local-work/`) | Every write is a new version. Deletes leave a marker, so history and restore still reach the old content. ## Who may reach what | Principal | `public/` | `agents/<self>/` | `agents/<other>/` | `spaces/<this>/` | `spaces/<other>/` | |-----------|-----------|------------------|-------------------|------------------|-------------------| | You (app, CLI, agents on your machine) | rw | rw | rw | rw | rw | | Agent A in Space S | r | rw | none | rw | none | | Space S, no agent | r | none | none | rw | none | Anything wider is a grant: a principal, a folder or file, `r` or `rw`, and an optional expiry. Only you grant, and every grant asks for Touch ID, your login password or the Keyvault passphrase first. An agent that needs more files a request; you approve it (with presence) or deny it. The daemon checks every read, write and listing before it touches storage. An agent in a Space never holds keys to the drive. ## From a terminal ```bash test="cli-shape" id="cua-volume-files" cua volume ls agents/ cua volume put public/rules.md ./rules.md cua volume cat agents/ada/memory/MEMORY.md cua volume history agents/ada/memory/MEMORY.md cua volume restore agents/ada/memory/MEMORY.md 0001790683200000-1a2b3c4d cua volume ls --as agent:ada --space local:work ``` `--as` shows the drive exactly as that agent sees it. It only narrows. ```bash test="cli-shape" id="cua-volume-grants" cua volume grant agent:researcher agents/writer/outputs/ cua volume grant space:local-build public/datasets/ --rw --for 2h cua volume grants cua volume requests cua volume approve 5f2c9a0b1d3e4f67 cua volume revoke 5f2c9a0b1d3e4f67 cua volume audit ``` See the [`cua volume` reference](</docs/cua-cli/reference/cli/volume>). ## From the SDK The drive methods live on `Spaces`; a `DriveView` takes an agent's view. They ask the Cua Spaces daemon, which runs the drive; an SDK runtime without Cua Spaces raises `HostCapabilityMissing`. ```python skip="cua-spaces" import cua spaces = cua.connect().spaces() # the Cua Spaces daemon await spaces.volume_write("public/rules.md", b"be kind", None, False, None) ada = cua.DriveView(as_agent="ada", in_space="local:work") await spaces.volume_write("agents/ada/memory/MEMORY.md", b"likes tea", None, False, ada) try: await spaces.volume_read("agents/bob/notes.md", None, ada) except cua.CuaError.PermissionDenied: print("ada may not read bob's home") print([e.path for e in (await spaces.volume_ls("agents/", ada)).entries]) # expect: ['agents/ada/'] ``` Every tool is also on the Spaces MCP server (`volume_ls`, `volume_read`, `volume_write`, ...): see the [volume tools reference](</docs/spaces/reference/volume>) and the [SDK reference](</docs/cua-sdk/reference/spaces/volume>). ## Secrets stay in the Keyvault Writes under `agents/` are scanned for API keys, tokens and private keys. A hit is refused with `secret_detected`, naming the kind and the line, never the value, and the audit log records it. Agent homes never sync harness credentials (`.env`, `auth.json`, `credentials/`, `vault/`). Keep secrets in the [Keyvault](</docs/spaces/guides/keyvault>). ## Audit `cua volume audit` lists grants, revocations, requests, refusals, blocked secrets, syncs, leases and every agent read and write, newest first. Each line's hash covers the previous one, so an edited or truncated log reports that it does not verify. ## Show it in Finder **Note** Cua Volume is becoming **Cua Volume**. The mounted volume already carries the new name; the `cua volume` commands and `drive_*` tools keep their names for now. The drive can appear as a volume named **Cua Volume**, so any app opens its files in place. It is off until you turn it on in Cua (Settings > Storage) or from a terminal: ```bash test="cli-shape" id="cua-volume-mount" cua volume mount cua volume status cua volume unmount ``` | System | How it mounts | Where | |--------|---------------|-------| | macOS | A local NFS server inside the daemon, mounted with the system's own client. No kernel extension and no approval step | `~/Cua Volume`, listed in Finder under Locations | | Linux | FUSE | `~/cua-volume` | The setting survives restarts: the daemon mounts the volume when it starts and unmounts it (after pending uploads land) when it stops. What happens underneath: - **Large files stream.** Opening a file reads only the bytes an app asks for, a block at a time, from a local block cache (`~/.cua/volume/cache`, 10 GiB by default, least recently used first). When an app reads sequentially, as a player or an editor does, the drive fetches ahead of it, so a 4K video starts in well under a second instead of after a full download. - **Writes upload on their own.** A file uploads when the app closes it, or after 1.5 seconds without writes. Large files go up in parts. The access rules, the audit log and the secret scanner apply exactly as they do to `cua volume put`. - **Finder's own files stay local.** `.DS_Store` and `._*` files are kept on this Mac and never upload. ### How fast it is Measured on an Apple M5 Max (macOS 26.5) through the mounted volume, each number from a cold start (the block cache cleared and the volume remounted). The videos are 4K H.264 at 150 Mb/s with the index at the end of the file, as cameras write it: 1.04 GB (56 s) and 10.4 GB (9 min 20 s). "Cloud" is a local MinIO behind a proxy that adds 25 ms each way and caps each connection at 50 MB/s. "Download first" is a full download with 8 parallel ranged requests, then the same step on the local copy. | Store | File | Probe (ffprobe) | Frame at 50% | Decode 10 s from the middle | Download first, then probe | |-------|------|-----------------|--------------|-----------------------------|----------------------------| | This Mac (`fs`) | 1 GB | 0.04 s | 0.11 s | 0.49 s | 0.40 s | | This Mac (`fs`) | 10 GB | 0.10 s | 0.21 s | 1.09 s | 5.7 s | | MinIO | 1 GB | 0.10 s | 0.20 s | 1.17 s | 0.35 s | | MinIO | 10 GB | 0.08 s | 0.18 s | 0.72 s | 7.5 s | | Cloud | 1 GB | 0.49 s | 0.50 s | 1.17 s | 3.3 s | | Cloud | 10 GB | 0.35 s | 0.34 s | 1.14 s | 32.6 s | | Store | File | Sequential read | Random 4 KiB read, p50 / p95 | Cache hits during the 10 s decode | |-------|------|-----------------|------------------------------|-----------------------------------| | This Mac (`fs`) | 10 GB | 1,324 MB/s | 1.1 / 1.8 ms | (no cache: already local) | | MinIO | 10 GB | 737 MB/s | 3.1 / 4.0 ms | 85% | | Cloud | 10 GB | 212 MB/s | 76 / 92 ms | 42% | A folder of 10,000 files lists through the volume in 0.18 s (`fs`) and 0.47 s (MinIO) the first time, and in about 1 ms after that. The benchmark is `libs/cua/crates/cua-volume/bench/drive_bench.py`; it prints the full set of numbers, including 1 GB rows and sync latency. ## Inside a Space Every Space mounts the same volume, so a program in the guest reads and writes it like any folder. The guest sees only that Space's view (the Space's row in the table above, or its agent's), and it never holds keys: your machine serves the view over the Space's own connection and applies the access rules, the audit log and the secret scanner there. | Guest | How it mounts | Where | |-------|---------------|-------| | macOS | The system's own NFS client | `~/Cua Volume` | | Linux | FUSE (the image's root volume helper mounts it) | `/volume`, else `~/Cua Volume` | | Windows | Coming soon. Until then, agents in a Windows Space use the `volume_*` tools | | The mount goes away when the Space detaches the volume, when its session is revoked and when the guest's cua-spacesd stops. `cua-spacesd doctor` checks the mount end to end (`volume.mount`). ### When the guest's cua-spacesd restarts An update, a crash or a reboot restarts the guest's cua-spacesd, which holds the volume's connection and the network-sharing (hotspot) connection. You do not need to reconnect the Space: - On a clean stop (`SIGTERM`) the daemon unmounts the volume before it exits, so no dead mount is left behind. - When it starts, the daemon unmounts anything a previous daemon left at the mount path (a crash, a force-quit) before it does anything else. - Your machine notices the connection closed, or that the new daemon reports the volume detached, and mounts the volume again, then restarts the hotspot if one was on. It retries with a growing wait (2 s, then 4 s, up to a minute) while the daemon is still coming up, and keeps a persistent agent's view on the volume. - A mount whose server is gone never hangs a program for long: the NFS mount is `soft`, interruptible and times out in seconds, and the system marks it dead and unmounts it (`deadtimeout`). ## Sync across devices Two machines using the same bucket see each other's changes within seconds. Each change is also written to a small change log in the bucket itself (`.cua-feed/`), and every machine checks it with a single listing request, twice a second while things change, backing off to every 5 seconds when idle. No Cua service is involved; it works with your own S3, R2 or MinIO bucket. Measured with two cua homes on one MinIO bucket (20 rounds each): a change made on one reaches the other's daemon in 0.50 s at the median and 0.50 s at p95 for a 1 KiB file, and shows in the other's mounted volume 2 ms to 5 ms later. A 64 MiB file shows 0.32 s after its upload finishes (p95 0.34 s). While nothing changes, the check backs off, so the first change after a quiet period can take up to 5 seconds. When two machines change the same file, the write that reaches the bucket last becomes the current version, and the other one is kept next to it as `name (conflict from <machine> <date>).ext`. Both stay in the file's history. Nothing is dropped silently. ```bash test="cli-shape" id="cua-volume-status" cua volume status --json ``` **Note** Keep the change log small with a bucket lifecycle rule that expires noncurrent versions under `.cua-feed/` after a day. Each machine already deletes feed entries older than a day. ## In the Cua Spaces app **First run.** The Cua Volume page asks where your files live: This Mac (the default, with the folder it uses), your own S3 bucket (AWS S3, R2, MinIO or any S3-compatible store), or later in Settings. Its one checkbox, **Add Cua Volume to Finder** (on Linux, **Mount Cua Volume**), is off by default; ticking it mounts the volume when you continue. Windows has no mount yet, so the page does not show there. ![The first run's Cua Volume page](</docs-assets/img/spaces/spaces-volume-onboarding.png>) For your own bucket the page offers a prompt to give your coding agent: it creates a private, versioned bucket and a user limited to it with the AWS CLI, then connects it with `cua volume config set` and `cua volume config set-keys`. The app notices the bucket as soon as it is configured and shows "Connected, versioning on". **Enter details manually** shows the endpoint, bucket, region and key fields with Test connection instead. ![Your S3 bucket: the prompt for your agent](</docs-assets/img/spaces/spaces-volume-bucket-prompt.png>) **Settings, Storage.** Where the files live, the volume, and the block cache. Test connection checks the endpoint, the keys and bucket versioning without saving anything. Keys go to the credential store, never to `config.json`, and Save switches the running volume without a restart. ![Settings, Storage](</docs-assets/img/spaces/spaces-settings-storage.png>) **Menu bar.** The menu shows the volume's sync state next to the number of Spaces (Synced, Syncing, Offline) and any conflicts, which open the Volume page. Nothing shows while the files stay on this Mac with no other device. **Volume page.** Status, not a file browser: Finder is. Open in Finder shows the volume, mounting it first when it is not mounted. Devices lists every machine on the bucket, this one with its pending uploads and last sync. Conflicts lists files two machines wrote at once: Open shows the other copy in Finder, Resolve clears the entry and keeps both files. ![The Volume page with two devices and a conflict](</docs-assets/img/spaces/spaces-volume-page.png>) ## Storage | Backend | Where | Set up | |---------|-------|--------| | `fs` (default) | `~/.cua/volume/data` on this machine | Nothing | | `s3` | Any S3-compatible bucket with versioning on (AWS S3, R2, MinIO) | `cua volume config set` and `cua volume config set-keys` | ```bash test="cli-shape" id="cua-volume-config" cua volume config show cua volume config set --backend s3 --endpoint http://127.0.0.1:9000 --bucket cua-volume --path-style cua volume config set-keys ``` `set-keys` reads the access key id and the secret from stdin and saves them in the credential store; they never go in `~/.cua/volume/config.json`. Restart the daemon after changing the backend from the terminal. Settings > Storage in Cua tests the bucket first (reachable, keys allowed to write, versioning on) and switches the running drive without a restart. ## Limits | Limit | Value | |-------|-------| | One read or write through a tool | 8 MiB | | One file through the mounted volume | No limit (streamed; large files upload in 16 MiB parts) | | Block cache | 10 GiB by default, at least 256 MiB | | Path length | 1024 bytes | | Agent names | 1 to 63 of `a-z`, `0-9`, `.`, `_`, `-` | | Requests waiting per agent | 8 | | Cloud keys | 15 to 60 minutes, at most 12 folders | | MinIO | a file and a folder cannot share a name (`a` and `a/b`) | --- # Use Spaces from an agent Give an AI coding agent the Spaces tools through cua daemon mcp, and run agents inside a Space as long-lived threads. The cua MCP server gives an agent 33 Spaces tools plus sandbox and computer tools. For the machine the agent runs on, use [Cua Driver](</docs/cua-driver/quickstart>) instead. ## Connect the agent `cua agents setup` adds the cua skills and MCP server to detected agents ([supported agents](</docs/cua-cli/guides/mcp-server#supported-agents>); the installer offers it after sign-in): ```bash test="cli-shape" id="cua-agents-detect" cua agents detect # read-only cua agents setup --agents claude,codex --yes cua agents status cua agents remove # removes only what cua added ``` By hand: ```bash skip="third-party-cli" claude mcp add cua -- cua daemon mcp codex mcp add cua -- cua daemon mcp ``` `cua daemon mcp` starts `cua daemon` if needed, so every agent on the machine shares the same Spaces, sandboxes and streams. Narrow the grant with `--permissions spaces:readonly` ([permissions](</docs/cua-cli/guides/mcp-server#permissions>)). ```text output list_spaces / create_space {reuse} -> pick, reuse or create a Space space_bash {space, command} -> run commands inside it send_file {space, path} -> drop a host file into ~/Downloads (sha256-verified) stream_endpoint -> a ticketed media URL list_tools / call_tool -> cua-driver tools in the Space, or any declared service delete_space {space} -> delete it and stop metering ``` `space` is an id or a name. A tool whose feature the Space lacks returns `capability_missing`. ## Run an agent inside a Space | Tool | Does | |------|------| | `agent_capabilities` | Ready harnesses (`claude-code`, `openai-codex`). | | `agent_start {space, agent, prompt, show}` | Start a detached run; `show` tails it in a terminal on the Space desktop. | | `agent_message {space, run_id, text, force}` | Next turn; refused mid-turn unless `force`. | | `agent_status`, `agent_list`, `agent_stop` | State, output tail, stop. | Runs live in the guest (`~/.spaces-agents/<run_id>/`) and survive the client going away. The daemon copies your agent's credential file (for example `~/.claude/.credentials.json`) into the Space at start; `CUA_SPACES_AGENT_CREDENTIALS_HOME=none` copies nothing. In TypeScript a run is a thread: ```ts skip="third-party-cli" import { embedded } from "@trycua/cua" import { startThread } from "@trycua/cua/spaces" const bot = await startThread(embedded().spaces(), { agent: "claude-code", prompt: "Open the repo in the Space and run the tests", placement: { type: "dedicated", on: "local", deleteOnClose: true }, }) await bot.send("Now fix the failing test") console.log(await bot.status()) await bot.close() // stops the run and deletes the Space ``` `placement` `shared` (`{ type: "shared", space, acknowledgeNoIsolation: true }`) reuses a Space you name; `adoptThread(spaces, spaceId, runId)` reattaches. The lower-level calls, one run per thread and one message per turn. The TypeScript and Rust tabs are from [the examples in other languages](</docs/spaces/examples/other-languages>), the Swift tab from [Cua Bots](</docs/spaces/examples/cua-bots>). A message sent mid-turn is refused (or queued) unless forced; show the reason. **TypeScript** ```ts test="excerpt" id="ts-agent-start" source="samples/openkoalabot-example-ts/src/core/app.ts#ts-agent-start" const report = await space.agentStart(bot.agent, prompt, false, options) const t = new BotThread(bot, report.runId, space) ``` ```ts test="excerpt" id="ts-agent-message" source="samples/openkoalabot-example-ts/src/core/app.ts#ts-agent-message" const r = await this.space.agentMessage(this.runId, message, false) if (!r.ok) { this.fold.note(this.turns.length, `Refused: ${r.reason}`) return { accepted: false, reason: r.reason } } ``` **Rust** ```rust test="excerpt" id="rs-agent" source="samples/openkoalabot-example-tauri/src-tauri/core/src/thread.rs#rs-agent" let r = self .agents .start(&self.agent, text, self.options.clone()) .await .map_err(agents_err)?; ``` ```rust test="excerpt" id="rs-agent-message" source="samples/openkoalabot-example-tauri/src-tauri/core/src/thread.rs#rs-agent-message" let r = self .agents .send(run, text, vec![]) .await .map_err(agents_err)?; if !r.accepts_message { self.system(format!("queued while {}: {}", r.phase, r.reason)); } ``` **Swift** ```swift test="excerpt" id="sw-agent-start" source="samples/cua-bots-macos/Sources/CuaBotsCua/CuaEngine.swift#sw-agent-start" let options = SpaceAgentOptions( envFromHost: endpoint?.keyVariables ?? Self.defaultKeyVariables(bot.harness), repo: nil, branch: nil, cwd: home, model: endpoint?.model, baseUrl: endpoint?.baseURL, exitWhenIdle: false) let report = try await space.agentStart(agent: bot.harness.rawValue, prompt: prompt, show: false, options: options) ``` ```swift test="excerpt" id="sw-agent-message" source="samples/cua-bots-macos/Sources/CuaBotsCua/CuaEngine.swift#sw-agent-message" let report = try await space(bot).agentMessage(runId: run, text: text, force: false) if !report.ok { throw EngineError.refused(report.reason) } ``` **Warning** `space_bash`, `send_file`, `teleport_app` and `hotspot_start` act with your authority. Grant `spaces:readonly` to agents that only need to look. --- # Updates How the Cua Spaces app for macOS updates itself, how to pick the Stable or Beta channel, and what it refreshes after an update. The Cua Spaces app for macOS checks for new versions and installs them for you. Everything is in **Settings, About**. ## How updates work The app checks once a day. When a new version is out, it offers to install it; with **Install updates automatically** on, it downloads the update in the background and installs it when you quit the app. **Check Now** checks right away, and **Last check** says when it last looked. Turn off **Automatically check for updates** to only check by hand. Every update is the same notarized disk image as the download on the release page, and the app installs it only when its signature matches the key built into the app. ## Channels **Update to** picks what you get: - **Stable**: releases. - **Beta**: releases and prereleases, with new features sooner and rough edges. Going back to Stable from a newer beta keeps the version you have until a newer stable release is out. ## After an update The first time the new version opens, it refreshes what Cua installed into your AI agents, the way `cua agents update` does: the Cua skill folders and the Cua MCP entries in Claude Code, Cursor and the other agents you set up. A skill folder you edited is left as you left it. It also restarts the Cua daemon when the app started it, so the daemon runs the new version too. This happens without any window. If something fails, the app shows one notice with what to run. On Linux and Windows, the Tauri app updates through its own updater. --- # Cua Bots: persistent bots with their own computers The featured Cua Spaces example: a source-available Mac and iPhone app for persistent bots, each with a face you design, its own Space, a memory in the Cua Volume layout, routines, approvals and rules. SwiftUI on the Cua Swift SDK. Cua Bots is an example app for persistent bots. You create a bot, give it a name and a koala face, and it gets its own computer (a Space). It keeps working while you're away, remembers what matters to you, and asks before it does anything your rules say it should ask about. The Mac app runs the bots; the iPhone app chats with them, watches their computers and answers their approvals. [Cua Bots: self-host your bots](</docs-assets/videos/cua-bots-launch.mp4>) The Cua Bots launch film (23 seconds). The app screens in it are real Cua Bots screenshots. ![A bot finished its research: the result card, a notification, and its profile with Activity and Outputs](</docs-assets/img/cua-bots/cua-bots-mac-research-ready.png>) ## Features - Create a bot in three steps: name it, design its face (color, eyes, ears), and choose where its computer runs (this Mac or the cloud) and which agent drives it (Hermes, OpenClaw, Codex or Claude Code). It introduces itself when its computer is up. - Its face changes with what it's doing: thinking, working, waiting for you, done or paused. - "Ada's computer": the bot's own Space, live beside the chat, framed in its color, with picture in picture. The bot has control until you take over; its face rides next to its pointer. - Memory in the Cua Volume layout at `agents/<name>/`: it outlives the bot's computer. Finished files land in `outputs/`, files you hand it in `inbox/`. - Works on its own: ask for something on a schedule and it becomes a routine. The Scheduled page lists every bot's routines and what they did. - Approvals: the bot asks before buying, deleting, or contacting anyone; you approve from the conversation, the Approvals page or your phone. - Custom rules: "When Ada wants to ... Ada should ..." with four choices (without asking, when you say so, ask first, hand off to you). Rules about passwords and money can't be changed. - Sign-ins without sharing passwords: when a bot needs to be signed in to a site, it asks; you use a saved sign-in from the Cua Keyvault or take over its computer and type it yourself. - Access to your Mac: allow or revoke it from the bot's profile. - Pause, resume and reset from the profile menu or the phone. - Notifications in the window, in macOS, and on the phone. - The iPhone app: your bots, their conversations, their computers (open in your control), approvals and notifications. ## Screenshots These are real runs against a local Space. The bot's agent is the real harness (Claude Code); its model is Cua's scripted mock provider, so every reply is scripted and says so where it matters. ![New bot: design its face](</docs-assets/img/cua-bots/cua-bots-mac-new-bot-look.png>) ![Ada's computer, live, with Ada in control](</docs-assets/img/cua-bots/cua-bots-mac-computer.png>) ![An approval card: Ada asks before buying anything](</docs-assets/img/cua-bots/cua-bots-mac-approval.png>) ![Signing a bot in to a site: a saved sign-in from the Keyvault, or take over](</docs-assets/img/cua-bots/cua-bots-mac-sign-in.png>) ![Custom rules](</docs-assets/img/cua-bots/cua-bots-mac-rules.png>) ![A bot's memory, kept in its Volume home](</docs-assets/img/cua-bots/cua-bots-mac-memory.png>) ![Every bot's scheduled work](</docs-assets/img/cua-bots/cua-bots-mac-scheduled.png>) ![The iPhone app: approving from the phone](</docs-assets/img/cua-bots/cua-bots-phone-approval.png>) ## How it maps to Cua | In the app | Cua underneath | |---|---| | A bot's computer | A Space per bot (`bot-<name>`), created with `Spaces.create` (`on: local` or `cloud`, `reuse`) | | The bot | A `cua agents` harness run in its Space (`Space.agentStart`), with its Volume home as the working directory | | Memory, outputs, inbox | The Cua Volume layout, `agents/<name>/`, kept in a folder on the Mac and copied into the Space before a turn and back after it | | Watching and taking over | `LiveStreamSession` and `StreamPiPController`; the pointer from `SpacesdClient.cursorPosition()` | | Routines | The SDK's routine clock, `RoutineStore` | | Sign-ins | The Cua Keyvault (`KeyvaultClient` from `CuaSpacesFFI`, the Cua Spaces app export) and `Space.teleport` | | Access to your Mac | `Host.startSharing` and `Host.stopSharing` (`cua host`) | | The phone | The bot's Space through spacesd, over the relay or directly: a state snapshot to read, an outbox to write, its screen as decoded frames | A bot drives the app with a few markers in its replies, which its instructions file (`CLAUDE.md`, `SOUL.md` or `AGENTS.md`) teaches it: `[[status: ...]]`, `[[step: ...]]`, `[[ask: action | details]]`, `[[login: site]]`, `[[notify: title | line]]`, `[[schedule: daily 09:00 | title | what to do]]` and `[[done: ...]]`. ## Run it From a clone of [trycua/cua](https://github.com/trycua/cua). The sample builds the Cua Spaces app export (which carries the cua SDK) from source, so you need Rust, and local Spaces need Docker. ```bash skip="no-runner" (cd libs/cua && cargo build --locked --release -p cua-spaces-ffi) libs/spaces-app-swift/scripts/stage-library.sh cd samples/cua-bots-macos swift build && .build/debug/CuaBots ``` Give the app a model key for the agents you pick (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`). The iPhone app is in `samples/cua-bots-ios`; its README covers the Xcode build and pairing. The app registers the Cua Spaces extensions at launch (`cuaSpacesRegister()`), so the runtime it embeds has persistent agents, the Keyvault and teleport. Because it links those Cua Spaces pieces, both samples are source-available under the FSL-1.1-MIT, like Cua Spaces; see [LICENSING.md](https://github.com/trycua/cua/blob/main/LICENSING.md). For examples in TypeScript and Rust (Tauri), see [Examples in other languages](</docs/spaces/examples/other-languages>). ## Limits - Rules are instructions to the bot, not enforcement: agent runs approve their own tool calls inside the bot's Space. - The routine clock runs while the Mac app is open. - Each bot's home is a folder on the Mac with the Cua Volume layout and secret refusal; the app doesn't sync it through the daemon's Cua Volume (`Spaces.volumeWrite`) yet. - Host access is per account, not per bot. - Saved sign-ins need the Cua daemon's Keyvault; approving them takes a Cua-signed app. - A phone off the Mac's network needs the bot's Space published through the relay, which the SDK can't do yet. --- # Infinite canvas: every window on one canvas A SwiftUI example on the Cua Spaces SDK: a hotkey opens a zoomable canvas of live windows from macOS, Linux and Omarchy Spaces, with agent and coworker cursors and agent threads. Infinite canvas puts every window of every Space on one zoomable canvas. A hotkey opens it, and each window is a live tile you can move, resize, zoom into and use. ![Infinite canvas: macOS, Linux and Omarchy windows, an agent thread and the stats card](</docs-assets/img/infinite-canvas/infinite-canvas.png>) ## Features - Global hotkey (⌥Space by default) opens and closes a full-screen canvas. - Trackpad pan, pinch and scroll zoom; ⌘0 fits every window. - One H.264 stream per window, hardware-decoded straight into the tile. - Level of detail: small and off-screen tiles ask the Space for fewer frames and fewer pixels; a tile you zoom into gets a keyframe. - Click a tile to give it your keyboard and pointer. - Agent cursors from each Space's presence, drawn with Cua Driver's cursor theme. - Coworker cursors: other people on the Space, each in their color. - Agent threads as canvas windows: send a message, watch the agent work in the tiles. - A thread's cursor waits in its title and glides out when the agent starts. - Real app icons from each Space, an OS mark per tile, a minimap and a stats card. - 59 tests and a benchmark target. ## Run it From a clone of [trycua/cua](https://github.com/trycua/cua), on macOS 26: ```bash skip="no-runner" (cd libs/cua && cargo build --release -p cua-sdk && node scripts/stage-uniffi-library.mjs --only=swift) cd samples/infinite-canvas-swift swift build -c release .build/release/infinite-canvas --spaces spaces.json --show ``` `spaces.json` lists the Spaces to show (any machine running cua-spacesd): ```json skip="no-runner" { "spaces": [ {"label": "Linux", "os": "linux", "url": "127.0.0.1:3211", "token": "…", "maxWindows": 4} ], "threads": ["Linux"] } ``` A local Linux Space for it: ```bash skip="no-runner" TOKEN=$(openssl rand -hex 16) docker run -d --name canvas-space --memory=4g -e CUA_ENV_TOKEN=$TOKEN \ -p 127.0.0.1:3211:3211 ghcr.io/trycua/linux:24.04 ``` Tests and the benchmark: ```bash skip="no-runner" scripts/test.sh swift build -c release --product canvas-bench && .build/release/canvas-bench --tiles 8 ``` ## Source [samples/infinite-canvas-swift](https://github.com/trycua/cua/tree/main/samples/infinite-canvas-swift): SwiftUI and AppKit on `Cua`, `CuaSpaces`, `CuaSpacesStreaming` and `CuaSpacesTeleport`. ## How each part works | Part | Guide | |------|-------| | Window streams and presence | [Stream a desktop](</docs/spaces/guides/stream-a-desktop>) | | Agent threads | [Use from an agent](</docs/spaces/guides/use-from-an-agent#run-an-agent-inside-a-space>) | | Adding Spaces by address | [Add a Space](</docs/spaces/guides/add-a-space>) | --- # Examples in other languages Agent-app examples on Cua Spaces in TypeScript, Rust (Tauri) and Swift: named bots, each working on a Space you watch live. [Cua Bots](</docs/spaces/examples/cua-bots>) is the featured example, in SwiftUI. The OpenKoalaBots samples build a smaller agent app three times, in TypeScript, Rust (Tauri) and Swift, so you can start from the language you ship in. Each one runs named bots as long-lived agent threads on a shared Space, streams the Space's desktop next to the thread, sends files into it and runs routines. ![The Tauri sample: a bot's thread next to the live Space](</docs-assets/img/openkoalabots/openkoalabots-tauri-thread.png>) ## Source | Language | Sample | Built on | |---|---|---| | TypeScript | [`samples/openkoalabot-example-ts`](https://github.com/trycua/cua/tree/main/samples/openkoalabot-example-ts) | A Node server and web UI on `@trycua/cua` | | Rust (Tauri) | [`samples/openkoalabot-example-tauri`](https://github.com/trycua/cua/tree/main/samples/openkoalabot-example-tauri) | A Tauri 2 app on the `cua-spaces` crate | | Swift | [`samples/openkoalabot-example-swift`](https://github.com/trycua/cua/tree/main/samples/openkoalabot-example-swift) | A SwiftUI app on `Cua` and `CuaSpaces` | All three pass one headless test, [`samples/openkoalabot-example-scenario`](https://github.com/trycua/cua/tree/main/samples/openkoalabot-example-scenario). ## Run one From a clone of [trycua/cua](https://github.com/trycua/cua). The samples build the cua SDK from source, so you need Rust. **TypeScript** ```bash skip="no-runner" (cd libs/cua && cargo build --release -p cua-sdk) (cd libs/cua/typescript && npm ci && npm run stage:uniffi && npm run build && npm run build:browser) cd samples/openkoalabot-example-ts npm ci && npm run build && npm run build:web npm run serve # open the URL it prints ``` **Rust + Tauri** ```bash skip="no-runner" cd samples/openkoalabot-example-tauri pnpm install --frozen-lockfile pnpm tauri build --debug --no-bundle src-tauri/target/debug/openkoalabot-example-tauri ``` **Swift** ```bash skip="no-runner" (cd libs/cua && cargo build --release -p cua-sdk && node scripts/stage-uniffi-library.mjs --only=swift) cd samples/openkoalabot-example-swift swift build && .build/debug/OpenKoalaBotExample ``` Each app needs a Space: create one with **New Space**, or add one by address. ## Where each part is explained | Part | Guide | |------|-------| | Create, add and delete a Space | [Add a Space](</docs/spaces/guides/add-a-space>) | | Live desktop and presence | [Stream a desktop](</docs/spaces/guides/stream-a-desktop>) | | File attachments | [Send files](</docs/spaces/guides/send-files>) | | Session teleport | [Teleport an app](</docs/spaces/guides/teleport-an-app>) | | Bots as agent threads | [Use from an agent](</docs/spaces/guides/use-from-an-agent#run-an-agent-inside-a-space>) | --- # Reference Every Spaces tool by category, with parameters, results, errors and approval. The Spaces contract: 86 tools with the same names and schemas on every surface: `cua mcp`, `cua daemon mcp` and the Spaces SDKs. Contract 0.7.0, MCP protocol revision 2025-06-18, transports `mcp_stdio` and `mcp_streamable_http`. Experimental: names and shapes can change between minor versions. ## Tools ### [Space lifecycle](</docs/spaces/reference/lifecycle>) Register, provision, list and release Spaces. | Tool | Description | Effect | | --- | --- | --- | | [`add_space`](</docs/spaces/reference/lifecycle#add_space>) | Add an existing machine as a Space by address and token. | mutating, idempotent | | [`remove_space`](</docs/spaces/reference/lifecycle#remove_space>) | Forget a registered Space without touching it. | mutating, idempotent | | [`list_spaces`](</docs/spaces/reference/lifecycle#list_spaces>) | List the Spaces this connection can see. | read-only, idempotent | | [`create_space`](</docs/spaces/reference/lifecycle#create_space>) | Create a Space: a new sandbox on this machine, on one of your machines, or in the cloud. | destructive | | [`delete_space`](</docs/spaces/reference/lifecycle#delete_space>) | Delete a Space's sandbox and forget it. | destructive | | [`stop_space`](</docs/spaces/reference/lifecycle#stop_space>) | Turn a Space off: suspend it (memory kept) or stop it (disk kept). | mutating, idempotent | | [`start_space`](</docs/spaces/reference/lifecycle#start_space>) | Turn a Space back on: resume or boot it. | mutating, idempotent | ### [Commands and files](</docs/spaces/reference/commands-and-files>) Run shell commands and move files in and out of a Space. | Tool | Description | Effect | | --- | --- | --- | | [`space_bash`](</docs/spaces/reference/commands-and-files#space_bash>) | Run a shell command inside the Space. | destructive | | [`space_write`](</docs/spaces/reference/commands-and-files#space_write>) | Write literal text to a path inside the Space. | destructive | | [`upload`](</docs/spaces/reference/commands-and-files#upload>) | Send a local file or folder into the Space. | destructive | | [`send_file`](</docs/spaces/reference/commands-and-files#send_file>) | Send a host file or folder into the Space's Downloads, verified by sha256 on arrival. | destructive | | [`download`](</docs/spaces/reference/commands-and-files#download>) | Fetch a file or folder out of the Space. | read-only, idempotent | ### [Streams and display](</docs/spaces/reference/streams-and-display>) Stream a Space's desktop or windows, and show a Space to the operator. | Tool | Description | Effect | | --- | --- | --- | | [`stream_endpoint`](</docs/spaces/reference/streams-and-display#stream_endpoint>) | Return a ticketed media endpoint for the Space's desktop or one window. | mutating, idempotent | | [`list_space_windows`](</docs/spaces/reference/streams-and-display#list_space_windows>) | List the windows open inside the Space. | read-only, idempotent | | [`stream_space_window`](</docs/spaces/reference/streams-and-display#stream_space_window>) | Draw one Space window on the operator's desktop. | mutating, idempotent | | [`show_space_pip`](</docs/spaces/reference/streams-and-display#show_space_pip>) | Pin the Space as picture-in-picture on the operator's desktop. | mutating, idempotent | | [`hide_space_pip`](</docs/spaces/reference/streams-and-display#hide_space_pip>) | Unpin the picture-in-picture window. | mutating, idempotent | | [`open_space_viewer`](</docs/spaces/reference/streams-and-display#open_space_viewer>) | Open the full Space viewer on the operator's desktop. | mutating, idempotent | ### [Services](</docs/spaces/reference/services>) Discover and call the MCP services inside a Space. | Tool | Description | Effect | | --- | --- | --- | | [`list_tools`](</docs/spaces/reference/services#list_tools>) | List the MCP services and tools reachable inside the Space. | read-only, idempotent | | [`call_tool`](</docs/spaces/reference/services#call_tool>) | Call a service tool inside the Space. | destructive | ### [Agents](</docs/spaces/reference/agents>) Run coding agents inside a Space. | Tool | Description | Effect | | --- | --- | --- | | [`agent_start`](</docs/spaces/reference/agents#agent_start>) | Start a coding agent in the Space. | destructive | | [`agent_message`](</docs/spaces/reference/agents#agent_message>) | Send a follow-up to an agent run. | destructive | | [`agent_events`](</docs/spaces/reference/agents#agent_events>) | Read an agent run's events after a cursor. | read-only, idempotent | | [`agent_status`](</docs/spaces/reference/agents#agent_status>) | Read an agent run's status and result. | read-only, idempotent | | [`agent_interrupt`](</docs/spaces/reference/agents#agent_interrupt>) | Interrupt an agent run's current turn. | destructive | | [`agent_stop`](</docs/spaces/reference/agents#agent_stop>) | Stop an agent run. | destructive | | [`agent_list`](</docs/spaces/reference/agents#agent_list>) | List agent runs in the Space. | read-only, idempotent | | [`agent_capabilities`](</docs/spaces/reference/agents#agent_capabilities>) | Report what the agent harnesses support. | read-only, idempotent | ### [Persistent agents](</docs/spaces/reference/persistent-agents>) Named agents whose memory outlives their Space: homes in Cua Volume, routines, notifications, pause and resume, access to the user's computers. | Tool | Description | Effect | | --- | --- | --- | | [`persistent_agent_create`](</docs/spaces/reference/persistent-agents#persistent_agent_create>) | Create a named agent whose memory outlives its Space. | destructive | | [`persistent_agent_list`](</docs/spaces/reference/persistent-agents#persistent_agent_list>) | List persistent agents. | read-only, idempotent | | [`persistent_agent_remove`](</docs/spaces/reference/persistent-agents#persistent_agent_remove>) | Forget a persistent agent. | destructive | | [`persistent_agent_send`](</docs/spaces/reference/persistent-agents#persistent_agent_send>) | Give a persistent agent a turn. | destructive | | [`persistent_agent_save`](</docs/spaces/reference/persistent-agents#persistent_agent_save>) | Save a persistent agent's home now. | mutating, idempotent | | [`agent_pause`](</docs/spaces/reference/persistent-agents#agent_pause>) | Pause a persistent agent: its run, its routines and its Space. | mutating, idempotent | | [`agent_resume`](</docs/spaces/reference/persistent-agents#agent_resume>) | Resume a paused persistent agent. | destructive | | [`routine_add`](</docs/spaces/reference/persistent-agents#routine_add>) | Schedule a recurring turn for a persistent agent. | destructive | | [`routine_list`](</docs/spaces/reference/persistent-agents#routine_list>) | List routines. | read-only, idempotent | | [`routine_remove`](</docs/spaces/reference/persistent-agents#routine_remove>) | Delete a routine. | mutating, idempotent | | [`routine_set_enabled`](</docs/spaces/reference/persistent-agents#routine_set_enabled>) | Turn a routine on or off. | mutating, idempotent | | [`notify_user`](</docs/spaces/reference/persistent-agents#notify_user>) | Tell the user something in the Cua app. | destructive | | [`notifications_list`](</docs/spaces/reference/persistent-agents#notifications_list>) | Read the notifications feed. | read-only, idempotent | | [`notifications_ack`](</docs/spaces/reference/persistent-agents#notifications_ack>) | Mark notifications read. | mutating, idempotent | | [`computer_access_grant`](</docs/spaces/reference/persistent-agents#computer_access_grant>) | Let a persistent agent use one of the user's computers. | destructive | | [`computer_access_revoke`](</docs/spaces/reference/persistent-agents#computer_access_revoke>) | Take back a persistent agent's access to a computer. | mutating, idempotent | | [`computer_access_list`](</docs/spaces/reference/persistent-agents#computer_access_list>) | List per-agent computer access. | read-only, idempotent | ### [Session teleport](</docs/spaces/reference/teleport>) Move a signed-in app session from this machine into a Space. | Tool | Description | Effect | | --- | --- | --- | | [`teleport_manifest`](</docs/spaces/reference/teleport#teleport_manifest>) | Describe what a session teleport would move. | read-only, idempotent | | [`teleport_app`](</docs/spaces/reference/teleport#teleport_app>) | Move an app session into the Space. | destructive | ### [Keyvault](</docs/spaces/reference/keyvault>) Use the user's saved credentials in a Space, only with their approval. | Tool | Description | Effect | | --- | --- | --- | | [`request_site_login`](</docs/spaces/reference/keyvault#request_site_login>) | Sign in to a site in the Space with the user's saved password. | destructive | ### [Hotspot](</docs/spaces/reference/hotspot>) Share this machine's network with a Space. | Tool | Description | Effect | | --- | --- | --- | | [`hotspot_start`](</docs/spaces/reference/hotspot#hotspot_start>) | Start sharing this machine's network with a Space. | destructive | | [`hotspot_stop`](</docs/spaces/reference/hotspot#hotspot_stop>) | Stop sharing this machine's network. | destructive | | [`hotspot_status`](</docs/spaces/reference/hotspot#hotspot_status>) | Report which Spaces are sharing this machine's network, with counters. | read-only, idempotent | ### [Cua Volume](</docs/spaces/reference/volume>) Read and write the volume every Space and agent shares, decide who may reach what, and see where it is stored and how it syncs. | Tool | Description | Effect | | --- | --- | --- | | [`volume_ls`](</docs/spaces/reference/volume#volume_ls>) | List a folder of the Cua Volume. | read-only, idempotent | | [`volume_read`](</docs/spaces/reference/volume#volume_read>) | Read a file from the Cua Volume. | read-only, idempotent | | [`volume_write`](</docs/spaces/reference/volume#volume_write>) | Write a file to the Cua Volume. | destructive | | [`volume_delete`](</docs/spaces/reference/volume#volume_delete>) | Delete a file from the Cua Volume. | destructive | | [`volume_history`](</docs/spaces/reference/volume#volume_history>) | List a Cua Volume file's versions. | read-only, idempotent | | [`volume_restore`](</docs/spaces/reference/volume#volume_restore>) | Restore an old version of a Cua Volume file. | destructive | | [`volume_grant`](</docs/spaces/reference/volume#volume_grant>) | Grant an agent or Space more Cua Volume access. | destructive | | [`volume_revoke`](</docs/spaces/reference/volume#volume_revoke>) | Revoke a Cua Volume grant. | mutating, idempotent | | [`volume_grants`](</docs/spaces/reference/volume#volume_grants>) | List Cua Volume grants. | read-only, idempotent | | [`volume_request_access`](</docs/spaces/reference/volume#volume_request_access>) | Ask the user for more Cua Volume access. | destructive | | [`volume_requests`](</docs/spaces/reference/volume#volume_requests>) | List Cua Volume access requests waiting for the user. | read-only, idempotent | | [`volume_approve`](</docs/spaces/reference/volume#volume_approve>) | Approve a Cua Volume access request. | destructive | | [`volume_deny`](</docs/spaces/reference/volume#volume_deny>) | Decline a Cua Volume access request. | destructive | | [`volume_audit`](</docs/spaces/reference/volume#volume_audit>) | Read the Cua Volume audit log. | read-only, idempotent | | [`volume_storage`](</docs/spaces/reference/volume#volume_storage>) | Show where the Cua Volume keeps its bytes. | read-only, idempotent | | [`volume_storage_set`](</docs/spaces/reference/volume#volume_storage_set>) | Test or change where the Cua Volume keeps its bytes. | mutating, idempotent | | [`volume_mount_status`](</docs/spaces/reference/volume#volume_mount_status>) | Show whether the Cua Volume is mounted as a volume. | read-only, idempotent | | [`volume_mount`](</docs/spaces/reference/volume#volume_mount>) | Mount the Cua Volume as a volume. | mutating, idempotent | | [`volume_unmount`](</docs/spaces/reference/volume#volume_unmount>) | Unmount the Cua Volume volume. | mutating, idempotent | | [`volume_sync_status`](</docs/spaces/reference/volume#volume_sync_status>) | Show the Cua Volume's sync state across devices. | read-only, idempotent | | [`volume_sync_events`](</docs/spaces/reference/volume#volume_sync_events>) | Wait for Cua Volume sync events. | read-only, idempotent | | [`volume_sync_resolve`](</docs/spaces/reference/volume#volume_sync_resolve>) | Mark a Cua Volume sync conflict as seen. | mutating, idempotent | | [`volume_cache_stats`](</docs/spaces/reference/volume#volume_cache_stats>) | Show the Cua Volume block cache. | read-only, idempotent | | [`volume_cache_set`](</docs/spaces/reference/volume#volume_cache_set>) | Set the Cua Volume block cache's size cap. | mutating, idempotent | | [`volume_cache_clear`](</docs/spaces/reference/volume#volume_cache_clear>) | Clear the Cua Volume block cache. | mutating, idempotent | ### [Sharing](</docs/spaces/reference/sharing>) Let other cua.ai accounts watch or edit a Space, through the relay. | Tool | Description | Effect | | --- | --- | --- | | [`share_space`](</docs/spaces/reference/sharing#share_space>) | Let another cua.ai account watch or edit a Space. | mutating, idempotent | | [`unshare_space`](</docs/spaces/reference/sharing#unshare_space>) | Stop sharing a Space with an account, or with everyone. | mutating, idempotent | | [`space_shares`](</docs/spaces/reference/sharing#space_shares>) | List who a Space is shared with. | read-only, idempotent | | [`relay_register_space`](</docs/spaces/reference/sharing#relay_register_space>) | Publish a Space on the relay for the account's other devices. | mutating, idempotent | | [`relay_unregister_space`](</docs/spaces/reference/sharing#relay_unregister_space>) | Take a Space off the relay. | mutating, idempotent | ### [Your cloud](</docs/spaces/reference/cloud>) Create Spaces in your own AWS, Google Cloud or Modal account: connect it, check it, and clean up what Cua created there. | Tool | Description | Effect | | --- | --- | --- | | [`cloud_status`](</docs/spaces/reference/cloud#cloud_status>) | List your clouds (AWS, Google Cloud, Modal): found credentials, the connected account and region, what each can run at what hourly cost, and what Cua created there. | read-only, idempotent | | [`cloud_connect`](</docs/spaces/reference/cloud#cloud_connect>) | Connect a cloud account Spaces can be created in. | mutating, idempotent | | [`cloud_test`](</docs/spaces/reference/cloud#cloud_test>) | Check a cloud account without creating anything. | read-only, idempotent | | [`cloud_disconnect`](</docs/spaces/reference/cloud#cloud_disconnect>) | Forget a connected cloud account. | mutating, idempotent | | [`cloud_sweep`](</docs/spaces/reference/cloud#cloud_sweep>) | Find and delete what Cua left in your clouds. | destructive | ## Space ids Every tool that takes `space` accepts a Space id or its name. | Provider | Id | Tools | | --- | --- | --- | | `cloud` | `cloud:<name>` | all but the provider-only ones | | `local` | `local:<name>` | all but the provider-only ones | | `direct` | `direct:<host:port>` | all but the provider-only ones | | `relay` | `relay:<machine-id>` | all but the provider-only ones | ## Approval - `cua mcp --permissions` (or `CUA_MCP_PERMISSIONS`) gates each tool as `spaces:<tool>`. `spaces:all` grants every tool, `spaces:readonly` only the read-only ones. - Each tool publishes MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`); clients use them to decide what to confirm. The **Approval** row of each tool shows them. - Metered tools () allocate or extend a billed cloud Space. Everything else is free. - Agent runs are auto-approved inside the Space: the Space is the sandbox. A session teleport needs an approval minted from its manifest, and `acknowledge_sensitive` for sensitive items. ## Errors A failed call returns a tool result with `isError: true` and a machine `kind`. See [Errors](</docs/spaces/reference/errors>). --- # Space lifecycle Register, provision, list and release Spaces. | Tool | Description | | --- | --- | | [`add_space`](#add_space) | Add an existing machine as a Space by address and token. | | [`remove_space`](#remove_space) | Forget a registered Space without touching it. | | [`list_spaces`](#list_spaces) | List the Spaces this connection can see. | | [`create_space`](#create_space) | Create a Space: a new sandbox on this machine, on one of your machines, or in the cloud. | | [`delete_space`](#delete_space) | Delete a Space's sandbox and forget it. | | [`stop_space`](#stop_space) | Turn a Space off: suspend it (memory kept) or stop it (disk kept). | | [`start_space`](#start_space) | Turn a Space back on: resume or boot it. | ## add_space Add an existing machine as a Space by address and token. Tries a GetCapabilities handshake with the Space's cua-spacesd first. A local:<name> or cloud:<name> id without one is still a Space, with an empty capability set (its declared services, tunnels and public URLs work); a direct URL without one is added as an MCP Space when it answers MCP initialize (service `mcp`), and refused with spacesd_not_available when it answers neither. The Space is stored in ~/.cua/spaces.json and its token in ~/.cua/spaces-credentials.json (0600). Re-adding the same address updates it. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:add_space`; mutating, idempotent | | Swift SDK | `SpacesConnection.addSpace(url:token:)` | | Rust | `Spaces::add` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | none | Display name. Default: the guest's hostname. | | `service` | `string` | `mcp` | For an MCP endpoint URL: the service name to register it under. Default `mcp`. | | `token` | `string` | none | The spacesd token (or the bearer an MCP endpoint needs). Stored in a separate 0600 credentials file, never in `spaces.json`. | | `url` | `string` | required | The Space's cua-spacesd: `http(s)://host:port` (port defaults to 3211), `host:port`, or a Space id (`cloud:<name>`, `local:<name>`). Any other MCP endpoint (for example `http://host:8765/mcp`) is added as a Space with one MCP service and no spacesd capabilities. | ### Result JSON: the registered Space (`id`, `name`, `provider`, `spacesd_version`, `features`, `os`, `services`, `added_at`) and `phase: "ready"`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`unauthenticated`](</docs/spaces/reference/errors#unauthenticated>) ## remove_space Forget a registered Space without touching it. Only the registration and its stored token are removed. The sandbox keeps running, including a Space in your cloud (its cloud resources and relay machine stay); delete_space deletes a Space that create_space made. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:remove_space`; mutating, idempotent | | Swift SDK | `SpacesConnection.removeSpace(_:)` | | Rust | `Spaces::remove` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result JSON: `{"removed": <Space>}`, the registration that was removed. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>) ## list_spaces List the Spaces this connection can see. Every registered Space (cloud, local, direct and relay) with its id (local:<name>, cloud:<name>, direct:<host:port>, relay:<machine-id>), provider, spacesd version (empty without one) and supported features as of the last handshake. The signed-in account's machines on the relay are listed too: those are the names `create_space` accepts as on="host:<machine>". A Space a machine provides carries `host` (that machine's id) and `host_name`; group them under it when you show them. A Space in your cloud carries `cloud` (the provider: aws, gcp, modal), `cloud_place` (account and region, to show with it) and `cloud_delete` (where delete_space can delete it: here, host:<machine>, or elsewhere). | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:list_spaces`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `SpacesConnection.spaces()` | | Rust | `Spaces::list` | ### Parameters No parameters. ### Result JSON: an array of Spaces (`id`, `name`, `provider`, `spacesd_version`, `features`, `os`, `services`, `added_at`, for a Space one of your machines provides `host` and `host_name`, for a Space that can be turned off and on `power` (`suspend` or `stop`) and `power_state`, and for a Space in your cloud `cloud`, `cloud_place` and `cloud_delete`, and for a relay machine the relay cannot reach now `status` (`offline` or `not sharing`)), each with `phase`: `ready`, or `offline` for a relay machine the relay cannot reach now (a machine that is off or stopped sharing, or a Space that is gone and left its record; `relay_unregister_space` removes it). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`relay`](</docs/spaces/reference/errors#relay>), [`unauthenticated`](</docs/spaces/reference/errors#unauthenticated>) ## create_space Create a Space: a new sandbox on this machine, on one of your machines, or in the cloud. Creates a new sandbox where `on` says (local: this machine, free; cloud: Cua cloud, metered; aws, gcp or modal: the user's own cloud account, connected with cloud_connect, billed there; default: the user's default location, else local), waits until it is ready (its spacesd when the image runs one, else its declared services), and registers it as local:<name> or cloud:<name>. `kind` (auto, container, vm) and `runtime` (auto, or an engine the location offers for the kind) are chosen automatically by default: the canonical Linux image runs as a gVisor container, macOS images as Lume VMs, Windows and disk-only images as VMs (QEMU locally, KubeVirt in the cloud). An impossible combination fails with invalid_placement and lists the valid values. Any image works; without cua-spacesd the capability set is empty. reuse=true returns a reachable registered Space in the same location with the requested services instead of creating one. On the user's own machines: on="host:<machine>" creates the Space on a machine set up with `cua host setup --provide-spaces` (for example "spin up 2 macOS Spaces on my spare Mac mini" is on="host:spare mac mini", image="macos", count=2). The machine is matched by id, exact name, or the words of its name; when several fit equally the call fails with ambiguous_host listing them (ask the user which, then pass on="host:<id>"). The host runs it with its own runtimes (Lume for macOS VMs on a Mac, Docker for Linux), the Space joins the relay and is returned as relay:<machine> with `host` set. A Mac runs at most two macOS VMs (Apple's macOS license, enforced by Apple Virtualization); a host at a limit fails with limit_exceeded. Only the machine's owner and the accounts it is shared with as editors can create there, from an enrolled device. on="direct:<addr>" is not accepted here: a direct address is one already-running Space, attached with add_space, not a host this call can create on. | | | | --- | --- | | Providers | cloud only (`cloud:<name>`), local only (`local:<name>`), relay only (`relay:<machine-id>`) | | Platforms | all (macos, windows, linux) | | Metering | cloud | | Approval | permission `spaces:create_space`; destructive | | Swift SDK | `Spaces.create(options)` | | Rust | `Spaces::create` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `command` | `string[]` | none | Entrypoint override (argv), for example `["python", "-m", "my_mcp", "--port", "8765"]`. | | `count` | `integer` | none | How many Spaces to create, 1 to 8 (default 1), all with the same settings (a name gets `-2`, `-3`, ... suffixes). With more than one the result is an array. At least 0. | | `env` | `map of string` | none | Guest environment variables. | | `image` | `string` | none | Guest image. Default: the canonical Linux image (`ghcr.io/trycua/linux:24.04`), whose variant follows the kind. | | `kind` | `"auto" \| "container" \| "vm"` | `auto` | What kind of machine. Default `auto` (from the image). `auto`: From the image: macOS and Windows images are VMs, an image with a container rootfs is a container, a disk-only image is a VM. `container`: A container (gVisor, or runc locally). `vm`: A virtual machine (QEMU or Lume locally, KubeVirt in the cloud). | | `name` | `string` | none | Name. Default: a generated `space-<hex>`. | | `on` | `string` | none | Where it runs: `local` (this machine, free), `cloud` (Cua cloud, metered), or `host:<machine>`: one of the user's own machines that provides Spaces (`cua host setup --provide-spaces` on it), named by id or by words from its name ("host:spare mac mini" matches "Mac mini (spare)" or `dillons-mac-mini`). Default: the user's default location (`cua config set default.on`, `CUA_DEFAULT_ON`), else `local`. Not `direct:<addr>`: that is one already-running Space at a fixed address, attached with `add_space`, not a host this can create on. An existing machine is added with `add_space`, not created. | | `reuse` | `boolean` | `false` | Return a reachable registered Space in the same location that has the requested services instead of creating one (get-or-create). Default false. | | `runtime` | `string` | `auto` | Which engine. Default `auto`: the safest one for the location and kind. Local: `gvisor` or `runc` (containers), `qemu` or `lume` (VMs). Cloud: `gvisor` (containers), `kubevirt` (VMs). Anything else fails with `invalid_placement`, listing the valid values. | | `services` | `map of integer` | none | Named services (name to guest port), for example `{"mcp": 8765}`. Each is probed for readiness and reachable with `list_tools` / `call_tool` (`service=<name>`). | | `spacesd` | `boolean` | `true` | Whether the image runs cua-spacesd. Default: true for the canonical images (and no image), false for any other image. A Space without it has an empty capability set: only its services work. | | `timeout` | `integer` | `600` | Seconds to wait for readiness (local). Default 600. At least 0. | | `wait` | `boolean` | `true` | Wait until the Space is ready (its spacesd, or its declared services). Default true. | ### Result JSON: the registered Space, `phase: "ready"` and `reused` (true when `reuse` returned an existing Space); with `wait: false`, `{"id", "phase": "starting"}` at once; with `count` above 1, an array of them. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`invalid_placement`](</docs/spaces/reference/errors#invalid_placement>), [`fleet`](</docs/spaces/reference/errors#fleet>), [`fleet_admission_denied`](</docs/spaces/reference/errors#fleet_admission_denied>), [`cloud_credit_exhausted`](</docs/spaces/reference/errors#cloud_credit_exhausted>), [`sandbox`](</docs/spaces/reference/errors#sandbox>), [`timeout`](</docs/spaces/reference/errors#timeout>), [`ambiguous_host`](</docs/spaces/reference/errors#ambiguous_host>), [`limit_exceeded`](</docs/spaces/reference/errors#limit_exceeded>) ### Notes - Cloud needs Fleet credentials (`cua auth login`); local needs a container engine, QEMU or Lume (`cua runtime doctor`). ## delete_space Delete a Space's sandbox and forget it. Deletes the sandbox of a Space that create_space made (cloud: metering stops; local: the instance is deleted; on one of your machines, on="host:...": that machine deletes it and it leaves the relay) and forgets it. A Space in your cloud (list_spaces: cloud, cloud_place, cloud_delete) is deleted permanently through the records of the device that created it: cloud_delete "here" deletes it from this device; "host:<machine>" asks that host (it must be online, and only its owner may); "elsewhere" is refused with where it was created: delete it there, or remove_space. Another Space added by address (direct:, relay:) is only forgotten: cua did not create it. Any hotspot on the Space is stopped first. To forget a Space without deleting anything, use remove_space. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:delete_space`; destructive | | Swift SDK | `Spaces.delete(space)` | | Rust | `Spaces::delete` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result Text: what was deleted (`Deleted <id> (...)`), or `Removed <id> (...)` for a Space added by address. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`fleet`](</docs/spaces/reference/errors#fleet>), [`sandbox`](</docs/spaces/reference/errors#sandbox>) ## stop_space Turn a Space off: suspend it (memory kept) or stop it (disk kept). Turns a Space off the way its provider can: a local container or QEMU VM is suspended (its memory is kept and it resumes where it was), a local Lume VM is stopped (its disk is kept and it boots again), a Space one of your machines provides is stopped by that machine, which frees its memory, and a Space in your own cloud (on="aws", "gcp") is stopped there: compute billing stops, its disk stays (storage is still billed). Anything streaming from the Space and any hotspot on it end first. A Fleet cloud Space, a Modal sandbox, a Space added by address and your own computers on the relay cannot be turned off (wrong_provider); delete_space deletes a Space instead. start_space turns it on again. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:stop_space`; mutating, idempotent | | Swift SDK | `Spaces.stop(space)` | | Rust | `Spaces::stop` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result JSON: `space`, `state` (`suspended` or `stopped`), `power` (`suspend` or `stop`) and `message`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`wrong_provider`](</docs/spaces/reference/errors#wrong_provider>), [`sandbox`](</docs/spaces/reference/errors#sandbox>), [`relay`](</docs/spaces/reference/errors#relay>), [`permission_denied`](</docs/spaces/reference/errors#permission_denied>), [`cloud`](</docs/spaces/reference/errors#cloud>) ### Notes - list_spaces says which Spaces turn off (`power`: `suspend` or `stop`; absent when it cannot) and how they were left (`power_state`). ## start_space Turn a Space back on: resume or boot it. Turns a Space stop_space turned off on again: resumes a suspended one, boots a stopped one (a Space one of your machines provides, or one in your cloud, joins the relay again), and leaves a running one as it is. Returns once it answers again (bounded). The same Spaces turn on as turn off. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:start_space`; mutating, idempotent | | Swift SDK | `Spaces.start(space)` | | Rust | `Spaces::start` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result JSON: `space`, `state` (`running`), `power` (`suspend` or `stop`) and `message`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`wrong_provider`](</docs/spaces/reference/errors#wrong_provider>), [`sandbox`](</docs/spaces/reference/errors#sandbox>), [`relay`](</docs/spaces/reference/errors#relay>), [`permission_denied`](</docs/spaces/reference/errors#permission_denied>), [`cloud`](</docs/spaces/reference/errors#cloud>), [`timeout`](</docs/spaces/reference/errors#timeout>) --- # Commands and files Run shell commands and move files in and out of a Space. | Tool | Description | | --- | --- | | [`space_bash`](#space_bash) | Run a shell command inside the Space. | | [`space_write`](#space_write) | Write literal text to a path inside the Space. | | [`upload`](#upload) | Send a local file or folder into the Space. | | [`send_file`](#send_file) | Send a host file or folder into the Space's Downloads, verified by sha256 on arrival. | | [`download`](#download) | Fetch a file or folder out of the Space. | ## space_bash Run a shell command inside the Space. Runs through the spacesd's ProcessService with `/bin/sh -c`. Output is stdout, then `[stderr]` and stderr, then `[exit N]`; each stream keeps its last 16000 characters when longer. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:space_bash`; destructive | | Swift SDK | `Space.bash(_:)` | | Rust | `Space::bash` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `command` | `string` | required | Command line, run with `/bin/sh -c` (`cmd.exe /C` on Windows guests). | | `space` | `string` | required | Space id or name. | | `timeout` | `integer` | `60` | Seconds before the command is killed. Default 60. At least 0. | ### Result Text: stdout, then `[stderr]` and stderr, then `[exit N]`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`timeout`](</docs/spaces/reference/errors#timeout>) ## space_write Write literal text to a path inside the Space. Written through FilesystemService, verified by sha256; no shell is involved, so nothing needs quoting. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:space_write`; destructive | | Swift SDK | `Space.write(_:to:)` | | Rust | `Space::write` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `content` | `string` | required | The exact file contents. | | `path` | `string` | required | Absolute path in the Space. Parent directories are created. | | `space` | `string` | required | Space id or name. | ### Result Text: `wrote <path> (<n> bytes, sha256 <hex> verified in Space)`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`transfer_failed`](</docs/spaces/reference/errors#transfer_failed>) ### Notes - Literal text: no local file is read and nothing is shell-quoted, which is the whole reason it exists next to space_bash. ## upload Send a local file or folder into the Space. Files stream through FilesystemService in chunks with a sha256 check; a folder is copied file by file (empty directories included, symlinks skipped). No size cap. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:upload`; destructive | | Swift SDK | `space.files.send(_:to:within:)` | | Rust | `Space::upload` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `dest` | `string` | `<guest home>/<name>` | Absolute destination path in the Space; an existing file there is replaced. Default: `<guest home>/<name>`, or `<name> (1).ext` when that is taken (nothing is replaced). | | `path` | `string` | required | Host path (file or folder). | | `space` | `string` | required | Space id or name. | ### Result JSON: `source`, `dest`, `kind` (`file` or `folder`), `files`, `directories`, `bytes`, `sha256` (single file) and `verified`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`transfer_failed`](</docs/spaces/reference/errors#transfer_failed>), [`io`](</docs/spaces/reference/errors#io>) ### Notes - Without `dest` nothing in the Space is replaced: a taken `<home>/<name>` lands as `<name> (1).ext`. An explicit `dest` is written as given and replaces a file already there. ## send_file Send a host file or folder into the Space's Downloads, verified by sha256 on arrival. The same transfer the Cua Spaces pop-out's Teleport drop zone performs: files land in the Space user's ~/Downloads through the spacesd's TeleportService (chunked, resumable, every file verified by sha256 before it is published). A transfer that does not land, or lands corrupted, is an error and not a reported success. respect_ignorefiles (default true) applies ONLY when `path` is a folder. It honors `.gitignore` and `.ignore` at the folder root and in every subdirectory (each scoped to its own subtree, so a nested ignore file overrides its parent), plus `.dockerignore` at the root only. All three use gitignore semantics: negation (`!`), directory-only patterns (`foo/`), anchoring, `*`/`?`/`[...]`, `**`, comments, last-matching-rule-wins. As in git, an excluded directory is pruned and `!` cannot re-include anything beneath it. `.git/` is always skipped and symlinks are never followed. For a single named file it has NO effect. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:send_file`; destructive | | Swift SDK | `Space.sendFile(_:into:respectingIgnoreFiles:)` | | Rust | `Space::send_file` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `path` | `string` | required | Host path (file or folder) to send. | | `respect_ignorefiles` | `boolean` | `true` | Honor `.gitignore` / `.ignore` (nested) and `.dockerignore` (root only) when sending a folder. Default true. No effect on a single named file. | | `space` | `string` | required | Space id or name. | | `target_directory` | `string` | `~/Downloads` | Subdirectory of the Space user's `~/Downloads` to land in. `~/Downloads` and `~/Downloads/<sub>` are accepted too. Default: `~/Downloads` itself. | ### Result JSON: `source`, `destination`, `dest`, `kind`, `files` (each `path`, `size`, `sha256`), `bytes`, `respect_ignorefiles`, `skipped_by_ignorefiles`, `skipped_count`, `skipped_by_guest` and `verified`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`transfer_failed`](</docs/spaces/reference/errors#transfer_failed>), [`io`](</docs/spaces/reference/errors#io>) ### Notes - Unlike `upload`, the copy is verified in the Space by sha256 before the call returns: a transfer that does not land, or lands corrupted, is an error and not a reported success. - `respect_ignorefiles` applies only when the path is a folder, with gitignore semantics; a file named explicitly is always sent. - A file of the same name already in the Space is kept: the new copy lands as `name (1).ext` and its path is in `files`. ## download Fetch a file or folder out of the Space. Files stream through FilesystemService and are verified by sha256; a folder is copied recursively. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:download`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `Space.download(_:into:)` | | Rust | `Space::download` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `dest` | `string` | `~/Downloads/cua-spaces` | Host destination directory. Default `~/Downloads/cua-spaces`. | | `path` | `string` | required | Absolute path in the Space (file or folder). | | `space` | `string` | required | Space id or name. | ### Result JSON: `source`, `dest`, `kind`, `files`, `bytes`, `sha256` (single file) and `verified`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`transfer_failed`](</docs/spaces/reference/errors#transfer_failed>), [`io`](</docs/spaces/reference/errors#io>) ### Notes - Nothing on the host is overwritten: when `dest` already has an entry of that name the copy lands as `name (1).ext` (or `name (1)` for a folder), and `dest` in the result is the path used. --- # Streams and display Stream a Space's desktop or windows, and show a Space to the operator. | Tool | Description | | --- | --- | | [`stream_endpoint`](#stream_endpoint) | Return a ticketed media endpoint for the Space's desktop or one window. | | [`list_space_windows`](#list_space_windows) | List the windows open inside the Space. | | [`stream_space_window`](#stream_space_window) | Draw one Space window on the operator's desktop. | | [`show_space_pip`](#show_space_pip) | Pin the Space as picture-in-picture on the operator's desktop. | | [`hide_space_pip`](#hide_space_pip) | Unpin the picture-in-picture window. | | [`open_space_viewer`](#open_space_viewer) | Open the full Space viewer on the operator's desktop. | ## stream_endpoint Return a ticketed media endpoint for the Space's desktop or one window. Opens a media session (StreamService.OpenMedia) for the desktop or one window and returns its ticketed WebSocket URL (rcdp wire v2, see libs/cua/proto/MEDIA.md). The ticket is short-lived, bound to that one session, and safe to put in a URL; it is not the Space's token. Works for every provider because the token comes from the SDK, not from the guest. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:stream_endpoint`; mutating, idempotent | | Requires (spacesd) | `desktop_stream` | | Swift SDK | `Space.streamEndpoint(forceRefresh:)` | | Rust | `Streams::open` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `app_name` | `string` | none | Stream the largest visible window of this app (case-insensitive substring). Ignored when `window_id` is set. | | `display` | `string` | none | Display id for a desktop stream. Default: the primary display. | | `max_fps` | `integer` | `30` | Maximum frames per second. Default 30. At least 0. | | `space` | `string` | required | Space id or name. | | `ticket_ttl` | `integer` | `60` | Ticket lifetime in seconds (at most 600). Default 60. At least 0. | | `window_id` | `string` | none | Window handle from `list_space_windows`. Streams that window. | ### Result JSON: `space`, `media_session_id`, `ws_url` (ticket included), `ticket`, `ticket_expires_at`, `codec` (`h264`, `bgra` or `png`), `wire_version`, `frame_size` and `needs_gateway_headers`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>), [`stream`](</docs/spaces/reference/errors#stream>) ### Notes - Giving the caller frames is a different act from presenting the Space to an operator. - Replaces local_rcdp, which only worked for Local macOS Spaces and read its token over ssh. ## list_space_windows List the windows open inside the Space. The windows a viewer can stream now, from StreamService.ListTargets: on screen, sized, normal app windows (no hidden, minimized or system-UI windows), each with its handle, title, app and bounds. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:list_space_windows`, in `spaces:readonly`; read-only, idempotent | | Requires (spacesd) | `windows` | | Swift SDK | `Space.windows()` | | Rust | `Streams::windows` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `app_name` | `string` | none | Only windows of this app (case-insensitive substring). | | `space` | `string` | required | Space id or name. | ### Result JSON: an array of windows (`window_id`, `epoch`, `title`, `app_name`, `app_id`, `pid`, `bounds`, `on_screen`, `focused`, `available`, `limitation`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>) ## stream_space_window Draw one Space window on the operator's desktop. FOR THE HUMAN. Opens a bidirectional window on the operator's desktop showing one Space window (the Cua Spaces app draws it from a media ticket). Pass app_name to auto-pick that app's largest visible window, or an exact window_id. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | macos (the operator's machine) | | Metering | free | | Approval | permission `spaces:stream_space_window`; mutating, idempotent | | Requires (spacesd) | `window_stream` | | Requires (this machine) | `operator_display` | | Swift SDK | `space.operatorDisplay.streamWindowToOperatorDesktop(_:)` | | Rust | `OperatorDisplay::stream_window` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `app_name` | `string` | none | Auto-pick this app's largest visible window, e.g. "Firefox". | | `space` | `string` | required | Space id or name. | | `title` | `string` | none | Label for the operator's window. Default: the window title. | | `window_id` | `string` | none | Exact window handle from `list_space_windows` (overrides `app_name`). | ### Result Text: `Streaming <window> of <space> to your desktop (bidirectional).` ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>), [`stream`](</docs/spaces/reference/errors#stream>) ### Notes - Operator-side: the pixels land on the human's machine, so the platform axis is the operator's OS and not the Space's. ## show_space_pip Pin the Space as picture-in-picture on the operator's desktop. FOR THE HUMAN, NOT FOR YOU. Pins an always-on-top picture-in-picture overlay of the Space onto the user's own desktop. Call it only when the user asks to see a Space; never to observe your own work. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | macos (the operator's machine) | | Metering | free | | Approval | permission `spaces:show_space_pip`; mutating, idempotent | | Requires (spacesd) | `desktop_stream` | | Requires (this machine) | `operator_display` | | Swift SDK | `space.operatorDisplay.pinPictureInPictureOnOperatorDesktop()` | | Rust | `OperatorDisplay::pin_pip` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result Text: `Pinned <space> as picture-in-picture on your desktop.` ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>) ## hide_space_pip Unpin the picture-in-picture window. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | macos (the operator's machine) | | Metering | free | | Approval | permission `spaces:hide_space_pip`; mutating, idempotent | | Requires (this machine) | `operator_display` | | Swift SDK | `space.operatorDisplay.unpinPictureInPictureFromOperatorDesktop()` | | Rust | `OperatorDisplay::unpin_pip` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result Text: `Unpinned <space> from your desktop.` ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>) ## open_space_viewer Open the full Space viewer on the operator's desktop. FOR THE HUMAN, NOT FOR YOU. Opens a live viewer window on the user's own desktop. Never call it to obtain a screenshot or to check on your own work; use the screenshot tool for that. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | macos (the operator's machine) | | Metering | free | | Approval | permission `spaces:open_space_viewer`; mutating, idempotent | | Requires (spacesd) | `desktop_stream` | | Requires (this machine) | `operator_display` | | Swift SDK | `space.operatorDisplay.openViewerOnOperatorDesktop()` | | Rust | `OperatorDisplay::open_viewer` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result Text: `Opened the <space> viewer on your desktop.` ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>) --- # Services Discover and call the MCP services inside a Space. | Tool | Description | | --- | --- | | [`list_tools`](#list_tools) | List the MCP services and tools reachable inside the Space. | | [`call_tool`](#call_tool) | Call a service tool inside the Space. | ## list_tools List the MCP services and tools reachable inside the Space. Lists the tools of an MCP service INSIDE the Space. `service` is any service the Space declares (an MCP server the image runs, reached over streamable HTTP; no spacesd needed) or `driver`, the spacesd's own cua-driver registry (screenshot, click, type, launch apps, accessibility, ...). The default is `driver`, or the only declared service of a Space without spacesd. With no `name`, returns a scannable overview; pass name=<substring> for full input schemas. Then invoke them with call_tool. The reply names every service the Space exposes. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:list_tools`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `space.services.list()` | | Rust | `Services::list_tools` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | none | Substring filter; matching tools come back with full input schemas. | | `service` | `string` | `driver` | Service. A service the Space declares (see `list_spaces`), reached over generic MCP (streamable HTTP), or `driver` (the spacesd's cua-driver registry; `mcp`, `cua-driver` and `computer-server` are aliases unless the Space declares a service by that name). Default: `driver`, or the only declared service of a Space without spacesd. | | `space` | `string` | required | Space id or name. | ### Result JSON: `service`, `count`, `tools` (`name` and `description`; with `name`, also `inputSchema`), `services`, `contract_version`, and a `hint` when no filter was given. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`mcp`](</docs/spaces/reference/errors#mcp>) ## call_tool Call a service tool inside the Space. Returns the tool's content parts unchanged, images included. A tool-level failure comes back with isError, not as a transport error. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:call_tool`; destructive | | Swift SDK | `space.services.call(_:on:_:)` | | Rust | `Services::call_tool` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `arguments` | `object` | `{}` | Arguments object for the tool. Default `{}`. | | `service` | `string` | `driver` | Service (see `list_tools`). Default: `driver`, or the only declared service of a Space without spacesd. | | `space` | `string` | required | Space id or name. | | `tool` | `string` | required | Tool name from `list_tools`. | ### Result The called tool's own result, unchanged: its content parts (images included), `structuredContent` and `isError`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`mcp`](</docs/spaces/reference/errors#mcp>) ### Notes - Open world twice over: the callee is a tool the SDK has never seen. --- # Agents Run coding agents inside a Space. | Tool | Description | | --- | --- | | [`agent_start`](#agent_start) | Start a coding agent in the Space. | | [`agent_message`](#agent_message) | Send a follow-up to an agent run. | | [`agent_events`](#agent_events) | Read an agent run's events after a cursor. | | [`agent_status`](#agent_status) | Read an agent run's status and result. | | [`agent_interrupt`](#agent_interrupt) | Interrupt an agent run's current turn. | | [`agent_stop`](#agent_stop) | Stop an agent run. | | [`agent_list`](#agent_list) | List agent runs in the Space. | | [`agent_capabilities`](#agent_capabilities) | Report what the agent harnesses support. | ## agent_start Start a coding agent in the Space. Installs the harness if needed (pinned, checksum-verified), gives it the Space's own MCP tools plus any mcp_servers, and runs it over the Agent Client Protocol as a detached spacesd process with auto-approved permissions (the Space is the sandbox). The run survives this server restarting or the connection dropping. Returns a run_id immediately: follow it with agent_events (cursor) or agent_status, continue it with agent_message, cancel a turn with agent_interrupt. Keys: env_from_host names provider key variables from this server's environment; host credential files are never copied. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_start`; destructive | | Swift SDK | `space.agents.start(...)` | | Rust | `Agents::start` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `"claude-code" \| "gemini-cli" \| "google-antigravity" \| "goose" \| "hermes" \| "openai-codex" \| "openclaw" \| "opencode" \| "pi"` | required | Harness id. `agent_capabilities` lists what each needs. | | `base_url` | `string` | none | A custom model endpoint base URL (a proxy or compatible server). | | `branch` | `string` | none | Branch or tag for `repo`. | | `cwd` | `string` | none | Working directory in the Space. Default: the run's own. | | `env` | `map of string` | `{}` | More environment for the agent, for example `{"HERMES_HOME": "/home/cua/bot"}`. Stored 0600 in the run and redacted from its output; name provider keys in `env_from_host` instead, so their values never pass through the conversation. | | `env_from_host` | `string[]` | `[]` | Provider key variables to forward from this server's environment, for example ["ANTHROPIC_API_KEY"]. Names only; values never pass through the conversation. | | `exit_when_idle` | `boolean` | `false` | Stop (resumably) once the prompt is answered. Default false. | | `home` | `string` | none | Run as this persistent agent: its home in the Cua Volume (`agents/<home>/`) is restored into the Space first and saved after every turn, the harness keeps its memory there, and the agent gets the `cua` bridge (notify_user, the drive). Creates the persistent agent (this harness, this Space) when it does not exist yet. Default: a one-off run whose state lives only in the Space. | | `mcp_servers` | `object[]` | `[]` | Extra MCP servers. The Space's own tools are always included. | | `mcp_servers[].args` | `string[]` | `[]` | Its arguments. | | `mcp_servers[].command` | `string` | `null` | Command run inside the Space. | | `mcp_servers[].name` | `string` | required | Name the agent sees. | | `mcp_servers[].url` | `string` | `null` | `http(s)://` URL. | | `model` | `string` | none | Model id. | | `prompt` | `string` | required | The task. | | `repo` | `string` | none | Git URL cloned into the working directory first. | | `show` | `boolean` | `false` | Open a terminal on the Space's desktop following the run. Default false. | | `space` | `string` | required | Space id or name. | ### Result JSON: `run_id`, `agent`, `space`, `process_tag`, `cwd`, `notes` and `hint`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`agent`](</docs/spaces/reference/errors#agent>) ### Notes - Runs auto-approved: the Space is the sandbox, which is why approvalsAreEnforced is false. ## agent_message Send a follow-up to an agent run. Continues the same session. While a turn runs the message is queued (or, with force=true, the turn is interrupted first); a run whose runner exited is restarted and resumes its session. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_message`; destructive | | Swift SDK | `AgentRun.send(_:)` | | Rust | `Agents::send` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `force` | `boolean` | `false` | Interrupt a running turn first instead of queueing. Default false. | | `run_id` | `string` | required | `run_id` from `agent_start`. | | `space` | `string` | required | Space id or name. | | `text` | `string` | required | The message. | ### Result JSON: `delivered`, `run_id`, `status`, `phase` and `reason`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`agent`](</docs/spaces/reference/errors#agent>) ## agent_events Read an agent run's events after a cursor. Normalized events (install, session, turn_started, message, thought, tool_call, tool_update, plan, usage, permission, turn_ended, error, exited, ...) after `cursor`. Pass the returned cursor back to continue; caught_up=true means nothing more is written yet. The same kinds for every harness. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_events`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `AgentRun.events(cursor:)` | | Rust | `Agents::events` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `cursor` | `integer` | `0` | `cursor` from the previous call. Default 0 (the start). At least 0. | | `max` | `integer` | `100` | Most events to return. Default 100. At least 0. | | `raw` | `boolean` | `false` | Include each event's raw ACP payload. Default false. | | `run_id` | `string` | required | `run_id` from `agent_start`. | | `space` | `string` | required | Space id or name. | ### Result JSON: `run_id`, `status`, `phase`, `events` (each `seq`, `ts_ms`, `turn`, `kind`, and `text`, `tool_id`, `tool_title`, `tool_kind`, `tool_status`, `stop_reason` when present; `category`: `message`, `user`, `activity` or `hidden`, and a one-line `summary` for activity; `raw` on request), `cursor` and `caught_up`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`agent`](</docs/spaces/reference/errors#agent>) ## agent_status Read an agent run's status and result. status is running, idle, failed, crashed or unknown ('unknown' is never a guess). Includes the last turn's result (final text, stop reason, usage) and a rendered output tail. Read from files in the Space; never disturbs the run. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_status`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `AgentRun.status()` | | Rust | `Agents::status` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `run_id` | `string` | required | `run_id` from `agent_start`. | | `space` | `string` | required | Space id or name. | | `tail` | `integer` | `40` | Rendered output lines to return. Default 40. At least 0. | ### Result JSON: `run_id`, `agent`, `status`, `phase`, `reason`, `turn`, `alive`, `accepts_message`, `summary`, `created_at`, `result` (`text`, `stop_reason`, `usage`, `error`, `tool_calls`), `output_tail` and `space`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`agent`](</docs/spaces/reference/errors#agent>) ## agent_interrupt Interrupt an agent run's current turn. Cancels the turn in flight (ACP session/cancel); the session stays open for agent_message. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_interrupt`; destructive | | Swift SDK | `AgentRun.interrupt()` | | Rust | `Agents::interrupt` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `run_id` | `string` | required | `run_id` from `agent_start`. | | `space` | `string` | required | Space id or name. | ### Result JSON: `run_id`, `interrupted` and `status`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`agent`](</docs/spaces/reference/errors#agent>) ## agent_stop Stop an agent run. Ends the run, then verifies its process is gone; reports stopped=false rather than claiming success. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_stop`; destructive | | Swift SDK | `AgentRun.stop()` | | Rust | `Agents::stop` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `run_id` | `string` | required | `run_id` from `agent_start`. | | `space` | `string` | required | Space id or name. | ### Result JSON: `run_id`, `stopped`, `alive` and `reason`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`agent`](</docs/spaces/reference/errors#agent>) ## agent_list List agent runs in the Space. Every run in the Space, newest first, including runs another client started. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_list`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `space.agents.list()` | | Rust | `Agents::list` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result JSON: `{"space", "runs"}`, each run with `run_id`, `agent`, `status`, `phase`, `reason`, `turn`, `alive`, `accepts_message`, `summary`, `created_at` and `label`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`agent`](</docs/spaces/reference/errors#agent>) ## agent_capabilities Report what the agent harnesses support. Every harness: readiness, what it installs, which key variables it reads, which endpoint wire formats it takes, and its known limits; plus the event kinds and statuses. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_capabilities`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `space.agents.harnessCapabilities()` | | Rust | `agents::capabilities` | ### Parameters No parameters. ### Result JSON: `protocol`, `statuses`, `event_kinds`, `harnesses` (each `id`, `name`, `ready`, `installs`, `keys`, `endpoint_wires`, `sandbox_mcp`, `interactive_cli`, `notes`), `forwardable_env` and `runner`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) --- # Persistent agents Named agents whose memory outlives their Space: homes in Cua Volume, routines, notifications, pause and resume, access to the user's computers. | Tool | Description | | --- | --- | | [`persistent_agent_create`](#persistent_agent_create) | Create a named agent whose memory outlives its Space. | | [`persistent_agent_list`](#persistent_agent_list) | List persistent agents. | | [`persistent_agent_remove`](#persistent_agent_remove) | Forget a persistent agent. | | [`persistent_agent_send`](#persistent_agent_send) | Give a persistent agent a turn. | | [`persistent_agent_save`](#persistent_agent_save) | Save a persistent agent's home now. | | [`agent_pause`](#agent_pause) | Pause a persistent agent: its run, its routines and its Space. | | [`agent_resume`](#agent_resume) | Resume a paused persistent agent. | | [`routine_add`](#routine_add) | Schedule a recurring turn for a persistent agent. | | [`routine_list`](#routine_list) | List routines. | | [`routine_remove`](#routine_remove) | Delete a routine. | | [`routine_set_enabled`](#routine_set_enabled) | Turn a routine on or off. | | [`notify_user`](#notify_user) | Tell the user something in the Cua app. | | [`notifications_list`](#notifications_list) | Read the notifications feed. | | [`notifications_ack`](#notifications_ack) | Mark notifications read. | | [`computer_access_grant`](#computer_access_grant) | Let a persistent agent use one of the user's computers. | | [`computer_access_revoke`](#computer_access_revoke) | Take back a persistent agent's access to a computer. | | [`computer_access_list`](#computer_access_list) | List per-agent computer access. | ## persistent_agent_create Create a named agent whose memory outlives its Space. A persistent agent is a harness with a name, a Space and a home in the Cua Volume (agents/<name>/). Its harness keeps its memory in the home (Claude Code auto memory, Codex memories, Hermes MEMORY.md, the OpenClaw workspace), which is restored into the Space before each run and saved after each turn, so the agent remembers across runs, Space releases and machines. One run writes a home at a time. Start it with persistent_agent_send (or agent_start with home=<name>); the daemon notifies the user when a turn ends. Inside the Space the agent gets the `cua` bridge: notify_user, the drive, and the user's computers it was granted. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:persistent_agent_create`; destructive | | Swift SDK | `spaces.persistentAgents.create(...)` | | Rust | `Persistent::create` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `"claude-code" \| "gemini-cli" \| "google-antigravity" \| "goose" \| "hermes" \| "openai-codex" \| "openclaw" \| "opencode" \| "pi"` | required | Harness id. `agent_capabilities` lists what each needs. | | `base_url` | `string` | none | A custom model endpoint base URL. | | `env` | `map of string` | `{}` | More environment for every run (not secrets; see `env_from_host`). | | `env_from_host` | `string[]` | `[]` | Provider key variables forwarded from this server's environment at every start, for example ["ANTHROPIC_API_KEY"]. Names only. | | `model` | `string` | none | Model id. | | `name` | `string` | required | The agent's name: 1-63 of a-z, 0-9, `.`, `_`, `-`. Its home is `agents/<name>/` in the Cua Volume. | | `space` | `string` | required | The Space it works in (id or name). | ### Result JSON: the agent record: `name`, `harness`, `space`, `model`, `base_url`, `env_from_host`, `paused`, `space_state` and `created_ms`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`agent`](</docs/spaces/reference/errors#agent>) ## persistent_agent_list List persistent agents. Every persistent agent of this machine's cua home, with its Space, its current run and when its home was last saved. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:persistent_agent_list`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.persistentAgents.list()` | | Rust | `Persistent::list` | ### Parameters No parameters. ### Result JSON: `{"agents"}`, each with `name`, `harness`, `space`, `paused`, `space_state`, `run_id`, `saved_ms` and `last_error`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## persistent_agent_remove Forget a persistent agent. Stops its run, removes its routines and forgets it. Its home (agents/<name>/) stays in the Cua Volume; delete it there. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:persistent_agent_remove`; destructive | | Swift SDK | `spaces.persistentAgents.remove(_:)` | | Rust | `Persistent::remove` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | The persistent agent's name. | ### Result JSON: `removed` (the record). The home stays in the Cua Volume. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`agent`](</docs/spaces/reference/errors#agent>) ## persistent_agent_send Give a persistent agent a turn. A follow-up to the agent's idle run, or a new run with its home restored when it has none. Refused while a turn runs (never interrupts) and while paused. Follow the run with agent_events on the agent's Space. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:persistent_agent_send`; destructive | | Swift SDK | `spaces.persistentAgents.send(_:_:)` | | Rust | `Persistent::send` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | The persistent agent's name. | | `text` | `string` | required | The message (the prompt of a new run, or a follow-up). | ### Result JSON: `run_id`, `started` (a new run, with the home restored) and `restored` (`files`, `bytes`, `millis`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`agent`](</docs/spaces/reference/errors#agent>), [`lease_held`](</docs/spaces/reference/errors#lease_held>) ## persistent_agent_save Save a persistent agent's home now. Saves the agent's home from its Space into the Cua Volume now (the daemon also saves after every turn): only files whose SHA-256 changed move. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:persistent_agent_save`; mutating, idempotent | | Swift SDK | `spaces.persistentAgents.save(_:)` | | Rust | `Persistent::save` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | The persistent agent's name. | ### Result JSON: `files`, `bytes`, `unchanged`, `removed`, `blocked` (files the secret scanner kept out) and `millis`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`agent`](</docs/spaces/reference/errors#agent>), [`secret_detected`](</docs/spaces/reference/errors#secret_detected>) ## agent_pause Pause a persistent agent: its run, its routines and its Space. One call: stops the run, saves the home, releases the home's lease, keeps the agent's routines from firing, and suspends its Space when it is local. A cloud Space cannot be suspended (Fleet has no per-claim pause), so pausing releases it after the home is saved; agent_resume creates it again from the same image and restores the home. Apps open in a released cloud Space do not come back. The user's own machines are never suspended. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:agent_pause`; mutating, idempotent | | Swift SDK | `spaces.persistentAgents.pause(_:)` | | Rust | `Persistent::pause` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | The persistent agent's name. | ### Result JSON: `stopped_run`, `saved` (the home save), `space_state` (`suspended`, `released` or `running`) and `millis`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`agent`](</docs/spaces/reference/errors#agent>), [`sandbox`](</docs/spaces/reference/errors#sandbox>) ## agent_resume Resume a paused persistent agent. Resumes a suspended local Space, or creates a released cloud Space again from the same image, restores the home and lets routines fire again. With `prompt`, starts a run on it. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | cloud | | Approval | permission `spaces:agent_resume`; destructive | | Swift SDK | `spaces.persistentAgents.resume(_:prompt:)` | | Rust | `Persistent::resume` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | The persistent agent's name. | | `prompt` | `string` | none | Start a run on this prompt once the agent is back. Default: none (the next message or routine starts one). | ### Result JSON: `space` (it can change when a cloud Space is created again), `recreated`, `restored`, `run_id` and `ready_ms` (from the call to the home being back in a ready Space). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`agent`](</docs/spaces/reference/errors#agent>), [`sandbox`](</docs/spaces/reference/errors#sandbox>), [`fleet`](</docs/spaces/reference/errors#fleet>) ## routine_add Schedule a recurring turn for a persistent agent. Fired by `cua daemon` whether or not an app is open, as a turn of the agent (`[routine] <title>: <prompt>`). A routine never interrupts a turn: a busy or paused agent skips that slot. A daemon that was asleep fires a missed routine once on waking. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:routine_add`; destructive | | Swift SDK | `spaces.persistentAgents.addRoutine(...)` | | Rust | `Persistent::routine_add` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `string` | required | The persistent agent that runs it. | | `daily_at` | `string` | none | Every day at `HH:MM` (local time). | | `enabled` | `boolean` | `true` | Default true. | | `every_minutes` | `integer` | none | Every N minutes. | | `prompt` | `string` | required | What the agent is asked each time. | | `title` | `string` | required | A short title (shown in the app and prefixed to the turn). | | `weekly_on` | `string` | none | Every week at `<weekday> HH:MM` (local time), for example `mon 09:00`. | ### Result JSON: the routine: `id`, `botID` (the agent), `title`, `prompt`, `schedule`, `isEnabled` and `createdAt`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## routine_list List routines. Routines with their next firing and what the last one did. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:routine_list`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.persistentAgents.routines(agent:)` | | Rust | `Persistent::routines` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `string` | none | Only this persistent agent's routines. Default: all. | ### Result JSON: `{"routines"}`, each with `id`, `botID`, `title`, `prompt`, `schedule`, `label`, `isEnabled`, `lastFiredAt`, `lastRunID`, `lastOutcome` and `next_fire`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## routine_remove Delete a routine. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:routine_remove`; mutating, idempotent | | Swift SDK | `spaces.persistentAgents.removeRoutine(_:)` | | Rust | `Persistent::routine_remove` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `id` | `string` | required | The routine's id. | ### Result JSON: `removed` (the id). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## routine_set_enabled Turn a routine on or off. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:routine_set_enabled`; mutating, idempotent | | Swift SDK | `spaces.persistentAgents.setRoutineEnabled(_:_:)` | | Rust | `Persistent::routine_set_enabled` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `enabled` | `boolean` | required | On or off. | | `id` | `string` | required | The routine's id. | ### Result JSON: the routine. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## notify_user Tell the user something in the Cua app. Posts a notification the Cua app shows ("Your research is ready"). Agents inside a Space get the same tool on their `cua` bridge. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:notify_user`; destructive | | Swift SDK | `spaces.notifications.post(...)` | | Rust | `notify::Feed::post` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `string` | none | The persistent agent it is about, if any. | | `body` | `string` | none | The details. Default empty. | | `title` | `string` | required | One short line. | ### Result JSON: `notified` and `id`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## notifications_list Read the notifications feed. The feed the Cua app turns into system notifications: turn ends of persistent agents, notify_user calls, access requests and failures. It persists while no app is open. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:notifications_list`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.notifications.list(...)` | | Rust | `notify::Feed::list` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `since_ms` | `integer` | none | Only ones newer than this (Unix ms). At least 0. | | `unread_only` | `boolean` | `false` | Only unread ones. Default false. | ### Result JSON: `{"notifications"}`, newest first, each with `id`, `at_ms`, `agent`, `kind` (`turn_ended`, `message`, `approval`, `error`), `title`, `body`, `run_id`, `space` and `read`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## notifications_ack Mark notifications read. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:notifications_ack`; mutating, idempotent | | Swift SDK | `spaces.notifications.markRead(_:)` | | Rust | `notify::Feed::ack` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `ids` | `string[]` | `[]` | Ids to mark read. Default: every notification. | ### Result JSON: `marked` (how many changed). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## computer_access_grant Let a persistent agent use one of the user's computers. Lets one persistent agent use one of the user's machines (its computer-use tools, through the agent's bridge). Asks the user for presence (Touch ID or passphrase) first. Other agents of the account get nothing. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:computer_access_grant`; destructive | | Swift SDK | `spaces.persistentAgents.allowComputer(_:machine:)` | | Rust | `access::Access::grant` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `string` | required | The persistent agent. | | `expires_in_secs` | `integer` | none | End the grant after this many seconds. Default: until revoked. At least 0. | | `machine` | `string` | required | The machine (a Space id, usually `relay:<machine-id>` from `cua host setup`). | ### Result JSON: the grant: `id`, `agent`, `machine`, `created_ms`, `expires_ms`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_confirmed`](</docs/spaces/reference/errors#not_confirmed>) ## computer_access_revoke Take back a persistent agent's access to a computer. Takes the access back at once: the agent's next call is refused. No presence needed. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:computer_access_revoke`; mutating, idempotent | | Swift SDK | `spaces.persistentAgents.revokeComputer(_:machine:)` | | Rust | `access::Access::revoke` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `string` | required | The persistent agent. | | `machine` | `string` | none | The machine. Default: every machine. | ### Result JSON: `revoked` (how many grants). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## computer_access_list List per-agent computer access. Which agents may use which of the user's computers, and the audited record of grants, uses and refusals. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:computer_access_list`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.persistentAgents.computerAccess(agent:)` | | Rust | `access::Access::grants` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `string` | none | Only this persistent agent's grants. Default: all. | | `audit` | `integer` | `0` | Include the audit log's newest entries (this many). Default 0. At least 0. | ### Result JSON: `{"grants"}` (each `id`, `agent`, `machine`, `created_ms`, `expires_ms`, `revoked`) and, with `audit`, `audit` (newest first: `ts_ms`, `principal`, `action`, `path` (the machine), `detail`) plus `audit_verified`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) --- # Session teleport Move a signed-in app session from this machine into a Space. | Tool | Description | | --- | --- | | [`teleport_manifest`](#teleport_manifest) | Describe what a session teleport would move. | | [`teleport_app`](#teleport_app) | Move an app session into the Space. | ## teleport_manifest Describe what a session teleport would move. Read on this machine from the app's own provider: items, sizes, which are sensitive and which are checked by default. With `space`, also reports whether the Space can import the app. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:teleport_manifest`, in `spaces:readonly`; read-only, idempotent | | Requires (this machine) | `app_sessions` | | Swift SDK | `space.sessions.manifest(for:scope:)` | | Rust | `Teleport::manifest` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `app` | `string` | required | App id, e.g. `firefox`, `chrome`, `claude-code`. | | `scope` | `string` | `full` | `full` (the profile) or `tabs`. Default `full`. | | `space` | `string` | none | Space to check the receiving side of (`GetManifest`). Optional. | ### Result JSON: `app`, `display_name`, `scope`, `items` (each `relative_path`, `label`, `estimated_bytes`, `is_sensitive`, `is_checked_by_default`), `total_estimated_bytes`, `notes`; with `space`, a `receiver` (`supported`, `limitation`, `app_installed`, `supported_apps`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>) ### Notes - The manifest is the thing an Approval is minted from: the SDK will not send a teleport the caller has not seen a manifest for. ## teleport_app Move an app session into the Space. Moves the app's signed-in session into the Space, but only through the Cua Keyvault and only with the user's consent. This tool never delivers on its own: the first call files a Keyvault access request and returns a request_id; the user approves it in Cua (Touch ID or the login password) or an unattended rule matches; a later call with the same request_id has the Keyvault broker perform the delivery over the daemon's authenticated channel. Without `include` the manifest's default-checked items move, never everything. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:teleport_app`; destructive | | Requires (spacesd) | `teleport.*` | | Requires (this machine) | `app_sessions` | | Swift SDK | `space.sessions.send(_ approval:)` | | Rust | `Teleport::send` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `acknowledge_sensitive` | `boolean` | none | Must be true when the selection holds a sensitive item (credentials, cookies). The consent is explicit rather than defaulted. | | `app` | `string` | required | App id, e.g. `firefox`, `chrome`, `claude-code`. | | `include` | `string[]` | none | Manifest `rel_path`s to send. Default: the manifest's default-checked items, never everything. An empty list is refused as ambiguous. | | `request_id` | `string` | none | The Keyvault request id returned by a previous call. Present on a retry after the user approved the teleport in Cua; the broker then performs the delivery. Absent on the first call. | | `scope` | `string` | `full` | `full` or `tabs`. Default `full`. | | `space` | `string` | required | Space id or name. | ### Result JSON. First call: `consent_required: true`, a `request_id`, `would_send` and `approve`. Retry with `request_id` once the user approved: `moved: true` with `transferred_paths` (what the Space imported), `import_ids` and `expires_ms`; or still `consent_required` while pending; or an `error` with the Keyvault code (`requires_cua_app`, `denied`, `disabled`) when it cannot proceed. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>), [`teleport_refused`](</docs/spaces/reference/errors#teleport_refused>), [`transfer_failed`](</docs/spaces/reference/errors#transfer_failed>) ### Notes - Works on every provider. A comment in the SDK claiming teleport was cloud-only was stale and was a real bug: callers were being told to provision a metered Space for work a free Local Space does. - Consent is owned by the Keyvault broker, not this tool: the automation caller is treated as an unverified third party, so a delivery needs a live consent or an audited unattended rule and can never be self-approved (red-team E2). --- # Keyvault Use the user's saved credentials in a Space, only with their approval. | Tool | Description | | --- | --- | | [`request_site_login`](#request_site_login) | Sign in to a site in the Space with the user's saved password. | ## request_site_login Sign in to a site in the Space with the user's saved password. Signs in to a website in the Space's browser with a password the user saved in the Cua Keyvault (imported from their browser). The first call files a Keyvault request and returns a request_id; the user approves it in Cua (Touch ID or the login password), once per sign-in by default. Call again with the same space, url and request_id: the Keyvault checks the page is on the saved login's exact origin, types the username and password through the Space's cua-driver, submits the form and returns what it did, never the password. Pass the browser tab you opened (session, target_id, tab_id from get_browser_state on the same Space) to sign in there; without one the Keyvault opens its own browser in the Space on `url` and returns its target_id and tab_id. `agent` names the persistent agent asking, shown to the user. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:request_site_login`; destructive | | Requires (spacesd) | `driver` | | Swift SDK | `space.requestSiteLogin(url:)` | | Rust | `site_login::request_site_login` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `agent` | `string` | none | The persistent agent asking (shown to the user in the approval). | | `relay_plaintext_ack` | `boolean` | `false` | Sends the password over a relay connection that predates end-to-end sealing even though the relay could read it in transit. Default `false`: such a Space refuses the fill instead (S1). | | `request_id` | `string` | none | The request id a previous call returned. Present on the retry after the user approved; absent on the first call. | | `session` | `string` | none | cua-driver lifecycle session label of the browser tab to sign in. | | `space` | `string` | required | Space id or name. | | `tab_id` | `string` | none | cua-driver tab id (from `get_browser_state`). | | `target_id` | `string` | none | cua-driver browser target id (from `get_browser_state`). | | `url` | `string` | required | The site's sign-in page (http or https), for example `https://github.com/login`. | | `username` | `string` | none | Which saved username, when the site has several. | | `wait_secs` | `integer` | `20` | Seconds to wait for the user's decision on a retry. Default 20, at most 120. At least 0. | ### Result JSON. First call: `status: "pending"`, a `request_id`, `site` and `space`. Retry with `request_id`: `status: "filled"` with `site`, `origin`, `username_hint` (masked), `submitted`, `page_url` and the browser `target_id`/`tab_id` it signed in; `status: "pending"` while the user decides; `status: "denied"` when they declined. Never the password. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>), [`login_refused`](</docs/spaces/reference/errors#login_refused>) ### Notes - The password never reaches the caller: the Keyvault fills the form through the Space's cua-driver and returns only a masked username. - Approval is required by default for every sign-in (one use per approval); only an unattended rule the user wrote skips it. --- # Hotspot Share this machine's network with a Space. | Tool | Description | | --- | --- | | [`hotspot_start`](#hotspot_start) | Start sharing this machine's network with a Space. | | [`hotspot_stop`](#hotspot_stop) | Stop sharing this machine's network. | | [`hotspot_status`](#hotspot_status) | Report which Spaces are sharing this machine's network, with counters. | ## hotspot_start Start sharing this machine's network with a Space. Starts the Space's reverse-SOCKS hotspot (TunnelService.StartHotspot) and serves its egress from this machine, so the Space browses as this machine. Runs until hotspot_stop, delete_space, or this server exiting. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:hotspot_start`; destructive | | Requires (spacesd) | `hotspot` | | Swift SDK | `connection.hotspot.start(sharingWith:)` | | Rust | `Spaces::hotspot_start` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `bypass` | `string[]` | none | Destinations (CIDRs or host suffixes) that keep using the Space's own network. | | `set_system_proxy` | `boolean` | `true` | Point the Space's system proxy (and `HTTP(S)_PROXY` for new processes) at the hotspot. Default true. | | `space` | `string` | required | Space id or name. | ### Result JSON: the hotspot (`space`, `state`, `hotspot_id`, `socks_address`, `active_connections`, `bytes_out`, `bytes_in`, `served_here`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>) ### Notes - Capability-gated rather than cloud-only: any Space whose spacesd supports `hotspot` can borrow this machine's network. ## hotspot_stop Stop sharing this machine's network. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:hotspot_stop`; destructive | | Requires (spacesd) | `hotspot` | | Swift SDK | `connection.hotspot.stop()` | | Rust | `Spaces::hotspot_stop` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | none | Space id or name. Default: every Space with an active hotspot. | ### Result JSON: `{"stopped": [...]}`, the hotspots that were stopped. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>) ## hotspot_status Report which Spaces are sharing this machine's network, with counters. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:hotspot_status`, in `spaces:readonly`; read-only, idempotent | | Requires (spacesd) | `hotspot` | | Swift SDK | `connection.hotspot.status()` | | Rust | `Spaces::hotspot_statuses` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | none | Space id or name. Default: every Space with an active hotspot. | ### Result JSON: `{"hotspots": [...]}`, each shaped like hotspot_start's result. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`spacesd_not_available`](</docs/spaces/reference/errors#spacesd_not_available>), [`capability_missing`](</docs/spaces/reference/errors#capability_missing>) --- # Cua Volume Read and write the volume every Space and agent shares, decide who may reach what, and see where it is stored and how it syncs. | Tool | Description | | --- | --- | | [`volume_ls`](#volume_ls) | List a folder of the Cua Volume. | | [`volume_read`](#volume_read) | Read a file from the Cua Volume. | | [`volume_write`](#volume_write) | Write a file to the Cua Volume. | | [`volume_delete`](#volume_delete) | Delete a file from the Cua Volume. | | [`volume_history`](#volume_history) | List a Cua Volume file's versions. | | [`volume_restore`](#volume_restore) | Restore an old version of a Cua Volume file. | | [`volume_grant`](#volume_grant) | Grant an agent or Space more Cua Volume access. | | [`volume_revoke`](#volume_revoke) | Revoke a Cua Volume grant. | | [`volume_grants`](#volume_grants) | List Cua Volume grants. | | [`volume_request_access`](#volume_request_access) | Ask the user for more Cua Volume access. | | [`volume_requests`](#volume_requests) | List Cua Volume access requests waiting for the user. | | [`volume_approve`](#volume_approve) | Approve a Cua Volume access request. | | [`volume_deny`](#volume_deny) | Decline a Cua Volume access request. | | [`volume_audit`](#volume_audit) | Read the Cua Volume audit log. | | [`volume_storage`](#volume_storage) | Show where the Cua Volume keeps its bytes. | | [`volume_storage_set`](#volume_storage_set) | Test or change where the Cua Volume keeps its bytes. | | [`volume_mount_status`](#volume_mount_status) | Show whether the Cua Volume is mounted as a volume. | | [`volume_mount`](#volume_mount) | Mount the Cua Volume as a volume. | | [`volume_unmount`](#volume_unmount) | Unmount the Cua Volume volume. | | [`volume_sync_status`](#volume_sync_status) | Show the Cua Volume's sync state across devices. | | [`volume_sync_events`](#volume_sync_events) | Wait for Cua Volume sync events. | | [`volume_sync_resolve`](#volume_sync_resolve) | Mark a Cua Volume sync conflict as seen. | | [`volume_cache_stats`](#volume_cache_stats) | Show the Cua Volume block cache. | | [`volume_cache_set`](#volume_cache_set) | Set the Cua Volume block cache's size cap. | | [`volume_cache_clear`](#volume_cache_clear) | Clear the Cua Volume block cache. | ## volume_ls List a folder of the Cua Volume. Cua Volume is one versioned drive per account, shared by every Space and agent: public/ (shared reference, read by everyone), agents/<agent>/ (a persistent agent's home: memory, outputs, inbox) and spaces/<space>/ (per-Space scratch). As the user (the default) you see everything. With as_agent (and in_space) you see exactly what that agent sees: it reads public/, writes its own home and its Space's folder, and anything wider needs a grant the user approves. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_ls`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeLs(path:asAgent:inSpace:)` | | Rust | `cua_volume::Session::ls` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `as_agent` | `string` | none | See the drive as this persistent agent does (`ada`). Only narrows: the agent's defaults and grants apply. Default: the user (the whole drive). | | `in_space` | `string` | none | With `as_agent`: the Space id the agent is in, whose `spaces/<space>/` folder it may write. | | `path` | `string` | none | Folder, for example `agents/ada/` or `public/`. Default: the root. | ### Result JSON: `path`, `principal` and `entries` (each `path`, `name`, `folder`, `size`, `modified_ms`, `etag`, `mode`, and `sync` when a file has something to say: `state` `pending_upload`, `conflict` (with `conflict_path`) or `conflict_copy`, and `written_by` when another device wrote it last). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>), [`volume_backend`](</docs/spaces/reference/errors#volume_backend>) ## volume_read Read a file from the Cua Volume. Reads at most 8 MiB. Text comes back as utf8, anything else as base64. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_read`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeRead(path:version:asAgent:inSpace:)` | | Rust | `cua_volume::Session::read` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `as_agent` | `string` | none | See `volume_ls`. | | `in_space` | `string` | none | See `volume_ls`. | | `path` | `string` | required | File path, for example `agents/ada/memory/MEMORY.md`. | | `version` | `string` | none | A version id from `volume_history`. Default: the current version. | ### Result JSON: `path`, `size`, `etag`, `version`, `modified_ms`, `encoding` (`utf8` or `base64`), `content`, and `sync` while the drive's services run (`state` `synced`, `pending_upload`, `conflict` or `conflict_copy`; `written_by`, `conflict_path`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>), [`volume_backend`](</docs/spaces/reference/errors#volume_backend>) ## volume_write Write a file to the Cua Volume. Every write is a new version (history is kept). if_etag makes it a compare-and-swap, create_only makes it create-only. Writes under agents/ are scanned for secrets and refused by kind and line; keep credentials in the Keyvault. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_write`; destructive | | Swift SDK | `spaces.volumeWrite(path:content:encoding:ifEtag:createOnly:asAgent:inSpace:)` | | Rust | `cua_volume::Session::write` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `as_agent` | `string` | none | See `volume_ls`. | | `content` | `string` | required | The content, as text or base64 (see `encoding`). At most 8 MiB. | | `create_only` | `boolean` | `false` | Write only if nothing is at `path` yet. Default false. | | `encoding` | `string` | `utf8` | `utf8` or `base64`. Default `utf8`. | | `if_etag` | `string` | none | Write only if the current version has this etag (from `volume_read` or `volume_ls`): a compare-and-swap. | | `in_space` | `string` | none | See `volume_ls`. | | `path` | `string` | required | File path. | ### Result JSON: the new version's `key`, `size`, `etag`, `version` and `modified_ms`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>), [`precondition_failed`](</docs/spaces/reference/errors#precondition_failed>), [`secret_detected`](</docs/spaces/reference/errors#secret_detected>), [`volume_backend`](</docs/spaces/reference/errors#volume_backend>) ## volume_delete Delete a file from the Cua Volume. Adds a delete marker; volume_history and volume_restore still reach the old versions. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_delete`; destructive | | Swift SDK | `spaces.volumeDelete(path:ifEtag:asAgent:inSpace:)` | | Rust | `cua_volume::Session::delete` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `as_agent` | `string` | none | See `volume_ls`. | | `if_etag` | `string` | none | Delete only if the current version has this etag. | | `in_space` | `string` | none | See `volume_ls`. | | `path` | `string` | required | File path. | ### Result JSON: `{"deleted": <path>}`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>), [`precondition_failed`](</docs/spaces/reference/errors#precondition_failed>), [`volume_backend`](</docs/spaces/reference/errors#volume_backend>) ## volume_history List a Cua Volume file's versions. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_history`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeHistory(path:asAgent:inSpace:)` | | Rust | `cua_volume::Session::history` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `as_agent` | `string` | none | See `volume_ls`. | | `in_space` | `string` | none | See `volume_ls`. | | `path` | `string` | required | File path. | ### Result JSON: `path` and `versions` (each `version`, `size`, `modified_ms`, `deleted`, `latest`), newest first. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>), [`volume_backend`](</docs/spaces/reference/errors#volume_backend>) ## volume_restore Restore an old version of a Cua Volume file. Writes the old version's content as a new version, so the restore itself can be undone. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_restore`; destructive | | Swift SDK | `spaces.volumeRestore(path:version:asAgent:inSpace:)` | | Rust | `cua_volume::Session::restore` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `as_agent` | `string` | none | See `volume_ls`. | | `in_space` | `string` | none | See `volume_ls`. | | `path` | `string` | required | File path. | | `version` | `string` | required | The version id to restore (from `volume_history`). | ### Result JSON: the new current version's `key`, `size`, `etag`, `version` and `modified_ms`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>), [`secret_detected`](</docs/spaces/reference/errors#secret_detected>), [`volume_backend`](</docs/spaces/reference/errors#volume_backend>) ## volume_grant Grant an agent or Space more Cua Volume access. Only for the user: widens an agent's or a Space's access past the defaults (an agent reads public/ and writes its own home and its Space's folder). The user confirms with presence (Touch ID, the login password or the vault passphrase) before anything widens; a grant that widens nothing is refused. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_grant`; destructive | | Swift SDK | `spaces.volumeGrant(principal:prefix:mode:expiresInSecs:note:)` | | Rust | `cua_volume::Drive::grant` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `expires_in_secs` | `integer` | none | Lifetime in seconds. Default: until revoked. At least 0. | | `mode` | `string` | required | `r` (read) or `rw` (read and write). | | `note` | `string` | none | A note shown next to the grant. | | `prefix` | `string` | required | A folder (`agents/writer/outputs/`) or one file. | | `principal` | `string` | required | `agent:<name>` or `space:<id>`. | ### Result JSON: the grant (`id`, `principal`, `prefix`, `mode`, `created_ms`, `expires_ms`, `revoked`, `note`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_confirmed`](</docs/spaces/reference/errors#not_confirmed>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_revoke Revoke a Cua Volume grant. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_revoke`; mutating, idempotent | | Swift SDK | `spaces.volumeRevoke(grantId:)` | | Rust | `cua_volume::Drive::revoke` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `grant_id` | `string` | required | The grant id (from `volume_grants`). | ### Result JSON: the revoked grant. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_grants List Cua Volume grants. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_grants`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeGrants(all:)` | | Rust | `cua_volume::Drive::grants` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `all` | `boolean` | `false` | Include expired and revoked grants. Default false. | ### Result JSON: `{"grants": [...]}`, live grants only unless `all`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_request_access Ask the user for more Cua Volume access. Files a request the user sees in Cua; it grants nothing by itself. Poll volume_ls or volume_read to see when access arrives. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_request_access`; destructive | | Swift SDK | `spaces.volumeRequestAccess(prefix:mode:reason:asAgent:inSpace:)` | | Rust | `cua_volume::Drive::request_access` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `as_agent` | `string` | none | The agent asking. | | `in_space` | `string` | none | See `volume_ls`. | | `mode` | `string` | required | `r` or `rw`. | | `prefix` | `string` | required | The folder or file wanted. | | `reason` | `string` | none | Why (shown to the user as the agent's words). | ### Result JSON: the request (`id`, `principal`, `prefix`, `mode`, `reason`, `created_ms`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_requests List Cua Volume access requests waiting for the user. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_requests`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeRequests()` | | Rust | `cua_volume::Drive::requests` | ### Parameters No parameters. ### Result JSON: `{"requests": [...]}`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_approve Approve a Cua Volume access request. Only for the user, with presence. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_approve`; destructive | | Swift SDK | `spaces.volumeApprove(requestId:expiresInSecs:)` | | Rust | `cua_volume::Drive::approve` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `expires_in_secs` | `integer` | none | Lifetime in seconds. Default: until revoked. At least 0. | | `request_id` | `string` | required | The request id (from `volume_requests`). | ### Result JSON: the grant the request became. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_confirmed`](</docs/spaces/reference/errors#not_confirmed>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_deny Decline a Cua Volume access request. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_deny`; destructive | | Swift SDK | `spaces.volumeDeny(requestId:)` | | Rust | `cua_volume::Drive::deny` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `request_id` | `string` | required | The request id. | ### Result JSON: `{"denied": <request id>}`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_audit Read the Cua Volume audit log. Every grant, revocation, request, refusal, blocked secret, sync, lease and agent access, from a hash-chained log; an edited or truncated log reports verified=false. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_audit`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeAudit(limit:)` | | Rust | `cua_volume::audit::AuditLog::tail` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `limit` | `integer` | `50` | How many. Default 50, at most 1000. At least 0. | ### Result JSON: `events` (each `seq`, `ts_ms`, `principal`, `action`, `path`, `detail`), newest first, and `verified` (with `error` when the hash chain does not verify). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_storage Show where the Cua Volume keeps its bytes. Only for the user. Keys are never returned; `has_keys` says whether the credential store holds them. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_storage`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeStorage()` | | Rust | `cua_volume::service::DriveService::storage` | ### Parameters No parameters. ### Result JSON: `backend` (`fs` or `s3`), `fs_path`, `s3` (`endpoint`, `region`, `bucket`, `root`, `path_style`), `has_keys`, `cloud_available`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_storage_set Test or change where the Cua Volume keeps its bytes. Only for the user. Tests the storage first (bucket versioning, list, then write, read back and delete a probe object; retries path-style addressing to tell when a self-hosted store needs it); with dry_run nothing else happens. Otherwise, when the test passes, saves the setting and the keys (credential store) and switches the running drive to it without a restart. The bucket must have versioning on. cloud is refused. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_storage_set`; mutating, idempotent | | Swift SDK | `spaces.volumeStorageSet(update:)` | | Rust | `cua_volume::service::DriveService::set_storage` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `access_key_id` | `string` | none | The access key id (with the secret; both or neither). Saved in the credential store, never in a file. | | `backend` | `string` | required | `fs` (this machine) or `s3` (an S3-compatible bucket: AWS S3, R2, MinIO). `cloud` is refused. | | `dry_run` | `boolean` | `false` | Only test the connection; change nothing. Default false. | | `s3` | `object` | none | The bucket, for `s3`. | | `s3.bucket` | `string` | required | | | `s3.endpoint` | `string` | `null` | `http://127.0.0.1:9000`, `https://<account>.r2.cloudflarestorage.com`; absent for AWS. | | `s3.path_style` | `boolean` | `null` | Path-style addressing (MinIO and most self-hosted stores). | | `s3.region` | `string` | `null` | `us-east-1`; `auto` for R2. Default `us-east-1`. | | `s3.root` | `string` | `null` | A key prefix inside the bucket. Default none. | | `secret_access_key` | `string` | none | The secret access key. | ### Result JSON: `ok`, `reachable`, `authorized`, `versioning`, `detail` (a sentence when not ok saying how to fix it), `problem` (`unreachable`, `bad_keys`, `forbidden`, `bucket_missing`, `versioning_off`, `path_style_needed`) and `applied` (saved and switched live). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_mount_status Show whether the Cua Volume is mounted as a volume. Only for the user. The drive can appear as a volume (Finder on macOS, a FUSE mount on Linux); it is off until the user turns it on with volume_mount. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_mount_status`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeMountStatus()` | | Rust | `cua_volume::service::DriveService::mount_status` | ### Parameters No parameters. ### Result JSON: `enabled`, `state` (`off`, `mounting`, `mounted`, `needs_approval`, `unsupported`, `error`), `method` (`nfs`, `fuse`, `fskit`, `none`), `path`, `volume_name`, `detail`, `settings_url`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_mount Mount the Cua Volume as a volume. Only for the user. Turns the mount on (kept across restarts) and mounts. Idempotent. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_mount`; mutating, idempotent | | Swift SDK | `spaces.volumeMount()` | | Rust | `cua_volume::service::DriveService::mount` | ### Parameters No parameters. ### Result JSON: the mount status after the attempt (see volume_mount_status). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_unmount Unmount the Cua Volume volume. Only for the user. Turns the mount off; files still uploading land first. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_unmount`; mutating, idempotent | | Swift SDK | `spaces.volumeUnmount()` | | Rust | `cua_volume::service::DriveService::unmount` | ### Parameters No parameters. ### Result JSON: the mount status after unmounting. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>), [`volume_backend`](</docs/spaces/reference/errors#volume_backend>) ## volume_sync_status Show the Cua Volume's sync state across devices. Devices sharing one bucket learn each other's changes through a change log in the bucket itself. A write that lost to a later one is never dropped: it stays in the file's history and as the visible conflict copy. A persistent agent may call it too (through its bridge): it sees the pending uploads and conflicts of files it can read and its own Space's volume. In a Space it reports this machine's view. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_sync_status`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeSyncStatus()` | | Rust | `cua_volume::service::DriveService::sync_status` | ### Parameters No parameters. ### Result JSON: `device_id`, `device_name`, `feed` (`live`, `off` for a store on this machine, `offline` when the bucket stops answering, with `last_error`), `poll_interval_ms`, `last_poll_ms`, `last_remote_change_ms`, `pending_uploads`, `pending_bytes`, `conflicts` (each `path`, `conflict_path`, `winner_device`, `loser_device`, `winner_version`, `loser_version`, `ts_ms`), `devices` (each `id`, `name`, `this_device`, `last_seen_ms`, `last_change_ms`, `changes`), `last_error`, `pending` (each `path`, `bytes`: files still uploading), `backend` (`fs` or `s3`), `mount` (this machine's mount: `off`, `mounting`, `mounted`, `needs_approval`, `unsupported`, `error`), `cache` (a bucket's block cache, when there is one) and `volumes` (the volume mounted in Spaces: each `space`, `mount_path`, `backend`, `principal`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_sync_events Wait for Cua Volume sync events. Only for the user. A long poll: answers at once when events newer than since_seq exist, else waits up to wait_ms for one. Kinds: remote_change, remote_delete, upload_started, upload_done, upload_failed, conflict, error. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_sync_events`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeSyncEvents(sinceSeq:waitMs:)` | | Rust | `cua_volume::service::DriveService::sync_events` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `since_seq` | `integer` | none | Return events after this `seq` (default 0: all kept). At least 0. | | `wait_ms` | `integer` | none | Wait up to this long for one when none is newer (default 0, at most 30000). At least 0. | ### Result JSON: `events` (each `seq`, `ts_ms`, `kind`, `path`, `device`, `size`, `version`, `detail`) and `next_seq`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_sync_resolve Mark a Cua Volume sync conflict as seen. Only for the user. Clears the conflict from the list; both files stay. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_sync_resolve`; mutating, idempotent | | Swift SDK | `spaces.volumeSyncResolve(path:)` | | Rust | `cua_volume::service::DriveService::sync_resolve` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `path` | `string` | required | The file (or its conflict copy). | ### Result JSON: `{"resolved": <path>}`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>), [`not_found`](</docs/spaces/reference/errors#not_found>) ## volume_cache_stats Show the Cua Volume block cache. Only for the user. Reads of a remote store go through a local block cache; counters are since the runtime started. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_cache_stats`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.volumeCacheStats()` | | Rust | `cua_volume::service::DriveService::cache_stats` | ### Parameters No parameters. ### Result JSON: `dir`, `size_bytes`, `capacity_bytes`, `block_bytes`, `blocks`, `hits`, `misses`, `hit_rate`, `prefetched_bytes`, `evictions`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_cache_set Set the Cua Volume block cache's size cap. Only for the user. Evicts down to the new cap at once; kept across restarts. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_cache_set`; mutating, idempotent | | Swift SDK | `spaces.volumeCacheSet(capacityBytes:)` | | Rust | `cua_volume::service::DriveService::cache_set` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `capacity_bytes` | `integer` | required | Bytes (at least 268435456, 256 MiB). At least 0. | ### Result JSON: the cache stats after the change. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) ## volume_cache_clear Clear the Cua Volume block cache. Only for the user. Drops every cached block; files stay in the drive. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:volume_cache_clear`; mutating, idempotent | | Swift SDK | `spaces.volumeCacheClear()` | | Rust | `cua_volume::service::DriveService::cache_clear` | ### Parameters No parameters. ### Result JSON: the cache stats after clearing. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`forbidden`](</docs/spaces/reference/errors#forbidden>) --- # Sharing Let other cua.ai accounts watch or edit a Space, through the relay. | Tool | Description | | --- | --- | | [`share_space`](#share_space) | Let another cua.ai account watch or edit a Space. | | [`unshare_space`](#unshare_space) | Stop sharing a Space with an account, or with everyone. | | [`space_shares`](#space_shares) | List who a Space is shared with. | | [`relay_register_space`](#relay_register_space) | Publish a Space on the relay for the account's other devices. | | [`relay_unregister_space`](#relay_unregister_space) | Take a Space off the relay. | ## share_space Let another cua.ai account watch or edit a Space. Shares the Space through the cua.ai relay with one account, by verified email or account id; the same allowlist and device enrollment `cua host share` uses. A viewer joins presence with their own named cursor and watches a view-only stream: the Space's driver refuses their input, shell, files and MCP. An editor gets full use, and their input is a human session. Sharing again changes the role. A host (`relay:<id>`) is shared directly; any other Space (local, cloud, or added by address) is attached to the relay on its first share: its own driver dials out, so it needs no inbound port. Invitees open `invitee_space`. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:share_space`; mutating, idempotent | | Requires (this machine) | `relay_account` | | Swift SDK | `space.sharing.share(with:role:)` | | Rust | `Spaces::share_space` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `role` | `string` | `viewer` | `viewer` (presence with their own cursor and a view-only stream) or `editor` (full use; their input is a human session). Default `viewer`. | | `space` | `string` | required | Space id or name. | | `who` | `string` | required | Who: an email address (verified by cua.ai) or an account id. Teams are not accepted: share with each member. | ### Result JSON: `space`, `machine` (the relay machine it is shared as; empty when not shared), `invitee_space` (what the people it is shared with open: `relay:<machine>`), `url`, `online` and `shares` (each `who`, `role`: viewer or editor, and `connected`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>), [`relay`](</docs/spaces/reference/errors#relay>), [`permission_denied`](</docs/spaces/reference/errors#permission_denied>), [`unauthenticated`](</docs/spaces/reference/errors#unauthenticated>) ### Notes - Viewers and editors are enforced by the Space's own cua-spacesd, not by the caller; a Space whose driver predates relay_attach reports capability_missing. ## unshare_space Stop sharing a Space with an account, or with everyone. Removes one account (or everyone) at once: the relay cuts their open streams and refuses new calls. With no `who`, the Space stops being shared and a Space that is not a host leaves the relay. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:unshare_space`; mutating, idempotent | | Requires (this machine) | `relay_account` | | Swift SDK | `space.sharing.unshare(_:)` | | Rust | `Spaces::unshare_space` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | | `who` | `string` | none | The email or account id to remove. Default: everyone (the Space stops being shared; a Space that is not a host leaves the relay). | ### Result JSON: `space`, `machine` (the relay machine it is shared as; empty when not shared), `invitee_space` (what the people it is shared with open: `relay:<machine>`), `url`, `online` and `shares` (each `who`, `role`: viewer or editor, and `connected`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>), [`relay`](</docs/spaces/reference/errors#relay>), [`permission_denied`](</docs/spaces/reference/errors#permission_denied>), [`unauthenticated`](</docs/spaces/reference/errors#unauthenticated>) ## space_shares List who a Space is shared with. Who the Space is shared with, in which role, and who of them is connected through the relay now. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:space_shares`, in `spaces:readonly`; read-only, idempotent | | Requires (this machine) | `relay_account` | | Swift SDK | `space.sharing.shares()` | | Rust | `Spaces::space_shares` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `audit` | `integer` | `0` | Also return this many of the newest owner-side audit lines. Default 0. At least 0. | | `space` | `string` | required | Space id or name. | ### Result JSON: the shares (see share_space) and, with `audit`, `audit`: the newest owner-side lines (`ts_ms`, `action`: share, unshare, attach or detach, `space`, `detail`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>), [`relay`](</docs/spaces/reference/errors#relay>), [`unauthenticated`](</docs/spaces/reference/errors#unauthenticated>) ## relay_register_space Publish a Space on the relay for the account's other devices. Publishes the Space as a machine of the signed-in account on the cua.ai relay, the way `cua host setup` publishes this computer: the Space's own driver joins the relay over outbound WSS with a machine token the account registered, so no inbound port is opened. The account's other devices (a phone off this network, the Cua app on another computer) then open `relay_space`; nobody else can until share_space adds them. Idempotent, and it re-attaches a Space that restarted. Needs a driver with relay_attach (a host is already registered). | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:relay_register_space`; mutating, idempotent | | Requires (this machine) | `relay_account` | | Swift SDK | `spaces.relay.register(_:)` | | Rust | `Spaces::relay_register` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result JSON: `space`, `relay_space` (`relay:<machine>`: how the account's other devices reach it), `machine`, `url` and `online`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>), [`relay`](</docs/spaces/reference/errors#relay>), [`unauthenticated`](</docs/spaces/reference/errors#unauthenticated>) ## relay_unregister_space Take a Space off the relay. Takes the Space off the relay: its driver leaves and its machine is removed, with every share. A `relay:<machine>` id removes that machine's record from the relay directory, which only its owner may do: a Space that registered itself (also after it was deleted), or a machine that is no longer connected. A host that is connected now is taken off with `cua host remove` on that machine instead. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:relay_unregister_space`; mutating, idempotent | | Requires (this machine) | `relay_account` | | Swift SDK | `spaces.relay.unregister(_:)` | | Rust | `Spaces::relay_unregister` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `space` | `string` | required | Space id or name. | ### Result JSON: `space` and `unregistered` (false when it was not on the relay). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`not_found`](</docs/spaces/reference/errors#not_found>), [`ambiguous_sandbox`](</docs/spaces/reference/errors#ambiguous_sandbox>), [`host_capability_missing`](</docs/spaces/reference/errors#host_capability_missing>), [`relay`](</docs/spaces/reference/errors#relay>), [`unauthenticated`](</docs/spaces/reference/errors#unauthenticated>), [`permission_denied`](</docs/spaces/reference/errors#permission_denied>) --- # Your cloud Create Spaces in your own AWS, Google Cloud or Modal account: connect it, check it, and clean up what Cua created there. | Tool | Description | | --- | --- | | [`cloud_status`](#cloud_status) | List your clouds (AWS, Google Cloud, Modal): found credentials, the connected account and region, what each can run at what hourly cost, and what Cua created there. | | [`cloud_connect`](#cloud_connect) | Connect a cloud account Spaces can be created in. | | [`cloud_test`](#cloud_test) | Check a cloud account without creating anything. | | [`cloud_disconnect`](#cloud_disconnect) | Forget a connected cloud account. | | [`cloud_sweep`](#cloud_sweep) | Find and delete what Cua left in your clouds. | ## cloud_status List your clouds (AWS, Google Cloud, Modal): found credentials, the connected account and region, what each can run at what hourly cost, and what Cua created there. Sandboxes and Spaces can run in the user's own cloud account: on="aws" (EC2), "gcp" (Compute Engine) or "modal" (Modal Sandboxes). EC2 and Compute Engine start one small VM per sandbox that runs the image with Docker; Modal runs one Modal sandbox. Either way the guest's cua-spacesd dials out to the cua.ai relay as a machine of the signed-in account (no inbound port), and the Space is that machine, relay:<machine>. Each cloud uses its own CLI sign-in; connect it first with cloud_connect (the user decides; it costs money in their account). Everything Cua creates is tagged cua-managed=true with this cua home's cua-owner and an expiry, recorded under the cua home, deleted with its sandbox, and ends itself at its expiry; cloud_sweep removes leftovers. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:cloud_status`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.cloud.status(provider:)` | | Rust | `cua_cloud::Clouds::status` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `provider` | `string` | none | Only this cloud (`aws`, `gcp`, `modal`). Default: every one. | ### Result JSON: `default_on`, `providers` (each `name`, `title`, `tier` (`vm`: a VM that provides Spaces, `sandbox`: one sandbox per Space), `connected`, `default`, `credentials` (`found`, `source`), `account`, `profile`, `region`, `zone`, `project`, `environment`, `label` ("AWS · us-west-2"), `ttl_hours` and `kinds` (each `image`, `kind`, `supported`, `reason`, `machine_type`, `usd_per_hour`)) and `resources` (what Cua created: `provider`, `id`, `type`, `space`, `region`, `state`, `created`, `expires`, `expired`). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## cloud_connect Connect a cloud account Spaces can be created in. Runs cloud_test first and connects only when every check passes. Stores the profile, region, project or environment names under the cua home (never a key: the cloud's own CLI sign-in stays where it is). With make_default, create_space without `on` creates there (`default.on`). | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:cloud_connect`; mutating, idempotent | | Swift SDK | `spaces.cloud.connect(_:makeDefault:ttlHours:)` | | Rust | `cua_cloud::Clouds::connect` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `environment` | `string` | none | Modal environment (default: the profile's). | | `make_default` | `boolean` | `false` | Also make it the default location (`default.on`). Default false. | | `profile` | `string` | none | AWS: the profile in `~/.aws/config` (default: `default`, or `AWS_PROFILE`). Modal: the profile in `~/.modal.toml` (default: the active one). | | `project` | `string` | none | GCP project id (default: `gcloud config get project`). | | `provider` | `string` | required | `aws`, `gcp` or `modal`. | | `region` | `string` | none | AWS region (default: the profile's, else `us-west-2`) or GCP region (default `us-central1`). | | `ttl_hours` | `integer` | `8` | Every Space created there deletes itself after this many hours (the instance or sandbox terminates, the sweeper removes what is left). Default 8; 0 keeps Spaces until you delete them (Modal caps a sandbox at 24). At least 0. | | `zone` | `string` | none | GCP zone in the region (default: its `-a` zone). | ### Result JSON: the connected cloud as `cloud_status` lists it, plus `checks` (each `name`, `ok`, `detail`) from the test it ran first. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`cloud`](</docs/spaces/reference/errors#cloud>) ## cloud_test Check a cloud account without creating anything. Creates nothing: it signs in with the cloud's own CLI credentials, asks the cloud to validate a create without doing it (AWS DryRun, Google Cloud permission checks), and reads quotas. A failed check says what to fix. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:cloud_test`, in `spaces:readonly`; read-only, idempotent | | Swift SDK | `spaces.cloud.test(_:)` | | Rust | `cua_cloud::Clouds::test` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `environment` | `string` | none | Modal environment (default: the profile's). | | `profile` | `string` | none | AWS: the profile in `~/.aws/config` (default: `default`, or `AWS_PROFILE`). Modal: the profile in `~/.modal.toml` (default: the active one). | | `project` | `string` | none | GCP project id (default: `gcloud config get project`). | | `provider` | `string` | required | `aws`, `gcp` or `modal`. | | `region` | `string` | none | AWS region (default: the profile's, else `us-west-2`) or GCP region (default `us-central1`). | | `zone` | `string` | none | GCP zone in the region (default: its `-a` zone). | ### Result JSON: `provider`, `ok`, `account` (the account, project or workspace the credentials reach) and `checks` (each `name`, `ok`, `detail`: credentials, permissions by dry run, quota, the region and machine types). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`cloud`](</docs/spaces/reference/errors#cloud>) ## cloud_disconnect Forget a connected cloud account. Forgets the connection (and `default.on` when it named this cloud). Nothing in the cloud is deleted. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:cloud_disconnect`; mutating, idempotent | | Swift SDK | `spaces.cloud.disconnect(_:)` | | Rust | `cua_cloud::Clouds::disconnect` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `provider` | `string` | required | `aws`, `gcp` or `modal`. | ### Result JSON: `provider`, `disconnected` and `left` (resources Cua still records there; delete their Spaces, or cloud_sweep with all=true, to remove them). ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>) ## cloud_sweep Find and delete what Cua left in your clouds. Acts only on resources that carry Cua's tags (cua-managed=true and this device's cua-owner) and that this cua home recorded, or that carry this device's owner id: never on anything else in the account. Default dry_run=true lists what it would delete: expired Spaces (past their ttl), resources whose Space is gone, and with all=true every Cua resource there. | | | | --- | --- | | Providers | all (cloud, local, direct, relay) | | Platforms | all (macos, windows, linux) | | Metering | free | | Approval | permission `spaces:cloud_sweep`; destructive | | Swift SDK | `spaces.cloud.sweep(provider:dryRun:all:)` | | Rust | `cua_cloud::Clouds::sweep` | ### Parameters | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `all` | `boolean` | `false` | Also delete Cua resources that have not expired yet (every Space in that cloud). Default false: only expired ones, and resources no Space refers to any more. | | `dry_run` | `boolean` | `true` | List what would be deleted without deleting it. Default true. | | `provider` | `string` | none | Only this cloud. Default: every connected one. | ### Result JSON: `dry_run` and `resources`, each `provider`, `id`, `type`, `space`, `expires`, `action` (`delete`, `deleted`, `keep` or `failed`) and `reason`. ### Errors [`invalid_argument`](</docs/spaces/reference/errors#invalid_argument>), [`cloud`](</docs/spaces/reference/errors#cloud>) --- # Errors Tool error kinds and JSON-RPC protocol errors of the Spaces tools. A tool that fails returns a normal result with `isError: true`, never a protocol error: ```json { "content": [{ "type": "text", "text": "error: <message>" }], "isError": true, "structuredContent": { "error": { "kind": "<kind>", "message": "<message>" } } } ``` Branch on `kind`; the message is for people. Every tool can return `invalid_argument`. A tool that takes `space` can also return `not_found` and `ambiguous_sandbox`; one with spacesd `requires`, `spacesd_not_available` and `capability_missing`; one with host requirements, `host_capability_missing`. Each tool lists the rest. ## Error kinds | Kind | Meaning | | --- | --- | | [`invalid_argument`](#invalid_argument) | An argument is missing, malformed or out of range. | | [`not_found`](#not_found) | No Space, run, window, service, tool or file with that id. | | [`spacesd_not_available`](#spacesd_not_available) | The machine answered but runs no cua-spacesd, so the spacesd primitives cannot work. | | [`capability_missing`](#capability_missing) | The Space's cua-spacesd reports a feature this tool needs as unsupported; the message names it and the driver's limitation. | | [`host_capability_missing`](#host_capability_missing) | A prerequisite on this machine is missing: Fleet credentials, a local runtime, an operator display or app-session providers. | | [`wrong_provider`](#wrong_provider) | The operation does not apply to this kind of Space. | | [`unauthenticated`](#unauthenticated) | The Space's spacesd or the cua.ai relay rejected the token. | | [`permission_denied`](#permission_denied) | The signed-in account may not reach that relay machine. | | [`ambiguous_host`](#ambiguous_host) | The machine name given to on="host:<name>" fits several of your machines equally well; the message lists them. | | [`limit_exceeded`](#limit_exceeded) | The host is at a capacity limit: two macOS VMs per Mac (Apple's macOS license, enforced by Apple Virtualization) or the host's own Space limit. | | [`ambiguous_sandbox`](#ambiguous_sandbox) | A short name matches more than one sandbox. | | [`transfer_failed`](#transfer_failed) | A file transfer did not land, or landed with a different sha256. | | [`teleport_refused`](#teleport_refused) | The teleport consent gate refused: a sensitive item without acknowledge_sensitive, a declined approval, or a Space that cannot import the app. | | [`login_refused`](#login_refused) | The Keyvault refused a site login: the user declined, no saved password matches the page's origin, the tab is on another origin, or the Keyvault is locked or off. | | [`agent`](#agent) | An agent run failed outside the transport: install, configuration or a refused message. | | [`timeout`](#timeout) | A wait ran out: a claim that never bound, a Space that never became ready, or a command past its timeout. | | [`cancelled`](#cancelled) | The create was cancelled (cancel_create, or its caller went away); what it made is gone. | | [`stream`](#stream) | The media plane failed to open or run the stream. | | [`env`](#env) | A cua-spacesd call failed. | | [`fleet`](#fleet) | The Fleet control plane failed a cloud Space call. | | [`fleet_admission_denied`](#fleet_admission_denied) | Fleet's admission refused the cloud Space: a size over this account's limits, or a template its policy does not admit. The message is Fleet's and names the limit. | | [`cloud_credit_exhausted`](#cloud_credit_exhausted) | The account is out of Cua Cloud credit (no credit left, and no card or plan): Fleet refused the new cloud Space. Running Spaces keep running; local Spaces are never affected. The message ends with the billing page's URL. | | [`invalid_placement`](#invalid_placement) | The location, kind or runtime does not exist, or the combination (with the image) does not. | | [`sandbox`](#sandbox) | Starting, stopping or deleting the Space's sandbox failed. | | [`cloud`](#cloud) | Your cloud account refused or failed a call Cua made for a Space there (AWS, Google Cloud, Modal): a missing permission, a quota, a region without the instance type. The message names the cloud's own error. | | [`relay`](#relay) | The cua.ai relay directory failed. | | [`mcp`](#mcp) | An MCP service inside the Space failed. | | [`io`](#io) | Reading or writing a local file failed. | | [`json`](#json) | A reply could not be parsed. | | [`forbidden`](#forbidden) | The drive's access rules refuse this principal on this path (an agent outside its own home, its Space's folder, read-only public/ and its grants). | | [`precondition_failed`](#precondition_failed) | A drive write or delete with if_etag or create_only found a different version at the path. | | [`secret_detected`](#secret_detected) | A write under agents/ carries something that looks like a secret (an API key, a token, a private key); it was not stored. The message names the kind and line, never the value. | | [`lease_held`](#lease_held) | Another run holds the persistent agent's home (one writer at a time). | | [`not_confirmed`](#not_confirmed) | The user did not confirm with presence (Touch ID, the login password or the vault passphrase), so no access was widened. | | [`volume_backend`](#volume_backend) | The drive's store failed or is not configured (a bucket that refused, missing keys, or a backend this build cannot serve). | ### invalid_argument An argument is missing, malformed or out of range. Fix: Check the call against the tool's input schema; the message names the argument. Listed by [`volume_storage_set`](</docs/spaces/reference/volume#volume_storage_set>), [`volume_cache_set`](</docs/spaces/reference/volume#volume_cache_set>), [`relay_unregister_space`](</docs/spaces/reference/sharing#relay_unregister_space>). ### not_found No Space, run, window, service, tool or file with that id. Fix: List what exists first (list_spaces, agent_list, list_space_windows, list_tools). Listed by [`volume_sync_resolve`](</docs/spaces/reference/volume#volume_sync_resolve>). ### spacesd_not_available The machine answered but runs no cua-spacesd, so the spacesd primitives cannot work. Fix: Use an image that runs cua-spacesd (for example ghcr.io/trycua/linux:24.04), or reach the Space's own services with list_tools and call_tool. ### capability_missing The Space's cua-spacesd reports a feature this tool needs as unsupported; the message names it and the driver's limitation. Fix: Use a Space whose spacesd supports the feature (see the tool's `requires`). ### host_capability_missing A prerequisite on this machine is missing: Fleet credentials, a local runtime, an operator display or app-session providers. Fix: Set up what the message names (see the tool's `host_requires`). ### wrong_provider The operation does not apply to this kind of Space. Fix: Use a tool that serves the Space's provider (see the tool's `providers`). Listed by [`stop_space`](</docs/spaces/reference/lifecycle#stop_space>), [`start_space`](</docs/spaces/reference/lifecycle#start_space>). ### unauthenticated The Space's spacesd or the cua.ai relay rejected the token. Fix: Re-add the Space with a valid token, or sign in again with `cua auth login`. Listed by [`add_space`](</docs/spaces/reference/lifecycle#add_space>), [`list_spaces`](</docs/spaces/reference/lifecycle#list_spaces>), [`share_space`](</docs/spaces/reference/sharing#share_space>), [`unshare_space`](</docs/spaces/reference/sharing#unshare_space>), [`space_shares`](</docs/spaces/reference/sharing#space_shares>), [`relay_register_space`](</docs/spaces/reference/sharing#relay_register_space>), [`relay_unregister_space`](</docs/spaces/reference/sharing#relay_unregister_space>). ### permission_denied The signed-in account may not reach that relay machine. Fix: Sign in with the account that owns the machine. Listed by [`stop_space`](</docs/spaces/reference/lifecycle#stop_space>), [`start_space`](</docs/spaces/reference/lifecycle#start_space>), [`share_space`](</docs/spaces/reference/sharing#share_space>), [`unshare_space`](</docs/spaces/reference/sharing#unshare_space>), [`relay_unregister_space`](</docs/spaces/reference/sharing#relay_unregister_space>). ### ambiguous_host The machine name given to on="host:<name>" fits several of your machines equally well; the message lists them. Fix: Ask the user which machine they meant, then pass on="host:<machine id>" from the list. Listed by [`create_space`](</docs/spaces/reference/lifecycle#create_space>). ### limit_exceeded The host is at a capacity limit: two macOS VMs per Mac (Apple's macOS license, enforced by Apple Virtualization) or the host's own Space limit. Fix: Delete a Space on that host (delete_space), pick another machine, or create a Linux Space instead. Listed by [`create_space`](</docs/spaces/reference/lifecycle#create_space>). ### ambiguous_sandbox A short name matches more than one sandbox. Fix: Use the full id (`local:<name>` or `cloud:<name>`). ### transfer_failed A file transfer did not land, or landed with a different sha256. Fix: Retry; check free space and permissions at the destination. Listed by [`space_write`](</docs/spaces/reference/commands-and-files#space_write>), [`upload`](</docs/spaces/reference/commands-and-files#upload>), [`send_file`](</docs/spaces/reference/commands-and-files#send_file>), [`download`](</docs/spaces/reference/commands-and-files#download>), [`teleport_app`](</docs/spaces/reference/teleport#teleport_app>). ### teleport_refused The teleport consent gate refused: a sensitive item without acknowledge_sensitive, a declined approval, or a Space that cannot import the app. Fix: Read teleport_manifest, then pass the items to move and acknowledge_sensitive=true for sensitive ones. Listed by [`teleport_app`](</docs/spaces/reference/teleport#teleport_app>). ### login_refused The Keyvault refused a site login: the user declined, no saved password matches the page's origin, the tab is on another origin, or the Keyvault is locked or off. Fix: Ask the user to approve the sign-in in Cua, import the site's saved password, or open the site's sign-in page and try again. Listed by [`request_site_login`](</docs/spaces/reference/keyvault#request_site_login>). ### agent An agent run failed outside the transport: install, configuration or a refused message. Fix: Read agent_status and its output_tail for the reason. Listed by [`agent_start`](</docs/spaces/reference/agents#agent_start>), [`agent_message`](</docs/spaces/reference/agents#agent_message>), [`agent_events`](</docs/spaces/reference/agents#agent_events>), [`agent_status`](</docs/spaces/reference/agents#agent_status>), [`agent_interrupt`](</docs/spaces/reference/agents#agent_interrupt>), [`agent_stop`](</docs/spaces/reference/agents#agent_stop>), [`agent_list`](</docs/spaces/reference/agents#agent_list>), [`persistent_agent_create`](</docs/spaces/reference/persistent-agents#persistent_agent_create>), [`persistent_agent_remove`](</docs/spaces/reference/persistent-agents#persistent_agent_remove>), [`persistent_agent_send`](</docs/spaces/reference/persistent-agents#persistent_agent_send>), [`persistent_agent_save`](</docs/spaces/reference/persistent-agents#persistent_agent_save>), [`agent_pause`](</docs/spaces/reference/persistent-agents#agent_pause>), [`agent_resume`](</docs/spaces/reference/persistent-agents#agent_resume>). ### timeout A wait ran out: a claim that never bound, a Space that never became ready, or a command past its timeout. Fix: Retry, or raise the tool's timeout. Listed by [`create_space`](</docs/spaces/reference/lifecycle#create_space>), [`start_space`](</docs/spaces/reference/lifecycle#start_space>), [`space_bash`](</docs/spaces/reference/commands-and-files#space_bash>). ### cancelled The create was cancelled (cancel_create, or its caller went away); what it made is gone. Fix: Nothing to clean up; create it again when you want it. ### stream The media plane failed to open or run the stream. Fix: Check that the Space's spacesd supports desktop_stream or window_stream, then retry. Listed by [`stream_endpoint`](</docs/spaces/reference/streams-and-display#stream_endpoint>), [`stream_space_window`](</docs/spaces/reference/streams-and-display#stream_space_window>). ### env A cua-spacesd call failed. Fix: Retry; if it persists, check the spacesd log in the Space. ### fleet The Fleet control plane failed a cloud Space call. Fix: See the Fleet errors reference for the HTTP status in the message. Listed by [`create_space`](</docs/spaces/reference/lifecycle#create_space>), [`delete_space`](</docs/spaces/reference/lifecycle#delete_space>), [`agent_resume`](</docs/spaces/reference/persistent-agents#agent_resume>). ### fleet_admission_denied Fleet's admission refused the cloud Space: a size over this account's limits, or a template its policy does not admit. The message is Fleet's and names the limit. Fix: Pick a size within the limit the message names (most accounts run 1-8 vCPUs and 1-32 GiB), or contact Cua support to raise the account's limits. Listed by [`create_space`](</docs/spaces/reference/lifecycle#create_space>). ### cloud_credit_exhausted The account is out of Cua Cloud credit (no credit left, and no card or plan): Fleet refused the new cloud Space. Running Spaces keep running; local Spaces are never affected. The message ends with the billing page's URL. Fix: Add credit on the billing page the message names (a plan, or a card for pay as you go), or create the Space locally. Listed by [`create_space`](</docs/spaces/reference/lifecycle#create_space>). ### invalid_placement The location, kind or runtime does not exist, or the combination (with the image) does not. Fix: Use one of the values the message lists, or leave kind and runtime unset (auto). Listed by [`create_space`](</docs/spaces/reference/lifecycle#create_space>). ### sandbox Starting, stopping or deleting the Space's sandbox failed. Fix: Check the local runtime (`cua doctor`) or the cloud pool, then retry. Listed by [`create_space`](</docs/spaces/reference/lifecycle#create_space>), [`delete_space`](</docs/spaces/reference/lifecycle#delete_space>), [`stop_space`](</docs/spaces/reference/lifecycle#stop_space>), [`start_space`](</docs/spaces/reference/lifecycle#start_space>), [`agent_pause`](</docs/spaces/reference/persistent-agents#agent_pause>), [`agent_resume`](</docs/spaces/reference/persistent-agents#agent_resume>). ### cloud Your cloud account refused or failed a call Cua made for a Space there (AWS, Google Cloud, Modal): a missing permission, a quota, a region without the instance type. The message names the cloud's own error. Fix: Fix what the message names in that account (`cua cloud test <provider>` checks without creating anything), or connect another region or project. Listed by [`stop_space`](</docs/spaces/reference/lifecycle#stop_space>), [`start_space`](</docs/spaces/reference/lifecycle#start_space>), [`cloud_connect`](</docs/spaces/reference/cloud#cloud_connect>), [`cloud_test`](</docs/spaces/reference/cloud#cloud_test>), [`cloud_sweep`](</docs/spaces/reference/cloud#cloud_sweep>). ### relay The cua.ai relay directory failed. Fix: Retry; check `cua auth status`. Listed by [`list_spaces`](</docs/spaces/reference/lifecycle#list_spaces>), [`stop_space`](</docs/spaces/reference/lifecycle#stop_space>), [`start_space`](</docs/spaces/reference/lifecycle#start_space>), [`share_space`](</docs/spaces/reference/sharing#share_space>), [`unshare_space`](</docs/spaces/reference/sharing#unshare_space>), [`space_shares`](</docs/spaces/reference/sharing#space_shares>), [`relay_register_space`](</docs/spaces/reference/sharing#relay_register_space>), [`relay_unregister_space`](</docs/spaces/reference/sharing#relay_unregister_space>). ### mcp An MCP service inside the Space failed. Fix: Check the service with list_tools; the message carries its error. Listed by [`list_tools`](</docs/spaces/reference/services#list_tools>), [`call_tool`](</docs/spaces/reference/services#call_tool>). ### io Reading or writing a local file failed. Fix: Check the path and its permissions on this machine. Listed by [`upload`](</docs/spaces/reference/commands-and-files#upload>), [`send_file`](</docs/spaces/reference/commands-and-files#send_file>), [`download`](</docs/spaces/reference/commands-and-files#download>). ### json A reply could not be parsed. Fix: Retry; report it if it persists. ### forbidden The drive's access rules refuse this principal on this path (an agent outside its own home, its Space's folder, read-only public/ and its grants). Fix: Ask for access with volume_request_access; the user approves it in Cua. Listed by [`volume_ls`](</docs/spaces/reference/volume#volume_ls>), [`volume_read`](</docs/spaces/reference/volume#volume_read>), [`volume_write`](</docs/spaces/reference/volume#volume_write>), [`volume_delete`](</docs/spaces/reference/volume#volume_delete>), [`volume_history`](</docs/spaces/reference/volume#volume_history>), [`volume_restore`](</docs/spaces/reference/volume#volume_restore>), [`volume_grant`](</docs/spaces/reference/volume#volume_grant>), [`volume_revoke`](</docs/spaces/reference/volume#volume_revoke>), [`volume_grants`](</docs/spaces/reference/volume#volume_grants>), [`volume_request_access`](</docs/spaces/reference/volume#volume_request_access>), [`volume_requests`](</docs/spaces/reference/volume#volume_requests>), [`volume_approve`](</docs/spaces/reference/volume#volume_approve>), [`volume_deny`](</docs/spaces/reference/volume#volume_deny>), [`volume_audit`](</docs/spaces/reference/volume#volume_audit>), [`volume_storage`](</docs/spaces/reference/volume#volume_storage>), [`volume_storage_set`](</docs/spaces/reference/volume#volume_storage_set>), [`volume_mount_status`](</docs/spaces/reference/volume#volume_mount_status>), [`volume_mount`](</docs/spaces/reference/volume#volume_mount>), [`volume_unmount`](</docs/spaces/reference/volume#volume_unmount>), [`volume_sync_status`](</docs/spaces/reference/volume#volume_sync_status>), [`volume_sync_events`](</docs/spaces/reference/volume#volume_sync_events>), [`volume_sync_resolve`](</docs/spaces/reference/volume#volume_sync_resolve>), [`volume_cache_stats`](</docs/spaces/reference/volume#volume_cache_stats>), [`volume_cache_set`](</docs/spaces/reference/volume#volume_cache_set>), [`volume_cache_clear`](</docs/spaces/reference/volume#volume_cache_clear>). ### precondition_failed A drive write or delete with if_etag or create_only found a different version at the path. Fix: Read the file again and retry with its current etag. Listed by [`volume_write`](</docs/spaces/reference/volume#volume_write>), [`volume_delete`](</docs/spaces/reference/volume#volume_delete>). ### secret_detected A write under agents/ carries something that looks like a secret (an API key, a token, a private key); it was not stored. The message names the kind and line, never the value. Fix: Remove the secret from the file; keep credentials in the Keyvault. Listed by [`persistent_agent_save`](</docs/spaces/reference/persistent-agents#persistent_agent_save>), [`volume_write`](</docs/spaces/reference/volume#volume_write>), [`volume_restore`](</docs/spaces/reference/volume#volume_restore>). ### lease_held Another run holds the persistent agent's home (one writer at a time). Fix: Wait for that run to stop, or stop it; the lease expires on its own. Listed by [`persistent_agent_send`](</docs/spaces/reference/persistent-agents#persistent_agent_send>). ### not_confirmed The user did not confirm with presence (Touch ID, the login password or the vault passphrase), so no access was widened. Fix: Ask the user to approve it in Cua. Listed by [`computer_access_grant`](</docs/spaces/reference/persistent-agents#computer_access_grant>), [`volume_grant`](</docs/spaces/reference/volume#volume_grant>), [`volume_approve`](</docs/spaces/reference/volume#volume_approve>). ### volume_backend The drive's store failed or is not configured (a bucket that refused, missing keys, or a backend this build cannot serve). Fix: Check `cua volume config show` and the message. Listed by [`volume_ls`](</docs/spaces/reference/volume#volume_ls>), [`volume_read`](</docs/spaces/reference/volume#volume_read>), [`volume_write`](</docs/spaces/reference/volume#volume_write>), [`volume_delete`](</docs/spaces/reference/volume#volume_delete>), [`volume_history`](</docs/spaces/reference/volume#volume_history>), [`volume_restore`](</docs/spaces/reference/volume#volume_restore>), [`volume_unmount`](</docs/spaces/reference/volume#volume_unmount>). ## Protocol errors JSON-RPC errors are about the message, not the tool. | Code | Name | When | | --- | --- | --- | | `-32700` | `PARSE_ERROR` | The message is not valid JSON. | | `-32600` | `INVALID_REQUEST` | The message is not a JSON-RPC request: an empty batch, or no `method`. | | `-32601` | `METHOD_NOT_FOUND` | An unknown method, or `tools/call` of a tool this server does not expose (not in the contract, or filtered out by permissions). | | `-32602` | `INVALID_PARAMS` | `tools/call` without a tool `name`. | --- # Cua Fleets Dedicated cloud capacity for sandboxes. Named pools, templates, claims, warm replicas and Terraform. Cua Fleets is the cloud behind `local=False`. A **pool** holds capacity for one sandbox template (image, runtime, size, command, services, sidecars). A **claim** reserves one sandbox from a pool. Named **services** route authenticated clients to ports inside the claimed sandbox. ```text output pool (template: image, runtime, cpu/memory, command, services, sidecars; sizing) └─ claim ── bound sandbox ── services: env -> 3211, mcp -> 8765, ... │ https://run.cua.ai/api/svc/{namespace}/{sandbox}-{service} ``` ## Fleets or `local=False` `Sandbox.create(image, local=False)` already runs on Fleets: the SDK creates a managed pool per image and shape, scales it from zero and deletes it when idle. Use Fleets directly when you need what a managed pool does not give you. | | Managed pool (`local=False`) | Your own pool | | --- | --- | --- | | Created by | `Sandbox.create(image, local=False)`, `cua sb create IMAGE --on cloud` | `Pool.apply(...)`, Terraform | | Name | `cua-auto-<hash>`, per account, image and shape | You choose it, globally unique | | Sizing | Autoscales from zero to `max_pool_size` (10); canonical images keep one warm | `replicas`, or autoscaling bounds | | Shared by | One account's processes | Any process or team with access | | Cleanup | Deleted after 30 minutes idle | You delete it, or set a TTL | | Extras | | Terraform, [claim secrets](</docs/fleets/guides/claim-secrets>), creation-age TTLs, template updates | ## Claims and services A claim's name is the sandbox's name (`cloud:<name>`). Releasing it returns or deletes the sandbox; the pool stays. Its TTL (15 minutes by default) is renewed while a process holds it. The `env` service (port `3211`) is cua-spacesd, when the image has it. Pools run containers under gVisor and VMs under KubeVirt, amd64 only ([images](</docs/fleets/guides/images>)). ## Guides - [Create capacity and claim from it](</docs/fleets/guides/capacity-and-claims>) - [Keep sandboxes warm](</docs/fleets/guides/warm-pools>) - [Manage pools with Terraform](</docs/fleets/guides/terraform>) - [Publish a Fleet image](</docs/fleets/guides/images>) - [Add sidecars on Fleet](</docs/fleets/guides/sidecars>) - [Deliver per-claim secrets](</docs/fleets/guides/claim-secrets>) - [Troubleshoot](</docs/fleets/guides/troubleshoot>) --- # Quickstart Your first cloud sandbox and your first pool. Start a desktop in the cloud, verify a file hash, then apply a pool and claim from it. Start a Linux desktop in the cloud, hash a file inside it and check the hash on your machine, then create a pool of your own and claim a sandbox from it. ## Prerequisites - The `cua` CLI, signed in (`cua auth login --remote` without a browser): ```bash test="cli-shape" id="cua-auth-login" curl -fsSL https://cua.ai/install.sh | sh cua auth login ``` - For scripts: Python `>=3.11,<3.14` with [`uv`](https://docs.astral.sh/uv/), or Node.js 20. They read `CUA_CLIENT_ID` and `CUA_CLIENT_SECRET` from `cua auth keys create NAME` ([credentials](</docs/cua-sdk/guides/cloud#credentials>)). Usage is billed while a sandbox runs; releasing it ends the charge. ## Run a cloud sandbox **CLI** ```bash test="cli-shape" id="cua-sb-create" cua sb create linux --on cloud --name first-fleet printf 'pear\napple\npear\nbanana\n' > fruit.txt cua sb cp fruit.txt first-fleet:/tmp/fruit.txt cua sb exec first-fleet sha256sum /tmp/fruit.txt shasum -a 256 fruit.txt cua sb screenshot first-fleet -o first-fleet.png ``` **Python** The script pins `cua-sandbox==0.9.0` and runs `Image.linux()` with `local=False` (`CUA_FLEET_IMAGE` overrides the image). ```bash skip="third-party-cli" curl -fsSLO https://cua.ai/docs-assets/scripts/first-cloud-fleet/first_cloud_fleet.py uv run first_cloud_fleet.py ``` **TypeScript** ```bash skip="host-install" npm install @trycua/cua@0.4.1 && npm install --save-dev tsx curl -fsSLo first-cloud-fleet.ts \ https://cua.ai/docs-assets/scripts/first-cloud-fleet/first-cloud-fleet.ts.txt npx tsx first-cloud-fleet.ts ``` Both hashes are `d9359aad71bda9a11a4fd2e8d72044d6f0f77519c6b6fb30d251806febcfb4d0`. Your machine computed the second one, so the check does not rely on the guest. The canonical image keeps warm capacity, so this takes seconds; another image's first start can take a few minutes. ## Create a pool and claim from it The sandbox above ran on a pool the SDK manages. Apply a pool you own, with one warm replica, claim a sandbox from it, and delete it: ```python test="docs,fleet" id="first-pool" prelude="fakefleet,pool-name" import os from cua_sandbox import CloudOptions, Image, Pool, PoolOptions, Sandbox, SandboxSpec pool = await Pool.apply( os.environ["CUA_POOL_NAME"], # globally unique, lowercase SandboxSpec(image=Image.linux(), services={"env": 3211}), PoolOptions(replicas=1), ) async with Sandbox.ephemeral(cloud=CloudOptions(pool=pool.name)) as sb: print((await sb.shell.run("uname -a")).stdout.strip()) await pool.delete() ``` ## Result ```text output Claimed ... Verified SHA-256: d9359aad71bda9a11a4fd2e8d72044d6f0f77519c6b6fb30d251806febcfb4d0 Screenshot: .../first-cloud-fleet.png Claim released ``` ## Clean up ```bash test="cli-shape" id="cua-sb-rm-first-fleet" cua sb rm -f first-fleet ``` The scripts release their sandbox even when a step fails. A killed process's sandbox expires after its TTL (15 minutes); idle managed pools are deleted after 30 minutes, or now with `cua fleet pools gc` ([Troubleshoot](</docs/fleets/guides/troubleshoot#clean-up-leftovers>)). ## Next - [Create capacity and claim from it](</docs/fleets/guides/capacity-and-claims>) - [Manage pools with Terraform](</docs/fleets/guides/terraform>) - [Keep sandboxes warm](</docs/fleets/guides/warm-pools>) --- # Create capacity and claim from it Apply a named pool, claim sandboxes from it, keep a sandbox across processes, and bound lifetimes with TTLs. `Sandbox.create(image, local=False)` needs no pool. Create one when you want a fixed name that other processes or teams claim from, a static warm size, a creation-age TTL, or Terraform ([Terraform](</docs/fleets/guides/terraform>)). Pool names are lowercase DNS labels, unique across all accounts. ## Create a pool **Python** ```python test="docs,fleet" id="create-pool" session="script" prelude="fakefleet,pool-name" import os from cua_sandbox import Image, Pool, PoolOptions, SandboxSpec pool = await Pool.apply( os.environ["CUA_POOL_NAME"], SandboxSpec(image=Image.linux(), cpu=4, memory_mb=4096, services={"env": 3211}), PoolOptions(replicas=1), ) print(f"Pool ready to claim from: {pool.name}") ``` **TypeScript** ```ts title="create-fleet-capacity.ts" test="docs" id="create-pool-typescript" prelude="fakefleet,pool-name" import { embedded, Pool, poolOptions, sandboxSpec } from '@trycua/cua'; const fleet = embedded().fleet(); const pool = await Pool.apply( fleet, process.env.CUA_POOL_NAME!, sandboxSpec('ghcr.io/trycua/linux:24.04', { cpu: 4, memoryMb: 4096, services: { env: 3211 } }), poolOptions({ runtime: 'gvisor', replicas: 1 }) ); await fleet.waitPoolReady(pool.name, 15 * 60_000); console.log(`Pool ready to claim from: ${pool.name}`); ``` `Pool.apply` creates or updates the pool and its template. `SandboxSpec` is what the sandboxes run; `PoolOptions` is how capacity is kept ([Pool reference](</docs/cua-sdk/reference/python/pool>)). `PoolAccessDeniedError` (`403`) usually means another account owns the name. ## Claim from it A pool fixes the image, command, services and size, so pass only the pool: **CLI** ```bash test="cli-shape" id="cua-sb-create" cua sb create --on cloud --pool my-team-desktop --name my-task cua sb exec my-task uname -a cua sb rm -f my-task ``` **Python** ```python test="docs,fleet" id="claim-python" prelude="fakefleet,team-pool" from cua_sandbox import CloudOptions, Sandbox async with Sandbox.ephemeral(cloud=CloudOptions(pool="my-team-desktop")) as sb: print((await sb.shell.run("uname -a")).stdout.strip()) ``` **TypeScript** ```ts test="docs,fleet" id="claim-typescript" prelude="fakefleet,team-pool" import { CloudOptions, embedded, SandboxCreateOptions } from '@trycua/cua'; const cua = embedded({ fleetFromSession: true }); const sb = await cua.sandboxes().create( SandboxCreateOptions.create({ on: 'cloud', image: '', cloud: CloudOptions.create({ pool: 'my-team-desktop' }), }) ); try { const out = await (await sb.spacesd(undefined)).sh('uname -a', undefined); console.log(new TextDecoder().decode(out.stdout).trim()); } finally { await sb.delete_(); // releases the sandbox; the pool stays } ``` Fields you pass with a pool must match its template, else `PoolSpecMismatch` shows the diff; `CloudOptions(pool=..., apply=True)` (CLI `--apply`) writes them to the template instead. A released sandbox may be recycled, so copy results out first. ### Claim calls | Call | Behavior | | --- | --- | | `async with pool.claim(name=None, service="env")` | Reattaches to claim `name` if it exists, else creates one; waits for `service`; releases on exit | | `await pool.claim()` | A connected `Sandbox`: `close()` releases it, `disconnect()` keeps the claim | | `await pool.create_claim(name=None)` | Creates without waiting. The handle has `wait()`, `renew(shutdown_time)`, `release()` and `to_dict()` (for `Sandbox.from_dict`); you own cleanup | | `await Pool.check(name, spec)` | Raises `PoolSpecMismatch` when the set fields of `spec` differ from the template | | `await Pool.apply_template(name, spec)` | Writes those fields to the template; the pool keeps its capacity | A failed claim releases a claim it created, never a preexisting one. Images without cua-spacesd pass their own `service=`. ## Keep a sandbox across processes Name the sandbox, do not release it, and extend its lease for idle periods (TypeScript: `sb.keepAlive(...)`, `sb.detach()`, `sandboxes().connect(name)`): ```python test="docs" id="keep-python" session="keep" prelude="fakefleet,image" sb = await Sandbox.create(image, local=False, name="my-task", keep_alive_minutes=120) reference = sb.to_dict() # store it (JSON-serializable) await sb.disconnect() # the sandbox stays held sb = await Sandbox.from_dict(reference) # later, in another process await sb.keep_alive(minutes=120) await sb.close() # releases it ``` ```bash test="cli-shape" id="cua-sb-keep-alive-my-task" cua sb keep-alive my-task --for 2h cua sb info my-task ``` ## Expiry | Deadline | Applies to | Set with | Renewed? | | --- | --- | --- | --- | | Sandbox TTL | Every cloud sandbox | `CloudOptions(claim_ttl=...)`, `--claim-ttl`; `keep_alive` | Yes, while a process holds it | | Creation-age TTL | Your pools and their claims | `ttl_seconds_after_created=` | No, counts from creation | | Idle TTL | Your pools | `PoolOptions(idle_ttl=...)` | Deletes the pool after this long without claims | ```python test="docs" id="claim-ttl" session="ttl" prelude="fakefleet" from cua_sandbox import CloudOptions, Image, Sandbox sb = await Sandbox.create(Image.linux(), cloud=CloudOptions(claim_ttl=30 * 60)) await sb.keep_alive(minutes=120) # an absolute shutdown time two hours from now ``` ```python test="docs" id="pool-ttl" session="pool" prelude="fakefleet,pool-vars" pool = await Pool.apply( Image.from_registry(IMAGE), name=POOL_NAME, replicas=1, ttl_seconds_after_created=86400, # the pool deletes itself after 24 hours ) async with pool.claim(name=CLAIM_NAME, ttl_seconds_after_created=3600) as sb: ... ``` - Reapplying a pool or reconnecting to a named claim does not reset a creation-age TTL, so a rerun can expire soon after it starts. - A pool that expires deletes its sandboxes too. Keep its TTL longer than the work, and copy results out first. - In TypeScript set `poolTtlSeconds` in `poolOptions({...})`; in Terraform, `ttl_seconds_after_created` on `fleets_pool`. ## Delete a pool Delete it after its users have released their sandboxes. Deleting removes its template and remaining sandboxes; deletion is asynchronous. **Python** ```python test="docs" id="delete" session="delete" prelude="fakefleet,pool-applied" await pool.delete() # the Pool from Pool.apply() also deletes its template ``` **TypeScript** ```ts test="docs" id="delete-typescript" prelude="fakefleet,pool-applied" await fleet.deletePool(POOL_NAME); ``` --- # Keep sandboxes warm Keep ready sandboxes in a managed or dedicated pool so claims bind in seconds, and size warm capacity against cost. A warm replica is a booted sandbox waiting for a claim. A claim on a warm pool binds in seconds; a claim on a cold pool waits for the image to pull and boot, which can take a few minutes. Warm replicas consume capacity, and are billed, whether or not anything claims them. ## Managed pools `Sandbox.create(image, local=False)` claims from a managed pool that scales from zero. `CloudOptions(warm=True)` keeps one sandbox of that image and shape ready. The canonical images (`Image.linux()`, `Image.windows()`) are warm by default; `CUA_FLEET_WARM` changes the default. ```python test="docs" id="managed-warm" session="managed" prelude="fakefleet,image" from cua_sandbox import CloudOptions sb = await Sandbox.create(image, cloud=CloudOptions(warm=True, max_pool_size=20)) ``` In the CLI, pass `--warm` or `--no-warm` to `cua sb create --on cloud`. A managed pool is deleted after 30 minutes without claims, warm or not. ## Your own pools A pool you apply keeps capacity in one of two modes: | Mode | `PoolOptions` | Behavior | | --- | --- | --- | | Static | `replicas=N` (default 1) | Keeps N sandboxes, claimed or not | | Autoscaling | `min_pool_size`, `max_pool_size` (`warm=True` means a floor of 1) | Scales with claims between the bounds | ```python test="docs,fleet" id="autoscaling-pool" session="pool" prelude="fakefleet,pool-name" import os from datetime import timedelta from cua_sandbox import Image, Pool, PoolOptions, SandboxSpec pool = await Pool.apply( os.environ["CUA_POOL_NAME"], SandboxSpec(image=Image.linux(), services={"env": 3211}), PoolOptions(min_pool_size=2, max_pool_size=10, idle_ttl=timedelta(hours=8)), ) await pool.delete() ``` `idle_ttl` deletes the pool after that long without claims, so a forgotten warm pool does not run forever. In Terraform the same choice is `replicas` or an `autoscaling` block ([Terraform](</docs/fleets/guides/terraform#sizing>)). ## Size it Set the floor to the claims you expect within one cold-start time, not to your peak: claims beyond the warm replicas still succeed after a cold start. A pool that must answer within a deadline (an external webhook) needs a warm replica for every concurrent claim. Terraform outputs and [run.cua.ai](https://run.cua.ai) show `ready_replicas`. --- # Manage pools with Terraform Declare Linux and Windows Fleet pools as fleets_pool resources, apply them, and export existing pools to Terraform. Each pool is a `fleets_pool` resource of the public `trycua/fleets` provider. Claim from it with `cua sb create --on cloud --pool NAME` or `Sandbox.create(cloud=CloudOptions(pool=NAME))`. The examples pin provider `0.2.0`; for OpenTofu, run the same commands with `tofu`. ## Authentication The provider has its own variables; `cua auth login`, `CUA_CLIENT_ID` and `FLEETS_TOKEN` do not configure it. Use an API key from `cua auth keys create NAME` ([credentials](</docs/cua-sdk/guides/cloud#credentials>)): ```bash skip="placeholder" export CYCLOPS_ENDPOINT="https://run.cua.ai" export CYCLOPS_CLIENT_ID="<your-client-id>" export CYCLOPS_CLIENT_SECRET="<your-client-secret>" export CYCLOPS_TOKEN_URL="https://auth.cua.ai/realms/cyclops-cs/protocol/openid-connect/token" unset CYCLOPS_ACCESS_TOKEN ``` `CYCLOPS_ACCESS_TOKEN` (a short-lived access token) takes precedence over the client credentials and cannot renew itself. ## Sizing Configure exactly one sizing mode per pool: `replicas` for a static warm size, or an `autoscaling` block (`min_pool_size`, `initial_pool_size`, `max_pool_size`) for claim-driven scaling. In autoscaling mode, `replicas` reports the current target after refresh. See [Keep sandboxes warm](</docs/fleets/guides/warm-pools>). ## A Linux pool This KubeVirt pool runs the canonical desktop disk and publishes cua-spacesd as the `env` service, which the SDK and CLI use for shell, files and screen: ```hcl title="main.tf" test="terraform" id="main-tf" terraform { required_providers { fleets = { source = "trycua/fleets" version = "0.2.0" } } } provider "fleets" { endpoint = "https://run.cua.ai" } resource "fleets_pool" "linux" { name = "linux-pool" cpu_cores = 4 memory = "8Gi" container_disk_image = "ghcr.io/trycua/linux:24.04-disk" runtime = "kubevirt" firmware = "bios" service { name = "env" target_port = 3211 protocol = "TCP" } autoscaling { min_pool_size = 0 initial_pool_size = 1 max_pool_size = 5 } } output "linux_pool" { value = { name = fleets_pool.linux.name namespace = fleets_pool.linux.namespace target_replicas = fleets_pool.linux.replicas current_replicas = fleets_pool.linux.current_replicas ready_replicas = fleets_pool.linux.ready_replicas } } ``` For a Windows pool, use a distinct `name`, `firmware = "efi"`, and a `readiness_probe_json` that waits for the port your image serves (for example `tcpSocket = { port = 3211 }` with a long `failureThreshold`). ## Apply and destroy ```bash skip="third-party-cli" terraform init terraform plan terraform apply terraform output terraform destroy # deletes the pool and its namespace ``` Commit `.terraform.lock.hcl`. The provider reports the pool's `namespace`, its `replicas` target and the `current_replicas` and `ready_replicas` counts. ## Export an existing pool `cua fleet pool export NAME --terraform` (Python: `Pool.export(name, terraform=True)`) prints a pool made with `Sandbox.create` or `Pool.apply` as a `fleets_pool` block: `command`, `args`, `env`, `process_mode`, `claim_secrets`, TTLs, services, autoscaling and one `sidecar` block per sidecar. A private-registry pool also gets a `fleets_registry_secret` resource whose username and password are Terraform variables. ```hcl skip="pseudo" sidecar { name = "db" image = "redis:7-alpine" ports = [6379] cpu = "250m" memory = "256Mi" } ``` The process fields, TTLs and `sidecar` blocks need a `trycua/fleets` provider newer than 0.3.0. ## Troubleshoot - `name` is a lowercase DNS label of at most 63 characters. Changing it replaces the pool and its namespace. - `403` on create: check the image reference and pull policy (`image_pull_secret` defaults to `ecr-credentials`). - Firmware must match the disk: `bios` or `efi` for amd64 Linux, `efi` for Windows. - Terraform state and shell history hold secrets; protect them. --- # Publish a Fleet image Which runtime boots a cloud image, and how to build, push and reference your own image with cua image build. A cloud sandbox boots an OCI artifact from a registry: the same reference you run locally. The runtime follows the artifact: | Artifact | Cloud runtime | Local runtime | | --- | --- | --- | | Container rootfs (any OCI image) | `gvisor` pod | Docker or Podman, gVisor when installed | | containerDisk (qcow2 at `/disk/disk.img`) | `kubevirt` VM | QEMU | | macOS (Lume) | Not offered | Lume | The canonical images publish both forms (`linux:24.04` and `linux:24.04-disk`) and one resolver picks the one a runtime needs, pinned by digest. Set the runtime yourself with `runtime="kubevirt"` (or `kind="vm"`) or `--runtime`; the SDK rejects a pairing the image cannot run (`InvalidPlacement`) before it creates anything. - The cloud runs amd64, so the image needs an amd64 variant. - Managed capacity is keyed by image digest and shape: two images never share replicas. Pin a digest when every sandbox must boot the same content. - A Windows containerDisk needs `os_type="windows"` (EFI firmware). For small changes (packages, files, env), add layers to an `Image` ([Choose and build an image](</docs/cua-sdk/guides/images>)). Build and publish your own image for a VM disk, a pinned artifact your team shares, or while cloud builds from layers are not available. This uses the `cua` 0.4.1 CLI. ## 1. Describe the image `cua image build` takes an `images.cua.ai/v1alpha1` Image resource. Layer types are `apt_install`, `pip_install`, `uv_install`, `app_install` and `run`; `env` and `ports` are recorded in the image. Keep secrets out of it. ```json title="image.json" test="config" id="json-config" schema="cua-image" { "apiVersion": "images.cua.ai/v1alpha1", "kind": "Image", "metadata": { "name": "my-workspace", "namespace": "my-workspace" }, "spec": { "recipe": { "osType": "linux", "distro": "ubuntu", "version": "24.04", "kind": "vm", "layers": [ { "type": "apt_install", "packages": ["curl", "git"] }, { "type": "run", "command": "mkdir -p /opt/app" } ], "env": { "APP_ENV": "production" }, "ports": [8080] } } } ``` ## 2. Build and push ```bash test="cli-shape" id="cua-image-build" # containerDisk for VMs: KubeVirt and local QEMU (boots the base VM, applies layers over SSH) cua image build image.json --base vm:ghcr.io/trycua/linux:24.04-disk \ --push ghcr.io/<you>/my-workspace:1.0-disk # rootfs for containers: gVisor in the cloud, Docker locally cua image build image.json --base container:ghcr.io/trycua/linux:24.04 \ --push ghcr.io/<you>/my-workspace:1.0 ``` The build prints `<reference>@sha256:<digest>`. It produces an image for the host architecture, so build cloud images on an x86_64 host. Registry credentials come from `CUA_REGISTRY_USERNAME` / `CUA_REGISTRY_PASSWORD`, `GITHUB_TOKEN` for `ghcr.io`, or your Docker config. **Warning** The cloud must be able to pull the image. A new GHCR package is private; make it public or pass a [registry secret](</docs/cua-sdk/guides/images#private-registries>). A pushed image carries everything on its disk. ## 3. Reference it ```bash test="cli-shape" id="cua-sb-create" cua sb create ghcr.io/<you>/my-workspace:1.0 --name ws # local container cua sb create ghcr.io/<you>/my-workspace:1.0 --on cloud --name ws-cloud ``` In code, pass the digest to `Image.from_registry(...)`. `cua image create --file image.json` submits the same resource for a remote build where the deployment supports it. --- # Add sidecars on Fleet Put sidecar containers in a pool's template, and how Fleet runs them next to container and VM sandboxes. Sidecars are extra containers next to a sandbox, reached by name: the sandbox reaches `db:6379`, a sidecar reaches the sandbox at `main`. The API is the same locally and in the cloud ([Add sidecar containers](</docs/cua-sdk/guides/sidecars>)); this page covers what Fleet does with them. ## In a pool template Sidecars are part of a pool's `SandboxSpec`, so every claim gets them: ```python test="docs" id="pool-sidecars" prelude="fakefleet,pool-name" import os from cua_sandbox import Container, Image, Pool, PoolOptions, SandboxSpec, tcp pool = await Pool.apply( os.environ["CUA_POOL_NAME"], SandboxSpec( image=Image.from_registry("python:3.12-slim"), command=["sleep", "infinity"], sidecars=[Container("redis:7-alpine", ports=[6379], name="db")], services={"db": 6379}, wait_for=tcp("db"), ), PoolOptions(replicas=1), ) await pool.delete() ``` `Sandbox.create(..., sidecars=[...], local=False)` does the same on a managed pool. Sidecars passed with `CloudOptions(pool=...)` are compared with the template like other fields (`PoolSpecMismatch` on a difference). ## How Fleet runs them | Sandbox | Sidecars run in | Name resolution | | --- | --- | --- | | Container image (gVisor) | The sandbox's pod | By name or `localhost` | | VM image (KubeVirt) | A companion gVisor pod | The guest's `/etc/hosts` names each sidecar (Linux with cloud-init); only declared `ports` are reachable | | VM without cloud-init (Windows) | A companion gVisor pod | `<sandbox>-sidecars.<namespace>.svc.cluster.local` | `runtime="runc"` is a local setting; the cloud ignores it. ## Limits - At most 8 sidecars per sandbox. - With sidecars, the service names `main`, `sidecars` and `sc` are reserved; a service with one of them is rejected. - A private sidecar image on the sandbox image's registry uses the same registry secret, on both runtimes. ## Terraform `cua fleet pool export NAME --terraform` writes each sidecar as a `sidecar {}` block of `fleets_pool` (`name`, `image`, `args`, `ports`, `cpu`, `memory`). Those blocks need a `trycua/fleets` provider newer than 0.3.0 ([Terraform](</docs/fleets/guides/terraform#export-an-existing-pool>)). --- # Deliver per-claim secrets Give each claim of a warm pool its own cua-spacesd token, delivered into the sandbox when the claim binds. A warm replica boots before anyone claims it, so it cannot be built with a secret meant for one claimant. Claim secrets close that gap: when a claim binds, Fleet delivers a Secret into the already-running sandbox, and the guest reads it at `/run/cua/env-token`. cua-spacesd uses that file as its env token, so each claim gets its own token, which never crosses the network to the guest. ## Enable it on the pool Set `claim_secrets=True` in the pool's `SandboxSpec` (Terraform: `claim_secrets = true` on `fleets_pool`). The image must run cua-spacesd, which waits for the token file before it accepts calls. ## Claim with a token ```python test="docs" id="claim-token" session="claim-token" prelude="fakefleet,pool-name" import os from cua_sandbox import Image, Pool, PoolOptions, SandboxSpec, generate_claim_token pool = await Pool.apply( os.environ["CUA_POOL_NAME"], SandboxSpec(image=Image.linux(), services={"env": 3211}, claim_secrets=True), PoolOptions(replicas=1), ) token = generate_claim_token() # 64 hex characters from the OS RNG async with pool.claim(claim_token=token) as sb: print((await sb.shell.run("uname -a")).stdout.strip()) await pool.delete() ``` The SDK writes the Secret `cua-claim-<claim>` (key `env-token`), creates the claim against it, and waits until spacesd answers with the token. | Language | Call | | --- | --- | | Python | `generate_claim_token()`, `pool.claim(claim_token=...)` | | TypeScript | `fleetGenerateClaimToken()`, `fleet.acquireWith(pool, options)` with `claimToken` set | | Rust | `fleet_generate_claim_token()`, `Fleet::acquire_with(pool, options)` with `claim_token` set | A token is 16 to 4096 characters of the bearer-token alphabet (`A-Z a-z 0-9 - . _ ~ + / =`); the SDK rejects one spacesd would refuse. ## When the token does not arrive Delivery is asynchronous (about 40 seconds on gVisor). The claim waits at most 90 seconds after it binds. If spacesd still reports `awaiting token`, the SDK releases the claim and raises `ClaimSecretsNotDelivered` (`CuaError.ClaimSecretsNotDelivered` in the other bindings). Check that the pool has `claim_secrets` enabled and that the image's cua-spacesd reads `/run/cua/env-token`. The Secret belongs to the claim: releasing the claim deletes it. Other credentials your workload needs still go in at runtime ([Pass secrets into a sandbox](</docs/cua-sdk/guides/secrets>)). --- # Troubleshoot cloud sandboxes Diagnose credential, access, image and readiness errors on Fleet, and clean up sandboxes and pools left behind. Start with what the current credentials can see, and keep tokens and signed URLs out of shared logs: ```bash test="cli-shape" id="cua-sb-ls" cua auth whoami cua sb ls --cloud cua fleet pools ls ``` ## Errors | Error | Cause and fix | | --- | --- | | `Fleet credentials missing` | No API key and no `cua auth login` session. Sign in, export a key, or run with `local=True` | | `401` | Revoked key, expired token, or `CUA_FLEET_BASE_URL` / `CUA_TOKEN_URL` pointing elsewhere. A stale `FLEETS_TOKEN` wins over a key; unset it | | `403` Payment method required | Add one in the dashboard | | `403` on create pool (`PoolAccessDeniedError`) | Pool names are global; another account owns it | | `403` on template | The image or its pull policy is not allowed: make it public or pass a [registry secret](</docs/cua-sdk/guides/images#private-registries>) | | `InvalidArgument` before anything is created | The runtime cannot run the image (`gvisor` needs a container image, `kubevirt` a containerDisk, both amd64), the sandbox has image layers the cloud cannot build yet, or a service uses a reserved name (`main`, `sidecars`, `sc`) next to sidecars | | `SpacesdNotAvailable` | The image has no cua-spacesd on `3211`; use its own services or an image that ships it | A `403` never proves a resource is absent. ## A sandbox does not become ready `progress=print` in Python (the CLI prints the same stages) shows where it waits. In `provisioning`, capacity is being found: a cold image takes a few minutes, and an image the cloud cannot pull stays there. In `starting`, your `wait_for` probes run: check that the server listens on the declared port on a non-loopback address. `cua sb logs NAME --source guest` shows the guest log. Raise the wait budget (`time_to_start=`, `--ready-timeout`) only after that; it does not extend any TTL. ## Clean up leftovers A run that stopped early needs no rerun: a cloud sandbox expires after its TTL (15 minutes by default) and idle managed pools are deleted after 30 minutes. To clean up now: ```bash test="cli-shape" id="cua-fleet-pools-gc" cua sb rm -f first-fleet # a named CLI sandbox cua fleet pools gc --idle 5m # idle managed pools and stuck claims ``` - A named Python sandbox: `await (await Sandbox.connect(NAME)).close()`. - A pool you own: `await pool.delete()` from `Pool.apply`, or `fleet.deletePool(NAME)` in TypeScript, after its users release their claims. `gc` never touches pools you created. Deletion is asynchronous; list again until the resource is gone. Add a [TTL](</docs/fleets/guides/capacity-and-claims#expiry>) to disposable work as a backstop. --- # Reference Cua Fleets objects, REST operations, SDK methods and Terraform schema. Everything Cua Fleets exposes, by object: the REST resources behind the Fleet API, the SDK `Fleet` handle, and the `trycua/fleets` Terraform provider. ## Objects | Task | Object | Description | | --- | --- | --- | | Keep capacity | [Pool](</docs/fleets/reference/pool>) | Warm capacity: how many sandboxes of one template Fleet keeps ready to claim. | | Keep capacity | [Template](</docs/fleets/reference/template>) | What each sandbox of a pool runs: image, runtime, size, services and probes. | | Use a sandbox | [Claim](</docs/fleets/reference/claim>) | A lease on one sandbox from a pool, until it is released or expires. | | Use a sandbox | [Sandbox](</docs/fleets/reference/sandbox>) | One running machine of a pool, bound to at most one claim. | | Use a sandbox | [Secret](</docs/fleets/reference/secret>) | Secrets the SDK writes in a pool's namespace: a claim's env token and registry pull credentials. | | Use a sandbox | [Service URL](</docs/fleets/reference/service-url>) | HTTP and WebSocket access to a sandbox's services, and shareable signed URLs. | | Build images | [Image](</docs/fleets/reference/image>) | A build recipe Fleet turns into a runnable image, and its build status. | | Account | [Namespace](</docs/fleets/reference/namespace>) | The account-owned scope every pool, template, claim and secret lives in. | | Account | [API key](</docs/fleets/reference/api-key>) | OAuth client credentials that act for your user, for the SDK, CLI and Terraform. | | Configure | [Terraform provider](</docs/fleets/reference/terraform>) | Provider settings and the resources it manages. | | Debug | [Errors](</docs/fleets/reference/errors>) | HTTP statuses and SDK errors, with causes and fixes. | ## Base URL and authentication - Base URL: `https://run.cua.ai` (`CUA_FLEET_BASE_URL` overrides it for the SDK). - Every request sends `Authorization: Bearer <token>`. Exchange an [API key](</docs/fleets/reference/api-key>) for a token with the OAuth client-credentials grant at `https://auth.cua.ai/realms/cyclops-cs/protocol/openid-connect/token`. - The SDK and CLI read `CUA_CLIENT_ID` and `CUA_CLIENT_SECRET`, a `cua auth login` session, or `FLEETS_TOKEN`. ## Conventions - Pools, templates, claims and sandboxes are Kubernetes custom resources (`osgym.cua.ai/v1alpha1`) under `/api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/`. Images are `images.cua.ai/v1alpha1`. - A pool owns the namespace of the same name; its template and claims live there too. - Names are DNS labels: lowercase letters, digits and `-`, at most 63 characters. - Lists return `{"items": [...]}`. Updates are JSON merge patches (`Content-Type: application/merge-patch+json`). - Errors outside the Kubernetes proxy return `{"error": "<message>"}`. ## Operations | Object | REST | SDK | Terraform | | --- | --- | --- | --- | | [Pool](</docs/fleets/reference/pool>) | [Create pool](</docs/fleets/reference/pool#create-pool>), [List pools](</docs/fleets/reference/pool#list-pools>), [Get pool](</docs/fleets/reference/pool#get-pool>), [Update pool](</docs/fleets/reference/pool#update-pool>), [Delete pool](</docs/fleets/reference/pool#delete-pool>) | `Fleet.apply_pool`, `Fleet.apply`, `Fleet.get_pool`, `Fleet.list_pools`, `Fleet.set_pool_replicas`, `Fleet.wait_pool_ready`, `Fleet.export_pool`, `Fleet.delete_pool`, `Fleet.ephemeral_pool_name`, `Fleet.pools` | `fleets_pool` | | [Template](</docs/fleets/reference/template>) | [Create template](</docs/fleets/reference/template#create-template>), [List templates](</docs/fleets/reference/template#list-templates>), [Get template](</docs/fleets/reference/template#get-template>), [Update template](</docs/fleets/reference/template#update-template>), [Delete template](</docs/fleets/reference/template#delete-template>) | `Fleet.apply_pool_template`, `Fleet.check_pool_spec`, `Fleet.list_templates` | `fleets_pool` | | [Claim](</docs/fleets/reference/claim>) | [Create claim](</docs/fleets/reference/claim#create-claim>), [List claims](</docs/fleets/reference/claim#list-claims>), [Get claim](</docs/fleets/reference/claim#get-claim>), [Renew claim](</docs/fleets/reference/claim#renew-claim>), [Delete claim](</docs/fleets/reference/claim#delete-claim>) | `Fleet.acquire`, `Fleet.acquire_with`, `Fleet.claim`, `Fleet.attach_claim`, `Fleet.list_claims`, `Fleet.keep_alive`, `Fleet.release` | none | | [Sandbox](</docs/fleets/reference/sandbox>) | none | `Fleet.attach_claim`, `Fleet.service_url` | none | | [Secret](</docs/fleets/reference/secret>) | none | none | none | | [Service URL](</docs/fleets/reference/service-url>) | [Call a service](</docs/fleets/reference/service-url#call-a-service>), [Create a signed URL](</docs/fleets/reference/service-url#create-a-signed-url>), [List signed URLs](</docs/fleets/reference/service-url#list-signed-urls>), [Revoke a signed URL](</docs/fleets/reference/service-url#revoke-a-signed-url>) | `Fleet.service_url`, `Fleet.create_signed_service_url` | none | | [Image](</docs/fleets/reference/image>) | [Create image](</docs/fleets/reference/image#create-image>), [List images](</docs/fleets/reference/image#list-images>), [Get image](</docs/fleets/reference/image#get-image>), [Delete image](</docs/fleets/reference/image#delete-image>), [Presign image uploads](</docs/fleets/reference/image#presign-image-uploads>) | `Fleet.create_image`, `Fleet.list_images`, `Fleet.get_image`, `Fleet.delete_image` | none | | [Namespace](</docs/fleets/reference/namespace>) | [Create a namespace](</docs/fleets/reference/namespace#create-a-namespace>), [List namespaces](</docs/fleets/reference/namespace#list-namespaces>), [Get a namespace](</docs/fleets/reference/namespace#get-a-namespace>), [Delete a namespace](</docs/fleets/reference/namespace#delete-a-namespace>) | none | none | | [API key](</docs/fleets/reference/api-key>) | [Create an API key](</docs/fleets/reference/api-key#create-an-api-key>), [List API keys](</docs/fleets/reference/api-key#list-api-keys>), [Revoke an API key](</docs/fleets/reference/api-key#revoke-an-api-key>) | none | none | --- # Pool Warm capacity: how many sandboxes of one template Fleet keeps ready to claim. On this page: [Object](#the-pool-object) · [REST](#rest) · [SDK](#sdk) · [Terraform](#terraform) ## The pool object `apiVersion: osgym.cua.ai/v1alpha1`, `kind: OSGymSandboxWarmPool`, in the pool's namespace. Plural `osgymsandboxwarmpools`, short name `oswp`. `metadata` is standard Kubernetes object metadata (`name`, `namespace`, `labels`). ### Spec | Field | Type | Default | Description | | --- | --- | --- | --- | | `spec.autoscaling` | `object` | none | Claim-driven autoscaling. When set, Fleet sizes the pool from its live claims (Pending and Bound) and owns `spec.replicas`: a Pending claim grows the pool, deleting claims shrinks it. Absent: `spec.replicas` is a static size. | | `spec.autoscaling.initialPoolSize` | `integer` | `0` | Warm head start: `spec.replicas` starts here when the pool is created, so the first claims bind without a cold start. It then follows claim demand, never below `minPoolSize`. At least 0. | | `spec.autoscaling.maxPoolSize` | `integer` | `50` | Most sandboxes the pool scales to. At least 1. | | `spec.autoscaling.minPoolSize` | `integer` | `0` | Fewest warm sandboxes kept while autoscaling. `0` lets the pool scale to zero when there are no claims. At least 0. | | `spec.idleTtlSeconds` | `integer` | none | Idle TTL in seconds. The pool-operator deletes the pool once it has had no Pending or Bound claims for this long, measured from status.lastActivityTime (or creation when no claim was ever made), when OSGYM_POOL_LIFECYCLE_MODE is enforce. When absent, the pool is never reaped for being idle. At least 0. | | `spec.replicas` | `integer` | required | Warm sandboxes to keep ready. With `autoscaling`, Fleet sets it. At least 0. | | `spec.sandboxTemplateRef` | `object` | required | The template the pool's sandboxes run. | | `spec.sandboxTemplateRef.name` | `string` | required | Name of the OSGymSandboxTemplate to use. | | `spec.ttlPolicy` | `"Retain" \| "Cascade"` | none | What expiry of ttlSecondsAfterCreated or idleTtlSeconds deletes. Retain (the behaviour when absent) deletes only the pool. Cascade also deletes the pool's dead unbound claims (TTL passed and older than max(900s, bindDeadline)); it never deletes Bound claims, the namespace or volumes. | | `spec.ttlSecondsAfterCreated` | `integer` | none | Creation-age TTL in seconds. When absent, the resource is not automatically reaped based on age. At least 0. | ### Status Set by Fleet; read-only. | Field | Type | Default | Description | | --- | --- | --- | --- | | `status.lastActivityTime` | `string` | none | When a claim against this pool was last created, bound or released. The idle TTL (spec.idleTtlSeconds) counts from here. | | `status.lastClaimedAt` | `string` | none | When a claim last bound a Sandbox from this pool. | | `status.readyReplicas` | `integer` | none | Sandboxes ready to be claimed. | | `status.replicas` | `integer` | none | Sandboxes the pool currently owns. | | `status.selector` | `string` | none | Label selector for the pool's OSGymSandboxes. | ### Example What the SDK sends for a default pool of the cua Linux image (`PoolSpec::new`, built by `cua-fleet`). ```json { "apiVersion": "osgym.cua.ai/v1alpha1", "kind": "OSGymSandboxWarmPool", "metadata": { "name": "my-pool", "namespace": "my-pool" }, "spec": { "replicas": 1, "sandboxTemplateRef": { "name": "my-pool" } } } ``` ## REST ### Create pool ```http POST /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxwarmpools ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Body: the [pool object](#the-pool-object) (`application/json`). Returns: `200`, `201`, `202` with the [pool object](#the-pool-object). The SDK first creates the namespace named after the pool ([Create a namespace](</docs/fleets/reference/namespace#create-a-namespace>)) and deletes it again if the pool is refused. Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### List pools ```http GET /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxwarmpools ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Returns: `200` with `{"items": [...]}`, a list of the [pool object](#the-pool-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Get pool ```http GET /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{name}/osgymsandboxwarmpools/{name} ``` A pool's namespace is its name, so the name fills both slots. | Parameter | In | Description | | --- | --- | --- | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200` with the [pool object](#the-pool-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Update pool ```http PATCH /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxwarmpools/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Body: a JSON merge patch of the [pool object](#the-pool-object) (`application/merge-patch+json`). Returns: `200` with the [pool object](#the-pool-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Delete pool ```http DELETE /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxwarmpools/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200`, `202`, `204` with no body. `404` is treated as success: the object is already gone. The SDK then deletes the namespace, with everything left in it. Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ## SDK Methods of the `Fleet` handle (`cua.fleet()`), with signatures in every language in the [Cua SDK reference](</docs/cua-sdk/reference>). | Method | Returns | Description | | --- | --- | --- | | [`Fleet.apply_pool(spec)`](</docs/cua-sdk/reference/fleet#fleetapply_pool>) | `FleetPool` | Deprecated: use `Fleet::apply`. | | [`Fleet.apply(name, spec, options)`](</docs/cua-sdk/reference/fleet#fleetapply>) | `FleetPool` | Reconciles pool `name` (= namespace = template) to run `spec` with `options`: the one pool writer (rolls a new pool back if the template fails). | | [`Fleet.get_pool(name)`](</docs/cua-sdk/reference/fleet#fleetget_pool>) | `FleetPool` | Looks up a pool. | | [`Fleet.list_pools(namespace)`](</docs/cua-sdk/reference/fleet#fleetlist_pools>) | `FleetPool[]` | Lists pools in a namespace. | | [`Fleet.set_pool_replicas(name, replicas)`](</docs/cua-sdk/reference/fleet#fleetset_pool_replicas>) | `FleetPool` | Sets warm replicas (0 suspends). | | [`Fleet.wait_pool_ready(name, timeout_ms)`](</docs/cua-sdk/reference/fleet#fleetwait_pool_ready>) | `FleetPool` | Waits for at least one ready replica. | | [`Fleet.export_pool(name)`](</docs/cua-sdk/reference/fleet#fleetexport_pool>) | `FleetPoolExport` | Reads pool `name` back as the shared model, with its `fleets_pool` Terraform block. | | [`Fleet.delete_pool(name)`](</docs/cua-sdk/reference/fleet#fleetdelete_pool>) | none | Deletes a pool, its namespace and its same-named template. | | `Fleet.ephemeral_pool_name()` | `string` | A random ephemeral pool name (`cua-eph-<hex>`). | | `Fleet.pools()` | `FleetPools` | Managed pools (list, gc). | | [`FleetPools.list()`](</docs/cua-sdk/reference/fleet#fleetpoolslist>) | `FleetManagedPool[]` | This account's managed pools. | | [`FleetPools.gc(idle_seconds)`](</docs/cua-sdk/reference/fleet#fleetpoolsgc>) | `FleetGcReport` | Deletes managed pools idle for `idle_seconds` (default 1800) and stuck Pending/Failed managed claims past their TTL. | | [`FleetPools.gc_pools(names, idle_seconds)`](</docs/cua-sdk/reference/fleet#fleetpoolsgc_pools>) | `FleetGcReport` | `FleetPools::gc` restricted to the named managed pools: each is deleted (with its namespace) once it has no claims and has been idle for `idle_seconds` (default 0, i.e. now). | ## Terraform Resource `fleets_pool` of the [`trycua/fleets` provider](</docs/fleets/reference/terraform>). A Cua Fleet computer-use pool: an OSGymSandboxWarmPool and its OSGymSandboxTemplate. The pool name also owns its same-named namespace. ```hcl title="main.tf" terraform { required_providers { fleets = { source = "trycua/fleets" version = "0.2.0" } } } provider "fleets" { endpoint = "https://run.cua.ai" } resource "fleets_pool" "linux" { name = "linux-pool" cpu_cores = 4 memory = "8Gi" container_disk_image = "ghcr.io/trycua/linux:24.04-disk" runtime = "kubevirt" firmware = "bios" service { name = "env" target_port = 3211 protocol = "TCP" } autoscaling { min_pool_size = 0 initial_pool_size = 1 max_pool_size = 5 } } output "linux_pool" { value = { name = fleets_pool.linux.name namespace = fleets_pool.linux.namespace target_replicas = fleets_pool.linux.replicas current_replicas = fleets_pool.linux.current_replicas ready_replicas = fleets_pool.linux.ready_replicas } } ``` ### Arguments Exactly one of `replicas` or `autoscaling` is required. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Pool and namespace DNS label. Changing it replaces the resource. 1 to 63 characters. Matches `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`. | | `replicas` | `number` | none | Static desired warm pool size. Exactly one of `replicas` or `autoscaling` must be configured. In autoscaling mode, do not configure it; after apply and refresh it reports the current pool target. At least 0. | | `cpu_cores` | `number` | required | Virtual CPUs per sandbox. At least 1. | | `memory` | `string` | required | Kubernetes memory quantity per sandbox. | | `container_disk_image` | `string` | required | OCI containerDisk or runtime image. | | `image_pull_secret` | `string` | none | Optional image pull secret. Omit it for an anonymous public-image pull. | | `runtime` | `string` | `"kubevirt"` | `kubevirt`, `macos`, or `gvisor`; defaults to `kubevirt`. | | `firmware` | `string` | `"bios"` | `bios` or `efi`; defaults to `bios`. | | `readiness_probe_json` | `string (JSON)` | none | Kubernetes probe objects encoded as JSON. | | `liveness_probe_json` | `string (JSON)` | none | Kubernetes probe objects encoded as JSON. | | `command` | `list(string)` | none | Entrypoint command list (replaces the image entrypoint). Pod runtimes (`gvisor`, `macos`) run it; KubeVirt ignores it. | | `claim_secrets` | `bool` | none | Opt in to claim-scoped secret delivery at `/run/cua` (the per-claim env token and a claim's `secretRef`). Claims with a `secretRef` fail on pools without it. | | `ttl_seconds_after_created` | `number` | none | Delete the pool this many seconds after it was created. Omit it to never reap by age. 0 to 4294967295. | | `idle_ttl_seconds` | `number` | none | Delete the pool after this many seconds with no Pending or Bound claims. Omit it to never reap for idleness. 0 to 4294967295. | | `ttl_policy` | `string` | none | What a TTL expiry deletes: `Retain` (the pool only, the behavior when omitted) or `Cascade` (also the pool's dead unbound claims; never Bound claims, the namespace or volumes). | ### `service` block Repeatable block. Repeatable service with `name`, `target_port`, and optional `protocol`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `service.name` | `string` | required | Service name suffix (sandbox name is prepended). | | `service.target_port` | `number` | required | Port on the VM pod to forward to. 1 to 65535. | | `service.protocol` | `string` | `"TCP"` | `TCP` or `UDP`. | ### `autoscaling` block Block. Claim-driven autoscaling limits. Exactly one of `replicas` or `autoscaling` is required. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `autoscaling.min_pool_size` | `number` | `0` | Minimum warm pool size while autoscaling is enabled. At least 0. | | `autoscaling.initial_pool_size` | `number` | `0` | Initial pool target when autoscaling starts. At least 0. | | `autoscaling.max_pool_size` | `number` | `50` | Maximum autoscaled pool size; defaults to `50` when omitted. At least 1. | ### Attributes Read-only, set by the provider. | Attribute | Type | Description | | --- | --- | --- | | `id` | `string` | The pool name. | | `namespace` | `string` | The pool's namespace (the pool name). | | `template_name` | `string` | Name of the OSGymSandboxTemplate backing this pool. | | `current_replicas` | `number` | Sandboxes the warm pool currently owns. | | `ready_replicas` | `number` | Sandboxes that are ready to be claimed. | `replicas` is also set in autoscaling mode: it reports the current pool target. ### Import Import IDs are pool names. ```shell terraform import fleets_pool.linux training-linux ``` --- # Template What each sandbox of a pool runs: image, runtime, size, services and probes. On this page: [Object](#the-template-object) · [REST](#rest) · [SDK](#sdk) · [Terraform](#terraform) ## The template object `apiVersion: osgym.cua.ai/v1alpha1`, `kind: OSGymSandboxTemplate`, in the pool's namespace. Plural `osgymsandboxtemplates`, short name `osbt`. `metadata` is standard Kubernetes object metadata (`name`, `namespace`, `labels`). ### Spec | Field | Type | Default | Description | | --- | --- | --- | --- | | `spec.vmTemplate` | `object` | required | What each sandbox runs. | | `spec.vmTemplate.args` | `string[]` | none | Arguments (Kubernetes args semantics: they replace the image CMD and follow vmTemplate.command). Runs on pod runtimes (gvisor/macos). On runtime kubevirt it needs processMode: Run and a command (a VM image has no entrypoint to pass them to); without Run the gateway and the pool-operator refuse it. | | `spec.vmTemplate.claimSecrets` | `boolean` | none | Opt in to claim-scoped secret delivery (OSGymSandboxClaim spec.secretRef). The pool-operator gives every sandbox an operator-owned Secret, empty while the sandbox is warm, fills it when a claim binds and wipes it on release, so a warm sandbox receives its claimant's secrets without a restart. Pod runtimes mount it read-only as a directory (never subPath) at /run/cua, root-owned, mode 0600; the image keeps its default root user, and its root token-sync helper hands the token to a non-root driver. KubeVirt shares it over virtiofs as tag cua-claim-secrets (the EnableVirtioFsConfigVolumes feature gate is GA from KubeVirt v1.8; older KubeVirt needs it enabled); the guest image mounts that tag read-only at /run/cua. cua-env-driver images enable their await-token mode only when /run/cua is a mount point, so the key env-token becomes /run/cua/env-token. Claims with a secretRef fail on templates without this flag. | | `spec.vmTemplate.command` | `string[]` | none | `gvisor` and `macos` only: entrypoint of the sandbox container, overriding the image's. | | `spec.vmTemplate.containerDiskImage` | `string` | required | The image: a containerDisk for `kubevirt`, a container image for `gvisor` and `macos`. | | `spec.vmTemplate.cpuCores` | `integer` | `4` | Virtual CPUs per sandbox. At least 1. | | `spec.vmTemplate.env` | `map of string` | none | Plain environment variables for the sandbox's command (not for secrets: the values are stored in the template and visible to anyone who can read it). Names must match ^[A-Za-z_][A-Za-z0-9_]*$. $(NAME) references in command/args expand as in Kubernetes. Pod runtimes (gvisor/macos) set them on the sandbox container. On runtime kubevirt they need processMode: Run and go to /etc/cua/env (root, 0600) and the command's environment; values must be single-line there. Without Run the gateway and the pool-operator refuse env on kubevirt. | | `spec.vmTemplate.firmware` | `"bios" \| "efi"` | `"bios"` | VM firmware for `kubevirt`: `efi` for UEFI-only images such as Windows, `bios` for the Linux images. | | `spec.vmTemplate.imagePullPolicy` | `"Always" \| "IfNotPresent" \| "Never"` | none | `gvisor` and `macos` only: when to pull the image (default `IfNotPresent`). | | `spec.vmTemplate.imagePullSecret` | `string` | none | Secret in the pool's namespace with registry credentials for a private image. Omit it for a public image. | | `spec.vmTemplate.memory` | `string` | `"4Gi"` | Memory per sandbox, as a Kubernetes quantity such as `8Gi`. | | `spec.vmTemplate.nestedVirtualization` | `boolean` | `false` | Expose hardware virtualization (KVM) inside the VM. | | `spec.vmTemplate.nodeSelector` | `map of string` | none | `gvisor` and `macos` only: node selector of the sandbox pod. Fleet's default fits the runtime. | | `spec.vmTemplate.oidc` | `object` | none | Workload identity: Fleet puts an OIDC access token for your account at `/var/run/cua/oidc/token` in the guest and keeps it fresh, so the workload can federate into AWS (`sts:AssumeRoleWithWebIdentity`) or any other provider that trusts the token. | | `spec.vmTemplate.oidc.awsRegion` | `string` | `"us-west-2"` | AWS region for AWS SDK calls in the guest. | | `spec.vmTemplate.oidc.awsRoleArn` | `string` | none | When set, the guest's AWS SDK assumes this role with the token (environment and `~/.aws/config` are set up). | | `spec.vmTemplate.oidc.credentialsSecret` | `string` | required | Secret in the pool's namespace holding the client credentials (`client_id`, `client_secret`) that mint the token. | | `spec.vmTemplate.oidc.refreshIntervalSeconds` | `integer` | `1800` | How often the guest refreshes the token (half its one-hour lifetime by default). At least 60. | | `spec.vmTemplate.oidc.tokenUrl` | `string` | required | Token endpoint that mints the workload token. | | `spec.vmTemplate.probes` | `object (any JSON)` | none | Kubernetes `readinessProbe` and `livenessProbe` for the VM, so a sandbox counts as ready only when the guest serves. | | `spec.vmTemplate.processMode` | `"Legacy" \| "Run"` | none | How command, args and env reach the sandbox. Absent or Legacy: unchanged behavior (pod runtimes run them; KubeVirt ignores command and refuses args and env). Run: every runtime runs them with the same semantics. Pod runtimes set them on the sandbox container; KubeVirt writes /etc/cua/env (0600), /etc/cua/command.sh (0700) and cua-command.service into the sandbox's cloud-init Secret, which the guest re-reads on every boot, so warm VMs get it on return-to-pool too. KubeVirt Run needs a Linux guest with cloud-init and systemd. | | `spec.vmTemplate.runtime` | `"kubevirt" \| "macos" \| "gvisor"` | `"kubevirt"` | `kubevirt`: each sandbox is a VM booted from `containerDiskImage`. `gvisor`: a gVisor container of the image. `macos`: a macOS sandbox. For `gvisor` and `macos`, `firmware`, `cpuCores` and `memory` are advisory. | | `spec.vmTemplate.runtimeClassName` | `string` | none | `gvisor` and `macos` only: RuntimeClass of the sandbox pod. Fleet's default fits the runtime. | | `spec.vmTemplate.services` | `object[]` | none | Ports each sandbox publishes, as a Service named `<sandbox>-<name>`. Reach them through service URLs. | | `spec.vmTemplate.services[].name` | `string` | required | Service name suffix (sandbox name is prepended). | | `spec.vmTemplate.services[].protocol` | `"TCP" \| "UDP"` | `"TCP"` | `TCP` or `UDP`. | | `spec.vmTemplate.services[].targetPort` | `integer` | required | Port in the sandbox. 1 to 65535. | | `spec.vmTemplate.tolerations` | `object (any JSON)[]` | none | `gvisor` and `macos` only: tolerations of the sandbox pod. Fleet's default fits the runtime. | ### Fields the SDK adds Written as JSON beyond the published schema; Fleet reads them. | Field | Type | Description | | --- | --- | --- | | `spec.vmTemplate.claimSecrets` | `boolean` | Opts the template into per-claim Secrets: the Secret a claim names in `spec.secretRef` is delivered into the bound sandbox at `/run/cua/env-token`. | | `spec.vmTemplate.env` | `map of string` | Environment of the sandbox process. | | `spec.vmTemplate.args` | `string[]` | Arguments of the sandbox process. | | `spec.vmTemplate.sidecars` | `object[]` | Containers that run next to the sandbox, on every runtime (at most 8). They reach the sandbox at host `main`. | | `spec.vmTemplate.processMode` | `"Legacy" \| "Run"` | `Legacy`: pod runtimes run `command`, `args` and `env`; KubeVirt ignores `command` and refuses `args` and `env`. `Run`: every runtime runs them (KubeVirt through cloud-init). | ### Example What the SDK sends for a default pool of the cua Linux image (`PoolSpec::new`, built by `cua-fleet`). ```json { "apiVersion": "osgym.cua.ai/v1alpha1", "kind": "OSGymSandboxTemplate", "metadata": { "name": "my-pool", "namespace": "my-pool" }, "spec": { "vmTemplate": { "containerDiskImage": "ghcr.io/trycua/linux:24.04-disk", "runtime": "kubevirt", "services": [ { "name": "env", "protocol": "TCP", "targetPort": 3211 } ] } } } ``` ## REST ### Create template ```http POST /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxtemplates ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Body: the [template object](#the-template-object) (`application/json`). Returns: `200`, `201`, `202` with the [template object](#the-template-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### List templates ```http GET /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxtemplates ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Returns: `200` with `{"items": [...]}`, a list of the [template object](#the-template-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Get template ```http GET /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxtemplates/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200` with the [template object](#the-template-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Update template ```http PATCH /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxtemplates/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Body: a JSON merge patch of the [template object](#the-template-object) (`application/merge-patch+json`). Returns: `200` with the [template object](#the-template-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Delete template ```http DELETE /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxtemplates/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200`, `202`, `204` with no body. `404` is treated as success: the object is already gone. Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ## SDK Methods of the `Fleet` handle (`cua.fleet()`), with signatures in every language in the [Cua SDK reference](</docs/cua-sdk/reference>). | Method | Returns | Description | | --- | --- | --- | | [`Fleet.apply_pool_template(pool, spec)`](</docs/cua-sdk/reference/fleet#fleetapply_pool_template>) | none | Lays the set fields of `spec` over pool `pool`'s template and writes it (the pool's capacity is kept; a no-op when nothing differs). | | [`Fleet.check_pool_spec(pool, spec)`](</docs/cua-sdk/reference/fleet#fleetcheck_pool_spec>) | none | Compares the set fields of `spec` with pool `pool`'s template: `PoolSpecMismatch` (with a readable diff) when they differ. | | [`Fleet.list_templates(namespace)`](</docs/cua-sdk/reference/fleet#fleetlist_templates>) | `string[]` | Lists templates in a namespace as JSON resources. | ## Terraform Terraform manages the template as part of its pool: [`fleets_pool`](</docs/fleets/reference/pool#terraform>) writes a template named `<pool>-template` from these arguments. | Terraform argument | Template field | | --- | --- | | [`cpu_cores`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.cpuCores` | | [`memory`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.memory` | | [`container_disk_image`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.containerDiskImage` | | [`image_pull_secret`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.imagePullSecret` | | [`runtime`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.runtime` | | [`firmware`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.firmware` | | [`readiness_probe_json`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.probes` | | [`liveness_probe_json`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.probes` | | [`command`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.command` | | [`claim_secrets`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.claimSecrets` | | [`service {}`](</docs/fleets/reference/pool#terraform>) | `spec.vmTemplate.services` | --- # Claim A lease on one sandbox from a pool, until it is released or expires. On this page: [Object](#the-claim-object) · [REST](#rest) · [SDK](#sdk) ## The claim object `apiVersion: osgym.cua.ai/v1alpha1`, `kind: OSGymSandboxClaim`, in the pool's namespace. Plural `osgymsandboxclaims`, short name `osbc`. `metadata` is standard Kubernetes object metadata (`name`, `namespace`, `labels`). ### Spec | Field | Type | Default | Description | | --- | --- | --- | --- | | `spec.bindDeadline` | `integer` | none | Seconds the claim may stay Pending before it is marked Failed (Fleet's default and the SDK's value: 900). A Pending claim is what grows an autoscaled pool, so leave room for a cold boot. At least 0. | | `spec.lifecycle` | `object` | none | When the claim expires and what happens to its sandbox. | | `spec.lifecycle.autoRenew` | `boolean` | `false` | Set by Fleet on claims it renews itself. Leave it `false`. | | `spec.lifecycle.shutdownPolicy` | `string` | `"Retain"` | `Retain`: when the claim is deleted, the sandbox returns to the pool and restarts clean. `Delete`: the sandbox is destroyed and the pool creates a new one. | | `spec.lifecycle.shutdownTime` | `string` | none | Absolute UTC expiry (RFC 3339). Fleet deletes the claim, and releases its sandbox, once it passes. Renew by patching it forward. | | `spec.sandboxTemplateRef` | `object` | required | The template of the pool to claim from. | | `spec.sandboxTemplateRef.name` | `string` | required | OSGymSandboxTemplate the warm pool's OSGymSandboxes use. | | `spec.secretRef` | `object` | none | Secret in the claim's namespace whose keys the pool-operator delivers as files into the bound sandbox once it binds, without restarting it: each key becomes /run/cua/<key> (mode 0600), e.g. env-token -> /run/cua/env-token for cua-env-driver. The data is copied into an operator-owned per-sandbox Secret, never into the claim status, and is wiped when the claim is released. The name must be cua-claim-<claim name>, and the claim's template must set vmTemplate.claimSecrets, or the claim fails. | | `spec.secretRef.name` | `string` | required | Secret name (must start with cua-claim-). At most 253 characters. Matches `^cua-claim-[a-z0-9]([-a-z0-9]*[a-z0-9])?$`. | | `spec.ttlSecondsAfterCreated` | `integer` | none | Creation-age TTL in seconds. When absent, the resource is not automatically reaped based on age. At least 0. | | `spec.warmpool` | `string` | `"default"` | Where to take a sandbox from: `default` (the pool running the template), `none`, or a pool name. | ### Status Set by Fleet; read-only. | Field | Type | Default | Description | | --- | --- | --- | --- | | `status.conditions` | `object[]` | none | Kubernetes conditions of the claim. | | `status.conditions[].lastTransitionTime` | `string` | none | When the condition last changed (RFC 3339). | | `status.conditions[].message` | `string` | none | Human-readable detail. | | `status.conditions[].reason` | `string` | none | Machine-readable reason. | | `status.conditions[].status` | `string` | none | `True`, `False` or `Unknown`. | | `status.conditions[].type` | `string` | none | Condition type. | | `status.phase` | `string` | none | `Pending`, `Bound` or `Failed`. | | `status.sandbox` | `object` | none | The bound sandbox. | | `status.sandbox.name` | `string` | none | Name of the bound sandbox. | | `status.sandbox.service` | `string` | none | In-cluster DNS name of the sandbox's Service. | ### Fields the SDK adds Written as JSON beyond the published schema; Fleet reads them. | Field | Type | Description | | --- | --- | --- | | `spec.secretRef.name` | `string` | The claim's `cua-claim-<claim>` Secret. The template must set `claimSecrets`. | ### Example What the SDK sends for a default pool of the cua Linux image (`PoolSpec::new`, built by `cua-fleet`). ```json { "apiVersion": "osgym.cua.ai/v1alpha1", "kind": "OSGymSandboxClaim", "metadata": { "name": "claim-1", "namespace": "my-pool" }, "spec": { "bindDeadline": 900, "sandboxTemplateRef": { "name": "my-pool" } } } ``` ## REST ### Create claim ```http POST /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxclaims ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Body: the [claim object](#the-claim-object) (`application/json`). Returns: `200`, `201`, `202` with the [claim object](#the-claim-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### List claims ```http GET /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxclaims ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Returns: `200` with `{"items": [...]}`, a list of the [claim object](#the-claim-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Get claim ```http GET /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxclaims/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200` with the [claim object](#the-claim-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Renew claim ```http PATCH /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxclaims/{name} ``` Push the claim's `spec.lifecycle.shutdownTime` forward. That absolute expiry is the only liveness input the pool operator's claim reaper honors, so a holder that outlives its current lease must renew before the deadline passes or the bound sandbox is deleted underneath it. Deliberately narrower than a claim update: nothing else on the claim can be mutated through the SDK. | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Body: a JSON merge patch of the [claim object](#the-claim-object) (`application/merge-patch+json`). Returns: `200` with the [claim object](#the-claim-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Delete claim ```http DELETE /api/k8s/apis/osgym.cua.ai/v1alpha1/namespaces/{namespace}/osgymsandboxclaims/{name} ``` Delete the claim and, when it references a claim-scoped Secret (`secret_files`), that Secret too. The pool-operator also owner-refs the Secret to the claim, so garbage collection is the backstop. | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200`, `202`, `204` with no body. `404` is treated as success: the object is already gone. Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ## SDK Methods of the `Fleet` handle (`cua.fleet()`), with signatures in every language in the [Cua SDK reference](</docs/cua-sdk/reference>). | Method | Returns | Description | | --- | --- | --- | | [`Fleet.acquire(pool, name, ttl_seconds)`](</docs/cua-sdk/reference/fleet/claims#fleetacquire>) | `FleetSandbox` | Claims a sandbox from a pool and waits for it to bind. | | [`Fleet.acquire_with(pool, options)`](</docs/cua-sdk/reference/fleet/claims#fleetacquire_with>) | `FleetSandbox` | `Fleet::acquire` with claim options, including a per-claim env token (bounded wait for its delivery; `ClaimSecretsNotDelivered` releases the claim). | | [`Fleet.claim(pool, name, ttl_seconds)`](</docs/cua-sdk/reference/fleet/claims#fleetclaim>) | `FleetClaim` | Creates a claim without waiting. | | [`Fleet.attach_claim(namespace, name)`](</docs/cua-sdk/reference/fleet/claims#fleetattach_claim>) | `FleetSandbox` | Waits for a named claim to bind. | | [`Fleet.list_claims(namespace)`](</docs/cua-sdk/reference/fleet/claims#fleetlist_claims>) | `FleetClaim[]` | Lists claims in a namespace. | | [`Fleet.keep_alive(namespace, name, seconds)`](</docs/cua-sdk/reference/fleet/claims#fleetkeep_alive>) | `string` | Extends a claim's lease; returns the RFC 3339 shutdown time. | | [`Fleet.release(namespace, name)`](</docs/cua-sdk/reference/fleet/claims#fleetrelease>) | none | Releases a claim (missing claims are fine). | --- # Sandbox One running machine of a pool, bound to at most one claim. A sandbox is created by its pool, never directly. `status.sandbox` of a bound [claim](</docs/fleets/reference/claim>) names it; the SDK returns it as a `FleetSandbox` (namespace, claim, name and services). On this page: [Object](#the-sandbox-object) · [SDK](#sdk) ## The sandbox object `apiVersion: osgym.cua.ai/v1alpha1`, `kind: OSGymSandbox`, in the pool's namespace. Plural `osgymsandboxes`, short name `osbx`. `metadata` is standard Kubernetes object metadata (`name`, `namespace`, `labels`). ### Spec | Field | Type | Default | Description | | --- | --- | --- | --- | | `spec.vmTemplate` | `object` | required | What each sandbox runs. | | `spec.vmTemplate.args` | `string[]` | none | Arguments (Kubernetes args semantics: they replace the image CMD and follow vmTemplate.command). Runs on pod runtimes (gvisor/macos). On runtime kubevirt it needs processMode: Run and a command (a VM image has no entrypoint to pass them to); without Run the gateway and the pool-operator refuse it. | | `spec.vmTemplate.claimSecrets` | `boolean` | none | Opt in to claim-scoped secret delivery (OSGymSandboxClaim spec.secretRef). The pool-operator gives every sandbox an operator-owned Secret, empty while the sandbox is warm, fills it when a claim binds and wipes it on release, so a warm sandbox receives its claimant's secrets without a restart. Pod runtimes mount it read-only as a directory (never subPath) at /run/cua, root-owned, mode 0600; the image keeps its default root user, and its root token-sync helper hands the token to a non-root driver. KubeVirt shares it over virtiofs as tag cua-claim-secrets (the EnableVirtioFsConfigVolumes feature gate is GA from KubeVirt v1.8; older KubeVirt needs it enabled); the guest image mounts that tag read-only at /run/cua. cua-env-driver images enable their await-token mode only when /run/cua is a mount point, so the key env-token becomes /run/cua/env-token. Claims with a secretRef fail on templates without this flag. | | `spec.vmTemplate.command` | `string[]` | none | `gvisor` and `macos` only: entrypoint of the sandbox container, overriding the image's. | | `spec.vmTemplate.containerDiskImage` | `string` | required | The image: a containerDisk for `kubevirt`, a container image for `gvisor` and `macos`. | | `spec.vmTemplate.cpuCores` | `integer` | `4` | Virtual CPUs per sandbox. At least 1. | | `spec.vmTemplate.env` | `map of string` | none | Plain environment variables for the sandbox's command (not for secrets: the values are stored in the template and visible to anyone who can read it). Names must match ^[A-Za-z_][A-Za-z0-9_]*$. $(NAME) references in command/args expand as in Kubernetes. Pod runtimes (gvisor/macos) set them on the sandbox container. On runtime kubevirt they need processMode: Run and go to /etc/cua/env (root, 0600) and the command's environment; values must be single-line there. Without Run the gateway and the pool-operator refuse env on kubevirt. | | `spec.vmTemplate.firmware` | `"bios" \| "efi"` | `"bios"` | VM firmware for `kubevirt`: `efi` for UEFI-only images such as Windows, `bios` for the Linux images. | | `spec.vmTemplate.imagePullPolicy` | `"Always" \| "IfNotPresent" \| "Never"` | none | `gvisor` and `macos` only: when to pull the image (default `IfNotPresent`). | | `spec.vmTemplate.imagePullSecret` | `string` | none | Secret in the pool's namespace with registry credentials for a private image. Omit it for a public image. | | `spec.vmTemplate.memory` | `string` | `"4Gi"` | Memory per sandbox, as a Kubernetes quantity such as `8Gi`. | | `spec.vmTemplate.nestedVirtualization` | `boolean` | `false` | Expose hardware virtualization (KVM) inside the VM. | | `spec.vmTemplate.nodeSelector` | `map of string` | none | `gvisor` and `macos` only: node selector of the sandbox pod. Fleet's default fits the runtime. | | `spec.vmTemplate.oidc` | `object` | none | Workload identity: Fleet puts an OIDC access token for your account at `/var/run/cua/oidc/token` in the guest and keeps it fresh, so the workload can federate into AWS (`sts:AssumeRoleWithWebIdentity`) or any other provider that trusts the token. | | `spec.vmTemplate.oidc.awsRegion` | `string` | `"us-west-2"` | AWS region for AWS SDK calls in the guest. | | `spec.vmTemplate.oidc.awsRoleArn` | `string` | none | When set, the guest's AWS SDK assumes this role with the token (environment and `~/.aws/config` are set up). | | `spec.vmTemplate.oidc.credentialsSecret` | `string` | required | Secret in the pool's namespace holding the client credentials (`client_id`, `client_secret`) that mint the token. | | `spec.vmTemplate.oidc.refreshIntervalSeconds` | `integer` | `1800` | How often the guest refreshes the token (half its one-hour lifetime by default). At least 60. | | `spec.vmTemplate.oidc.tokenUrl` | `string` | required | Token endpoint that mints the workload token. | | `spec.vmTemplate.probes` | `object (any JSON)` | none | Kubernetes `readinessProbe` and `livenessProbe` for the VM, so a sandbox counts as ready only when the guest serves. | | `spec.vmTemplate.processMode` | `"Legacy" \| "Run"` | none | How command, args and env reach the sandbox. Absent or Legacy: unchanged behavior (pod runtimes run them; KubeVirt ignores command and refuses args and env). Run: every runtime runs them with the same semantics. Pod runtimes set them on the sandbox container; KubeVirt writes /etc/cua/env (0600), /etc/cua/command.sh (0700) and cua-command.service into the sandbox's cloud-init Secret, which the guest re-reads on every boot, so warm VMs get it on return-to-pool too. KubeVirt Run needs a Linux guest with cloud-init and systemd. | | `spec.vmTemplate.runtime` | `"kubevirt" \| "macos" \| "gvisor"` | `"kubevirt"` | `kubevirt`: each sandbox is a VM booted from `containerDiskImage`. `gvisor`: a gVisor container of the image. `macos`: a macOS sandbox. For `gvisor` and `macos`, `firmware`, `cpuCores` and `memory` are advisory. | | `spec.vmTemplate.runtimeClassName` | `string` | none | `gvisor` and `macos` only: RuntimeClass of the sandbox pod. Fleet's default fits the runtime. | | `spec.vmTemplate.services` | `object[]` | none | Ports each sandbox publishes, as a Service named `<sandbox>-<name>`. Reach them through service URLs. | | `spec.vmTemplate.services[].name` | `string` | required | Service name suffix (sandbox name is prepended). | | `spec.vmTemplate.services[].protocol` | `"TCP" \| "UDP"` | `"TCP"` | `TCP` or `UDP`. | | `spec.vmTemplate.services[].targetPort` | `integer` | required | Port in the sandbox. 1 to 65535. | | `spec.vmTemplate.tolerations` | `object (any JSON)[]` | none | `gvisor` and `macos` only: tolerations of the sandbox pod. Fleet's default fits the runtime. | ### Status Set by Fleet; read-only. | Field | Type | Default | Description | | --- | --- | --- | --- | | `status.message` | `string` | none | Human-readable detail, set when the OSGymSandbox is stuck (e.g. spec.vmTemplate.containerDiskImage missing). | | `status.phase` | `string` | none | `Pending`, `Ready` or `Terminating`. | | `status.ready` | `boolean` | none | Whether the sandbox is ready. | | `status.resetIssuedAt` | `string` | none | When Fleet began restarting the sandbox to return it to its pool. | | `status.resetVmiUid` | `string` | none | Identifies the VM instance that restart replaces. | | `status.runtime` | `string` | none | Runtime that provisioned the sandbox: `macos`, `gvisor`, or empty for `kubevirt`. | | `status.service` | `string` | none | In-cluster DNS name of the sandbox's Service. | | `status.vmName` | `string` | none | Name of the sandbox's VM (`kubevirt`). | ## SDK Methods of the `Fleet` handle (`cua.fleet()`), with signatures in every language in the [Cua SDK reference](</docs/cua-sdk/reference>). | Method | Returns | Description | | --- | --- | --- | | [`Fleet.attach_claim(namespace, name)`](</docs/cua-sdk/reference/fleet/claims#fleetattach_claim>) | `FleetSandbox` | Waits for a named claim to bind. | | [`Fleet.service_url(sandbox, service)`](</docs/cua-sdk/reference/fleet/claims#fleetservice_url>) | `string` | The gateway URL of a sandbox service (needs the Fleet bearer). | --- # Secret Secrets the SDK writes in a pool's namespace: a claim's env token and registry pull credentials. ## Claim secret Every claim the SDK makes carries a fresh env token for the sandbox's cua-spacesd. The SDK writes it as an Opaque Secret `cua-claim-<claim>` (key `env-token`, label `osgym.cua.ai/claim: <claim>`), then creates the claim with `spec.secretRef` naming it. Fleet delivers it into the bound sandbox at `/run/cua/env-token`; the token never crosses the network to the guest. After the claim binds, the SDK waits up to 90 seconds for the guest to accept the token, then releases the claim with [`ClaimSecretsNotDelivered`](</docs/fleets/reference/errors#claimsecretsnotdelivered>). ```json { "apiVersion": "v1", "data": { "env-token": "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZg==" }, "kind": "Secret", "metadata": { "labels": { "osgym.cua.ai/claim": "claim-1" }, "name": "cua-claim-claim-1", "namespace": "my-pool" }, "type": "Opaque" } ``` ## Registry pull secret Credentials for a private registry, written as a `kubernetes.io/dockerconfigjson` Secret `cua-registry-<16 hex>` (a hash of registry and user, so a new password updates the same Secret) and named by the template's `imagePullSecret`. ```json { "apiVersion": "v1", "data": { ".dockerconfigjson": "eyJhdXRocyI6eyJnaGNyLmlvIjp7ImF1dGgiOiJiMk4wYjJOaGREbzhkRzlyWlc0KyIsInBhc3N3b3JkIjoiPHRva2VuPiIsInVzZXJuYW1lIjoib2N0b2NhdCJ9fX0=" }, "kind": "Secret", "metadata": { "labels": { "cua.ai/registry-secret": "true" }, "name": "cua-registry-91c6584ad5bdef3a", "namespace": "my-pool" }, "type": "kubernetes.io/dockerconfigjson" } ``` ## REST Secrets are core Kubernetes objects behind the same proxy as the Fleet resources. ### Create a secret ```http POST /api/k8s/api/v1/namespaces/{namespace}/secrets ``` Body: the Secret. `409` means a Secret of that name exists (another claim attempt); the SDK does not overwrite it. ### Delete a secret ```http DELETE /api/k8s/api/v1/namespaces/{namespace}/secrets/{name} ``` `404` is treated as success. The SDK deletes a claim's Secret when it releases the claim. Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). --- # Service URL HTTP and WebSocket access to a sandbox's services, and shareable signed URLs. On this page: [REST](#rest) · [SDK](#sdk) ## REST ### Call a service ```http GET /api/svc/{namespace}/{service}/{path} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `service` | path | The Kubernetes Service of the sandbox service: `<sandbox>-<service>`. | | `path` | path | The upstream path, forwarded verbatim (it must start with `/`). Optional. | Returns: `200`. Upstream response. Errors: [`400`](</docs/fleets/reference/errors#http-400>), [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Create a signed URL ```http POST /api/signed-service-urls/{namespace} ``` Creates a temporary bearer link for one logical sandbox service. | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Body (`application/json`): | Field | Type | Default | Description | | --- | --- | --- | --- | | `claim` | `string` | required | The claim that holds the sandbox. | | `expiresInSeconds` | `integer` | required | Lifetime of the URL, in seconds. 60 to 86400. | | `label` | `string` | none | A label for the URL, at most 120 UTF-8 bytes. At most 120 characters. | | `logicalService` | `string` | required | The service as the template names it, for example `env`. | | `sandbox` | `string` | required | The sandbox name (`status.sandbox.name` of the claim). | | `service` | `string` | required | The sandbox's Service: `<sandbox>-<logicalService>`. | Returns: `201` with an object with `claim` (string), `createdAt` (string), `expiresAt` (string), `id` (string), `label` (string), `logicalService` (string), `namespace` (string), `revokedAt` (string), `sandbox` (string), `service` (string), `url` (string). Errors: [`400`](</docs/fleets/reference/errors#http-400>), [`401`](</docs/fleets/reference/errors#http-401>), [`404`](</docs/fleets/reference/errors#http-404>), [`500`](</docs/fleets/reference/errors#http-500>), [`503`](</docs/fleets/reference/errors#http-503>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### List signed URLs ```http GET /api/signed-service-urls/{namespace} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `claim` | query | The claim name. | Returns: `200` with an array; each item is an object with `claim` (string), `createdAt` (string), `expiresAt` (string), `id` (string), `label` (string), `logicalService` (string), `namespace` (string), `revokedAt` (string), `sandbox` (string), `service` (string), `url` (string). Errors: [`400`](</docs/fleets/reference/errors#http-400>), [`401`](</docs/fleets/reference/errors#http-401>), [`500`](</docs/fleets/reference/errors#http-500>), [`503`](</docs/fleets/reference/errors#http-503>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Revoke a signed URL ```http DELETE /api/signed-service-urls/{namespace}/{id} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `id` | path | The object id. | Returns: `204`. No Content. Errors: [`400`](</docs/fleets/reference/errors#http-400>), [`401`](</docs/fleets/reference/errors#http-401>), [`404`](</docs/fleets/reference/errors#http-404>), [`500`](</docs/fleets/reference/errors#http-500>), [`503`](</docs/fleets/reference/errors#http-503>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ## SDK Methods of the `Fleet` handle (`cua.fleet()`), with signatures in every language in the [Cua SDK reference](</docs/cua-sdk/reference>). | Method | Returns | Description | | --- | --- | --- | | [`Fleet.service_url(sandbox, service)`](</docs/cua-sdk/reference/fleet/claims#fleetservice_url>) | `string` | The gateway URL of a sandbox service (needs the Fleet bearer). | | [`Fleet.create_signed_service_url(sandbox, service, label, expires_in_seconds)`](</docs/cua-sdk/reference/fleet/claims#fleetcreate_signed_service_url>) | `FleetSignedUrl` | Mints a signed, shareable service URL. | --- # Image A build recipe Fleet turns into a runnable image, and its build status. On this page: [Object](#the-image-object) · [REST](#rest) · [SDK](#sdk) ## The image object The full Image resource. `apiVersion: images.cua.ai/v1alpha1`, `kind: Image`. The same resource `cua image build` reads from `image.json`. Fleet does not build `kind: container` recipes yet: a cloud Image with layers fails with a clear error. Build it locally and push it to a registry instead. ### Metadata and spec | Field | Type | Default | Description | | --- | --- | --- | --- | | `metadata.annotations` | `map of string` | none | Annotations on the Image resource. | | `metadata.labels` | `map of string` | none | Labels on the Image resource. | | `metadata.name` | `string` | required | Image name: a DNS label. 1 to 63 characters. Matches `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`. | | `metadata.namespace` | `string` | required | Namespace the image belongs to. 1 to 63 characters. Matches `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`. | | `spec.build` | `object` | none | Build resources and limits. | | `spec.build.diskSize` | `string` | `"40Gi"` | Disk size of the build machine (default `40Gi`). Matches `^[1-9][0-9]*(Gi\|Ti)$`. | | `spec.build.timeoutSeconds` | `integer` | `7200` | Build budget in seconds (default 7200). 60 to 86400. | | `spec.metadata` | `object` | none | Free-form metadata kept with the image. | | `spec.metadata.tags` | `map of string` | none | Tags, as key-value pairs. | | `spec.recipe` | `object` | required | What to build. | | `spec.recipe.distro` | `string` | required | Distribution of the base, for example `ubuntu`. 1 to 64 characters. | | `spec.recipe.env` | `map of string` | none | Environment variables of the image. | | `spec.recipe.files` | `object[]` | none | Uploaded files copied into the image. At most 128 items. | | `spec.recipe.files[].destination` | `string` | required | Absolute path in the image. At most 4096 characters. Matches `^/.*`. | | `spec.recipe.files[].source` | `object` | required | The uploaded file. | | `spec.recipe.files[].source.digest` | `string` | required | SHA-256 digest of the file. Matches `^sha256:[0-9a-f]{64}$`. | | `spec.recipe.files[].source.reference` | `string` | required | Upload reference from the image upload API (`uploads/<namespace>/<id>`). 1 to 2048 characters. Matches `^uploads/[a-z0-9]([-a-z0-9]*[a-z0-9])?/[A-Za-z0-9_-]+$`. | | `spec.recipe.files[].source.sizeBytes` | `integer` | required | Size in bytes. At least 0. | | `spec.recipe.from` | `string` | none | Registry base image (`kind: container`): layers are built on top of it and the result is an OCI rootfs. 1 to 1024 characters. | | `spec.recipe.fromPullSecret` | `string` | none | A `cua-registry-*` pull Secret in this namespace for a private base. At most 253 characters. Matches `^cua-registry-[a-z0-9]([-a-z0-9]*[a-z0-9])?$`. | | `spec.recipe.kind` | `"vm" \| "container"` | required | `vm`: a VM disk image. `container`: an OCI rootfs built on `from`. | | `spec.recipe.layers` | `object[]` | required | Build steps, run in order. At most 128 items. | | `spec.recipe.osType` | `"linux"` | required | Guest OS family. | | `spec.recipe.ports` | `integer[]` | none | Ports the image exposes. At most 32 items. | | `spec.recipe.version` | `string` | required | Distribution version, for example `24.04`. 1 to 64 characters. | ### Status Set by Fleet; read-only. | Field | Type | Default | Description | | --- | --- | --- | --- | | `status.artifacts` | `object` | none | What the build produced. | | `status.artifacts.oci` | `object` | none | The built OCI image. | | `status.artifacts.oci.digest` | `string` | required | Image digest. Matches `^sha256:[0-9a-f]{64}$`. | | `status.artifacts.oci.reference` | `string` | required | Image reference. 1 to 2048 characters. | | `status.artifacts.volumeSnapshot` | `object` | none | The built VM disk, as a volume snapshot. | | `status.artifacts.volumeSnapshot.name` | `string` | required | Name of the snapshot. 1 to 253 characters. | | `status.artifacts.volumeSnapshot.namespace` | `string` | required | Namespace of the snapshot. 1 to 63 characters. | | `status.buildIdentity` | `string` | none | Identifier of the build run. At most 63 characters. | | `status.conditions` | `object[]` | none | Kubernetes-style conditions of the build. | | `status.conditions[].lastTransitionTime` | `string` | required | When the condition last changed (RFC 3339). | | `status.conditions[].message` | `string` | none | Human-readable detail. At most 32768 characters. | | `status.conditions[].observedGeneration` | `integer` | none | The spec generation the condition describes. At least 0. | | `status.conditions[].reason` | `string` | required | Machine-readable reason. 1 to 128 characters. | | `status.conditions[].status` | `"True" \| "False" \| "Unknown"` | required | `True`, `False` or `Unknown`. | | `status.conditions[].type` | `string` | required | Condition type. 1 to 64 characters. | | `status.logs` | `object` | none | Where the build log is. | | `status.logs.livePodName` | `string` | none | Pod that streams the log while the build runs. At most 253 characters. | | `status.logs.retainedReference` | `string` | none | Where the log is kept after the build. At most 2048 characters. | | `status.observedGeneration` | `integer` | none | The spec generation this status describes. At least 0. | | `status.phase` | `"Pending" \| "Validating" \| "Building" \| "PushingOCI" \| "Importing" \| "Quiescing" \| "Snapshotting" \| "Ready" \| "Failed" \| "Cancelling"` | none | Build phase. | | `status.recipeDigest` | `string` | none | Digest of the recipe that was built. Matches `^sha256:[0-9a-f]{64}$`. | ### Layer types `spec.recipe.layers[]` holds build steps, each tagged by `type`. | `type` | Fields | | --- | --- | | `apt_install` | `packages` `string[]` | | `pip_install` | `packages` `string[]` | | `uv_install` | `packages` `string[]` | | `app_install` | `appId` `string` | | `run` | `command` `string` | ## REST ### Create image ```http POST /api/k8s/apis/images.cua.ai/v1alpha1/namespaces/{namespace}/images ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Body: the [image object](#the-image-object) (`application/json`). Returns: `201` with the [image object](#the-image-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### List images ```http GET /api/k8s/apis/images.cua.ai/v1alpha1/namespaces/{namespace}/images ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | Returns: `200` with `{"items": [...]}`, a list of the [image object](#the-image-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Get image ```http GET /api/k8s/apis/images.cua.ai/v1alpha1/namespaces/{namespace}/images/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200` with the [image object](#the-image-object). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Delete image ```http DELETE /api/k8s/apis/images.cua.ai/v1alpha1/namespaces/{namespace}/images/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `namespace` | path | The pool's namespace. A pool, its namespace and its template share one name. | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200`, `202`, `204` with no body. `404` is treated as success: the object is already gone. Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Presign image uploads ```http POST /api/image-uploads/presign ``` Body (`application/json`): `namespace` (string), `files` (object[]). Each of `files[]` has `digest` (string), `sizeBytes` (integer), `name` (string): the file's SHA-256 digest, size and name. Returns: `200` with `files[]`, one instruction per file: `digest` (string), `sizeBytes` (integer), `reference` (string), `upload` (object). `reference` is what an image recipe's `files[].source.reference` names. `upload` (`method` (string), `url` (string), `headers` (map of string)) is a presigned request to send the bytes with; it is absent when Fleet already has the file. Errors: see [Errors](</docs/fleets/reference/errors#http-status-codes>). ## SDK Methods of the `Fleet` handle (`cua.fleet()`), with signatures in every language in the [Cua SDK reference](</docs/cua-sdk/reference>). | Method | Returns | Description | | --- | --- | --- | | [`Fleet.create_image(namespace, manifest_json)`](</docs/cua-sdk/reference/fleet/images#fleetcreate_image>) | `string` | Creates an image resource (remote build) from a JSON manifest. | | [`Fleet.list_images(namespace)`](</docs/cua-sdk/reference/fleet/images#fleetlist_images>) | `string[]` | Lists image resources (JSON) in a namespace. | | [`Fleet.get_image(namespace, name)`](</docs/cua-sdk/reference/fleet/images#fleetget_image>) | `string` | Gets an image resource (JSON). | | [`Fleet.delete_image(namespace, name)`](</docs/cua-sdk/reference/fleet/images#fleetdelete_image>) | none | Deletes an image resource. | --- # Namespace The account-owned scope every pool, template, claim and secret lives in. On this page: [REST](#rest) ## REST ### Create a namespace ```http POST /api/namespaces ``` Body (`application/json`): | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | none | Namespace name: a DNS label. | Returns: `201` with an object with `createdAt` (string), `labels` (map of string), `name` (string), `status` (string). Errors: [`400`](</docs/fleets/reference/errors#http-400>), [`401`](</docs/fleets/reference/errors#http-401>), [`409`](</docs/fleets/reference/errors#http-409>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### List namespaces ```http GET /api/namespaces ``` Returns: `200` with an array; each item is an object with `createdAt` (string), `labels` (map of string), `name` (string), `status` (string). Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Get a namespace ```http GET /api/namespaces/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `200` with an object with `createdAt` (string), `labels` (map of string), `name` (string), `status` (string). Errors: [`400`](</docs/fleets/reference/errors#http-400>), [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`404`](</docs/fleets/reference/errors#http-404>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Delete a namespace ```http DELETE /api/namespaces/{name} ``` | Parameter | In | Description | | --- | --- | --- | | `name` | path | The object name: a DNS label (lowercase letters, digits and `-`, at most 63 characters). | Returns: `204`. No Content. Errors: [`400`](</docs/fleets/reference/errors#http-400>), [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>), [`404`](</docs/fleets/reference/errors#http-404>), [`502`](</docs/fleets/reference/errors#http-502>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). --- # API key OAuth client credentials that act for your user, for the SDK, CLI and Terraform. On this page: [REST](#rest) ## REST ### Create an API key ```http POST /api/user-keys ``` Body (`application/json`): | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | none | A name for the key. | | `scope` | `string[]` | none | Namespaces the key is limited to. | Returns: `201` with: | Field | Type | Default | Description | | --- | --- | --- | --- | | `client_id` | `string` | none | OAuth client ID. | | `client_secret` | `string` | none | OAuth client secret. Returned once; it cannot be read again. | | `name` | `string` | none | The key's name. | | `scope` | `string[]` | none | Namespaces the key is limited to. | | `token_url` | `string` | none | Token endpoint for the client-credentials grant. | Errors: [`400`](</docs/fleets/reference/errors#http-400>), [`401`](</docs/fleets/reference/errors#http-401>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### List API keys ```http GET /api/user-keys ``` Returns: `200` with an object with `keys` (object[]), `keys[].client_id` (string), `keys[].id` (string), `keys[].name` (string), `keys[].scope` (string[]). Errors: [`401`](</docs/fleets/reference/errors#http-401>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). ### Revoke an API key ```http DELETE /api/user-keys/{id} ``` | Parameter | In | Description | | --- | --- | --- | | `id` | path | The object id. | Returns: `204`. No Content. Errors: [`401`](</docs/fleets/reference/errors#http-401>), [`403`](</docs/fleets/reference/errors#http-403>) ([Errors](</docs/fleets/reference/errors#http-status-codes>)). --- # Terraform provider The trycua/fleets provider: settings, environment variables and resources. The `trycua/fleets` provider manages Fleet pools. Install it from the Terraform Registry with `terraform init`; OpenTofu works the same with `tofu`. ```hcl terraform { required_providers { fleets = { source = "trycua/fleets" } } } ``` ## Provider arguments Use either `access_token`, or all three of `client_id`, `client_secret` and `token_url` (an [API key](</docs/fleets/reference/api-key>)). An argument wins over its environment variable. | Argument | Type | Default | Environment variable | Description | | --- | --- | --- | --- | --- | | `endpoint` | `string` | required | `CYCLOPS_ENDPOINT` | Fleet API base URL, for example `https://run.cua.ai`. | | `access_token` | `string` | none | `CYCLOPS_ACCESS_TOKEN` | Bearer token. Sensitive. | | `client_id` | `string` | none | `CYCLOPS_CLIENT_ID` | Fleet user-key client ID. | | `client_secret` | `string` | none | `CYCLOPS_CLIENT_SECRET` | Fleet user-key client secret. Sensitive. | | `token_url` | `string` | none | `CYCLOPS_TOKEN_URL` | OAuth token endpoint returned with the Fleet user key. | `CUA_CLIENT_ID`, `CUA_CLIENT_SECRET`, `FLEETS_TOKEN` and `cua auth login` do not configure the provider. ## Resources | Resource | Manages | Reference | | --- | --- | --- | | `fleets_pool` | A Cua Fleet computer-use pool: an OSGymSandboxWarmPool and its OSGymSandboxTemplate. The pool name also owns its same-named namespace. | [Pool](</docs/fleets/reference/pool#terraform>) | The provider has no data sources. --- # Errors Fleet HTTP status codes and SDK errors, with causes and fixes. Every error the Fleet API and the Fleet SDK layer return, with its cause and fix. SDK languages surface them as [`CuaError`](</docs/cua-sdk/reference/errors>) cases, named in each entry. ## HTTP status codes Errors from the gateway carry `{"error": "<message>"}`; errors from the Kubernetes proxy carry a Kubernetes `Status` object with `message` and `reason`. | Status | Meaning | | --- | --- | | [`400`](#http-400) | The request is malformed: a missing field, a bad name, or a value out of range. | | [`401`](#http-401) | No token, or an expired or invalid one. | | [`403`](#http-403) | The token is valid but may not act on this namespace, or the proxy does not allow this request. On a read, Fleet answers 403 for a namespace that no longer exists; on a pool write, the pool name belongs to another account. | | [`404`](#http-404) | The object does not exist. | | [`409`](#http-409) | An object with that name exists. | | [`500`](#http-500) | Fleet failed to process the request. | | [`502`](#http-502) | Fleet could not reach the backend behind the gateway (the Kubernetes API or the sandbox service). | | [`503`](#http-503) | The feature is not configured in this environment (for example signed service URLs). | ### HTTP 400 The request is malformed: a missing field, a bad name, or a value out of range. Fix: Check the body against the object reference; the `error` message names the field. ### HTTP 401 No token, or an expired or invalid one. Fix: Send `Authorization: Bearer <token>` with a fresh token from your API key. ### HTTP 403 The token is valid but may not act on this namespace, or the proxy does not allow this request. On a read, Fleet answers 403 for a namespace that no longer exists; on a pool write, the pool name belongs to another account. Fix: Check the namespace and name. For a new pool, pick another pool name (names are unique across accounts). ### HTTP 404 The object does not exist. Fix: Check the name and namespace; list the collection to see what exists. ### HTTP 409 An object with that name exists. Fix: Use another name, or read the existing object. ### HTTP 500 Fleet failed to process the request. Fix: Retry; report persistent failures with the request and time. ### HTTP 502 Fleet could not reach the backend behind the gateway (the Kubernetes API or the sandbox service). Fix: Retry. For a service URL, check that the sandbox is bound and its service is listening. ### HTTP 503 The feature is not configured in this environment (for example signed service URLs). Fix: Use the SDK service URL instead, or contact support. ## SDK errors Raised by the Fleet layer of the SDK (`cua_fleet::Error`). | Error | Surfaces as | | --- | --- | | [`MissingCredentials`](#missingcredentials) | `CuaError.ProviderNotConfigured` | | [`InvalidArgument`](#invalidargument) | `CuaError.InvalidArgument` | | [`Sdk`](#sdk) | `CuaError.NotFound` (a missing object), else `CuaError.Fleet` | | [`AdmissionDenied`](#admissiondenied) | `CuaError.FleetAdmissionDenied` | | [`CreditExhausted`](#creditexhausted) | `CuaError.CloudCreditExhausted` | | [`UnknownService`](#unknownservice) | `CuaError.NotFound` | | [`Timeout`](#timeout) | `CuaError.Timeout` | | [`Env`](#env) | `CuaError.SpacesdNotAvailable`, `CuaError.Unauthenticated`, `CuaError.InvalidArgument`, `CuaError.Timeout`, `CuaError.Transport`, `CuaError.Env` | | [`Unsupported`](#unsupported) | `CuaError.Unsupported` | | [`PoolSpecMismatch`](#poolspecmismatch) | `CuaError.PoolSpecMismatch` | | [`ClaimSecretsNotDelivered`](#claimsecretsnotdelivered) | `CuaError.ClaimSecretsNotDelivered` | ### MissingCredentials No Fleet credentials: neither a `cua auth login` session, `FLEETS_TOKEN` nor `CUA_CLIENT_ID` + `CUA_CLIENT_SECRET` (`MISSING_CREDENTIALS`). Run `cua auth login`, set the client credentials, or run the sandbox locally. ```text Fleet credentials missing: run `cua auth login` or set CUA_CLIENT_ID/CUA_CLIENT_SECRET, or pass local=True ``` Surfaces as `CuaError.ProviderNotConfigured`. ### InvalidArgument A value the SDK checks before calling Fleet is invalid (a name that is not a DNS label, a bad secret, an unusable spec). The message names the value; fix it and retry. ```text invalid argument: <0> ``` Surfaces as `CuaError.InvalidArgument`. ### Sdk The Fleet API call failed: an HTTP status, a transport or token failure, or an unexpected body (`SdkError`, listed under Fleet API client errors). Surfaces as `CuaError.NotFound` (a missing object), else `CuaError.Fleet`. ### AdmissionDenied Fleet's admission refused a write: a sandbox size or pool size over this account's limits, or a template its policy does not admit. The message is Fleet's own and names the limit. Change the value, or ask Cua support to raise the account's limits. ```text Fleet refused <operation> (HTTP <status>): <message> ``` Surfaces as `CuaError.FleetAdmissionDenied`. ### CreditExhausted The account is out of Cua Cloud credit: no credit left, and no card or plan (Fleet answers HTTP 402 `cloud_credit_exhausted`). Running sandboxes keep running; new ones are refused. Add credit (a plan or a card for pay as you go) on the page at `billing_url`. ```text <message> Add credit at <billing_url> ``` Surfaces as `CuaError.CloudCreditExhausted`. ### UnknownService The claim's sandbox does not expose the service. Use one of the services its template declares (`available`), or add the service to the pool's template. ```text sandbox <sandbox> exposes no service <service> (available: <available>) ``` Surfaces as `CuaError.NotFound`. ### Timeout A wait ran out: a pool that never became ready, or a claim that never bound. Check the pool's ready replicas and capacity, then retry with a longer timeout. ```text timed out: <0> ``` Surfaces as `CuaError.Timeout`. ### Env The claimed sandbox's cua-spacesd failed or is unreachable. Surfaces as `CuaError.SpacesdNotAvailable`, `CuaError.Unauthenticated`, `CuaError.InvalidArgument`, `CuaError.Timeout`, `CuaError.Transport`, `CuaError.Env`. ### Unsupported The runtime or this Fleet release cannot run what the spec asks (for example `env` on a KubeVirt pool). The message names the field; drop it or pick a runtime that supports it. ```text unsupported: <0> ``` Surfaces as `CuaError.Unsupported`. ### PoolSpecMismatch Surfaces as `CuaError.PoolSpecMismatch`. ### ClaimSecretsNotDelivered Surfaces as `CuaError.ClaimSecretsNotDelivered`. ## Fleet API client errors The Fleet API client under the SDK (`SdkError`). They reach SDK callers through `Sdk` above: as `CuaError.NotFound` for a missing object, otherwise `CuaError.Fleet`, with this message. ### Configuration ```text invalid configuration: <reason> ``` ### InvalidResourceName ```text invalid <field> <value>: <reason> ``` ### Transport ```text transport error: <reason> ``` ### Token ```text token error: <reason> ``` ### Body ```text invalid response body: <reason> ``` ### Status ```text <operation> returned HTTP <status>: <body> ``` ### SignedServiceUrlsUnavailable ```text signed service URLs are not configured in this environment ``` ### SdkError.UnknownService ```text unknown sandbox service <requested>; available services: <available> ``` ### InvalidServicePath ```text invalid service path: <path> ``` ### ClaimFailed ```text claim entered terminal phase <phase>: <status> ``` ### ClaimTimeout ```text claim did not bind before the polling limit ``` ### PoolAccessDenied --- # Cua Bench Run the Cua Bench registry tasksets against any computer-use agent on local or cloud desktop sandboxes, or write your own verifiable tasks. Cua Bench (`cb`, MIT) runs verifiable computer-use tasks on real desktop sandboxes and scores any agent on them. Start from a taskset in the **[Cua Bench registry](https://cua.ai/cuabench/registry)**: one command runs it on this machine or in the cloud. ```bash test="cli-shape" id="bench-index-run" cb run cua-bench-basic --agent cua-agent --model anthropic/claude-sonnet-4-20250514 ``` [An open-source registry and runner for computer-use tasks](<https://github.com/user-attachments/assets/35bf5156-d326-4484-ad1a-87a7991c85f6>) A task goes from the registry through variations, agent adapters, the CLI runner, and self-hosted execution. ## How a task runs ```text output dataset -> task -> variant -> session -> trajectory -> evaluator -> reward ``` A task module (`main.py`) has four functions: | Function | Does | |----------|------| | `@tasks_config` | Returns the variants: same objective, different prompt, data, theme or OS. | | `@setup_task` | Prepares the variant's environment and opens the apps. | | `@solve_task` | Optional oracle: one known way to succeed. A failing oracle means a broken task, not a bad agent. | | `@evaluate_task` | Inspects the final state and returns rewards. It scores an oracle, a human or an agent the same way. | Tasks run in a real sandbox from the [cua SDK](</docs/cua-sdk>) on this machine (`cb run --on local`, the default). ## Next - [Quickstart](</docs/cua-bench/quickstart>): run a registry taskset, read the score, add an agent. - [Run a benchmark](</docs/cua-bench/guides/benchmarks>): every registry taskset, versions, pass@k, local or cloud. - [Write a task](</docs/cua-bench/guides/write-a-task>): the task module contract. - [Adapter benchmarks](</docs/cua-bench/guides/adapter-benchmarks>): OSWorld-Verified, MiniWoB++, WebVoyager and other adapters. [Task registry](<https://cua.ai/cuabench/registry>) Browse the tasksets, their tasks and previews. [Source](<https://github.com/trycua/cua/tree/main/libs/cua-bench>) The package, tasksets and CLI. --- # Quickstart Run a Cua Bench registry taskset on a local desktop sandbox with one command, read the results, then run it with an agent. Pick a taskset from the [Cua Bench registry](https://cua.ai/cuabench/registry), run it on a local desktop sandbox, and read the score. No API key until the agent step. ## Prerequisites - Python 3.12 or 3.13 and [uv](https://docs.astral.sh/uv/). - A local container runtime (Docker; gVisor when installed): check with `cua runtime doctor`. ## Install ```bash skip="host-install" uv tool install cua-bench ``` ## Run a registry taskset List the tasksets, then run one task of `cua-bench-basic` with its built-in oracle: ```bash test="cli-shape" id="bench-quickstart-run" cb dataset list cb run cua-bench-basic --task-filter click-button --max-variants 1 ``` `cb` fetches the taskset at its pinned version, starts a Linux desktop sandbox, opens the task's app as a window on it, runs the oracle, scores the result (`reward=1`) and releases the sandbox. A failing oracle means the task or your setup is broken, not the agent. ## Read the results ```bash test="cli-shape" id="bench-quickstart-results" cb run list cb run info <run-id> ``` Each variant's `result.json`, `run.log` and `trajectory.json` land in `~/.local/share/cua-bench/runs/<run-id>/`, next to a `summary.json`. ## Run it with an agent ```bash test="cli-shape" id="bench-quickstart-agent" cb run cua-bench-basic --agent cua-agent --model anthropic/claude-sonnet-4-20250514 -j 4 ``` It runs every task on this machine (`--on local` is the default). ## Try a task yourself `cb interact` sets up a task on a desktop you can see, waits while you solve it, then evaluates it and releases the sandbox: ```bash test="cli-shape" id="bench-quickstart-interact" cb interact click-button --dataset cua-bench-basic ``` ## Clean up Run results stay in `~/.local/share/cua-bench/runs` (a warning past 10 GiB, `CUA_BENCH_WARN_SIZE`). Keep fewer with `--keep-runs N`, `--max-age DAYS` or `--max-results-size SIZE` on `cb run`, or: ```bash test="cli-shape" id="bench-quickstart-prune" cb prune --runs --keep 10 ``` ## Next - [Run a benchmark](</docs/cua-bench/guides/benchmarks>): every registry taskset, selection, versions and pass@k. - [Write a task](</docs/cua-bench/guides/write-a-task>): make your own. - [Adapter benchmarks](</docs/cua-bench/guides/adapter-benchmarks>): OSWorld-Verified and other adapters. --- # Run a benchmark Pick a taskset from the Cua Bench registry, run it with one cb run command on a local or cloud desktop sandbox, and read the results. A benchmark in Cua Bench is a registry taskset: a versioned set of desktop tasks, each with a setup, an oracle and an evaluator. Browse them at **[cua.ai/cuabench/registry](https://cua.ai/cuabench/registry)** and run one by name. ## 1. Pick a taskset ```bash test="cli-shape" id="bench-registry-list" cb dataset list ``` | Taskset | Tasks | What the agent does | Desktop image | | --- | --: | --- | --- | | `cua-bench-basic` | 13 (68 variants) | Click, type, drag, pick and fill in small desktop apps | `bench-web` (Linux desktop with bench-ui) | | `cua-bench-kicad` | 25 | Expert KiCad schematic edits, scored by netlist | `ghcr.io/trycua/linux` | | `cua-bench-workflows` | 2 (52 variants) | Multi-step OpenShot and Unity workflows | `ghcr.io/trycua/linux` | Every task runs on a full desktop sandbox. Tasks that open their own app (the `cua-bench-basic` widgets) start it as a pywebview window on that desktop; the agent sees the screen and drives mouse and keyboard, never a DOM. A taskset name resolves through the index bundled with `cb` to a pinned git commit, fetched once into `~/.cua/cbregistry`. `cua-bench-kicad` and `cua-bench-workflows` install their app with `sudo apt` during setup, which a local gVisor sandbox does not allow: run them locally with `--runtime runc`. The workflows have no oracle; check them with `--noop` or an agent. ```bash test="cli-shape" id="bench-registry-kicad" cb run cua-bench-kicad --task-filter 154d0750 --runtime runc ``` ## 2. Run it ```bash test="cli-shape" id="bench-registry-oracle" cb run cua-bench-basic --task-filter click-button --max-variants 1 ``` With no agent, `cb run` runs each task's oracle: a quick check that the taskset works on your machine (`reward=1`). Then give it an agent: ```bash test="cli-shape" id="bench-registry-agent" cb run cua-bench-basic --agent cua-agent --model anthropic/claude-sonnet-4-20250514 -j 4 ``` | Flag | Does | | --- | --- | | `<name>@<version>` | Pins a taskset version (`cua-bench-basic@1.0`); a plain name is the newest. | | `--task-filter` | Glob over task names (`'click*,drag*'`). | | `--max-variants N` | First N variants of each task. | | `-j N` | Variants running at once. | | `--attempts N` | Runs every variant N times; `summary.json` reports pass@k. | | `--dry-run` | Prints each variant's image, kind and backend; starts nothing. | `--on local` (the default) runs sandboxes on this machine (Docker, gVisor when installed; check with `cua runtime doctor`). Agents: `cua-agent` (any model the [Cua agent](</docs/cua-sdk>) supports) or your own with `--agent-import-path module:Class`. `--model` needs that provider's API key in the environment. ## 3. Read the results ```bash test="cli-shape" id="bench-registry-results" cb run list cb run info <run-id> ``` Each run writes `summary.json` (mean reward, pass@k) and one directory per variant with `result.json`, `run.log` and the agent's `trajectory.json` (ATIF, with screenshots), under `~/.local/share/cua-bench/runs`. Every key is in [Results](</docs/cua-bench/reference/results>). ## Try a task by hand `cb interact` opens one task's desktop, waits while you solve it, then scores it: ```bash test="cli-shape" id="bench-registry-interact" cb interact click-button --dataset cua-bench-basic ``` ## Your own registry `CUA_BENCH_REGISTRY` points `cb` at another index (a path or URL). Each entry pins a directory of tasks to a commit; `image` names the desktop image for tasks that set none: ```json test="config" id="bench-registry-index" { "datasets": [ { "name": "my-tasks", "version": "1.0", "description": "Our internal desktop tasks", "git_url": "https://github.com/acme/bench-tasks.git", "git_commit_id": "0123456789abcdef0123456789abcdef01234567", "path": "tasks", "image": "BENCH_WEB" } ] } ``` `cb dataset list` shows its entries; Harbor-format entries (a `tasks` list pinned per task) are fetched as-is. To write tasks for it, see [Write a task](</docs/cua-bench/guides/write-a-task>). ## Next - [Adapter benchmarks](</docs/cua-bench/guides/adapter-benchmarks>): OSWorld-Verified, MiniWoB++, WebVoyager, Online-Mind2Web, WebGym and the grounding sets, through adapters. - [`cb run` reference](</docs/cua-bench/reference/cli/run>) --- # Write a task The Cua Bench task module: variants, setup, oracle and evaluator, the computer configuration, and the session operations they use. A task is a directory with a `main.py` whose decorated functions `cb` discovers. `cb task create <name>` scaffolds one; this is the whole contract. ## A minimal task This is the task `cb task create` scaffolds: each variant asks for one word in a file, in the Linux sandbox. ```python skip="no-runner" import cua_bench as cb ANSWER = "/tmp/answer.txt" WORDS = ["hello", "bench"] @cb.tasks_config(split="train") def load(): return [ cb.Task( description=f"Create the file {ANSWER} containing only the word '{word}'.", metadata={"word": word}, computer={ "provider": "native", "setup_config": {"os_type": "linux", "width": 1024, "height": 768}, }, ) for word in WORDS ] @cb.setup_task(split="train") async def start(task_cfg: cb.Task, session: cb.DesktopSession): await session.run_command(f"rm -f {ANSWER}", check=False) @cb.evaluate_task(split="train") async def evaluate(task_cfg: cb.Task, session: cb.DesktopSession) -> list[float]: if not await session.file_exists(ANSWER): return [0.0] answer = (await session.read_file(ANSWER)).strip() return [1.0 if answer == task_cfg.metadata["word"] else 0.0] @cb.solve_task(split="train") async def solve(task_cfg: cb.Task, session: cb.DesktopSession): await session.write_file(ANSWER, task_cfg.metadata["word"]) ``` | Decorator | Signature | Returns | |-----------|-----------|---------| | `@cb.tasks_config` | `load()` | `list[cb.Task]`, the variants, indexed from 0 by `--variant-id`. Runs when the module loads. | | `@cb.setup_task` | `async (task_cfg, session)` | Nothing. Prepares the selected variant. | | `@cb.solve_task` | `async (task_cfg, session)` | Nothing. Optional oracle, run by `--oracle`. | | `@cb.evaluate_task` | `async (task_cfg, session)` | Rewards, commonly `list[float]`; several components are allowed. | Each decorator takes a split, positionally or as `split=` (default `train`). Keep the oracle and the evaluator independent: the evaluator defines success, the oracle is one way to reach it. ## `cb.Task` | Field | Description | |-------|-------------| | `description` | The objective shown to the agent (required). | | `task_id` | Stable identifier. | | `metadata` | Variant data your lifecycle functions read. | | `computer` | `{"provider": ..., "setup_config": {...}}`. | `provider` is `native` (a cua SDK sandbox; the retired `simulated` provider runs on the `bench-web` Linux desktop, which ships bench-ui, with a warning). `setup_config` takes: | Key | Meaning | |-----|---------| | `os_type` | `linux`, `windows`, `macos` (local only) or `android` (local only). | | `image` | Registry image. `--image` overrides it; else `CUA_BENCH_IMAGE`, else the SDK default. | | `runtime` | The kind: `container` (Linux only) or `vm`. `--kind` overrides it. | | `server_port` | Port of a daemon the image serves itself, used for readiness. | | `width`, `height` | Screen size. | Setup, oracle or agent, and evaluation run in the `cb` process and drive a native sandbox through cua-spacesd, so the image must ship it (the default `linux` image does). ## Session operations `cb.DesktopSession` operations are async; support depends on the provider. | Operation | Result | |-----------|--------| | `run_command(cmd, check=...)` | Runs a shell command in the sandbox. | | `read_file(path)`, `write_file(path, content)`, `file_exists(path)` | Files in the sandbox. | | `launch_window(html=..., title=...)` | Opens HTML, a folder or a URL (images that ship bench-ui); returns a window id. | | `execute_javascript(pid, script)` | Evaluates JavaScript in a task window. | | `click_element(pid, selector)` | Scrolls a CSS selector into view and clicks it; an `<option>` is selected. | | `execute_action(action)` | Sends a Cua Bench input action. | | `screenshot()` | PNG bytes. | ## Check it Scaffold a task, check its functions, then score it with its oracle in a local Linux sandbox: ```bash test="cli-shape" id="bench-task-create" cb task create first-task cb task info first-task cb run first-task --variant-id 0 --oracle ``` ![cb task info: a native Linux task with 2 variants and setup, solve and evaluate](</docs-assets/img/cua-bench/cb-task-info.png>) A missing check mark in `info` means a function is not decorated with the split being loaded. To solve it yourself, `cb interact first-task --variant-id 0` opens the sandbox; press Enter to evaluate and release it: ![cb interact: setup complete, evaluation result 1.0, sandbox released](</docs-assets/img/cua-bench/cb-interact.png>) ![The task sandbox mid-task: a terminal writing hello to /tmp/answer.txt](</docs-assets/img/cua-bench/cb-interact-desktop.png>) To publish tasks as a taskset, pin their directory in a registry index: see [Your own registry](</docs/cua-bench/guides/benchmarks#your-own-registry>). --- # Adapter benchmarks Run established benchmarks through Cua Bench adapters: OSWorld-Verified, MiniWoB++, WebVoyager, Online-Mind2Web, WebGym and the grounding sets, locally or in the cloud. Cua Bench's own tasksets come from the [registry](</docs/cua-bench/guides/benchmarks>). This page covers established benchmarks from other suites, which run through adapters in `libs/cua-bench/tasks/` against converted benchmark images. Every adapter runs with one command: `cb run dataset <tasks> --agent <agent> --model <model>`. Converted images come in two variants, a container (`<ver>`) and a VM disk (`<ver>-disk`): `--kind container|vm` picks one. Both run cua-spacesd, and every result records `image_ref`, `image_variant`, `image_digest` and `arch`. ## Desktop (1) | Benchmark | What | Tasks | Image | | --- | --- | --: | --- | | [OSWorld-Verified](#osworld-verified) | Ubuntu desktop apps, real evaluators | 369 | `bench-osworld:verified` | ## Browser (4) | Benchmark | What | Tasks | Image | | --- | --- | --: | --- | | [MiniWoB++](https://github.com/Farama-Foundation/miniwob-plusplus) | Synthetic web widgets, offline | 130 | [`bench-web:1.0`](#bench-web) | | [WebVoyager](https://github.com/MinorJerry/WebVoyager) | Live sites, LLM judge | 643 | [`bench-web:1.0`](#bench-web) | | [Online-Mind2Web](https://github.com/OSU-NLP-Group/Online-Mind2Web) | Live sites, WebJudge | 300 | [`bench-web:1.0`](#bench-web) | | [WebGym](https://github.com/microsoft/webgym) | Live-web test split | 1,167 | [`bench-web:1.0`](#bench-web) | ## Grounding (2) | Benchmark | What | Items | Image | | --- | --- | --: | --- | | [OSWorld-G](https://github.com/xlang-ai/OSWorld-G) | Click the described element | 564 | none | | [ScreenSpot-Pro](https://huggingface.co/datasets/likaixin/ScreenSpot-Pro) | High-resolution professional app screenshots | ~1,581 | none | Images live under `ghcr.io/trycua/`. How they are built: [Convert benchmark images](</docs/cua-bench/guides/convert-benchmark-images>). ## OSWorld-Verified The upstream Ubuntu 22.04 disk ([xlang-ai/OSWorld](https://github.com/xlang-ai/OSWorld), Apache-2.0) with cua-spacesd added. | | | | --- | --- | | Image | `ghcr.io/trycua/bench-osworld:verified`, `:verified-disk` | | Arch | amd64 | | Server | OSWorld control server on `5000` (setup and evaluation) | | Needs | outbound network in the guest; KVM for local VMs; `cua-bench[osworld]` | ```bash skip="placeholder" pip install "cua-bench[osworld]" cb run dataset libs/cua-bench/tasks/osworld --agent <agent> --model <model> --kind vm cb run dataset libs/cua-bench/tasks/osworld --agent <agent> --model <model> --kind container ``` OSWorld's `SetupController` and evaluators run on your machine against the guest's server. `CUA_BENCH_OSWORLD_SPLIT` picks the list (`test_all`, `test_small`, `test_nogdrive`, `parity`). - The container variant has no systemd: 13 tasks that need systemd, logind, GNOME settings daemons or audio are marked `container_fidelity: "degraded"`. Run those with `--kind vm`. - The image bundles Google Chrome and VS Code under their own terms. You can also build it yourself with the pipeline. - The 8 Google Drive tasks need OSWorld's OAuth setup (upstream `SETUP_GUIDELINE.md`). ### Your own OSWorld disk with a Cua Driver MCP service To keep the upstream disk as is and add only a `cua-driver mcp` service (Streamable HTTP on `3000/mcp`), use the recipe in [`samples/python/osworld-image`](https://github.com/trycua/cua/tree/main/samples/python/osworld-image). It edits an overlay with `virt-customize` (no guest network, `base.qcow2` untouched) and packages `disk.img` as a containerDisk. This disk has no cua-spacesd; the SDK's OSWorld adapter (`agent_type="osworld"`) talks to the control server on `5000` instead. ```bash skip="placeholder" docker build -t osworld-guestfs:local osworld-image/guestfs docker run --rm --device /dev/kvm -v "$PWD:/work" osworld-guestfs:local \ bash /work/osworld-image/build-osworld-cua.sh # -> disk.img export OSWORLD_IMAGE="<registry>/<repo>@sha256:<digest>" # after pushing the containerDisk uv run samples/python/3_osworld_fleet.py # OSWORLD_LOCAL=1 boots it under QEMU ``` - Wrap OSWorld setup and evaluation in timeouts: its controller has none. - Downscale the 1920x1080 screenshot to about 1280 px for the model and scale clicks back. - Local VM guests have outbound network by default (`network="none"` turns it off). - A cloud sandbox that never becomes ready: the probe is TCP on `5000`, so the image is still pulling or the guest has no DHCP lease. `cua fleet pools ls` shows its capacity. ## bench-web `ghcr.io/trycua/bench-web:1.0` and `:1.0-disk` (public, amd64 and arm64): a Linux desktop with Chromium and a DevTools controller (`bench-web-ctl` on `7000`) serving the browser benchmarks. It also ships bench-ui (pywebview), so it is the desktop image for the registry's [desktop tasksets](</docs/cua-bench/guides/benchmarks>). ### MiniWoB++ MIT. Needs nothing; scored by the page's own reward. Select with `CUA_BENCH_MINIWOB_TASKS` and `CUA_BENCH_MINIWOB_SEEDS`. ```bash skip="placeholder" cb run dataset libs/cua-bench/tasks/miniwob --agent <agent> --model <model> CUA_BENCH_MINIWOB_TASKS=click-button,enter-text cb run dataset libs/cua-bench/tasks/miniwob --oracle --kind vm ``` ### WebVoyager Apache-2.0. Needs guest network and `OPENAI_API_KEY`; scored by WebVoyager `auto_eval` over the final screenshot and answer. Select with `CUA_BENCH_WEBVOYAGER_SITES`. ```bash skip="placeholder" CUA_BENCH_WEBVOYAGER_SITES=Allrecipes,ArXiv cb run dataset libs/cua-bench/tasks/webvoyager \ --agent <agent> --model <model> --max-variants 10 ``` ### Online-Mind2Web MIT, data CC-BY-4.0. Needs guest network, `OPENAI_API_KEY`, the [gated task list](https://huggingface.co/datasets/osunlp/Online-Mind2Web) and `hf auth login`; scored by WebJudge. Select with `CUA_BENCH_OM2W_LEVEL` (`easy`, `medium`, `hard`). ```bash skip="placeholder" CUA_BENCH_OM2W_LEVEL=easy cb run dataset libs/cua-bench/tasks/online_mind2web \ --agent <agent> --model <model> --max-variants 10 ``` ### WebGym MIT, data CDLA-Permissive-2.0. Needs guest network and `OPENAI_API_KEY`; scored by an LLM judge. Select with `--max-variants`. ```bash skip="placeholder" cb run dataset libs/cua-bench/tasks/webgym --agent <agent> --model <model> --kind vm ``` `--oracle` runs scripted solutions where a set has them; `--noop` scores an untouched page. ## Screenshot-only sets These score grounding on screenshots; no sandbox starts. Images are fetched once, checked against pinned hashes and cached. - OSWorld-G (Apache-2.0): the first click must land in the target box or polygon; refusal items score only when the agent reports the task infeasible. - ScreenSpot-Pro (MIT): one point action per item, from pro apps on Windows, macOS and Linux; fetched per item, so a subset never downloads the 3.3 GB set. ```bash skip="placeholder" cb run dataset libs/cua-bench/tasks/osworld_g --agent <agent> --model <model> CUA_BENCH_SSPRO_APPS=excel_macos cb run dataset libs/cua-bench/tasks/screenspot_pro --oracle ``` --- # Convert benchmark images Turn an upstream benchmark environment into one Cua image with a container and a VM variant, publish it, and check eval parity. The pipeline lives in `scripts/bench-images/`. Each benchmark is described once in `libs/images/bench/<id>/bench.json` (pinned sources, license, redistribution class, server, variants), then fetched, built as both variants, smoked, and published. ```bash skip="no-runner" python3 scripts/bench-images/bench.py validate python3 scripts/bench-images/bench.py fetch osworld # sha256-checked downloads scripts/bench-images/build-qcow2.sh osworld # rootfs + containerdisk variants scripts/bench-images/publish.sh web # stage immutable pins, then --promote ``` | Variant | Tag | Runs on | Init | | --- | --- | --- | --- | | `rootfs` | `<ver>` | gVisor or runc locally, gVisor in the cloud | supervisord | | `containerdisk` | `<ver>-disk` | QEMU locally, KubeVirt in the cloud | systemd | `--promote` moves `<ver>` and `<ver>-disk` only after `cua-spacesd doctor` passes on every staged digest, arch and variant. The converted images are listed in [Adapter benchmarks](</docs/cua-bench/guides/adapter-benchmarks>). - On an arm64 host, amd64 images run under x86 emulation, where Mesa's llvmpipe draws a black screen; server, file and spacesd checks still work. Doctor and screen evidence for amd64 need a native amd64 host. - A cloud sandbox of a private package never becomes ready until pool pull secrets ship; publish the package publicly or use an allowlisted registry. --- # Overview Every cb command, the task module contract and the result files, generated from cua-bench. ## Commands | Command group | Description | | --- | --- | | [`cb`](</docs/cua-bench/reference/cli>) | Synopsis, global options, exit codes and every command. | | [`cb run`](</docs/cua-bench/reference/cli/run>) | Run a task or dataset with an agent or the oracle, run one interactively, and follow runs. | | [`cb task`](</docs/cua-bench/reference/cli/task>) | Inspect, list, scaffold and generate tasks. | | [`cb dataset`](</docs/cua-bench/reference/cli/dataset>) | List registry datasets and build training data from runs. | | [`cb trace`](</docs/cua-bench/reference/cli/trace>) | View traces and agent trajectories. | | [Sandboxes and account](</docs/cua-bench/reference/cli/env>) | Cloud pools and local sandboxes, sign-in, status and cleanup. | | [`cb agent`](</docs/cua-bench/reference/cli/agent>) | Scaffold custom agents. | | [Images and platforms](</docs/cua-bench/reference/cli/images>) | The canonical images and platform configurations. | ## Schemas | Schema | Description | | --- | --- | | [Task definition](</docs/cua-bench/reference/task-definition>) | cb.Task, setup_config, the lifecycle decorators, the session API and actions. | | [Results](</docs/cua-bench/reference/results>) | Every key of result.json and summary.json. | --- # cb cua-bench: run computer-use tasks and benchmark datasets on local and cloud sandboxes. ```text output cb <COMMAND> [OPTIONS] ``` Run `cb <command> --help` for the build you have. ## Commands ### `cb run` Run a task or dataset with an agent or the oracle, run one interactively, and follow runs. [Reference](</docs/cua-bench/reference/cli/run>). | Command | Description | | --- | --- | | [`cb run`](</docs/cua-bench/reference/cli/run#cb-run>) | Run a task or dataset, and manage runs. | | [`cb run task`](</docs/cua-bench/reference/cli/run#cb-run-task>) | Run one task variant. | | [`cb run dataset`](</docs/cua-bench/reference/cli/run#cb-run-dataset>) | Run every task and variant of a dataset in parallel. | | [`cb run list`](</docs/cua-bench/reference/cli/run#cb-run-list>) | List all runs with status. | | [`cb run info`](</docs/cua-bench/reference/cli/run#cb-run-info>) | Show detailed info about a run. | | [`cb run watch`](</docs/cua-bench/reference/cli/run#cb-run-watch>) | Watch a run in real-time with live updates. | | [`cb run stop`](</docs/cua-bench/reference/cli/run#cb-run-stop>) | Stop a run and release its sandboxes. | | [`cb run logs`](</docs/cua-bench/reference/cli/run#cb-run-logs>) | View combined logs from a run or session. | | [`cb interact`](</docs/cua-bench/reference/cli/run#cb-interact>) | Run a task's setup with its desktop visible, then evaluate. | ### `cb task` Inspect, list, scaffold and generate tasks. [Reference](</docs/cua-bench/reference/cli/task>). | Command | Description | | --- | --- | | [`cb task`](</docs/cua-bench/reference/cli/task#cb-task>) | Inspect and manage task environments. | | [`cb task info`](</docs/cua-bench/reference/cli/task#cb-task-info>) | Show detailed information about a task. | | [`cb task list`](</docs/cua-bench/reference/cli/task#cb-task-list>) | List tasks in a directory. | | [`cb task create`](</docs/cua-bench/reference/cli/task#cb-task-create>) | Scaffold a new task environment. | | [`cb task generate`](</docs/cua-bench/reference/cli/task#cb-task-generate>) | Generate a task from a prompt using Claude. | ### `cb dataset` List registry datasets and build training data from runs. [Reference](</docs/cua-bench/reference/cli/dataset>). | Command | Description | | --- | --- | | [`cb dataset`](</docs/cua-bench/reference/cli/dataset#cb-dataset>) | Manage datasets and build from outputs. | | [`cb dataset list`](</docs/cua-bench/reference/cli/dataset#cb-dataset-list>) | List available datasets from registry. | | [`cb dataset build`](</docs/cua-bench/reference/cli/dataset#cb-dataset-build>) | Build a dataset from batch outputs. | ### `cb trace` View traces and agent trajectories. [Reference](</docs/cua-bench/reference/cli/trace>). | Command | Description | | --- | --- | | [`cb trace`](</docs/cua-bench/reference/cli/trace#cb-trace>) | View and analyze trace datasets. | | [`cb trace view`](</docs/cua-bench/reference/cli/trace#cb-trace-view>) | View a single trace in browser. | | [`cb trace traj`](</docs/cua-bench/reference/cli/trace#cb-trace-traj>) | View agent trajectories from a run in cua.ai/trajectory-viewer. | ### Sandboxes and account Cloud pools and local sandboxes, sign-in, status and cleanup. [Reference](</docs/cua-bench/reference/cli/env>). | Command | Description | | --- | --- | | [`cb env`](</docs/cua-bench/reference/cli/env#cb-env>) | List or garbage-collect sandboxes and pools. | | [`cb env ls`](</docs/cua-bench/reference/cli/env#cb-env-ls>) | List managed cloud pools and claims. | | [`cb env gc`](</docs/cua-bench/reference/cli/env#cb-env-gc>) | Delete idle managed pools and stuck claims. | | [`cb login`](</docs/cua-bench/reference/cli/env#cb-login>) | Sign in for --on cloud (runs `cua auth login`). | | [`cb status`](</docs/cua-bench/reference/cli/env#cb-status>) | Show system status dashboard. | | [`cb prune`](</docs/cua-bench/reference/cli/env#cb-prune>) | Clean up cua-bench run data (and 0.2.x image leftovers). | ### `cb agent` Scaffold custom agents. [Reference](</docs/cua-bench/reference/cli/agent>). | Command | Description | | --- | --- | | [`cb agent`](</docs/cua-bench/reference/cli/agent#cb-agent>) | Scaffold custom agents. | | [`cb agent init`](</docs/cua-bench/reference/cli/agent#cb-agent-init>) | Create a new agent from template. | | [`cb agent build`](</docs/cua-bench/reference/cli/agent#cb-agent-build>) | Deprecated: cb run executes agents in-process. | | [`cb agent push`](</docs/cua-bench/reference/cli/agent#cb-agent-push>) | Deprecated: agent images are no longer used. | ### Images and platforms The canonical images and platform configurations. [Reference](</docs/cua-bench/reference/cli/images>). | Command | Description | | --- | --- | | [`cb image`](</docs/cua-bench/reference/cli/images#cb-image>) | Show the canonical images (the local image store is deprecated). | | [`cb image list`](</docs/cua-bench/reference/cli/images#cb-image-list>) | List all images. | | [`cb image info`](</docs/cua-bench/reference/cli/images#cb-image-info>) | Show image details. | | [`cb image create`](</docs/cua-bench/reference/cli/images#cb-image-create>) | Deprecated: create an image from a platform. | | [`cb image delete`](</docs/cua-bench/reference/cli/images#cb-image-delete>) | Deprecated: delete a stored image. | | [`cb image clone`](</docs/cua-bench/reference/cli/images#cb-image-clone>) | Deprecated: clone a stored image. | | [`cb image shell`](</docs/cua-bench/reference/cli/images#cb-image-shell>) | Deprecated: interactive shell into a stored image. | | [`cb platform`](</docs/cua-bench/reference/cli/images#cb-platform>) | Show available platform configurations. | | [`cb platform list`](</docs/cua-bench/reference/cli/images#cb-platform-list>) | List all available platforms. | | [`cb platform info`](</docs/cua-bench/reference/cli/images#cb-platform-info>) | Show platform details. | ## Global options `--help` prints help for any command; `--version` prints the version. ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success: every variant completed. | | `1` | Failure: a variant failed or was cancelled, or the command could not run. | | `2` | Usage error: an unknown command, flag or value. | | `130` | Interrupted (Ctrl-C); running variants are cancelled. | --- # cb run Run a task or dataset with an agent or the oracle, run one interactively, and follow runs. | Command | Description | | --- | --- | | [`cb run`](#cb-run) | Run a task or dataset, and manage runs. | | [`cb run task`](#cb-run-task) | Run one task variant. | | [`cb run dataset`](#cb-run-dataset) | Run every task and variant of a dataset in parallel. | | [`cb run list`](#cb-run-list) | List all runs with status. | | [`cb run info`](#cb-run-info) | Show detailed info about a run. | | [`cb run watch`](#cb-run-watch) | Watch a run in real-time with live updates. | | [`cb run stop`](#cb-run-stop) | Stop a run and release its sandboxes. | | [`cb run logs`](#cb-run-logs) | View combined logs from a run or session. | | [`cb interact`](#cb-interact) | Run a task's setup with its desktop visible, then evaluate. | Every command also accepts the [global options](</docs/cua-bench/reference/cli#global-options>). ## `cb run` Run a task or dataset, and manage runs. Run a task (a directory with main.py) or a dataset, with an agent or the task's oracle. `cb run <path>` is shorthand for `cb run task|dataset <path>`. ```text output cb run [COMMAND] ``` **Examples** ```bash test="cli-shape" id="ref-cb-run" # Run a task with its oracle solution cb run ./tasks/hello_file_env # Run a dataset with an agent, four variants at once cb run cua-bench-basic --agent cua-agent --model anthropic/claude-sonnet-4-20250514 -j 4 # List runs cb run list ``` ### `cb run task` Run one task variant. ```text output cb run task [OPTIONS] <task_path> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<task_path>` | string | required | Path to task directory (containing main.py). | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--variant-id` | | integer | `0` | Task variant index (default: 0). | | `--agent` | | string | | Agent to use for evaluation (e.g., cua-agent). | | `--agent-import-path` | | string | | Import path for custom agent (e.g., "path.to.agent:MyCustomAgent"). | | `--model` | | string | | Model to use with the agent (e.g., anthropic/claude-sonnet-4-20250514). | | `--max-steps` | | integer | `100` | Maximum number of steps for agent execution (default: 100). | | `--on` | | `local` \| `cloud` \| `e2b` \| `daytona` \| `modal` \| `cloudflare` \| `vercel` \| `morph` \| `runloop` \| `fly` \| `blaxel` \| `codesandbox` \| `northflank` \| `aws` \| `gcp` | | Where sandboxes run: local (this machine), cloud, your own cloud account (aws, gcp, modal; connect it first with `cua cloud connect <provider>`), or a contrib provider (e2b, daytona; needs a cua SDK built with contrib and the provider's key). Default: CUA_DEFAULT_ON, else `cua config set default.on`, else local. | | `--kind` | | `auto` \| `container` \| `vm` | | What kind of sandbox: container or vm. Default auto: Linux as a container unless the task or image says VM; Windows, macOS and Android are VM-only. Default: CUA_DEFAULT_KIND, else `cua config set default.kind`. | | `--runtime` | | `auto` \| `gvisor` \| `runc` \| `qemu` \| `lume` \| `kubevirt` | | Which engine: gvisor or runc (local containers), qemu or lume (local VMs), gvisor or kubevirt (cloud containers, VMs). Implies its kind. Default auto: the SDK picks (CUA_DEFAULT_RUNTIME, else `cua config set default.runtime`). | | `--image` | | string | | Image for every task (overrides the task's setup_config.image): an OS alias (linux, windows, macos:tahoe), ghcr.io/trycua/<os>, any registry ref or a digest, or pool:<name> for an existing Fleet pool. Env: CUA_BENCH_IMAGE. | | `--cpu` | | integer | | vCPUs per sandbox. | | `--memory` | | string | | Memory per sandbox (e.g. 4096, 4G). | | `--claim-ttl` | | string | | cloud: how long a claim outlives a crashed run (default 15m; renewed while running). | | `--output-dir` | | string | | Output directory for session results. | | `--attempts` | | integer | `1` | Run every variant this many times (fresh sandbox each); summary.json reports pass@k (default: 1). | | `--retries` | | integer | `0` | Retry a variant that failed before evaluation (sandbox start, connection) up to this many times, with backoff (default: 0). | | `--keep-runs` | | integer | | After the run, delete all but the N newest runs in the runs directory (default: keep everything; env CUA_BENCH_KEEP_RUNS). | | `--max-age` | | number | | After the run, delete runs older than DAYS (env CUA_BENCH_MAX_AGE_DAYS). | | `--max-results-size` | | string | | After the run, delete the oldest runs until the runs directory fits in SIZE, e.g. 20G (env CUA_BENCH_MAX_RESULTS_SIZE). | | `--oracle` | | boolean | `false` | Run the oracle solution (the default when no agent is given). | | `--noop` | | boolean | `false` | Set up and evaluate with no actions (the null baseline for eval parity). | | `--warm` | | boolean | `false` | cloud: keep one replica ready when the managed pool is first created. | | `--detach` | `-d` | boolean | `false` | Run in the background and return (follow with cb run watch <id>). | | `--dry-run` | | boolean | `false` | Print each variant's image, kind, variant and backend, then exit without starting a sandbox. | **Examples** ```bash test="cli-shape" id="ref-cb-run-task" # Run variant 0 with the oracle solution cb run task ./tasks/hello_file_env # Run variant 2 with an agent in the cloud cb run task ./tasks/hello_file_env --variant-id 2 --agent cua-agent --model anthropic/claude-sonnet-4-20250514 --on cloud # Show what would run without starting a sandbox cb run ./tasks/hello_file_env --dry-run ``` ### `cb run dataset` Run every task and variant of a dataset in parallel. ```text output cb run dataset [OPTIONS] <dataset_path> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<dataset_path>` | string | required | Path to dataset directory, or dataset name from registry. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--max-parallel` | `-j` | integer | `4` | Variants running at once; in the cloud also the managed pool's max size (default: 4). | | `--max-variants` | | integer | | Maximum number of variants to run per task (default: all). | | `--task-filter` | | string | | Filter tasks by name pattern (glob). | | `--agent` | | string | | Agent to use for evaluation (e.g., cua-agent). | | `--agent-import-path` | | string | | Import path for custom agent (e.g., "path.to.agent:MyCustomAgent"). | | `--model` | | string | | Model to use with the agent (e.g., anthropic/claude-sonnet-4-20250514). | | `--max-steps` | | integer | `100` | Maximum number of steps for agent execution (default: 100). | | `--on` | | `local` \| `cloud` \| `e2b` \| `daytona` \| `modal` \| `cloudflare` \| `vercel` \| `morph` \| `runloop` \| `fly` \| `blaxel` \| `codesandbox` \| `northflank` \| `aws` \| `gcp` | | Where sandboxes run: local (this machine), cloud, your own cloud account (aws, gcp, modal; connect it first with `cua cloud connect <provider>`), or a contrib provider (e2b, daytona; needs a cua SDK built with contrib and the provider's key). Default: CUA_DEFAULT_ON, else `cua config set default.on`, else local. | | `--kind` | | `auto` \| `container` \| `vm` | | What kind of sandbox: container or vm. Default auto: Linux as a container unless the task or image says VM; Windows, macOS and Android are VM-only. Default: CUA_DEFAULT_KIND, else `cua config set default.kind`. | | `--runtime` | | `auto` \| `gvisor` \| `runc` \| `qemu` \| `lume` \| `kubevirt` | | Which engine: gvisor or runc (local containers), qemu or lume (local VMs), gvisor or kubevirt (cloud containers, VMs). Implies its kind. Default auto: the SDK picks (CUA_DEFAULT_RUNTIME, else `cua config set default.runtime`). | | `--image` | | string | | Image for every task (overrides the task's setup_config.image): an OS alias (linux, windows, macos:tahoe), ghcr.io/trycua/<os>, any registry ref or a digest, or pool:<name> for an existing Fleet pool. Env: CUA_BENCH_IMAGE. | | `--cpu` | | integer | | vCPUs per sandbox. | | `--memory` | | string | | Memory per sandbox (e.g. 4096, 4G). | | `--claim-ttl` | | string | | cloud: how long a claim outlives a crashed run (default 15m; renewed while running). | | `--output-dir` | | string | | Output directory for session results. | | `--attempts` | | integer | `1` | Run every variant this many times (fresh sandbox each); summary.json reports pass@k (default: 1). | | `--retries` | | integer | `0` | Retry a variant that failed before evaluation (sandbox start, connection) up to this many times, with backoff (default: 0). | | `--keep-runs` | | integer | | After the run, delete all but the N newest runs in the runs directory (default: keep everything; env CUA_BENCH_KEEP_RUNS). | | `--max-age` | | number | | After the run, delete runs older than DAYS (env CUA_BENCH_MAX_AGE_DAYS). | | `--max-results-size` | | string | | After the run, delete the oldest runs until the runs directory fits in SIZE, e.g. 20G (env CUA_BENCH_MAX_RESULTS_SIZE). | | `--oracle` | | boolean | `false` | Run the oracle solution (the default when no agent is given). | | `--noop` | | boolean | `false` | Set up and evaluate with no actions (the null baseline for eval parity). | | `--warm` | | boolean | `false` | cloud: keep one replica ready when the managed pool is first created. | | `--detach` | `-d` | boolean | `false` | Run in the background and return (follow with cb run watch <id>). | | `--dry-run` | | boolean | `false` | Print each variant's image, kind, variant and backend, then exit without starting a sandbox. | **Examples** ```bash test="cli-shape" id="ref-cb-run-dataset" # Run a registry dataset with its oracle solutions cb run dataset cua-bench-basic # Run one variant of each matching task, eight at once, in the background cb run ./tasks --task-filter 'click*' --max-variants 1 -j 8 --detach # Run every variant three times and report pass@k cb run dataset cua-bench-basic --agent cua-agent --attempts 3 ``` ### `cb run list` List all runs with status. ```text output cb run list [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--verbose` | `-v` | boolean | `false` | Show verbose debugging information. | **Examples** ```bash test="cli-shape" id="ref-cb-run-list" # List runs cb run list # Include debugging details cb run list --verbose ``` ### `cb run info` Show detailed info about a run. ```text output cb run info <run_id> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<run_id>` | string | required | Run ID to show info for. | **Examples** ```bash test="cli-shape" id="ref-cb-run-info" # Show one run cb run info 30c12572 ``` ### `cb run watch` Watch a run in real-time with live updates. ```text output cb run watch <run_id> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<run_id>` | string | required | Run ID to watch. | **Examples** ```bash test="cli-shape" id="ref-cb-run-watch" # Follow a detached run cb run watch 30c12572 ``` ### `cb run stop` Stop a run and release its sandboxes. ```text output cb run stop <run_id> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<run_id>` | string | required | Run ID to stop. | **Examples** ```bash test="cli-shape" id="ref-cb-run-stop" # Stop a run cb run stop 30c12572 ``` ### `cb run logs` View combined logs from a run or session. ```text output cb run logs [OPTIONS] <identifier> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<identifier>` | string | required | Run ID or Session ID to view logs for. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--tail` | integer | | Show only the last N lines. | **Examples** ```bash test="cli-shape" id="ref-cb-run-logs" # Print a run's logs cb run logs 30c12572 # Print the last 50 lines cb run logs 30c12572 --tail 50 ``` ## `cb interact` Run a task's setup with its desktop visible, then evaluate. ```text output cb interact [OPTIONS] <env_path> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<env_path>` | string | required | Path to the task directory, or a task name with --dataset or --dataset-path. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--variant-id` | integer | `0` | Task variant index (default: 0). | | `--dataset` | string | | Registry dataset to resolve the task from (CUA_REGISTRY_HOME). | | `--dataset-path` | string | | Path to dataset directory containing multiple tasks. | | `--max-steps` | integer | | Maximum number of env.step() calls before stopping. | | `--screenshot` | string | | Save a screenshot to this file. | | `--trace-out` | string | | Record a trace and save it as a dataset to this path on exit. | | `--on` | `local` \| `cloud` \| `e2b` \| `daytona` \| `modal` \| `cloudflare` \| `vercel` \| `morph` \| `runloop` \| `fly` \| `blaxel` \| `codesandbox` \| `northflank` \| `aws` \| `gcp` | | Where sandboxes run: local (this machine), cloud, your own cloud account (aws, gcp, modal; connect it first with `cua cloud connect <provider>`), or a contrib provider (e2b, daytona; needs a cua SDK built with contrib and the provider's key). Default: CUA_DEFAULT_ON, else `cua config set default.on`, else local. | | `--kind` | `auto` \| `container` \| `vm` | | What kind of sandbox: container or vm. Default auto: Linux as a container unless the task or image says VM; Windows, macOS and Android are VM-only. Default: CUA_DEFAULT_KIND, else `cua config set default.kind`. | | `--runtime` | `auto` \| `gvisor` \| `runc` \| `qemu` \| `lume` \| `kubevirt` | | Which engine: gvisor or runc (local containers), qemu or lume (local VMs), gvisor or kubevirt (cloud containers, VMs). Implies its kind. Default auto: the SDK picks (CUA_DEFAULT_RUNTIME, else `cua config set default.runtime`). | | `--image` | string | | Image for every task (overrides the task's setup_config.image): an OS alias (linux, windows, macos:tahoe), ghcr.io/trycua/<os>, any registry ref or a digest, or pool:<name> for an existing Fleet pool. Env: CUA_BENCH_IMAGE. | | `--cpu` | integer | | vCPUs per sandbox. | | `--memory` | string | | Memory per sandbox (e.g. 4096, 4G). | | `--claim-ttl` | string | | cloud: how long a claim outlives a crashed run (default 15m; renewed while running). | | `--oracle` | boolean | `false` | Run the solution after setup. | | `--view` | boolean | `false` | Open the trace viewer when done. | | `--no-wait` | boolean | `false` | Skip the interactive prompt (useful for SSH/CI testing). | | `--no-browser` | boolean | `false` | Print the Display: URL without opening it (also CUA_BENCH_NO_BROWSER=1). | | `--warm` | boolean | `false` | cloud: keep one replica ready when the managed pool is first created. | **Examples** ```bash test="cli-shape" id="ref-cb-interact" # Open a task and its display, wait for Enter, then evaluate cb interact ./tasks/hello_file_env # Run variant 1 with its oracle and save a screenshot cb interact ./tasks/hello_file_env --variant-id 1 --oracle --screenshot shot.png # Resolve a task from a registry dataset cb interact click-button --dataset cua-bench-basic ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success: every variant completed. | | `1` | Failure: a variant failed or was cancelled, or the command could not run. | | `2` | Usage error: an unknown command, flag or value. | | `130` | Interrupted (Ctrl-C); running variants are cancelled. | --- # cb task Inspect, list, scaffold and generate tasks. | Command | Description | | --- | --- | | [`cb task info`](#cb-task-info) | Show detailed information about a task. | | [`cb task list`](#cb-task-list) | List tasks in a directory. | | [`cb task create`](#cb-task-create) | Scaffold a new task environment. | | [`cb task generate`](#cb-task-generate) | Generate a task from a prompt using Claude. | Every command also accepts the [global options](</docs/cua-bench/reference/cli#global-options>). Inspect and manage task environments. ```text output cb task [COMMAND] ``` **Examples** ```bash test="cli-shape" id="ref-cb-task" # List the tasks in a directory cb task list ./tasks # Inspect one task cb task info ./tasks/hello_file_env ``` ## `cb task info` Show detailed information about a task. ```text output cb task info <path> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<path>` | string | required | Path to task directory (containing main.py). | **Examples** ```bash test="cli-shape" id="ref-cb-task-info" # Inspect a task's variants and lifecycle functions cb task info ./tasks/hello_file_env ``` ## `cb task list` List tasks in a directory. ```text output cb task list [<path>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<path>` | string | `.` | Path to directory containing tasks (default: current directory). | **Examples** ```bash test="cli-shape" id="ref-cb-task-list" # List tasks here cb task list # List tasks in a directory cb task list ./tasks ``` ## `cb task create` Scaffold a new task environment. ```text output cb task create [<path>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<path>` | string | `.` | Target directory to create the task in (default: current directory). | **Examples** ```bash test="cli-shape" id="ref-cb-task-create" # Scaffold a task interactively under ./tasks cb task create ./tasks ``` ## `cb task generate` Generate a task from a prompt using Claude. ```text output cb task generate [OPTIONS] <prompt> [<output>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<prompt>` | string | required | Natural language description of the task to generate (e.g., "2048 game"). | | `<output>` | string | optional | Output directory (default: named after the prompt). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--no-interaction` | boolean | `false` | Skip interactive prompts and run Claude non-interactively. | **Examples** ```bash test="cli-shape" id="ref-cb-task-generate" # Generate a task cb task generate '2048 game' # Generate into a directory without prompts cb task generate 'Sort a spreadsheet column' ./tasks/sort_column --no-interaction ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success: every variant completed. | | `1` | Failure: a variant failed or was cancelled, or the command could not run. | | `2` | Usage error: an unknown command, flag or value. | | `130` | Interrupted (Ctrl-C); running variants are cancelled. | --- # cb dataset List registry datasets and build training data from runs. | Command | Description | | --- | --- | | [`cb dataset list`](#cb-dataset-list) | List available datasets from registry. | | [`cb dataset build`](#cb-dataset-build) | Build a dataset from batch outputs. | Every command also accepts the [global options](</docs/cua-bench/reference/cli#global-options>). Manage datasets and build from outputs. ```text output cb dataset [COMMAND] ``` **Examples** ```bash test="cli-shape" id="ref-cb-dataset" # List registry datasets cb dataset list # Build a training dataset from run outputs cb dataset build ./outputs ``` ## `cb dataset list` List available datasets from registry. ```text output cb dataset list ``` **Examples** ```bash test="cli-shape" id="ref-cb-dataset-list" # List registry datasets cb dataset list ``` ## `cb dataset build` Build a dataset from batch outputs. ```text output cb dataset build [OPTIONS] <outputs_path> [<max_samples>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<outputs_path>` | string | required | Path to outputs folder from batch dump (e.g., ./outputs or /tmp/td_output). | | `<max_samples>` | integer | optional | Limit number of samples processed (useful for testing). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--mode` | string | `aguvis-stage-1` | Processing mode: aguvis-stage-1 (action augmentation) or gui-r1 (low-level click). | | `--dataset-name` | string | | Dataset name when saving to disk (default: td_<mode>_dataset). | | `--save-dir` | string | | Directory to save a JSONL dataset (default: <outputs>/processed if not pushing). | | `--repo-id` | string | | HF Hub repository ID (e.g., username/repo). Required with --push-to-hub. | | `--push-to-hub` | boolean | `false` | Push the dataset to the Hugging Face Hub. | | `--private` | boolean | `false` | When pushing to hub, create/update the repo as private. | **Examples** ```bash test="cli-shape" id="ref-cb-dataset-build" # Build an aguvis-stage-1 dataset from run outputs cb dataset build ./outputs # Build 100 gui-r1 samples cb dataset build ./outputs 100 --mode gui-r1 # Push a private dataset to the Hugging Face Hub cb dataset build ./outputs --push-to-hub --repo-id me/cua-clicks --private ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success: every variant completed. | | `1` | Failure: a variant failed or was cancelled, or the command could not run. | | `2` | Usage error: an unknown command, flag or value. | | `130` | Interrupted (Ctrl-C); running variants are cancelled. | --- # cb trace View traces and agent trajectories. | Command | Description | | --- | --- | | [`cb trace view`](#cb-trace-view) | View a single trace in browser. | | [`cb trace traj`](#cb-trace-traj) | View agent trajectories from a run in cua.ai/trajectory-viewer. | Every command also accepts the [global options](</docs/cua-bench/reference/cli#global-options>). View and analyze trace datasets. ```text output cb trace [COMMAND] ``` **Examples** ```bash test="cli-shape" id="ref-cb-trace" # Open a run's trace in the browser cb trace view 30c12572 # Open a run's agent trajectories cb trace traj 30c12572 ``` ## `cb trace view` View a single trace in browser. ```text output cb trace view <identifier> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<identifier>` | string | required | Run ID or session ID (e.g., "30c12572" or "task-30c12572-click-button-v0"). | **Examples** ```bash test="cli-shape" id="ref-cb-trace-view" # Open a run's trace cb trace view 30c12572 # Open one session's trace cb trace view task-30c12572-click-button-v0 ``` ## `cb trace traj` View agent trajectories from a run in cua.ai/trajectory-viewer. ```text output cb trace traj [OPTIONS] <identifier> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<identifier>` | string | required | Run ID (e.g., "30c12572"). | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--port` | `-p` | integer | `8090` | Local file server port (default: 8090). | **Examples** ```bash test="cli-shape" id="ref-cb-trace-traj" # Open a run's trajectories cb trace traj 30c12572 # Serve the files on another port cb trace traj 30c12572 --port 9000 ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success: every variant completed. | | `1` | Failure: a variant failed or was cancelled, or the command could not run. | | `2` | Usage error: an unknown command, flag or value. | | `130` | Interrupted (Ctrl-C); running variants are cancelled. | --- # Sandboxes and account Cloud pools and local sandboxes, sign-in, status and cleanup. | Command | Description | | --- | --- | | [`cb env`](#cb-env) | List or garbage-collect sandboxes and pools. | | [`cb env ls`](#cb-env-ls) | List managed cloud pools and claims. | | [`cb env gc`](#cb-env-gc) | Delete idle managed pools and stuck claims. | | [`cb login`](#cb-login) | Sign in for --on cloud (runs `cua auth login`). | | [`cb status`](#cb-status) | Show system status dashboard. | | [`cb prune`](#cb-prune) | Clean up cua-bench run data (and 0.2.x image leftovers). | Every command also accepts the [global options](</docs/cua-bench/reference/cli#global-options>). ## `cb env` List or garbage-collect sandboxes and pools. ```text output cb env [COMMAND] ``` **Examples** ```bash test="cli-shape" id="ref-cb-env" # List managed cloud pools and claims cb env ls # Delete pools idle for an hour cb env gc --idle 1h ``` ### `cb env ls` List managed cloud pools and claims. ```text output cb env ls [OPTIONS] ``` Alias: `cb env list`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | List local sandboxes instead. | **Examples** ```bash test="cli-shape" id="ref-cb-env-ls" # List managed cloud pools and claims cb env ls # List local sandboxes cb env ls --local ``` ### `cb env gc` Delete idle managed pools and stuck claims. ```text output cb env gc [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--idle` | string | `30m` | Delete pools unused for this long (default 30m). | **Examples** ```bash test="cli-shape" id="ref-cb-env-gc" # Delete pools unused for 30 minutes cb env gc # Delete pools idle for two hours cb env gc --idle 2h ``` ## `cb login` Sign in for --on cloud (runs `cua auth login`). ```text output cb login [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--no-browser` | boolean | `false` | Print the sign-in URL only. | **Examples** ```bash test="cli-shape" id="ref-cb-login" # Sign in with a browser cb login # Print the sign-in URL instead of opening a browser cb login --no-browser ``` ## `cb status` Show system status dashboard. ```text output cb status ``` **Examples** ```bash test="cli-shape" id="ref-cb-status" # Show runs, sandboxes and credentials at a glance cb status ``` ## `cb prune` Clean up cua-bench run data (and 0.2.x image leftovers). ```text output cb prune [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--keep` | | integer | | With --runs: keep the N newest runs and remove only older ones. | | `--older-than` | | number | | With --runs: remove only runs older than DAYS. | | `--all` | `-a` | boolean | `false` | Remove runs and 0.2.x image leftovers. | | `--images` | | boolean | `false` | Remove 0.2.x stored images (deprecated store). | | `--overlays` | | boolean | `false` | Remove 0.2.x task overlays (deprecated store). | | `--runs` | | boolean | `false` | Remove run logs and registry. | | `--docker` | | boolean | `false` | Deprecated: cua-bench no longer creates Docker resources. | | `--dry-run` | | boolean | `false` | Show what would be deleted without deleting. | | `--force` | `-f` | boolean | `false` | Skip confirmation prompts. | **Examples** ```bash test="cli-shape" id="ref-cb-prune" # Preview what would be removed cb prune --all --dry-run # Remove run logs without asking cb prune --runs --force # Keep the 20 newest runs, delete the rest cb prune --runs --keep 20 # Delete runs older than 30 days cb prune --runs --older-than 30 ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success: every variant completed. | | `1` | Failure: a variant failed or was cancelled, or the command could not run. | | `2` | Usage error: an unknown command, flag or value. | | `130` | Interrupted (Ctrl-C); running variants are cancelled. | --- # cb agent Scaffold custom agents. | Command | Description | | --- | --- | | [`cb agent init`](#cb-agent-init) | Create a new agent from template. | | [`cb agent build`](#cb-agent-build) | Deprecated: cb run executes agents in-process. | | [`cb agent push`](#cb-agent-push) | Deprecated: agent images are no longer used. | Every command also accepts the [global options](</docs/cua-bench/reference/cli#global-options>). Scaffold custom agents. Create and manage custom agents for cua-bench. ```text output cb agent [COMMAND] ``` **Examples** ```bash test="cli-shape" id="ref-cb-agent" # Scaffold an agent in ./my-agent cb agent init my-agent ``` ## `cb agent init` Create a new agent from template. Scaffold a new custom agent with working sample code. ```text output cb agent init [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Agent name (e.g., 'my-agent'). | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--output-dir` | `-o` | string | | Output directory (default: ./<name>). | **Examples** ```bash test="cli-shape" id="ref-cb-agent-init" # Scaffold an agent in ./my-agent cb agent init my-agent # Scaffold it into another directory cb agent init my-agent --output-dir ./agents/my-agent ``` ## `cb agent build` Deprecated: cb run executes agents in-process. Build a Docker image for an agent directory containing a Dockerfile. ```text output cb agent build [OPTIONS] <path> ``` **Examples** ```bash test="cli-shape" id="ref-cb-agent-build" # Build an agent image (deprecated) cb agent build ./my-agent --tag my-agent:dev ``` ## `cb agent push` Deprecated: agent images are no longer used. Push a Docker image through the CUA API proxy with workspace namespacing. ```text output cb agent push [OPTIONS] [<image_name>] ``` **Examples** ```bash test="cli-shape" id="ref-cb-agent-push" # Push an agent image (deprecated) cb agent push my-agent:dev ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success: every variant completed. | | `1` | Failure: a variant failed or was cancelled, or the command could not run. | | `2` | Usage error: an unknown command, flag or value. | | `130` | Interrupted (Ctrl-C); running variants are cancelled. | --- # Images and platforms The canonical images and platform configurations. | Command | Description | | --- | --- | | [`cb image`](#cb-image) | Show the canonical images (the local image store is deprecated). | | [`cb image list`](#cb-image-list) | List all images. | | [`cb image info`](#cb-image-info) | Show image details. | | [`cb image create`](#cb-image-create) | Deprecated: create an image from a platform. | | [`cb image delete`](#cb-image-delete) | Deprecated: delete a stored image. | | [`cb image clone`](#cb-image-clone) | Deprecated: clone a stored image. | | [`cb image shell`](#cb-image-shell) | Deprecated: interactive shell into a stored image. | | [`cb platform`](#cb-platform) | Show available platform configurations. | | [`cb platform list`](#cb-platform-list) | List all available platforms. | | [`cb platform info`](#cb-platform-info) | Show platform details. | Every command also accepts the [global options](</docs/cua-bench/reference/cli#global-options>). ## `cb image` Show the canonical images (the local image store is deprecated). ```text output cb image [COMMAND] ``` **Examples** ```bash test="cli-shape" id="ref-cb-image" # List the canonical images cb image # Show one image cb image info linux ``` ### `cb image list` List all images. ```text output cb image list [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--platform` | string | | Filter by platform. | | `--format` | `table` \| `json` | `table` | Output format. | **Examples** ```bash test="cli-shape" id="ref-cb-image-list" # List images cb image list # List one platform's images as JSON cb image list --platform linux-docker --format json ``` ### `cb image info` Show image details. ```text output cb image info <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Image name. | **Examples** ```bash test="cli-shape" id="ref-cb-image-info" # Show one image cb image info linux ``` ### `cb image create` Deprecated: create an image from a platform. ```text output cb image create [OPTIONS] <platform> ``` **Examples** ```bash test="cli-shape" id="ref-cb-image-create" # Create a Linux image (deprecated) cb image create linux-docker --name my-linux ``` ### `cb image delete` Deprecated: delete a stored image. ```text output cb image delete [OPTIONS] <name> ``` **Examples** ```bash test="cli-shape" id="ref-cb-image-delete" # Delete a stored image (deprecated) cb image delete my-linux --force ``` ### `cb image clone` Deprecated: clone a stored image. ```text output cb image clone [OPTIONS] <source> <target> ``` **Examples** ```bash test="cli-shape" id="ref-cb-image-clone" # Clone a stored image (deprecated) cb image clone my-linux my-linux-2 ``` ### `cb image shell` Deprecated: interactive shell into a stored image. ```text output cb image shell [OPTIONS] <name> ``` **Examples** ```bash test="cli-shape" id="ref-cb-image-shell" # Open a shell in a stored image (deprecated) cb image shell my-linux ``` ## `cb platform` Show available platform configurations. ```text output cb platform [COMMAND] ``` **Examples** ```bash test="cli-shape" id="ref-cb-platform" # List platforms cb platform # Show one platform cb platform info linux-docker ``` ### `cb platform list` List all available platforms. ```text output cb platform list [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--format` | `table` \| `json` | `table` | Output format. | **Examples** ```bash test="cli-shape" id="ref-cb-platform-list" # List platforms cb platform list # As JSON cb platform list --format json ``` ### `cb platform info` Show platform details. ```text output cb platform info <platform> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<platform>` | string | required | Platform name (e.g., linux-docker, windows-qemu). | **Examples** ```bash test="cli-shape" id="ref-cb-platform-info" # Show one platform cb platform info windows-qemu ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success: every variant completed. | | `1` | Failure: a variant failed or was cancelled, or the command could not run. | | `2` | Usage error: an unknown command, flag or value. | | `130` | Interrupted (Ctrl-C); running variants are cancelled. | --- # Task definition The task module contract: cb.Task, setup_config, the lifecycle decorators, the session API and actions. A task is a directory with a `main.py`. `cb` imports it, calls the `@cb.tasks_config` function to list the variants, then for each variant starts a sandbox from its `computer` and runs setup, the agent (or the `@cb.solve_task` oracle) and evaluation. Each lifecycle function receives the variant's `cb.Task` and the session on its sandbox. ## `cb.Task` One task variant, as returned by the `@cb.tasks_config` function. | Field | Type | Default | Description | | --- | --- | --- | --- | | `description` | `str` | required | The instruction the agent receives. | | `task_id` | `str \| null` | `None` | A stable identifier for the variant (optional; the index is used otherwise). | | `metadata` | `dict \| null` | `None` | Variant-specific values your setup, solve and evaluate functions read. | | `computer` | `dict \| null` | `None` | The sandbox: `{"provider": "native", "setup_config": {...}}` (see `DesktopSetupConfig`). `native` (the default) is the only provider. | ## `setup_config` A task's `computer["setup_config"]`: the sandbox the task runs in. Every key is optional. Command-line flags (`--image`, `--kind`, `--cpu`, `--memory`) override the task's values; `--runtime` (the engine) is a command-line choice only. | Key | Type | Description | | --- | --- | --- | | `os_type` | `string` | Guest OS: `linux` (the default; `ubuntu` is an alias), `windows` (`win11`, `win10` and older names are aliases), `macos` or `android`. macOS and Android run only with `--on local`. | | `width` | `int` | Screen width in pixels. | | `height` | `int` | Screen height in pixels. | | `image` | `str` | Registry image or OS alias (`linux`, `windows`, `macos:tahoe`), or `pool:<name>` for an existing Fleet pool. `--image` wins, then this, then `CUA_BENCH_IMAGE`, then the canonical image of `os_type`. | | `kind` | `str` | Preferred kind: `container` (Linux only) or `vm`. | | `kinds` | `list[str]` | Kinds the task supports (a requirement, unlike `kind`): for example `["vm"]` for a task that needs its own kernel. | | `requires` | `list[str]` | What the target must provide: `kvm`, `env:<NAME>` (a set environment variable), `openai` (`env:OPENAI_API_KEY`) or `hf-gated` (`env:HF_TOKEN`). | | `server_port` | `int` | A port the image serves itself, used as the readiness probe. | | `memory` | `str` | VM memory, for example `"8GB"`. | | `cpu` | `str` | VM CPUs, for example `"4"`. | | `background` | `str` | Ignored (kept so older tasks still load). | | `wallpaper` | `str` | Ignored (kept so older tasks still load). | | `installed_apps` | `list[str]` | Ignored (kept so older tasks still load). | | `storage` | `str` | Deprecated and ignored. | | `provider_type` | `str` | Deprecated: `"cloud"` runs on Fleet; use `--on cloud` instead. | ## Lifecycle decorators | Decorator | Description | | --- | --- | | `@cb.tasks_config` | Marks the function that lists the task's variants. The function takes no arguments and returns a list of `cb.Task`; the CLI indexes them from 0 (`--variant-id`). Use as `@cb.tasks_config` or with a split: `@cb.tasks_config("train")` (default `train`). | | `@cb.setup_task` | Marks the function that prepares a variant's sandbox. `async def setup(task_cfg: cb.Task, session: cb.DesktopSession)` runs after the sandbox starts and before the agent or the oracle. Use as `@cb.setup_task` or with a split: `@cb.setup_task("train")`. | | `@cb.solve_task` | Marks the oracle: the function that solves a variant (optional). `async def solve(task_cfg: cb.Task, session: cb.DesktopSession)` runs instead of an agent with `--oracle` or when no agent is given. Use as `@cb.solve_task` or with a split: `@cb.solve_task("train")`. | | `@cb.evaluate_task` | Marks the function that scores a variant. `async def evaluate(task_cfg: cb.Task, session: cb.DesktopSession)` returns the evaluation, commonly `list[float]`; the reward is the mean of its numbers. Use as `@cb.evaluate_task` or with a split: `@cb.evaluate_task("train")`. | ## Session Unified desktop session using the cua-sandbox SDK. | Method | Returns | Description | | --- | --- | --- | | `computer` | `Any` | The cua-computer style `Computer` of this session (0.2.x). | | `sandbox` | `Any` | The connected `cua_sandbox.Sandbox` (None for computer-server sessions). | | `await step(action: Action)` | `None` | Execute an action (alias for execute_action, for env.step() compatibility). | | `await start(config: Optional[DesktopSetupConfig]=None, headless: Optional[bool]=None)` | `None` | Start the session and connect to the environment. | | `interface` | `Any` | The cua-computer 0.5 interface surface of this session. | | `await serve_static(url_path: str, local_path: str)` | `None` | Serve static files - not applicable for remote environments. | | `await launch_window(url: Optional[str]=None, *, html: Optional[str]=None, folder: Optional[str]=None, title: str='Window', x: Optional[int]=None, y: Optional[int]=None, width: int=600, height: int=400, icon: Optional[str]=None, use_inner_size: bool=False, title_bar_style: str='default')` | `int \| str` | Launch a window in the remote environment using bench_ui (pywebview). | | `await get_element_rect(pid: int \| str, selector: str, *, space: Literal['window', 'screen']='window', timeout: float=0.5)` | `dict[str, Any] \| None` | Get element rect by CSS selector using bench_ui. | | `await execute_javascript(pid: int \| str, javascript: str)` | `Any` | Execute JavaScript in a pywebview window using bench_ui. | | `await execute_action(action: Action)` | `None` | Execute an action on the remote desktop using the SDK. | | `await screenshot()` | `bytes` | Capture screenshot from remote environment. | | `await get_snapshot()` | `Snapshot` | Get snapshot of desktop state with active window info. | | `await close()` | `None` | Close the session and cleanup resources. | | `await close_all_windows()` | `None` | Close all windows - best effort. | | `page` | `Any` | Return underlying page object - not applicable for remote. | | `vnc_url` | `str` | Return the VNC URL for accessing the environment. | | `apps` | `AppsProxy` | Access registered apps via session.apps.{app_name}. | | `await click_element(pid: int \| str, selector: str)` | `None` | Find element by CSS selector and click its center. | | `await right_click_element(pid: int \| str, selector: str)` | `None` | Find element by CSS selector and right-click its center. | | `await get_accessibility_tree()` | `Dict[str, Any]` | Get the accessibility tree if supported ({} when not). | | `await shell_command(command: str, *, check: bool=True, timeout: Optional[float]=None)` | `Dict[str, Any]` | Execute a shell command. | | `await read_file(path: str)` | `str` | Read a text file from the environment. | | `await write_file(path: str, content: str)` | `None` | Write a text file to the environment. | | `await read_bytes(path: str)` | `bytes` | Read a file as bytes from the environment. | | `await write_bytes(path: str, data: bytes)` | `None` | Write bytes to a file in the environment. | | `await file_exists(path: str)` | `bool` | Whether `path` is an existing file (not a directory), as in 0.2.x. | | `await directory_exists(path: str)` | `bool` | Check if a directory exists in the environment. | | `await list_dir(path: str)` | `list[str]` | List contents of a directory in the environment. | | `await run_command(command: str, *, check: bool=True, timeout: Optional[float]=None)` | `Dict[str, Any]` | Execute a shell command (alias for `shell_command`). | | `await launch_application(app_name: str)` | `None` | Launch an application by name. | | `await check_status()` | `bool` | Check if the environment is responsive. | | `await wait_until_ready(timeout: int=60, poll_interval: float=2.0)` | `bool` | Wait until the environment is ready. | | `await click(x: int, y: int)` | `None` | Click at coordinates. | | `await right_click(x: int, y: int)` | `None` | Right-click at coordinates. | | `await double_click(x: int, y: int)` | `None` | Double-click at coordinates. | | `await type(text: str)` | `None` | Type text. | | `await key(key: str)` | `None` | Press a key. | | `await hotkey(keys: list[str])` | `None` | Press a key combination. | | `await scroll(direction: str='down', amount: int=300)` | `None` | Scroll the screen. | | `await move_to(x: int, y: int)` | `None` | Move cursor to coordinates. | | `await drag(from_x: int, from_y: int, to_x: int, to_y: int)` | `None` | Drag from one position to another. | | `os_type` | `str` | Return the OS type for this session. | | `await install_app(app_name: str, *, with_shortcut: bool=True, **kwargs)` | `None` | Install a registered app on the native desktop environment. | | `await launch_app(app_name: str, **kwargs)` | `None` | Launch a registered app on the native desktop environment. | ## Actions Input actions for `session.execute_action` (from `cua_bench`). | Action | Fields | | --- | --- | | `ClickAction` | `x: int`, `y: int` | | `RightClickAction` | `x: int`, `y: int` | | `DoubleClickAction` | `x: int`, `y: int` | | `MiddleClickAction` | `x: int`, `y: int` | | `DragAction` | `from_x: int`, `from_y: int`, `to_x: int`, `to_y: int`, `duration: float = 1.0` | | `MoveToAction` | `x: int`, `y: int`, `duration: float = 0.0` | | `ScrollAction` | `direction: string = 'up'`, `amount: int = 100` | | `TypeAction` | `text: str` | | `KeyAction` | `key: str` | | `HotkeyAction` | `keys: list[str]` | | `DoneAction` | none | | `WaitAction` | `seconds: float = 1.0` | ## Example ```python """A tiny native task for smoke-testing a target: write a word to a file. Runs on any Linux image with cua-spacesd (shell and files), for example: cb run example_tasks/hello_file_env # local gVisor container cb run example_tasks/hello_file_env --on cloud --image <registry ref> """ import cua_bench as cb WORDS = ["hello", "bench"] @cb.tasks_config(split="train") def load(): return [ cb.Task( description=f"Write the word '{word}' into /tmp/cb_answer.txt.", metadata={"word": word}, computer={"provider": "native", "setup_config": {"os_type": "linux"}}, ) for word in WORDS ] @cb.setup_task(split="train") async def setup(task_cfg, session: cb.DesktopSession): await session.run_command("rm -f /tmp/cb_answer.txt", check=False) await session.write_file("/tmp/cb_goal.txt", task_cfg.metadata["word"]) @cb.solve_task(split="train") async def solve(task_cfg, session: cb.DesktopSession): await session.run_command("cp /tmp/cb_goal.txt /tmp/cb_answer.txt") @cb.evaluate_task(split="train") async def evaluate(task_cfg, session: cb.DesktopSession) -> list[float]: if not await session.file_exists("/tmp/cb_answer.txt"): return [0.0] answer = (await session.read_file("/tmp/cb_answer.txt")).strip() return [1.0 if answer == task_cfg.metadata["word"] else 0.0] ``` --- # Results The result.json and summary.json a run writes: every key, its type and meaning. A run writes one directory per variant and a `summary.json` next to them: ```text output <output-dir>/ summary.json <task>_v<variant>/ (with _a<attempt> for --attempts repeats) result.json run.log trajectory.json (ATIF-v1.8, screenshots under imgs/) task_<variant>_trace/ (a Hugging Face dataset of the trace events) ``` Both files carry `schema_version`; new keys are additive, and a rename or removal bumps the version. ## `result.json` One variant's outcome: the cua-bench fields of its `result.json`. | Key | Type | Description | | --- | --- | --- | | `schema_version` | `int` | Layout version; a rename or removal bumps it, new keys do not. | | `session_id` | `str` | The session id (also the variant's log and trace key). | | `task` | `str` | Task directory name. | | `variant` | `int` | Variant index (`--variant-id`). | | `status` | `str` | `completed`, `failed` or `cancelled`. | | `reward` | `float \| null` | The mean of the evaluation's numbers (`true` counts as 1), or `null` when evaluation did not run. | | `evaluation` | `any` | What `@cb.evaluate_task` returned (commonly a list of floats). | | `error` | `str \| null` | The error message when the variant failed. | | `duration_s` | `float` | Wall time of the variant in seconds. | | `on` | `str` | Where it ran: `local` or `cloud`. | | `backend` | `str` | What ran it, `<location>-<engine>` (`local-gvisor`, `local-qemu`, `cloud-gvisor`, `cloud-kubevirt`, `cloud-pool:<name>`). | | `image` | `str \| null` | The image as the task or `--image` named it. | | `pool` | `str \| null` | The Fleet pool that served the claim (cloud runs). | | `output_dir` | `str` | The variant's output directory. | | `extra` | `dict` | Extra values the task or agent reported. | | `kind` | `str \| null` | `container` or `vm`. | | `runtime` | `str \| null` | The engine (`gvisor`, `runc`, `qemu`, `lume`, `kubevirt`) when one was chosen; `None`: the SDK picked. | | `image_ref` | `str \| null` | The resolved image reference. | | `image_variant` | `str \| null` | The image variant that ran: `rootfs`, `containerdisk` or `lume`. | | `image_digest` | `str \| null` | The pinned `repo@sha256:...` when the SDK reports it. | | `arch` | `str \| null` | Guest architecture, when known. | | `attempt` | `int` | The `--attempts` repeat index (0 for the first). | | `retries` | `int` | How many infrastructure retries (`--retries`) the variant took. | | `sandbox` | `str \| null` | The sandbox's qualified ref (`local:<name>`, `cloud:<name>`), when known. | | `id` | `str` | A random id for this trial. | | `task_name` | `str` | The task directory name. | | `trial_name` | `str` | `<task>_v<variant>`, with `_a<attempt>` for repeats. | | `trial_uri` | `str \| null` | `file://` URI of the variant's output directory. | | `source` | `str` | Always `cua-bench`. | | `agent_info` | `dict` | The agent label, version and model (`{name, provider}`). | | `verifier_result` | `dict \| null` | `{"rewards": {"reward": <float>, ...}}` (numeric evaluation keys included), or `null`. | | `exception_info` | `dict \| null` | Exception type, message, traceback and time when the variant raised, else `null`. | | `started_at` | `str \| null` | When the trial started. | | `finished_at` | `str \| null` | When the trial finished. | | `environment_setup` | `Span \| null` | Sandbox start and task setup. | | `agent_setup` | `null` | Always `null` (agents run in the cb process). | | `agent_execution` | `Span \| null` | The agent's (or the oracle's) run. | | `verifier` | `Span \| null` | The evaluation. | Phase spans (`environment_setup`, `agent_execution`, `verifier`) are `started_at` and `finished_at` timestamps (ISO 8601, UTC). ## `summary.json` `summary.json`: the run's totals, written next to each variant's directory. | Key | Type | Description | | --- | --- | --- | | `schema_version` | `int` | Layout version; a rename or removal bumps it, new keys do not. | | `total` | `int` | Variants run. | | `completed` | `int` | Variants that completed. | | `failed` | `int` | Variants that failed or were cancelled. | | `avg_reward` | `float \| null` | Mean reward over variants with a reward, or `null`. | | `results` | `list[SummaryResult]` | One row per variant (`SummaryResult`). | | `n_total_trials` | `int` | The number of trials (Harbor), equal to `total`. | | `stats` | `SummaryStats` | Harbor-compatible statistics (`SummaryStats`). | | `pass_at_k` | `dict \| null` | `{"<k>": <pass@k>}` with `--attempts` above 1 (binary rewards, unbiased estimator), else `null`. | | `targets` | `list[SummaryTarget]` | Where the variants ran (`SummaryTarget`). | ### `results[]` One row of `summary.json` `results`: a variant's outcome. | Key | Type | Description | | --- | --- | --- | | `results[].task` | `str` | Task directory name. | | `results[].variant` | `int` | Variant index (`--variant-id`). | | `results[].status` | `str` | `completed`, `failed` or `cancelled`. | | `results[].reward` | `float \| null` | The mean of the evaluation's numbers (`true` counts as 1), or `null` when evaluation did not run. | | `results[].pool` | `str \| null` | The Fleet pool that served the claim (cloud runs). | | `results[].duration_s` | `float` | Wall time of the variant in seconds. | | `results[].error` | `str \| null` | The error message when the variant failed. | | `results[].kind` | `str \| null` | `container` or `vm`. | | `results[].runtime` | `str \| null` | The engine (gvisor, runc, qemu, lume, kubevirt) when one was chosen, else `null` (the SDK picked). | | `results[].image_variant` | `str \| null` | The image variant that ran: `rootfs`, `containerdisk` or `lume`. | | `results[].image_digest` | `str \| null` | The pinned `repo@sha256:...` when the SDK reports it. | | `results[].attempt` | `int` | The `--attempts` repeat index (0 for the first). | | `results[].retries` | `int` | How many infrastructure retries (`--retries`) the variant took. | ### `stats` | Key | Type | Description | | --- | --- | --- | | `stats.n_completed` | `int` | Variants that completed. | | `stats.n_errored` | `int` | Variants with no reward that did not complete (the run broke). | | `stats.n_retries` | `int` | Infrastructure retries across the run. | ### `targets[]` Where variants ran: one row per (on, kind, runtime, image variant, image). | Key | Type | Description | | --- | --- | --- | | `targets[].on` | `str` | Where it ran: `local` or `cloud`. | | `targets[].kind` | `str \| null` | `container` or `vm`. | | `targets[].runtime` | `str \| null` | The engine (`gvisor`, `runc`, `qemu`, `lume`, `kubevirt`) when one was chosen; `None`: the SDK picked. | | `targets[].image_variant` | `str \| null` | The image variant that ran: `rootfs`, `containerdisk` or `lume`. | | `targets[].image` | `str \| null` | The pinned digest when known, else the image reference. | | `targets[].count` | `int` | Variants that ran there. | --- # Lume Create and run macOS and Linux VMs on Apple silicon from the CLI, a local HTTP API or MCP. Lume is a VM manager for Apple silicon Macs, built on Apple's Virtualization.framework. It creates vanilla macOS VMs straight from Apple restore images with SSH and autologin ready, runs Linux guests, and exposes the same VM store through the `lume` CLI, a local HTTP API and an MCP server. ```bash test="cli-shape" id="lume-overview" lume create macos-tahoe --ipsw latest --unattended tahoe lume run macos-tahoe lume ssh macos-tahoe 'sw_vers' ``` The [cua SDK](</docs/cua-sdk>) installs and drives Lume itself for local macOS sandboxes; use Lume directly when you want the VMs yourself. | Requirement | | |---|---| | Host | Apple silicon, macOS 13 or later | | Memory | 8 GB free (16 GB for an 8 GB guest) | | Disk | 50 GB free per macOS guest plus its restore image | | Concurrency | Up to two macOS guests at a time (Apple's limit) | ## Next - [Quickstart](</docs/lume/quickstart>): install, create a Tahoe VM, SSH in. - [Manage VMs](</docs/lume/guides/manage-vms>): run, attach, share folders, storage, clone, resize, SIP, updates. - [GPU passthrough](</docs/lume/guides/gpu-passthrough>): unlock Metal capabilities for one workload. - [API and MCP](</docs/lume/guides/api-and-mcp>): drive Lume from scripts or an AI client. - [Linux guests](</docs/lume/guides/linux-guests>): Omarchy on ARM64, and an OpenClaw gateway. - [How Lume works](</docs/lume/concepts/how-lume-works>): unattended setup, SIP, disk growth, telemetry. --- # Quickstart Install Lume, create a vanilla macOS Tahoe VM from an Apple restore image, and connect over SSH. Install Lume, create a macOS Tahoe VM straight from Apple's restore image, and SSH into it. ## Prerequisites - An Apple silicon Mac on macOS 13 or later. - 8 GB free memory and about 50 GB free disk. ## Run ```bash test="cli-shape,lume" id="install" /bin/bash -c "$(curl -fsSL https://cua.ai/lume/install.sh)" lume --version ``` ```bash test="cli-shape" id="lume-create-macos-tahoe" lume create macos-tahoe --ipsw latest --unattended tahoe ``` `--ipsw latest` downloads the newest supported restore image (or pass a local `.ipsw`; `lume ipsw` prints the URL). The `tahoe` preset prepares the guest offline: user `lume` with password `lume`, SSH, autologin, no sleep or screen lock. Creation boots once, checks SSH and leaves the VM stopped. ```bash test="cli-shape" id="lume-run-macos-tahoe" lume run macos-tahoe ``` ## Result The VM's desktop opens in Lume's viewer. From another terminal: ```bash test="cli-shape" id="lume-ssh-macos-tahoe" lume ssh macos-tahoe 'sw_vers; id -un' ``` prints the guest's macOS version and `lume`. Change the password before using the VM for anything sensitive. ![lume ls listing local macOS VMs](</docs-assets/img/lume/lume-ls.png>) ## Clean up ```bash test="cli-shape" id="lume-delete-macos-tahoe" lume stop macos-tahoe lume delete macos-tahoe ``` ## Next - [Manage VMs](</docs/lume/guides/manage-vms>) - [Drive Lume from scripts and agents](</docs/lume/guides/api-and-mcp>) - [CLI reference](</docs/lume/reference/cli>) --- # Manage VMs Run, attach to, share folders with, store, clone, resize and remove Lume VMs, change SIP, and update Lume. ## Inspect ```bash test="cli-shape,lume" id="lume-ls" lume ls ``` ```bash test="cli-shape" id="lume-get-macos-tahoe" lume get macos-tahoe ``` `get` shows state, IP address, display, resources and SSH availability. States: `stopped`, `running`, `provisioning`, and `provisioning (stale)` (an operation died; check the logs and delete the VM). ## Run, attach, stop ```bash test="cli-shape" id="lume-run-macos-tahoe" lume run macos-tahoe # native viewer, attached to this terminal lume run macos-tahoe --detach # background; logs in ~/Library/Logs/lume/ lume run macos-tahoe --display vnc # Screen Sharing instead lume run macos-tahoe --display none # no viewer (alias --no-display) lume run macos-tahoe --display none --vnc disabled # no VNC server at all ``` ```bash test="cli-shape" id="lume-attach-macos-tahoe" lume attach macos-tahoe # reopen a viewer (--display native|vnc) lume restart macos-tahoe # graceful, over SSH lume shutdown macos-tahoe # graceful, over SSH lume stop macos-tahoe # immediate ``` - Closing the native window hides it; the guest keeps running. - Every display mode starts a VNC server unless `--vnc disabled`, which also turns off clipboard shortcuts in the native viewer and conflicts with `--display vnc`, `--vnc-port` and `--vnc-password`. - Drop files or folders from Finder onto the native viewer to copy them over SSH to `/Users/lume/Desktop` (existing names are never overwritten). - One `lume run` process owns a VM. "Auxiliary storage is locked" means another one still runs: `lume stop` it first. ## Share a folder ```bash test="cli-shape" id="lume-run-macos-tahoe-5" lume run macos-tahoe --shared-dir ~/Projects lume run macos-tahoe --shared-dir ~/Projects:ro ``` It appears at `/Volumes/My Shared Files`. The native viewer's **Share Folder** adds one while the guest runs, for that run only. ## Store VMs elsewhere ```bash test="cli-shape" id="lume-config-storage-add" lume config storage add external /Volumes/External/lume lume config storage list lume config storage default external lume run macos-tahoe --storage external ``` The default store is `~/.lume`. Run Lume as the user who owns it: `sudo lume` looks in root's home and cannot see your VMs. ## Clone and delete ```bash test="cli-shape" id="lume-clone-macos-tahoe-macos-tahoe-backu" lume clone macos-tahoe macos-tahoe-backup lume delete macos-tahoe-backup ``` `clone` copies `disk.img` and `nvram.bin` together; never copy one without the other. Disks are sparse, so host usage grows as the guest writes. ## Grow a disk ```bash test="cli-shape" id="lume-set-macos-tahoe" lume stop macos-tahoe lume set macos-tahoe --disk-size 120GB --dry-run # validate only lume set macos-tahoe --disk-size 120GB # add --keep-backup to keep the rollback copy lume run --no-display macos-tahoe lume ssh macos-tahoe 'diskutil info / | grep "Container Total Space"' ``` Increase only, on a stopped VM with the standard layout, no FileVault, and a host macOS at least as new as the guest. `--no-backup` removes automatic rollback. Linux guests: Lume grows the image; grow the partition and filesystem in the guest. [How it works](</docs/lume/concepts/how-lume-works#disk-growth>). ## Change SIP The VM must be macOS, stopped, with a working admin account and Remote Login (true for unattended VMs). Install the Recovery VNC driver once: ```bash skip="host-install" python3 -m pip install --user vncdotool ``` ```bash test="cli-shape" id="lume-sip-off-macos-tahoe" lume sip off macos-tahoe lume sip on macos-tahoe --admin-user admin --admin-password-stdin printf '%s\n' "$LUME_ADMIN_PASSWORD" | \ lume sip off macos-tahoe --admin-user admin --admin-password-stdin --yes ``` A successful run ends with `System Integrity Protection status: disabled.` (or `enabled.`) from a normal boot and leaves the VM stopped. Recovery input accepts admin passwords of lowercase letters, digits and hyphens. For many SIP-disabled VMs, change a seed once and `lume clone` it. [How it works](</docs/lume/concepts/how-lume-works#sip>). ## Update Lume ```bash test="cli-shape" id="lume-check-update" lume check-update lume update --apply lume channel set stable # or nightly; takes effect on the next update ``` ```bash skip="host-install" curl -fsSL https://cua.ai/lume/install.sh | bash -s -- --channel nightly curl -fsSL https://cua.ai/lume/install.sh | \ LUME_VERSION=nightly-lume-v0.5.4-nightly.20260812.123456789 bash # pin one nightly ``` Stable is the default. A pinned nightly does not change the saved channel. ## Troubleshooting | Symptom | Fix | |---|---| | Auxiliary storage is locked | Another `lume run` owns the VM: `lume stop <vm>`. | | `sudo lume` cannot find a VM | Run as the user who created it. | | SSH unavailable | Check `lume get <vm>` shows `running` with an IP; unattended VMs use `lume` / `lume`. | | Sequoia shows an Accessibility step on first boot | Known ([#2155](https://github.com/trycua/cua/issues/2155)); Tahoe is the verified preset. | --- # Unlock Metal for a workload Unlock a tested Metal capability profile for one workload inside a Lume macOS VM. A stock macOS guest reports conservative Metal capabilities, so some apps skip GPU paths the paravirtualized device can run. A small MIT shim, injected into one process, raises two answers: `supportsFamily:` up to a configured Apple family, and the maximum threadgroup memory. It does not pass a physical GPU through, patch the host, or touch the guest kernel. Background and benchmarks: [technical writeup](/blog/gpu-passthrough-macos-vms). **Warning** Experimental. Tested on Lume 0.5.1, an M1 Ultra host on macOS 26.6.1, and the Tahoe guest at macOS 26.5.2, with a capability probe, two llama.cpp workloads and one MLX-LM run. Reported support is not proof every Metal API works: test each workload. ## 1. Build the shim On an Apple silicon Mac with Xcode Command Line Tools: ```bash skip="no-runner" git clone https://github.com/trycua/cua.git cd cua/libs/lume/metal-capability-shim ./Scripts/build.sh ./Scripts/verify.sh # architectures, signatures, checksums ``` ```text output dist/ ├── LumeMetalCapabilities-arm64.dylib ├── LumeMetalCapabilities-arm64e.dylib ├── SHA256SUMS └── metal-capabilities ``` Most guest workloads are `arm64`; check yours with `lipo -archs <executable>`. ## 2. Enable the host feature level Applies to VMs your user launches; restart the VM so the device is recreated: ```bash test="cli-shape" id="lume-stop-my-vm" lume stop my-vm defaults write com.apple.gpusw.ParavirtualizedGraphics \ ForceUnrestrictedDeviceFeatureLevel -bool true defaults read com.apple.gpusw.ParavirtualizedGraphics \ ForceUnrestrictedDeviceFeatureLevel lume run my-vm ``` `defaults read` prints `1`. Undo with `defaults delete` of the same key and a VM restart. ## 3. Copy it into the guest ```bash test="cli-shape" id="lume-ssh-my-vm" lume ssh my-vm "mkdir -p '/Users/lume/.local/share/lume/metal-capabilities'" VM_IP=$(lume get my-vm --format json | jq -r '.[0].ipAddress') scp ./dist/LumeMetalCapabilities-arm64.dylib ./dist/metal-capabilities \ "lume@${VM_IP}:/Users/lume/.local/share/lume/metal-capabilities/" ``` Compare `shasum -a 256` of both files on the host and in the guest. ## 4. Verify with the probe ```bash test="cli-shape" id="lume-ssh-my-vm-4" lume ssh my-vm \ "DYLD_INSERT_LIBRARIES='/Users/lume/.local/share/lume/metal-capabilities/LumeMetalCapabilities-arm64.dylib' \ LUME_METAL_APPLE_FAMILY_MAX=1009 \ '/Users/lume/.local/share/lume/metal-capabilities/metal-capabilities' 1009" ``` Run it once without the two variables to see the stock answers. On the tested guest `supportsFamily:1009` went from `false` to `true` and threadgroup memory from 32,768 to 65,536 bytes. | Variable | Default | Effect | | --- | --: | --- | | `LUME_METAL_APPLE_FAMILY_MAX` | required | Apple-family ceiling, `1001` to `1999`; tested `1009` (Apple 9). Missing or invalid means stock behavior. | | `LUME_METAL_MAX_THREADGROUP_MEMORY` | `65536` | Minimum reported threadgroup memory. | | `LUME_METAL_RECOMMENDED_WORKING_SET_SIZE` | unchanged | Reported working set, only when set. | ## 5. Run your workload Set the same two variables on the one process (and its children) that needs them: ```bash test="cli-shape" id="lume-ssh-my-vm-5" lume ssh my-vm \ "DYLD_INSERT_LIBRARIES='/Users/lume/.local/share/lume/metal-capabilities/LumeMetalCapabilities-arm64.dylib' \ LUME_METAL_APPLE_FAMILY_MAX=1009 \ /absolute/path/to/your-workload first-argument" ``` Never use `launchctl setenv DYLD_INSERT_LIBRARIES`; it affects the whole login session. For a long-running service, use a per-user LaunchAgent in the guest with the variables in its `EnvironmentVariables`: ```xml skip="placeholder" <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.trycua.lume.metal-workload</string> <key>ProgramArguments</key> <array> <string>/absolute/path/to/your-workload</string> <string>first-argument</string> </array> <key>EnvironmentVariables</key> <dict> <key>DYLD_INSERT_LIBRARIES</key> <string>/Users/lume/.local/share/lume/metal-capabilities/LumeMetalCapabilities-arm64.dylib</string> <key>LUME_METAL_APPLE_FAMILY_MAX</key> <string>1009</string> </dict> <key>StandardErrorPath</key> <string>/Users/lume/Library/Logs/Lume/metal-workload.stderr.log</string> </dict> </plist> ``` ```bash skip="macos-vm" PLIST="$HOME/Library/LaunchAgents/com.trycua.lume.metal-workload.plist" mkdir -p "$HOME/Library/Logs/Lume" plutil -lint "$PLIST" launchctl bootstrap "gui/$(id -u)" "$PLIST" launchctl print "gui/$(id -u)/com.trycua.lume.metal-workload" # remove: launchctl bootout "gui/$(id -u)/com.trycua.lume.metal-workload" ``` If the workload fails, run it once without the shim before debugging further. ## In Cua Spaces The GPU acceleration checkbox in New Space (Resources), `--gpu` on `cua spaces create`, or `gpu="paravirtual"` in the SDK does step 2 for you: cua sets the feature level before each start of that VM and removes it when the last GPU VM is deleted, if cua set it. While set, it applies to every VM your user starts. Steps 1 and 3 to 5 stay per workload. See [Add a Space](</docs/spaces/guides/add-a-space#gpu-acceleration>). --- # Drive Lume from scripts and agents Drive Lume from scripts through its local HTTP API, or from an AI client through its MCP server. Both interfaces control the same VM store as the CLI. Use the API for scripts and services, MCP for AI clients. ## HTTP API The installer runs `lume serve` at login on port 7777 (install with `--no-background-service` to manage it yourself): ```bash test="cli-shape" id="lume-serve" lume serve # localhost:7777 lume serve --port 7778 ``` ```bash skip="no-runner" curl http://localhost:7777/lume/vms ``` It listens on `localhost` only. Keep it there unless you have a network boundary and an authentication plan. Endpoints: [HTTP API reference](</docs/lume/reference/http-api>). ## MCP server `lume serve --mcp` speaks MCP over stdio, so the client starts it. Use the path from `which lume`: ```json test="config" id="json-config" schema="mcp-client-config" { "mcpServers": { "lume": { "command": "/Users/your-name/.local/bin/lume", "args": ["serve", "--mcp"] } } } ``` Restart the client. It can list, create, run, stop, clone and resize VMs and run SSH commands in unattended guests ([MCP tools](</docs/lume/reference/mcp-tools>)). Test the server with: ```bash skip="third-party-cli" npx @modelcontextprotocol/inspector lume serve --mcp ``` The MCP server can create, delete and SSH into VMs. Configure it only for clients you trust, and give agents a dedicated VM. --- # Run Omarchy and OpenClaw Run Omarchy on an ARM64 Arch Linux guest with optional Rosetta, and run an OpenClaw gateway in an isolated Lume VM. ## Omarchy on ARM64 Official Omarchy 4.0.1 source (commit `13f18b2cb7286fb54f87daf571a031aa6af3d8f0`) on an Arch Linux ARM base. It boots into Hyprland, takes keyboard and pointer input, runs ARM64 apps, and runs x86-64 Linux programs through Rosetta. **Warning: Experimental** Not an official Omarchy ARM64 release (its ISO and packages are x86-64 only). Graphics are software (`llvmpipe`, no Vulkan device), there is no audio on the tested kernel (`CONFIG_SND_VIRTIO` off), and 27 Omarchy packages are missing from the ARM repositories. Cua Driver support on Hyprland is tracked separately ([platform support](</docs/cua-driver/concepts/platform-support>)). ![Omarchy running in an ARM64 Lume VM](</docs-assets/img/lume/omarchy-arm64/desktop.png>) Tested with Lume 0.5.3, 8 vCPUs, 8 GiB, an 80 GiB disk and kernel `7.2.0-2-aarch64-ARCH`. ### 1. Get and verify Archboot ```bash skip="no-runner" curl -fLO https://release.archboot.com/aarch64/2026.08/iso/archboot-2026.08.25-02.28-7.2.0-2-aarch64-ARCH-aarch64.iso curl -fLO https://release.archboot.com/aarch64/2026.08/b2sum.txt curl -fLO https://release.archboot.com/aarch64/2026.08/b2sum.txt.sig gpg --keyserver hkps://keyserver.ubuntu.com \ --recv-keys 5B7E3FB71B7F10329A1C03AB771DF6627EDF681F gpg --verify b2sum.txt.sig b2sum.txt b2sum archboot-2026.08.25-02.28-7.2.0-2-aarch64-ARCH-aarch64.iso ``` Check the key fingerprint (`5B7E 3FB7 1B7F 1032 9A1C 03AB 771D F662 7EDF 681F`, Tobias Powalowski) through an independent Arch channel. The tested ISO digest: ```text output 562bd4c54d879d7d2b80f565186b3804456f99a6e3913f7eda7bf9b0e9f132d6e9fcbd083998cb3dcbbf065528a2a404c4ce6151f90b3228dade788237ae1bc7 ``` ### 2. Create the VM and install Arch ```bash test="cli-shape" id="lume-create-omarchy-arm64" lume create omarchy-arm64 \ --os linux \ --cpu 8 \ --memory 8GB \ --disk-size 80GB \ --display 1920x1200 ``` ```bash test="cli-shape" id="lume-run-omarchy-arm64" lume run omarchy-arm64 \ --mount "$PWD/archboot-2026.08.25-02.28-7.2.0-2-aarch64-ARCH-aarch64.iso" \ --display vnc ``` In the Archboot root shell (the script erases `/dev/vda` after you type `ERASE`): ```bash skip="macos-vm" curl -fLO https://raw.githubusercontent.com/trycua/cua/main/docs/public/scripts/omarchy-arm64-lume/install-arch.sh chmod +x install-arch.sh TIMEZONE=UTC ./install-arch.sh /dev/vda arch-chroot /mnt passwd omarchy umount -R /mnt poweroff ``` ### 3. Install Omarchy Boot without the ISO, sign in as `omarchy`, and run the pinned installer: ```bash test="cli-shape" id="lume-run-omarchy-arm64-2" lume run omarchy-arm64 --display vnc ``` ```bash skip="macos-vm" curl -fLO https://raw.githubusercontent.com/trycua/cua/main/docs/public/scripts/omarchy-arm64-lume/install-omarchy.sh chmod +x install-omarchy.sh sudo ./install-omarchy.sh sudo reboot ``` The guest reboots into the Omarchy desktop. ### 4. Optional: Rosetta With Rosetta on the host, Lume attaches it as a `rosetta` virtiofs share: ```bash skip="macos-vm" curl -fLO https://raw.githubusercontent.com/trycua/cua/main/docs/public/scripts/omarchy-arm64-lume/enable-rosetta.sh chmod +x enable-rosetta.sh sudo ./enable-rosetta.sh sudo reboot ``` A static x86-64 binary then runs directly (a BusyBox build prints `x86_64` for `uname -m`). Dynamically linked programs also need x86-64 libraries. The missing packages are listed in `/var/log/omarchy-v4.0.1-unavailable-arm64-packages.txt`. Most are packaging gaps (ARM64 upstream builds or architecture-independent), seven are plausible source builds, and `obsidian` has no ARM64 Linux build. Add them one at a time; never install x86-64 packages into the ARM root. ## OpenClaw in a macOS VM Keep an OpenClaw gateway isolated from your daily macOS and run it headless. ```bash test="cli-shape" id="lume-create-openclaw" lume create openclaw --ipsw latest --unattended tahoe lume run --no-display openclaw lume ssh openclaw ``` In the guest: ```bash skip="third-party-cli" curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh \ | bash -s -- --no-onboard openclaw onboard --install-daemon openclaw gateway status ``` The gateway listens on the guest's loopback. From the host, forward it and open `http://127.0.0.1:18789`: ```bash skip="placeholder" ssh -N -L 18789:127.0.0.1:18789 lume@<VM_IP> # VM_IP from `lume get openclaw` ``` For iMessage and other macOS-only channels, run with a display (`lume run openclaw`), sign in to Messages, and grant Full Disk Access and Automation ([OpenClaw iMessage guide](https://docs.openclaw.ai/providers/imessage)). Then save a golden copy: ```bash test="cli-shape" id="lume-stop-openclaw-3" lume stop openclaw lume clone openclaw openclaw-golden lume run --no-display openclaw ``` The clone holds your provider and channel credentials; protect it like the original. If the gateway does not start after boot, run `openclaw gateway install` in the guest. --- # How Lume works Offline unattended setup, SIP through paired Recovery, macOS disk growth with rollback, and what Lume telemetry collects. ## Unattended setup `lume create --unattended tahoe` (or `sequoia`) never clicks through Setup Assistant. Lume: 1. installs macOS from the IPSW into a new disk; 2. boots once so macOS writes its first-boot state, then stops; 3. mounts the Data volume on the host and writes the `lume` account, SSH, autologin and no-sleep settings as plain files; 4. boots again and finishes only when SSH answers. Writing files avoids depending on display timing or a release's Setup Assistant layout. Tahoe is the verified preset; Sequoia can still show an Accessibility step on first display boot ([#2155](https://github.com/trycua/cua/issues/2155)). ## SIP On Apple silicon, SIP lives in a LocalPolicy signed by the Secure Enclave (Virtualization.framework provides it for VMs). It belongs to a paired state: `disk.img` (macOS, personalized boot files, Recovery), `nvram.bin` (security and anti-replay state) and the VM configuration. Editing files cannot sign a policy, so `lume sip` uses three boots: 1. normal: check the admin account over SSH; 2. Recovery: run `csrutil enable|disable` in Terminal over a temporary VNC session, reading the prompt and result with OCR (Lume stops the run if Tahoe Recovery does not halt); 3. normal: `csrutil status` over SSH. Only the canonical `System Integrity Protection status: enabled.` or `disabled.` is accepted; a customized policy is rejected. `lume clone` copies disk and NVRAM together, which is why cloning a prepared seed works and copying one file does not. ## Disk growth A macOS disk is laid out as ISC, main APFS, then RecoveryOS. Growing the image appends space after RecoveryOS, where APFS cannot reach it: ```text output [ ISC ][ main APFS ][ RecoveryOS ][ free ] -> [ ISC ][ expanded main APFS ][ RecoveryOS ] ``` `lume set --disk-size` on a stopped VM copies RecoveryOS to the new end with its type, id, attributes and contents intact, rewrites both GPT copies, and grows APFS with `diskutil`. It is a transaction: a copy-on-write backup first, a marker that blocks run, clone and push meanwhile, verification of the moved bytes and the new capacity, and restore on failure (also on the next attempt after a crash). Unfamiliar layouts and FileVault guests are refused. Linux images have no RecoveryOS: Lume grows the image and the guest grows its filesystem. ## Telemetry Lume sends content-free, pseudonymous events to PostHog EU, on by default, after a first-run notice. ```bash test="cli-shape" id="lume-config-telemetry-status" lume config telemetry status lume config telemetry disable lume config telemetry enable lume config telemetry reset-id ``` `LUME_TELEMETRY_ENABLED` overrides the setting per process. Events carry a fixed name and bounded fields (Lume version, host OS major version and arch, CI flag, transport, allowlisted operation, success, error class, duration bucket), capped at 1,000 per process per hour. They never include VM or image names, paths, URLs, arguments, MCP payloads, SSH commands or output, VNC credentials, screenshots or raw errors. GeoIP is disabled. The update check (`lume check-update`, `lume update`) is a separate request to GitHub Releases, cached for 20 hours, without the telemetry id: ```bash test="cli-shape,lume" id="lume-check-update" LUME_UPDATE_CHECK=false lume check-update ``` --- # Overview Every lume command, HTTP endpoint and MCP tool, generated from the Lume binary. ## Commands | Command group | Description | | --- | --- | | [`lume`](</docs/lume/reference/cli>) | Synopsis, global options, exit codes and every command. | | [VMs](</docs/lume/reference/cli/vms>) | Create, run, inspect, change, clone and delete virtual machines. | | [Images](</docs/lume/reference/cli/images>) | Pull, push and convert VM images, find restore images, and prune the cache. | | [Guest access](</docs/lume/reference/cli/guest>) | Prepare unattended setup, open SSH sessions and change SIP in a guest. | | [Server](</docs/lume/reference/cli/server>) | Run the HTTP API and MCP server, read its logs, and dump the CLI and API definitions. | | [Configuration](</docs/lume/reference/cli/config>) | Storage locations, the image cache and telemetry settings. | | [Updates](</docs/lume/reference/cli/updates>) | Check for, apply and choose the channel of Lume updates. | ## HTTP API | API | Description | | --- | --- | | [HTTP API](</docs/lume/reference/http-api>) | Every endpoint `lume serve` exposes, with parameters and examples. | ## MCP tools | Tools | Description | | --- | --- | | [MCP tools](</docs/lume/reference/mcp-tools>) | Every tool Lume's MCP server exposes, with its parameters. | --- # lume A lightweight CLI and local API server to build, run and manage macOS VMs. ```text output lume [OPTIONS] <COMMAND> ``` Run `lume <command> --help` for the build you have. ## Commands ### VMs Create, run, inspect, change, clone and delete virtual machines. [Reference](</docs/lume/reference/cli/vms>). | Command | Description | | --- | --- | | [`lume create`](</docs/lume/reference/cli/vms#lume-create>) | Create a new virtual machine. | | [`lume run`](</docs/lume/reference/cli/vms#lume-run>) | Run a virtual machine. | | [`lume attach`](</docs/lume/reference/cli/vms#lume-attach>) | Open a viewer for a running virtual machine. | | [`lume stop`](</docs/lume/reference/cli/vms#lume-stop>) | Stop a virtual machine. | | [`lume shutdown`](</docs/lume/reference/cli/vms#lume-shutdown>) | Gracefully shut down a virtual machine. | | [`lume restart`](</docs/lume/reference/cli/vms#lume-restart>) | Gracefully restart a virtual machine. | | [`lume ls`](</docs/lume/reference/cli/vms#lume-ls>) | List virtual machines. | | [`lume get`](</docs/lume/reference/cli/vms#lume-get>) | Get detailed information about a virtual machine. | | [`lume set`](</docs/lume/reference/cli/vms#lume-set>) | Set new values for CPU, memory, and disk size of a virtual machine. | | [`lume clone`](</docs/lume/reference/cli/vms#lume-clone>) | Clone an existing virtual machine. | | [`lume delete`](</docs/lume/reference/cli/vms#lume-delete>) | Delete a virtual machine. | ### Images Pull, push and convert VM images, find restore images, and prune the cache. [Reference](</docs/lume/reference/cli/images>). | Command | Description | | --- | --- | | [`lume images`](</docs/lume/reference/cli/images#lume-images>) | List available macOS images from local cache. | | [`lume pull`](</docs/lume/reference/cli/images#lume-pull>) | Pull a prebuilt or custom macOS image from an OCI-compatible registry. | | [`lume push`](</docs/lume/reference/cli/images#lume-push>) | Push a macOS VM to an OCI-compatible registry. | | [`lume convert`](</docs/lume/reference/cli/images#lume-convert>) | Convert a legacy Lume image to OCI-compliant format. | | [`lume ipsw`](</docs/lume/reference/cli/images#lume-ipsw>) | Get macOS restore image IPSW URL. | | [`lume prune`](</docs/lume/reference/cli/images#lume-prune>) | Remove cached images. | ### Guest access Prepare unattended setup, open SSH sessions and change SIP in a guest. [Reference](</docs/lume/reference/cli/guest>). | Command | Description | | --- | --- | | [`lume setup`](</docs/lume/reference/cli/guest#lume-setup>) | Prepare unattended macOS setup. | | [`lume ssh`](</docs/lume/reference/cli/guest#lume-ssh>) | Connect to a VM via SSH or execute commands remotely. | | [`lume sip`](</docs/lume/reference/cli/guest#lume-sip>) | Enable or disable System Integrity Protection on a macOS VM. | ### Server Run the HTTP API and MCP server, read its logs, and dump the CLI and API definitions. [Reference](</docs/lume/reference/cli/server>). | Command | Description | | --- | --- | | [`lume serve`](</docs/lume/reference/cli/server#lume-serve>) | Start the VM management server. | | [`lume logs`](</docs/lume/reference/cli/server#lume-logs>) | View lume serve logs. | | [`lume logs info`](</docs/lume/reference/cli/server#lume-logs-info>) | View info logs from the daemon. | | [`lume logs error`](</docs/lume/reference/cli/server#lume-logs-error>) | View error logs from the daemon. | | [`lume logs all`](</docs/lume/reference/cli/server#lume-logs-all>) | View both info and error logs from the daemon. | | [`lume dump-docs`](</docs/lume/reference/cli/server#lume-dump-docs>) | Output CLI and API documentation as JSON for tooling and integrations. | ### Configuration Storage locations, the image cache and telemetry settings. [Reference](</docs/lume/reference/cli/config>). | Command | Description | | --- | --- | | [`lume config`](</docs/lume/reference/cli/config#lume-config>) | Get or set lume configuration. | | [`lume config get`](</docs/lume/reference/cli/config#lume-config-get>) | Get current configuration. | | [`lume config storage`](</docs/lume/reference/cli/config#lume-config-storage>) | Manage VM storage locations. | | [`lume config storage add`](</docs/lume/reference/cli/config#lume-config-storage-add>) | Add a new VM storage location. | | [`lume config storage remove`](</docs/lume/reference/cli/config#lume-config-storage-remove>) | Remove a VM storage location. | | [`lume config storage list`](</docs/lume/reference/cli/config#lume-config-storage-list>) | List all VM storage locations. | | [`lume config storage default`](</docs/lume/reference/cli/config#lume-config-storage-default>) | Set the default VM storage location. | | [`lume config cache`](</docs/lume/reference/cli/config#lume-config-cache>) | Manage image cache settings. | | [`lume config cache dir`](</docs/lume/reference/cli/config#lume-config-cache-dir>) | Get or set cache directory. | | [`lume config cache enable`](</docs/lume/reference/cli/config#lume-config-cache-enable>) | Enable image caching. | | [`lume config cache disable`](</docs/lume/reference/cli/config#lume-config-cache-disable>) | Disable image caching. | | [`lume config cache status`](</docs/lume/reference/cli/config#lume-config-cache-status>) | Show cache status and directory. | | [`lume config telemetry`](</docs/lume/reference/cli/config#lume-config-telemetry>) | Manage pseudonymous telemetry settings. | | [`lume config telemetry status`](</docs/lume/reference/cli/config#lume-config-telemetry-status>) | Show current telemetry status. | | [`lume config telemetry enable`](</docs/lume/reference/cli/config#lume-config-telemetry-enable>) | Enable pseudonymous telemetry. | | [`lume config telemetry disable`](</docs/lume/reference/cli/config#lume-config-telemetry-disable>) | Disable pseudonymous telemetry. | | [`lume config telemetry reset-id`](</docs/lume/reference/cli/config#lume-config-telemetry-reset-id>) | Delete the pseudonymous installation ID and registration markers. | | [`lume config registry`](</docs/lume/reference/cli/config#lume-config-registry>) | Manage container registry settings. | | [`lume config registry status`](</docs/lume/reference/cli/config#lume-config-registry-status>) | Show current registry configuration. | | [`lume config registry type`](</docs/lume/reference/cli/config#lume-config-registry-type>) | Get or set registry type. | | [`lume config registry ghcr`](</docs/lume/reference/cli/config#lume-config-registry-ghcr>) | Configure GitHub Container Registry settings. | | [`lume config registry gcs`](</docs/lume/reference/cli/config#lume-config-registry-gcs>) | Configure GCS registry settings. | | [`lume config network`](</docs/lume/reference/cli/config#lume-config-network>) | Manage network settings. | | [`lume config network interfaces`](</docs/lume/reference/cli/config#lume-config-network-interfaces>) | List available network interfaces for bridged networking. | ### Updates Check for, apply and choose the channel of Lume updates. [Reference](</docs/lume/reference/cli/updates>). | Command | Description | | --- | --- | | [`lume check-update`](</docs/lume/reference/cli/updates#lume-check-update>) | Check whether a newer Lume release is available. | | [`lume update`](</docs/lume/reference/cli/updates#lume-update>) | Check for a Lume update and optionally apply it. | | [`lume channel`](</docs/lume/reference/cli/updates#lume-channel>) | Inspect or change the stable/nightly update channel; selection does not install. | | [`lume channel status`](</docs/lume/reference/cli/updates#lume-channel-status>) | Show selected and current release channels. | | [`lume channel set`](</docs/lume/reference/cli/updates#lume-channel-set>) | Save stable or nightly as the update channel. | ## Global options `--help` prints help for any command; `--version` prints the version. ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | The command failed (for example the VM does not exist or the operation errored). | | `64` | Usage error: an unknown command or option, a missing argument, or an invalid value. | --- # VMs Create, run, inspect, change, clone and delete virtual machines. | Command | Description | | --- | --- | | [`lume create`](#lume-create) | Create a new virtual machine. | | [`lume run`](#lume-run) | Run a virtual machine. | | [`lume attach`](#lume-attach) | Open a viewer for a running virtual machine. | | [`lume stop`](#lume-stop) | Stop a virtual machine. | | [`lume shutdown`](#lume-shutdown) | Gracefully shut down a virtual machine. | | [`lume restart`](#lume-restart) | Gracefully restart a virtual machine. | | [`lume ls`](#lume-ls) | List virtual machines. | | [`lume get`](#lume-get) | Get detailed information about a virtual machine. | | [`lume set`](#lume-set) | Set new values for CPU, memory, and disk size of a virtual machine. | | [`lume clone`](#lume-clone) | Clone an existing virtual machine. | | [`lume delete`](#lume-delete) | Delete a virtual machine. | Every command also accepts the [global options](</docs/lume/reference/cli#global-options>). ## `lume create` Create a new virtual machine. ```text output lume create [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name for the virtual machine. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--os` | string | `macOS` | Operating system to install: macOS or linux. | | `--cpu` | integer | `4` | Number of CPU cores. | | `--memory` | string | `8GB` | Memory size (8, 8GB or 8192MB; a bare number is GB). | | `--disk-size` | string | | Disk size (100, 100GB or 102400MB; a bare number is GB). Defaults to 100GB for macOS and 50GB for Linux. | | `--display` | string | `1024x768` | Display resolution as WIDTHxHEIGHT. | | `--ipsw` | string | | Path to a macOS restore image (IPSW), or 'latest' to download the latest supported version. Required for macOS VMs. | | `--storage` | string | | VM storage location name, or a direct path to the VM location. | | `--unattended` | string | | Prepare macOS unattended setup offline after install. Built-in presets: sequoia, tahoe; a YAML path is accepted for compatibility. macOS VMs only. | | `--debug-dir` | string | | Compatibility option; ignored by offline setup. | | `--vnc-port` | integer | `0` | Port for the temporary verification VNC server (0 picks a free port). | | `--network` | string | `nat` | Network mode: nat, bridged (picks an interface), or bridged:<interface> (for example bridged:en0). | | `--debug` | boolean | `false` | Compatibility flag; ignored by offline setup. | | `--no-display` | boolean | `false` | Compatibility flag; offline setup verifies headlessly. | **Examples** ```bash test="cli-shape" id="ref-lume-create" # A macOS VM from the latest IPSW, set up unattended (SSH user lume/lume) lume create macos-tahoe --ipsw latest --unattended tahoe # A Linux VM lume create dev --os linux --cpu 4 --memory 8GB --disk-size 50GB # Bridge the VM onto the host's en0 network lume create macos-tahoe --ipsw latest --network bridged:en0 ``` ## `lume run` Run a virtual machine. ```text output lume run [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the VM, or an image to pull and run (format: name or name:tag). | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--display` | | `vnc` \| `native` \| `none` | `native` | Local viewer to open. The VNC server stays available in every mode. | | `--log-file` | | string | | Log path for --detach (default: ~/Library/Logs/lume/{vm}.log). | | `--shared-dir` | | string | | Directory to share with the VM: a path (read-write) or path:ro / path:rw. Repeatable. | | `--mount` | | string | | For Linux VMs only, a read-only disk image to attach. | | `--usb-storage` | | string | | Disk image to attach as a USB mass storage device. Repeatable. | | `--disk` | | string | | Disk image to attach as a read-write virtio-blk device. Repeatable. | | `--registry` | | string | `ghcr.io` | Container registry to pull images from. | | `--organization` | | string | `trycua` | Organization to pull images from. | | `--vnc` | | `enabled` \| `disabled` | `enabled` | VNC server policy. disabled starts the VM with no VNC listener and reports a null vncUrl. | | `--vnc-port` | | integer | `0` | Port for the VNC server (0 picks a free port). | | `--vnc-password` | | string | | Password for the VNC server (default: a random passphrase). | | `--recovery-mode` | | boolean | `false` | For macOS VMs only, boot into recovery mode. | | `--storage` | | string | | VM storage location name, or a direct path to the VM location. | | `--disk-path` | | string | | Use this disk image instead of disk.img in the VM directory. | | `--nvram-path` | | string | | Use this NVRAM file instead of nvram.bin in the VM directory. | | `--network` | | string | | Network override for this run: nat, bridged, or bridged:<interface> (default: the VM's configured mode). | | `--no-display` | `-d` | boolean | `false` | Compatibility alias for --display none. | | `--detach` | | boolean | `false` | Run the VM in the background and return immediately. | | `--clipboard` | | boolean | `false` | Sync the clipboard both ways over SSH. Automatic with the native macOS display. | **Examples** ```bash test="cli-shape" id="ref-lume-run" lume run my-vm # Headless, with a read-only shared folder lume run my-vm --shared-dir ~/src:ro --display none # In the background with no VNC listener lume run my-vm --detach --display none --vnc disabled ``` ## `lume attach` Open a viewer for a running virtual machine. The native display is used when the `lume run` process that owns the VM supports live attachment; VNC is the fallback. ```text output lume attach [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the virtual machine. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--display` | `native` \| `vnc` | | Viewer to open (default: native, falling back to VNC). | | `--storage` | string | | VM storage location to use. | **Examples** ```bash test="cli-shape" id="ref-lume-attach" lume attach my-vm # Open Screen Sharing over VNC lume attach my-vm --display vnc ``` ## `lume stop` Stop a virtual machine. ```text output lume stop [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the VM to stop. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--storage` | string | | VM storage location name, or a direct path to the VM location. | | `--timeout` | integer | `10` | Seconds to wait for a graceful shutdown before forcing power off. | | `--force` | boolean | `false` | Power off the VM immediately instead of attempting a graceful shutdown first. | **Examples** ```bash test="cli-shape" id="ref-lume-stop" lume stop my-vm lume stop my-vm --force ``` ## `lume shutdown` Gracefully shut down a virtual machine. Requests the operation inside the guest over SSH. VMs created with --unattended use lume/lume credentials by default. ```text output lume shutdown [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the virtual machine. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--user` | `-u` | string | `lume` | SSH username. | | `--password` | `-p` | string | `lume` | SSH and sudo password. | | `--storage` | | string | | VM storage location to use. | | `--timeout` | `-t` | integer | `30` | SSH command timeout in seconds. | **Examples** ```bash test="cli-shape" id="ref-lume-shutdown" lume shutdown my-vm # With another SSH user and a longer timeout lume shutdown my-vm --user admin --timeout 60 ``` ## `lume restart` Gracefully restart a virtual machine. Requests the operation inside the guest over SSH. VMs created with --unattended use lume/lume credentials by default. ```text output lume restart [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the virtual machine. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--user` | `-u` | string | `lume` | SSH username. | | `--password` | `-p` | string | `lume` | SSH and sudo password. | | `--storage` | | string | | VM storage location to use. | | `--timeout` | `-t` | integer | `30` | SSH command timeout in seconds. | **Examples** ```bash test="cli-shape" id="ref-lume-restart" lume restart my-vm # With another SSH user and a longer timeout lume restart my-vm --user admin --timeout 60 ``` ## `lume ls` List virtual machines. ```text output lume ls [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--format` | `-f` | `json` \| `text` | `text` | Output format. | | `--storage` | | string | | Show only VMs in this storage location. | **Examples** ```bash test="cli-shape" id="ref-lume-ls" lume ls lume ls --format json ``` ## `lume get` Get detailed information about a virtual machine. ```text output lume get [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the VM. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--format` | `-f` | `json` \| `text` | `text` | Output format. | | `--storage` | | string | | VM storage location name, or a direct path to the VM location. | **Examples** ```bash test="cli-shape" id="ref-lume-get" lume get my-vm # Machine-readable, including the IP address and VNC URL lume get my-vm --format json ``` ## `lume set` Set new values for CPU, memory, and disk size of a virtual machine. ```text output lume set [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the VM. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--cpu` | integer | | New number of CPU cores. | | `--memory` | string | | New memory size (8, 8GB or 8192MB; a bare number is GB). | | `--disk-size` | string | | New total disk size, increase only. For macOS VMs this relocates the recovery partition and grows the main APFS container; the VM must be stopped and it may take several minutes. | | `--display` | string | | New display resolution as WIDTHxHEIGHT. | | `--machine-identifier` | string | | New machine identifier: 'random', or a base64 identifier from `lume get --format json` to give this VM an existing machine identity (per-machine licenses then see one machine). macOS VMs only; the VM must be stopped. | | `--mac-address` | string | | New MAC address: 'random' (locally administered) or aa:bb:cc:dd:ee:ff. The VM must be stopped. The DHCP lease follows the MAC, so two running VMs with one MAC claim the same IP. | | `--storage` | string | | VM storage location name, or a direct path to the VM location. | | `--no-backup` | boolean | `false` | Skip the pre-resize disk backup (macOS resize only). Faster, but a failure cannot be rolled back. | | `--keep-backup` | boolean | `false` | Keep the pre-resize backup after a successful macOS resize. | | `--dry-run` | boolean | `false` | Validate the disk-resize plan and print it without changing anything (macOS resize only). | **Examples** ```bash test="cli-shape" id="ref-lume-set" lume set my-vm --cpu 8 --memory 16GB # Check a disk resize plan, then run it without --dry-run lume set my-vm --disk-size 120GB --dry-run lume set my-vm --mac-address random ``` ## `lume clone` Clone an existing virtual machine. ```text output lume clone [OPTIONS] <name> <new-name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the source VM. | | `<new-name>` | string | required | Name for the cloned VM. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--source-storage` | string | | Source VM storage location. | | `--dest-storage` | string | | Destination VM storage location. | **Examples** ```bash test="cli-shape" id="ref-lume-clone" # Keep a golden copy before changing a VM lume clone macos-tahoe macos-tahoe-backup # Copy a VM to another storage location lume clone macos-tahoe macos-tahoe --source-storage default --dest-storage external ``` ## `lume delete` Delete a virtual machine. ```text output lume delete [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the VM to delete. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--storage` | string | | VM storage location name, or a direct path to the VM location. | | `--force` | boolean | `false` | Delete without asking for confirmation. | **Examples** ```bash test="cli-shape" id="ref-lume-delete" lume delete my-vm # Without the confirmation prompt lume delete my-vm --force ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | The command failed (for example the VM does not exist or the operation errored). | | `64` | Usage error: an unknown command or option, a missing argument, or an invalid value. | --- # Images Pull, push and convert VM images, find restore images, and prune the cache. | Command | Description | | --- | --- | | [`lume images`](#lume-images) | List available macOS images from local cache. | | [`lume pull`](#lume-pull) | Pull a prebuilt or custom macOS image from an OCI-compatible registry. | | [`lume push`](#lume-push) | Push a macOS VM to an OCI-compatible registry. | | [`lume convert`](#lume-convert) | Convert a legacy Lume image to OCI-compliant format. | | [`lume ipsw`](#lume-ipsw) | Get macOS restore image IPSW URL. | | [`lume prune`](#lume-prune) | Remove cached images. | Every command also accepts the [global options](</docs/lume/reference/cli#global-options>). ## `lume images` List available macOS images from local cache. ```text output lume images [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--organization` | string | `trycua` | Organization to list images for. | **Examples** ```bash test="cli-shape" id="ref-lume-images" lume images ``` ## `lume pull` Pull a prebuilt or custom macOS image from an OCI-compatible registry. ```text output lume pull [OPTIONS] <image> [<name>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<image>` | string | required | Image to pull (format: name:tag). | | `<name>` | string | optional | Name for the resulting VM (defaults to the image name without its tag). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--registry` | string | `ghcr.io` | Container registry to pull from. | | `--organization` | string | `trycua` | Organization to pull from. | | `--storage` | string | | VM storage location name, or a direct path to the VM location. | | `--username` | string | | Registry username for authentication. | | `--password` | string | | Registry password for authentication. | | `--force` | boolean | `false` | Download again even if the image is cached. | | `--verbose` | boolean | `false` | Enable verbose logging. | **Examples** ```bash test="cli-shape" id="ref-lume-pull" # Pull an image from ghcr.io/trycua as a VM named macos-tahoe-vanilla lume pull macos-tahoe-vanilla:latest # Pull it as my-vm into another storage location lume pull macos-tahoe-vanilla:latest my-vm --storage external ``` ## `lume push` Push a macOS VM to an OCI-compatible registry. ```text output lume push [OPTIONS] <name> <image> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the VM to push. | | `<image>` | string | required | Image tag to push (format: name:tag). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--additional-tags` | string | | Additional tags to push the same image to. Takes one or more values. | | `--registry` | string | `ghcr.io` | Container registry to push to. | | `--organization` | string | `trycua` | Organization to push to. | | `--storage` | string | | VM storage location to use. | | `--chunk-size-mb` | integer | `512` | Chunk size for large files, in MB. | | `--verbose` | boolean | `false` | Enable verbose logging. | | `--dry-run` | boolean | `false` | Prepare files without uploading to the registry. | | `--reassemble` | boolean | `false` | With --dry-run, also reassemble the chunks to verify integrity. | | `--single-layer` | boolean | `false` | Push the disk as a single layer (kubelet-compatible, no chunking). | | `--legacy` | boolean | `false` | Use the legacy Lume LZ4-chunked format instead of the OCI-compliant format. | **Examples** ```bash test="cli-shape" id="ref-lume-push" # Push my-vm as ghcr.io/my-org/my-image:latest lume push my-vm my-image:latest --organization my-org # Push one image under several tags lume push my-vm my-image:1.0 --additional-tags latest stable # Prepare and verify the chunks without uploading lume push my-vm my-image:latest --dry-run --reassemble ``` ## `lume convert` Convert a legacy Lume image to OCI-compliant format. Pulls a legacy Lume image from the registry, pushes it again in OCI-compliant format under a new name and tag, then removes the temporary local VM. ```text output lume convert [OPTIONS] <source-image> <target-image> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<source-image>` | string | required | Source image to convert (legacy format, for example macos-tahoe:latest). | | `<target-image>` | string | required | Target image to push in OCI format (format: name:tag). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--additional-tags` | string | | Additional tags to push the OCI image to. Takes one or more values. | | `--registry` | string | `ghcr.io` | Registry to pull from and push to. | | `--organization` | string | `trycua` | Registry organization. | | `--verbose` | boolean | `false` | Enable verbose logging. | | `--dry-run` | boolean | `false` | Prepare files without uploading to the registry. | | `--single-layer` | boolean | `false` | Push the disk as a single layer (kubelet-compatible, no chunking). | **Examples** ```bash test="cli-shape" id="ref-lume-convert" lume convert macos-tahoe:latest macos-tahoe:latest-oci ``` ## `lume ipsw` Get macOS restore image IPSW URL. Prints the URL of the latest supported restore image. Download it, then pass the file to `lume create --ipsw`. ```text output lume ipsw ``` **Examples** ```bash test="cli-shape" id="ref-lume-ipsw" lume ipsw ``` ## `lume prune` Remove cached images. ```text output lume prune ``` **Examples** ```bash test="cli-shape" id="ref-lume-prune" lume prune ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | The command failed (for example the VM does not exist or the operation errored). | | `64` | Usage error: an unknown command or option, a missing argument, or an invalid value. | --- # Guest access Prepare unattended setup, open SSH sessions and change SIP in a guest. | Command | Description | | --- | --- | | [`lume setup`](#lume-setup) | Prepare unattended macOS setup. | | [`lume ssh`](#lume-ssh) | Connect to a VM via SSH or execute commands remotely. | | [`lume sip`](#lume-sip) | Enable or disable System Integrity Protection on a macOS VM. | Every command also accepts the [global options](</docs/lume/reference/cli#global-options>). ## `lume setup` Prepare unattended macOS setup. Lume prepares the macOS disk offline, skips Setup Assistant, enables autologin and SSH, disables screensaver lock, verifies SSH, then stops the VM. ```text output lume setup [OPTIONS] <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the virtual machine. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--unattended` | string | | Built-in preset (sequoia, tahoe) or a YAML path, kept for compatibility and optional post-SSH commands (default: tahoe). | | `--storage` | string | | VM storage location name, or a direct path to the VM location. | | `--vnc-port` | integer | `0` | Port for the temporary verification VNC server (0 picks a free port). | | `--debug-dir` | string | | Compatibility option; ignored by offline setup. | | `--no-display` | boolean | `false` | Compatibility flag; offline setup verifies headlessly. | | `--debug` | boolean | `false` | Compatibility flag; ignored by offline setup. | **Examples** ```bash test="cli-shape" id="ref-lume-setup" lume setup my-vm lume setup my-vm --unattended sequoia ``` ## `lume ssh` Connect to a VM via SSH or execute commands remotely. Opens an interactive shell, or runs one command and exits with its status. Password authentication is handled for you (no sshpass needed). Requires Remote Login in the guest; VMs created with --unattended have it enabled with credentials lume/lume. ```text output lume ssh [OPTIONS] <name> [<command>...] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Name of the virtual machine. | | `<command>...` | string | optional | Command to execute (omit for an interactive shell). Repeatable. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--user` | `-u` | string | `lume` | SSH username. | | `--password` | `-p` | string | `lume` | SSH password. | | `--storage` | | string | | Storage location name or path. | | `--timeout` | `-t` | integer | `60` | Command timeout in seconds (0 for no timeout). | **Examples** ```bash test="cli-shape" id="ref-lume-ssh" # Interactive shell lume ssh my-vm # Run one command lume ssh my-vm "ls -la" # A long-running command with no timeout lume ssh my-vm --timeout 0 "cd /app && npm test" ``` ## `lume sip` Enable or disable System Integrity Protection on a macOS VM. Boots the stopped VM normally to validate the admin credentials, runs `csrutil disable` (or `enable`) in paired recoveryOS over VNC, then boots normally once more to verify the result. The VM needs an admin account and Remote Login; VMs from unattended setup use lume/lume. Prefer --admin-password-stdin: --admin-password is visible to other processes. Requires vncdotool (pip3 install vncdotool). ```text output lume sip [OPTIONS] <state> <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<state>` | `on` \| `off` | required | Desired SIP state. | | `<name>` | string | required | Name of the virtual machine. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--admin-user` | | string | `lume` | Administrator username in the guest. | | `--admin-password` | | string | | Administrator password in the guest (default: lume); prefer --admin-password-stdin. | | `--screenshot-dir` | | string | | Save step-by-step framebuffer PNGs here for debugging. | | `--vnc-port` | | integer | `5999` | TCP port for the temporary recovery VNC server. | | `--storage` | | string | | VM storage location. | | `--timeout` | | integer | `900` | Overall timeout in seconds. | | `--yes` | `-y` | boolean | `false` | Skip the interactive confirmation prompt. | | `--admin-password-stdin` | | boolean | `false` | Read one administrator-password line from standard input without echo. | **Examples** ```bash test="cli-shape" id="ref-lume-sip" lume sip off my-vm --yes # With another admin account, password read from stdin lume sip on my-vm --yes --admin-user alice --admin-password-stdin ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | The command failed (for example the VM does not exist or the operation errored). | | `64` | Usage error: an unknown command or option, a missing argument, or an invalid value. | --- # Server Run the HTTP API and MCP server, read its logs, and dump the CLI and API definitions. | Command | Description | | --- | --- | | [`lume serve`](#lume-serve) | Start the VM management server. | | [`lume logs`](#lume-logs) | View lume serve logs. | | [`lume logs info`](#lume-logs-info) | View info logs from the daemon. | | [`lume logs error`](#lume-logs-error) | View error logs from the daemon. | | [`lume logs all`](#lume-logs-all) | View both info and error logs from the daemon. | | [`lume dump-docs`](#lume-dump-docs) | Output CLI and API documentation as JSON for tooling and integrations. | Every command also accepts the [global options](</docs/lume/reference/cli#global-options>). ## `lume serve` Start the VM management server. Serves the HTTP API on localhost, or with --mcp an MCP server over stdio for AI agents. ```text output lume serve [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--port` | integer | `7777` | Port to listen on (HTTP mode only). | | `--mcp` | boolean | `false` | Run as an MCP server (stdio transport) for AI agents. | **Examples** ```bash test="cli-shape" id="ref-lume-serve" # HTTP API on http://localhost:7777 lume serve lume serve --port 7778 # MCP server over stdio (what MCP clients launch) lume serve --mcp ``` ## `lume logs` View lume serve logs. ```text output lume logs <COMMAND> ``` ### `lume logs info` View info logs from the daemon. ```text output lume logs info [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--lines` | `-l` | integer | | Number of lines to display from the end of the file. | | `--follow` | `-f` | boolean | `false` | Follow the log continuously (like tail -f). | **Examples** ```bash test="cli-shape" id="ref-lume-logs-info" lume logs info --lines 100 lume logs info --follow ``` ### `lume logs error` View error logs from the daemon. ```text output lume logs error [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--lines` | `-l` | integer | | Number of lines to display from the end of the file. | | `--follow` | `-f` | boolean | `false` | Follow the log continuously (like tail -f). | **Examples** ```bash test="cli-shape" id="ref-lume-logs-error" lume logs error --lines 100 lume logs error --follow ``` ### `lume logs all` View both info and error logs from the daemon. ```text output lume logs all [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--lines` | `-l` | integer | | Number of lines to display from the end of each file. | | `--follow` | `-f` | boolean | `false` | Follow the log continuously (like tail -f). | **Examples** ```bash test="cli-shape" id="ref-lume-logs-all" lume logs all --lines 100 lume logs all --follow ``` ## `lume dump-docs` Output CLI and API documentation as JSON for tooling and integrations. Emits the CLI, HTTP API or MCP tool metadata (arguments, options, flags, endpoints, tool schemas, help text, defaults and types) that the generated reference is built from. ```text output lume dump-docs [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--type` | `cli` \| `api` \| `mcp` \| `all` | `cli` | Documentation type to output. | | `--pretty` | boolean | `false` | Pretty-print the JSON output with sorted keys. | **Examples** ```bash test="cli-shape" id="ref-lume-dump-docs" lume dump-docs --type cli --pretty # The MCP server's tool list lume dump-docs --type mcp ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | The command failed (for example the VM does not exist or the operation errored). | | `64` | Usage error: an unknown command or option, a missing argument, or an invalid value. | --- # Configuration Storage locations, the image cache and telemetry settings. | Command | Description | | --- | --- | | [`lume config get`](#lume-config-get) | Get current configuration. | | [`lume config storage`](#lume-config-storage) | Manage VM storage locations. | | [`lume config storage add`](#lume-config-storage-add) | Add a new VM storage location. | | [`lume config storage remove`](#lume-config-storage-remove) | Remove a VM storage location. | | [`lume config storage list`](#lume-config-storage-list) | List all VM storage locations. | | [`lume config storage default`](#lume-config-storage-default) | Set the default VM storage location. | | [`lume config cache`](#lume-config-cache) | Manage image cache settings. | | [`lume config cache dir`](#lume-config-cache-dir) | Get or set cache directory. | | [`lume config cache enable`](#lume-config-cache-enable) | Enable image caching. | | [`lume config cache disable`](#lume-config-cache-disable) | Disable image caching. | | [`lume config cache status`](#lume-config-cache-status) | Show cache status and directory. | | [`lume config telemetry`](#lume-config-telemetry) | Manage pseudonymous telemetry settings. | | [`lume config telemetry status`](#lume-config-telemetry-status) | Show current telemetry status. | | [`lume config telemetry enable`](#lume-config-telemetry-enable) | Enable pseudonymous telemetry. | | [`lume config telemetry disable`](#lume-config-telemetry-disable) | Disable pseudonymous telemetry. | | [`lume config telemetry reset-id`](#lume-config-telemetry-reset-id) | Delete the pseudonymous installation ID and registration markers. | | [`lume config registry`](#lume-config-registry) | Manage container registry settings. | | [`lume config registry status`](#lume-config-registry-status) | Show current registry configuration. | | [`lume config registry type`](#lume-config-registry-type) | Get or set registry type. | | [`lume config registry ghcr`](#lume-config-registry-ghcr) | Configure GitHub Container Registry settings. | | [`lume config registry gcs`](#lume-config-registry-gcs) | Configure GCS registry settings. | | [`lume config network`](#lume-config-network) | Manage network settings. | | [`lume config network interfaces`](#lume-config-network-interfaces) | List available network interfaces for bridged networking. | Every command also accepts the [global options](</docs/lume/reference/cli#global-options>). Get or set lume configuration. Without a subcommand, prints the current configuration (`lume config get`). ```text output lume config <COMMAND> ``` ## `lume config get` Get current configuration. ```text output lume config get ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-get" lume config get ``` ## `lume config storage` Manage VM storage locations. ```text output lume config storage <COMMAND> ``` ### `lume config storage add` Add a new VM storage location. ```text output lume config storage add <name> <path> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Storage name (letters, digits, dashes and underscores). | | `<path>` | string | required | Path to the VM storage directory. | **Examples** ```bash test="cli-shape" id="ref-lume-config-storage-add" lume config storage add external /Volumes/External/lume ``` ### `lume config storage remove` Remove a VM storage location. ```text output lume config storage remove <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Storage name to remove. | **Examples** ```bash test="cli-shape" id="ref-lume-config-storage-remove" lume config storage remove external ``` ### `lume config storage list` List all VM storage locations. ```text output lume config storage list ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-storage-list" lume config storage list ``` ### `lume config storage default` Set the default VM storage location. ```text output lume config storage default <name> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<name>` | string | required | Storage name to set as default. | **Examples** ```bash test="cli-shape" id="ref-lume-config-storage-default" lume config storage default external ``` ## `lume config cache` Manage image cache settings. ```text output lume config cache <COMMAND> ``` ### `lume config cache dir` Get or set cache directory. ```text output lume config cache dir [<path>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<path>` | string | optional | Path to the cache directory (omit to show the current one). | **Examples** ```bash test="cli-shape" id="ref-lume-config-cache-dir" lume config cache dir lume config cache dir /Volumes/External/lume-cache ``` ### `lume config cache enable` Enable image caching. ```text output lume config cache enable ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-cache-enable" lume config cache enable ``` ### `lume config cache disable` Disable image caching. ```text output lume config cache disable ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-cache-disable" lume config cache disable ``` ### `lume config cache status` Show cache status and directory. ```text output lume config cache status ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-cache-status" lume config cache status ``` ## `lume config telemetry` Manage pseudonymous telemetry settings. ```text output lume config telemetry <COMMAND> ``` ### `lume config telemetry status` Show current telemetry status. ```text output lume config telemetry status ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-telemetry-status" lume config telemetry status ``` ### `lume config telemetry enable` Enable pseudonymous telemetry. ```text output lume config telemetry enable ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-telemetry-enable" lume config telemetry enable ``` ### `lume config telemetry disable` Disable pseudonymous telemetry. ```text output lume config telemetry disable ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-telemetry-disable" lume config telemetry disable ``` ### `lume config telemetry reset-id` Delete the pseudonymous installation ID and registration markers. ```text output lume config telemetry reset-id ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-telemetry-reset-id" lume config telemetry reset-id ``` ## `lume config registry` Manage container registry settings. ```text output lume config registry <COMMAND> ``` ### `lume config registry status` Show current registry configuration. ```text output lume config registry status ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-registry-status" lume config registry status ``` ### `lume config registry type` Get or set registry type. ```text output lume config registry type [<type>] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<type>` | string | optional | Registry type to use: ghcr or gcs (omit to show the current one). | **Examples** ```bash test="cli-shape" id="ref-lume-config-registry-type" lume config registry type lume config registry type ghcr ``` ### `lume config registry ghcr` Configure GitHub Container Registry settings. ```text output lume config registry ghcr [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--registry` | string | | Registry URL (default: ghcr.io). | | `--organization` | string | | Organization or namespace (default: trycua). | **Examples** ```bash test="cli-shape" id="ref-lume-config-registry-ghcr" lume config registry ghcr --organization my-org ``` ### `lume config registry gcs` Configure GCS registry settings. ```text output lume config registry gcs [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--api-url` | string | | API URL for signed URL generation. | | `--api-key` | string | | API key for authentication. | **Examples** ```bash test="cli-shape" id="ref-lume-config-registry-gcs" lume config registry gcs --api-url https://images.example.com --api-key <key> ``` ## `lume config network` Manage network settings. ```text output lume config network <COMMAND> ``` ### `lume config network interfaces` List available network interfaces for bridged networking. ```text output lume config network interfaces ``` **Examples** ```bash test="cli-shape" id="ref-lume-config-network-interfaces" lume config network interfaces ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | The command failed (for example the VM does not exist or the operation errored). | | `64` | Usage error: an unknown command or option, a missing argument, or an invalid value. | --- # Updates Check for, apply and choose the channel of Lume updates. | Command | Description | | --- | --- | | [`lume check-update`](#lume-check-update) | Check whether a newer Lume release is available. | | [`lume update`](#lume-update) | Check for a Lume update and optionally apply it. | | [`lume channel`](#lume-channel) | Inspect or change the stable/nightly update channel; selection does not install. | | [`lume channel status`](#lume-channel-status) | Show selected and current release channels. | | [`lume channel set`](#lume-channel-set) | Save stable or nightly as the update channel. | Every command also accepts the [global options](</docs/lume/reference/cli#global-options>). ## `lume check-update` Check whether a newer Lume release is available. Read-only update check. Uses GitHub Releases, caches the result briefly, and never installs anything. ```text output lume check-update [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit the structured update-state payload as JSON. | | `--no-cache` | boolean | `false` | Bypass the local update-check cache. | **Examples** ```bash test="cli-shape" id="ref-lume-check-update" lume check-update lume check-update --json --no-cache ``` ## `lume update` Check for a Lume update and optionally apply it. Without --apply, this command only checks for a newer release and prints the command to install it. With --apply, it runs the official Lume installer pinned to the discovered version and exits with the installer's status. ```text output lume update [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--apply` | boolean | `false` | Apply the update while preserving the current background-service choice. | | `--json` | boolean | `false` | Emit the structured update-state payload as JSON. | **Examples** ```bash test="cli-shape" id="ref-lume-update" lume update lume update --apply ``` ## `lume channel` Inspect or change the stable/nightly update channel; selection does not install. The selection persists but never installs by itself; run `lume update --apply` after changing it. Without a subcommand, prints the status. ```text output lume channel <COMMAND> ``` ### `lume channel status` Show selected and current release channels. ```text output lume channel status [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable channel state as JSON. | **Examples** ```bash test="cli-shape" id="ref-lume-channel-status" lume channel status --json ``` ### `lume channel set` Save stable or nightly as the update channel. ```text output lume channel set [OPTIONS] <channel> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<channel>` | string | required | Release channel: stable or nightly. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--json` | boolean | `false` | Emit machine-readable channel state as JSON. | **Examples** ```bash test="cli-shape" id="ref-lume-channel-set" lume channel set nightly lume channel set stable ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | The command failed (for example the VM does not exist or the operation errored). | | `64` | Usage error: an unknown command or option, a missing argument, or an invalid value. | --- # HTTP API Every endpoint of the Lume HTTP API server (lume serve). HTTP API for managing macOS and Linux virtual machines Documented against Lume **0.6.1**. Run `lume --version` for your installed version. ## Default URL ``` http://localhost:7777 ``` Start the server with `lume serve` or specify a custom port with `lume serve --port <port>`. ## VM Management ### List all virtual machines ```http output GET /lume/vms ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `storage` | query | `string` | | Filter by storage location name. | **Example request** **Curl** ```bash curl "http://localhost:7777/lume/vms" ``` **Python** ```python import requests response = requests.get("http://localhost:7777/lume/vms") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms`); const data = await response.json(); ``` **Response** - `200`: Success. - `400`: Bad request. ### Get detailed information about a specific virtual machine ```http output GET /lume/vms/:name ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `name` | path | `string` | required | Name of the VM. | | `storage` | query | `string` | | VM storage location to use. | **Example request** **Curl** ```bash curl "http://localhost:7777/lume/vms/my-vm" ``` **Python** ```python import requests response = requests.get("http://localhost:7777/lume/vms/my-vm") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms/my-vm`); const data = await response.json(); ``` **Response** - `200`: Success (while an async pull runs, only name, status "pulling", downloadProgress, downloadedBytes, totalBytes and bytesPerSecond). - `400`: VM not found, invalid request, or the async pull failed. ### Create a new virtual machine ```http output POST /lume/vms ``` **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Name for the virtual machine. | | `os` | `string` | required | Operating system to install (macOS or linux). | | `cpu` | `integer` | required | Number of CPU cores. | | `memory` | `string` | required | Memory size (e.g., 8GB). | | `diskSize` | `string` | required | Disk size (e.g., 50GB). | | `display` | `string` | required | Display resolution (e.g., 1024x768). | | `ipsw` | `string` | | Path to IPSW file or 'latest' for macOS VMs. | | `storage` | `string` | | VM storage location to use. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/vms" \ -H "Content-Type: application/json" \ -d '{ "name": "my-vm", "os": "macOS", "cpu": 4, "memory": "8GB", "diskSize": "50GB", "display": "1024x768" }' ``` **Python** ```python import requests data = { "name": "my-vm", "os": "macOS", "cpu": 4, "memory": "8GB", "diskSize": "50GB", "display": "1024x768", } response = requests.post("http://localhost:7777/lume/vms", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "my-vm", os: "macOS", cpu: 4, memory: "8GB", diskSize: "50GB", display: "1024x768", }), }); const data = await response.json(); ``` **Response** - `200`: VM created successfully. - `400`: Invalid request body or VM creation failed. ### Delete a virtual machine and its associated files ```http output DELETE /lume/vms/:name ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `name` | path | `string` | required | Name of the VM to delete. | | `storage` | query | `string` | | VM storage location. | **Example request** **Curl** ```bash curl -X DELETE "http://localhost:7777/lume/vms/my-vm" ``` **Python** ```python import requests response = requests.delete("http://localhost:7777/lume/vms/my-vm") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms/my-vm`, { method: "DELETE", }); const data = await response.json(); ``` **Response** - `200`: VM deleted successfully. - `400`: VM not found or deletion failed. ### Create a copy of an existing virtual machine ```http output POST /lume/vms/clone ``` **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Name of the source VM. | | `newName` | `string` | required | Name for the cloned VM. | | `sourceLocation` | `string` | | Source VM storage location. | | `destLocation` | `string` | | Destination VM storage location. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/vms/clone" \ -H "Content-Type: application/json" \ -d '{ "name": "my-vm", "newName": "example" }' ``` **Python** ```python import requests data = { "name": "my-vm", "newName": "example", } response = requests.post("http://localhost:7777/lume/vms/clone", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms/clone`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "my-vm", newName: "example", }), }); const data = await response.json(); ``` **Response** - `200`: VM cloned successfully. - `400`: Clone operation failed. ### Update virtual machine configuration settings ```http output PATCH /lume/vms/:name ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `name` | path | `string` | required | Name of the VM to update. | **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `cpu` | `integer` | | New number of CPU cores. | | `memory` | `string` | | New memory size (e.g., 16GB). | | `diskSize` | `string` | | New total disk size (increase only). | | `display` | `string` | | New display resolution. | | `storage` | `string` | | VM storage location. | | `noBackup` | `boolean` | `false` | Skip the macOS rollback backup. | | `keepBackup` | `boolean` | `false` | Keep rollback files after success. | | `dryRun` | `boolean` | `false` | Validate the resize plan without modifying the disk. | **Example request** **Curl** ```bash curl -X PATCH "http://localhost:7777/lume/vms/my-vm" \ -H "Content-Type: application/json" \ -d '{}' ``` **Python** ```python import requests data = { } response = requests.patch("http://localhost:7777/lume/vms/my-vm", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms/my-vm`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ }), }); const data = await response.json(); ``` **Response** - `200`: Settings updated successfully. - `400`: Invalid settings or update failed. ### Start a virtual machine ```http output POST /lume/vms/:name/run ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `name` | path | `string` | required | Name of the VM to start. | **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `noDisplay` | `boolean` | `false` | Run without VNC display. | | `sharedDirectories` | `array` | | Directories to share with the VM. | | `recoveryMode` | `boolean` | `false` | Boot macOS VM in recovery mode. | | `storage` | `string` | | VM storage location. | | `clipboard` | `boolean` | `false` | Enable bidirectional clipboard sync via SSH (experimental). | | `vnc` | `string` | `enabled` | VNC server policy: 'enabled' or 'disabled'. 'disabled' requires noDisplay=true, starts no VNC listener, and reports a null vncUrl. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/vms/my-vm/run" \ -H "Content-Type: application/json" \ -d '{}' ``` **Python** ```python import requests data = { } response = requests.post("http://localhost:7777/lume/vms/my-vm/run", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms/my-vm/run`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ }), }); const data = await response.json(); ``` **Response** - `202`: VM start initiated (async operation). - `409`: Host limit of running macOS guests reached. - `400`: Invalid request or VM not found. ### Stop a running virtual machine ```http output POST /lume/vms/:name/stop ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `name` | path | `string` | required | Name of the VM to stop. | **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `storage` | `string` | | VM storage location. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/vms/my-vm/stop" \ -H "Content-Type: application/json" \ -d '{}' ``` **Python** ```python import requests data = { } response = requests.post("http://localhost:7777/lume/vms/my-vm/stop", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms/my-vm/stop`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ }), }); const data = await response.json(); ``` **Response** - `200`: VM stopped successfully. - `400`: Stop operation failed. ## Image Management ### List available images from local cache ```http output GET /lume/images ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `organization` | query | `string` | | Organization to list images for (default: trycua). | **Example request** **Curl** ```bash curl "http://localhost:7777/lume/images" ``` **Python** ```python import requests response = requests.get("http://localhost:7777/lume/images") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/images`); const data = await response.json(); ``` **Response** - `200`: Success. - `400`: Failed to list images. ### Get the latest macOS restore image (IPSW) URL ```http output GET /lume/ipsw ``` **Example request** **Curl** ```bash curl "http://localhost:7777/lume/ipsw" ``` **Python** ```python import requests response = requests.get("http://localhost:7777/lume/ipsw") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/ipsw`); const data = await response.json(); ``` **Response** - `200`: Success. - `400`: Failed to get IPSW URL. ### Pull a VM image from a container registry ```http output POST /lume/pull ``` **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `string` | required | Image to pull (format: name:tag). | | `name` | `string` | | Name for the resulting VM. | | `registry` | `string` | `ghcr.io` | Container registry URL. | | `organization` | `string` | `trycua` | Organization to pull from. | | `storage` | `string` | | VM storage location. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/pull" \ -H "Content-Type: application/json" \ -d '{ "image": "macos-tahoe-vanilla:latest" }' ``` **Python** ```python import requests data = { "image": "macos-tahoe-vanilla:latest", } response = requests.post("http://localhost:7777/lume/pull", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/pull`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ image: "macos-tahoe-vanilla:latest", }), }); const data = await response.json(); ``` **Response** - `200`: Image pulled successfully. - `400`: Pull operation failed. ### Start a background pull ```http output POST /lume/pull/start ``` **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `image` | `string` | required | Image to pull (format: name:tag). | | `name` | `string` | | Name for the resulting VM. | | `registry` | `string` | `ghcr.io` | Container registry URL. | | `organization` | `string` | `trycua` | Organization to pull from. | | `storage` | `string` | | VM storage location. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/pull/start" \ -H "Content-Type: application/json" \ -d '{ "image": "macos-tahoe-vanilla:latest" }' ``` **Python** ```python import requests data = { "image": "macos-tahoe-vanilla:latest", } response = requests.post("http://localhost:7777/lume/pull/start", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/pull/start`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ image: "macos-tahoe-vanilla:latest", }), }); const data = await response.json(); ``` **Response** - `202`: Pull started or already in progress. - `400`: Invalid request body. ### Cancel a background pull ```http output POST /lume/pull/cancel ``` **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | VM name of the pull to cancel. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/pull/cancel" \ -H "Content-Type: application/json" \ -d '{ "name": "my-vm" }' ``` **Python** ```python import requests data = { "name": "my-vm", } response = requests.post("http://localhost:7777/lume/pull/cancel", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/pull/cancel`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "my-vm", }), }); const data = await response.json(); ``` **Response** - `200`: The pull stopped and was cleaned up. - `400`: Invalid request body. - `404`: No background pull in progress for the name (for example it already finished or was cancelled). - `500`: The pull did not stop within 30 seconds. ### Push a VM image to a container registry ```http output POST /lume/vms/push ``` **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Name of the local VM to push. | | `imageName` | `string` | required | Base name for the image in the registry. | | `tags` | `array` | required | List of tags to push. | | `registry` | `string` | `ghcr.io` | Container registry URL. | | `organization` | `string` | `trycua` | Organization to push to. | | `storage` | `string` | | VM storage location. | | `chunkSizeMb` | `integer` | `512` | Chunk size for upload in MB. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/vms/push" \ -H "Content-Type: application/json" \ -d '{ "name": "my-vm", "imageName": "example", "tags": [ "latest" ] }' ``` **Python** ```python import requests data = { "name": "my-vm", "imageName": "example", "tags": ["latest"], } response = requests.post("http://localhost:7777/lume/vms/push", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/vms/push`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "my-vm", imageName: "example", tags: ["latest"], }), }); const data = await response.json(); ``` **Response** - `202`: Push initiated (async operation). - `400`: Invalid request. ### Remove cached images to free up disk space ```http output POST /lume/prune ``` **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/prune" ``` **Python** ```python import requests response = requests.post("http://localhost:7777/lume/prune") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/prune`); const data = await response.json(); ``` **Response** - `200`: Images pruned successfully. - `400`: Prune operation failed. ## Configuration ### Get current Lume configuration settings ```http output GET /lume/config ``` **Example request** **Curl** ```bash curl "http://localhost:7777/lume/config" ``` **Python** ```python import requests response = requests.get("http://localhost:7777/lume/config") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/config`); const data = await response.json(); ``` **Response** - `200`: Success. - `400`: Failed to get config. ### Update Lume configuration settings ```http output POST /lume/config ``` **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `homeDirectory` | `string` | | VM home directory path. | | `cacheDirectory` | `string` | | Cache directory path. | | `cachingEnabled` | `boolean` | | Enable or disable image caching. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/config" \ -H "Content-Type: application/json" \ -d '{}' ``` **Python** ```python import requests data = { } response = requests.post("http://localhost:7777/lume/config", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/config`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ }), }); const data = await response.json(); ``` **Response** - `200`: Configuration updated successfully. - `400`: Invalid request. ### List all VM storage locations ```http output GET /lume/config/locations ``` **Example request** **Curl** ```bash curl "http://localhost:7777/lume/config/locations" ``` **Python** ```python import requests response = requests.get("http://localhost:7777/lume/config/locations") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/config/locations`); const data = await response.json(); ``` **Response** - `200`: Success. - `400`: Failed to get locations. ### Add a new VM storage location ```http output POST /lume/config/locations ``` **Request body** | Field | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Storage location name. | | `path` | `string` | required | Path to storage directory. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/config/locations" \ -H "Content-Type: application/json" \ -d '{ "name": "my-vm", "path": "/path/to/storage" }' ``` **Python** ```python import requests data = { "name": "my-vm", "path": "/path/to/storage", } response = requests.post("http://localhost:7777/lume/config/locations", json=data) print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/config/locations`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "my-vm", path: "/path/to/storage", }), }); const data = await response.json(); ``` **Response** - `200`: Location added successfully. - `400`: Invalid request or location already exists. ### Remove a VM storage location ```http output DELETE /lume/config/locations/:name ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `name` | path | `string` | required | Name of the location to remove. | **Example request** **Curl** ```bash curl -X DELETE "http://localhost:7777/lume/config/locations/my-vm" ``` **Python** ```python import requests response = requests.delete("http://localhost:7777/lume/config/locations/my-vm") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/config/locations/my-vm`, { method: "DELETE", }); const data = await response.json(); ``` **Response** - `200`: Location removed successfully. - `400`: Location not found or cannot be removed. ### Set the default VM storage location ```http output POST /lume/config/locations/default/:name ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `name` | path | `string` | required | Name of the location to set as default. | **Example request** **Curl** ```bash curl -X POST "http://localhost:7777/lume/config/locations/default/my-vm" ``` **Python** ```python import requests response = requests.post("http://localhost:7777/lume/config/locations/default/my-vm") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/config/locations/default/my-vm`); const data = await response.json(); ``` **Response** - `200`: Default location set successfully. - `400`: Location not found. ## Logs ### Retrieve Lume server logs ```http output GET /lume/logs ``` **Parameters** | Parameter | In | Type | Default | Description | | --- | --- | --- | --- | --- | | `type` | query | `string` | | Log type: 'info', 'error', or 'all' (default: all). | | `lines` | query | `integer` | | Number of lines to return from end of log. | **Example request** **Curl** ```bash curl "http://localhost:7777/lume/logs" ``` **Python** ```python import requests response = requests.get("http://localhost:7777/lume/logs") print(response.json()) ``` **TypeScript** ```typescript const response = await fetch(`http://localhost:7777/lume/logs`); const data = await response.json(); ``` **Response** - `200`: Success. - `400`: Failed to read logs. --- # MCP tools Every tool Lume's MCP server exposes, with its parameters. `lume serve --mcp` serves these tools over stdio. See [API and MCP](</docs/lume/guides/api-and-mcp>) for client setup. ## VM tools ### `lume_list_vms` List all virtual machines with their status, IP addresses, and resource allocation. Effect: read-only, idempotent. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `storage` | `string` | | Optional storage location name or path to filter VMs. | ### `lume_get_vm` Get detailed information about a specific VM including IP address, VNC URL, and SSH availability. Effect: read-only, idempotent. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Name of the VM. | | `storage` | `string` | | Optional storage location name or path. | **Example arguments** ```json {"name":"<name>"} ``` ### `lume_run_vm` Start a VM with optional shared directory for file access. The shared directory will be available at /Volumes/My Shared Files inside the VM. Effect: mutating. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Name of the VM to start. | | `no_display` | `boolean` | | Run headless without VNC window (default: true). | | `shared_dir` | `string` | | Host directory path to share with the VM (appears at /Volumes/My Shared Files). | | `storage` | `string` | | Optional storage location name or path. | | `vnc` | `"enabled" \| "disabled"` | | VNC server policy. 'disabled' starts no VNC listener and reports a null vncUrl (default: enabled). | **Example arguments** ```json {"name":"<name>"} ``` ### `lume_stop_vm` Stop a running VM gracefully. Effect: mutating. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Name of the VM to stop. | | `storage` | `string` | | Optional storage location name or path. | **Example arguments** ```json {"name":"<name>"} ``` ### `lume_clone_vm` Clone a VM to create a copy. Useful for creating golden images for instant reset. Effect: mutating. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Name of the source VM to clone. | | `new_name` | `string` | required | Name for the cloned VM. | **Example arguments** ```json {"name":"<name>","new_name":"<new_name>"} ``` ### `lume_delete_vm` Delete a VM and all its associated files. Effect: destructive. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Name of the VM to delete. | | `storage` | `string` | | Optional storage location name or path. | **Example arguments** ```json {"name":"<name>"} ``` ### `lume_create_vm` Create a new macOS VM asynchronously. Returns immediately while the VM is being provisioned. Poll lume_list_vms or lume_get_vm to check progress (status will be 'provisioning' until complete). Effect: mutating. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `cpu` | `integer` | | Number of CPU cores (default: 4). | | `disk_size` | `string` | | Disk size, e.g., '100GB' (default: 100GB). | | `ipsw` | `string` | | Path to IPSW file or 'latest' to download (default: latest). | | `memory` | `string` | | Memory size, e.g., '8GB' (default: 8GB). | | `name` | `string` | required | Name for the new VM. | | `storage` | `string` | | Optional storage location name or path. | | `unattended` | `string` | | Unattended setup preset (e.g., 'tahoe', 'sequoia') for automatic macOS configuration. | **Example arguments** ```json {"name":"<name>"} ``` ### `lume_resize_disk` Grow a stopped VM's disk. For macOS VMs this preserves the paired RecoveryOS partition (relocating it to the new end of disk) and expands the main APFS container so future macOS updates keep working. Grow-only; the VM must be stopped; may take several minutes. Effect: destructive. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `disk_size` | `string` | required | New disk size, e.g., '100GB'. Must be larger than the current size. | | `dry_run` | `boolean` | | Validate and print the resize plan without modifying the disk. Default false. | | `keep_backup` | `boolean` | | Keep the rollback backup after a successful resize; cannot be combined with no_backup. Default false. | | `name` | `string` | required | Name of the VM to resize. | | `no_backup` | `boolean` | | Skip the pre-resize backup (faster, but no automatic rollback on failure). Default false. | | `storage` | `string` | | Optional storage location name or path. | **Example arguments** ```json {"name":"<name>","disk_size":"<disk_size>"} ``` ## Guest tools ### `lume_exec` Execute a command inside a running VM via SSH. Requires SSH to be enabled in the VM (default for VMs created with --unattended tahoe). Effect: destructive. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `command` | `string` | required | Shell command to execute inside the VM. | | `name` | `string` | required | Name of the VM. | | `password` | `string` | | SSH password (default: lume). | | `storage` | `string` | | Optional storage location name or path. | | `user` | `string` | | SSH username (default: lume). | **Example arguments** ```json {"name":"<name>","command":"<command>"} ``` ## Maintenance tools ### `check_for_update` Check the saved stable/nightly Lume channel for a release on GitHub. Returns current and selected channels. Read-only; never installs. Mirrors `lume check-update --json`. Effect: read-only, idempotent. No parameters. --- # Cua CLI The cua command: sign-in, sandboxes local and cloud, one-shot computer actions, MCP, Spaces, unattended access and teleport. `cua` is one binary built on the [cua SDK](</docs/cua-sdk>). It creates and drives sandboxes on this machine or in the cloud, serves them to AI agents over MCP, and manages Spaces. ## Install ```bash skip="host-install" curl -fsSL https://cua.ai/install.sh | sh # macOS, Linux: a checklist of what to install irm https://cua.ai/install.ps1 | iex # Windows (PowerShell) pip install cua # CLI only (or uv tool install cua) npm install -g @trycua/cua # CLI only ``` Add `-s -- --select cua-driver` to also set up the cua-driver MCP server and skill for your agents, or `--only ITEMS` to skip the checklist. Installer flags: [Cua Spaces quickstart](</docs/spaces/quickstart#run>). ## Commands | Group | Alias | Purpose | |-------|-------|---------| | `cua auth` | | Sign in, session, Fleet identity, API keys ([Authentication](</docs/cua-cli/guides/authentication>)). | | `cua agents` | | Install cua skills and the cua MCP server into AI coding agents. | | `cua sandbox` | `sb` | Create, list and operate sandboxes; `--on local\|cloud\|aws\|gcp\|modal\|direct:<addr>`, `--kind`, `--runtime`. | | `cua config` | | User defaults: where sandboxes run (`default.on`), the default kind and runtime. | | `cua do` | | One computer action (click, type, screenshot, ...) against the selected sandbox. | | `cua mcp` | `serve-mcp` | Stdio MCP server ([MCP server](</docs/cua-cli/guides/mcp-server>)). | | `cua daemon` | | Shared background runtime; `cua daemon mcp`. | | `cua spaces` | | Registered Spaces and your relay machines ([Cua Spaces](</docs/spaces>)). | | `cua cloud` | | Your own AWS, Google Cloud or Modal account as a location ([Use your own cloud](</docs/cua-sdk/guides/your-cloud>)). | | `cua host` | | [Unattended access](</docs/spaces/guides/unattended-access>) to this machine. | | `cua teleport` | | [Move an app session](</docs/spaces/guides/teleport-an-app>) into a sandbox. | | `cua image` | `img` | Local OCI images and Fleet image resources. | | `cua fleet` | | Pools behind your cloud sandboxes (advanced). | | `cua spacesd` | | Talk to any cua-spacesd by URL. | | `cua runtime` | | Inspect or provision local runtimes. | | `cua cache` | | Disk used by images, sandboxes and logs, and its cleanup ([Disk usage](</docs/cua-sdk/guides/disk-usage>)). | | `cua doctor` | | Check the host, an image and a sandbox's guest in one report. | | `cua skills`, `cua trajectory` | `traj` | Recorded demonstrations and `cua do` sessions. | | `cua wif-token` | | GitHub Actions OIDC token for Fleet. | Every command accepts `--json`, `--embedded`, `--daemon <addr>`, `--daemon-token <token>` and `--state-dir <dir>`. The CLI uses a running `cua daemon` when there is one, else an embedded runtime. `cua <command> --help` and the [CLI reference](</docs/cua-cli/reference/cli>) list every option. | Exit code | Meaning | |------|---------| | `0` | Success. | | `1` | Failure. | | `2` | Invalid argument. | | `3` | Not found (sandbox, window, skill, image). | | `4` | Not supported or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied. | | `7` | Not enough free disk for a pull, build or VM create (`cua cache prune`). | `cua sb exec` and `cua spacesd exec` exit with the command's own status. State lives in `~/.cua` (`CUA_HOME` moves it). Start with the [Quickstart](</docs/cua-cli/quickstart>). --- # Quickstart Install the cua CLI, start a local sandbox, run a command in it, then start the same sandbox in the cloud. Start a Linux sandbox on this machine, run a command in it, then run the same sandbox in the cloud by changing one flag. ## Prerequisites - macOS, Linux or Windows with Docker (or another local runtime: `cua runtime doctor` lists them). - A [cua.ai](https://cua.ai) account for the cloud step only. ## Run ```bash skip="host-install" curl -fsSL https://cua.ai/install.sh | sh -s -- --cli-only # or: pip install cua, npm i -g @trycua/cua ``` ```bash test="cli-shape" id="cua-quickstart-local" cua sb create linux --name dev cua sb exec dev "uname -a" cua sb screenshot dev -o dev.png ``` ## Result ![cua sb create, ls, exec and view for a local Linux sandbox](</docs-assets/img/cua-cli/cua-sb-create.png>) `exec` prints the guest's kernel line and exits with the command's status; `dev.png` is the sandbox desktop. With `--json` the result is structured: ```bash test="cli-shape" id="cua-quickstart-json" cua --json sb exec dev pwd # {"stdout": ..., "stderr": ..., "returncode": 0} ``` ## Drive the desktop `--wait desktop` returns once the desktop session takes input (display, window manager and Cua Driver). `cua do` input waits for it too, and fails when input reaches no window. ```bash test="cli-shape" id="cua-quickstart-do" cua sb create linux --name desk --wait desktop cua do switch desk cua do launch xfce4-terminal # waits for the new window and focuses it cua do type "echo hello" cua do key enter ``` ## Clean up ```bash test="cli-shape" id="cua-quickstart-rm" cua sb rm dev cua sb rm desk ``` ## Next - [Authentication](</docs/cua-cli/guides/authentication>): API keys and CI. - [MCP server](</docs/cua-cli/guides/mcp-server>): give an AI agent these sandboxes. - [CLI reference](</docs/cua-cli/reference/cli>): every command and option. --- # Authenticate Sign in with cua auth login, see which Fleet credential the CLI uses, create API keys, and authenticate in GitHub Actions. One sign-in covers the CLI, the SDK, `cua daemon` and the Cua Spaces app: they share one credential store. Local sandboxes need no sign-in. ## Sign in ```bash test="cli-shape" id="cua-auth-login" cua auth login # browser (PKCE, loopback redirect) cua auth login --remote # device code, for SSH sessions cua auth whoami # the Fleet credential in use, checked against Fleet cua auth status # session and expiry, no network cua auth logout # revoke and remove the session ``` After an interactive login, `cua auth login` offers to set up your AI coding agents (skills plus the cua MCP server); `--agents claude,codex`, `--yes`, `--skills-only`, `--mcp-only` and `--no-onboarding` control it, and `never` is remembered in `~/.cua/config`. Tokens refresh automatically and are never printed. | Platform | Session stored in | |----------|-------------------| | macOS, builds signed by Cua | Keychain, service `run.cua.ai`, account `cua-cli` | | macOS, builds from source | `~/.cua/credentials.json`, mode 600 | | Windows | Credential Manager, same service and account | | Linux | `~/.cua/credentials.json`, mode 600 | `CUA_CREDENTIAL_STORE=file` or `keychain` picks the store. A build from source is a new app to the Keychain every time it is rebuilt, so it uses the file and never asks for Keychain access. ## Which credential Fleet uses The first that is set wins: | Order | Source | For | |-------|--------|-----| | 1 | `FLEETS_TOKEN` | A workload token, for example from `cua wif-token github`. | | 2 | `CUA_CLIENT_ID` + `CUA_CLIENT_SECRET` | An API key, for scripts and servers (`CUA_TOKEN_URL` overrides the endpoint). | | 3 | The `cua auth login` session | Interactive use. | Legacy `key_...` keys belong to the retired VM API and do not work with Fleet. ## API keys Create keys from a login session (an API key cannot manage keys: exit `6`): ```bash test="cli-shape" id="cua-auth-keys" cua auth keys create ci-runner # prints CUA_CLIENT_ID and CUA_CLIENT_SECRET once cua auth keys ls cua auth keys rm <id> ``` ## GitHub Actions `cua wif-token github` exchanges the job's OIDC identity for a Fleet token. It needs `id-token: write` and a Fleet trust policy for the repository: ```yaml test="config" id="yaml-config" permissions: id-token: write contents: read steps: - name: Use a cloud sandbox run: | export FLEETS_TOKEN="$(cua wif-token github)" cua sb create linux --on cloud --name "run-$GITHUB_RUN_ID" cua sb exec "run-$GITHUB_RUN_ID" "uname -a" cua sb rm "run-$GITHUB_RUN_ID" --force ``` **Warning** The stored refresh token mints access tokens until revoked. On shared hosts, run `cua auth logout` when done, or use an API key or workload token. Other variables (`CUA_FLEET_BASE_URL`, `CUA_OIDC_ISSUER`, `CUA_AUTH_FLOW`, `CUA_NO_BROWSER`, ...) are listed in the [CLI reference](</docs/cua-cli/reference/cli>). --- # Connect an agent over MCP Serve Spaces, sandbox, computer and skills tools to an AI client with cua mcp or cua daemon mcp, and grant only the permissions it needs. `cua mcp` is a stdio MCP server with the Spaces tools plus sandbox, computer and skills tools. `cua daemon mcp` is the same server through `cua daemon` (started if needed), so several agents share Spaces, streams and sandboxes. For the machine the agent itself runs on, use [Cua Driver](</docs/cua-driver/quickstart>) instead. ```bash skip="third-party-cli" claude mcp add cua -- cua daemon mcp claude mcp add cua -- cua mcp --sandbox dev --permissions sandbox:readonly,computer:all ``` `cua agents setup` adds this entry for detected agents. `--sandbox` (`CUA_SANDBOX`) is the default target of the computer tools; each call can pass `sandbox`. Cloud sandboxes use the CLI's [credentials](</docs/cua-cli/guides/authentication#which-credential-fleet-uses>). Every tool is in [MCP tools](</docs/cua-cli/reference/mcp-tools>). ## Supported agents `cua agents setup --agents <id>` installs the cua skills and adds the `cua` server to these user-scope configs. Other servers and comments are kept, and `cua agents remove` takes out only what cua added. | Agent | id | Skills | MCP config | |-------|----|--------|------------| | Claude Code | `claude-code` | `~/.claude/skills` | `~/.claude.json` | | OpenAI Codex | `codex` | `~/.agents/skills` | `~/.codex/config.toml` | | Cursor | `cursor` | `~/.agents/skills` | `~/.cursor/mcp.json` | | Gemini CLI | `gemini-cli` | `~/.agents/skills` | `~/.gemini/settings.json` | | Cline | `cline` | `~/.cline/skills` | `~/.cline/data/settings/cline_mcp_settings.json` | | Kiro | `kiro` | `~/.kiro/skills` | `~/.kiro/settings/mcp.json` | | OpenClaw | `openclaw` | `~/.agents/skills` | `~/.openclaw/openclaw.json` | | OpenCode | `opencode` | `~/.agents/skills` | `~/.config/opencode/opencode.json` | | Pi | `pi` | `~/.agents/skills` | none (Pi has no MCP) | | Windsurf (Devin Desktop) | `windsurf` | `~/.agents/skills` | `~/.config/devin/mcp_config.json` | | GitHub Copilot CLI | `copilot-cli` | `~/.agents/skills` | `~/.copilot/mcp-config.json` | | Amp | `amp` | `~/.agents/skills` | `~/.config/amp/settings.json` | | Goose | `goose` | `~/.agents/skills` | `~/.config/goose/config.yaml` | | Zed | `zed` | `~/.agents/skills` | `~/.config/zed/settings.json` | | VS Code (Copilot agent mode) | `vscode` | `~/.agents/skills` | `mcp.json` in the Code user folder | | Google Antigravity | `antigravity` | `~/.gemini/config/skills` | `~/.gemini/config/mcp_config.json` | | Hermes Agent (Nous Research) | `hermes` | `~/.hermes/skills` | `~/.hermes/config.yaml` (`mcp_servers`) | Agent home overrides are honored (`CLAUDE_CONFIG_DIR`, `CODEX_HOME`, `HERMES_HOME`, ...). On Windows, Hermes lives in `%LOCALAPPDATA%\hermes`. Start a new Hermes session (or run `/reload-mcp`) to load the server. ## Permissions `--permissions` (`CUA_MCP_PERMISSIONS`) takes `group:action` strings and groups, comma-separated. Only granted tools are listed; others are refused. | Group | Expands to | |-------|-----------| | `all` | Everything. | | `spaces:all`, `spaces:readonly` | Every Spaces tool, or the read-only ones (`list_spaces`, `download`, `agent_status`, ...). | | `sandbox:all` | `sandbox:list`, `create`, `delete`, `start`, `stop`, `restart`, `suspend`, `get`, `view` | | `sandbox:readonly` | `sandbox:list`, `sandbox:get` | | `computer:all` | `computer:screenshot`, `click`, `type`, `key`, `scroll`, `drag`, `hotkey`, `clipboard`, `file`, `shell`, `window`, `accessibility` | | `computer:readonly` | `computer:screenshot` | | `skills:all`, `skills:readonly` | `skills:list`, `read`, `record`, `delete`, or only `list` and `read` | **Warning** Only an empty list grants everything. An unknown permission is skipped with a warning; if nothing valid remains no tools are registered, so a typo fails closed. ## Choose a grant `computer:shell`, `computer:file`, `spaces:space_bash` and `spaces:teleport_app` run commands, touch files or use your app sessions in the target; `sandbox:delete` and `spaces:delete_space` destroy sandboxes. | Intent | Grant | |--------|-------| | Look, not touch | `sandbox:readonly,computer:readonly,spaces:readonly` | | Drive a UI without a shell | `computer:screenshot,computer:click,computer:type,computer:key,computer:scroll` | | Automate one sandbox | `computer:all` with `--sandbox <name>` | | Spaces agent | `spaces:all` | Computer-tool coordinates are in the pixel space of the last `computer_screenshot` (capped at 1200 px on the long edge). Spaces tools that need cua-spacesd return `capability_missing` on images without it; see [Use Spaces from an agent](</docs/spaces/guides/use-from-an-agent>). --- # Overview Every cua command and every cua MCP tool, generated from the CLI definition. ## Commands | Command group | Description | | --- | --- | | [`cua`](</docs/cua-cli/reference/cli>) | Synopsis, global options, exit codes and every command. | | [`cua auth`](</docs/cua-cli/reference/cli/auth>) | Sign in, inspect your Fleet identity, manage API keys and CI tokens. | | [`cua sandbox`](</docs/cua-cli/reference/cli/sandbox>) | Create, list, connect to, suspend and delete sandboxes, local or in the cloud. | | [Sandbox access](</docs/cua-cli/reference/cli/sandbox-access>) | Run commands, copy files, forward ports, take screenshots, reach the MCP and view a sandbox (`cua sandbox exec`, `cp`, `view`, ...). | | [`cua config`](</docs/cua-cli/reference/cli/config>) | User defaults: where sandboxes run (`default.on`), the default kind and runtime, cloud defaults. | | [`cua viewer`](</docs/cua-cli/reference/cli/viewer>) | One local browser page that lists every sandbox and opens its viewer. | | [`cua do`](</docs/cua-cli/reference/cli/do>) | One-shot computer actions (screenshot, click, type, windows) against a selected target. | | [`cua image`](</docs/cua-cli/reference/cli/image>) | Pull, build and push OCI images, and manage Fleet image resources; list the image catalog. | | [`cua fleet`](</docs/cua-cli/reference/cli/fleet>) | Managed Fleet pools behind `cua sandbox create`. | | [`cua mcp`](</docs/cua-cli/reference/cli/mcp>) | The stdio MCP server for AI assistants. | | [`cua agent`](</docs/cua-cli/reference/cli/agent>) | Run coding agents (Claude Code, Codex, Gemini CLI, ...) inside a sandbox and follow, continue or stop them. | | [`cua agents`](</docs/cua-cli/reference/cli/agents>) | Install cua skills and the cua MCP server into AI coding agents. | | [`cua skills`](</docs/cua-cli/reference/cli/skills>) | Record, list and replay demonstrations (skills). | | [`cua trajectory`](</docs/cua-cli/reference/cli/trajectory>) | Recorded `cua do` trajectories. | | [`cua spaces`](</docs/cua-cli/reference/cli/spaces>) | Registered Spaces and your machines on the relay. | | [`cua cloud`](</docs/cua-cli/reference/cli/cloud>) | Your own AWS, Google Cloud or Modal account as a place for sandboxes and Spaces: connect, test, status, sweep. | | [`cua host`](</docs/cua-cli/reference/cli/host>) | Set this machine up for unattended access. | | [`cua devices`](</docs/cua-cli/reference/cli/devices>) | Enroll this device on the relay, approve, rename and revoke devices, and read the access log. | | [`cua teleport`](</docs/cua-cli/reference/cli/teleport>) | Move a desktop app session into a sandbox. | | [`cua volume`](</docs/cua-cli/reference/cli/volume>) | Cua Volume: files, versions, grants, access requests and audit of the volume every Space and agent shares. | | [`cua keyvault`](</docs/cua-cli/reference/cli/keyvault>) | Set up, unlock and lock the Cua Keyvault, with the OS key store or a passphrase. | | [`cua spacesd`](</docs/cua-cli/reference/cli/spacesd>) | Talk to a cua-spacesd directly by URL. | | [`cua daemon`](</docs/cua-cli/reference/cli/daemon>) | Start, stop and inspect the cua daemon. | | [`cua cache`](</docs/cua-cli/reference/cli/cache>) | See and reclaim the disk used by images, sandboxes, builds and logs. | | [`cua runtime`](</docs/cua-cli/reference/cli/runtime>) | Inspect and provision local runtimes. | | [`cua doctor`](</docs/cua-cli/reference/cli/doctor>) | Check the host, an image and a sandbox guest, or eval parity between image variants. | | [`cua telemetry`](</docs/cua-cli/reference/cli/telemetry>) | Anonymous usage telemetry (status, off, show-last). | ## MCP tools | Tools | Description | | --- | --- | | [All tools](</docs/cua-cli/reference/mcp-tools>) | Every `cua mcp` tool with a one-line summary. | | [Spaces tools](</docs/cua-cli/reference/mcp-tools/spaces>) | The Spaces contract: register, claim and release Spaces, run commands, move files, stream, run agents and teleport. 86 tools. | | [Image tools](</docs/cua-cli/reference/mcp-tools/images>) | The sandbox image catalog: which images exist and which ship a browser. 1 tools. | | [Sandbox tools](</docs/cua-cli/reference/mcp-tools/sandbox>) | Create and manage sandboxes, local or in the cloud. 11 tools. | | [Teleport tools](</docs/cua-cli/reference/mcp-tools/teleport>) | Move a signed-in browser session, per site, into a sandbox through the Cua Keyvault, with consent. 1 tools. | | [Computer tools](</docs/cua-cli/reference/mcp-tools/computer>) | Screenshots, input, windows, files and shell on a sandbox desktop. 35 tools. | | [Skills tools](</docs/cua-cli/reference/mcp-tools/skills>) | List, read, record and delete skills (recorded demonstrations). 4 tools. | --- # cua Cua: sandboxes, computer actions, MCP, skills and the cua daemon. ```text output cua [OPTIONS] <COMMAND> ``` Run `cua <command> --help` for the build you have. ## Commands ### `cua auth` Sign in, inspect your Fleet identity, manage API keys and CI tokens. [Reference](</docs/cua-cli/reference/cli/auth>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua auth`](</docs/cua-cli/reference/cli/auth#cua-auth>) | Log in to Cua and inspect Fleet identity. | [`login`](</docs/cua-cli/reference/cli/auth#cua-auth-login>), [`logout`](</docs/cua-cli/reference/cli/auth#cua-auth-logout>), [`status`](</docs/cua-cli/reference/cli/auth#cua-auth-status>), [`whoami`](</docs/cua-cli/reference/cli/auth#cua-auth-whoami>), [`keys`](</docs/cua-cli/reference/cli/auth#cua-auth-keys>), [`keys ls`](</docs/cua-cli/reference/cli/auth#cua-auth-keys-ls>), [`keys create`](</docs/cua-cli/reference/cli/auth#cua-auth-keys-create>), [`keys rm`](</docs/cua-cli/reference/cli/auth#cua-auth-keys-rm>), [`provider`](</docs/cua-cli/reference/cli/auth#cua-auth-provider>), [`provider ls`](</docs/cua-cli/reference/cli/auth#cua-auth-provider-ls>), [`provider set`](</docs/cua-cli/reference/cli/auth#cua-auth-provider-set>), [`provider rm`](</docs/cua-cli/reference/cli/auth#cua-auth-provider-rm>) | | [`cua wif-token`](</docs/cua-cli/reference/cli/auth#cua-wif-token>) | Workload identity federation tokens. | [`github`](</docs/cua-cli/reference/cli/auth#cua-wif-token-github>) | ### `cua sandbox` Create, list, connect to, suspend and delete sandboxes, local or in the cloud. [Reference](</docs/cua-cli/reference/cli/sandbox>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua sandbox`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox>) | Sandboxes: local, cloud or an existing machine (direct). | [`create`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-create>), [`launch`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-launch>), [`connect`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-connect>), [`ls`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-ls>), [`info`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-info>), [`suspend`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-suspend>), [`resume`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-resume>), [`restart`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-restart>), [`keep-alive`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-keep-alive>), [`rm`](</docs/cua-cli/reference/cli/sandbox#cua-sandbox-rm>) | ### Sandbox access Run commands, copy files, forward ports, take screenshots, reach the MCP and view a sandbox (`cua sandbox exec`, `cp`, `view`, ...). [Reference](</docs/cua-cli/reference/cli/sandbox-access>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua sandbox exec`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-exec>) | Run a command (joined into one shell line, as `sh -c`). | | | [`cua sandbox shell`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-shell>) | Interactive shell (PTY), or run a command in a PTY. | | | [`cua sandbox cp`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-cp>) | Copy files: `cp NAME:/guest/path LOCAL` or `cp LOCAL NAME:/guest/path` (NAME may be a ref: `cp local:box:/tmp/x .`). | | | [`cua sandbox overlay`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-overlay>) | Inject freshly built binaries into a running sandbox (see `create --overlay`): NAME=PATH or NAME=PATH:GUEST_PATH. | | | [`cua sandbox logs`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-logs>) | Show logs: the backend console (QEMU serial, container) when there is one, else the guest system log through the spacesd. | | | [`cua sandbox port-forward`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-port-forward>) | Forward a guest port: `port-forward NAME PORT[:LOCAL]` (a local sandbox's port not published at create, and cloud sandboxes, tunnel through cua-spacesd when the image has it; cloud images without it get HTTP and WebSocket through a loopback proxy to the cloud gateway). | | | [`cua sandbox url`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-url>) | Print a URL for a named service: usable from this machine (default), or `--public` for a shareable URL that expires (`--ttl`, default 1h; cloud: a signed URL; local: a token URL served by the cua daemon). | | | [`cua sandbox screenshot`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-screenshot>) | Save a screenshot (PNG). | | | [`cua sandbox mcp`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp>) | Talk to an MCP server a sandbox serves: `mcp NAME SERVICE tools`, `mcp NAME SERVICE call TOOL '{"a":1}'`. | [`config`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-config>), [`info`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-info>), [`tools`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-tools>), [`call`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-call>), [`resources`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-resources>), [`templates`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-templates>), [`read`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-read>), [`prompts`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-prompts>), [`prompt`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-prompt>), [`request`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-mcp-request>) | | [`cua sandbox view`](</docs/cua-cli/reference/cli/sandbox-access#cua-sandbox-view>) | Open the sandbox desktop in the browser (the cua-spacesd HTML5 viewer). | | ### `cua config` User defaults: where sandboxes run (`default.on`), the default kind and runtime, cloud defaults. [Reference](</docs/cua-cli/reference/cli/config>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua config`](</docs/cua-cli/reference/cli/config#cua-config>) | User defaults (~/.cua/config.toml): where sandboxes run when --on is not given (default.on), the default kind and runtime, cloud defaults. | [`list`](</docs/cua-cli/reference/cli/config#cua-config-list>), [`get`](</docs/cua-cli/reference/cli/config#cua-config-get>), [`set`](</docs/cua-cli/reference/cli/config#cua-config-set>), [`unset`](</docs/cua-cli/reference/cli/config#cua-config-unset>), [`path`](</docs/cua-cli/reference/cli/config#cua-config-path>) | ### `cua viewer` One local browser page that lists every sandbox and opens its viewer. [Reference](</docs/cua-cli/reference/cli/viewer>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua viewer`](</docs/cua-cli/reference/cli/viewer#cua-viewer>) | Serve the HTML5 viewer for every sandbox on one loopback port. | | ### `cua do` One-shot computer actions (screenshot, click, type, windows) against a selected target. [Reference](</docs/cua-cli/reference/cli/do>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua do`](</docs/cua-cli/reference/cli/do#cua-do>) | One-shot computer actions against the selected target. | [`switch`](</docs/cua-cli/reference/cli/do#cua-do-switch>), [`status`](</docs/cua-cli/reference/cli/do#cua-do-status>), [`ls`](</docs/cua-cli/reference/cli/do#cua-do-ls>), [`zoom`](</docs/cua-cli/reference/cli/do#cua-do-zoom>), [`unzoom`](</docs/cua-cli/reference/cli/do#cua-do-unzoom>), [`screenshot`](</docs/cua-cli/reference/cli/do#cua-do-screenshot>), [`snapshot`](</docs/cua-cli/reference/cli/do#cua-do-snapshot>), [`click`](</docs/cua-cli/reference/cli/do#cua-do-click>), [`dclick`](</docs/cua-cli/reference/cli/do#cua-do-dclick>), [`move`](</docs/cua-cli/reference/cli/do#cua-do-move>), [`type`](</docs/cua-cli/reference/cli/do#cua-do-type>), [`key`](</docs/cua-cli/reference/cli/do#cua-do-key>), [`hotkey`](</docs/cua-cli/reference/cli/do#cua-do-hotkey>), [`scroll`](</docs/cua-cli/reference/cli/do#cua-do-scroll>), [`drag`](</docs/cua-cli/reference/cli/do#cua-do-drag>), [`shell`](</docs/cua-cli/reference/cli/do#cua-do-shell>), [`open`](</docs/cua-cli/reference/cli/do#cua-do-open>), [`launch`](</docs/cua-cli/reference/cli/do#cua-do-launch>), [`window`](</docs/cua-cli/reference/cli/do#cua-do-window>), [`window ls`](</docs/cua-cli/reference/cli/do#cua-do-window-ls>), [`window unfocus`](</docs/cua-cli/reference/cli/do#cua-do-window-unfocus>), [`window focus`](</docs/cua-cli/reference/cli/do#cua-do-window-focus>), [`window minimize`](</docs/cua-cli/reference/cli/do#cua-do-window-minimize>), [`window maximize`](</docs/cua-cli/reference/cli/do#cua-do-window-maximize>), [`window restore`](</docs/cua-cli/reference/cli/do#cua-do-window-restore>), [`window close`](</docs/cua-cli/reference/cli/do#cua-do-window-close>), [`window resize`](</docs/cua-cli/reference/cli/do#cua-do-window-resize>), [`window move`](</docs/cua-cli/reference/cli/do#cua-do-window-move>), [`window info`](</docs/cua-cli/reference/cli/do#cua-do-window-info>), [`a11y`](</docs/cua-cli/reference/cli/do#cua-do-a11y>), [`a11y tree`](</docs/cua-cli/reference/cli/do#cua-do-a11y-tree>), [`a11y find`](</docs/cua-cli/reference/cli/do#cua-do-a11y-find>), [`a11y act`](</docs/cua-cli/reference/cli/do#cua-do-a11y-act>), [`cursor`](</docs/cua-cli/reference/cli/do#cua-do-cursor>), [`clipboard`](</docs/cua-cli/reference/cli/do#cua-do-clipboard>), [`clipboard get`](</docs/cua-cli/reference/cli/do#cua-do-clipboard-get>), [`clipboard set`](</docs/cua-cli/reference/cli/do#cua-do-clipboard-set>) | | [`cua do-host-consent`](</docs/cua-cli/reference/cli/do#cua-do-host-consent>) | Grant consent for `cua do switch host`. | | ### `cua image` Pull, build and push OCI images, and manage Fleet image resources; list the image catalog. [Reference](</docs/cua-cli/reference/cli/image>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua image`](</docs/cua-cli/reference/cli/image#cua-image>) | Images: local OCI pull/build/push and Fleet image resources. | [`pull`](</docs/cua-cli/reference/cli/image#cua-image-pull>), [`build`](</docs/cua-cli/reference/cli/image#cua-image-build>), [`push`](</docs/cua-cli/reference/cli/image#cua-image-push>), [`ls`](</docs/cua-cli/reference/cli/image#cua-image-ls>), [`info`](</docs/cua-cli/reference/cli/image#cua-image-info>), [`rm`](</docs/cua-cli/reference/cli/image#cua-image-rm>), [`create`](</docs/cua-cli/reference/cli/image#cua-image-create>) | | [`cua images`](</docs/cua-cli/reference/cli/image#cua-images>) | The sandbox image catalog (the images the docs list): which exist, their variants, and which ship cua-spacesd and a browser. | [`ls`](</docs/cua-cli/reference/cli/image#cua-images-ls>), [`info`](</docs/cua-cli/reference/cli/image#cua-images-info>), [`build`](</docs/cua-cli/reference/cli/image#cua-images-build>), [`pack`](</docs/cua-cli/reference/cli/image#cua-images-pack>), [`publish`](</docs/cua-cli/reference/cli/image#cua-images-publish>), [`release`](</docs/cua-cli/reference/cli/image#cua-images-release>), [`promote`](</docs/cua-cli/reference/cli/image#cua-images-promote>) | ### `cua fleet` Managed Fleet pools behind `cua sandbox create`. [Reference](</docs/cua-cli/reference/cli/fleet>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua fleet`](</docs/cua-cli/reference/cli/fleet#cua-fleet>) | Fleet: managed pools behind `cua sandbox create` without `--pool`. | [`pools`](</docs/cua-cli/reference/cli/fleet#cua-fleet-pools>), [`pools ls`](</docs/cua-cli/reference/cli/fleet#cua-fleet-pools-ls>), [`pools gc`](</docs/cua-cli/reference/cli/fleet#cua-fleet-pools-gc>), [`pool`](</docs/cua-cli/reference/cli/fleet#cua-fleet-pool>), [`pool export`](</docs/cua-cli/reference/cli/fleet#cua-fleet-pool-export>) | ### `cua mcp` The stdio MCP server for AI assistants. [Reference](</docs/cua-cli/reference/cli/mcp>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua mcp`](</docs/cua-cli/reference/cli/mcp#cua-mcp>) | Stdio MCP server exposing sandboxes, computer control and skills. | | ### `cua agent` Run coding agents (Claude Code, Codex, Gemini CLI, ...) inside a sandbox and follow, continue or stop them. [Reference](</docs/cua-cli/reference/cli/agent>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua agent`](</docs/cua-cli/reference/cli/agent#cua-agent>) | Run coding agents inside a sandbox: run, ls, logs, send, interrupt, stop. | [`run`](</docs/cua-cli/reference/cli/agent#cua-agent-run>), [`ls`](</docs/cua-cli/reference/cli/agent#cua-agent-ls>), [`logs`](</docs/cua-cli/reference/cli/agent#cua-agent-logs>), [`send`](</docs/cua-cli/reference/cli/agent#cua-agent-send>), [`interrupt`](</docs/cua-cli/reference/cli/agent#cua-agent-interrupt>), [`stop`](</docs/cua-cli/reference/cli/agent#cua-agent-stop>), [`status`](</docs/cua-cli/reference/cli/agent#cua-agent-status>), [`rm`](</docs/cua-cli/reference/cli/agent#cua-agent-rm>), [`ensure`](</docs/cua-cli/reference/cli/agent#cua-agent-ensure>), [`harnesses`](</docs/cua-cli/reference/cli/agent#cua-agent-harnesses>), [`create`](</docs/cua-cli/reference/cli/agent#cua-agent-create>), [`persistent`](</docs/cua-cli/reference/cli/agent#cua-agent-persistent>), [`tell`](</docs/cua-cli/reference/cli/agent#cua-agent-tell>), [`save`](</docs/cua-cli/reference/cli/agent#cua-agent-save>), [`pause`](</docs/cua-cli/reference/cli/agent#cua-agent-pause>), [`resume`](</docs/cua-cli/reference/cli/agent#cua-agent-resume>), [`forget`](</docs/cua-cli/reference/cli/agent#cua-agent-forget>), [`routine`](</docs/cua-cli/reference/cli/agent#cua-agent-routine>), [`routine add`](</docs/cua-cli/reference/cli/agent#cua-agent-routine-add>), [`routine ls`](</docs/cua-cli/reference/cli/agent#cua-agent-routine-ls>), [`routine rm`](</docs/cua-cli/reference/cli/agent#cua-agent-routine-rm>), [`routine enable`](</docs/cua-cli/reference/cli/agent#cua-agent-routine-enable>), [`routine disable`](</docs/cua-cli/reference/cli/agent#cua-agent-routine-disable>), [`allow-computer`](</docs/cua-cli/reference/cli/agent#cua-agent-allow-computer>), [`revoke-computer`](</docs/cua-cli/reference/cli/agent#cua-agent-revoke-computer>), [`computer-access`](</docs/cua-cli/reference/cli/agent#cua-agent-computer-access>), [`notifications`](</docs/cua-cli/reference/cli/agent#cua-agent-notifications>) | ### `cua agents` Install cua skills and the cua MCP server into AI coding agents. [Reference](</docs/cua-cli/reference/cli/agents>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua agents`](</docs/cua-cli/reference/cli/agents#cua-agents>) | AI coding agents on this machine: install cua skills and configure the cua MCP server. | [`detect`](</docs/cua-cli/reference/cli/agents#cua-agents-detect>), [`setup`](</docs/cua-cli/reference/cli/agents#cua-agents-setup>), [`status`](</docs/cua-cli/reference/cli/agents#cua-agents-status>), [`remove`](</docs/cua-cli/reference/cli/agents#cua-agents-remove>), [`update`](</docs/cua-cli/reference/cli/agents#cua-agents-update>) | ### `cua skills` Record, list and replay demonstrations (skills). [Reference](</docs/cua-cli/reference/cli/skills>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua skills`](</docs/cua-cli/reference/cli/skills#cua-skills>) | Recorded demonstrations (skills) for agents. | [`list`](</docs/cua-cli/reference/cli/skills#cua-skills-list>), [`read`](</docs/cua-cli/reference/cli/skills#cua-skills-read>), [`replay`](</docs/cua-cli/reference/cli/skills#cua-skills-replay>), [`delete`](</docs/cua-cli/reference/cli/skills#cua-skills-delete>), [`clean`](</docs/cua-cli/reference/cli/skills#cua-skills-clean>), [`record`](</docs/cua-cli/reference/cli/skills#cua-skills-record>) | ### `cua trajectory` Recorded `cua do` trajectories. [Reference](</docs/cua-cli/reference/cli/trajectory>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua trajectory`](</docs/cua-cli/reference/cli/trajectory#cua-trajectory>) | Recorded `cua do` trajectories. | [`ls`](</docs/cua-cli/reference/cli/trajectory#cua-trajectory-ls>), [`view`](</docs/cua-cli/reference/cli/trajectory#cua-trajectory-view>), [`stop`](</docs/cua-cli/reference/cli/trajectory#cua-trajectory-stop>), [`clean`](</docs/cua-cli/reference/cli/trajectory#cua-trajectory-clean>) | ### `cua spaces` Registered Spaces and your machines on the relay. [Reference](</docs/cua-cli/reference/cli/spaces>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua spaces`](</docs/cua-cli/reference/cli/spaces#cua-spaces>) | Spaces: registered ones and your machines on the relay. | [`ls`](</docs/cua-cli/reference/cli/spaces#cua-spaces-ls>), [`add`](</docs/cua-cli/reference/cli/spaces#cua-spaces-add>), [`create`](</docs/cua-cli/reference/cli/spaces#cua-spaces-create>), [`cancel`](</docs/cua-cli/reference/cli/spaces#cua-spaces-cancel>), [`gpus`](</docs/cua-cli/reference/cli/spaces#cua-spaces-gpus>), [`delete`](</docs/cua-cli/reference/cli/spaces#cua-spaces-delete>), [`stop`](</docs/cua-cli/reference/cli/spaces#cua-spaces-stop>), [`start`](</docs/cua-cli/reference/cli/spaces#cua-spaces-start>), [`rm`](</docs/cua-cli/reference/cli/spaces#cua-spaces-rm>), [`share`](</docs/cua-cli/reference/cli/spaces#cua-spaces-share>), [`unshare`](</docs/cua-cli/reference/cli/spaces#cua-spaces-unshare>), [`shares`](</docs/cua-cli/reference/cli/spaces#cua-spaces-shares>), [`relay-register`](</docs/cua-cli/reference/cli/spaces#cua-spaces-relay-register>), [`relay-unregister`](</docs/cua-cli/reference/cli/spaces#cua-spaces-relay-unregister>) | ### `cua cloud` Your own AWS, Google Cloud or Modal account as a place for sandboxes and Spaces: connect, test, status, sweep. [Reference](</docs/cua-cli/reference/cli/cloud>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua cloud`](</docs/cua-cli/reference/cli/cloud#cua-cloud>) | Spaces in your own cloud account (AWS, Google Cloud, Modal): connect, status, test, disconnect, sweep. | [`status`](</docs/cua-cli/reference/cli/cloud#cua-cloud-status>), [`connect`](</docs/cua-cli/reference/cli/cloud#cua-cloud-connect>), [`test`](</docs/cua-cli/reference/cli/cloud#cua-cloud-test>), [`disconnect`](</docs/cua-cli/reference/cli/cloud#cua-cloud-disconnect>), [`sweep`](</docs/cua-cli/reference/cli/cloud#cua-cloud-sweep>) | ### `cua host` Set this machine up for unattended access. [Reference](</docs/cua-cli/reference/cli/host>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua host`](</docs/cua-cli/reference/cli/host#cua-host>) | Unattended access to this machine (setup, status, stop, start, remove). | [`setup`](</docs/cua-cli/reference/cli/host#cua-host-setup>), [`config`](</docs/cua-cli/reference/cli/host#cua-host-config>), [`status`](</docs/cua-cli/reference/cli/host#cua-host-status>), [`stop`](</docs/cua-cli/reference/cli/host#cua-host-stop>), [`start`](</docs/cua-cli/reference/cli/host#cua-host-start>), [`share`](</docs/cua-cli/reference/cli/host#cua-host-share>), [`unshare`](</docs/cua-cli/reference/cli/host#cua-host-unshare>), [`remove`](</docs/cua-cli/reference/cli/host#cua-host-remove>) | ### `cua devices` Enroll this device on the relay, approve, rename and revoke devices, and read the access log. [Reference](</docs/cua-cli/reference/cli/devices>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua devices`](</docs/cua-cli/reference/cli/devices#cua-devices>) | This device as a client of your cua.ai account on the relay: enroll (second factor), approve other devices, list, rename, revoke, and the audit log of who accessed what. | [`status`](</docs/cua-cli/reference/cli/devices#cua-devices-status>), [`enroll`](</docs/cua-cli/reference/cli/devices#cua-devices-enroll>), [`approve`](</docs/cua-cli/reference/cli/devices#cua-devices-approve>), [`ls`](</docs/cua-cli/reference/cli/devices#cua-devices-ls>), [`rename`](</docs/cua-cli/reference/cli/devices#cua-devices-rename>), [`revoke`](</docs/cua-cli/reference/cli/devices#cua-devices-revoke>), [`audit`](</docs/cua-cli/reference/cli/devices#cua-devices-audit>) | ### `cua teleport` Move a desktop app session into a sandbox. [Reference](</docs/cua-cli/reference/cli/teleport>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua teleport`](</docs/cua-cli/reference/cli/teleport#cua-teleport>) | Move a desktop app session (tabs, profile, sign-in) into a sandbox. | [`providers`](</docs/cua-cli/reference/cli/teleport#cua-teleport-providers>), [`manifest`](</docs/cua-cli/reference/cli/teleport#cua-teleport-manifest>), [`push`](</docs/cua-cli/reference/cli/teleport#cua-teleport-push>) | ### `cua volume` Cua Volume: files, versions, grants, access requests and audit of the volume every Space and agent shares. [Reference](</docs/cua-cli/reference/cli/volume>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua volume`](</docs/cua-cli/reference/cli/volume#cua-volume>) | Cua Volume: the versioned volume every Space and agent shares (public/, agents/<agent>/, spaces/<space>/), its grants and audit. | [`ls`](</docs/cua-cli/reference/cli/volume#cua-volume-ls>), [`cat`](</docs/cua-cli/reference/cli/volume#cua-volume-cat>), [`put`](</docs/cua-cli/reference/cli/volume#cua-volume-put>), [`rm`](</docs/cua-cli/reference/cli/volume#cua-volume-rm>), [`history`](</docs/cua-cli/reference/cli/volume#cua-volume-history>), [`restore`](</docs/cua-cli/reference/cli/volume#cua-volume-restore>), [`grant`](</docs/cua-cli/reference/cli/volume#cua-volume-grant>), [`revoke`](</docs/cua-cli/reference/cli/volume#cua-volume-revoke>), [`grants`](</docs/cua-cli/reference/cli/volume#cua-volume-grants>), [`requests`](</docs/cua-cli/reference/cli/volume#cua-volume-requests>), [`approve`](</docs/cua-cli/reference/cli/volume#cua-volume-approve>), [`deny`](</docs/cua-cli/reference/cli/volume#cua-volume-deny>), [`audit`](</docs/cua-cli/reference/cli/volume#cua-volume-audit>), [`mount`](</docs/cua-cli/reference/cli/volume#cua-volume-mount>), [`unmount`](</docs/cua-cli/reference/cli/volume#cua-volume-unmount>), [`status`](</docs/cua-cli/reference/cli/volume#cua-volume-status>), [`config`](</docs/cua-cli/reference/cli/volume#cua-volume-config>), [`config show`](</docs/cua-cli/reference/cli/volume#cua-volume-config-show>), [`config set`](</docs/cua-cli/reference/cli/volume#cua-volume-config-set>), [`config set-keys`](</docs/cua-cli/reference/cli/volume#cua-volume-config-set-keys>) | ### `cua keyvault` Set up, unlock and lock the Cua Keyvault, with the OS key store or a passphrase. [Reference](</docs/cua-cli/reference/cli/keyvault>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua keyvault`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault>) | The Cua Keyvault the daemon hosts: status, init, unlock, lock, saved passwords and approvals. | [`status`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-status>), [`init`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-init>), [`unlock`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-unlock>), [`lock`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-lock>), [`import-passwords`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-import-passwords>), [`import-session`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-import-session>), [`requests`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-requests>), [`approve`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-approve>), [`deny`](</docs/cua-cli/reference/cli/keyvault#cua-keyvault-deny>) | ### `cua spacesd` Talk to a cua-spacesd directly by URL. [Reference](</docs/cua-cli/reference/cli/spacesd>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua spacesd`](</docs/cua-cli/reference/cli/spacesd#cua-spacesd>) | Talk to a cua-spacesd directly by URL. | [`caps`](</docs/cua-cli/reference/cli/spacesd#cua-spacesd-caps>), [`exec`](</docs/cua-cli/reference/cli/spacesd#cua-spacesd-exec>), [`cp`](</docs/cua-cli/reference/cli/spacesd#cua-spacesd-cp>), [`shell`](</docs/cua-cli/reference/cli/spacesd#cua-spacesd-shell>), [`targets`](</docs/cua-cli/reference/cli/spacesd#cua-spacesd-targets>), [`call`](</docs/cua-cli/reference/cli/spacesd#cua-spacesd-call>) | ### `cua daemon` Start, stop and inspect the cua daemon. [Reference](</docs/cua-cli/reference/cli/daemon>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua daemon`](</docs/cua-cli/reference/cli/daemon#cua-daemon>) | The cua daemon. | [`start`](</docs/cua-cli/reference/cli/daemon#cua-daemon-start>), [`stop`](</docs/cua-cli/reference/cli/daemon#cua-daemon-stop>), [`status`](</docs/cua-cli/reference/cli/daemon#cua-daemon-status>), [`mcp`](</docs/cua-cli/reference/cli/daemon#cua-daemon-mcp>) | ### `cua cache` See and reclaim the disk used by images, sandboxes, builds and logs. [Reference](</docs/cua-cli/reference/cli/cache>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua cache`](</docs/cua-cli/reference/cli/cache#cua-cache>) | Disk used by images, sandboxes, builds and logs, and its cleanup. | [`ls`](</docs/cua-cli/reference/cli/cache#cua-cache-ls>), [`du`](</docs/cua-cli/reference/cli/cache#cua-cache-du>), [`prune`](</docs/cua-cli/reference/cli/cache#cua-cache-prune>), [`config`](</docs/cua-cli/reference/cli/cache#cua-cache-config>) | ### `cua runtime` Inspect and provision local runtimes. [Reference](</docs/cua-cli/reference/cli/runtime>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua runtime`](</docs/cua-cli/reference/cli/runtime#cua-runtime>) | Local runtimes (cua-vmm). | [`doctor`](</docs/cua-cli/reference/cli/runtime#cua-runtime-doctor>), [`setup`](</docs/cua-cli/reference/cli/runtime#cua-runtime-setup>) | ### `cua doctor` Check the host, an image and a sandbox guest, or eval parity between image variants. [Reference](</docs/cua-cli/reference/cli/doctor>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua doctor`](</docs/cua-cli/reference/cli/doctor#cua-doctor>) | Check the host, an image and a sandbox's guest in one report (`cua doctor [REF]`), or eval parity between two variants (`cua doctor parity A B`). | [`parity`](</docs/cua-cli/reference/cli/doctor#cua-doctor-parity>) | ### `cua telemetry` Anonymous usage telemetry (status, off, show-last). [Reference](</docs/cua-cli/reference/cli/telemetry>). | Command | Description | Subcommands | | --- | --- | --- | | [`cua telemetry`](</docs/cua-cli/reference/cli/telemetry#cua-telemetry>) | Anonymous usage telemetry (status, on, off, show-last, reset-id, schema). | [`status`](</docs/cua-cli/reference/cli/telemetry#cua-telemetry-status>), [`on`](</docs/cua-cli/reference/cli/telemetry#cua-telemetry-on>), [`off`](</docs/cua-cli/reference/cli/telemetry#cua-telemetry-off>), [`show-last`](</docs/cua-cli/reference/cli/telemetry#cua-telemetry-show-last>), [`reset-id`](</docs/cua-cli/reference/cli/telemetry#cua-telemetry-reset-id>), [`schema`](</docs/cua-cli/reference/cli/telemetry#cua-telemetry-schema>) | ## Global options Accepted by every command, before or after the subcommand. | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--embedded` | boolean | `false` | | Use an embedded runtime even when a daemon runs. | | `--daemon` | string | | `CUA_DAEMON` | Daemon address (socket path or loopback URL). | | `--daemon-token` | string | | `CUA_DAEMON_TOKEN` | Daemon token (loopback URLs). | | `--state-dir` | string | | | Sandbox state directory (embedded; default ~/.cua/sandboxes). | | `--json` | boolean | `false` | | Print JSON. | `--help` prints help for any command; `--version` prints the version. ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua auth Sign in, inspect your Fleet identity, manage API keys and CI tokens. | Command | Description | | --- | --- | | [`cua auth`](#cua-auth) | Log in to Cua and inspect Fleet identity. | | [`cua auth login`](#cua-auth-login) | Log in (browser sign-in; `--remote` for a device code), then offer to set up your AI coding agents. | | [`cua auth logout`](#cua-auth-logout) | Revoke and remove the local session. | | [`cua auth status`](#cua-auth-status) | Local session state (no network). | | [`cua auth whoami`](#cua-auth-whoami) | The active Fleet identity, verified against Fleet. | | [`cua auth keys`](#cua-auth-keys) | Fleet user API keys (client credentials). | | [`cua auth keys ls`](#cua-auth-keys-ls) | List API keys. | | [`cua auth keys create`](#cua-auth-keys-create) | Create an API key (prints CUA_CLIENT_ID / CUA_CLIENT_SECRET once). | | [`cua auth keys rm`](#cua-auth-keys-rm) | Delete an API key by id. | | [`cua auth provider`](#cua-auth-provider) | Keys of the contrib sandbox providers (`--on e2b`, `--on daytona`, `--on modal`). | | [`cua auth provider ls`](#cua-auth-provider-ls) | Providers, whether this build has them and where their keys come from (never the values). | | [`cua auth provider set`](#cua-auth-provider-set) | Store a provider key, read from stdin (no echo on a terminal). | | [`cua auth provider rm`](#cua-auth-provider-rm) | Remove a provider's stored keys. | | [`cua wif-token`](#cua-wif-token) | Workload identity federation tokens. | | [`cua wif-token github`](#cua-wif-token-github) | Print a GitHub Actions OIDC token for Fleets. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). ## `cua auth` Log in to Cua and inspect Fleet identity. ```text output cua auth [OPTIONS] <COMMAND> ``` ### `cua auth login` Log in (browser sign-in; `--remote` for a device code), then offer to set up your AI coding agents. ```text output cua auth login [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--agents` | | string | | Agents: `all` (installed), `none`, or ids such as `claude,codex,hermes` (`cua agents detect` lists every supported agent). | | `--mcp-command` | | string | | Command agents launch for the MCP server (default: the `cua` on PATH, else this binary). | | `--no-browser` | | boolean | `false` | Do not open a browser; print the URL. | | `--remote` | | boolean | `false` | Sign in from another device with a code (for SSH sessions and machines without a browser). | | `--no-onboarding` | | boolean | `false` | Skip the agent onboarding prompt. | | `--skills-only` | | boolean | `false` | Only install skills. Alias: `--no-mcp`. | | `--mcp-only` | | boolean | `false` | Only configure the MCP server. Alias: `--no-skills`. | | `--yes` | `-y` | boolean | `false` | Answer yes to every prompt. | | `--force` | | boolean | `false` | Replace skill folders cua did not write (the old one is backed up). | **Examples** ```bash test="cli-shape" id="ref-cua-auth-login" # Sign in with the browser cua auth login # Sign in over SSH with a device code cua auth login --remote # Sign in and set up Claude Code without prompts cua auth login --agents claude -y ``` ### `cua auth logout` Revoke and remove the local session. ```text output cua auth logout [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-auth-logout" cua auth logout ``` ### `cua auth status` Local session state (no network). ```text output cua auth status [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-auth-status" cua auth status cua auth status --json ``` ### `cua auth whoami` The active Fleet identity, verified against Fleet. ```text output cua auth whoami [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-auth-whoami" cua auth whoami ``` ### `cua auth keys` Fleet user API keys (client credentials). ```text output cua auth keys [OPTIONS] <COMMAND> ``` #### `cua auth keys ls` List API keys. ```text output cua auth keys ls [OPTIONS] ``` Alias: `cua auth keys list`. **Examples** ```bash test="cli-shape" id="ref-cua-auth-keys-ls" cua auth keys ls ``` #### `cua auth keys create` Create an API key (prints CUA_CLIENT_ID / CUA_CLIENT_SECRET once). ```text output cua auth keys create [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Key name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--scope` | string | | Scopes (repeatable). | **Examples** ```bash test="cli-shape" id="ref-cua-auth-keys-create" # A key for CI cua auth keys create ci ``` #### `cua auth keys rm` Delete an API key by id. ```text output cua auth keys rm [OPTIONS] <ID> ``` Alias: `cua auth keys delete`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ID>` | string | required | Key id (from `cua auth keys ls`). | **Examples** ```bash test="cli-shape" id="ref-cua-auth-keys-rm" cua auth keys rm <key-id> ``` ### `cua auth provider` Keys of the contrib sandbox providers (`--on e2b`, `--on daytona`, `--on modal`). ```text output cua auth provider [OPTIONS] <COMMAND> ``` #### `cua auth provider ls` Providers, whether this build has them and where their keys come from (never the values). ```text output cua auth provider ls [OPTIONS] ``` Alias: `cua auth provider list`. **Examples** ```bash test="cli-shape" id="ref-cua-auth-provider-ls" cua auth provider ls cua auth provider ls --json ``` #### `cua auth provider set` Store a provider key, read from stdin (no echo on a terminal). An environment variable of the same name wins over the stored key. ```text output cua auth provider set [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Provider (`e2b`, `daytona`, `modal`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--var` | string | | Only this variable (for example MODAL_TOKEN_SECRET). | **Examples** ```bash test="cli-shape" id="ref-cua-auth-provider-set" # Prompt for E2B_API_KEY (no echo) cua auth provider set e2b # One variable only cua auth provider set daytona --var DAYTONA_API_KEY ``` #### `cua auth provider rm` Remove a provider's stored keys. ```text output cua auth provider rm [OPTIONS] <NAME> ``` Alias: `cua auth provider delete`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Provider. | **Examples** ```bash test="cli-shape" id="ref-cua-auth-provider-rm" cua auth provider rm e2b ``` ## `cua wif-token` Workload identity federation tokens. ```text output cua wif-token [OPTIONS] <COMMAND> ``` ### `cua wif-token github` Print a GitHub Actions OIDC token for Fleets. ```text output cua wif-token github [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--audience` | string | `fleets` | Token audience. | **Examples** ```bash test="cli-shape" id="ref-cua-wif-token-github" # In a GitHub Actions job with `id-token: write` cua wif-token github ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua sandbox Create, list, connect to, suspend and delete sandboxes, local or in the cloud. | Command | Description | | --- | --- | | [`cua sandbox create`](#cua-sandbox-create) | Create a sandbox: `cua sb create IMAGE [--on local\|cloud\|direct:<addr>] [--kind auto\|container\|vm] [--runtime auto\|gvisor\|runc\|qemu\|lume\|kubevirt]`. | | [`cua sandbox launch`](#cua-sandbox-launch) | Deprecated alias of `create` that defaults to `--on cloud`. | | [`cua sandbox connect`](#cua-sandbox-connect) | Reattach to a sandbox (NAME or a ref such as `cloud:NAME`) and print it. | | [`cua sandbox ls`](#cua-sandbox-ls) | List sandboxes: local, direct and cloud (live Fleet claims) by default, each with its `location`; `--local` or `--cloud` filters. | | [`cua sandbox info`](#cua-sandbox-info) | Show one sandbox (or a Fleet pool of that name). | | [`cua sandbox suspend`](#cua-sandbox-suspend) | Suspend (local: pause/snapshot; Fleet managed pool: release the claim and keep the record; explicit Fleet pool: scale to zero). | | [`cua sandbox resume`](#cua-sandbox-resume) | Resume (Fleet managed pool: claim a fresh sandbox of the same shape). | | [`cua sandbox restart`](#cua-sandbox-restart) | Restart. | | [`cua sandbox keep-alive`](#cua-sandbox-keep-alive) | Extend a Fleet claim's lease. | | [`cua sandbox rm`](#cua-sandbox-rm) | Delete a sandbox (Fleet: release the claim; managed pools are kept for reuse). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Sandboxes: local, cloud or an existing machine (direct). ```text output cua sandbox [OPTIONS] <COMMAND> ``` Alias: `cua sb`. ## `cua sandbox create` Create a sandbox: `cua sb create IMAGE [--on local|cloud|direct:<addr>] [--kind auto|container|vm] [--runtime auto|gvisor|runc|qemu|lume|kubevirt]`. ```text output cua sandbox create [OPTIONS] [IMAGE] [-- <COMMAND>...] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<IMAGE>` | string | optional | Image: a registry reference (`ghcr.io/org/image:tag`), an alias (`linux`, `ubuntu`, `windows`, `macos[:tahoe\|sequoia]`, `omarchy`), or locally `pool:<name>` to run that cloud pool's template image with its firmware, services and readiness probe (`fleet:<name>` is the deprecated spelling). | | `<COMMAND>...` | string | optional | The sandbox's command, after `--` (replaces the image's entrypoint): `cua sb create python:3.12-slim --service mcp=8765 -- python -m srv`. Repeatable. | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--on` | string | | | Where it runs: local, cloud, direct:<addr> (an existing machine running cua-spacesd), or a registered provider. Default: `cua config get default.on` (`CUA_DEFAULT_ON`), else local. | | `--kind` | string | | | What kind of machine: auto (from the image: a container when it has a container rootfs; macOS, Windows and disk-only images are VMs), container or vm. | | `--runtime` | string | | | Which engine: auto (the safest available) or one the location offers for the kind. Local: gvisor or runc (containers), qemu or lume (VMs). Cloud: gvisor (containers), kubevirt (VMs). A local sandbox with --sidecar needs `--runtime runc` where gVisor would run (separate gVisor containers cannot share a network namespace). | | `--tier` | string | | | Image tier for an alias: `slim`, `full` (default) or macOS `xcode`. | | `--name` | string | | | Sandbox name (generated when omitted). | | `--open` | string | | | With --browser: open this URL. | | `--install` | string | | | Install these into the sandbox once it is up: harness ids (`claude-code`) or installables (`blender`, `vscode`, `node`), pinned and checksum-verified (`cua agent ensure` later does the same). Repeatable. | | `--cpu` | integer | | | vCPUs. Alias: `--cpus`. | | `--memory` | string | | | Memory, e.g. 8GB or 4096MB (a bare number is GB). | | `--network` | `default` \| `none` | | | Guest network: `default` (outbound network, like a Docker container) or `none` (no egress; published ports still work). `none` needs a local QEMU VM; containers, Lume and cloud sandboxes reject it. | | `--disk` | string | | | Disk size (accepted; the image decides today). | | `--port` | string | | | Expose a guest port: NAME=PORT (a named service) or PORT (repeatable). Alias: `--service`. | | `--wait` | string | | | Wait until ready: tcp:SERVICE, http:SERVICE/path, a guest port (tcp:PORT, http:PORT/path), or `desktop` (the desktop session takes input: display, window manager and cua-driver) (repeatable). Alias: `--wait-for`. | | `--ready-timeout` | integer | | | Readiness budget in seconds, counted once the image is downloaded, the wait for the image's cua-spacesd included (default 600, plus up to 120 for cua-spacesd). | | `--overlay` | string | | | Inject a freshly built binary once the sandbox is up: NAME=PATH (`cua-driver`, `cua-spacesd`) or NAME=PATH:GUEST_PATH (repeatable). It replaces the guest file atomically, is recorded with its sha256 (checked by `cua doctor`) and what runs it is restarted. Needs cua-spacesd in the image. | | `--env` | string | | | Environment KEY=VALUE for the sandbox (repeatable). | | `--sidecar` | string | | | A sidecar container, reachable from the sandbox at its name (it reaches the sandbox at `main`): IMAGE[,port=PORT][,env=K=V][,name=NAME] (repeatable; `port=` and `env=` repeat too). Local containers and every cloud sandbox; locally it needs `--runtime runc` where gVisor would run. With sidecars the service names main, sidecars and sc are reserved. | | `--registry-secret` | string | | | Credentials for a private image: env:USER_VAR:PASSWORD_VAR (read from this environment) or aws-ecr[:REGION] (the AWS CLI's login token). | | `--max-pool-size` | integer | | | Cloud: most sandboxes of this image at once. | | `--claim-ttl` | integer | | | Cloud: how long the sandbox outlives this command without a keep-alive (`15m`, `900`); renewed while the daemon holds it. | | `--pool` | string | | | Cloud: use this dedicated pool instead of shared capacity. The sandbox fields given (IMAGE, --cmd, --env, --port, --sidecar, --cpu, --memory) must match its template, or creation fails with the diff. | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token (direct: sandboxes). | | `--gpu` | string | | | A GPU: the runtime's own (`--gpu`), or an option `cua spaces gpus` lists (`paravirtual`: GPU acceleration for a macOS VM on Lume, experimental; `virgl`: QEMU on Linux; `nvidia`: a runc container on Linux; a provider's GPU type). | | `--browser` | boolean | `false` | | Start Chromium in it for the cua-driver browser tools (IMAGE defaults to `linux`). | | `--warm` | boolean | `false` | | Cloud: keep one warm sandbox of this image ready (the default for the canonical linux/windows/macos images). | | `--no-warm` | boolean | `false` | | Cloud: no warm sandbox, even for a canonical image. | | `--apply` | boolean | `false` | | Cloud, with --pool: update the pool's template to the given fields instead of failing when they differ. | | `--view` | boolean | `false` | | Open the desktop in the HTML5 viewer once the sandbox is ready (`cua sb view`). | | `--keep-on-failure` | boolean | `false` | | Keep the sandbox (its cloud claim, VM or container) when it fails to become ready, to debug it; the ref and how to delete it are printed. Default: a failed create deletes it and releases its claim. | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-create" # A Linux desktop on this machine (a gVisor container) cua sb create linux --name dev # A web server container, ready when its HTTP service answers cua sb create python:3.12-slim --service web=8000 --wait http:web/ -- python -m http.server 8000 # The same Linux image in the cloud instead, as a VM cua sb create linux --on cloud --kind vm --name dev ``` ## `cua sandbox launch` Deprecated alias of `create` that defaults to `--on cloud`. ```text output cua sandbox launch [OPTIONS] [IMAGE] ``` **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-launch" # A cloud Linux sandbox (same as `cua sb create linux --on cloud`) cua sb launch linux --name dev ``` ## `cua sandbox connect` Reattach to a sandbox (NAME or a ref such as `cloud:NAME`) and print it. ```text output cua sandbox connect [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-connect" cua sb connect dev cua sb connect cloud:dev ``` ## `cua sandbox ls` List sandboxes: local, direct and cloud (live Fleet claims) by default, each with its `location`; `--local` or `--cloud` filters. ```text output cua sandbox ls [OPTIONS] ``` Alias: `cua sandbox list`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Only local sandboxes. | | `--cloud` | boolean | `false` | Only cloud sandboxes, listed live from Fleet. | | `--no-size` | boolean | `false` | Skip the SIZE column (disk used by each local sandbox), which reads the VM, container and Lume stores. | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-ls" cua sb ls cua sb ls --cloud --json ``` ## `cua sandbox info` Show one sandbox (or a Fleet pool of that name). ```text output cua sandbox info [OPTIONS] <NAME> ``` Alias: `cua sandbox get`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-info" cua sb info dev cua sb info cloud:dev --json ``` ## `cua sandbox suspend` Suspend (local: pause/snapshot; Fleet managed pool: release the claim and keep the record; explicit Fleet pool: scale to zero). ```text output cua sandbox suspend [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-suspend" cua sb suspend dev ``` ## `cua sandbox resume` Resume (Fleet managed pool: claim a fresh sandbox of the same shape). ```text output cua sandbox resume [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-resume" cua sb resume dev ``` ## `cua sandbox restart` Restart. ```text output cua sandbox restart [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-restart" cua sb restart dev ``` ## `cua sandbox keep-alive` Extend a Fleet claim's lease. ```text output cua sandbox keep-alive [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--for` | integer | `15m` | Lease from now (`15m`, `2h`, `900`). | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-keep-alive" # Keep the claim for two more hours cua sb keep-alive cloud:dev --for 2h ``` ## `cua sandbox rm` Delete a sandbox (Fleet: release the claim; managed pools are kept for reuse). ```text output cua sandbox rm [OPTIONS] <NAME> ``` Alias: `cua sandbox delete`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--force` | `-f` | boolean | `false` | Skip the confirmation prompt. Required when there is no terminal to ask on (scripts, agents): without it nothing is deleted. | | `--local` | | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-rm" cua sb rm dev # Without the prompt (required in scripts and agents) cua sb rm cloud:dev --force ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # Sandbox access Run commands, copy files, forward ports, take screenshots, reach the MCP and view a sandbox (`cua sandbox exec`, `cp`, `view`, ...). | Command | Description | | --- | --- | | [`cua sandbox exec`](#cua-sandbox-exec) | Run a command (joined into one shell line, as `sh -c`). | | [`cua sandbox shell`](#cua-sandbox-shell) | Interactive shell (PTY), or run a command in a PTY. | | [`cua sandbox cp`](#cua-sandbox-cp) | Copy files: `cp NAME:/guest/path LOCAL` or `cp LOCAL NAME:/guest/path` (NAME may be a ref: `cp local:box:/tmp/x .`). | | [`cua sandbox overlay`](#cua-sandbox-overlay) | Inject freshly built binaries into a running sandbox (see `create --overlay`): NAME=PATH or NAME=PATH:GUEST_PATH. | | [`cua sandbox logs`](#cua-sandbox-logs) | Show logs: the backend console (QEMU serial, container) when there is one, else the guest system log through the spacesd. | | [`cua sandbox port-forward`](#cua-sandbox-port-forward) | Forward a guest port: `port-forward NAME PORT[:LOCAL]` (a local sandbox's port not published at create, and cloud sandboxes, tunnel through cua-spacesd when the image has it; cloud images without it get HTTP and WebSocket through a loopback proxy to the cloud gateway). | | [`cua sandbox url`](#cua-sandbox-url) | Print a URL for a named service: usable from this machine (default), or `--public` for a shareable URL that expires (`--ttl`, default 1h; cloud: a signed URL; local: a token URL served by the cua daemon). | | [`cua sandbox screenshot`](#cua-sandbox-screenshot) | Save a screenshot (PNG). | | [`cua sandbox mcp`](#cua-sandbox-mcp) | Talk to an MCP server a sandbox serves: `mcp NAME SERVICE tools`, `mcp NAME SERVICE call TOOL '{"a":1}'`. | | [`cua sandbox mcp config`](#cua-sandbox-mcp-config) | The endpoint URL and headers, for any MCP client. | | [`cua sandbox mcp info`](#cua-sandbox-mcp-info) | Server name, version and negotiated protocol revision. | | [`cua sandbox mcp tools`](#cua-sandbox-mcp-tools) | List tools. | | [`cua sandbox mcp call`](#cua-sandbox-mcp-call) | Call a tool with a JSON object of arguments. | | [`cua sandbox mcp resources`](#cua-sandbox-mcp-resources) | List resources. | | [`cua sandbox mcp templates`](#cua-sandbox-mcp-templates) | List resource templates. | | [`cua sandbox mcp read`](#cua-sandbox-mcp-read) | Read a resource. | | [`cua sandbox mcp prompts`](#cua-sandbox-mcp-prompts) | List prompts. | | [`cua sandbox mcp prompt`](#cua-sandbox-mcp-prompt) | Render a prompt with a JSON object of string arguments. | | [`cua sandbox mcp request`](#cua-sandbox-mcp-request) | Any other method (for example `skills/list`), printing the result. | | [`cua sandbox view`](#cua-sandbox-view) | Open the sandbox desktop in the browser (the cua-spacesd HTML5 viewer). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). ## `cua sandbox exec` Run a command (joined into one shell line, as `sh -c`). Exits with the command's own status. A local Lume sandbox without cua-spacesd runs it via SSH (`lume ssh`). ```text output cua sandbox exec [OPTIONS] <NAME> <COMMAND>... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | `<COMMAND>...` | string | required | Command and arguments. Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-exec" cua sb exec dev uname -a cua sb exec dev 'ls /tmp | wc -l' ``` ## `cua sandbox shell` Interactive shell (PTY), or run a command in a PTY. ```text output cua sandbox shell [OPTIONS] <NAME> [COMMAND]... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | `<COMMAND>...` | string | optional | Command to run instead of the login shell. Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--cols` | integer | | Terminal width (default: this terminal's). | | `--rows` | integer | | Terminal height (default: this terminal's). | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-shell" cua sb shell dev cua sb shell dev top ``` ## `cua sandbox cp` Copy files: `cp NAME:/guest/path LOCAL` or `cp LOCAL NAME:/guest/path` (NAME may be a ref: `cp local:box:/tmp/x .`). ```text output cua sandbox cp [OPTIONS] <SRC> <DST> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SRC>` | string | required | Source: a local path or `NAME:/guest/path`. | | `<DST>` | string | required | Destination: a local path or `NAME:/guest/path`. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-cp" # Upload cua sb cp ./notes.txt dev:/tmp/notes.txt # Download cua sb cp dev:/tmp/notes.txt . ``` ## `cua sandbox overlay` Inject freshly built binaries into a running sandbox (see `create --overlay`): NAME=PATH or NAME=PATH:GUEST_PATH. ```text output cua sandbox overlay [OPTIONS] <NAME> <OVERLAYS>... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | `<OVERLAYS>...` | string | required | NAME=PATH[:GUEST_PATH] (repeatable). Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--timeout` | integer | `180` | Budget for a cua-spacesd restart, in seconds. | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-overlay" # The cua-driver under test, then check that it is what runs cua sb overlay dev cua-driver=./target/release/cua-driver cua doctor dev --expect cua-driver=sha256:<sha256> # The daemon too (restarts it and waits for the new build) cua sb overlay dev cua-spacesd=./target/release/cua-spacesd ``` ## `cua sandbox logs` Show logs: the backend console (QEMU serial, container) when there is one, else the guest system log through the spacesd. ```text output cua sandbox logs [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--tail` | `-n` | integer | `200` | Lines from the end. | | `--source` | | string | `auto` | auto, console or guest. | | `--local` | | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-logs" cua sb logs dev cua sb logs dev -n 50 --source guest ``` ## `cua sandbox port-forward` Forward a guest port: `port-forward NAME PORT[:LOCAL]` (a local sandbox's port not published at create, and cloud sandboxes, tunnel through cua-spacesd when the image has it; cloud images without it get HTTP and WebSocket through a loopback proxy to the cloud gateway). ```text output cua sandbox port-forward [OPTIONS] <NAME> <SPEC> ``` Alias: `cua sandbox forward`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | `<SPEC>` | string | required | PORT or PORT:LOCAL_PORT. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | | `--no-wait` | boolean | `false` | Print the target and exit instead of forwarding until Ctrl-C. | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-port-forward" # Guest port 8080 on localhost:8080 until Ctrl-C cua sb port-forward dev 8080 # Guest port 5432 on localhost:15432 cua sb port-forward cloud:dev 5432:15432 ``` ## `cua sandbox url` Print a URL for a named service: usable from this machine (default), or `--public` for a shareable URL that expires (`--ttl`, default 1h; cloud: a signed URL; local: a token URL served by the cua daemon). ```text output cua sandbox url [OPTIONS] <NAME> <SERVICE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | `<SERVICE>` | string | required | Service name (from `--service` at create time). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--ttl` | integer | | Lifetime of a --public URL (`10m`, `1h`, `3600`; 60 s to 24 h). | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | | `--public` | boolean | `false` | A shareable URL that expires. | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-url" cua sb url dev web # A shareable URL for 10 minutes cua sb url cloud:dev web --public --ttl 10m ``` ## `cua sandbox screenshot` Save a screenshot (PNG). A local Lume sandbox without cua-spacesd is captured via VNC. ```text output cua sandbox screenshot [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--output` | `-o` | path | `screenshot.png` | Output file (PNG). | | `--local` | | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-screenshot" cua sb screenshot dev -o desktop.png ``` ## `cua sandbox mcp` Talk to an MCP server a sandbox serves: `mcp NAME SERVICE tools`, `mcp NAME SERVICE call TOOL '{"a":1}'`. Generic streamable HTTP over the service (local or cloud); no cua-spacesd needed. ```text output cua sandbox mcp [OPTIONS] <NAME> <SERVICE> <COMMAND> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | `<SERVICE>` | string | required | Service name (for example `mcp`, or `port-8765`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--path` | string | `/mcp` | Endpoint path on the service. | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp" cua sb mcp dev mcp tools cua sb mcp dev mcp call echo '{"text":"hi"}' ``` ### `cua sandbox mcp config` The endpoint URL and headers, for any MCP client. Credential headers print masked (`Bearer ****`) unless `--show-secrets`; Fleet bearers are short-lived. ```text output cua sandbox mcp <NAME> <SERVICE> config [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--show-secrets` | boolean | `false` | Print credential header values in full. | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-config" cua sb mcp dev mcp config ``` ### `cua sandbox mcp info` Server name, version and negotiated protocol revision. ```text output cua sandbox mcp <NAME> <SERVICE> info [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-info" cua sb mcp dev mcp info ``` ### `cua sandbox mcp tools` List tools. ```text output cua sandbox mcp <NAME> <SERVICE> tools [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-tools" cua sb mcp dev mcp tools ``` ### `cua sandbox mcp call` Call a tool with a JSON object of arguments. ```text output cua sandbox mcp <NAME> <SERVICE> call [OPTIONS] <TOOL> [ARGUMENTS] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<TOOL>` | string | required | Tool name. | | `<ARGUMENTS>` | string | optional | Arguments (JSON object). Default `{}`. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--out` | path | | Save image, audio and blob content here instead of summarizing. | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-call" cua sb mcp dev mcp call echo '{"text":"hi"}' cua sb mcp dev mcp call screenshot --out ./shots ``` ### `cua sandbox mcp resources` List resources. ```text output cua sandbox mcp <NAME> <SERVICE> resources [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-resources" cua sb mcp dev mcp resources ``` ### `cua sandbox mcp templates` List resource templates. ```text output cua sandbox mcp <NAME> <SERVICE> templates [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-templates" cua sb mcp dev mcp templates ``` ### `cua sandbox mcp read` Read a resource. ```text output cua sandbox mcp <NAME> <SERVICE> read [OPTIONS] <URI> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<URI>` | string | required | Resource URI. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--out` | path | | Save blob contents here instead of summarizing. | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-read" cua sb mcp dev mcp read file:///tmp/report.txt ``` ### `cua sandbox mcp prompts` List prompts. ```text output cua sandbox mcp <NAME> <SERVICE> prompts [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-prompts" cua sb mcp dev mcp prompts ``` ### `cua sandbox mcp prompt` Render a prompt with a JSON object of string arguments. ```text output cua sandbox mcp <NAME> <SERVICE> prompt [OPTIONS] <NAME> [ARGUMENTS] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Prompt name. | | `<ARGUMENTS>` | string | optional | Arguments (JSON object of strings). Default `{}`. | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-prompt" cua sb mcp dev mcp prompt summarize '{"topic":"logs"}' ``` ### `cua sandbox mcp request` Any other method (for example `skills/list`), printing the result. ```text output cua sandbox mcp <NAME> <SERVICE> request [OPTIONS] <METHOD> [PARAMS] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<METHOD>` | string | required | JSON-RPC method. | | `<PARAMS>` | string | optional | Params (JSON object). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-mcp-request" cua sb mcp dev mcp request skills/list ``` ## `cua sandbox view` Open the sandbox desktop in the browser (the cua-spacesd HTML5 viewer). A local Lume sandbox without cua-spacesd opens its VNC display with `lume attach`. ```text output cua sandbox view [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Sandbox name or ref (`local:NAME`, `cloud:NAME`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--service` | string | | Open this web service of the sandbox instead of the viewer (for images without cua-spacesd that serve their own display page). | | `--files` | string | | Guest folder for uploads and folder sharing (default `~`; `none` turns files off). | | `--ttl` | string | | Link lifetime (for example `1h`, `30m`). Default 1h. | | `--view-only` | boolean | `false` | Watch only: no input, clipboard, files or microphone. | | `--no-open` | boolean | `false` | Print the link without opening a browser. | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-sandbox-view" cua sb view dev # Watch only, print the link instead of opening it cua sb view dev --view-only --no-open # An image that ships its own web display instead of cua-spacesd cua sb view old-box --service web ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua config User defaults: where sandboxes run (`default.on`), the default kind and runtime, cloud defaults. | Command | Description | | --- | --- | | [`cua config list`](#cua-config-list) | Every setting: its effective value and where it comes from (env, config or default). | | [`cua config get`](#cua-config-get) | One setting's effective value. | | [`cua config set`](#cua-config-set) | Write a setting to the config file. | | [`cua config unset`](#cua-config-unset) | Remove a setting from the config file (the environment variable or the built-in default applies again). | | [`cua config path`](#cua-config-path) | The config file's path. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). User defaults (~/.cua/config.toml): where sandboxes run when --on is not given (default.on), the default kind and runtime, cloud defaults. ```text output cua config [OPTIONS] <COMMAND> ``` ## `cua config list` Every setting: its effective value and where it comes from (env, config or default). ```text output cua config list [OPTIONS] ``` Alias: `cua config ls`. **Examples** ```bash test="cli-shape" id="ref-cua-config-list" cua config list cua config list --json ``` ## `cua config get` One setting's effective value. ```text output cua config get [OPTIONS] <KEY> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<KEY>` | string | required | Setting (`default.on`, `default.kind`, `default.runtime`, `cloud.warm`, `cloud.max_pool_size`, `cloud.claim_ttl`). | **Examples** ```bash test="cli-shape" id="ref-cua-config-get" cua config get default.on ``` ## `cua config set` Write a setting to the config file. ```text output cua config set [OPTIONS] <KEY> <VALUE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<KEY>` | string | required | Setting. | | `<VALUE>` | string | required | Value (checked: `default.on` takes local, cloud or a provider). | **Examples** ```bash test="cli-shape" id="ref-cua-config-set" # Create sandboxes in the cloud unless --on says otherwise cua config set default.on cloud # Back to this machine cua config set default.on local # Prefer VMs when the image offers both cua config set default.kind vm ``` ## `cua config unset` Remove a setting from the config file (the environment variable or the built-in default applies again). ```text output cua config unset [OPTIONS] <KEY> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<KEY>` | string | required | Setting. | **Examples** ```bash test="cli-shape" id="ref-cua-config-unset" cua config unset default.on ``` ## `cua config path` The config file's path. ```text output cua config path [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-config-path" cua config path ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua viewer One local browser page that lists every sandbox and opens its viewer. Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Serve the HTML5 viewer for every sandbox on one loopback port. ```text output cua viewer [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--listen` | string | `127.0.0.1:0` | Address to listen on (loopback only). | | `--no-open` | boolean | `false` | Print the URL without opening a browser. | **Examples** ```bash test="cli-shape" id="ref-cua-viewer" # List your sandboxes in the browser and open any of them cua viewer # Fixed port, print the URL only cua viewer --listen 127.0.0.1:8211 --no-open ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua do One-shot computer actions (screenshot, click, type, windows) against a selected target. | Command | Description | | --- | --- | | [`cua do`](#cua-do) | One-shot computer actions against the selected target. | | [`cua do switch`](#cua-do-switch) | Select the target: a sandbox name or ref, a relay Space (`relay:<id>`), `host`, `url <URL>`, or a legacy `<provider> <name>` pair. | | [`cua do status`](#cua-do-status) | Show the current target and zoom state. | | [`cua do ls`](#cua-do-ls) | List targets (optionally of one provider). | | [`cua do zoom`](#cua-do-zoom) | Crop screenshots to a window and map coordinates into it. | | [`cua do unzoom`](#cua-do-unzoom) | Return to full-screen screenshots. | | [`cua do screenshot`](#cua-do-screenshot) | Take a screenshot (saved to ~/.cua/screenshots, newest 20 kept, unless --save). | | [`cua do snapshot`](#cua-do-snapshot) | Screenshot plus an AI summary of the screen and its interactive elements (needs ANTHROPIC_API_KEY). | | [`cua do click`](#cua-do-click) | Click at image coordinates. | | [`cua do dclick`](#cua-do-dclick) | Double-click at image coordinates. | | [`cua do move`](#cua-do-move) | Move the cursor (image coordinates). | | [`cua do type`](#cua-do-type) | Type text. | | [`cua do key`](#cua-do-key) | Press a key (enter, escape, tab, ...). | | [`cua do hotkey`](#cua-do-hotkey) | Keyboard shortcut (cmd+c, ctrl+shift+s). | | [`cua do scroll`](#cua-do-scroll) | Scroll in a direction. | | [`cua do drag`](#cua-do-drag) | Drag between two points (image coordinates). | | [`cua do shell`](#cua-do-shell) | Run a shell command, or open an interactive terminal (PTY) when no command is given on a terminal. | | [`cua do open`](#cua-do-open) | Open a file or URL. | | [`cua do launch`](#cua-do-launch) | Launch an application. | | [`cua do window`](#cua-do-window) | Window management. | | [`cua do window ls`](#cua-do-window-ls) | List windows (optionally filtered by app or title). | | [`cua do window unfocus`](#cua-do-window-unfocus) | Remove focus from the current window (presses Escape). | | [`cua do window focus`](#cua-do-window-focus) | Focus a window. | | [`cua do window minimize`](#cua-do-window-minimize) | Minimize a window. | | [`cua do window maximize`](#cua-do-window-maximize) | Maximize a window. | | [`cua do window restore`](#cua-do-window-restore) | Restore a minimized or maximized window. | | [`cua do window close`](#cua-do-window-close) | Close a window. | | [`cua do window resize`](#cua-do-window-resize) | Resize a window. | | [`cua do window move`](#cua-do-window-move) | Move a window. | | [`cua do window info`](#cua-do-window-info) | Show a window as JSON. | | [`cua do a11y`](#cua-do-a11y) | Accessibility tree and actions. | | [`cua do a11y tree`](#cua-do-a11y-tree) | Print the accessibility tree (JSON). | | [`cua do a11y find`](#cua-do-a11y-find) | Find elements by name (and role). | | [`cua do a11y act`](#cua-do-a11y-act) | Act on an element: press, focus, set-value, increment, ... | | [`cua do cursor`](#cua-do-cursor) | Print the cursor position (screen points). | | [`cua do clipboard`](#cua-do-clipboard) | Clipboard text. | | [`cua do clipboard get`](#cua-do-clipboard-get) | Print clipboard text. | | [`cua do clipboard set`](#cua-do-clipboard-set) | Set clipboard text. | | [`cua do-host-consent`](#cua-do-host-consent) | Grant consent for `cua do switch host`. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). ## `cua do` One-shot computer actions against the selected target. ```text output cua do [OPTIONS] <COMMAND> ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--no-record` | boolean | `false` | Disable trajectory recording for this command. | **Examples** ```bash test="cli-shape" id="ref-cua-do" # Act on a sandbox, then take a screenshot cua do switch dev cua do screenshot # A relay Space (`cua spaces ls`) cua do switch relay:0123abcd4567ef89 ``` ### `cua do switch` Select the target: a sandbox name or ref, a relay Space (`relay:<id>`), `host`, `url <URL>`, or a legacy `<provider> <name>` pair. ```text output cua do switch [OPTIONS] <TARGET> [NAME] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<TARGET>` | string | required | Sandbox name or ref, `relay:<id>`, `host`, `url`, or a legacy provider word. | | `<NAME>` | string | optional | The URL after `url`, or the sandbox name after a provider word. | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token (`url` and `host`). | | `--as` | string | | | Sandbox name to register a `url` target under. | **Examples** ```bash test="cli-shape" id="ref-cua-do-switch" cua do switch dev cua do switch cloud:dev cua do switch relay:0123abcd4567ef89 # This machine (after `cua do-host-consent`) cua do switch host ``` ### `cua do status` Show the current target and zoom state. ```text output cua do status [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-do-status" cua do status ``` ### `cua do ls` List targets (optionally of one provider). ```text output cua do ls [OPTIONS] [PROVIDER] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PROVIDER>` | string | optional | Only targets of this provider. | **Examples** ```bash test="cli-shape" id="ref-cua-do-ls" cua do ls ``` ### `cua do zoom` Crop screenshots to a window and map coordinates into it. ```text output cua do zoom [OPTIONS] <WINDOW_NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_NAME>` | string | required | Window title or app name. | **Examples** ```bash test="cli-shape" id="ref-cua-do-zoom" cua do zoom Firefox ``` ### `cua do unzoom` Return to full-screen screenshots. ```text output cua do unzoom [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-do-unzoom" cua do unzoom ``` ### `cua do screenshot` Take a screenshot (saved to ~/.cua/screenshots, newest 20 kept, unless --save). ```text output cua do screenshot [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--save` | `-s` | path | | Save the PNG here. | **Examples** ```bash test="cli-shape" id="ref-cua-do-screenshot" cua do screenshot cua do screenshot --save desktop.png ``` ### `cua do snapshot` Screenshot plus an AI summary of the screen and its interactive elements (needs ANTHROPIC_API_KEY). ```text output cua do snapshot [OPTIONS] [INSTRUCTIONS]... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<INSTRUCTIONS>...` | string | optional | What to focus the summary on. Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--model` | string | | Model (default `claude-haiku-4-5`, or CUA_SNAPSHOT_MODEL). | **Examples** ```bash test="cli-shape" id="ref-cua-do-snapshot" cua do snapshot cua do snapshot find the search box ``` ### `cua do click` Click at image coordinates. ```text output cua do click [OPTIONS] <X> <Y> [BUTTON] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<X>` | integer | required | X in screenshot pixels. | | `<Y>` | integer | required | Y in screenshot pixels. | | `<BUTTON>` | `left` \| `right` \| `middle` | `left` | Mouse button. | **Examples** ```bash test="cli-shape" id="ref-cua-do-click" cua do click 640 360 cua do click 640 360 right ``` ### `cua do dclick` Double-click at image coordinates. ```text output cua do dclick [OPTIONS] <X> <Y> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<X>` | integer | required | X in screenshot pixels. | | `<Y>` | integer | required | Y in screenshot pixels. | **Examples** ```bash test="cli-shape" id="ref-cua-do-dclick" cua do dclick 640 360 ``` ### `cua do move` Move the cursor (image coordinates). ```text output cua do move [OPTIONS] <X> <Y> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<X>` | integer | required | X in screenshot pixels. | | `<Y>` | integer | required | Y in screenshot pixels. | **Examples** ```bash test="cli-shape" id="ref-cua-do-move" cua do move 640 360 ``` ### `cua do type` Type text. ```text output cua do type [OPTIONS] <TEXT> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<TEXT>` | string | required | Text to type. | **Examples** ```bash test="cli-shape" id="ref-cua-do-type" cua do type 'hello world' ``` ### `cua do key` Press a key (enter, escape, tab, ...). ```text output cua do key [OPTIONS] <KEY> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<KEY>` | string | required | Key name. | **Examples** ```bash test="cli-shape" id="ref-cua-do-key" cua do key enter ``` ### `cua do hotkey` Keyboard shortcut (cmd+c, ctrl+shift+s). ```text output cua do hotkey [OPTIONS] <KEYS> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<KEYS>` | string | required | Keys joined with `+`. | **Examples** ```bash test="cli-shape" id="ref-cua-do-hotkey" cua do hotkey ctrl+shift+s ``` ### `cua do scroll` Scroll in a direction. ```text output cua do scroll [OPTIONS] <DIRECTION> [AMOUNT] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<DIRECTION>` | `up` \| `down` \| `left` \| `right` | required | Direction. | | `<AMOUNT>` | integer | `3` | Scroll steps. | **Examples** ```bash test="cli-shape" id="ref-cua-do-scroll" cua do scroll down cua do scroll up 10 ``` ### `cua do drag` Drag between two points (image coordinates). ```text output cua do drag [OPTIONS] <X1> <Y1> <X2> <Y2> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<X1>` | integer | required | Start X in screenshot pixels. | | `<Y1>` | integer | required | Start Y in screenshot pixels. | | `<X2>` | integer | required | End X in screenshot pixels. | | `<Y2>` | integer | required | End Y in screenshot pixels. | **Examples** ```bash test="cli-shape" id="ref-cua-do-drag" cua do drag 100 200 400 200 ``` ### `cua do shell` Run a shell command, or open an interactive terminal (PTY) when no command is given on a terminal. ```text output cua do shell [OPTIONS] [COMMAND]... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<COMMAND>...` | string | optional | Command and arguments. Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--cols` | integer | | Terminal width (default: this terminal's). | | `--rows` | integer | | Terminal height (default: this terminal's). | **Examples** ```bash test="cli-shape" id="ref-cua-do-shell" cua do shell ls -la /tmp # An interactive terminal cua do shell ``` ### `cua do open` Open a file or URL. ```text output cua do open [OPTIONS] <PATH> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PATH>` | string | required | File path or URL in the target. | **Examples** ```bash test="cli-shape" id="ref-cua-do-open" cua do open https://example.com ``` ### `cua do launch` Launch an application. ```text output cua do launch [OPTIONS] <APP> [ARGS]... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<APP>` | string | required | Application name or path. | | `<ARGS>...` | string | optional | Arguments passed to the application. Repeatable. | **Examples** ```bash test="cli-shape" id="ref-cua-do-launch" cua do launch firefox cua do launch firefox --private-window ``` ### `cua do window` Window management. ```text output cua do window [OPTIONS] <COMMAND> ``` #### `cua do window ls` List windows (optionally filtered by app or title). ```text output cua do window ls [OPTIONS] [APP] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<APP>` | string | optional | Only windows whose app or title contains this. | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-ls" cua do window ls cua do window ls firefox ``` #### `cua do window unfocus` Remove focus from the current window (presses Escape). ```text output cua do window unfocus [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-do-window-unfocus" cua do window unfocus ``` #### `cua do window focus` Focus a window. ```text output cua do window focus [OPTIONS] <WINDOW_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_ID>` | string | required | Window id (from `cua do window ls`). | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-focus" cua do window focus <window-id> ``` #### `cua do window minimize` Minimize a window. ```text output cua do window minimize [OPTIONS] <WINDOW_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_ID>` | string | required | Window id (from `cua do window ls`). | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-minimize" cua do window minimize <window-id> ``` #### `cua do window maximize` Maximize a window. ```text output cua do window maximize [OPTIONS] <WINDOW_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_ID>` | string | required | Window id (from `cua do window ls`). | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-maximize" cua do window maximize <window-id> ``` #### `cua do window restore` Restore a minimized or maximized window. ```text output cua do window restore [OPTIONS] <WINDOW_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_ID>` | string | required | Window id (from `cua do window ls`). | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-restore" cua do window restore <window-id> ``` #### `cua do window close` Close a window. ```text output cua do window close [OPTIONS] <WINDOW_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_ID>` | string | required | Window id (from `cua do window ls`). | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-close" cua do window close <window-id> ``` #### `cua do window resize` Resize a window. ```text output cua do window resize [OPTIONS] <WINDOW_ID> <WIDTH> <HEIGHT> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_ID>` | string | required | Window id (from `cua do window ls`). | | `<WIDTH>` | integer | required | Width in screen points. | | `<HEIGHT>` | integer | required | Height in screen points. | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-resize" cua do window resize <window-id> 1280 800 ``` #### `cua do window move` Move a window. ```text output cua do window move [OPTIONS] <WINDOW_ID> <X> <Y> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_ID>` | string | required | Window id (from `cua do window ls`). | | `<X>` | integer | required | Left edge in screen points. | | `<Y>` | integer | required | Top edge in screen points. | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-move" cua do window move <window-id> 0 0 ``` #### `cua do window info` Show a window as JSON. ```text output cua do window info [OPTIONS] <WINDOW_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WINDOW_ID>` | string | required | Window id (from `cua do window ls`). | **Examples** ```bash test="cli-shape" id="ref-cua-do-window-info" cua do window info <window-id> ``` ### `cua do a11y` Accessibility tree and actions. ```text output cua do a11y [OPTIONS] <COMMAND> ``` #### `cua do a11y tree` Print the accessibility tree (JSON). ```text output cua do a11y tree [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--window` | string | | Only this window (default: the focused one). | | `--depth` | integer | `0` | Maximum depth (0: unlimited). | **Examples** ```bash test="cli-shape" id="ref-cua-do-a11y-tree" cua do a11y tree cua do a11y tree --window <window-id> --depth 4 ``` #### `cua do a11y find` Find elements by name (and role). ```text output cua do a11y find [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Element name (substring). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--role` | string | | Only elements with this role. | | `--window` | string | | Only this window (default: the focused one). | **Examples** ```bash test="cli-shape" id="ref-cua-do-a11y-find" cua do a11y find Save --role button ``` #### `cua do a11y act` Act on an element: press, focus, set-value, increment, ... ```text output cua do a11y act [OPTIONS] <ELEMENT> [ACTION] [VALUE] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ELEMENT>` | string | required | Element id from `tree` or `find`. | | `<ACTION>` | string | `press` | Action. | | `<VALUE>` | string | optional | Value for `set-value`. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--snapshot` | string | | Snapshot id from `tree`/`find` (default: the latest). | **Examples** ```bash test="cli-shape" id="ref-cua-do-a11y-act" cua do a11y act <element-id> cua do a11y act <element-id> set-value 'hello' ``` ### `cua do cursor` Print the cursor position (screen points). ```text output cua do cursor [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-do-cursor" cua do cursor ``` ### `cua do clipboard` Clipboard text. ```text output cua do clipboard [OPTIONS] <COMMAND> ``` #### `cua do clipboard get` Print clipboard text. ```text output cua do clipboard get [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-do-clipboard-get" cua do clipboard get ``` #### `cua do clipboard set` Set clipboard text. ```text output cua do clipboard set [OPTIONS] <TEXT> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<TEXT>` | string | required | Text to put on the clipboard. | **Examples** ```bash test="cli-shape" id="ref-cua-do-clipboard-set" cua do clipboard set 'hello' ``` ## `cua do-host-consent` Grant consent for `cua do switch host`. ```text output cua do-host-consent [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-do-host-consent" # Allow `cua do` to act on this machine's desktop cua do-host-consent ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua image Pull, build and push OCI images, and manage Fleet image resources; list the image catalog. | Command | Description | | --- | --- | | [`cua image`](#cua-image) | Images: local OCI pull/build/push and Fleet image resources. | | [`cua image pull`](#cua-image-pull) | Pull an image into the local cache. | | [`cua image build`](#cua-image-build) | Build an image from an images.cua.ai/v1alpha1 Image resource (JSON). | | [`cua image push`](#cua-image-push) | Copy an image (host architecture) to another registry reference. | | [`cua image ls`](#cua-image-ls) | List Fleet image resources. | | [`cua image info`](#cua-image-info) | Show a Fleet image resource. | | [`cua image rm`](#cua-image-rm) | Delete a Fleet image resource. | | [`cua image create`](#cua-image-create) | Submit an Image manifest (JSON) to Fleet for a remote build. | | [`cua images`](#cua-images) | The sandbox image catalog (the images the docs list): which exist, their variants, and which ship cua-spacesd and a browser. | | [`cua images ls`](#cua-images-ls) | List the catalog: ref, OS, container or VM, local and cloud runtime, cua-spacesd, browsers, summary. | | [`cua images info`](#cua-images-info) | Show one catalog entry (a ref or an alias such as `linux`). | | [`cua images build`](#cua-images-build) | Build a sandbox image definition (a libs/images directory): the rootfs, the bootable VM disk and the KubeVirt containerDisk. | | [`cua images pack`](#cua-images-pack) | Pack the disks `cua images build --outputs disk` wrote as KubeVirt containerDisks `<repo>:<tag>-<arch>` (push, or load into docker). | | [`cua images publish`](#cua-images-publish) | Publish a built image as immutable pins `<series>-<stamp>` and `<series>-disk-<stamp>`, refusing any that exist. | | [`cua images release`](#cua-images-release) | Run an image's release pipeline (its release.json): build, doctor lanes, content digest, save; with --publish push, publish pins and verify; with --promote move the floating tags. | | [`cua images promote`](#cua-images-promote) | Move a series' moving tags (`edge`, `edge-disk`) to the pins a `cua images publish` record names, after the gates passed. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). ## `cua image` Images: local OCI pull/build/push and Fleet image resources. ```text output cua image [OPTIONS] <COMMAND> ``` Alias: `cua img`. ### `cua image pull` Pull an image into the local cache. ```text output cua image pull [OPTIONS] <REFERENCE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<REFERENCE>` | string | required | Image reference or alias. | **Examples** ```bash test="cli-shape" id="ref-cua-image-pull" cua image pull linux cua image pull ghcr.io/trycua/linux:24.04 ``` ### `cua image build` Build an image from an images.cua.ai/v1alpha1 Image resource (JSON). ```text output cua image build [OPTIONS] --base <BASE> <SPEC> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPEC>` | path | required | Image resource file (JSON). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--base` | string | required | Base image (`container:<ref>`, `vm:<ref>`, `disk:<path>`). | | `--push` | string | | Push the result to this reference. | **Examples** ```bash test="cli-shape" id="ref-cua-image-build" cua image build image.json --base container:ubuntu:24.04 cua image build image.json --base container:ubuntu:24.04 --push ghcr.io/acme/desktop:1 ``` ### `cua image push` Copy an image (host architecture) to another registry reference. ```text output cua image push [OPTIONS] <REFERENCE> <DESTINATION> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<REFERENCE>` | string | required | Source image reference or alias. | | `<DESTINATION>` | string | required | Destination registry reference. | **Examples** ```bash test="cli-shape" id="ref-cua-image-push" cua image push linux ghcr.io/acme/desktop:1 ``` ### `cua image ls` List Fleet image resources. ```text output cua image ls [OPTIONS] ``` Alias: `cua image list`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--namespace` | string | | Namespace (default: every namespace, or CUA_FLEET_NAMESPACE). | **Examples** ```bash test="cli-shape" id="ref-cua-image-ls" cua image ls cua image ls --namespace acme ``` ### `cua image info` Show a Fleet image resource. ```text output cua image info [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Image resource name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--namespace` | string | | Namespace (default: CUA_FLEET_NAMESPACE). | **Examples** ```bash test="cli-shape" id="ref-cua-image-info" cua image info desktop ``` ### `cua image rm` Delete a Fleet image resource. ```text output cua image rm [OPTIONS] <NAME> ``` Alias: `cua image delete`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Image resource name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--namespace` | string | | Namespace (default: CUA_FLEET_NAMESPACE). | | `--force` | boolean | `false` | Do not ask for confirmation. | **Examples** ```bash test="cli-shape" id="ref-cua-image-rm" cua image rm desktop ``` ### `cua image create` Submit an Image manifest (JSON) to Fleet for a remote build. ```text output cua image create [OPTIONS] --file <FILE> ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--file` | `-f` | path | required | Image manifest file (JSON). | | `--namespace` | | string | | Namespace (default: CUA_FLEET_NAMESPACE). | **Examples** ```bash test="cli-shape" id="ref-cua-image-create" cua image create -f image.json ``` ## `cua images` The sandbox image catalog (the images the docs list): which exist, their variants, and which ship cua-spacesd and a browser. ```text output cua images [OPTIONS] <COMMAND> ``` ### `cua images ls` List the catalog: ref, OS, container or VM, local and cloud runtime, cua-spacesd, browsers, summary. ```text output cua images ls [OPTIONS] ``` Alias: `cua images list`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--os` | string | | Only this OS: linux, windows or macos. | | `--all` | boolean | `false` | Include unpublished entries (benchmark images). | | `--browser` | boolean | `false` | Only images with a browser the cua-driver browser tools drive. | **Examples** ```bash test="cli-shape" id="ref-cua-images-ls" cua images ls # Images whose browser the cua-driver browser tools drive cua images ls --browser cua images ls --all --json ``` ### `cua images info` Show one catalog entry (a ref or an alias such as `linux`). ```text output cua images info [OPTIONS] <REFERENCE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<REFERENCE>` | string | required | Image ref or alias. | **Examples** ```bash test="cli-shape" id="ref-cua-images-info" cua images info linux ``` ### `cua images build` Build a sandbox image definition (a libs/images directory): the rootfs, the bootable VM disk and the KubeVirt containerDisk. Outputs: rootfs <repo>:docker-<tag>-<arch>; disk <out>/<name>/<arch>/disk.img; containerdisk <repo>:<tag>-<arch>. --push pushes the outputs the image.json lists (a VM-only image never pushes its rootfs). ```text output cua images build [OPTIONS] <DIR> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<DIR>` | path | required | Image definition directory (Dockerfile, image.json). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--platform` | string | | Platforms, comma separated (default: the host's, or the image's only one). | | `--tag` | string | `local` | Tag for the per-arch outputs. | | `--repo` | string | | Repository (default: image.json `repository`, else cua-e2e-local/<name>). | | `--outputs` | string | `rootfs` | Outputs: rootfs, disk, containerdisk (comma separated). | | `--build-arg` | string | | Docker build argument KEY=VALUE (repeatable). | | `--build-context` | string | | Named build context NAME=PATH (repeatable). | | `--label` | string | | Extra rootfs label KEY=VALUE (repeatable). | | `--out` | path | | Where disks go (default: $CUA_IMAGES_OUT or ~/.cache/cua-images). | | `--disk-size` | string | `20G` | Virtual size of the disk. | | `--target` | string | | Dockerfile stage to build (a tier: `slim`, `full`); disks go to <out>/<name>-<target>/<arch>. Default: the Dockerfile's last stage. | | `--push` | boolean | `false` | Push the published outputs. | | `--dry-run` | boolean | `false` | Print the steps without running them. | **Examples** ```bash test="cli-shape" id="ref-cua-images-build" cua images build libs/images/omarchy --outputs rootfs,disk cua images build libs/images/omarchy --tag build-1a2b3c4d --outputs containerdisk --push cua images build libs/images/linux --target slim --tag 24.04-slim-local --outputs rootfs,disk cua images build libs/images/linux --platform linux/amd64,linux/arm64 --dry-run ``` ### `cua images pack` Pack the disks `cua images build --outputs disk` wrote as KubeVirt containerDisks `<repo>:<tag>-<arch>` (push, or load into docker). Packs exactly the disk the doctor checked, so the pushed bytes are the tested ones. ```text output cua images pack [OPTIONS] <DIR> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<DIR>` | path | required | Image definition directory. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--platform` | string | | Platforms, comma separated (default: the host's, or the image's only one). | | `--tag` | string | `local` | Tag for the per-arch containerDisks. | | `--repo` | string | | Repository (default: image.json `repository`, else cua-e2e-local/<name>). | | `--out` | path | | Where the disks are (default: $CUA_IMAGES_OUT or ~/.cache/cua-images). | | `--target` | string | | The `--target` the disks were built with (their <name>-<target> dir). | | `--push` | boolean | `false` | Push instead of loading into docker. | | `--dry-run` | boolean | `false` | Print what would be packed. | **Examples** ```bash test="cli-shape" id="ref-cua-images-pack" cua images pack libs/images/omarchy --tag ci-1a2b3c4d --push --json ``` ### `cua images publish` Publish a built image as immutable pins `<series>-<stamp>` and `<series>-disk-<stamp>`, refusing any that exist. Moves nothing. Reads the per-arch images `cua images build --tag TAG --push` wrote (<repo>:<tag>-<arch>, and <repo>:docker-<tag>-<arch> when the image has a rootfs output). Writes the record `cua images promote` takes. ```text output cua images publish [OPTIONS] --tag <TAG> --series <SERIES> <DIR> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<DIR>` | path | required | Image definition directory. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--tag` | string | required | The build tag the per-arch images were pushed under. | | `--series` | string | required | Series / channel (`edge`, `24.04`). | | `--stamp` | string | | Pin stamp `<yyyymmdd>-<sha7>` (default: today and git HEAD). | | `--repo` | string | | Repository (default: image.json `repository`). | | `--platform` | string | | Platforms (default: image.json `platforms`). | | `--annotation` | string | | Extra index annotation KEY=VALUE (repeatable). | | `--descriptor-annotation` | string | | Annotation on one child's descriptor, VARIANT/ARCH:KEY=VALUE (`rootfs/amd64:ai.cua.doctor.status=pass`; repeatable). | | `--record` | path | | Write the publish record (JSON) here. | | `--dry-run` | boolean | `false` | Resolve and print, push nothing. | **Examples** ```bash test="cli-shape" id="ref-cua-images-publish" cua images publish libs/images/omarchy --tag build-1a2b3c4d --series edge --record pins.json cua images publish libs/images/linux --tag build-20260925-1a2b3c4 --series 24.04 --descriptor-annotation rootfs/amd64:ai.cua.doctor.status=pass cua images publish libs/images/omarchy --tag build-1a2b3c4d --series edge --dry-run ``` ### `cua images release` Run an image's release pipeline (its release.json): build, doctor lanes, content digest, save; with --publish push, publish pins and verify; with --promote move the floating tags. The same command runs locally and in CI. Phases: prepare, build, gate (doctor lanes), stage, push, publish, verify, promote. Push refuses unless every required gate passed (this run, or a state-*.json merged from other jobs in <work>/evidence). Evidence, logs and the summary go to <work>/evidence (a plain directory CI uploads as-is). ```text output cua images release [OPTIONS] <DIR> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<DIR>` | path | required | Image directory with a release.json. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--tier` | string | | Tier (`slim`, `full`) for images that have tiers. | | `--arch` | string | | Arches, comma separated (default: release.json `arches`). Repeatable. | | `--stamp` | string | | Pin stamp `<yyyymmdd>-<sha7>` (default: today and git HEAD). | | `--work` | path | | Work directory (default: $CUA_RELEASE_WORK, else under the cua build cache, which `cua cache prune` manages). | | `--steps` | string | | Only these steps: phase names or step-id prefixes (comma separated). Repeatable. | | `--from` | string | | Rerun from this phase or step; the steps before it must have passed. | | `--var` | string | | Variable KEY=VALUE for release.json templates (repeatable). | | `--assume` | string | | Treat a host capability as present (`kvm`) or absent (`!runsc`). Repeatable. | | `--scope` | string | | This run's state file name, state-<scope>.json (default: the arches); CI jobs sharing an evidence directory use distinct scopes. | | `--resume` | boolean | `false` | Skip steps that passed with the same inputs and whose outputs exist. | | `--dry-run` | boolean | `false` | Print the plan; run nothing. | | `--publish` | boolean | `false` | Also push, publish immutable pins and verify them. | | `--promote` | boolean | `false` | Also move the floating tags to the pins. | **Examples** ```bash test="cli-shape" id="ref-cua-images-release" # Plan only cua images release libs/images/linux --tier slim --dry-run # Build and doctor amd64 locally; resume after a failure cua images release libs/images/linux --tier slim --arch amd64 --resume # Rerun from the doctor lanes (the build must have passed) cua images release libs/images/linux --tier slim --from gate # One CI job's slice cua images release libs/images/linux --tier full --arch arm64 --steps build,gate,stage --work release # Publish and promote (the gates must have passed) cua images release libs/images/omarchy --publish --promote --resume ``` ### `cua images promote` Move a series' moving tags (`edge`, `edge-disk`) to the pins a `cua images publish` record names, after the gates passed. ```text output cua images promote [OPTIONS] <RECORD> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<RECORD>` | path | required | Record written by `cua images publish --record`. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--dry-run` | boolean | `false` | Check and print, move nothing. | **Examples** ```bash test="cli-shape" id="ref-cua-images-promote" cua images promote pins.json cua images promote pins.json --dry-run ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua fleet Managed Fleet pools behind `cua sandbox create`. | Command | Description | | --- | --- | | [`cua fleet pools`](#cua-fleet-pools) | Managed pools (`cua-auto-*`): reused per image and shape, autoscaled from zero, garbage-collected when idle. | | [`cua fleet pools ls`](#cua-fleet-pools-ls) | List this account's managed pools. | | [`cua fleet pools gc`](#cua-fleet-pools-gc) | Delete managed pools idle for `--idle` and stuck claims. | | [`cua fleet pool`](#cua-fleet-pool) | One pool (any pool you own, not only managed ones). | | [`cua fleet pool export`](#cua-fleet-pool-export) | Print a pool's spec: the shared sandbox model as JSON, or with `--terraform` the equivalent `fleets_pool` resource block. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Fleet: managed pools behind `cua sandbox create` without `--pool`. ```text output cua fleet [OPTIONS] <COMMAND> ``` ## `cua fleet pools` Managed pools (`cua-auto-*`): reused per image and shape, autoscaled from zero, garbage-collected when idle. ```text output cua fleet pools [OPTIONS] <COMMAND> ``` ### `cua fleet pools ls` List this account's managed pools. ```text output cua fleet pools ls [OPTIONS] ``` Alias: `cua fleet pools list`. **Examples** ```bash test="cli-shape" id="ref-cua-fleet-pools-ls" cua fleet pools ls ``` ### `cua fleet pools gc` Delete managed pools idle for `--idle` and stuck claims. ```text output cua fleet pools gc [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--idle` | string | `30m` | Idle threshold (`30m`, `2h`, `1d`). | | `--pool` | string | | Only these pools (repeatable). Default: every managed pool. | **Examples** ```bash test="cli-shape" id="ref-cua-fleet-pools-gc" cua fleet pools gc cua fleet pools gc --idle 2h ``` ## `cua fleet pool` One pool (any pool you own, not only managed ones). ```text output cua fleet pool [OPTIONS] <COMMAND> ``` ### `cua fleet pool export` Print a pool's spec: the shared sandbox model as JSON, or with `--terraform` the equivalent `fleets_pool` resource block. ```text output cua fleet pool export [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Pool name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--terraform` | boolean | `false` | Emit Terraform HCL (the cyclops-cs `fleets_pool` resource). | **Examples** ```bash test="cli-shape" id="ref-cua-fleet-pool-export" cua fleet pool export ci-linux cua fleet pool export ci-linux --terraform > pool.tf ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua mcp The stdio MCP server for AI assistants. Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Stdio MCP server exposing sandboxes, computer control and skills. ```text output cua mcp [OPTIONS] ``` Alias: `cua serve-mcp`. | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--permissions` | string | | `CUA_MCP_PERMISSIONS` | Comma-separated permissions or groups (default: all). | | `--sandbox` | string | | `CUA_SANDBOX` | Default sandbox for computer tools. | **Examples** ```bash test="cli-shape" id="ref-cua-mcp" # Serve every tool over stdio (what MCP clients launch) cua mcp # Read-only sandbox tools, computer tools on the sandbox dev cua mcp --permissions sandbox:readonly,computer:all --sandbox dev ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua agent Run coding agents (Claude Code, Codex, Gemini CLI, ...) inside a sandbox and follow, continue or stop them. | Command | Description | | --- | --- | | [`cua agent run`](#cua-agent-run) | Start a coding agent in a sandbox (returns at once unless --follow). | | [`cua agent ls`](#cua-agent-ls) | List runs in a sandbox. | | [`cua agent logs`](#cua-agent-logs) | Print a run's normalized events (--follow to stream). | | [`cua agent send`](#cua-agent-send) | Send a follow-up (queued while a turn runs). | | [`cua agent interrupt`](#cua-agent-interrupt) | Cancel the turn in flight (the session stays open). | | [`cua agent stop`](#cua-agent-stop) | Stop a run and verify it is gone. | | [`cua agent status`](#cua-agent-status) | A run's status and last result. | | [`cua agent rm`](#cua-agent-rm) | Stop a run and delete its record (secrets included). | | [`cua agent ensure`](#cua-agent-ensure) | Install harnesses or apps now (`claude-code`, `blender`, ...). | | [`cua agent harnesses`](#cua-agent-harnesses) | The harnesses: readiness, installs, key variables, limits. | | [`cua agent create`](#cua-agent-create) | Create a persistent agent: a named harness whose memory lives in the Cua Volume (agents/NAME/) and outlives its Space. | | [`cua agent persistent`](#cua-agent-persistent) | List persistent agents. | | [`cua agent tell`](#cua-agent-tell) | Give a persistent agent a turn (starts a run with its home restored, or follows up on its idle run). | | [`cua agent save`](#cua-agent-save) | Save a persistent agent's home into the drive now. | | [`cua agent pause`](#cua-agent-pause) | Pause a persistent agent: its run, its routines, and its Space (suspended when local; a cloud Space is released after the home is saved). | | [`cua agent resume`](#cua-agent-resume) | Resume a paused persistent agent. | | [`cua agent forget`](#cua-agent-forget) | Forget a persistent agent (its home stays in the drive). | | [`cua agent routine`](#cua-agent-routine) | A persistent agent's routines, fired by the daemon with or without the app open. | | [`cua agent routine add`](#cua-agent-routine-add) | Add a routine (give one of --every, --daily, --weekly). | | [`cua agent routine ls`](#cua-agent-routine-ls) | List routines. | | [`cua agent routine rm`](#cua-agent-routine-rm) | Delete a routine. | | [`cua agent routine enable`](#cua-agent-routine-enable) | Turn a routine on. | | [`cua agent routine disable`](#cua-agent-routine-disable) | Turn a routine off. | | [`cua agent allow-computer`](#cua-agent-allow-computer) | Let a persistent agent use one of your computers (asks for presence). | | [`cua agent revoke-computer`](#cua-agent-revoke-computer) | Take a persistent agent's computer access back. | | [`cua agent computer-access`](#cua-agent-computer-access) | Which agents may use which computers, and the audit log. | | [`cua agent notifications`](#cua-agent-notifications) | The notifications the Cua app shows (turn ends, notify_user, requests). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Run coding agents inside a sandbox: run, ls, logs, send, interrupt, stop. ```text output cua agent [OPTIONS] <COMMAND> ``` ## `cua agent run` Start a coding agent in a sandbox (returns at once unless --follow). ```text output cua agent run [OPTIONS] <SANDBOX> <HARNESS> <PROMPT> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | Sandbox (`local:NAME`, `cloud:NAME`, `direct:HOST:PORT`, or NAME). | | `<HARNESS>` | string | required | Harness (`cua agent harnesses`). | | `<PROMPT>` | string | required | The task. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--env-from-host` | | string | | Forward a provider key from this shell's environment (repeatable), for example ANTHROPIC_API_KEY. The value never appears in argv. | | `--repo` | | string | | Git repository cloned into the working directory first. | | `--branch` | | string | | | | `--cwd` | | string | | Working directory in the sandbox. | | `--model` | | string | | | | `--base-url` | | string | | A custom model endpoint (proxy or compatible server). | | `--wire` | | string | | Its wire format: anthropic, openai-responses, openai-chat, gemini. | | `--mcp` | | string | | An MCP server for the agent: NAME=URL (HTTP, as reachable inside the sandbox) or NAME=cmd:COMMAND ARGS... (stdio, run in the sandbox). Repeatable. | | `--file` | | path | | Attach a local file to the prompt (repeatable). | | `--label` | | string | | | | `--local` | | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | | `--no-sandbox-mcp` | | boolean | `false` | Do not give the agent the sandbox's own MCP tools. | | `--no-skills` | | boolean | `false` | Do not copy the cua skills into the harness. | | `--exit-when-idle` | | boolean | `false` | Stop (resumably) once the prompt is answered. | | `--follow` | `-f` | boolean | `false` | Stream events until the turn ends. | **Examples** ```bash test="cli-shape" id="ref-cua-agent-run" cua agent run local:dev claude-code "fix the failing test" --env-from-host ANTHROPIC_API_KEY -f cua agent run cloud:ci openai-codex "add tests" --repo https://github.com/org/repo --exit-when-idle ``` ## `cua agent ls` List runs in a sandbox. ```text output cua agent ls [OPTIONS] <SANDBOX> ``` Alias: `cua agent list`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | Sandbox (`local:NAME`, `cloud:NAME`, `direct:HOST:PORT`, or NAME). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-agent-ls" cua agent ls local:dev ``` ## `cua agent logs` Print a run's normalized events (--follow to stream). ```text output cua agent logs [OPTIONS] <SANDBOX> <RUN_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | | | `<RUN_ID>` | string | required | | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--local` | | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | | `--follow` | `-f` | boolean | `false` | Keep streaming until the turn ends. | | `--raw` | | boolean | `false` | Print each event's JSON (raw ACP payload included). | **Examples** ```bash test="cli-shape" id="ref-cua-agent-logs" cua agent logs local:dev run-1a2b3c4d -f ``` ## `cua agent send` Send a follow-up (queued while a turn runs). ```text output cua agent send [OPTIONS] <SANDBOX> <RUN_ID> <TEXT> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | | | `<RUN_ID>` | string | required | | | `<TEXT>` | string | required | | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--local` | | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | | `--follow` | `-f` | boolean | `false` | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-send" cua agent send local:dev run-1a2b3c4d "now add a regression test" -f ``` ## `cua agent interrupt` Cancel the turn in flight (the session stays open). ```text output cua agent interrupt [OPTIONS] <SANDBOX> <RUN_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | | | `<RUN_ID>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-agent-interrupt" cua agent interrupt local:dev run-1a2b3c4d ``` ## `cua agent stop` Stop a run and verify it is gone. ```text output cua agent stop [OPTIONS] <SANDBOX> <RUN_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | | | `<RUN_ID>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-agent-stop" cua agent stop local:dev run-1a2b3c4d ``` ## `cua agent status` A run's status and last result. ```text output cua agent status [OPTIONS] <SANDBOX> <RUN_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | | | `<RUN_ID>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-agent-status" cua agent status local:dev run-1a2b3c4d ``` ## `cua agent rm` Stop a run and delete its record (secrets included). ```text output cua agent rm [OPTIONS] <SANDBOX> <RUN_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | | | `<RUN_ID>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-agent-rm" cua agent rm local:dev run-1a2b3c4d ``` ## `cua agent ensure` Install harnesses or apps now (`claude-code`, `blender`, ...). ```text output cua agent ensure [OPTIONS] <SANDBOX> <IDS>... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SANDBOX>` | string | required | | | `<IDS>...` | string | required | Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--local` | boolean | `false` | Look NAME up among local sandboxes only (same as `local:NAME`). | | `--cloud` | boolean | `false` | Look NAME up among cloud sandboxes only (same as `cloud:NAME`). | **Examples** ```bash test="cli-shape" id="ref-cua-agent-ensure" cua agent ensure local:dev claude-code vscode ``` ## `cua agent harnesses` The harnesses: readiness, installs, key variables, limits. ```text output cua agent harnesses [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-agent-harnesses" cua agent harnesses cua --json agent harnesses ``` ## `cua agent create` Create a persistent agent: a named harness whose memory lives in the Cua Volume (agents/NAME/) and outlives its Space. ```text output cua agent create [OPTIONS] --harness <HARNESS> --in <SPACE> <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--harness` | string | required | Harness id (`cua agent harnesses`). | | `--in` | string | required | The Space it works in. | | `--model` | string | | | | `--base-url` | string | | | | `--env-from-host` | string | | Provider key variable to forward at each start. Repeatable. | **Examples** ```bash test="cli-shape" id="ref-cua-agent-create" cua agent create ada --harness hermes --in local:dev --env-from-host OPENAI_API_KEY cua agent create scout --harness claude-code --in cloud:scout --env-from-host ANTHROPIC_API_KEY ``` ## `cua agent persistent` List persistent agents. ```text output cua agent persistent [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-agent-persistent" cua agent persistent ``` ## `cua agent tell` Give a persistent agent a turn (starts a run with its home restored, or follows up on its idle run). ```text output cua agent tell [OPTIONS] <NAME> <TEXT> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | | | `<TEXT>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-tell" cua agent tell ada "what did you learn yesterday?" ``` ## `cua agent save` Save a persistent agent's home into the drive now. ```text output cua agent save [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-save" cua agent save ada ``` ## `cua agent pause` Pause a persistent agent: its run, its routines, and its Space (suspended when local; a cloud Space is released after the home is saved). ```text output cua agent pause [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-pause" cua agent pause ada ``` ## `cua agent resume` Resume a paused persistent agent. ```text output cua agent resume [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--prompt` | string | | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-resume" cua agent resume ada cua agent resume ada --prompt "pick up where you left off" ``` ## `cua agent forget` Forget a persistent agent (its home stays in the drive). ```text output cua agent forget [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-forget" cua agent forget ada ``` ## `cua agent routine` A persistent agent's routines, fired by the daemon with or without the app open. ```text output cua agent routine [OPTIONS] <COMMAND> ``` ### `cua agent routine add` Add a routine (give one of --every, --daily, --weekly). ```text output cua agent routine add [OPTIONS] --title <TITLE> --prompt <PROMPT> <AGENT> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<AGENT>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--title` | string | required | | | `--prompt` | string | required | | | `--every` | integer | | Every N minutes. | | `--daily` | string | | Every day at HH:MM. | | `--weekly` | string | | Every week, `mon 09:00`. | **Examples** ```bash test="cli-shape" id="ref-cua-agent-routine-add" cua agent routine add ada --title "Inbox sweep" --prompt "Triage new issues" --every 60 cua agent routine add ada --title "Morning" --prompt "Plan the day" --daily 08:00 ``` ### `cua agent routine ls` List routines. ```text output cua agent routine ls [OPTIONS] [AGENT] ``` Alias: `cua agent routine list`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<AGENT>` | string | optional | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-routine-ls" cua agent routine ls cua agent routine ls ada ``` ### `cua agent routine rm` Delete a routine. ```text output cua agent routine rm [OPTIONS] <ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ID>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-routine-rm" cua agent routine rm 0F3A2C1E-8B7D-4E6F-9A1B-2C3D4E5F6A7B ``` ### `cua agent routine enable` Turn a routine on. ```text output cua agent routine enable [OPTIONS] <ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ID>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-routine-enable" cua agent routine enable 0F3A2C1E-8B7D-4E6F-9A1B-2C3D4E5F6A7B ``` ### `cua agent routine disable` Turn a routine off. ```text output cua agent routine disable [OPTIONS] <ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ID>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-routine-disable" cua agent routine disable 0F3A2C1E-8B7D-4E6F-9A1B-2C3D4E5F6A7B ``` ## `cua agent allow-computer` Let a persistent agent use one of your computers (asks for presence). ```text output cua agent allow-computer [OPTIONS] <NAME> <MACHINE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | | | `<MACHINE>` | string | required | The machine's Space id (`cua host status` shows this machine's). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--for` | integer | | End the grant after this many seconds. | **Examples** ```bash test="cli-shape" id="ref-cua-agent-allow-computer" cua agent allow-computer ada relay:0123456789abcdef ``` ## `cua agent revoke-computer` Take a persistent agent's computer access back. ```text output cua agent revoke-computer [OPTIONS] <NAME> [MACHINE] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | | | `<MACHINE>` | string | optional | | **Examples** ```bash test="cli-shape" id="ref-cua-agent-revoke-computer" cua agent revoke-computer ada cua agent revoke-computer ada relay:0123456789abcdef ``` ## `cua agent computer-access` Which agents may use which computers, and the audit log. ```text output cua agent computer-access [OPTIONS] [NAME] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | optional | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--audit` | integer | `0` | Also print this many audit entries. | **Examples** ```bash test="cli-shape" id="ref-cua-agent-computer-access" cua agent computer-access --audit 20 ``` ## `cua agent notifications` The notifications the Cua app shows (turn ends, notify_user, requests). ```text output cua agent notifications [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--unread` | boolean | `false` | | | `--mark-read` | boolean | `false` | Mark every notification read. | **Examples** ```bash test="cli-shape" id="ref-cua-agent-notifications" cua agent notifications --unread cua agent notifications --mark-read ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua agents Install cua skills and the cua MCP server into AI coding agents. | Command | Description | | --- | --- | | [`cua agents detect`](#cua-agents-detect) | List supported AI coding agents and which are installed (read-only). | | [`cua agents setup`](#cua-agents-setup) | Install the default cua skills and configure the cua MCP server. | | [`cua agents status`](#cua-agents-status) | What cua configured for each agent. | | [`cua agents remove`](#cua-agents-remove) | Remove the skills and MCP entries cua added (nothing else). | | [`cua agents update`](#cua-agents-update) | Refresh installed cua skills and managed MCP entries to this version. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). AI coding agents on this machine: install cua skills and configure the cua MCP server. ```text output cua agents [OPTIONS] <COMMAND> ``` ## `cua agents detect` List supported AI coding agents and which are installed (read-only). ```text output cua agents detect [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-agents-detect" cua agents detect ``` ## `cua agents setup` Install the default cua skills and configure the cua MCP server. ```text output cua agents setup [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--agents` | | string | | Agents: `all` (installed), `none`, or ids such as `claude,codex,hermes` (`cua agents detect` lists every supported agent). | | `--mcp-command` | | string | | Command agents launch for the MCP server (default: the `cua` on PATH, else this binary). | | `--skills-only` | | boolean | `false` | Only install skills. Alias: `--no-mcp`. | | `--mcp-only` | | boolean | `false` | Only configure the MCP server. Alias: `--no-skills`. | | `--yes` | `-y` | boolean | `false` | Answer yes to every prompt. | | `--force` | | boolean | `false` | Replace skill folders cua did not write (the old one is backed up). | | `--cua-driver` | | boolean | `false` | Set up background computer-use instead: the cua-driver skill and the cua-driver MCP server (`cua-driver mcp`). --mcp-command then names the cua-driver binary. | **Examples** ```bash test="cli-shape" id="ref-cua-agents-setup" # Every installed agent, asking before each change cua agents setup # Claude Code and Codex, no prompts cua agents setup --agents claude,codex -y # Hermes Agent (Nous Research): ~/.hermes/skills and ~/.hermes/config.yaml cua agents setup --agents hermes -y # Only the MCP server entry cua agents setup --mcp-only # Background computer-use: the cua-driver skill and MCP server cua agents setup --cua-driver --agents all -y ``` ## `cua agents status` What cua configured for each agent. ```text output cua agents status [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-agents-status" cua agents status ``` ## `cua agents remove` Remove the skills and MCP entries cua added (nothing else). ```text output cua agents remove [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--agents` | | string | `all` | Agents: `all` (default) or ids such as `claude,codex`. | | `--skills-only` | | boolean | `false` | Only remove skills. | | `--mcp-only` | | boolean | `false` | Only remove the MCP entry. | | `--yes` | `-y` | boolean | `false` | Do not ask for confirmation. | **Examples** ```bash test="cli-shape" id="ref-cua-agents-remove" cua agents remove cua agents remove --agents codex --skills-only -y ``` ## `cua agents update` Refresh installed cua skills and managed MCP entries to this version. ```text output cua agents update [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-agents-update" cua agents update ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua skills Record, list and replay demonstrations (skills). | Command | Description | | --- | --- | | [`cua skills list`](#cua-skills-list) | List saved skills. | | [`cua skills read`](#cua-skills-read) | Print a skill. | | [`cua skills replay`](#cua-skills-replay) | Open a skill's video recording. | | [`cua skills delete`](#cua-skills-delete) | Delete a skill. | | [`cua skills clean`](#cua-skills-clean) | Delete every skill (asks first). | | [`cua skills record`](#cua-skills-record) | Record a demonstration in the HTML5 viewer (it records the screen and your input). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Recorded demonstrations (skills) for agents. ```text output cua skills [OPTIONS] <COMMAND> ``` ## `cua skills list` List saved skills. ```text output cua skills list [OPTIONS] ``` Alias: `cua skills ls`. **Examples** ```bash test="cli-shape" id="ref-cua-skills-list" cua skills list ``` ## `cua skills read` Print a skill. ```text output cua skills read [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Skill name. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--format` | `-f` | `md` \| `json` | `md` | Output format. | **Examples** ```bash test="cli-shape" id="ref-cua-skills-read" cua skills read export-invoice cua skills read export-invoice --format json ``` ## `cua skills replay` Open a skill's video recording. ```text output cua skills replay [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Skill name. | **Examples** ```bash test="cli-shape" id="ref-cua-skills-replay" cua skills replay export-invoice ``` ## `cua skills delete` Delete a skill. ```text output cua skills delete [OPTIONS] <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<NAME>` | string | required | Skill name. | **Examples** ```bash test="cli-shape" id="ref-cua-skills-delete" cua skills delete export-invoice ``` ## `cua skills clean` Delete every skill (asks first). ```text output cua skills clean [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--yes` | `-y` | boolean | `false` | Do not ask for confirmation. | **Examples** ```bash test="cli-shape" id="ref-cua-skills-clean" cua skills clean # Without the prompt cua skills clean --yes ``` ## `cua skills record` Record a demonstration in the HTML5 viewer (it records the screen and your input). ```text output cua skills record [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--sandbox` | `-s` | string | | Sandbox whose display service to record. | | `--viewer-url` | `-u` | string | | Viewer URL to record (a `cua sb view --no-open` link). Alias: `--vnc-url`. | | `--provider` | `-p` | `anthropic` \| `openai` | `anthropic` | Captioning provider. | | `--model` | `-m` | string | | Captioning model. | | `--api-key` | `-k` | string | | API key for the captioning provider. | | `--name` | `-n` | string | | Skill name. | | `--description` | `-d` | string | | Skill description. | **Examples** ```bash test="cli-shape" id="ref-cua-skills-record" # Record on the sandbox dev and caption with Anthropic cua skills record --sandbox dev --name export-invoice ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua trajectory Recorded `cua do` trajectories. | Command | Description | | --- | --- | | [`cua trajectory ls`](#cua-trajectory-ls) | List sessions. | | [`cua trajectory view`](#cua-trajectory-view) | Zip, serve on loopback and open the hosted viewer. | | [`cua trajectory stop`](#cua-trajectory-stop) | Stop the file server started by `view`. | | [`cua trajectory clean`](#cua-trajectory-clean) | Delete sessions. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Recorded `cua do` trajectories. ```text output cua trajectory [OPTIONS] <COMMAND> ``` Alias: `cua traj`. ## `cua trajectory ls` List sessions. ```text output cua trajectory ls [OPTIONS] [MACHINE] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<MACHINE>` | string | optional | Only sessions of this target (sandbox name or `host`). | **Examples** ```bash test="cli-shape" id="ref-cua-trajectory-ls" cua traj ls # Only the sessions recorded on the sandbox dev cua traj ls dev ``` ## `cua trajectory view` Zip, serve on loopback and open the hosted viewer. ```text output cua trajectory view [OPTIONS] [TARGET] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<TARGET>` | string | optional | Session id or target name (default: the latest session). | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--port` | `-p` | integer | `8089` | Loopback port of the file server. | **Examples** ```bash test="cli-shape" id="ref-cua-trajectory-view" # The latest session cua traj view cua traj view <session> --port 8090 ``` ## `cua trajectory stop` Stop the file server started by `view`. ```text output cua trajectory stop [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-trajectory-stop" cua traj stop ``` ## `cua trajectory clean` Delete sessions. ```text output cua trajectory clean [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--older-than` | | integer | | Only sessions older than this many days. | | `--machine` | | string | | Only sessions of this target. | | `--yes` | `-y` | boolean | `false` | Do not ask for confirmation. | **Examples** ```bash test="cli-shape" id="ref-cua-trajectory-clean" # Sessions older than a week, without the prompt cua traj clean --older-than 7 --yes ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua spaces Registered Spaces and your machines on the relay. | Command | Description | | --- | --- | | [`cua spaces ls`](#cua-spaces-ls) | Registered Spaces and your machines on the relay. | | [`cua spaces add`](#cua-spaces-add) | Register a Space after a capabilities handshake with its cua-spacesd (or, for another URL, an MCP initialize). | | [`cua spaces create`](#cua-spaces-create) | Create Spaces: on this machine, on one of your machines that provides Spaces (`cua host setup --provide-spaces` there), or in the cloud. | | [`cua spaces cancel`](#cua-spaces-cancel) | Cancel a create that is still running (here, in the daemon, or in another terminal): it stops and what it made is removed. | | [`cua spaces gpus`](#cua-spaces-gpus) | The GPU options each runtime offers on this machine (or in the cloud). | | [`cua spaces delete`](#cua-spaces-delete) | Delete a Space's sandbox (on this machine, on the machine that provides it, or in the cloud) and forget it (asks first). | | [`cua spaces stop`](#cua-spaces-stop) | Turn a Space off: a local container or QEMU VM is suspended (its memory is kept), a Lume VM or a Space one of your machines provides is stopped (its disk is kept), and so is a Space in your own cloud (`--on aws`, `gcp`; compute billing stops). | | [`cua spaces start`](#cua-spaces-start) | Turn a Space back on: resume a suspended one or boot a stopped one. | | [`cua spaces rm`](#cua-spaces-rm) | Forget a registered Space and its stored token. | | [`cua spaces share`](#cua-spaces-share) | Let another cua.ai account watch (viewer) or use (editor) a Space, through the relay. | | [`cua spaces unshare`](#cua-spaces-unshare) | Stop sharing a Space with one account at once, or with everyone. | | [`cua spaces shares`](#cua-spaces-shares) | Who a Space is shared with, and who of them is connected now. | | [`cua spaces relay-register`](#cua-spaces-relay-register) | Publish a Space on the relay for your other devices (a phone off this network): its own driver dials out, as `cua host setup` does for this computer. | | [`cua spaces relay-unregister`](#cua-spaces-relay-unregister) | Take a Space off the relay (every share with it ends). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Spaces: registered ones and your machines on the relay. ```text output cua spaces [OPTIONS] <COMMAND> ``` ## `cua spaces ls` Registered Spaces and your machines on the relay. ```text output cua spaces ls [OPTIONS] ``` Alias: `cua spaces list`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--relay` | string | | Relay URL (default $CUA_RELAY_URL, else https://relay.cua.ai). | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-ls" cua spaces ls cua spaces ls --json ``` ## `cua spaces add` Register a Space after a capabilities handshake with its cua-spacesd (or, for another URL, an MCP initialize). Re-adding an address updates it. The token is stored apart from the registry, 0600. ```text output cua spaces add [OPTIONS] <URL> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<URL>` | string | required | The Space's cua-spacesd (`http(s)://host:port`, `host:port`, default port 3211), a Space id (`local:<name>`, `cloud:<name>`), or any MCP endpoint URL. | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token (or the bearer an MCP endpoint needs). | | `--name` | string | | | Display name (default: the guest's hostname). | | `--service` | string | | | For an MCP endpoint URL: the service name to register it under (default `mcp`). | | `--host` | boolean | `false` | | It is a machine that provides Spaces over its direct address (Tailscale or LAN): `--on host:<name>` finds it without the relay. Its token is stored like any direct Space's. | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-add" cua spaces add 10.0.0.5:3211 --token "$TOKEN" --name studio cua spaces add local:my-sandbox cua spaces add http://127.0.0.1:8765/mcp --service tools # A machine that provides Spaces over Tailscale, without the relay (`cua host setup --direct ... --provide-spaces` on it prints this) cua spaces add 100.101.102.103:3211 --host --name "Mac mini (spare)" --token "$TOKEN" ``` ## `cua spaces create` Create Spaces: on this machine, on one of your machines that provides Spaces (`cua host setup --provide-spaces` there), or in the cloud. ```text output cua spaces create [OPTIONS] [IMAGE] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<IMAGE>` | string | optional | Image: an alias (`linux`, `macos`, `macos:26`) or a registry reference. Default: the canonical Linux image. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--on` | string | | Where: `local`, `cloud`, `host:<machine>`, or a machine name (`mac-mini`, "spare mac mini"). Default: your default location. | | `--count` | integer | `1` | How many (1 to 8). | | `--name` | string | | Name (with --count, `-2`, `-3`, ... are appended). | | `--kind` | string | | auto \| container \| vm. | | `--runtime` | string | | auto, or an engine the location offers (lume, runc, gvisor, qemu). | | `--cpus` | integer | | vCPUs. | | `--memory-mb` | integer | | Memory in MiB. | | `--disk-gb` | integer | | Grow a VM's disk to this many GiB. | | `--gpu` | string | | A GPU: the runtime's own (`--gpu`), or an option `cua spaces gpus` lists. macOS VMs: GPU acceleration (experimental, Apple silicon; https://cua.ai/docs/lume/guides/gpu-passthrough). | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-create" cua spaces create linux # Two macOS VMs on your spare Mac mini (matched by name) cua spaces create macos:26 --on "spare mac mini" --count 2 cua spaces create --on host:0123abcd4567ef89 linux --json ``` ## `cua spaces cancel` Cancel a create that is still running (here, in the daemon, or in another terminal): it stops and what it made is removed. Finished image downloads stay cached. Ctrl-C during `cua spaces create` does the same. ```text output cua spaces cancel [OPTIONS] <SPACE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | The id the Space will have (`local:<name>`, printed when the create starts) or its name. | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-cancel" cua spaces cancel local:space-1a2b3c cua spaces cancel studio ``` ## `cua spaces gpus` The GPU options each runtime offers on this machine (or in the cloud). ```text output cua spaces gpus [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--on` | string | | `local` (default) or `cloud`. | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-gpus" cua spaces gpus cua spaces gpus --json ``` ## `cua spaces delete` Delete a Space's sandbox (on this machine, on the machine that provides it, or in the cloud) and forget it (asks first). ```text output cua spaces delete [OPTIONS] <SPACE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id or name. | | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--force` | `-f` | boolean | `false` | Skip the confirmation (required without a terminal). | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-delete" cua spaces delete relay:space-0123456789abcdef cua spaces delete local:studio --force ``` ## `cua spaces stop` Turn a Space off: a local container or QEMU VM is suspended (its memory is kept), a Lume VM or a Space one of your machines provides is stopped (its disk is kept), and so is a Space in your own cloud (`--on aws`, `gcp`; compute billing stops). Fleet cloud Spaces, Modal sandboxes and Spaces added by address cannot be turned off. ```text output cua spaces stop [OPTIONS] <SPACE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id or name. | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-stop" cua spaces stop local:studio cua spaces stop relay:space-0123456789abcdef --json ``` ## `cua spaces start` Turn a Space back on: resume a suspended one or boot a stopped one. ```text output cua spaces start [OPTIONS] <SPACE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id or name. | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-start" cua spaces start local:studio cua spaces start studio --json ``` ## `cua spaces rm` Forget a registered Space and its stored token. The sandbox keeps running. The `relay:<machine>` record of a Space that registered itself and is gone (not connected) also leaves your relay directory. ```text output cua spaces rm [OPTIONS] <SPACE> ``` Alias: `cua spaces remove`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id, address or display name (as `cua spaces ls` shows). | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-rm" cua spaces rm direct:10.0.0.5:3211 cua spaces rm studio ``` ## `cua spaces share` Let another cua.ai account watch (viewer) or use (editor) a Space, through the relay. They open it as `relay:<machine>`. ```text output cua spaces share [OPTIONS] <SPACE> <WHO> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id, address or display name. | | `<WHO>` | string | required | Their email (verified by cua.ai) or account id. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--role` | `viewer` \| `editor` | `viewer` | `viewer`: presence and a view-only stream. `editor`: full use. | | `--yes` | boolean | `false` | Confirm without a prompt (needed without a terminal when no daemon runs; a running daemon asks with Touch ID or your password instead). | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-share" cua spaces share local:studio bob@example.com cua spaces share local:studio bob@example.com --role editor cua spaces share relay:0123abcd4567ef89 acct-42 --role viewer ``` ## `cua spaces unshare` Stop sharing a Space with one account at once, or with everyone. ```text output cua spaces unshare [OPTIONS] <SPACE> [WHO] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id, address or display name. | | `<WHO>` | string | optional | Their email or account id. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--all` | boolean | `false` | Stop sharing with everyone (the Space leaves the relay). | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-unshare" cua spaces unshare local:studio bob@example.com cua spaces unshare local:studio --all ``` ## `cua spaces shares` Who a Space is shared with, and who of them is connected now. ```text output cua spaces shares [OPTIONS] <SPACE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id, address or display name. | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-shares" cua spaces shares local:studio cua spaces shares local:studio --json ``` ## `cua spaces relay-register` Publish a Space on the relay for your other devices (a phone off this network): its own driver dials out, as `cua host setup` does for this computer. Nobody else gets in until you share it. ```text output cua spaces relay-register [OPTIONS] <SPACE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id, address or display name. | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-relay-register" cua spaces relay-register local:studio ``` ## `cua spaces relay-unregister` Take a Space off the relay (every share with it ends). With a `relay:<machine>` id, removes that machine's record from your relay directory: a Space that registered itself (also after it was deleted), or a machine of yours that is gone. Only its owner can. ```text output cua spaces relay-unregister [OPTIONS] <SPACE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<SPACE>` | string | required | Space id, address or display name. | **Examples** ```bash test="cli-shape" id="ref-cua-spaces-relay-unregister" cua spaces relay-unregister local:studio cua spaces relay-unregister relay:space-0123abcd4567ef89 ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua cloud Your own AWS, Google Cloud or Modal account as a place for sandboxes and Spaces: connect, test, status, sweep. | Command | Description | | --- | --- | | [`cua cloud status`](#cua-cloud-status) | Your clouds: found credentials, the connected account and region, what each runs at what hourly cost, and what Cua created there. | | [`cua cloud connect`](#cua-cloud-connect) | Connect a cloud account Spaces can be created in (after the checks `cua cloud test` runs). | | [`cua cloud test`](#cua-cloud-test) | Check a cloud account without creating anything: credentials, permissions (by dry run), quota, the region and machine types. | | [`cua cloud disconnect`](#cua-cloud-disconnect) | Forget a connected cloud (nothing in it is deleted). | | [`cua cloud sweep`](#cua-cloud-sweep) | Find what Cua left in your clouds: expired Spaces and resources whose Space is gone (with --all, every Cua resource). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Spaces in your own cloud account (AWS, Google Cloud, Modal): connect, status, test, disconnect, sweep. ```text output cua cloud [OPTIONS] <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-cloud" cua cloud status cua cloud connect aws --region us-west-2 --default cua spaces create --on aws cua cloud sweep ``` ## `cua cloud status` Your clouds: found credentials, the connected account and region, what each runs at what hourly cost, and what Cua created there. ```text output cua cloud status [OPTIONS] [PROVIDER] ``` Alias: `cua cloud ls`. | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PROVIDER>` | string | optional | Only this cloud (`aws`, `gcp`, `modal`). | **Examples** ```bash test="cli-shape" id="ref-cua-cloud-status" cua cloud status cua cloud status aws --json ``` ## `cua cloud connect` Connect a cloud account Spaces can be created in (after the checks `cua cloud test` runs). Credentials stay in the cloud's own CLI; Cua stores only the profile, region, project or environment names. ```text output cua cloud connect [OPTIONS] <PROVIDER> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PROVIDER>` | string | required | `aws`, `gcp` or `modal`. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--profile` | string | | AWS profile (`~/.aws/config`) or Modal profile (`~/.modal.toml`). | | `--region` | string | | AWS or Google Cloud region. | | `--zone` | string | | Google Cloud zone (default: the region's `-a` zone). | | `--project` | string | | Google Cloud project (default: `gcloud config get project`). | | `--environment` | string | | Modal environment (default: the profile's). | | `--ttl-hours` | integer | | Delete each Space there after this many hours (default 8; 0: never; Modal caps a sandbox at 24). | | `--default` | boolean | `false` | Also make it the default location (`default.on`). | **Examples** ```bash test="cli-shape" id="ref-cua-cloud-connect" cua cloud connect aws --region us-west-2 --default cua cloud connect aws --profile sandbox cua cloud connect gcp --project my-project --region us-central1 cua cloud connect modal --environment spaces # Spaces there delete themselves after 4 hours (default 8; 0: never) cua cloud connect aws --ttl-hours 4 ``` ## `cua cloud test` Check a cloud account without creating anything: credentials, permissions (by dry run), quota, the region and machine types. ```text output cua cloud test [OPTIONS] <PROVIDER> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PROVIDER>` | string | required | `aws`, `gcp` or `modal`. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--profile` | string | | AWS profile (`~/.aws/config`) or Modal profile (`~/.modal.toml`). | | `--region` | string | | AWS or Google Cloud region. | | `--zone` | string | | Google Cloud zone (default: the region's `-a` zone). | | `--project` | string | | Google Cloud project (default: `gcloud config get project`). | | `--environment` | string | | Modal environment (default: the profile's). | **Examples** ```bash test="cli-shape" id="ref-cua-cloud-test" cua cloud test aws cua cloud test gcp --project my-project ``` ## `cua cloud disconnect` Forget a connected cloud (nothing in it is deleted). ```text output cua cloud disconnect [OPTIONS] <PROVIDER> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PROVIDER>` | string | required | `aws`, `gcp` or `modal`. | **Examples** ```bash test="cli-shape" id="ref-cua-cloud-disconnect" cua cloud disconnect aws ``` ## `cua cloud sweep` Find what Cua left in your clouds: expired Spaces and resources whose Space is gone (with --all, every Cua resource). Lists them unless --delete. Only resources Cua tagged and recorded are ever touched. ```text output cua cloud sweep [OPTIONS] [PROVIDER] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PROVIDER>` | string | optional | Only this cloud. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--delete` | boolean | `false` | Delete what it finds (default: list only). | | `--all` | boolean | `false` | Include Cua resources that have not expired (every Space there). | **Examples** ```bash test="cli-shape" id="ref-cua-cloud-sweep" cua cloud sweep cua cloud sweep aws --delete cua cloud sweep --all --delete ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua host Set this machine up for unattended access. | Command | Description | | --- | --- | | [`cua host setup`](#cua-host-setup) | Set this machine up for unattended access: install cua-spacesd as a service that joins the cua.ai relay as your account (default) or serves on a direct ip:port. | | [`cua host config`](#cua-host-config) | Show or change what this machine shares: its own desktop, and whether it creates Spaces for your other devices. | | [`cua host status`](#cua-host-status) | Show whether this machine is set up, the service, and who is connected. | | [`cua host stop`](#cua-host-stop) | Stop sharing: disconnect everyone and refuse new connections. | | [`cua host start`](#cua-host-start) | Start sharing again after `stop`. | | [`cua host share`](#cua-host-share) | Share a relay machine (this one, or `--machine`) with other cua.ai accounts (id or verified email). | | [`cua host unshare`](#cua-host-unshare) | Stop sharing a relay machine with accounts; their open connections are cut. | | [`cua host remove`](#cua-host-remove) | Unregister from the relay, uninstall the service and delete the host configuration (asks first). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Unattended access to this machine (setup, status, stop, start, remove). ```text output cua host [OPTIONS] <COMMAND> ``` ## `cua host setup` Set this machine up for unattended access: install cua-spacesd as a service that joins the cua.ai relay as your account (default) or serves on a direct ip:port. ```text output cua host setup [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--relay` | string | | Relay URL (default $CUA_RELAY_URL, else https://relay.cua.ai). | | `--direct` | string | | Serve directly on ip:port with a local env token (LAN / port-forwarded) instead of joining a relay. | | `--name` | string | | Display name (default: the host name). | | `--allow` | string | | Also allow this account (id or email). Repeatable. | | `--driver-bin` | path | | cua-spacesd binary (default $CUA_SPACESD_BIN, a bundled one, or the release download). | | `--runner` | string | `auto` | auto \| systemd \| launchd \| windows-task \| process. | | `--profile` | `desktop` \| `spare` | `desktop` | What this machine is for: `desktop` (share this desktop; the default) or `spare` (do not share the desktop; provide Spaces). | | `--max-spaces` | integer | | Provided Spaces at once (default 4; 0: no limit). | | `--remote` | boolean | `false` | When sign-in is needed, use a device code to approve it in a browser on another machine. The account session is not saved. | | `--desktop` | boolean | `false` | Share this machine's own desktop as a Space. | | `--no-desktop` | boolean | `false` | Do not share this machine's own desktop (screen, input, shell, files): relayed callers reach only the Spaces it provides. | | `--provide-spaces` | boolean | `false` | Create Spaces for your enrolled devices on this machine's runtimes (Lume macOS VMs, Docker Linux containers). | | `--no-provide-spaces` | boolean | `false` | Do not create Spaces for other devices. | | `--allow-any-address` | boolean | `false` | With --direct and Spaces: take host calls, and connections to the Spaces this machine forwards, from any address (and allow a public --direct address). By default only loopback, Tailscale and private LAN addresses may. The direct listener is plaintext and its token is a shell on this machine: prefer Tailscale. | **Examples** ```bash test="cli-shape" id="ref-cua-host-setup" # Join the cua.ai relay as your account (shares this desktop) cua host setup # A spare machine: do not share its desktop, run Spaces for your devices cua host setup --profile spare --name "Mac mini (spare)" cua host setup --no-desktop --provide-spaces # Serve on the LAN instead of the relay cua host setup --direct 0.0.0.0:3211 --name studio-mac # A spare Mac on your tailnet that runs Spaces, without the relay (prints the command that adds it on your laptop) cua host setup --direct 100.101.102.103:3211 --profile spare ``` ## `cua host config` Show or change what this machine shares: its own desktop, and whether it creates Spaces for your other devices. ```text output cua host config [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--desktop` | `on` \| `off` | | Share this machine's own desktop: on \| off. | | `--provide-spaces` | `on` \| `off` | | Create Spaces for your enrolled devices: on \| off. | | `--max-spaces` | integer | | Provided Spaces at once (0: no limit). | | `--max-macos-vms` | integer | | macOS VMs at once, 0 to 2 (Apple's license allows two per Mac). | **Examples** ```bash test="cli-shape" id="ref-cua-host-config" cua host config # Stop sharing this desktop; keep providing Spaces cua host config --desktop off --provide-spaces on cua host config --max-spaces 6 ``` ## `cua host status` Show whether this machine is set up, the service, and who is connected. ```text output cua host status [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-host-status" cua host status ``` ## `cua host stop` Stop sharing: disconnect everyone and refuse new connections. ```text output cua host stop [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-host-stop" cua host stop ``` ## `cua host start` Start sharing again after `stop`. ```text output cua host start [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-host-start" cua host start ``` ## `cua host share` Share a relay machine (this one, or `--machine`) with other cua.ai accounts (id or verified email). Run from an enrolled device; every change is in the owner's audit log (`cua devices audit`). ```text output cua host share [OPTIONS] <WHO>... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WHO>...` | string | required | Accounts to add. Repeatable. Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--machine` | string | | Machine id (default: this machine). | | `--relay` | string | | Relay URL (default: the one this machine joined, else $CUA_RELAY_URL / https://relay.cua.ai). | | `--yes` | boolean | `false` | Do not ask for confirmation. | **Examples** ```bash test="cli-shape" id="ref-cua-host-share" cua host share friend@example.com cua host share friend@example.com --machine 0123abcd4567ef89 --yes ``` ## `cua host unshare` Stop sharing a relay machine with accounts; their open connections are cut. ```text output cua host unshare [OPTIONS] <WHO>... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<WHO>...` | string | required | Accounts to remove. Repeatable. Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--machine` | string | | Machine id (default: this machine). | | `--relay` | string | | Relay URL. | **Examples** ```bash test="cli-shape" id="ref-cua-host-unshare" cua host unshare friend@example.com ``` ## `cua host remove` Unregister from the relay, uninstall the service and delete the host configuration (asks first). ```text output cua host remove [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--force` | `-f` | boolean | `false` | Skip the confirmation prompt. Required when there is no terminal to ask on (scripts, agents): without it nothing is removed. | **Examples** ```bash test="cli-shape" id="ref-cua-host-remove" cua host remove # Without the prompt (required in scripts and agents) cua host remove --force ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua devices Enroll this device on the relay, approve, rename and revoke devices, and read the access log. | Command | Description | | --- | --- | | [`cua devices status`](#cua-devices-status) | This device: enrolled, waiting for approval, or not enrolled. | | [`cua devices enroll`](#cua-devices-enroll) | Enroll this device. | | [`cua devices approve`](#cua-devices-approve) | Approve a new device (or re-verify an expired one) from this enrolled device, by the code it shows or by its id. | | [`cua devices ls`](#cua-devices-ls) | Your devices and their state. | | [`cua devices rename`](#cua-devices-rename) | Rename a device. | | [`cua devices revoke`](#cua-devices-revoke) | Revoke a device: it can no longer list or reach your machines. | | [`cua devices audit`](#cua-devices-audit) | Who accessed what, and when: enrollments, device sessions, machine access (yours and by accounts you share with), sharing changes. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). This device as a client of your cua.ai account on the relay: enroll (second factor), approve other devices, list, rename, revoke, and the audit log of who accessed what. ```text output cua devices [OPTIONS] <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-devices" cua devices enroll cua devices approve K7QX-M2RP cua devices ls cua devices audit ``` ## `cua devices status` This device: enrolled, waiting for approval, or not enrolled. ```text output cua devices status [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--relay` | string | | Relay URL (default $CUA_RELAY_URL, else https://relay.cua.ai). | **Examples** ```bash test="cli-shape" id="ref-cua-devices-status" cua devices status ``` ## `cua devices enroll` Enroll this device. Right after a fresh `cua auth login` it enrolls at once (and replaces this machine's older device key, if any); otherwise it shows a one-time code to approve from an enrolled device (`cua devices approve <code>` or the Cua Spaces app). ```text output cua devices enroll [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--name` | string | | Display name (default: the host name). | | `--relay` | string | | Relay URL. | | `--wait` | boolean | `false` | Wait (up to 10 minutes) until an enrolled device approves. | **Examples** ```bash test="cli-shape" id="ref-cua-devices-enroll" cua devices enroll cua devices enroll --name "work laptop" --wait ``` ## `cua devices approve` Approve a new device (or re-verify an expired one) from this enrolled device, by the code it shows or by its id. ```text output cua devices approve [OPTIONS] [CODE] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<CODE>` | string | optional | The one-time code the new device shows, or its device id (`dev_…`, from `cua devices ls`). | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--device` | string | | Or the device id (`cua devices ls`). | | `--relay` | string | | Relay URL. | | `--yes` | boolean | `false` | Do not ask for confirmation. | **Examples** ```bash test="cli-shape" id="ref-cua-devices-approve" cua devices approve K7QX-M2RP cua devices approve dev_0123456789abcdef01234567 ``` ## `cua devices ls` Your devices and their state. ```text output cua devices ls [OPTIONS] ``` Alias: `cua devices list`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--relay` | string | | Relay URL. | **Examples** ```bash test="cli-shape" id="ref-cua-devices-ls" cua devices ls cua devices ls --json ``` ## `cua devices rename` Rename a device. ```text output cua devices rename [OPTIONS] <ID> <NAME> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ID>` | string | required | Device id. | | `<NAME>` | string | required | New name. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--relay` | string | | Relay URL. | **Examples** ```bash test="cli-shape" id="ref-cua-devices-rename" cua devices rename dev_0123456789abcdef01234567 "work phone" ``` ## `cua devices revoke` Revoke a device: it can no longer list or reach your machines. ```text output cua devices revoke [OPTIONS] <ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ID>` | string | required | Device id. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--relay` | string | | Relay URL. | | `--yes` | boolean | `false` | Do not ask for confirmation. | **Examples** ```bash test="cli-shape" id="ref-cua-devices-revoke" cua devices revoke dev_0123456789abcdef01234567 ``` ## `cua devices audit` Who accessed what, and when: enrollments, device sessions, machine access (yours and by accounts you share with), sharing changes. ```text output cua devices audit [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--limit` | integer | `50` | Newest events to show. | | `--relay` | string | | Relay URL. | **Examples** ```bash test="cli-shape" id="ref-cua-devices-audit" cua devices audit cua devices audit --limit 20 --json ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua teleport Move a desktop app session into a sandbox. | Command | Description | | --- | --- | | [`cua teleport providers`](#cua-teleport-providers) | List the apps this machine can teleport (JSON). | | [`cua teleport manifest`](#cua-teleport-manifest) | Describe what teleporting an app would move (JSON). | | [`cua teleport push`](#cua-teleport-push) | Teleport an app session into a sandbox. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Move a desktop app session (tabs, profile, sign-in) into a sandbox. ```text output cua teleport [OPTIONS] <COMMAND> ``` ## `cua teleport providers` List the apps this machine can teleport (JSON). ```text output cua teleport providers [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-teleport-providers" cua teleport providers ``` ## `cua teleport manifest` Describe what teleporting an app would move (JSON). ```text output cua teleport manifest [OPTIONS] --app <APP> ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--app` | string | required | App: a bundle id or app name ("com.google.Chrome", "Slack", "claude-code"). | | `--scope` | string | `full` | `tabs` (open tabs and session state) or `full` (the whole profile). | | `--profile` | string | | Chrome profile to capture: a name ("Profile 1") or a path. | | `--display-name` | string | | Display name for the app (UI only). | **Examples** ```bash test="cli-shape" id="ref-cua-teleport-manifest" cua teleport manifest --app com.google.Chrome --scope tabs ``` ## `cua teleport push` Teleport an app session into a sandbox. ```text output cua teleport push [OPTIONS] --app <APP> ``` | Flag | Short | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | --- | | `--app` | | string | required | | App: a bundle id or app name ("com.google.Chrome", "Slack", "claude-code"). | | `--scope` | | string | `full` | | `tabs` (open tabs and session state) or `full` (the whole profile). | | `--profile` | | string | | | Chrome profile to capture: a name ("Profile 1") or a path. | | `--display-name` | | string | | | Display name for the app (UI only). | | `--sandbox` | `-s` | string | | | Destination sandbox (name). | | `--url` | | string | | | Destination cua-spacesd URL instead of a sandbox. | | `--token` | | string | | `CUA_ENV_TOKEN` | Token for `--url`. | | `--include` | | string | | | Send exactly this manifest item (repeatable; see `manifest`). Default: the provider's default selection, which leaves credentials-heavy or bulky items (cookies, transcripts) out unless named here. | | `--all` | | boolean | `false` | | Send every item the scope offers. | | `--no-launch` | | boolean | `false` | | Do not launch the app in the sandbox after importing. | | `--progress` | | boolean | `false` | | Print `progress <sent> <total>` lines while uploading. | | `--relay-plaintext-ack` | | boolean | `false` | | Send over a `relay:` destination even though this Space's image predates end-to-end sealing (S1): without this, such a send refuses before reading or uploading anything. Set it only after you understand that relay.cua.ai (or a self-hosted cua-relay) could read this delivery in the clear; local and direct destinations are unaffected either way. | **Examples** ```bash test="cli-shape" id="ref-cua-teleport-push" # Chrome's open tabs into the sandbox dev cua teleport push --app com.google.Chrome --scope tabs --sandbox dev # The whole Slack profile, every item cua teleport push --app Slack --sandbox dev --all ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua volume Cua Volume: files, versions, grants, access requests and audit of the volume every Space and agent shares. | Command | Description | | --- | --- | | [`cua volume ls`](#cua-volume-ls) | List a folder (default: the root). | | [`cua volume cat`](#cua-volume-cat) | Print a file (or one of its versions) to stdout. | | [`cua volume put`](#cua-volume-put) | Write a file from a local file, or from stdin with `-`. | | [`cua volume rm`](#cua-volume-rm) | Delete a file (its history stays). | | [`cua volume history`](#cua-volume-history) | A file's versions, newest first. | | [`cua volume restore`](#cua-volume-restore) | Make an old version current again (a new version). | | [`cua volume grant`](#cua-volume-grant) | Let an agent or a Space read (or, with --rw, write) a folder beyond its defaults. | | [`cua volume revoke`](#cua-volume-revoke) | Revoke a grant. | | [`cua volume grants`](#cua-volume-grants) | The grants (live ones; --all includes expired and revoked). | | [`cua volume requests`](#cua-volume-requests) | Access requests agents filed, waiting for you. | | [`cua volume approve`](#cua-volume-approve) | Turn a request into a grant. | | [`cua volume deny`](#cua-volume-deny) | Decline a request. | | [`cua volume audit`](#cua-volume-audit) | The audit log, newest first, and whether its hash chain verified. | | [`cua volume mount`](#cua-volume-mount) | Show the drive as a volume (Finder on macOS, FUSE on Linux). | | [`cua volume unmount`](#cua-volume-unmount) | Turn the volume off (files still uploading land first). | | [`cua volume status`](#cua-volume-status) | The volume, sync across devices, and the block cache. | | [`cua volume config`](#cua-volume-config) | Which store the drive uses (fs, s3, cloud). | | [`cua volume config show`](#cua-volume-config-show) | The backend and bucket settings in effect (never keys). | | [`cua volume config set`](#cua-volume-config-set) | Choose the backend. | | [`cua volume config set-keys`](#cua-volume-config-set-keys) | Save S3 keys in the credential store: the access key id and the secret, one per line on stdin. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Cua Volume: the versioned volume every Space and agent shares (public/, agents/<agent>/, spaces/<space>/), its grants and audit. (`cua volume` still works for one release.). ```text output cua volume [OPTIONS] <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-volume" cua volume ls agents/ cua volume cat agents/ada/memory/MEMORY.md cua volume put public/rules.md ./rules.md cua volume grant agent:researcher agents/writer/outputs/ cua volume audit ``` ## `cua volume ls` List a folder (default: the root). ```text output cua volume ls [OPTIONS] [PATH] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PATH>` | string | optional | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--as` | string | | View the drive as this persistent agent does (`agent:ada` or `ada`). | | `--space` | string | | With --as: the Space the agent is in (its `spaces/<space>/` folder). | **Examples** ```bash test="cli-shape" id="ref-cua-volume-ls" cua volume ls cua volume ls agents/ada/ cua volume ls --as agent:ada --space local:work ``` ## `cua volume cat` Print a file (or one of its versions) to stdout. ```text output cua volume cat [OPTIONS] <PATH> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PATH>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--version` | string | | | | `--as` | string | | View the drive as this persistent agent does (`agent:ada` or `ada`). | | `--space` | string | | With --as: the Space the agent is in (its `spaces/<space>/` folder). | **Examples** ```bash test="cli-shape" id="ref-cua-volume-cat" cua volume cat agents/ada/memory/MEMORY.md cua volume cat public/rules.md --version 0001790683200000-1a2b3c4d ``` ## `cua volume put` Write a file from a local file, or from stdin with `-`. ```text output cua volume put [OPTIONS] <PATH> <SOURCE> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PATH>` | string | required | | | `<SOURCE>` | string | required | Local file, or `-` for stdin. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--if-etag` | string | | Write only if the current version has this etag. | | `--as` | string | | View the drive as this persistent agent does (`agent:ada` or `ada`). | | `--space` | string | | With --as: the Space the agent is in (its `spaces/<space>/` folder). | | `--create-only` | boolean | `false` | Write only if nothing is at the path yet. | **Examples** ```bash test="cli-shape" id="ref-cua-volume-put" cua volume put public/rules.md ./rules.md cua volume put public/rules.md - cua volume put agents/ada/inbox/brief.md ./brief.md --create-only ``` ## `cua volume rm` Delete a file (its history stays). ```text output cua volume rm [OPTIONS] <PATH> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PATH>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--if-etag` | string | | | | `--as` | string | | View the drive as this persistent agent does (`agent:ada` or `ada`). | | `--space` | string | | With --as: the Space the agent is in (its `spaces/<space>/` folder). | **Examples** ```bash test="cli-shape" id="ref-cua-volume-rm" cua volume rm spaces/local-work/scratch.txt ``` ## `cua volume history` A file's versions, newest first. ```text output cua volume history [OPTIONS] <PATH> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PATH>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--as` | string | | View the drive as this persistent agent does (`agent:ada` or `ada`). | | `--space` | string | | With --as: the Space the agent is in (its `spaces/<space>/` folder). | **Examples** ```bash test="cli-shape" id="ref-cua-volume-history" cua volume history agents/ada/memory/MEMORY.md ``` ## `cua volume restore` Make an old version current again (a new version). ```text output cua volume restore [OPTIONS] <PATH> <VERSION> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PATH>` | string | required | | | `<VERSION>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--as` | string | | View the drive as this persistent agent does (`agent:ada` or `ada`). | | `--space` | string | | With --as: the Space the agent is in (its `spaces/<space>/` folder). | **Examples** ```bash test="cli-shape" id="ref-cua-volume-restore" cua volume restore agents/ada/memory/MEMORY.md 0001790683200000-1a2b3c4d ``` ## `cua volume grant` Let an agent or a Space read (or, with --rw, write) a folder beyond its defaults. Asks for Touch ID or your passphrase. ```text output cua volume grant [OPTIONS] <PRINCIPAL> <PREFIX> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<PRINCIPAL>` | string | required | `agent:<name>` or `space:<id>`. | | `<PREFIX>` | string | required | A folder (ending in /) or one file. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--for` | string | | Lifetime (`30m`, `2h`, `7d`). Default: until revoked. | | `--note` | string | | A note shown next to the grant. | | `--rw` | boolean | `false` | Read and write (default: read only). | **Examples** ```bash test="cli-shape" id="ref-cua-volume-grant" cua volume grant agent:researcher agents/writer/outputs/ cua volume grant space:local-build public/datasets/ --rw --for 2h ``` ## `cua volume revoke` Revoke a grant. ```text output cua volume revoke [OPTIONS] <GRANT_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<GRANT_ID>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-volume-revoke" cua volume revoke 5f2c9a0b1d3e4f67 ``` ## `cua volume grants` The grants (live ones; --all includes expired and revoked). ```text output cua volume grants [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--all` | boolean | `false` | | **Examples** ```bash test="cli-shape" id="ref-cua-volume-grants" cua volume grants ``` ## `cua volume requests` Access requests agents filed, waiting for you. ```text output cua volume requests [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-volume-requests" cua volume requests ``` ## `cua volume approve` Turn a request into a grant. Asks for Touch ID or your passphrase. ```text output cua volume approve [OPTIONS] <REQUEST_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<REQUEST_ID>` | string | required | | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--for` | string | | | **Examples** ```bash test="cli-shape" id="ref-cua-volume-approve" cua volume approve 5f2c9a0b1d3e4f67 cua volume approve 5f2c9a0b1d3e4f67 --for 1d ``` ## `cua volume deny` Decline a request. ```text output cua volume deny [OPTIONS] <REQUEST_ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<REQUEST_ID>` | string | required | | **Examples** ```bash test="cli-shape" id="ref-cua-volume-deny" cua volume deny 5f2c9a0b1d3e4f67 ``` ## `cua volume audit` The audit log, newest first, and whether its hash chain verified. ```text output cua volume audit [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--limit` | integer | `50` | | **Examples** ```bash test="cli-shape" id="ref-cua-volume-audit" cua volume audit cua volume audit --limit 200 --json ``` ## `cua volume mount` Show the drive as a volume (Finder on macOS, FUSE on Linux). Off until turned on; stays on across restarts. ```text output cua volume mount [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-volume-mount" cua volume mount cua volume mount --json ``` ## `cua volume unmount` Turn the volume off (files still uploading land first). ```text output cua volume unmount [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-volume-unmount" cua volume unmount ``` ## `cua volume status` The volume, sync across devices, and the block cache. ```text output cua volume status [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-volume-status" cua volume status cua volume status --json ``` ## `cua volume config` Which store the drive uses (fs, s3, cloud). ```text output cua volume config [OPTIONS] <COMMAND> ``` ### `cua volume config show` The backend and bucket settings in effect (never keys). ```text output cua volume config show [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-volume-config-show" cua volume config show ``` ### `cua volume config set` Choose the backend. Keys are never arguments: see set-keys. ```text output cua volume config set [OPTIONS] --backend <BACKEND> ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--backend` | string | required | `fs` (default), `s3`, or `cloud` (off unless the Cua cloud enables it). | | `--endpoint` | string | | | | `--region` | string | | | | `--bucket` | string | | | | `--root` | string | | A key prefix every drive key lives under. | | `--path-style` | boolean | `false` | | **Examples** ```bash test="cli-shape" id="ref-cua-volume-config-set" cua volume config set --backend fs cua volume config set --backend s3 --endpoint http://127.0.0.1:9000 --bucket cua-volume --path-style ``` ### `cua volume config set-keys` Save S3 keys in the credential store: the access key id and the secret, one per line on stdin. ```text output cua volume config set-keys [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-volume-config-set-keys" cua volume config set-keys ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua keyvault Set up, unlock and lock the Cua Keyvault, with the OS key store or a passphrase. | Command | Description | | --- | --- | | [`cua keyvault status`](#cua-keyvault-status) | Whether the Keyvault exists and is unlocked, and which protectors (Touch ID with the OS key store, a passphrase) the daemon can use. | | [`cua keyvault init`](#cua-keyvault-init) | Create the Keyvault and print its recovery key once. | | [`cua keyvault unlock`](#cua-keyvault-unlock) | Unlock the Keyvault with the OS key store, or with its passphrase. | | [`cua keyvault lock`](#cua-keyvault-lock) | Lock the Keyvault (unlocking needs the OS key store or the passphrase again). | | [`cua keyvault import-passwords`](#cua-keyvault-import-passwords) | Import a browser's saved passwords into the Keyvault, one item per site. | | [`cua keyvault import-session`](#cua-keyvault-import-session) | Import a signed-in session (cookies, or a single-app session like Slack's) into the Keyvault, without delivering it anywhere. | | [`cua keyvault requests`](#cua-keyvault-requests) | Requests waiting for your approval (site logins, teleports). | | [`cua keyvault approve`](#cua-keyvault-approve) | Approve a waiting request (the daemon asks for Touch ID or your login password). | | [`cua keyvault deny`](#cua-keyvault-deny) | Decline a waiting request. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). The Cua Keyvault the daemon hosts: status, init, unlock, lock, saved passwords and approvals. Passphrases are prompted for (or read from stdin), never arguments. ```text output cua keyvault [OPTIONS] <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-keyvault" cua keyvault status cua keyvault init --passphrase cua keyvault unlock --passphrase cua keyvault import-passwords --browser chrome cua keyvault requests cua keyvault approve <request-id> cua keyvault lock ``` ## `cua keyvault status` Whether the Keyvault exists and is unlocked, and which protectors (Touch ID with the OS key store, a passphrase) the daemon can use. ```text output cua keyvault status [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-status" cua keyvault status cua keyvault status --json ``` ## `cua keyvault init` Create the Keyvault and print its recovery key once. Uses the OS key store (Touch ID confirms) unless --passphrase or --passphrase-stdin. ```text output cua keyvault init [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--passphrase` | boolean | `false` | Use a passphrase, typed at a terminal prompt with echo off. | | `--passphrase-stdin` | boolean | `false` | Use a passphrase read from the first line of stdin (scripts). | **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-init" # The OS key store (the signed Cua daemon only) cua keyvault init # A passphrase, typed twice at a prompt cua keyvault init --passphrase # Scripts: the passphrase is the first line of stdin cua keyvault init --passphrase-stdin ``` ## `cua keyvault unlock` Unlock the Keyvault with the OS key store, or with its passphrase. ```text output cua keyvault unlock [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--passphrase` | boolean | `false` | Use a passphrase, typed at a terminal prompt with echo off. | | `--passphrase-stdin` | boolean | `false` | Use a passphrase read from the first line of stdin (scripts). | **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-unlock" cua keyvault unlock cua keyvault unlock --passphrase ``` ## `cua keyvault lock` Lock the Keyvault (unlocking needs the OS key store or the passphrase again). ```text output cua keyvault lock [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-lock" cua keyvault lock ``` ## `cua keyvault import-passwords` Import a browser's saved passwords into the Keyvault, one item per site. They stay sealed: agents sign in with them through `request_site_login` after you approve, and never see them. The daemon asks for Touch ID or your login password. ```text output cua keyvault import-passwords [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--browser` | `chrome` | `chrome` | The browser to import from. | | `--profile` | string | | The browser profile (name or path); default: the default profile. | | `--site` | string | | Only this site (a registrable domain such as github.com). Repeatable; default: every saved site. | **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-import-passwords" cua keyvault import-passwords --browser chrome cua keyvault import-passwords --browser chrome --site github.com --site example.com cua keyvault import-passwords --browser chrome --profile "Profile 1" ``` ## `cua keyvault import-session` Import a signed-in session (cookies, or a single-app session like Slack's) into the Keyvault, without delivering it anywhere. A later `cua teleport push --sandbox NAME` (or the review sheet's "Save to Keyvault") can deliver it; it stays sealed until then. The daemon asks for Touch ID or your login password. ```text output cua keyvault import-session [OPTIONS] --app <APP> ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--app` | string | required | The app to import from (a teleport provider id: `chrome`, `firefox`, `slack`, ...; `cua teleport providers` lists every one on this machine). | | `--profile` | string | | The browser profile (name or path); default: the default profile. Ignored for a single-app provider (Slack, Discord, ...). | | `--site` | string | | Only this site's cookies (a registrable domain such as github.com). Repeatable; a browser with none named imports every site's cookies. Ignored (the whole session is one item) for a single-app provider. | | `--include-storage` | boolean | `false` | Also carry each named site's localStorage / IndexedDB origins. | | `--include-passwords` | boolean | `false` | Also carry each named site's saved passwords, as part of this same session item (`cua keyvault import-passwords` keeps them as their own items instead, one per site). | | `--session-only` | boolean | `false` | Only session cookies: drop every persistent cookie. | | `--drop-long-lived` | boolean | `false` | Drop persistent cookies that expire more than 30 days out (long-lived refresh tokens), keeping short sessions. | **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-import-session" cua keyvault import-session --app chrome cua keyvault import-session --app chrome --site github.com --site example.com cua keyvault import-session --app firefox --profile "Profile 1" cua keyvault import-session --app slack ``` ## `cua keyvault requests` Requests waiting for your approval (site logins, teleports). ```text output cua keyvault requests [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-requests" cua keyvault requests cua keyvault requests --json ``` ## `cua keyvault approve` Approve a waiting request (the daemon asks for Touch ID or your login password). A site login is approved for one sign-in. ```text output cua keyvault approve [OPTIONS] <ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ID>` | string | required | The request id (`cua keyvault requests`). | **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-approve" cua keyvault approve 5f2c9a0e ``` ## `cua keyvault deny` Decline a waiting request. ```text output cua keyvault deny [OPTIONS] <ID> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<ID>` | string | required | The request id. | **Examples** ```bash test="cli-shape" id="ref-cua-keyvault-deny" cua keyvault deny 5f2c9a0e ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua spacesd Talk to a cua-spacesd directly by URL. | Command | Description | | --- | --- | | [`cua spacesd caps`](#cua-spacesd-caps) | Print capabilities. | | [`cua spacesd exec`](#cua-spacesd-exec) | Run a command (argv, no shell). | | [`cua spacesd cp`](#cua-spacesd-cp) | Copy files: `cua spacesd cp <URL> local :remote` uploads, `cua spacesd cp <URL> :remote local` downloads. | | [`cua spacesd shell`](#cua-spacesd-shell) | Interactive shell (PTY). | | [`cua spacesd targets`](#cua-spacesd-targets) | List streamable targets (displays, and windows with --windows). | | [`cua spacesd call`](#cua-spacesd-call) | Call any cua.env.v1 RPC with a proto3-JSON body. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Talk to a cua-spacesd directly by URL. ```text output cua spacesd [OPTIONS] <COMMAND> ``` ## `cua spacesd caps` Print capabilities. ```text output cua spacesd caps [OPTIONS] <URL> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<URL>` | string | required | spacesd URL (`http://host:port`). | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token. | **Examples** ```bash test="cli-shape" id="ref-cua-spacesd-caps" cua spacesd caps http://127.0.0.1:3211 ``` ## `cua spacesd exec` Run a command (argv, no shell). ```text output cua spacesd exec [OPTIONS] <URL> <COMMAND>... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<URL>` | string | required | spacesd URL (`http://host:port`). | | `<COMMAND>...` | string | required | Program and arguments. Repeatable. | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token. | **Examples** ```bash test="cli-shape" id="ref-cua-spacesd-exec" cua spacesd exec http://127.0.0.1:3211 uname -a ``` ## `cua spacesd cp` Copy files: `cua spacesd cp <URL> local :remote` uploads, `cua spacesd cp <URL> :remote local` downloads. ```text output cua spacesd cp [OPTIONS] <URL> <SRC> <DST> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<URL>` | string | required | spacesd URL (`http://host:port`). | | `<SRC>` | string | required | Source: a local path, or `:/guest/path`. | | `<DST>` | string | required | Destination: a local path, or `:/guest/path`. | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token. | **Examples** ```bash test="cli-shape" id="ref-cua-spacesd-cp" # Upload cua spacesd cp http://127.0.0.1:3211 ./notes.txt :/tmp/notes.txt # Download cua spacesd cp http://127.0.0.1:3211 :/tmp/notes.txt ./notes.txt ``` ## `cua spacesd shell` Interactive shell (PTY). ```text output cua spacesd shell [OPTIONS] <URL> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<URL>` | string | required | spacesd URL (`http://host:port`). | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token. | **Examples** ```bash test="cli-shape" id="ref-cua-spacesd-shell" cua spacesd shell http://127.0.0.1:3211 ``` ## `cua spacesd targets` List streamable targets (displays, and windows with --windows). ```text output cua spacesd targets [OPTIONS] <URL> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<URL>` | string | required | spacesd URL (`http://host:port`). | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token. | | `--windows` | boolean | `false` | | Include window targets. | **Examples** ```bash test="cli-shape" id="ref-cua-spacesd-targets" cua spacesd targets http://127.0.0.1:3211 --windows ``` ## `cua spacesd call` Call any cua.env.v1 RPC with a proto3-JSON body. ```text output cua spacesd call [OPTIONS] <URL> <METHOD> [BODY] ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<URL>` | string | required | spacesd URL (`http://host:port`). | | `<METHOD>` | string | required | `Service/Method`. | | `<BODY>` | string | `{}` | Request body (proto3 JSON). | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token. | **Examples** ```bash test="cli-shape" id="ref-cua-spacesd-call" cua spacesd call http://127.0.0.1:3211 SystemService/GetCapabilities ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua daemon Start, stop and inspect the cua daemon. | Command | Description | | --- | --- | | [`cua daemon start`](#cua-daemon-start) | Start the daemon (in the background unless --foreground). | | [`cua daemon stop`](#cua-daemon-stop) | Stop the daemon. | | [`cua daemon status`](#cua-daemon-status) | Show daemon status. | | [`cua daemon mcp`](#cua-daemon-mcp) | Stdio MCP server backed by the daemon (started if needed): the Spaces contract tools plus the sandbox/computer/skills tools (same server as `cua mcp`). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). The cua daemon. ```text output cua daemon [OPTIONS] <COMMAND> ``` ## `cua daemon start` Start the daemon (in the background unless --foreground). ```text output cua daemon start [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--socket` | path | | Unix socket (default ~/.cua/cua.sock). | | `--loopback` | string | `127.0.0.1:0` | Loopback address (default 127.0.0.1:0; "off" disables). | | `--foreground` | boolean | `false` | Stay in the foreground. | **Examples** ```bash test="cli-shape" id="ref-cua-daemon-start" cua daemon start # In the foreground, for a service manager cua daemon start --foreground ``` ## `cua daemon stop` Stop the daemon. ```text output cua daemon stop [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-daemon-stop" cua daemon stop ``` ## `cua daemon status` Show daemon status. ```text output cua daemon status [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-daemon-status" cua daemon status ``` ## `cua daemon mcp` Stdio MCP server backed by the daemon (started if needed): the Spaces contract tools plus the sandbox/computer/skills tools (same server as `cua mcp`). ```text output cua daemon mcp [OPTIONS] ``` | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--permissions` | string | | `CUA_MCP_PERMISSIONS` | Comma-separated permissions or groups (default: all). | | `--sandbox` | string | | `CUA_SANDBOX` | Default sandbox for computer tools. | **Examples** ```bash test="cli-shape" id="ref-cua-daemon-mcp" cua daemon mcp cua daemon mcp --permissions spaces:all ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua cache See and reclaim the disk used by images, sandboxes, builds and logs. | Command | Description | | --- | --- | | [`cua cache ls`](#cua-cache-ls) | List what the SDK keeps on disk, one row per image, sandbox, build output, install archive or log. | | [`cua cache du`](#cua-cache-du) | Disk usage per category, the cache budget and free space. | | [`cua cache prune`](#cua-cache-prune) | Reap orphaned ephemeral sandboxes, remove orphans (abandoned downloads, unused layers) and evict least-recently-used cache down to the budget. | | [`cua cache config`](#cua-cache-config) | Show or set the cache settings (`$CUA_HOME/cache.json`; the CUA_CACHE_BUDGET, CUA_DISK_MIN_FREE, CUA_DISK_WARN_FREE and CUA_ORPHAN_REAP_MINUTES environment variables win). | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Disk used by images, sandboxes, builds and logs, and its cleanup. ```text output cua cache [OPTIONS] <COMMAND> ``` ## `cua cache ls` List what the SDK keeps on disk, one row per image, sandbox, build output, install archive or log. ```text output cua cache ls [OPTIONS] ``` Alias: `cua cache list`. | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--category` | string | | Only this category (images, docker-images, lume-bases, builds, installables, icons, sandboxes, checkpoints, logs, data, legacy). | **Examples** ```bash test="cli-shape" id="ref-cua-cache-ls" cua cache ls # Only cached images cua cache ls --category images ``` ## `cua cache du` Disk usage per category, the cache budget and free space. ```text output cua cache du [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-cache-du" cua cache du cua cache du --json ``` ## `cua cache prune` Reap orphaned ephemeral sandboxes, remove orphans (abandoned downloads, unused layers) and evict least-recently-used cache down to the budget. Never removes a sandbox or anything one uses. ```text output cua cache prune [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--budget` | string | | Budget for this run (`auto`, `off` or a size such as `20G`). | | `--all` | boolean | `false` | Evict every unused cache entry, not only down to the budget. | | `--dry-run` | boolean | `false` | Show what would be removed; remove nothing. | **Examples** ```bash test="cli-shape" id="ref-cua-cache-prune" # See what a cleanup would remove cua cache prune --dry-run # Evict every unused cache entry cua cache prune --all ``` ## `cua cache config` Show or set the cache settings (`$CUA_HOME/cache.json`; the CUA_CACHE_BUDGET, CUA_DISK_MIN_FREE, CUA_DISK_WARN_FREE and CUA_ORPHAN_REAP_MINUTES environment variables win). ```text output cua cache config [OPTIONS] ``` | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--budget` | string | | Cache budget: `auto` (min of 30 GiB and 10% of free space plus cache), `off`, or a size. | | `--min-free` | string | | Free space pulls, builds and VM creates always leave (size). | | `--warn-free` | string | | Warn when free space drops under this (size). | | `--orphan-reap-minutes` | integer | | Minutes before an ephemeral sandbox whose process died is reaped. | | `--auto-gc` | `true` \| `false` | | Clean up automatically after pulls and builds and on daemon idle. | **Examples** ```bash test="cli-shape" id="ref-cua-cache-config" # Show the settings cua cache config # Cap the cache at 20 GB and keep 10 GB free cua cache config --budget 20G --min-free 10G ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua runtime Inspect and provision local runtimes. | Command | Description | | --- | --- | | [`cua runtime doctor`](#cua-runtime-doctor) | Inspect local runtimes (read-only). | | [`cua runtime setup`](#cua-runtime-setup) | Provision local runtimes. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Local runtimes (cua-vmm). ```text output cua runtime [OPTIONS] <COMMAND> ``` ## `cua runtime doctor` Inspect local runtimes (read-only). ```text output cua runtime doctor [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-runtime-doctor" cua runtime doctor ``` ## `cua runtime setup` Provision local runtimes. ```text output cua runtime setup [OPTIONS] [COMPONENTS]... ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<COMPONENTS>...` | string | optional | Components: `qemu`, `lume`, `runsc` (default: everything provisionable). Repeatable. | | Flag | Type | Default | Description | | --- | --- | --- | --- | | `--dry-run` | boolean | `false` | Only report what would be done. | **Examples** ```bash test="cli-shape" id="ref-cua-runtime-setup" # See what would be installed cua runtime setup --dry-run cua runtime setup qemu ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua doctor Check the host, an image and a sandbox guest, or eval parity between image variants. Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Check the host, an image and a sandbox's guest in one report (`cua doctor [REF]`), or eval parity between two variants (`cua doctor parity A B`). ```text output cua doctor [OPTIONS] [TARGET] cua doctor <COMMAND> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<TARGET>` | string | optional | Sandbox to check: `local:<name>`, `cloud:<name>`, `direct:<host:port>`, a bare name, or a spacesd URL. Omit for the host checks only. | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--image` | string | | | Image to resolve for the image checks (default: the sandbox's). | | `--arch` | string | | | Architecture to resolve the image for (default: the sandbox's, else this host's). | | `--format` | `human` \| `json` \| `junit` | `human` | | Output format (`--json` selects json). | | `--effects` | `none` \| `virtual` | `none` | | Which guest checks may act on the guest. | | `--only` | string | | | Guest check ids or groups to run (comma separated). Repeatable. | | `--skip` | string | | | Guest check ids or groups to skip. Repeatable. | | `--timeout` | integer | `240` | | Guest budget in seconds. | | `--token` | string | | `CUA_ENV_TOKEN` | spacesd token for a URL target (default: CUA_ENV_TOKEN). | | `--shim` | string | | | Check an image without cua-spacesd through its own API instead: `computer-server=<url>`, `osworld=<url>` or `mcp=<url>`. | | `--expect-manifest` | path | | | The image's claims (manifest JSON), for the shim or to override the guest's own. | | `--out` | path | | | Also write the JSON report here. | | `--junit` | path | | | Also write JUnit XML here. | | `--expect` | string | | | Fail unless the build running in the guest is the expected one: COMPONENT=WANT with WANT `sha256:<hex>` (the executable), `git:<sha>` or a version (repeatable). Components: cua-spacesd, cua-driver, or an overlay name. `cua sb create --overlay` prints the sha256 to pass. | | `--strict` | boolean | `false` | | Fail on warnings and on required checks that were skipped. | | `--no-host` | boolean | `false` | | Skip the host checks. | **Examples** ```bash test="cli-shape" id="ref-cua-doctor" # Host checks only cua doctor # Host, image and guest checks for the sandbox dev cua doctor dev # CI: fail on warnings, write JUnit XML cua doctor cloud:dev --strict --junit doctor.xml ``` ## `cua doctor parity` Eval parity between two variants of one image (rootfs vs containerDisk): doctor on both, fidelity diff, task outcomes. ```text output cua doctor parity [OPTIONS] <A> <B> ``` | Argument | Type | Default | Description | | --- | --- | --- | --- | | `<A>` | string | required | First variant (a sandbox ref or a spacesd URL). | | `<B>` | string | required | Second variant. | | Flag | Type | Default | Env var | Description | | --- | --- | --- | --- | --- | | `--token` | string | | `CUA_ENV_TOKEN` | Token for URL targets (both), default CUA_ENV_TOKEN. | | `--token-b` | string | | | Token for B when it differs from A's. | | `--tasks` | string | `builtin:form,builtin:file` | | Tasks (`builtin:form`, `builtin:file`), comma separated. Repeatable. | | `--allow-diff` | string | | | Extra fidelity keys allowed to differ. Repeatable. | | `--out` | path | | | Write parity.json here. | | `--timeout` | integer | `240` | | Doctor budget per variant, seconds. | **Examples** ```bash test="cli-shape" id="ref-cua-doctor-parity" cua doctor parity local:linux-rootfs local:linux-vm cua doctor parity local:linux-rootfs local:linux-vm --out parity.json ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # cua telemetry Anonymous usage telemetry (status, off, show-last). | Command | Description | | --- | --- | | [`cua telemetry status`](#cua-telemetry-status) | Whether telemetry is on, why, and the exact properties every event carries. | | [`cua telemetry on`](#cua-telemetry-on) | Turn usage telemetry on (writes [telemetry] enabled = \"on\" to ~/.cua/config.toml). | | [`cua telemetry off`](#cua-telemetry-off) | Turn usage telemetry off (same as `cua config set telemetry off`). | | [`cua telemetry show-last`](#cua-telemetry-show-last) | Print the last events this machine queued or sent, exactly as sent. | | [`cua telemetry reset-id`](#cua-telemetry-reset-id) | Delete the anonymous install id and salt (new ones are created on the next event). | | [`cua telemetry schema`](#cua-telemetry-schema) | Every event and property that may be sent, with its allowed values. | Every command also accepts the [global options](</docs/cua-cli/reference/cli#global-options>). Anonymous usage telemetry (status, on, off, show-last, reset-id, schema). What is sent: https://cua.ai/docs/cua-sdk/concepts/telemetry. ```text output cua telemetry [OPTIONS] <COMMAND> ``` **Examples** ```bash test="cli-shape" id="ref-cua-telemetry" cua telemetry status cua telemetry off cua telemetry show-last ``` ## `cua telemetry status` Whether telemetry is on, why, and the exact properties every event carries. ```text output cua telemetry status [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-telemetry-status" cua telemetry status cua telemetry status --json ``` ## `cua telemetry on` Turn usage telemetry on (writes [telemetry] enabled = \"on\" to ~/.cua/config.toml). ```text output cua telemetry on [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-telemetry-on" cua telemetry on ``` ## `cua telemetry off` Turn usage telemetry off (same as `cua config set telemetry off`). ```text output cua telemetry off [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-telemetry-off" cua telemetry off ``` ## `cua telemetry show-last` Print the last events this machine queued or sent, exactly as sent. ```text output cua telemetry show-last [OPTIONS] ``` | Flag | Short | Type | Default | Description | | --- | --- | --- | --- | --- | | `--limit` | `-n` | integer | `20` | How many. | **Examples** ```bash test="cli-shape" id="ref-cua-telemetry-show-last" cua telemetry show-last cua telemetry show-last -n 5 --json ``` ## `cua telemetry reset-id` Delete the anonymous install id and salt (new ones are created on the next event). ```text output cua telemetry reset-id [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-telemetry-reset-id" cua telemetry reset-id ``` ## `cua telemetry schema` Every event and property that may be sent, with its allowed values. ```text output cua telemetry schema [OPTIONS] ``` **Examples** ```bash test="cli-shape" id="ref-cua-telemetry-schema" cua telemetry schema --json ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success. | | `1` | Failure, or `cua do` reported an error. | | `2` | Invalid argument, or an ambiguous sandbox name (qualify it: `local:NAME`, `cloud:NAME`). | | `3` | Not found: sandbox, window, skill or image (or an image not published yet). | | `4` | Not supported, or not configured (for example no Fleet credentials). | | `5` | No cua-spacesd answered, or a transport failure. | | `6` | Unauthenticated or permission denied (by Cua, Fleet or your cloud account). | | `7` | Not enough free disk space (see `cua cache`). | | `130` | Cancelled (Ctrl-C during a create): what it made was removed. | --- # All tools Every tool the cua MCP server exposes, with its permission and parameters. `cua mcp` (and `cua daemon mcp`, the same server backed by the cua daemon) serves these tools over stdio. See [MCP server](</docs/cua-cli/guides/mcp-server>) for client setup. Contract version 0.7.0; MCP protocol revision 2025-06-18. ## Spaces tools The Spaces contract: register, claim and release Spaces, run commands, move files, stream, run agents and teleport. [Reference](</docs/cua-cli/reference/mcp-tools/spaces>). | Tool | Description | | --- | --- | | [`add_space`](</docs/spaces/reference/lifecycle#add_space>) | Add an existing machine as a Space by address and token. | | [`remove_space`](</docs/spaces/reference/lifecycle#remove_space>) | Forget a registered Space without touching it. | | [`list_spaces`](</docs/spaces/reference/lifecycle#list_spaces>) | List the Spaces this connection can see. | | [`create_space`](</docs/spaces/reference/lifecycle#create_space>) | Create a Space: a new sandbox on this machine, on one of your machines, or in the cloud. | | [`delete_space`](</docs/spaces/reference/lifecycle#delete_space>) | Delete a Space's sandbox and forget it. | | [`stop_space`](</docs/spaces/reference/lifecycle#stop_space>) | Turn a Space off: suspend it (memory kept) or stop it (disk kept). | | [`start_space`](</docs/spaces/reference/lifecycle#start_space>) | Turn a Space back on: resume or boot it. | | [`space_bash`](</docs/spaces/reference/commands-and-files#space_bash>) | Run a shell command inside the Space. | | [`space_write`](</docs/spaces/reference/commands-and-files#space_write>) | Write literal text to a path inside the Space. | | [`upload`](</docs/spaces/reference/commands-and-files#upload>) | Send a local file or folder into the Space. | | [`send_file`](</docs/spaces/reference/commands-and-files#send_file>) | Send a host file or folder into the Space's Downloads, verified by sha256 on arrival. | | [`download`](</docs/spaces/reference/commands-and-files#download>) | Fetch a file or folder out of the Space. | | [`stream_endpoint`](</docs/spaces/reference/streams-and-display#stream_endpoint>) | Return a ticketed media endpoint for the Space's desktop or one window. | | [`list_space_windows`](</docs/spaces/reference/streams-and-display#list_space_windows>) | List the windows open inside the Space. | | [`stream_space_window`](</docs/spaces/reference/streams-and-display#stream_space_window>) | Draw one Space window on the operator's desktop. | | [`show_space_pip`](</docs/spaces/reference/streams-and-display#show_space_pip>) | Pin the Space as picture-in-picture on the operator's desktop. | | [`hide_space_pip`](</docs/spaces/reference/streams-and-display#hide_space_pip>) | Unpin the picture-in-picture window. | | [`open_space_viewer`](</docs/spaces/reference/streams-and-display#open_space_viewer>) | Open the full Space viewer on the operator's desktop. | | [`list_tools`](</docs/spaces/reference/services#list_tools>) | List the MCP services and tools reachable inside the Space. | | [`call_tool`](</docs/spaces/reference/services#call_tool>) | Call a service tool inside the Space. | | [`agent_start`](</docs/spaces/reference/agents#agent_start>) | Start a coding agent in the Space. | | [`agent_message`](</docs/spaces/reference/agents#agent_message>) | Send a follow-up to an agent run. | | [`agent_events`](</docs/spaces/reference/agents#agent_events>) | Read an agent run's events after a cursor. | | [`agent_status`](</docs/spaces/reference/agents#agent_status>) | Read an agent run's status and result. | | [`agent_interrupt`](</docs/spaces/reference/agents#agent_interrupt>) | Interrupt an agent run's current turn. | | [`agent_stop`](</docs/spaces/reference/agents#agent_stop>) | Stop an agent run. | | [`agent_list`](</docs/spaces/reference/agents#agent_list>) | List agent runs in the Space. | | [`agent_capabilities`](</docs/spaces/reference/agents#agent_capabilities>) | Report what the agent harnesses support. | | [`persistent_agent_create`](</docs/spaces/reference/persistent-agents#persistent_agent_create>) | Create a named agent whose memory outlives its Space. | | [`persistent_agent_list`](</docs/spaces/reference/persistent-agents#persistent_agent_list>) | List persistent agents. | | [`persistent_agent_remove`](</docs/spaces/reference/persistent-agents#persistent_agent_remove>) | Forget a persistent agent. | | [`persistent_agent_send`](</docs/spaces/reference/persistent-agents#persistent_agent_send>) | Give a persistent agent a turn. | | [`persistent_agent_save`](</docs/spaces/reference/persistent-agents#persistent_agent_save>) | Save a persistent agent's home now. | | [`agent_pause`](</docs/spaces/reference/persistent-agents#agent_pause>) | Pause a persistent agent: its run, its routines and its Space. | | [`agent_resume`](</docs/spaces/reference/persistent-agents#agent_resume>) | Resume a paused persistent agent. | | [`routine_add`](</docs/spaces/reference/persistent-agents#routine_add>) | Schedule a recurring turn for a persistent agent. | | [`routine_list`](</docs/spaces/reference/persistent-agents#routine_list>) | List routines. | | [`routine_remove`](</docs/spaces/reference/persistent-agents#routine_remove>) | Delete a routine. | | [`routine_set_enabled`](</docs/spaces/reference/persistent-agents#routine_set_enabled>) | Turn a routine on or off. | | [`notify_user`](</docs/spaces/reference/persistent-agents#notify_user>) | Tell the user something in the Cua app. | | [`notifications_list`](</docs/spaces/reference/persistent-agents#notifications_list>) | Read the notifications feed. | | [`notifications_ack`](</docs/spaces/reference/persistent-agents#notifications_ack>) | Mark notifications read. | | [`computer_access_grant`](</docs/spaces/reference/persistent-agents#computer_access_grant>) | Let a persistent agent use one of the user's computers. | | [`computer_access_revoke`](</docs/spaces/reference/persistent-agents#computer_access_revoke>) | Take back a persistent agent's access to a computer. | | [`computer_access_list`](</docs/spaces/reference/persistent-agents#computer_access_list>) | List per-agent computer access. | | [`teleport_manifest`](</docs/spaces/reference/teleport#teleport_manifest>) | Describe what a session teleport would move. | | [`teleport_app`](</docs/spaces/reference/teleport#teleport_app>) | Move an app session into the Space. | | [`request_site_login`](</docs/spaces/reference/keyvault#request_site_login>) | Sign in to a site in the Space with the user's saved password. | | [`hotspot_start`](</docs/spaces/reference/hotspot#hotspot_start>) | Start sharing this machine's network with a Space. | | [`hotspot_stop`](</docs/spaces/reference/hotspot#hotspot_stop>) | Stop sharing this machine's network. | | [`hotspot_status`](</docs/spaces/reference/hotspot#hotspot_status>) | Report which Spaces are sharing this machine's network, with counters. | | [`volume_ls`](</docs/spaces/reference/volume#volume_ls>) | List a folder of the Cua Volume. | | [`volume_read`](</docs/spaces/reference/volume#volume_read>) | Read a file from the Cua Volume. | | [`volume_write`](</docs/spaces/reference/volume#volume_write>) | Write a file to the Cua Volume. | | [`volume_delete`](</docs/spaces/reference/volume#volume_delete>) | Delete a file from the Cua Volume. | | [`volume_history`](</docs/spaces/reference/volume#volume_history>) | List a Cua Volume file's versions. | | [`volume_restore`](</docs/spaces/reference/volume#volume_restore>) | Restore an old version of a Cua Volume file. | | [`volume_grant`](</docs/spaces/reference/volume#volume_grant>) | Grant an agent or Space more Cua Volume access. | | [`volume_revoke`](</docs/spaces/reference/volume#volume_revoke>) | Revoke a Cua Volume grant. | | [`volume_grants`](</docs/spaces/reference/volume#volume_grants>) | List Cua Volume grants. | | [`volume_request_access`](</docs/spaces/reference/volume#volume_request_access>) | Ask the user for more Cua Volume access. | | [`volume_requests`](</docs/spaces/reference/volume#volume_requests>) | List Cua Volume access requests waiting for the user. | | [`volume_approve`](</docs/spaces/reference/volume#volume_approve>) | Approve a Cua Volume access request. | | [`volume_deny`](</docs/spaces/reference/volume#volume_deny>) | Decline a Cua Volume access request. | | [`volume_audit`](</docs/spaces/reference/volume#volume_audit>) | Read the Cua Volume audit log. | | [`volume_storage`](</docs/spaces/reference/volume#volume_storage>) | Show where the Cua Volume keeps its bytes. | | [`volume_storage_set`](</docs/spaces/reference/volume#volume_storage_set>) | Test or change where the Cua Volume keeps its bytes. | | [`volume_mount_status`](</docs/spaces/reference/volume#volume_mount_status>) | Show whether the Cua Volume is mounted as a volume. | | [`volume_mount`](</docs/spaces/reference/volume#volume_mount>) | Mount the Cua Volume as a volume. | | [`volume_unmount`](</docs/spaces/reference/volume#volume_unmount>) | Unmount the Cua Volume volume. | | [`volume_sync_status`](</docs/spaces/reference/volume#volume_sync_status>) | Show the Cua Volume's sync state across devices. | | [`volume_sync_events`](</docs/spaces/reference/volume#volume_sync_events>) | Wait for Cua Volume sync events. | | [`volume_sync_resolve`](</docs/spaces/reference/volume#volume_sync_resolve>) | Mark a Cua Volume sync conflict as seen. | | [`volume_cache_stats`](</docs/spaces/reference/volume#volume_cache_stats>) | Show the Cua Volume block cache. | | [`volume_cache_set`](</docs/spaces/reference/volume#volume_cache_set>) | Set the Cua Volume block cache's size cap. | | [`volume_cache_clear`](</docs/spaces/reference/volume#volume_cache_clear>) | Clear the Cua Volume block cache. | | [`share_space`](</docs/spaces/reference/sharing#share_space>) | Let another cua.ai account watch or edit a Space. | | [`unshare_space`](</docs/spaces/reference/sharing#unshare_space>) | Stop sharing a Space with an account, or with everyone. | | [`space_shares`](</docs/spaces/reference/sharing#space_shares>) | List who a Space is shared with. | | [`relay_register_space`](</docs/spaces/reference/sharing#relay_register_space>) | Publish a Space on the relay for the account's other devices. | | [`relay_unregister_space`](</docs/spaces/reference/sharing#relay_unregister_space>) | Take a Space off the relay. | | [`cloud_status`](</docs/spaces/reference/cloud#cloud_status>) | List your clouds (AWS, Google Cloud, Modal): found credentials, the connected account and region, what each can run at what hourly cost, and what Cua created there. | | [`cloud_connect`](</docs/spaces/reference/cloud#cloud_connect>) | Connect a cloud account Spaces can be created in. | | [`cloud_test`](</docs/spaces/reference/cloud#cloud_test>) | Check a cloud account without creating anything. | | [`cloud_disconnect`](</docs/spaces/reference/cloud#cloud_disconnect>) | Forget a connected cloud account. | | [`cloud_sweep`](</docs/spaces/reference/cloud#cloud_sweep>) | Find and delete what Cua left in your clouds. | ## Image tools The sandbox image catalog: which images exist and which ship a browser. [Reference](</docs/cua-cli/reference/mcp-tools/images>). | Tool | Description | | --- | --- | | [`images_list`](</docs/cua-cli/reference/mcp-tools/images#images_list>) | List the sandbox image catalog (the images the docs list): ref, OS, container or VM, local and cloud runtime, whether it ships cua-spacesd and which browsers, its other variants, and the command that creates it. | ## Sandbox tools Create and manage sandboxes, local or in the cloud. [Reference](</docs/cua-cli/reference/mcp-tools/sandbox>). | Tool | Description | | --- | --- | | [`sandbox_list`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_list>) | List sandboxes: local, direct and cloud (Fleet) ones by default, each row with location (local, cloud or direct). | | [`sandbox_create`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_create>) | Create a sandbox from any image. | | [`sandbox_get`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_get>) | Get details for a sandbox. | | [`sandbox_start`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_start>) | Start (resume) a sandbox. | | [`sandbox_stop`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_stop>) | Stop (suspend) a sandbox. | | [`sandbox_restart`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_restart>) | Restart a sandbox. | | [`sandbox_suspend`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_suspend>) | Suspend a sandbox. | | [`sandbox_delete`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_delete>) | Delete a sandbox. | | [`sandbox_view`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_view>) | Get a browser link to the sandbox desktop in the cua-spacesd HTML5 viewer (video, audio, input, clipboard, files). | | [`sandbox_vnc`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_vnc>) | Deprecated: use sandbox_view. | | [`sandbox_open_browser`](</docs/cua-cli/reference/mcp-tools/sandbox#sandbox_open_browser>) | Launch Chromium with a fresh throwaway profile inside an existing sandbox (an image with cua-spacesd and Chromium, see images_list), bind it and optionally open a URL. | ## Teleport tools Move a signed-in browser session, per site, into a sandbox through the Cua Keyvault, with consent. [Reference](</docs/cua-cli/reference/mcp-tools/teleport>). | Tool | Description | | --- | --- | | [`teleport_browser_session`](</docs/cua-cli/reference/mcp-tools/teleport#teleport_browser_session>) | Teleport the user's signed-in session for specific sites (for example github.com) from their Firefox or Chrome into a sandbox, through the Cua Keyvault. | ## Computer tools Screenshots, input, windows, files and shell on a sandbox desktop. [Reference](</docs/cua-cli/reference/mcp-tools/computer>). | Tool | Description | | --- | --- | | [`computer_screenshot`](</docs/cua-cli/reference/mcp-tools/computer#computer_screenshot>) | Take a screenshot (long edge at most 1200 px). | | [`computer_click`](</docs/cua-cli/reference/mcp-tools/computer#computer_click>) | Click at screenshot coordinates. | | [`computer_double_click`](</docs/cua-cli/reference/mcp-tools/computer#computer_double_click>) | Double-click at screenshot coordinates. | | [`computer_move_cursor`](</docs/cua-cli/reference/mcp-tools/computer#computer_move_cursor>) | Move the cursor. | | [`computer_mouse_down`](</docs/cua-cli/reference/mcp-tools/computer#computer_mouse_down>) | Press a mouse button. | | [`computer_mouse_up`](</docs/cua-cli/reference/mcp-tools/computer#computer_mouse_up>) | Release a mouse button. | | [`computer_type`](</docs/cua-cli/reference/mcp-tools/computer#computer_type>) | Type text. | | [`computer_key`](</docs/cua-cli/reference/mcp-tools/computer#computer_key>) | Press a key (enter, escape, tab, a, ...). | | [`computer_key_down`](</docs/cua-cli/reference/mcp-tools/computer#computer_key_down>) | Hold a key. | | [`computer_key_up`](</docs/cua-cli/reference/mcp-tools/computer#computer_key_up>) | Release a key. | | [`computer_hotkey`](</docs/cua-cli/reference/mcp-tools/computer#computer_hotkey>) | Press a shortcut such as ctrl+c or cmd+shift+s. | | [`computer_scroll`](</docs/cua-cli/reference/mcp-tools/computer#computer_scroll>) | Scroll up, down, left or right. | | [`computer_drag`](</docs/cua-cli/reference/mcp-tools/computer#computer_drag>) | Drag between two points. | | [`computer_clipboard_get`](</docs/cua-cli/reference/mcp-tools/computer#computer_clipboard_get>) | Read clipboard text. | | [`computer_clipboard_set`](</docs/cua-cli/reference/mcp-tools/computer#computer_clipboard_set>) | Set clipboard text. | | [`computer_file_read`](</docs/cua-cli/reference/mcp-tools/computer#computer_file_read>) | Read a text file. | | [`computer_file_write`](</docs/cua-cli/reference/mcp-tools/computer#computer_file_write>) | Write a text file (the Spaces `space_write` implementation, on this sandbox). | | [`computer_file_list`](</docs/cua-cli/reference/mcp-tools/computer#computer_file_list>) | List a directory. | | [`computer_shell`](</docs/cua-cli/reference/mcp-tools/computer#computer_shell>) | Run a shell command (sh -c, 120 s timeout; the Spaces `space_bash` implementation, on this sandbox). | | [`computer_window_list`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_list>) | List windows. | | [`computer_window_open`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_open>) | Open a file or URL. | | [`computer_window_focus`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_focus>) | Focus a window. | | [`computer_window_unfocus`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_unfocus>) | Remove focus from the current window (Escape). | | [`computer_window_minimize`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_minimize>) | Minimize a window. | | [`computer_window_maximize`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_maximize>) | Maximize a window. | | [`computer_window_close`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_close>) | Close a window. | | [`computer_window_resize`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_resize>) | Resize a window. | | [`computer_window_move`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_move>) | Move a window. | | [`computer_window_get_info`](</docs/cua-cli/reference/mcp-tools/computer#computer_window_get_info>) | Get a window's title, position and size. | | [`computer_launch`](</docs/cua-cli/reference/mcp-tools/computer#computer_launch>) | Launch an application. | | [`computer_get_screen_size`](</docs/cua-cli/reference/mcp-tools/computer#computer_get_screen_size>) | Primary display size in points. | | [`computer_get_cursor_position`](</docs/cua-cli/reference/mcp-tools/computer#computer_get_cursor_position>) | Cursor position in points. | | [`computer_get_current_window`](</docs/cua-cli/reference/mcp-tools/computer#computer_get_current_window>) | The focused window's id and title. | | [`computer_get_accessibility_tree`](</docs/cua-cli/reference/mcp-tools/computer#computer_get_accessibility_tree>) | Accessibility tree of the focused (or given) window. | | [`computer_accessibility_act`](</docs/cua-cli/reference/mcp-tools/computer#computer_accessibility_act>) | Act on an accessibility element (press, focus, set_value, ...). | ## Skills tools List, read, record and delete skills (recorded demonstrations). [Reference](</docs/cua-cli/reference/mcp-tools/skills>). | Tool | Description | | --- | --- | | [`skills_list`](</docs/cua-cli/reference/mcp-tools/skills#skills_list>) | List recorded skills. | | [`skills_read`](</docs/cua-cli/reference/mcp-tools/skills#skills_read>) | Read a skill and its steps. | | [`skills_delete`](</docs/cua-cli/reference/mcp-tools/skills#skills_delete>) | Delete a skill. | | [`skills_record`](</docs/cua-cli/reference/mcp-tools/skills#skills_record>) | How to record a skill (recording is interactive: cua skills record). | ## Permissions Tools are gated by `--permissions` (or `CUA_MCP_PERMISSIONS`), a comma-separated list of permissions and groups; the default is `all`. Each group has an `all` and a `readonly` form (`sandbox:readonly`); each tool lists its own permission. | Group | Grants | `:readonly` grants | | --- | --- | --- | | `computer:all` | 12 permissions | `computer:screenshot` | | `images:all` | `images:list` | `images:list` | | `sandbox:all` | 10 permissions | `sandbox:list`, `sandbox:get` | | `skills:all` | `skills:list`, `skills:read`, `skills:record`, `skills:delete` | `skills:list`, `skills:read` | | `spaces:all` | 86 permissions | 28 permissions | | `teleport:all` | `teleport:browser` | none | --- # Spaces tools The Spaces contract: register, claim and release Spaces, run commands, move files, stream, run agents and teleport. These tools are the [Spaces contract](</docs/spaces/reference>), with the same names and schemas on every surface. Each links to its entry there (parameters, result, errors, providers, metering); `cua mcp` gates each one behind the permission `spaces:<tool>`. ## Space lifecycle Full entries: [Space lifecycle](</docs/spaces/reference/lifecycle>). | Tool | Description | | --- | --- | | [`add_space`](</docs/spaces/reference/lifecycle#add_space>) | Add an existing machine as a Space by address and token. | | [`remove_space`](</docs/spaces/reference/lifecycle#remove_space>) | Forget a registered Space without touching it. | | [`list_spaces`](</docs/spaces/reference/lifecycle#list_spaces>) | List the Spaces this connection can see. | | [`create_space`](</docs/spaces/reference/lifecycle#create_space>) | Create a Space: a new sandbox on this machine, on one of your machines, or in the cloud. | | [`delete_space`](</docs/spaces/reference/lifecycle#delete_space>) | Delete a Space's sandbox and forget it. | | [`stop_space`](</docs/spaces/reference/lifecycle#stop_space>) | Turn a Space off: suspend it (memory kept) or stop it (disk kept). | | [`start_space`](</docs/spaces/reference/lifecycle#start_space>) | Turn a Space back on: resume or boot it. | ## Commands and files Full entries: [Commands and files](</docs/spaces/reference/commands-and-files>). | Tool | Description | | --- | --- | | [`space_bash`](</docs/spaces/reference/commands-and-files#space_bash>) | Run a shell command inside the Space. | | [`space_write`](</docs/spaces/reference/commands-and-files#space_write>) | Write literal text to a path inside the Space. | | [`upload`](</docs/spaces/reference/commands-and-files#upload>) | Send a local file or folder into the Space. | | [`send_file`](</docs/spaces/reference/commands-and-files#send_file>) | Send a host file or folder into the Space's Downloads, verified by sha256 on arrival. | | [`download`](</docs/spaces/reference/commands-and-files#download>) | Fetch a file or folder out of the Space. | ## Streams and display Full entries: [Streams and display](</docs/spaces/reference/streams-and-display>). | Tool | Description | | --- | --- | | [`stream_endpoint`](</docs/spaces/reference/streams-and-display#stream_endpoint>) | Return a ticketed media endpoint for the Space's desktop or one window. | | [`list_space_windows`](</docs/spaces/reference/streams-and-display#list_space_windows>) | List the windows open inside the Space. | | [`stream_space_window`](</docs/spaces/reference/streams-and-display#stream_space_window>) | Draw one Space window on the operator's desktop. | | [`show_space_pip`](</docs/spaces/reference/streams-and-display#show_space_pip>) | Pin the Space as picture-in-picture on the operator's desktop. | | [`hide_space_pip`](</docs/spaces/reference/streams-and-display#hide_space_pip>) | Unpin the picture-in-picture window. | | [`open_space_viewer`](</docs/spaces/reference/streams-and-display#open_space_viewer>) | Open the full Space viewer on the operator's desktop. | ## Services Full entries: [Services](</docs/spaces/reference/services>). | Tool | Description | | --- | --- | | [`list_tools`](</docs/spaces/reference/services#list_tools>) | List the MCP services and tools reachable inside the Space. | | [`call_tool`](</docs/spaces/reference/services#call_tool>) | Call a service tool inside the Space. | ## Agents Full entries: [Agents](</docs/spaces/reference/agents>). | Tool | Description | | --- | --- | | [`agent_start`](</docs/spaces/reference/agents#agent_start>) | Start a coding agent in the Space. | | [`agent_message`](</docs/spaces/reference/agents#agent_message>) | Send a follow-up to an agent run. | | [`agent_events`](</docs/spaces/reference/agents#agent_events>) | Read an agent run's events after a cursor. | | [`agent_status`](</docs/spaces/reference/agents#agent_status>) | Read an agent run's status and result. | | [`agent_interrupt`](</docs/spaces/reference/agents#agent_interrupt>) | Interrupt an agent run's current turn. | | [`agent_stop`](</docs/spaces/reference/agents#agent_stop>) | Stop an agent run. | | [`agent_list`](</docs/spaces/reference/agents#agent_list>) | List agent runs in the Space. | | [`agent_capabilities`](</docs/spaces/reference/agents#agent_capabilities>) | Report what the agent harnesses support. | ## Persistent agents Full entries: [Persistent agents](</docs/spaces/reference/persistent-agents>). | Tool | Description | | --- | --- | | [`persistent_agent_create`](</docs/spaces/reference/persistent-agents#persistent_agent_create>) | Create a named agent whose memory outlives its Space. | | [`persistent_agent_list`](</docs/spaces/reference/persistent-agents#persistent_agent_list>) | List persistent agents. | | [`persistent_agent_remove`](</docs/spaces/reference/persistent-agents#persistent_agent_remove>) | Forget a persistent agent. | | [`persistent_agent_send`](</docs/spaces/reference/persistent-agents#persistent_agent_send>) | Give a persistent agent a turn. | | [`persistent_agent_save`](</docs/spaces/reference/persistent-agents#persistent_agent_save>) | Save a persistent agent's home now. | | [`agent_pause`](</docs/spaces/reference/persistent-agents#agent_pause>) | Pause a persistent agent: its run, its routines and its Space. | | [`agent_resume`](</docs/spaces/reference/persistent-agents#agent_resume>) | Resume a paused persistent agent. | | [`routine_add`](</docs/spaces/reference/persistent-agents#routine_add>) | Schedule a recurring turn for a persistent agent. | | [`routine_list`](</docs/spaces/reference/persistent-agents#routine_list>) | List routines. | | [`routine_remove`](</docs/spaces/reference/persistent-agents#routine_remove>) | Delete a routine. | | [`routine_set_enabled`](</docs/spaces/reference/persistent-agents#routine_set_enabled>) | Turn a routine on or off. | | [`notify_user`](</docs/spaces/reference/persistent-agents#notify_user>) | Tell the user something in the Cua app. | | [`notifications_list`](</docs/spaces/reference/persistent-agents#notifications_list>) | Read the notifications feed. | | [`notifications_ack`](</docs/spaces/reference/persistent-agents#notifications_ack>) | Mark notifications read. | | [`computer_access_grant`](</docs/spaces/reference/persistent-agents#computer_access_grant>) | Let a persistent agent use one of the user's computers. | | [`computer_access_revoke`](</docs/spaces/reference/persistent-agents#computer_access_revoke>) | Take back a persistent agent's access to a computer. | | [`computer_access_list`](</docs/spaces/reference/persistent-agents#computer_access_list>) | List per-agent computer access. | ## Session teleport Full entries: [Session teleport](</docs/spaces/reference/teleport>). | Tool | Description | | --- | --- | | [`teleport_manifest`](</docs/spaces/reference/teleport#teleport_manifest>) | Describe what a session teleport would move. | | [`teleport_app`](</docs/spaces/reference/teleport#teleport_app>) | Move an app session into the Space. | ## Keyvault Full entries: [Keyvault](</docs/spaces/reference/keyvault>). | Tool | Description | | --- | --- | | [`request_site_login`](</docs/spaces/reference/keyvault#request_site_login>) | Sign in to a site in the Space with the user's saved password. | ## Hotspot Full entries: [Hotspot](</docs/spaces/reference/hotspot>). | Tool | Description | | --- | --- | | [`hotspot_start`](</docs/spaces/reference/hotspot#hotspot_start>) | Start sharing this machine's network with a Space. | | [`hotspot_stop`](</docs/spaces/reference/hotspot#hotspot_stop>) | Stop sharing this machine's network. | | [`hotspot_status`](</docs/spaces/reference/hotspot#hotspot_status>) | Report which Spaces are sharing this machine's network, with counters. | ## Cua Volume Full entries: [Cua Volume](</docs/spaces/reference/volume>). | Tool | Description | | --- | --- | | [`volume_ls`](</docs/spaces/reference/volume#volume_ls>) | List a folder of the Cua Volume. | | [`volume_read`](</docs/spaces/reference/volume#volume_read>) | Read a file from the Cua Volume. | | [`volume_write`](</docs/spaces/reference/volume#volume_write>) | Write a file to the Cua Volume. | | [`volume_delete`](</docs/spaces/reference/volume#volume_delete>) | Delete a file from the Cua Volume. | | [`volume_history`](</docs/spaces/reference/volume#volume_history>) | List a Cua Volume file's versions. | | [`volume_restore`](</docs/spaces/reference/volume#volume_restore>) | Restore an old version of a Cua Volume file. | | [`volume_grant`](</docs/spaces/reference/volume#volume_grant>) | Grant an agent or Space more Cua Volume access. | | [`volume_revoke`](</docs/spaces/reference/volume#volume_revoke>) | Revoke a Cua Volume grant. | | [`volume_grants`](</docs/spaces/reference/volume#volume_grants>) | List Cua Volume grants. | | [`volume_request_access`](</docs/spaces/reference/volume#volume_request_access>) | Ask the user for more Cua Volume access. | | [`volume_requests`](</docs/spaces/reference/volume#volume_requests>) | List Cua Volume access requests waiting for the user. | | [`volume_approve`](</docs/spaces/reference/volume#volume_approve>) | Approve a Cua Volume access request. | | [`volume_deny`](</docs/spaces/reference/volume#volume_deny>) | Decline a Cua Volume access request. | | [`volume_audit`](</docs/spaces/reference/volume#volume_audit>) | Read the Cua Volume audit log. | | [`volume_storage`](</docs/spaces/reference/volume#volume_storage>) | Show where the Cua Volume keeps its bytes. | | [`volume_storage_set`](</docs/spaces/reference/volume#volume_storage_set>) | Test or change where the Cua Volume keeps its bytes. | | [`volume_mount_status`](</docs/spaces/reference/volume#volume_mount_status>) | Show whether the Cua Volume is mounted as a volume. | | [`volume_mount`](</docs/spaces/reference/volume#volume_mount>) | Mount the Cua Volume as a volume. | | [`volume_unmount`](</docs/spaces/reference/volume#volume_unmount>) | Unmount the Cua Volume volume. | | [`volume_sync_status`](</docs/spaces/reference/volume#volume_sync_status>) | Show the Cua Volume's sync state across devices. | | [`volume_sync_events`](</docs/spaces/reference/volume#volume_sync_events>) | Wait for Cua Volume sync events. | | [`volume_sync_resolve`](</docs/spaces/reference/volume#volume_sync_resolve>) | Mark a Cua Volume sync conflict as seen. | | [`volume_cache_stats`](</docs/spaces/reference/volume#volume_cache_stats>) | Show the Cua Volume block cache. | | [`volume_cache_set`](</docs/spaces/reference/volume#volume_cache_set>) | Set the Cua Volume block cache's size cap. | | [`volume_cache_clear`](</docs/spaces/reference/volume#volume_cache_clear>) | Clear the Cua Volume block cache. | ## Sharing Full entries: [Sharing](</docs/spaces/reference/sharing>). | Tool | Description | | --- | --- | | [`share_space`](</docs/spaces/reference/sharing#share_space>) | Let another cua.ai account watch or edit a Space. | | [`unshare_space`](</docs/spaces/reference/sharing#unshare_space>) | Stop sharing a Space with an account, or with everyone. | | [`space_shares`](</docs/spaces/reference/sharing#space_shares>) | List who a Space is shared with. | | [`relay_register_space`](</docs/spaces/reference/sharing#relay_register_space>) | Publish a Space on the relay for the account's other devices. | | [`relay_unregister_space`](</docs/spaces/reference/sharing#relay_unregister_space>) | Take a Space off the relay. | ## Your cloud Full entries: [Your cloud](</docs/spaces/reference/cloud>). | Tool | Description | | --- | --- | | [`cloud_status`](</docs/spaces/reference/cloud#cloud_status>) | List your clouds (AWS, Google Cloud, Modal): found credentials, the connected account and region, what each can run at what hourly cost, and what Cua created there. | | [`cloud_connect`](</docs/spaces/reference/cloud#cloud_connect>) | Connect a cloud account Spaces can be created in. | | [`cloud_test`](</docs/spaces/reference/cloud#cloud_test>) | Check a cloud account without creating anything. | | [`cloud_disconnect`](</docs/spaces/reference/cloud#cloud_disconnect>) | Forget a connected cloud account. | | [`cloud_sweep`](</docs/spaces/reference/cloud#cloud_sweep>) | Find and delete what Cua left in your clouds. | Served by `cua mcp`; see [MCP tools](</docs/cua-cli/reference/mcp-tools>) for every tool. --- # Image tools The sandbox image catalog: which images exist and which ship a browser. Served by `cua mcp`; see [MCP tools](</docs/cua-cli/reference/mcp-tools>) for every tool. ## `images_list` List the sandbox image catalog (the images the docs list): ref, OS, container or VM, local and cloud runtime, whether it ships cua-spacesd and which browsers, its other variants, and the command that creates it. browser_tools=true means the in-sandbox cua-driver browser tools can drive its browser. Call this first to choose an image. Permission: `images:list`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `all` | `boolean` | | Include unpublished entries (benchmark images). | | `browser` | `boolean` | | Only images whose browser the cua-driver browser tools drive. | | `os` | `string` | | Only this OS: linux, windows or macos. | --- # Sandbox tools Create and manage sandboxes, local or in the cloud. | Tool | Description | | --- | --- | | [`sandbox_list`](#sandbox_list) | List sandboxes: local, direct and cloud (Fleet) ones by default, each row with location (local, cloud or direct). | | [`sandbox_create`](#sandbox_create) | Create a sandbox from any image. | | [`sandbox_get`](#sandbox_get) | Get details for a sandbox. | | [`sandbox_start`](#sandbox_start) | Start (resume) a sandbox. | | [`sandbox_stop`](#sandbox_stop) | Stop (suspend) a sandbox. | | [`sandbox_restart`](#sandbox_restart) | Restart a sandbox. | | [`sandbox_suspend`](#sandbox_suspend) | Suspend a sandbox. | | [`sandbox_delete`](#sandbox_delete) | Delete a sandbox. | | [`sandbox_view`](#sandbox_view) | Get a browser link to the sandbox desktop in the cua-spacesd HTML5 viewer (video, audio, input, clipboard, files). | | [`sandbox_vnc`](#sandbox_vnc) | Deprecated: use sandbox_view. | | [`sandbox_open_browser`](#sandbox_open_browser) | Launch Chromium with a fresh throwaway profile inside an existing sandbox (an image with cua-spacesd and Chromium, see images_list), bind it and optionally open a URL. | Served by `cua mcp`; see [MCP tools](</docs/cua-cli/reference/mcp-tools>) for every tool. ## `sandbox_list` List sandboxes: local, direct and cloud (Fleet) ones by default, each row with location (local, cloud or direct). Pass location to list only one kind. If the cloud cannot be listed, the local rows come back with a warning. Permission: `sandbox:list`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `location` | `"local" \| "cloud" \| "direct"` | | Only sandboxes in this location: local, cloud or direct (default: all). | ## `sandbox_create` Create a sandbox from any image. `on` says where (local, the default unless the user configured another with `cua config set default.on`; or cloud), `kind` what (auto, container, vm) and `runtime` which engine (auto, or gvisor, runc, qemu, lume locally; gvisor, kubevirt in the cloud); an impossible combination fails and lists the valid values. To browse the web, pass browser=true (and optionally url): it starts the canonical Linux image, registers it as a Space, launches Chromium with a throwaway profile inside it and returns the space, session, target_id and tab_id to pass to call_tool with the in-sandbox browser tools (browser_navigate, get_browser_state, browser_click, browser_type). Pass command, env and services to run a server in it, for example an MCP server: services {"mcp": 8765}. Without browser=true it does not register a Space: to reach its MCP server with list_tools/call_tool (service="mcp"), use create_space instead (same arguments), or add this one with add_space (address local:<name>). Permission: `sandbox:create`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `browser` | `boolean` | | Start a browser in it, ready for the cua-driver browser tools (default image: the canonical Linux image). | | `command` | `string[]` | | Entrypoint override (argv), for example ["python", "-m", "my_mcp"]. | | `env` | `object` | | Environment variables (string values). | | `image` | `string` | | Image reference, any registry (overrides os_type). | | `kind` | `"auto" \| "container" \| "vm"` | | What kind of machine: auto (default; from the image), container or vm. | | `name` | `string` | | Sandbox name. | | `on` | `string` | | Where it runs: local or cloud (default: the user's default location, else local). | | `os_type` | `string` | | linux (default) or macos. | | `pool` | `string` | | Use this dedicated cloud pool (Fleets advanced; implies on=cloud). | | `runtime` | `string` | | Which engine: auto (default) or one the location offers for the kind (local: gvisor, runc, qemu, lume; cloud: gvisor, kubevirt). | | `services` | `object` | | Named services: name to guest port, for example {"mcp": 8765}; each is probed for readiness. | | `url` | `string` | | With browser=true: open this URL first. | ## `sandbox_get` Get details for a sandbox. Permission: `sandbox:get`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | **Example arguments** ```json {"name":"<name>"} ``` ## `sandbox_start` Start (resume) a sandbox. Permission: `sandbox:start`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | **Example arguments** ```json {"name":"<name>"} ``` ## `sandbox_stop` Stop (suspend) a sandbox. Permission: `sandbox:stop`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | **Example arguments** ```json {"name":"<name>"} ``` ## `sandbox_restart` Restart a sandbox. Permission: `sandbox:restart`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | **Example arguments** ```json {"name":"<name>"} ``` ## `sandbox_suspend` Suspend a sandbox. Permission: `sandbox:suspend`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | **Example arguments** ```json {"name":"<name>"} ``` ## `sandbox_delete` Delete a sandbox. Permission: `sandbox:delete`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | **Example arguments** ```json {"name":"<name>"} ``` ## `sandbox_view` Get a browser link to the sandbox desktop in the cua-spacesd HTML5 viewer (video, audio, input, clipboard, files). The link expires after an hour. Permission: `sandbox:view`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | **Example arguments** ```json {"name":"<name>"} ``` ## `sandbox_vnc` Deprecated: use sandbox_view. Returns the same viewer link. Permission: `sandbox:view`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | **Example arguments** ```json {"name":"<name>"} ``` ## `sandbox_open_browser` Launch Chromium with a fresh throwaway profile inside an existing sandbox (an image with cua-spacesd and Chromium, see images_list), bind it and optionally open a URL. Returns the space, session, target_id and tab_id for call_tool with the in-sandbox browser tools. sandbox_create with browser=true already does this. Permission: `sandbox:browser`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | | `session` | `string` | | cua-driver session label to use on every later browser call (default: browse). | | `url` | `string` | | Open this URL first. | **Example arguments** ```json {"name":"<name>"} ``` --- # Teleport tools Move a signed-in browser session, per site, into a sandbox through the Cua Keyvault, with consent. Served by `cua mcp`; see [MCP tools](</docs/cua-cli/reference/mcp-tools>) for every tool. ## `teleport_browser_session` Teleport the user's signed-in session for specific sites (for example github.com) from their Firefox or Chrome into a sandbox, through the Cua Keyvault. Name each site: nothing is selected by default and there is no all-sessions option. The first call moves nothing: it files a Keyvault request and returns consent_required, a summary and a request_id. Show the summary to the user; they approve it in Cua (Touch ID or password). Then call again with the request_id to move exactly those sites. Use it only when the user asks to browse as themselves. Permission: `teleport:browser`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `browser` | `"firefox" \| "chrome"` | required | firefox or chrome (chromium, brave and edge profiles use chrome). | | `duration_minutes` | `integer` | | How long the sandbox may keep them (default 15, at most 1440). | | `name` | `string` | required | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations. | | `reason` | `string` | | Why, in a few words; shown to the user as unverified. | | `request_id` | `string` | | The request_id from the first call, after the user approved it in Cua. | | `sites` | `string[]` | required | The exact sites to teleport, for example ["github.com"]. | **Example arguments** ```json {"name":"<name>","browser":"firefox","sites":["<site>"]} ``` --- # Computer tools Screenshots, input, windows, files and shell on a sandbox desktop. | Tool | Description | | --- | --- | | [`computer_screenshot`](#computer_screenshot) | Take a screenshot (long edge at most 1200 px). | | [`computer_click`](#computer_click) | Click at screenshot coordinates. | | [`computer_double_click`](#computer_double_click) | Double-click at screenshot coordinates. | | [`computer_move_cursor`](#computer_move_cursor) | Move the cursor. | | [`computer_mouse_down`](#computer_mouse_down) | Press a mouse button. | | [`computer_mouse_up`](#computer_mouse_up) | Release a mouse button. | | [`computer_type`](#computer_type) | Type text. | | [`computer_key`](#computer_key) | Press a key (enter, escape, tab, a, ...). | | [`computer_key_down`](#computer_key_down) | Hold a key. | | [`computer_key_up`](#computer_key_up) | Release a key. | | [`computer_hotkey`](#computer_hotkey) | Press a shortcut such as ctrl+c or cmd+shift+s. | | [`computer_scroll`](#computer_scroll) | Scroll up, down, left or right. | | [`computer_drag`](#computer_drag) | Drag between two points. | | [`computer_clipboard_get`](#computer_clipboard_get) | Read clipboard text. | | [`computer_clipboard_set`](#computer_clipboard_set) | Set clipboard text. | | [`computer_file_read`](#computer_file_read) | Read a text file. | | [`computer_file_write`](#computer_file_write) | Write a text file (the Spaces `space_write` implementation, on this sandbox). | | [`computer_file_list`](#computer_file_list) | List a directory. | | [`computer_shell`](#computer_shell) | Run a shell command (sh -c, 120 s timeout; the Spaces `space_bash` implementation, on this sandbox). | | [`computer_window_list`](#computer_window_list) | List windows. | | [`computer_window_open`](#computer_window_open) | Open a file or URL. | | [`computer_window_focus`](#computer_window_focus) | Focus a window. | | [`computer_window_unfocus`](#computer_window_unfocus) | Remove focus from the current window (Escape). | | [`computer_window_minimize`](#computer_window_minimize) | Minimize a window. | | [`computer_window_maximize`](#computer_window_maximize) | Maximize a window. | | [`computer_window_close`](#computer_window_close) | Close a window. | | [`computer_window_resize`](#computer_window_resize) | Resize a window. | | [`computer_window_move`](#computer_window_move) | Move a window. | | [`computer_window_get_info`](#computer_window_get_info) | Get a window's title, position and size. | | [`computer_launch`](#computer_launch) | Launch an application. | | [`computer_get_screen_size`](#computer_get_screen_size) | Primary display size in points. | | [`computer_get_cursor_position`](#computer_get_cursor_position) | Cursor position in points. | | [`computer_get_current_window`](#computer_get_current_window) | The focused window's id and title. | | [`computer_get_accessibility_tree`](#computer_get_accessibility_tree) | Accessibility tree of the focused (or given) window. | | [`computer_accessibility_act`](#computer_accessibility_act) | Act on an accessibility element (press, focus, set_value, ...). | Computer tools act on a sandbox desktop through its cua-spacesd. Coordinates are in the pixel space of the last `computer_screenshot` of that sandbox. Served by `cua mcp`; see [MCP tools](</docs/cua-cli/reference/mcp-tools>) for every tool. ## `computer_screenshot` Take a screenshot (long edge at most 1200 px). Later coordinates are in its pixel space. Permission: `computer:screenshot`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_click` Click at screenshot coordinates. Permission: `computer:click`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `button` | `string` | | left (default), right or middle. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `x` | `integer` | required | X in screenshot pixels. | | `y` | `integer` | required | Y in screenshot pixels. | **Example arguments** ```json {"x":100,"y":200} ``` ## `computer_double_click` Double-click at screenshot coordinates. Permission: `computer:click`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `x` | `integer` | required | X in screenshot pixels. | | `y` | `integer` | required | Y in screenshot pixels. | **Example arguments** ```json {"x":100,"y":200} ``` ## `computer_move_cursor` Move the cursor. Permission: `computer:click`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `x` | `integer` | required | X in screenshot pixels. | | `y` | `integer` | required | Y in screenshot pixels. | **Example arguments** ```json {"x":100,"y":200} ``` ## `computer_mouse_down` Press a mouse button. Permission: `computer:click`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `button` | `string` | | left, right or middle. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `x` | `integer` | required | X in screenshot pixels. | | `y` | `integer` | required | Y in screenshot pixels. | **Example arguments** ```json {"x":100,"y":200} ``` ## `computer_mouse_up` Release a mouse button. Permission: `computer:click`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `button` | `string` | | left, right or middle. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `x` | `integer` | required | X in screenshot pixels. | | `y` | `integer` | required | Y in screenshot pixels. | **Example arguments** ```json {"x":100,"y":200} ``` ## `computer_type` Type text. Permission: `computer:type`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `text` | `string` | required | Text. | **Example arguments** ```json {"text":"<text>"} ``` ## `computer_key` Press a key (enter, escape, tab, a, ...). Permission: `computer:key`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `key` | `string` | required | Key. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"key":"<key>"} ``` ## `computer_key_down` Hold a key. Permission: `computer:key`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `key` | `string` | required | Key. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"key":"<key>"} ``` ## `computer_key_up` Release a key. Permission: `computer:key`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `key` | `string` | required | Key. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"key":"<key>"} ``` ## `computer_hotkey` Press a shortcut such as ctrl+c or cmd+shift+s. Permission: `computer:hotkey`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `keys` | `string` | required | Keys joined by +. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"keys":"<keys>"} ``` ## `computer_scroll` Scroll up, down, left or right. Permission: `computer:scroll`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `amount` | `integer` | | Lines (default 3). | | `direction` | `string` | | down (default), up, left, right. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_drag` Drag between two points. Permission: `computer:drag`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `end_x` | `integer` | required | End X. | | `end_y` | `integer` | required | End Y. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `start_x` | `integer` | required | Start X. | | `start_y` | `integer` | required | Start Y. | **Example arguments** ```json {"start_x":100,"start_y":200,"end_x":100,"end_y":200} ``` ## `computer_clipboard_get` Read clipboard text. Permission: `computer:clipboard`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_clipboard_set` Set clipboard text. Permission: `computer:clipboard`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `text` | `string` | required | Text. | **Example arguments** ```json {"text":"<text>"} ``` ## `computer_file_read` Read a text file. Permission: `computer:file`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `path` | `string` | required | Path. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"path":"<path>"} ``` ## `computer_file_write` Write a text file (the Spaces `space_write` implementation, on this sandbox). Permission: `computer:file`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `content` | `string` | required | Content. | | `path` | `string` | required | Path. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"path":"<path>","content":"<content>"} ``` ## `computer_file_list` List a directory. Permission: `computer:file`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `path` | `string` | | Path (default .). | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_shell` Run a shell command (sh -c, 120 s timeout; the Spaces `space_bash` implementation, on this sandbox). Permission: `computer:shell`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `command` | `string` | required | Command line. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"command":"<command>"} ``` ## `computer_window_list` List windows. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `app` | `string` | | Filter by app or title. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_window_open` Open a file or URL. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `path` | `string` | required | Path or URL. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"path":"<path>"} ``` ## `computer_window_focus` Focus a window. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `window_id` | `string` | required | Window id. | **Example arguments** ```json {"window_id":"<window_id>"} ``` ## `computer_window_unfocus` Remove focus from the current window (Escape). Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_window_minimize` Minimize a window. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `window_id` | `string` | required | Window id. | **Example arguments** ```json {"window_id":"<window_id>"} ``` ## `computer_window_maximize` Maximize a window. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `window_id` | `string` | required | Window id. | **Example arguments** ```json {"window_id":"<window_id>"} ``` ## `computer_window_close` Close a window. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `window_id` | `string` | required | Window id. | **Example arguments** ```json {"window_id":"<window_id>"} ``` ## `computer_window_resize` Resize a window. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `height` | `integer` | required | Height. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `width` | `integer` | required | Width. | | `window_id` | `string` | required | Window id. | **Example arguments** ```json {"window_id":"<window_id>","width":1,"height":1} ``` ## `computer_window_move` Move a window. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `window_id` | `string` | required | Window id. | | `x` | `integer` | required | X. | | `y` | `integer` | required | Y. | **Example arguments** ```json {"window_id":"<window_id>","x":100,"y":200} ``` ## `computer_window_get_info` Get a window's title, position and size. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `window_id` | `string` | required | Window id. | **Example arguments** ```json {"window_id":"<window_id>"} ``` ## `computer_launch` Launch an application. Permission: `computer:window`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `app` | `string` | required | App name. | | `args` | `string[]` | | Arguments. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | **Example arguments** ```json {"app":"<app>"} ``` ## `computer_get_screen_size` Primary display size in points. Permission: `computer:screenshot`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_get_cursor_position` Cursor position in points. Permission: `computer:screenshot`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_get_current_window` The focused window's id and title. Permission: `computer:screenshot`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | ## `computer_get_accessibility_tree` Accessibility tree of the focused (or given) window. Permission: `computer:screenshot`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `max_depth` | `integer` | | Depth limit. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `window_id` | `string` | | Window id. | ## `computer_accessibility_act` Act on an accessibility element (press, focus, set_value, ...). Permission: `computer:accessibility`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `action` | `string` | | press (default), focus, set_value, ... | | `element_id` | `string` | required | Element id. | | `sandbox` | `string` | | Sandbox ref (local:<name>, cloud:<name>, direct:<host:port>) or a name unique across locations (default: --sandbox / CUA_SANDBOX). | | `snapshot_id` | `string` | | Snapshot id from the tree. | | `value` | `string` | | Value for set_value. | **Example arguments** ```json {"element_id":"<element_id>"} ``` --- # Skills tools List, read, record and delete skills (recorded demonstrations). | Tool | Description | | --- | --- | | [`skills_list`](#skills_list) | List recorded skills. | | [`skills_read`](#skills_read) | Read a skill and its steps. | | [`skills_delete`](#skills_delete) | Delete a skill. | | [`skills_record`](#skills_record) | How to record a skill (recording is interactive: cua skills record). | Served by `cua mcp`; see [MCP tools](</docs/cua-cli/reference/mcp-tools>) for every tool. ## `skills_list` List recorded skills. Permission: `skills:list`. No parameters. ## `skills_read` Read a skill and its steps. Permission: `skills:read`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Skill name. | **Example arguments** ```json {"name":"<name>"} ``` ## `skills_delete` Delete a skill. Permission: `skills:delete`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Skill name. | **Example arguments** ```json {"name":"<name>"} ``` ## `skills_record` How to record a skill (recording is interactive: cua skills record). Permission: `skills:record`. | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `name` | `string` | required | Skill name. | **Example arguments** ```json {"name":"<name>"} ``` --- # Docs for agents Give a coding agent Cua's docs and source as Markdown or over MCP. ## Markdown Every page has a Markdown twin: append `.md` to its URL, or send `Accept: text/markdown`. - [`/docs/llms.txt`](https://cua.ai/docs/llms.txt): index of every page. - [`/docs/llms-full.txt`](https://cua.ai/docs/llms-full.txt): all pages in one file. ## MCP server `https://vk-mcp.cua.ai/mcp` (streamable HTTP, read-only) searches the docs and the versioned source. ```bash skip="third-party-cli" claude mcp add --transport http cua-docs https://vk-mcp.cua.ai/mcp ``` Cursor (`mcp.json`), Windsurf (`~/.codeium/windsurf/mcp_config.json`) and Cline take the same entry: ```json test="config" id="json-config" schema="mcp-client-config" { "mcpServers": { "Cua Docs & Code": { "url": "https://vk-mcp.cua.ai/mcp" } } } ``` GitHub Copilot reads `.vscode/mcp.json` with `servers` in place of `mcpServers`: ```json test="config" id="json-config-2" schema="mcp-client-config" { "servers": { "Cua Docs & Code": { "url": "https://vk-mcp.cua.ai/mcp" } } } ``` | Tool | Input | Returns | | --- | --- | --- | | `query_docs_db` | `sql`: a `SELECT` over `pages` / `pages_fts` (`url`, `title`, `category`, `content`) | Rows | | `query_docs_vectors` | `query`, `limit` (10, max 100), `where`, `select` | Page chunks with `_distance` | | `query_code_db` | `sql`: a `SELECT` over `code_files` / `code_files_fts` (`component`, `version`, `file_path`, `content`, `language`) | Rows | | `query_code_vectors` | `query`, `limit`, `where` (e.g. `version = '0.7.3'`), `select`, `component` | Code chunks with `_distance` | The code index covers tagged releases of Python, TypeScript and JavaScript sources. List what it holds before relying on a version: ```sql skip="live-cloud-only" SELECT DISTINCT component, version FROM code_files; ```