Installation
pip install opencomputer-sdk
from opencomputer import Sandbox, Image, Snapshots
Sandbox
Class Methods
await Sandbox.create(...): Sandbox
Create a new sandbox.
| Parameter | Type | Default | Description |
|---|
template | str | "base" | Template name |
timeout | int | 300 | Idle timeout in seconds |
api_key | str | env var | API key |
api_url | str | env var | API URL |
envs | dict[str, str] | None | Environment variables |
metadata | dict[str, str] | None | Arbitrary metadata |
image | Image | None | Declarative image definition (see Image) |
snapshot | str | None | Name of a pre-built snapshot |
on_build_log | Callable[[str], None] | None | Build log callback (when using image) |
sandbox = await Sandbox.create(template="my-stack", timeout=600)
cpuCount and memoryMB are not available in the Python SDK. Use the HTTP API for custom resources.
await Sandbox.connect(sandbox_id, ...): Sandbox
| Parameter | Type | Description |
|---|
sandbox_id | str | Sandbox ID (required) |
api_key | str | API key (optional) |
api_url | str | API URL (optional) |
sandbox = await Sandbox.connect("sb-abc123")
await Sandbox.create_from_checkpoint(checkpoint_id, ...): Sandbox
| Parameter | Type | Default | Description |
|---|
checkpoint_id | str | — | Checkpoint ID (required) |
timeout | int | 300 | Idle timeout |
api_key | str | env var | API key |
api_url | str | env var | API URL |
forked = await Sandbox.create_from_checkpoint("cp-abc123")
await Sandbox.create_checkpoint_patch(checkpoint_id, script, ...): dict
| Parameter | Type | Required | Description |
|---|
checkpoint_id | str | Yes | Target checkpoint |
script | str | Yes | Bash script |
description | str | No | Description |
result = await Sandbox.create_checkpoint_patch("cp-abc", script="apt install -y curl")
await Sandbox.list_checkpoint_patches(checkpoint_id, ...): list[dict]
patches = await Sandbox.list_checkpoint_patches("cp-abc")
await Sandbox.delete_checkpoint_patch(checkpoint_id, patch_id, ...): None
await Sandbox.delete_checkpoint_patch("cp-abc", "pa-xyz")
Context Manager
Auto-kills the sandbox on exit:
async with await Sandbox.create() as sandbox:
result = await sandbox.exec.run("echo hello")
print(result.stdout)
# sandbox.kill() called automatically
Instance Methods
await sandbox.kill(): None
Terminate the sandbox.
await sandbox.is_running(): bool
Check if the sandbox is running.
await sandbox.set_timeout(timeout): None
Update idle timeout.
| Parameter | Type | Description |
|---|
timeout | int | New timeout in seconds |
await sandbox.create_checkpoint(name): dict
Create a named checkpoint. Returns checkpoint info as a dictionary.
await sandbox.list_checkpoints(): list[dict]
List all checkpoints for the sandbox.
await sandbox.restore_checkpoint(checkpoint_id): None
Revert in-place to a checkpoint.
await sandbox.delete_checkpoint(checkpoint_id): None
Delete a checkpoint.
await sandbox.create_preview_url(port, domain?, auth_config?): dict
| Parameter | Type | Required | Description |
|---|
port | int | Yes | Container port (1–65535) |
domain | str | No | Custom domain |
auth_config | dict | No | Auth configuration |
await sandbox.list_preview_urls(): list[dict]
await sandbox.delete_preview_url(port): None
await sandbox.close(): None
Close HTTP clients. Called automatically by the context manager.
Not Available in Python
These features are TypeScript-only. Use the HTTP API directly:
hibernate() / wake()
cpuCount / memoryMB on create
Properties
| Property | Type | Description |
|---|
sandbox_id | str | Sandbox ID |
status | str | Current status |
agent | Agent | Agent sessions |
exec | Exec | Command execution |
files | Filesystem | File operations |
pty | Pty | Terminal sessions |
commands | Exec | Deprecated — alias for exec |
Agent
Accessed via sandbox.agent.
await sandbox.agent.start(...): AgentSession
Start an agent session.
| Parameter | Type | Description |
|---|
prompt | str | Initial prompt |
model | str | Claude model |
system_prompt | str | System prompt |
allowed_tools | list[str] | Restrict tools |
permission_mode | str | Permission mode |
max_turns | int | Max turns (default: 50) |
cwd | str | Working directory |
mcp_servers | dict[str, Any] | MCP server configuration |
on_event | Callable[[AgentEvent], None] | Event callback |
on_error | Callable[[str], None] | Stderr callback |
session = await sandbox.agent.start(
prompt="Build a todo app",
on_event=lambda e: print(e.type),
)
resume, on_exit, and on_scrollback_end are not available in the Python SDK.
await sandbox.agent.attach(session_id, ...): AgentSession
Reconnect to a running agent session. Accepts on_event and on_error.
await sandbox.agent.list(): list[AgentSessionInfo]
List all agent sessions.
AgentSession
| Member | Type | Description |
|---|
session_id | str | Session ID |
sandbox_id | str | Sandbox ID |
send_prompt(text) | method | Send follow-up prompt |
interrupt() | method | Interrupt current turn |
configure(...) | method | Update model, tools, cwd |
await kill(signal=9) | method | Kill agent process |
await close() | method | Close WebSocket |
await session.collect_events(): list[AgentEvent]
Collect all events until the agent process exits. Python-unique alternative to callbacks.
session = await sandbox.agent.start(prompt="Fix the tests")
events = await session.collect_events()
for event in events:
if event.type == "result":
print(event.data)
await session.wait(): int
Wait for the agent to finish. Returns the exit code.
exit_code = await session.wait()
AgentEvent
Dataclass with dict-like access:
@dataclass
class AgentEvent:
type: str
data: dict[str, Any]
# Usage:
event["message"] # dict-like access
event.get("message") # safe access with default
event.type # attribute access
AgentSessionInfo
@dataclass
class AgentSessionInfo:
session_id: str
sandbox_id: str
running: bool
started_at: str
Exec
Accessed via sandbox.exec.
await sandbox.exec.run(command, ...): ProcessResult
Run a command synchronously via sh -c.
| Parameter | Type | Default | Description |
|---|
command | str | — | Shell command (required) |
timeout | int | 60 | Timeout in seconds |
env | dict[str, str] | None | Environment variables |
cwd | str | None | Working directory |
result = await sandbox.exec.run("npm test", cwd="/app")
await sandbox.exec.start(command, ...): str
Start a long-running command. Returns the session ID (not a session object).
| Parameter | Type | Default | Description |
|---|
command | str | — | Command (required) |
args | list[str] | None | Arguments |
env | dict[str, str] | None | Environment variables |
cwd | str | None | Working directory |
timeout | int | None | Timeout in seconds |
session_id = await sandbox.exec.start("node server.js", cwd="/app")
Python exec.start() returns a session ID string. There are no streaming callbacks, ExecSession object, or maxRunAfterDisconnect. For streaming, use the WebSocket binary protocol.
await sandbox.exec.list(): list[ExecSessionInfo]
List all exec sessions.
await sandbox.exec.kill(session_id, signal=9): None
Kill an exec session.
Not Available in Python
exec.attach() — reconnect to running session
- Streaming callbacks (
on_stdout, on_stderr, on_exit)
ExecSession object with send_stdin() and done
max_run_after_disconnect
ProcessResult
@dataclass
class ProcessResult:
exit_code: int
stdout: str
stderr: str
ExecSessionInfo
@dataclass
class ExecSessionInfo:
session_id: str
sandbox_id: str
command: str
args: list[str]
running: bool
exit_code: int | None
started_at: str
attached_clients: int
Filesystem
Accessed via sandbox.files.
await sandbox.files.read(path): str
Read a file as a UTF-8 string.
await sandbox.files.read_bytes(path): bytes
Read a file as raw bytes.
await sandbox.files.write(path, content): None
Write content to a file. Accepts str or bytes.
await sandbox.files.list(path="/"): list[EntryInfo]
List directory contents.
await sandbox.files.make_dir(path): None
Create a directory.
await sandbox.files.remove(path): None
Delete a file or directory.
await sandbox.files.exists(path): bool
Check if a path exists. Client-side wrapper — attempts a read and returns False on error.
EntryInfo
@dataclass
class EntryInfo:
name: str
is_dir: bool
path: str
size: int = 0
Pty
Accessed via sandbox.pty.
await sandbox.pty.create(cols=80, rows=24, on_output=None): PtySession
Create an interactive terminal session.
| Parameter | Type | Default | Description |
|---|
cols | int | 80 | Terminal columns |
rows | int | 24 | Terminal rows |
on_output | Callable[[bytes], None] | None | Output callback |
PtySession
| Member | Type | Description |
|---|
session_id | str | Session ID |
sandbox_id | str | Sandbox ID |
send(data) | method | Send input (str or bytes) |
await recv() | method | Receive output bytes (Python-unique) |
await close() | method | Close the PTY |
recv() is Python-unique — a pull-based alternative to the on_output callback. Returns raw bytes.
Image
Fluent, immutable builder for declarative sandbox images. Each method returns a new Image instance.
Image.base(): Image
Start from the default OpenSandbox environment (Ubuntu 22.04, Python, Node.js, build tools).
from opencomputer import Image
image = (
Image.base()
.apt_install(["curl", "jq"])
.pip_install(["requests", "pandas"])
.env({"PROJECT_ROOT": "/workspace"})
.workdir("/workspace")
)
Builder Methods
| Method | Parameters | Description |
|---|
apt_install(packages) | list[str] | Install system packages via apt-get |
pip_install(packages) | list[str] | Install Python packages via pip |
run_commands(*cmds) | str (variadic) | Run shell commands during build |
env(vars) | dict[str, str] | Set environment variables |
workdir(path) | str | Set default working directory |
add_file(remote_path, content) | str, str | Embed a file with inline content |
add_local_file(local_path, remote_path) | str, str | Read a local file into the image |
add_local_dir(local_path, remote_path) | str, str | Read a local directory into the image |
image.to_dict(): dict
Returns the image manifest as a plain dict.
image.cache_key(): str
Computes a deterministic SHA-256 hash of the manifest for cache lookups.
Snapshots
Standalone class — not a property on Sandbox.
Snapshots(api_key=..., api_url=...)
from opencomputer import Snapshots
snapshots = Snapshots()
# or with explicit config:
snapshots = Snapshots(api_key="...", api_url="...")
| Parameter | Type | Description |
|---|
api_key | str | API key (falls back to OPENCOMPUTER_API_KEY env var) |
api_url | str | API URL (falls back to OPENCOMPUTER_API_URL env var) |
await snapshots.create(name, image, on_build_logs=None): dict
Create a pre-built snapshot from a declarative image.
| Parameter | Type | Required | Description |
|---|
name | str | Yes | Unique snapshot name |
image | Image | Yes | Declarative image definition |
on_build_logs | Callable[[str], None] | No | Build log streaming callback |
await snapshots.list(): list[dict]
List all snapshots for the current organization.
await snapshots.get(name): dict
Get a snapshot by name.
await snapshots.delete(name): None
Delete a snapshot. Existing sandboxes created from it are not affected.
SnapshotInfo
Snapshot methods return dicts with these fields:
{
"id": str, # Unique snapshot identifier
"name": str, # Snapshot name
"status": str, # "building" | "ready" | "failed"
"contentHash": str, # SHA-256 hash of the image manifest
"checkpointId": str, # Linked checkpoint ID
"manifest": dict, # The declarative image manifest
"createdAt": str, # ISO 8601 creation timestamp
"lastUsedAt": str, # ISO 8601 last usage timestamp
}
Secret Stores
await SecretStore.create(**kwargs)
Create a new secret store.
from opencomputer import SecretStore
store = await SecretStore.create(
name='my-agent-secrets',
egress_allowlist=['api.anthropic.com'],
)
Store name (unique per organization).
egress_allowlist
list[str] | None
default:"None"
Allowed egress hosts (e.g. ["api.anthropic.com"]).
Returns: dict — Secret store info with id, name, egressAllowlist, etc.
await SecretStore.list(**kwargs)
List all secret stores.
stores = await SecretStore.list()
Returns: list[dict]
await SecretStore.get(store_id, **kwargs)
Get a secret store by ID.
store = await SecretStore.get('store-uuid')
UUID of the secret store.
Returns: dict
await SecretStore.update(store_id, **kwargs)
Partial updates — only the fields you pass are changed.
updated = await SecretStore.update(
'store-uuid',
name='new-name',
egress_allowlist=['api.anthropic.com', '*.openai.com'],
)
UUID of the store to update.
New store name (empty = no change).
egress_allowlist
list[str] | None
default:"None"
New allowed egress hosts (None = no change).
Returns: dict
await SecretStore.delete(store_id, **kwargs)
Deletes the store and all its secrets. Running sandboxes are not affected.
await SecretStore.delete('store-uuid')
Returns: None
await SecretStore.set_secret(store_id, name, value, **kwargs)
Set a secret in a store. Secrets are encrypted at rest. The value is never returned by the API.
await SecretStore.set_secret('store-uuid', 'ANTHROPIC_API_KEY', 'sk-ant-...')
Optionally restrict which hosts can receive this secret:
await SecretStore.set_secret(
'store-uuid',
'ANTHROPIC_API_KEY',
'sk-ant-...',
allowed_hosts=['api.anthropic.com'],
)
UUID of the secret store.
Secret name (used as the env var name in sandboxes).
Secret value (encrypted at rest, never returned by API).
allowed_hosts
list[str] | None
default:"None"
Restrict this secret to specific hosts only.
Returns: None
await SecretStore.list_secrets(store_id, **kwargs)
Returns secret metadata only. Values are never exposed.
entries = await SecretStore.list_secrets('store-uuid')
# [{'name': 'ANTHROPIC_API_KEY', 'allowedHosts': ['api.anthropic.com'], ...}, ...]
Returns: list[dict]
await SecretStore.delete_secret(store_id, name, **kwargs)
await SecretStore.delete_secret('store-uuid', 'DATABASE_URL')
Returns: None
Creating a Sandbox with a Secret Store
Pass the secret_store parameter to Sandbox.create() to inject the store’s secrets:
from opencomputer import Sandbox
sandbox = await Sandbox.create(
secret_store='my-agent-secrets',
timeout=600,
)
# Secrets are available as sealed env vars
result = await sandbox.commands.run('echo $ANTHROPIC_API_KEY')
# stdout: "osb_sealed_abc123..." (sealed, not the real key)
# But outbound HTTPS requests get the real value substituted by the proxy
api_result = await sandbox.commands.run(
'curl -s https://api.anthropic.com/v1/messages -H "x-api-key: $ANTHROPIC_API_KEY" ...'
)
# The proxy replaces the sealed token with the real key before it hits Anthropic