diff --git a/src/content/docs/sandbox/api/index.mdx b/src/content/docs/sandbox/api/index.mdx index 37c4472e359f17c..91d6892b93a8dee 100644 --- a/src/content/docs/sandbox/api/index.mdx +++ b/src/content/docs/sandbox/api/index.mdx @@ -43,6 +43,14 @@ The Sandbox SDK provides a comprehensive API for executing code, managing files, Execute Python and JavaScript code with rich outputs including charts, tables, and formatted data. + + Run OpenCode AI coding agent with web UI or programmatic SDK access for AI-powered code editing. + + +``` + +**Parameters**: +- `sandbox` (required) - Sandbox instance to run OpenCode in +- `options` (optional): + - `port` - Server port (default: `4096`) + - `directory` - Working directory (default: container cwd) + - `config` - OpenCode configuration with provider API keys + +**Returns**: `Promise` with: +- `port` - Port number the server is running on +- `url` - Base URL (`http://localhost:{port}`) +- `close()` - Function to stop the server + +**Process reuse**: If an OpenCode server is already running on the specified port, this function reuses it instead of starting a new one. + + +``` +import { getSandbox } from '@cloudflare/sandbox'; +import { createOpencodeServer } from '@cloudflare/sandbox/opencode'; + +const sandbox = getSandbox(env.Sandbox, 'my-agent'); + +const server = await createOpencodeServer(sandbox, { + directory: '/home/user/my-project', + config: { + provider: { + anthropic: { + options: { + apiKey: env.ANTHROPIC_API_KEY + } + } + } + } +}); + +console.log('Server running at:', server.url); + +// Proxy to the server manually +const response = await sandbox.containerFetch(request, server.port); + +// Stop server when done +await server.close(); +``` + + +### `createOpencode()` + +Start an OpenCode server and return a typed SDK client for programmatic access. + +```ts +const result = await createOpencode( + sandbox: Sandbox, + options?: OpencodeOptions +): Promise> +``` + +**Parameters**: +- `sandbox` (required) - Sandbox instance to run OpenCode in +- `options` (optional): + - `port` - Server port (default: `4096`) + - `directory` - Working directory (default: container cwd) + - `config` - OpenCode configuration with provider API keys + +**Type parameter**: +- `TClient` - Type of SDK client (for example, `OpencodeClient` from `@opencode-ai/sdk`) + +**Returns**: `Promise>` with: +- `client` - Typed OpenCode SDK client +- `server` - Server lifecycle management (same as `createOpencodeServer()` return) + +**Peer dependency**: Requires `@opencode-ai/sdk` to be installed. + +**Process reuse**: If an OpenCode server is already running on the specified port, this function reuses it instead of starting a new one. + + +``` +import { getSandbox } from '@cloudflare/sandbox'; +import { createOpencode } from '@cloudflare/sandbox/opencode'; +import type { OpencodeClient } from '@opencode-ai/sdk'; + +const sandbox = getSandbox(env.Sandbox, 'automation'); + +// Get both server and typed client +const { client, server } = await createOpencode(sandbox, { + directory: '/home/user/my-project', + config: { + provider: { + anthropic: { + options: { + apiKey: env.ANTHROPIC_API_KEY + } + } + } + } +}); + +// Use SDK for programmatic access +const session = await client.session.create({ + body: { title: 'Task Name' }, + query: { directory: '/home/user/my-project' } +}); + +const result = await client.session.prompt({ + path: { id: session.data.id }, + query: { directory: '/home/user/my-project' }, + body: { + model: { + providerID: 'anthropic', + modelID: 'claude-haiku-4-5' + }, + parts: [ + { + type: 'text', + text: 'Write a function to parse CSV files' + } + ] + } +}); + +console.log('Response:', result.data); + +await server.close(); +``` + + +### `proxyToOpencode()` + +Proxy HTTP requests to OpenCode web UI with automatic redirect handling. + +```ts +const response = proxyToOpencode( + request: Request, + sandbox: Sandbox, + server: OpencodeServer +): Response | Promise +``` + +**Parameters**: +- `request` (required) - Incoming HTTP request +- `sandbox` (required) - Sandbox instance running OpenCode +- `server` (required) - OpenCode server handle from `createOpencodeServer()` + +**Returns**: `Response` or `Promise` - Proxied response from OpenCode or redirect + +**Behavior**: +- For GET requests to HTML pages without `?url=` parameter: Returns redirect to set the parameter +- For all other requests: Proxies directly to the container + +The `?url=` parameter tells OpenCode's frontend to make API calls through the proxy instead of directly to localhost:4096. + + +``` +import { getSandbox } from '@cloudflare/sandbox'; +import { createOpencodeServer, proxyToOpencode } from '@cloudflare/sandbox/opencode'; + +export default { + async fetch(request: Request, env: Env): Promise { + const sandbox = getSandbox(env.Sandbox, 'opencode'); + + const server = await createOpencodeServer(sandbox, { + directory: '/home/user/project', + config: { + provider: { + anthropic: { + options: { + apiKey: env.ANTHROPIC_API_KEY + } + } + } + } + }); + + return proxyToOpencode(request, sandbox, server); + } +}; +``` + + +## Types + +### `OpencodeOptions` + +Configuration options for starting OpenCode server. + +```ts +interface OpencodeOptions { + port?: number; + directory?: string; + config?: Config; +} +``` + +**Properties**: +- `port` - Server port (default: `4096`) +- `directory` - Working directory for OpenCode (default: container cwd) +- `config` - OpenCode configuration object (from `@opencode-ai/sdk`) + +### `OpencodeServer` + +Server lifecycle management interface. + +```ts +interface OpencodeServer { + port: number; + url: string; + close(): Promise; +} +``` + +**Properties**: +- `port` - Port number the server is running on +- `url` - Base URL for API access (`http://localhost:{port}`) +- `close()` - Stop the server gracefully + +### `OpencodeResult` + +Result from `createOpencode()` containing both client and server. + +```ts +interface OpencodeResult { + client: TClient; + server: OpencodeServer; +} +``` + +**Properties**: +- `client` - Typed OpenCode SDK client (type from user's `@opencode-ai/sdk` version) +- `server` - Server lifecycle management + +## Error codes + +### `OPENCODE_STARTUP_FAILED` + +Thrown when OpenCode server fails to start. + +**Error properties**: +- `code` - `"OPENCODE_STARTUP_FAILED"` +- `message` - Error message with stderr output +- `context`: + - `port` - Port that failed to start + - `stderr` - Server error output + - `command` - Command that was executed + + +``` +import { createOpencodeServer } from '@cloudflare/sandbox/opencode'; + +try { + const server = await createOpencodeServer(sandbox, { + directory: '/nonexistent', + config: { provider: { anthropic: { options: { apiKey: 'invalid' } } } } + }); +} catch (error) { + if (error.code === 'OPENCODE_STARTUP_FAILED') { + console.error('Port:', error.context.port); + console.error('Stderr:', error.context.stderr); + console.error('Command:', error.context.command); + } +} +``` + + +## Container requirements + +Use the `-opencode` Docker image variant which includes OpenCode CLI pre-installed: + +```dockerfile +FROM cloudflare/sandbox:latest-opencode +``` + +Or in `wrangler.jsonc`: + +```json +{ + "[[unsafe.bindings]]": { + "name": "Sandbox", + "type": "durable_object_namespace", + "class_name": "Sandbox", + "image": "cloudflare/sandbox:latest-opencode" + } +} +``` + +The base `cloudflare/sandbox` image does not include OpenCode. Only the `-opencode` variant includes the necessary CLI installation. + +## Related resources + +- [Use OpenCode AI agent](/sandbox-sdk/guides/opencode-integration/) - Step-by-step usage guide +- [OpenCode SDK documentation](https://docs.opencode.ai/) - Official OpenCode SDK reference +- [Background processes](/sandbox-sdk/guides/background-processes/) - Process management patterns +- [Expose services](/sandbox-sdk/guides/expose-services/) - Port forwarding and service exposure diff --git a/src/content/docs/sandbox/configuration/dockerfile.mdx b/src/content/docs/sandbox/configuration/dockerfile.mdx index d904a07b6abe0ee..414f266550d6644 100644 --- a/src/content/docs/sandbox/configuration/dockerfile.mdx +++ b/src/content/docs/sandbox/configuration/dockerfile.mdx @@ -9,10 +9,17 @@ Customize the sandbox container image with your own packages, tools, and configu ## Base image -The Sandbox SDK uses a Ubuntu-based Linux container with Python, Node.js (via Bun), and common development tools pre-installed: +The Sandbox SDK provides multiple image variants for different use cases: ```dockerfile +# Default image (no Python) FROM docker.io/cloudflare/sandbox:0.3.3 + +# Python variant (includes Python 3.11 and data science libraries) +FROM docker.io/cloudflare/sandbox:0.3.3-python + +# OpenCode variant (includes OpenCode AI agent CLI) +FROM docker.io/cloudflare/sandbox:0.3.3-opencode ``` :::caution[Version synchronization required] @@ -23,15 +30,47 @@ Always match the Docker image version to your npm package version. If you're usi See [Version compatibility](/sandbox/concepts/sandboxes/#version-compatibility) for troubleshooting version mismatch warnings. ::: +### Default image + **What's included:** - Ubuntu 22.04 LTS base -- Python 3.11.14 with pip and venv - Node.js 20 LTS with npm - Bun 1.x (JavaScript/TypeScript runtime) -- Pre-installed Python packages: matplotlib, numpy, pandas, ipython - System utilities: curl, wget, git, jq, zip, unzip, file, procps, ca-certificates +Use this lightweight image when you do not need Python or specialized tools. + +### Python variant + +Add `-python` suffix to the image tag to include Python and data science libraries: + +```dockerfile +FROM docker.io/cloudflare/sandbox:0.3.3-python +``` + +**Additional packages:** + +- Python 3.11.14 with pip and venv +- Pre-installed Python packages: matplotlib, numpy, pandas, ipython + +Use this variant for data analysis, scientific computing, or when using the Code Interpreter API. + +### OpenCode variant + +Add `-opencode` suffix to the image tag to include OpenCode AI agent: + +```dockerfile +FROM docker.io/cloudflare/sandbox:0.3.3-opencode +``` + +**Additional packages:** + +- OpenCode CLI pre-installed and configured +- All packages from the default image + +Use this variant when running the OpenCode AI agent. Refer to [Use OpenCode AI agent](/sandbox-sdk/guides/opencode-integration/) for usage examples. + ## Creating a custom image Create a `Dockerfile` in your project root: diff --git a/src/content/docs/sandbox/guides/opencode-integration.mdx b/src/content/docs/sandbox/guides/opencode-integration.mdx new file mode 100644 index 000000000000000..94745d40a33f378 --- /dev/null +++ b/src/content/docs/sandbox/guides/opencode-integration.mdx @@ -0,0 +1,330 @@ +--- +title: Use OpenCode AI agent +pcx_content_type: how-to +sidebar: + order: 6 +description: Run OpenCode AI coding agent inside Sandbox containers with web UI or programmatic SDK access. +--- + +import { TypeScriptExample, PackageManagers } from "~/components"; + +This guide shows you how to run OpenCode, an AI coding agent, inside Sandbox containers. OpenCode can interact with your codebase, execute commands, and handle complex coding tasks through either a web UI or programmatic SDK. + +## When to use OpenCode + +Use OpenCode integration when you need: + +- **AI-powered code editing** - Let an AI agent read, write, and modify code +- **Interactive development** - Use the web UI for real-time AI-assisted coding +- **Automated workflows** - Build programmatic agents that modify code autonomously +- **Secure isolation** - Run AI agents in isolated containers with resource limits + +## Prerequisites + +1. **Docker image**: Use the `-opencode` variant of the Sandbox image which includes OpenCode CLI pre-installed +2. **Peer dependency**: Install `@opencode-ai/sdk` for programmatic access (not needed for web UI only) +3. **API key**: Configure your AI provider API key (for example, `ANTHROPIC_API_KEY`) + +## Web UI integration + +Expose OpenCode's web interface through your Worker. Users can access the full OpenCode experience in their browser. + +### Configure your container + +Update your `wrangler.jsonc` to use the OpenCode image variant: + +```json +{ + "name": "opencode-worker", + "compatibility_date": "2025-12-10", + "durable_objects": { + "bindings": [ + { + "name": "Sandbox", + "class_name": "Sandbox", + "script_name": "opencode-worker" + } + ] + }, + "[[unsafe.bindings]]": { + "name": "Sandbox", + "type": "durable_object_namespace", + "class_name": "Sandbox", + "image": "cloudflare/sandbox:latest-opencode" + } +} +``` + +### Create the proxy worker + + +``` +import { getSandbox } from '@cloudflare/sandbox'; +import { createOpencodeServer, proxyToOpencode } from '@cloudflare/sandbox/opencode'; + +export { Sandbox } from '@cloudflare/sandbox'; + +export default { + async fetch(request: Request, env: Env): Promise { + const sandbox = getSandbox(env.Sandbox, 'opencode-session'); + + // Start OpenCode server + const server = await createOpencodeServer(sandbox, { + directory: '/home/user/my-project', + config: { + provider: { + anthropic: { + options: { + apiKey: env.ANTHROPIC_API_KEY + } + } + } + } + }); + + // Proxy requests to OpenCode web UI + return proxyToOpencode(request, sandbox, server); + } +}; +``` + + +### Clone a repository + +To give OpenCode a codebase to work with, clone a repository before starting the server: + + +``` +const sandbox = getSandbox(env.Sandbox, 'opencode-session'); + +// Clone repository +await sandbox.gitCheckout('https://github.com/your-org/your-repo.git', { + targetDir: '/home/user/my-project' +}); + +// Start OpenCode in the cloned directory +const server = await createOpencodeServer(sandbox, { + directory: '/home/user/my-project', + config: { + provider: { + anthropic: { + options: { + apiKey: env.ANTHROPIC_API_KEY + } + } + } + } +}); + +return proxyToOpencode(request, sandbox, server); +``` + + +Users can now browse to your Worker URL and interact with OpenCode in their browser. + +## Programmatic SDK access + +Build automated agents that interact with OpenCode programmatically using the typed SDK client. + +### Install peer dependency + + + +### Create SDK client + + +``` +import { getSandbox } from '@cloudflare/sandbox'; +import { createOpencode } from '@cloudflare/sandbox/opencode'; +import type { OpencodeClient } from '@opencode-ai/sdk'; + +const sandbox = getSandbox(env.Sandbox, 'automation'); + +// Create server and typed SDK client +const { client, server } = await createOpencode(sandbox, { + directory: '/home/user/my-project', + config: { + provider: { + anthropic: { + options: { + apiKey: env.ANTHROPIC_API_KEY + } + } + } + } +}); + +// Create a session +const session = await client.session.create({ + body: { title: 'Automated Task' }, + query: { directory: '/home/user/my-project' } +}); + +// Send a prompt +const result = await client.session.prompt({ + path: { id: session.data.id }, + query: { directory: '/home/user/my-project' }, + body: { + model: { + providerID: 'anthropic', + modelID: 'claude-haiku-4-5' + }, + parts: [ + { + type: 'text', + text: 'Add a new function that validates email addresses' + } + ] + } +}); + +console.log('Agent response:', result.data); + +// Clean up +await server.close(); +``` + + +### Extract response text + +OpenCode responses contain structured data with multiple parts. Extract text content: + + +``` +const result = await client.session.prompt({ + path: { id: session.data.id }, + query: { directory: '/home/user/my-project' }, + body: { + model: { + providerID: 'anthropic', + modelID: 'claude-haiku-4-5' + }, + parts: [ + { + type: 'text', + text: 'Summarize the main function in this file' + } + ] + } +}); + +// Find text response +const parts = result.data?.parts ?? []; +const textPart = parts.find((p) => p.type === 'text'); +console.log(textPart?.text); +``` + + +## Configuration options + +### Provider configuration + +Configure AI provider credentials in the `config.provider` object: + + +``` +const config = { + provider: { + anthropic: { + options: { + apiKey: env.ANTHROPIC_API_KEY + } + }, + openai: { + options: { + apiKey: env.OPENAI_API_KEY + } + } + } +}; + +const server = await createOpencodeServer(sandbox, { + directory: '/workspace', + config +}); +``` + + +### Port configuration + +Change the default port (4096) if needed: + + +``` +const server = await createOpencodeServer(sandbox, { + port: 8080, + directory: '/workspace' +}); +``` + + +### Working directory + +OpenCode uses the specified directory as its working directory: + + +``` +// Start OpenCode in a specific project directory +const server = await createOpencodeServer(sandbox, { + directory: '/home/user/projects/my-app' +}); +``` + + +If no directory is specified, OpenCode uses the container's current working directory. + +## Process reuse + +The OpenCode integration automatically detects and reuses existing server processes. If an OpenCode server is already running on the same port, subsequent calls will reuse it instead of starting a new one: + + +``` +// First request starts the server +const server1 = await createOpencodeServer(sandbox, { port: 4096 }); + +// Second request reuses the existing server +const server2 = await createOpencodeServer(sandbox, { port: 4096 }); + +// Both references point to the same running process +``` + + +This prevents resource waste and improves response times for subsequent requests. + +## Error handling + +Handle server startup failures: + + +``` +try { + const server = await createOpencodeServer(sandbox, { + directory: '/home/user/project', + config: { + provider: { + anthropic: { + options: { + apiKey: env.ANTHROPIC_API_KEY + } + } + } + } + }); + + return proxyToOpencode(request, sandbox, server); +} catch (error) { + if (error.code === 'OPENCODE_STARTUP_FAILED') { + console.error('Startup failed:', error.context.stderr); + return new Response('OpenCode failed to start', { status: 500 }); + } + throw error; +} +``` + + +## Related resources + +- [OpenCode SDK documentation](https://docs.opencode.ai/) +- [Code Interpreter API](/sandbox-sdk/api/interpreter/) - For simple Python/JavaScript execution +- [Process management](/sandbox-sdk/guides/background-processes/) - Managing long-running processes +- [Expose services](/sandbox-sdk/guides/expose-services/) - Port forwarding patterns diff --git a/src/content/docs/sandbox/index.mdx b/src/content/docs/sandbox/index.mdx index 434fa3641586487..cfd11b20971ad93 100644 --- a/src/content/docs/sandbox/index.mdx +++ b/src/content/docs/sandbox/index.mdx @@ -179,6 +179,12 @@ Execute Python and JavaScript code with rich outputs including charts, tables, a + + +Run OpenCode AI coding agent inside Sandbox containers. Get web UI access or programmatic SDK control for AI-powered code editing and autonomous development workflows. + + + Mount S3-compatible object storage (R2, S3, GCS, and more) as local filesystems. Access buckets using standard file operations with data that persists across sandbox lifecycles. Production deployment required. @@ -193,7 +199,7 @@ Build powerful applications with Sandbox: ### AI Code Execution -Execute code generated by Large Language Models safely and reliably. Perfect for AI agents, code assistants, and autonomous systems that need to run untrusted code. +Execute code generated by Large Language Models safely and reliably. Perfect for AI agents, code assistants, and autonomous systems that need to run untrusted code. Run OpenCode AI agent with web UI or programmatic SDK access for AI-powered code editing workflows. ### Data Analysis & Notebooks