Interfaces
Shell, mouse, keyboard, screen, clipboard, window, terminal, files, apps, mobile and driver 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.
class ShellShell command execution.
async def run(
command: str,
timeout: int = 30,
background: bool = False,
) -> CommandResultRun 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.
class CommandResult| Attribute | Type | Description |
|---|---|---|
stdout | str | |
stderr | str | |
returncode | int | |
success | bool |
class MouseMouse control.
async def click(x: int, y: int, button: str = 'left') -> NoneExample
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())async def right_click(x: int, y: int) -> Noneasync def double_click(x: int, y: int) -> Noneasync def move(x: int, y: int) -> Noneasync def scroll(x: int, y: int, scroll_x: int = 0, scroll_y: int = 3) -> NoneScroll 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.
async def mouse_down(x: int, y: int, button: str = 'left') -> Noneasync def mouse_up(x: int, y: int, button: str = 'left') -> Noneasync def drag(
start_x: int,
start_y: int,
end_x: int,
end_y: int,
button: str = 'left',
) -> Noneclass KeyboardKeyboard control.
async def type(text: str) -> NoneType a string of text.
async def keypress(keys: Union[List[str], str]) -> NonePress a key combination (e.g. ["ctrl", "c"] or "enter").
async def key_down(key: str) -> Noneasync def key_up(key: str) -> Noneclass ScreenScreen capture and info.
async def screenshot(format: str = 'png', quality: int = 95) -> bytesCapture 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. |
async def screenshot_base64(format: str = 'png', quality: int = 95) -> strCapture a screenshot and return as a base64-encoded string.
async def size() -> Tuple[int, int]Return (width, height) of the screen.
class ClipboardClipboard read/write.
async def get() -> strReturn the current clipboard text.
async def set(text: str) -> NoneSet the clipboard text.
class WindowWindow management.
async def get_active_title() -> strReturn the title of the currently focused window ("" when none).
cua-spacesd answers from WindowsService.ListWindows (the focused window); computer-server never implemented this command.
class TerminalPTY terminal sessions.
async def create(command: Optional[str] = None, cols: int = 80, rows: int = 24) -> dictCreate a new PTY session. Returns {"pid": int, "cols": int, "rows": int}.
async def send_input(pid: int, data: str) -> NoneSend input to a PTY session.
async def info(pid: int) -> Optional[dict]Return session info or None if gone.
async def close(pid: int) -> boolKill a PTY session.
class FilesFile and directory operations inside the sandbox.
All paths are inside the sandbox unless otherwise noted.
async def exists(path: str) -> boolasync def is_dir(path: str) -> boolasync def size(path: str) -> intasync def list(path: str) -> list[FileEntry]async def make_dir(path: str) -> Noneasync def remove_dir(path: str) -> Noneasync def remove(path: str) -> Noneasync def read_text(path: str) -> strasync def write_text(path: str, content: str) -> NoneExample
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())async def read_bytes(path: str, offset: int = 0, length: Optional[int] = None) -> bytesasync def write_bytes(path: str, content: bytes) -> Noneasync def upload(local_path: Union[str, Path], remote_path: str) -> NonePush a file from the host into the sandbox.
async def download(remote_path: str, local_path: Union[str, Path]) -> NonePull a file from the sandbox down to the host.
class FileEntry| Attribute | Type | Description |
|---|---|---|
name | str | |
path | str | |
is_dir | bool | |
size | Optional[int] |
class AppsInstall and launch cataloged applications inside the sandbox.
async def install(app_id: str) -> CommandResultInstall an app by its catalog ID (e.g. "unity", "godot-engine").
async def launch(app_id: str) -> CommandResultLaunch a previously-installed app by its catalog ID.
class MobileMobile (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.
async def tap(x: int, y: int) -> Noneasync def long_press(x: int, y: int, duration_ms: int = 1000) -> Noneasync def double_tap(x: int, y: int, delay: float = 0.1) -> Noneasync def type_text(text: str) -> Noneasync def swipe(x1: int, y1: int, x2: int, y2: int, duration_ms: int = 300) -> Noneasync def scroll_up(x: int, y: int, distance: int = 600, duration_ms: int = 400) -> Noneasync def scroll_down(
x: int,
y: int,
distance: int = 600,
duration_ms: int = 400,
) -> Noneasync def scroll_left(
x: int,
y: int,
distance: int = 400,
duration_ms: int = 300,
) -> Noneasync def scroll_right(
x: int,
y: int,
distance: int = 400,
duration_ms: int = 300,
) -> Noneasync def fling(x1: int, y1: int, x2: int, y2: int) -> Noneasync def gesture(
*finger_paths: tuple[int, int],
duration_ms: int = 400,
steps: int = 0,
) -> NoneInject 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:
# 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). |
async def pinch_in(cx: int, cy: int, spread: int = 300, duration_ms: int = 400) -> NonePinch-in (zoom out) with two real simultaneous fingers.
async def pinch_out(cx: int, cy: int, spread: int = 300, duration_ms: int = 400) -> NonePinch-out (zoom in) with two real simultaneous fingers.
async def key(keycode: int) -> Noneasync def home() -> Noneasync def back() -> Noneasync def recents() -> Noneasync def power() -> Noneasync def volume_up() -> Noneasync def volume_down() -> Noneasync def enter() -> Noneasync def backspace() -> Noneasync def wake() -> Noneasync def notifications() -> Noneasync def close_notifications() -> Noneclass DriverAccessor 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.
def session_name(driver: CuaDriver) -> strReturn 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.
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.
async def close() -> NoneInvalidate all connections before the owning transport is closed.
class DriverConnectionError(RuntimeError)Sanitized failure to establish or use a sandbox Driver service.