Metadata-Version: 2.4
Name: cua-sandbox
Version: 0.1.24
Summary: CUA Sandbox — ephemeral and persistent sandboxed computer environments
Project-URL: Homepage, https://github.com/trycua/cua
Project-URL: Repository, https://github.com/trycua/cua
Author-email: TryCua <hello@trycua.com>
License-Expression: MIT
Keywords: automation,computer-use,container,sandbox,vm
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: <3.14,>=3.11
Requires-Dist: cua-auto>=0.1.2
Requires-Dist: cua-core<0.4.0,>=0.3.0
Requires-Dist: cua-fleet==0.0.8
Requires-Dist: grpcio==1.78.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: oras>=0.2.40
Requires-Dist: paramiko>=5.0.0
Requires-Dist: protobuf==6.33.6
Requires-Dist: pycdlib>=1.14.0
Requires-Dist: vncdotool>=1.2.0
Requires-Dist: websockets>=12.0
Provides-Extra: auth
Requires-Dist: webbrowser-open>=0.0.1; extra == 'auth'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# cua-sandbox

Sandboxed VM environments with a unified Python API. Cloud by default.

```bash
pip install cua-sandbox
```

Fleet support is provided by the published `cua-fleet` wheel. It bundles the platform-specific `fleet_sdk` native binding.
Install from the Cua wheel index when resolving dependencies with pip:

```bash
pip install --extra-index-url https://wheels.cua.ai/simple cua-sandbox
```

## Ephemeral sandbox

Created on enter, destroyed on exit.

```python
from cua_sandbox import Sandbox, Image

async with Sandbox.ephemeral(Image.linux()) as sb:
    await sb.shell.run("uname -a")
    await sb.screenshot()
```

## Persistent sandbox

Provision a new sandbox that stays alive after your script exits.

```python
from cua_sandbox import Sandbox, Image

sb = await Sandbox.create(Image.linux())
await sb.shell.run("uname -a")
print(sb.name)  # save this to reconnect later
await sb.disconnect()
```

## Connect to existing sandbox

Attach to a sandbox that's already running. Works as a plain await or context manager.

```python
from cua_sandbox import Sandbox

# plain await
sb = await Sandbox.connect("my-sandbox")
await sb.shell.run("whoami")
await sb.disconnect()

# context manager — disconnects on exit, sandbox keeps running
async with Sandbox.connect("my-sandbox") as sb:
    await sb.shell.run("whoami")
```

## Destroy a sandbox

```python
await sb.destroy()  # disconnect + permanently delete
```

## Local VM

Spins up a local VM using QEMU or Lume, destroyed on exit.

```python
from cua_sandbox import Sandbox, Image
from cua_sandbox.runtime import QEMURuntime

async with Sandbox.ephemeral(Image.linux(), local=True, runtime=QEMURuntime()) as sb:
    await sb.shell.run("uname -a")
```

## Localhost (unsandboxed)

Direct host control — **not sandboxed**, use with caution.

```python
from cua_sandbox import Localhost

async with Localhost.connect() as host:
    await host.shell.run("echo hello")
    await host.screenshot()
```


## Cloud sandbox

Fleet is the OAuth cloud backend. Configure OAuth credentials once; Fleet uses `https://run.cua.ai` by default and can be overridden with `configure(fleet_base_url=...)` or `CUA_FLEET_BASE_URL`. The legacy API-key VM API continues to use `https://api.cua.ai`. Cloud images must use a registry reference; `expose()` declares additional Fleet services.

Fleet does not support snapshots or custom disks, and currently supports only `us-east-1`. `await sb.tunnel.forward(3000)` returns the authenticated Fleet service URL for an exposed port; it does not open a local SSH tunnel.

## Fleet pools

Use a pool to keep reusable registry-image sandboxes warm. Reconciliation is idempotent: it creates a missing pool or updates the existing pool with the same name. Each claim is released when the context exits, including when the block raises.

```python
from cua_sandbox import Image, Pool

pool = await Pool.reconcile({
    "name": "foo",
    "image": Image.from_registry("registry.example/workspace:latest"),
})

async with pool.claim() as sb:
    result = await sb.shell.run("echo hello")

    # Requests use the same authenticated Fleet claim.
    response = await sb.services.request(
        "mcp", method="POST", path="/mcp", json={"jsonrpc": "2.0", "method": "tools/list", "id": 1}
    )
    response.raise_for_status()
```

For scripts that use the synchronous facade:

```python
from cua_sandbox import Image
from cua_sandbox.sync import Pool

pool = Pool.reconcile({"name": "foo", "image": Image.from_registry("example:latest")})
with pool.claim() as sb:
    result = sb.shell.run("echo hello")
```

```python
import os

import cua_sandbox as cua
from cua_sandbox import Image, Sandbox

cua.configure(
    client_id=os.environ["CUA_CLIENT_ID"],
    client_secret=os.environ["CUA_CLIENT_SECRET"],
)

async with Sandbox.ephemeral(
    Image.from_registry("registry.example/desktop-workspace@sha256:...").expose(3000)
) as sb:
    await sb.shell.run("uname -a")
```
