mirror of
https://github.com/cloudflare/cloudflare-docs.git
synced 2026-01-16 23:11:06 +00:00
Add docs for Sandbox SDK (#25791)
* Add landing page for sandbox SDK * Add first turorial * Start with API reference * Complete v1 of API reference * Use TypeScriptExample component * Fix broken code snippets * Update codeowners * Add how-to guides * Add concepts * Fix syntax errors * Add tutorials * Simplify configuration reference * Simplify guides * Remove unnecessary prerequisites * Simplify concepts * Cleanup API reference * Update icons * Simplify getting started page * Update code executor example * Update tutorials * Fix frontmatter * Fix links
This commit is contained in:
parent
db9b73f988
commit
99c717e166
35 changed files with 5304 additions and 0 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
|
@ -138,6 +138,7 @@
|
|||
/src/content/docs/workers/observability/ @irvinebroque @mikenomitch @rohinlohe @kodster28 @cloudflare/pcx-technical-writing
|
||||
/src/content/docs/workers/static-assets @irvinebroque @GregBrimble @WalshyDev @kodster28 @cloudflare/deploy-config @cloudflare/pcx-technical-writing
|
||||
/src/content/docs/workflows/ @elithrar @celso @cloudflare/pcx-technical-writing
|
||||
/src/content/docs/sandbox/ @whoiskatrin @ghostwriternr @cloudflare/pcx-technical-writing @ai-agents
|
||||
|
||||
# DDoS Protection
|
||||
|
||||
|
|
|
|||
219
src/content/docs/sandbox/api/commands.mdx
Normal file
219
src/content/docs/sandbox/api/commands.mdx
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
---
|
||||
title: Commands
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
import { Details, TypeScriptExample } from "~/components";
|
||||
|
||||
Execute commands and manage background processes in the sandbox's isolated container environment.
|
||||
|
||||
## Methods
|
||||
|
||||
### `exec()`
|
||||
|
||||
Execute a command and return the complete result.
|
||||
|
||||
```ts
|
||||
const result = await sandbox.exec(command: string, options?: ExecOptions): Promise<ExecuteResponse>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `command` - The command to execute (can include arguments)
|
||||
- `options` (optional):
|
||||
- `stream` - Enable streaming callbacks (default: `false`)
|
||||
- `onOutput` - Callback for real-time output: `(stream: 'stdout' | 'stderr', data: string) => void`
|
||||
- `timeout` - Maximum execution time in milliseconds
|
||||
|
||||
**Returns**: `Promise<ExecuteResponse>` with `success`, `stdout`, `stderr`, `exitCode`
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const result = await sandbox.exec('npm run build');
|
||||
|
||||
if (result.success) {
|
||||
console.log('Build output:', result.stdout);
|
||||
} else {
|
||||
console.error('Build failed:', result.stderr);
|
||||
}
|
||||
|
||||
// With streaming
|
||||
await sandbox.exec('npm install', {
|
||||
stream: true,
|
||||
onOutput: (stream, data) => console.log(`[${stream}] ${data}`)
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `execStream()`
|
||||
|
||||
Execute a command and return a Server-Sent Events stream for real-time processing.
|
||||
|
||||
```ts
|
||||
const stream = await sandbox.execStream(command: string, options?: ExecOptions): Promise<ReadableStream>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `command` - The command to execute
|
||||
- `options` - Same as `exec()`
|
||||
|
||||
**Returns**: `Promise<ReadableStream>` emitting `ExecEvent` objects (`start`, `stdout`, `stderr`, `complete`, `error`)
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
|
||||
|
||||
const stream = await sandbox.execStream('npm run build');
|
||||
|
||||
for await (const event of parseSSEStream<ExecEvent>(stream)) {
|
||||
switch (event.type) {
|
||||
case 'stdout':
|
||||
console.log('Output:', event.data);
|
||||
break;
|
||||
case 'complete':
|
||||
console.log('Exit code:', event.exitCode);
|
||||
break;
|
||||
case 'error':
|
||||
console.error('Failed:', event.error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `startProcess()`
|
||||
|
||||
Start a long-running background process.
|
||||
|
||||
```ts
|
||||
const process = await sandbox.startProcess(command: string, options?: ProcessOptions): Promise<ProcessInfo>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `command` - The command to start as a background process
|
||||
- `options` (optional):
|
||||
- `cwd` - Working directory
|
||||
- `env` - Environment variables
|
||||
|
||||
**Returns**: `Promise<ProcessInfo>` with `id`, `pid`, `command`, `status`
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const server = await sandbox.startProcess('python -m http.server 8000');
|
||||
console.log('Started with PID:', server.pid);
|
||||
|
||||
// With custom environment
|
||||
const app = await sandbox.startProcess('node app.js', {
|
||||
cwd: '/workspace/my-app',
|
||||
env: { NODE_ENV: 'production', PORT: '3000' }
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `listProcesses()`
|
||||
|
||||
List all running processes.
|
||||
|
||||
```ts
|
||||
const processes = await sandbox.listProcesses(): Promise<ProcessInfo[]>
|
||||
```
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const processes = await sandbox.listProcesses();
|
||||
|
||||
for (const proc of processes) {
|
||||
console.log(`${proc.id}: ${proc.command} (PID ${proc.pid})`);
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `killProcess()`
|
||||
|
||||
Terminate a specific process.
|
||||
|
||||
```ts
|
||||
await sandbox.killProcess(processId: string, signal?: string): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `processId` - The process ID (from `startProcess()` or `listProcesses()`)
|
||||
- `signal` - Signal to send (default: `"SIGTERM"`)
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const server = await sandbox.startProcess('python -m http.server 8000');
|
||||
await sandbox.killProcess(server.id);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `killAllProcesses()`
|
||||
|
||||
Terminate all running processes.
|
||||
|
||||
```ts
|
||||
await sandbox.killAllProcesses(): Promise<void>
|
||||
```
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.killAllProcesses();
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `streamProcessLogs()`
|
||||
|
||||
Stream logs from a running process in real-time.
|
||||
|
||||
```ts
|
||||
const stream = await sandbox.streamProcessLogs(processId: string): Promise<ReadableStream>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `processId` - The process ID
|
||||
|
||||
**Returns**: `Promise<ReadableStream>` emitting `LogEvent` objects
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { parseSSEStream, type LogEvent } from '@cloudflare/sandbox';
|
||||
|
||||
const server = await sandbox.startProcess('node server.js');
|
||||
const logStream = await sandbox.streamProcessLogs(server.id);
|
||||
|
||||
for await (const log of parseSSEStream<LogEvent>(logStream)) {
|
||||
console.log(`[${log.timestamp}] ${log.data}`);
|
||||
|
||||
if (log.data.includes('Server started')) break;
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `getProcessLogs()`
|
||||
|
||||
Get accumulated logs from a process.
|
||||
|
||||
```ts
|
||||
const logs = await sandbox.getProcessLogs(processId: string): Promise<string>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `processId` - The process ID
|
||||
|
||||
**Returns**: `Promise<string>` with all accumulated output
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const server = await sandbox.startProcess('node server.js');
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
const logs = await sandbox.getProcessLogs(server.id);
|
||||
console.log('Server logs:', logs);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Background processes guide](/sandbox/guides/background-processes/) - Managing long-running processes
|
||||
- [Files API](/sandbox/api/files/) - File operations
|
||||
167
src/content/docs/sandbox/api/files.mdx
Normal file
167
src/content/docs/sandbox/api/files.mdx
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
---
|
||||
title: Files
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
Read, write, and manage files in the sandbox filesystem. All paths are absolute (e.g., `/workspace/app.js`).
|
||||
|
||||
## Methods
|
||||
|
||||
### `writeFile()`
|
||||
|
||||
Write content to a file.
|
||||
|
||||
```ts
|
||||
await sandbox.writeFile(path: string, content: string, options?: WriteFileOptions): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Absolute path to the file
|
||||
- `content` - Content to write
|
||||
- `options` (optional):
|
||||
- `encoding` - File encoding (default: `"utf-8"`)
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.writeFile('/workspace/app.js', `console.log('Hello!');`);
|
||||
|
||||
// Binary data
|
||||
await sandbox.writeFile('/tmp/image.png', base64Data, { encoding: 'base64' });
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `readFile()`
|
||||
|
||||
Read a file from the sandbox.
|
||||
|
||||
```ts
|
||||
const file = await sandbox.readFile(path: string, options?: ReadFileOptions): Promise<FileInfo>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Absolute path to the file
|
||||
- `options` (optional):
|
||||
- `encoding` - File encoding (default: `"utf-8"`)
|
||||
|
||||
**Returns**: `Promise<FileInfo>` with `content` and `encoding`
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const file = await sandbox.readFile('/workspace/package.json');
|
||||
const pkg = JSON.parse(file.content);
|
||||
|
||||
// Binary data
|
||||
const image = await sandbox.readFile('/tmp/image.png', { encoding: 'base64' });
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `mkdir()`
|
||||
|
||||
Create a directory.
|
||||
|
||||
```ts
|
||||
await sandbox.mkdir(path: string, options?: MkdirOptions): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Absolute path to the directory
|
||||
- `options` (optional):
|
||||
- `recursive` - Create parent directories if needed (default: `false`)
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.mkdir('/workspace/src');
|
||||
|
||||
// Nested directories
|
||||
await sandbox.mkdir('/workspace/src/components/ui', { recursive: true });
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `deleteFile()`
|
||||
|
||||
Delete a file.
|
||||
|
||||
```ts
|
||||
await sandbox.deleteFile(path: string): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Absolute path to the file
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.deleteFile('/workspace/temp.txt');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `renameFile()`
|
||||
|
||||
Rename a file.
|
||||
|
||||
```ts
|
||||
await sandbox.renameFile(oldPath: string, newPath: string): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `oldPath` - Current file path
|
||||
- `newPath` - New file path
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.renameFile('/workspace/draft.txt', '/workspace/final.txt');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `moveFile()`
|
||||
|
||||
Move a file to a different directory.
|
||||
|
||||
```ts
|
||||
await sandbox.moveFile(sourcePath: string, destinationPath: string): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `sourcePath` - Current file path
|
||||
- `destinationPath` - Destination path
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.moveFile('/tmp/download.txt', '/workspace/data.txt');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `gitCheckout()`
|
||||
|
||||
Clone a git repository.
|
||||
|
||||
```ts
|
||||
await sandbox.gitCheckout(repoUrl: string, options?: GitCheckoutOptions): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `repoUrl` - Git repository URL
|
||||
- `options` (optional):
|
||||
- `branch` - Branch to checkout (default: main branch)
|
||||
- `targetDir` - Directory to clone into (default: repo name)
|
||||
- `depth` - Clone depth for shallow clone
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.gitCheckout('https://github.com/user/repo');
|
||||
|
||||
// Specific branch
|
||||
await sandbox.gitCheckout('https://github.com/user/repo', {
|
||||
branch: 'develop',
|
||||
targetDir: 'my-project'
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Manage files guide](/sandbox/guides/manage-files/) - Detailed guide with best practices
|
||||
- [Commands API](/sandbox/api/commands/) - Execute commands
|
||||
68
src/content/docs/sandbox/api/index.mdx
Normal file
68
src/content/docs/sandbox/api/index.mdx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
title: API Reference
|
||||
pcx_content_type: navigation
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
import { LinkTitleCard, CardGrid } from "~/components";
|
||||
|
||||
The Sandbox SDK provides a comprehensive API for executing code, managing files, running processes, and exposing services in isolated sandboxes. All operations are performed through the `Sandbox` instance you obtain via `getSandbox()`.
|
||||
|
||||
## Getting a sandbox instance
|
||||
|
||||
```typescript
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, 'user-123');
|
||||
```
|
||||
|
||||
The sandbox ID should be unique per user or session. The same ID will always return the same sandbox instance with persistent state.
|
||||
|
||||
## API organization
|
||||
|
||||
The Sandbox SDK is organized into focused APIs:
|
||||
|
||||
<CardGrid>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Commands"
|
||||
href="/sandbox/api/commands/"
|
||||
icon="seti:shell"
|
||||
>
|
||||
Execute commands and stream output. Includes `exec()`, `execStream()`, and background process management.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Files"
|
||||
href="/sandbox/api/files/"
|
||||
icon="document"
|
||||
>
|
||||
Read, write, and manage files in the sandbox filesystem. Includes directory operations and file metadata.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Code Interpreter"
|
||||
href="/sandbox/api/interpreter/"
|
||||
icon="laptop"
|
||||
>
|
||||
Execute Python and JavaScript code with rich outputs including charts, tables, and formatted data.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Ports"
|
||||
href="/sandbox/api/ports/"
|
||||
icon="download"
|
||||
>
|
||||
Expose services running in the sandbox via preview URLs. Access web servers and APIs from the internet.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Sessions"
|
||||
href="/sandbox/api/sessions/"
|
||||
icon="seti:plan"
|
||||
>
|
||||
Advanced: Create isolated execution contexts with persistent shell state. Configure environment variables and manage container lifecycle.
|
||||
</LinkTitleCard>
|
||||
|
||||
</CardGrid>
|
||||
186
src/content/docs/sandbox/api/interpreter.mdx
Normal file
186
src/content/docs/sandbox/api/interpreter.mdx
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
---
|
||||
title: Code Interpreter
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
Execute Python, JavaScript, and TypeScript code with support for data visualizations, tables, and rich output formats. Contexts maintain state (variables, imports, functions) across executions.
|
||||
|
||||
## Methods
|
||||
|
||||
### `createCodeContext()`
|
||||
|
||||
Create a persistent execution context for running code.
|
||||
|
||||
```ts
|
||||
const context = await sandbox.createCodeContext(options?: CreateContextOptions): Promise<CodeContext>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `options` (optional):
|
||||
- `language` - `"python" | "javascript" | "typescript"` (default: `"python"`)
|
||||
- `cwd` - Working directory (default: `"/workspace"`)
|
||||
- `envVars` - Environment variables
|
||||
- `timeout` - Request timeout in milliseconds (default: 30000)
|
||||
|
||||
**Returns**: `Promise<CodeContext>` with `id`, `language`, `cwd`, `createdAt`, `lastUsed`
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const ctx = await sandbox.createCodeContext({
|
||||
language: 'python',
|
||||
envVars: { API_KEY: env.API_KEY }
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `runCode()`
|
||||
|
||||
Execute code in a context and return the complete result.
|
||||
|
||||
```ts
|
||||
const result = await sandbox.runCode(code: string, options?: RunCodeOptions): Promise<ExecutionResult>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `code` - The code to execute (required)
|
||||
- `options` (optional):
|
||||
- `context` - Context to run in (recommended - see below)
|
||||
- `language` - `"python" | "javascript" | "typescript"` (default: `"python"`)
|
||||
- `timeout` - Execution timeout in milliseconds (default: 60000)
|
||||
- `onStdout`, `onStderr`, `onResult`, `onError` - Streaming callbacks
|
||||
|
||||
**Returns**: `Promise<ExecutionResult>` with:
|
||||
- `code` - The executed code
|
||||
- `logs` - `stdout` and `stderr` arrays
|
||||
- `results` - Array of rich outputs (see [Rich Output Formats](#rich-output-formats))
|
||||
- `error` - Execution error if any
|
||||
- `executionCount` - Execution counter
|
||||
|
||||
**Recommended usage - create explicit context**:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const ctx = await sandbox.createCodeContext({ language: 'python' });
|
||||
|
||||
await sandbox.runCode('import math; radius = 5', { context: ctx });
|
||||
const result = await sandbox.runCode('math.pi * radius ** 2', { context: ctx });
|
||||
|
||||
console.log(result.results[0].text); // "78.53981633974483"
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
:::note[Default context behavior]
|
||||
If no `context` is provided, a default context is automatically created/reused for the specified `language`. While convenient for quick tests, **explicitly creating contexts is recommended** for production use to maintain predictable state.
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const result = await sandbox.runCode(`
|
||||
data = [1, 2, 3, 4, 5]
|
||||
print(f"Sum: {sum(data)}")
|
||||
sum(data)
|
||||
`, { language: 'python' });
|
||||
|
||||
console.log(result.logs.stdout); // ["Sum: 15"]
|
||||
console.log(result.results[0].text); // "15"
|
||||
```
|
||||
</TypeScriptExample>
|
||||
:::
|
||||
|
||||
**Error handling**:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const result = await sandbox.runCode('x = 1 / 0', { language: 'python' });
|
||||
|
||||
if (result.error) {
|
||||
console.error(result.error.name); // "ZeroDivisionError"
|
||||
console.error(result.error.value); // "division by zero"
|
||||
console.error(result.error.traceback); // Stack trace array
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `listCodeContexts()`
|
||||
|
||||
List all active code execution contexts.
|
||||
|
||||
```ts
|
||||
const contexts = await sandbox.listCodeContexts(): Promise<CodeContext[]>
|
||||
```
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const contexts = await sandbox.listCodeContexts();
|
||||
console.log(`Found ${contexts.length} contexts`);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `deleteCodeContext()`
|
||||
|
||||
Delete a code execution context and free its resources.
|
||||
|
||||
```ts
|
||||
await sandbox.deleteCodeContext(contextId: string): Promise<void>
|
||||
```
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const ctx = await sandbox.createCodeContext({ language: 'python' });
|
||||
await sandbox.runCode('print("Hello")', { context: ctx });
|
||||
await sandbox.deleteCodeContext(ctx.id);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Rich Output Formats
|
||||
|
||||
Results include: `text`, `html`, `png`, `jpeg`, `svg`, `latex`, `markdown`, `json`, `chart`, `data`
|
||||
|
||||
**Charts (matplotlib)**:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const result = await sandbox.runCode(`
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
x = np.linspace(0, 10, 100)
|
||||
plt.plot(x, np.sin(x))
|
||||
plt.show()
|
||||
`, { language: 'python' });
|
||||
|
||||
if (result.results[0]?.png) {
|
||||
const imageBuffer = Buffer.from(result.results[0].png, 'base64');
|
||||
return new Response(imageBuffer, {
|
||||
headers: { 'Content-Type': 'image/png' }
|
||||
});
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
**Tables (pandas)**:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const result = await sandbox.runCode(`
|
||||
import pandas as pd
|
||||
df = pd.DataFrame({'Name': ['Alice', 'Bob'], 'Age': [25, 30]})
|
||||
df
|
||||
`, { language: 'python' });
|
||||
|
||||
if (result.results[0]?.html) {
|
||||
return new Response(result.results[0].html, {
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
});
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Build an AI Code Executor](/sandbox/tutorials/ai-code-executor/) - Complete tutorial
|
||||
- [Commands API](/sandbox/api/commands/) - Lower-level command execution
|
||||
- [Files API](/sandbox/api/files/) - File operations
|
||||
86
src/content/docs/sandbox/api/ports.mdx
Normal file
86
src/content/docs/sandbox/api/ports.mdx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
title: Ports
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
Expose services running in your sandbox via public preview URLs. See [Preview URLs concept](/sandbox/concepts/preview-urls/) for details.
|
||||
|
||||
## Methods
|
||||
|
||||
### `exposePort()`
|
||||
|
||||
Expose a port and get a preview URL.
|
||||
|
||||
```ts
|
||||
const response = await sandbox.exposePort(port: number, options?: ExposePortOptions): Promise<ExposePortResponse>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `port` - Port number to expose (1024-65535)
|
||||
- `options` (optional):
|
||||
- `name` - Friendly name for the port
|
||||
|
||||
**Returns**: `Promise<ExposePortResponse>` with `port`, `exposedAt` (preview URL), `name`
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.startProcess('python -m http.server 8000');
|
||||
const exposed = await sandbox.exposePort(8000);
|
||||
|
||||
console.log('Available at:', exposed.exposedAt);
|
||||
// https://abc123-8000.sandbox.workers.dev
|
||||
|
||||
// Multiple services with names
|
||||
await sandbox.startProcess('node api.js');
|
||||
const api = await sandbox.exposePort(3000, { name: 'api' });
|
||||
|
||||
await sandbox.startProcess('npm run dev');
|
||||
const frontend = await sandbox.exposePort(5173, { name: 'frontend' });
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `unexposePort()`
|
||||
|
||||
Remove an exposed port and close its preview URL.
|
||||
|
||||
```ts
|
||||
await sandbox.unexposePort(port: number): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `port` - Port number to unexpose
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.unexposePort(8000);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `getExposedPorts()`
|
||||
|
||||
Get information about all currently exposed ports.
|
||||
|
||||
```ts
|
||||
const response = await sandbox.getExposedPorts(): Promise<GetExposedPortsResponse>
|
||||
```
|
||||
|
||||
**Returns**: `Promise<GetExposedPortsResponse>` with `ports` array (containing `port`, `exposedAt`, `name`)
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const { ports } = await sandbox.getExposedPorts();
|
||||
|
||||
for (const port of ports) {
|
||||
console.log(`${port.name || port.port}: ${port.exposedAt}`);
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Preview URLs concept](/sandbox/concepts/preview-urls/) - How preview URLs work
|
||||
- [Commands API](/sandbox/api/commands/) - Start background processes
|
||||
155
src/content/docs/sandbox/api/sessions.mdx
Normal file
155
src/content/docs/sandbox/api/sessions.mdx
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
---
|
||||
title: Sessions
|
||||
pcx_content_type: configuration
|
||||
sidebar:
|
||||
order: 5
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
Create isolated execution contexts within a sandbox. Each session maintains its own shell state, environment variables, and working directory. See [Session management concept](/sandbox/concepts/sessions/) for details.
|
||||
|
||||
:::note
|
||||
Every sandbox has a default session that automatically maintains shell state. Create additional sessions when you need isolated shell contexts for different environments or parallel workflows.
|
||||
:::
|
||||
|
||||
## Methods
|
||||
|
||||
### `createSession()`
|
||||
|
||||
Create a new isolated execution session.
|
||||
|
||||
```ts
|
||||
const session = await sandbox.createSession(options?: SessionOptions): Promise<ExecutionSession>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `options` (optional):
|
||||
- `id` - Custom session ID (auto-generated if not provided)
|
||||
- `env` - Environment variables for this session
|
||||
- `cwd` - Working directory (default: `"/workspace"`)
|
||||
|
||||
**Returns**: `Promise<ExecutionSession>` with all sandbox methods bound to this session
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Multiple isolated environments
|
||||
const prodSession = await sandbox.createSession({
|
||||
id: 'prod',
|
||||
env: { NODE_ENV: 'production', API_URL: 'https://api.example.com' },
|
||||
cwd: '/workspace/prod'
|
||||
});
|
||||
|
||||
const testSession = await sandbox.createSession({
|
||||
id: 'test',
|
||||
env: { NODE_ENV: 'test', API_URL: 'http://localhost:3000' },
|
||||
cwd: '/workspace/test'
|
||||
});
|
||||
|
||||
// Run in parallel
|
||||
const [prodResult, testResult] = await Promise.all([
|
||||
prodSession.exec('npm run build'),
|
||||
testSession.exec('npm run build')
|
||||
]);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `getSession()`
|
||||
|
||||
Retrieve an existing session by ID.
|
||||
|
||||
```ts
|
||||
const session = await sandbox.getSession(sessionId: string): Promise<ExecutionSession>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `sessionId` - ID of an existing session
|
||||
|
||||
**Returns**: `Promise<ExecutionSession>` bound to the specified session
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// First request - create session
|
||||
const session = await sandbox.createSession({ id: 'user-123' });
|
||||
await session.exec('git clone https://github.com/user/repo.git');
|
||||
await session.exec('cd repo && npm install');
|
||||
|
||||
// Second request - resume session (environment and cwd preserved)
|
||||
const session = await sandbox.getSession('user-123');
|
||||
const result = await session.exec('cd repo && npm run build');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `setEnvVars()`
|
||||
|
||||
Set environment variables in the sandbox.
|
||||
|
||||
```ts
|
||||
await sandbox.setEnvVars(envVars: Record<string, string>): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `envVars` - Key-value pairs of environment variables to set
|
||||
|
||||
:::caution
|
||||
Call `setEnvVars()` **before** any other sandbox operations to ensure environment variables are available from the start.
|
||||
:::
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const sandbox = getSandbox(env.Sandbox, 'user-123');
|
||||
|
||||
// Set environment variables first
|
||||
await sandbox.setEnvVars({
|
||||
API_KEY: env.OPENAI_API_KEY,
|
||||
DATABASE_URL: env.DATABASE_URL,
|
||||
NODE_ENV: 'production'
|
||||
});
|
||||
|
||||
// Now commands can access these variables
|
||||
await sandbox.exec('python script.py');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### `destroy()`
|
||||
|
||||
Destroy the sandbox container and free up resources.
|
||||
|
||||
```ts
|
||||
await sandbox.destroy(): Promise<void>
|
||||
```
|
||||
|
||||
:::note
|
||||
Containers automatically sleep after 3 minutes of inactivity, but still count toward account limits. Use `destroy()` to immediately free up resources.
|
||||
:::
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
async function executeCode(code: string): Promise<string> {
|
||||
const sandbox = getSandbox(env.Sandbox, `temp-${Date.now()}`);
|
||||
|
||||
try {
|
||||
await sandbox.writeFile('/tmp/code.py', code);
|
||||
const result = await sandbox.exec('python /tmp/code.py');
|
||||
return result.stdout;
|
||||
} finally {
|
||||
await sandbox.destroy();
|
||||
}
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## ExecutionSession methods
|
||||
|
||||
The `ExecutionSession` object has all sandbox methods bound to the specific session:
|
||||
|
||||
**Commands**: [`exec()`](/sandbox/api/commands/#exec), [`execStream()`](/sandbox/api/commands/#execstream)
|
||||
**Processes**: [`startProcess()`](/sandbox/api/commands/#startprocess), [`listProcesses()`](/sandbox/api/commands/#listprocesses), [`killProcess()`](/sandbox/api/commands/#killprocess), [`killAllProcesses()`](/sandbox/api/commands/#killallprocesses), [`getProcessLogs()`](/sandbox/api/commands/#getprocesslogs), [`streamProcessLogs()`](/sandbox/api/commands/#streamprocesslogs)
|
||||
**Files**: [`writeFile()`](/sandbox/api/files/#writefile), [`readFile()`](/sandbox/api/files/#readfile), [`mkdir()`](/sandbox/api/files/#mkdir), [`deleteFile()`](/sandbox/api/files/#deletefile), [`renameFile()`](/sandbox/api/files/#renamefile), [`moveFile()`](/sandbox/api/files/#movefile), [`gitCheckout()`](/sandbox/api/files/#gitcheckout)
|
||||
**Environment**: [`setEnvVars()`](/sandbox/api/sessions/#setenvvars)
|
||||
**Code Interpreter**: [`createCodeContext()`](/sandbox/api/interpreter/#createcodecontext), [`runCode()`](/sandbox/api/interpreter/#runcode), [`listCodeContexts()`](/sandbox/api/interpreter/#listcodecontexts), [`deleteCodeContext()`](/sandbox/api/interpreter/#deletecodecontext)
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Session management concept](/sandbox/concepts/sessions/) - How sessions work
|
||||
- [Commands API](/sandbox/api/commands/) - Execute commands
|
||||
139
src/content/docs/sandbox/concepts/architecture.mdx
Normal file
139
src/content/docs/sandbox/concepts/architecture.mdx
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
---
|
||||
title: Architecture
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
The Sandbox SDK provides isolated code execution environments on Cloudflare's edge network. It combines three Cloudflare technologies:
|
||||
|
||||
- **Workers** - JavaScript runtime at the edge
|
||||
- **Durable Objects** - Stateful compute with persistent storage
|
||||
- **Containers** - Isolated execution environments with full Linux capabilities
|
||||
|
||||
## Three-layer architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Your Application │
|
||||
│ (Cloudflare Worker) │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
├─ getSandbox()
|
||||
├─ exec()
|
||||
├─ writeFile()
|
||||
│
|
||||
┌────────────────▼──────────────────┐
|
||||
│ Container-enabled Durable Object │
|
||||
│ (SDK methods via RPC from Worker) │
|
||||
└───────────────────────────────────┘
|
||||
│ HTTP/JSON
|
||||
│
|
||||
┌───────▼───────┐
|
||||
│ Durable Object │ Layer 2: State Management
|
||||
│ (Persistent) │
|
||||
└───────┬───────┘
|
||||
│ Container Protocol
|
||||
│
|
||||
┌───────▼───────┐
|
||||
│ Container │ Layer 3: Isolated Execution
|
||||
│ (Linux + Bun) │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
### Layer 1: Client SDK
|
||||
|
||||
The developer-facing API you use in your Workers:
|
||||
|
||||
```typescript
|
||||
import { getSandbox } from "@cloudflare/sandbox";
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
|
||||
const result = await sandbox.exec("python script.py");
|
||||
```
|
||||
|
||||
**Purpose**: Provide a clean, type-safe TypeScript interface for all sandbox operations.
|
||||
|
||||
### Layer 2: Durable Object
|
||||
|
||||
Manages sandbox lifecycle and routing:
|
||||
|
||||
```typescript
|
||||
export class Sandbox extends DurableObject<Env> {
|
||||
// Extends Cloudflare Container for isolation
|
||||
// Routes requests between client and container
|
||||
// Manages preview URLs and state
|
||||
}
|
||||
```
|
||||
|
||||
**Purpose**: Provide persistent, stateful sandbox instances with unique identities.
|
||||
|
||||
**Why Durable Objects**:
|
||||
|
||||
- **Persistent identity** - Same sandbox ID always routes to same instance
|
||||
- **State management** - Filesystem and processes persist between requests
|
||||
- **Geographic distribution** - Sandboxes run close to users
|
||||
- **Automatic scaling** - Cloudflare manages provisioning
|
||||
|
||||
### Layer 3: Container Runtime
|
||||
|
||||
Executes code in isolation with full Linux capabilities.
|
||||
|
||||
**Purpose**: Safely execute untrusted code.
|
||||
|
||||
**Why containers**:
|
||||
|
||||
- **True isolation** - Process-level isolation with namespaces
|
||||
- **Full environment** - Real Linux with Python, Node.js, Git, etc.
|
||||
- **Resource limits** - CPU, memory, disk constraints
|
||||
|
||||
## Request flow
|
||||
|
||||
When you execute a command:
|
||||
|
||||
```typescript
|
||||
await sandbox.exec("python script.py");
|
||||
```
|
||||
|
||||
1. **Client SDK** validates parameters and sends HTTP request to Durable Object
|
||||
2. **Durable Object** authenticates and routes to container runtime
|
||||
3. **Container Runtime** validates inputs, executes command, captures output
|
||||
4. **Response flows back** through all layers with proper error transformation
|
||||
|
||||
## State persistence
|
||||
|
||||
Sandboxes maintain state across requests:
|
||||
|
||||
**Filesystem**:
|
||||
|
||||
```typescript
|
||||
// Request 1
|
||||
await sandbox.writeFile("/workspace/data.txt", "hello");
|
||||
|
||||
// Request 2 (minutes later)
|
||||
const file = await sandbox.readFile("/workspace/data.txt");
|
||||
// Returns 'hello' - file persisted
|
||||
```
|
||||
|
||||
**Processes**:
|
||||
|
||||
```typescript
|
||||
// Request 1
|
||||
await sandbox.startProcess("node server.js");
|
||||
|
||||
// Request 2 (minutes later)
|
||||
const processes = await sandbox.listProcesses();
|
||||
// Server still running
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
**Cold start**: 100-300ms (container initialization)
|
||||
**Warm start**: \<10ms (reuse existing container)
|
||||
**Network latency**: 10-50ms (edge-to-edge)
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Sandbox lifecycle](/sandbox/concepts/sandboxes/) - How sandboxes are created and managed
|
||||
- [Container runtime](/sandbox/concepts/containers/) - Inside the execution environment
|
||||
- [Security model](/sandbox/concepts/security/) - How isolation and validation work
|
||||
- [Session management](/sandbox/concepts/sessions/) - Advanced state management
|
||||
131
src/content/docs/sandbox/concepts/containers.mdx
Normal file
131
src/content/docs/sandbox/concepts/containers.mdx
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
title: Container runtime
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
Each sandbox runs in an isolated Linux container based on Ubuntu 22.04.
|
||||
|
||||
## Pre-installed software
|
||||
|
||||
The base container comes pre-packaged with a full development environment:
|
||||
|
||||
**Languages and runtimes**:
|
||||
- Python 3.11 (with pip)
|
||||
- Node.js 20 LTS (with npm)
|
||||
- Bun (JavaScript/TypeScript runtime)
|
||||
|
||||
**Python packages**:
|
||||
- NumPy - Numerical computing
|
||||
- pandas - Data analysis
|
||||
- Matplotlib - Plotting and visualization
|
||||
- IPython - Interactive Python
|
||||
|
||||
**Development tools**:
|
||||
- Git - Version control
|
||||
- Build tools (gcc, make, pkg-config)
|
||||
- Text editors (vim, nano)
|
||||
- Process monitoring (htop, procps)
|
||||
|
||||
**Utilities**:
|
||||
- curl, wget - HTTP clients
|
||||
- jq - JSON processor
|
||||
- Network tools (ping, dig, netstat)
|
||||
- Compression (zip, unzip)
|
||||
|
||||
Install additional software at runtime or [customize the base image](/sandbox/configuration/dockerfile/):
|
||||
|
||||
```bash
|
||||
# Python packages
|
||||
pip install scikit-learn tensorflow
|
||||
|
||||
# Node.js packages
|
||||
npm install express
|
||||
|
||||
# System packages
|
||||
apt-get install redis-server
|
||||
```
|
||||
|
||||
## Filesystem
|
||||
|
||||
The container provides a standard Linux filesystem. You can read and write anywhere you have permissions.
|
||||
|
||||
**Standard directories**:
|
||||
- `/workspace` - Default working directory for user code
|
||||
- `/tmp` - Temporary files
|
||||
- `/home` - User home directory
|
||||
- `/usr/bin`, `/usr/local/bin` - Executable binaries
|
||||
|
||||
**Example**:
|
||||
```typescript
|
||||
await sandbox.writeFile('/workspace/app.py', 'print("Hello")');
|
||||
await sandbox.writeFile('/tmp/cache.json', '{}');
|
||||
await sandbox.exec('ls -la /workspace');
|
||||
```
|
||||
|
||||
## Process management
|
||||
|
||||
Processes run as you'd expect in a regular Linux environment.
|
||||
|
||||
**Foreground processes** (`exec()`):
|
||||
```typescript
|
||||
const result = await sandbox.exec('npm test');
|
||||
// Waits for completion, returns output
|
||||
```
|
||||
|
||||
**Background processes** (`startProcess()`):
|
||||
```typescript
|
||||
const process = await sandbox.startProcess('node server.js');
|
||||
// Returns immediately, process runs in background
|
||||
```
|
||||
|
||||
## Network capabilities
|
||||
|
||||
**Outbound connections** work:
|
||||
```bash
|
||||
curl https://api.example.com/data
|
||||
pip install requests
|
||||
npm install express
|
||||
```
|
||||
|
||||
**Inbound connections** require port exposure:
|
||||
```typescript
|
||||
await sandbox.startProcess('python -m http.server 8000');
|
||||
const exposed = await sandbox.exposePort(8000);
|
||||
console.log(exposed.exposedAt); // Public URL
|
||||
```
|
||||
|
||||
**Localhost** works within sandbox:
|
||||
```bash
|
||||
redis-server & # Start server
|
||||
redis-cli ping # Connect locally
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
**Between sandboxes** (isolated):
|
||||
- Each sandbox is a separate container
|
||||
- Filesystem, memory and network are all isolated
|
||||
|
||||
**Within sandbox** (shared):
|
||||
- All processes see the same files
|
||||
- Processes can communicate with each other
|
||||
- Environment variables are session-scoped
|
||||
|
||||
To run untrusted code, use separate sandboxes per user:
|
||||
```typescript
|
||||
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
**Cannot**:
|
||||
- Load kernel modules or access host hardware
|
||||
- Run nested containers (no Docker-in-Docker)
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Architecture](/sandbox/concepts/architecture/) - How containers fit in the system
|
||||
- [Security model](/sandbox/concepts/security/) - Container isolation details
|
||||
- [Sandbox lifecycle](/sandbox/concepts/sandboxes/) - Container lifecycle management
|
||||
23
src/content/docs/sandbox/concepts/index.mdx
Normal file
23
src/content/docs/sandbox/concepts/index.mdx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
title: Concepts
|
||||
pcx_content_type: navigation
|
||||
sidebar:
|
||||
order: 5
|
||||
---
|
||||
|
||||
These pages explain how the Sandbox SDK works, why it's designed the way it is, and the concepts you need to understand to use it effectively.
|
||||
|
||||
## Available concepts
|
||||
|
||||
- [Architecture](/sandbox/concepts/architecture/) - How the SDK is structured and why
|
||||
- [Sandbox lifecycle](/sandbox/concepts/sandboxes/) - Understanding sandbox states and behavior
|
||||
- [Container runtime](/sandbox/concepts/containers/) - How code executes in isolated containers
|
||||
- [Session management](/sandbox/concepts/sessions/) - When and how to use sessions
|
||||
- [Preview URLs](/sandbox/concepts/preview-urls/) - How service exposure works
|
||||
- [Security model](/sandbox/concepts/security/) - Isolation, validation, and safety mechanisms
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Tutorials](/sandbox/tutorials/) - Learn by building complete applications
|
||||
- [How-to guides](/sandbox/guides/) - Solve specific problems
|
||||
- [API reference](/sandbox/api/) - Technical details and method signatures
|
||||
173
src/content/docs/sandbox/concepts/preview-urls.mdx
Normal file
173
src/content/docs/sandbox/concepts/preview-urls.mdx
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
---
|
||||
title: Preview URLs
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 5
|
||||
---
|
||||
|
||||
Preview URLs provide public access to services running inside sandboxes. When you expose a port, you get a unique HTTPS URL that proxies requests to your service.
|
||||
|
||||
```typescript
|
||||
await sandbox.startProcess('python -m http.server 8000');
|
||||
const exposed = await sandbox.exposePort(8000);
|
||||
|
||||
console.log(exposed.exposedAt);
|
||||
// https://abc123-8000.sandbox.workers.dev
|
||||
```
|
||||
|
||||
## URL format
|
||||
|
||||
Preview URLs follow this pattern:
|
||||
|
||||
```
|
||||
https://{sandbox-id}-{port}.sandbox.workers.dev
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
- Port 3000: `https://abc123-3000.sandbox.workers.dev`
|
||||
- Port 8080: `https://abc123-8080.sandbox.workers.dev`
|
||||
|
||||
**URL stability**: URLs remain the same for a given sandbox ID and port. You can share, bookmark, or use them in webhooks.
|
||||
|
||||
## Request routing
|
||||
|
||||
```
|
||||
User's Browser
|
||||
↓ HTTPS
|
||||
Your Worker
|
||||
↓
|
||||
Durable Object (sandbox)
|
||||
↓ HTTP
|
||||
Your Service (on exposed port)
|
||||
```
|
||||
|
||||
**Important**: You must handle preview URL routing in your Worker using `proxyToSandbox()`:
|
||||
|
||||
```typescript
|
||||
import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";
|
||||
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
// Route preview URL requests to sandboxes
|
||||
const proxyResponse = await proxyToSandbox(request, env);
|
||||
if (proxyResponse) return proxyResponse;
|
||||
|
||||
// Your custom routes here
|
||||
// ...
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Without this, preview URLs won't work.
|
||||
|
||||
## Multiple ports
|
||||
|
||||
Expose multiple services simultaneously:
|
||||
|
||||
```typescript
|
||||
await sandbox.startProcess('node api.js'); // Port 3000
|
||||
await sandbox.startProcess('node admin.js'); // Port 3001
|
||||
|
||||
const api = await sandbox.exposePort(3000, { name: 'api' });
|
||||
const admin = await sandbox.exposePort(3001, { name: 'admin' });
|
||||
|
||||
// Each gets its own URL:
|
||||
// https://abc123-3000.sandbox.workers.dev
|
||||
// https://abc123-3001.sandbox.workers.dev
|
||||
```
|
||||
|
||||
## What works
|
||||
|
||||
- HTTP/HTTPS requests
|
||||
- WebSocket (WSS) via HTTP upgrade
|
||||
- Server-Sent Events
|
||||
- All HTTP methods (GET, POST, PUT, DELETE, etc.)
|
||||
- Request and response headers
|
||||
|
||||
## What doesn't work
|
||||
|
||||
- Raw TCP/UDP connections
|
||||
- Custom protocols (must wrap in HTTP)
|
||||
- Ports 80/443 (use 1024+)
|
||||
|
||||
## Security
|
||||
|
||||
:::caution
|
||||
Preview URLs are publicly accessible. Anyone with the URL can access your service.
|
||||
:::
|
||||
|
||||
**Add authentication in your service**:
|
||||
|
||||
```python
|
||||
from flask import Flask, request, abort
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/data')
|
||||
def get_data():
|
||||
token = request.headers.get('Authorization')
|
||||
if token != 'Bearer secret-token':
|
||||
abort(401)
|
||||
return {'data': 'protected'}
|
||||
```
|
||||
|
||||
**Security features**:
|
||||
- All traffic is HTTPS (automatic TLS)
|
||||
- URLs use random sandbox IDs (hard to guess)
|
||||
- You control authentication in your service
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### URL not accessible
|
||||
|
||||
Check if service is running and listening:
|
||||
|
||||
```typescript
|
||||
// 1. Is service running?
|
||||
const processes = await sandbox.listProcesses();
|
||||
|
||||
// 2. Is port exposed?
|
||||
const ports = await sandbox.getExposedPorts();
|
||||
|
||||
// 3. Is service binding to 0.0.0.0 (not 127.0.0.1)?
|
||||
// Good:
|
||||
app.run(host='0.0.0.0', port=3000)
|
||||
|
||||
// Bad (localhost only):
|
||||
app.run(host='127.0.0.1', port=3000)
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
**Service design**:
|
||||
- Bind to `0.0.0.0` to make accessible
|
||||
- Add authentication (don't rely on URL secrecy)
|
||||
- Include health check endpoints
|
||||
- Handle CORS if accessed from browsers
|
||||
|
||||
**Cleanup**:
|
||||
- Unexpose ports when done: `await sandbox.unexposePort(port)`
|
||||
- Stop processes: `await sandbox.killAllProcesses()`
|
||||
|
||||
## Local development
|
||||
|
||||
:::caution[Local development only]
|
||||
When using `wrangler dev`, you must expose ports in your Dockerfile:
|
||||
|
||||
```dockerfile
|
||||
FROM docker.io/cloudflare/sandbox:0.3.3
|
||||
|
||||
# Required for local development
|
||||
EXPOSE 3000
|
||||
EXPOSE 8080
|
||||
```
|
||||
|
||||
Without `EXPOSE`, you'll see: `connect(): Connection refused: container port not found`
|
||||
|
||||
This is **only required for local development**. In production, all container ports are automatically accessible.
|
||||
:::
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Ports API reference](/sandbox/api/ports/) - Complete port exposure API
|
||||
- [Expose services guide](/sandbox/guides/expose-services/) - Practical patterns
|
||||
141
src/content/docs/sandbox/concepts/sandboxes.mdx
Normal file
141
src/content/docs/sandbox/concepts/sandboxes.mdx
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
---
|
||||
title: Sandbox lifecycle
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
A sandbox is an isolated execution environment where your code runs. Each sandbox:
|
||||
|
||||
- Has a unique identifier (sandbox ID)
|
||||
- Contains an isolated filesystem
|
||||
- Runs in a dedicated Linux container
|
||||
- Persists state between requests
|
||||
- Exists as a Cloudflare Durable Object
|
||||
|
||||
## Lifecycle states
|
||||
|
||||
### Creation
|
||||
|
||||
A sandbox is created the first time you reference its ID:
|
||||
|
||||
```typescript
|
||||
const sandbox = getSandbox(env.Sandbox, 'user-123');
|
||||
await sandbox.exec('echo "Hello"'); // First request creates sandbox
|
||||
```
|
||||
|
||||
**Duration**: 100-300ms (cold start) or \<10ms (if warm)
|
||||
|
||||
### Active
|
||||
|
||||
The sandbox is running and processing requests. Filesystem, processes, and environment variables persist across requests.
|
||||
|
||||
### Idle
|
||||
|
||||
After inactivity, the sandbox may enter idle state. Filesystem state is preserved, but the container may be paused. Next request triggers a warm start.
|
||||
|
||||
### Destruction
|
||||
|
||||
Sandboxes are explicitly destroyed or automatically cleaned up:
|
||||
|
||||
```typescript
|
||||
await sandbox.destroy();
|
||||
// All files, processes, and state deleted permanently
|
||||
```
|
||||
|
||||
## Persistence
|
||||
|
||||
Between requests to the same sandbox:
|
||||
|
||||
**What persists**:
|
||||
- Files in `/workspace`, `/tmp`, `/home`
|
||||
- Background processes (started with `startProcess()`)
|
||||
- Code interpreter contexts and variables
|
||||
- Environment variables and port exposures
|
||||
|
||||
**What doesn't persist**:
|
||||
- Nothing survives `destroy()`
|
||||
- Background processes may stop after container restarts (rare)
|
||||
|
||||
## Naming strategies
|
||||
|
||||
### Per-user sandboxes
|
||||
|
||||
```typescript
|
||||
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);
|
||||
```
|
||||
|
||||
User's work persists across sessions. Good for interactive environments, playgrounds, and notebooks.
|
||||
|
||||
### Per-session sandboxes
|
||||
|
||||
```typescript
|
||||
const sessionId = `session-${Date.now()}-${Math.random()}`;
|
||||
const sandbox = getSandbox(env.Sandbox, sessionId);
|
||||
// Later:
|
||||
await sandbox.destroy();
|
||||
```
|
||||
|
||||
Fresh environment each time. Good for one-time execution, CI/CD, and isolated tests.
|
||||
|
||||
### Per-task sandboxes
|
||||
|
||||
```typescript
|
||||
const sandbox = getSandbox(env.Sandbox, `build-${repoName}-${commit}`);
|
||||
```
|
||||
|
||||
Idempotent operations with clear task-to-sandbox mapping. Good for builds, pipelines, and background jobs.
|
||||
|
||||
## Request routing
|
||||
|
||||
The first request to a sandbox determines its geographic location. Subsequent requests route to the same location.
|
||||
|
||||
**For global apps**:
|
||||
- Option 1: Multiple sandboxes per user with region suffix (`user-123-us`, `user-123-eu`)
|
||||
- Option 2: Single sandbox per user (simpler, but some users see higher latency)
|
||||
|
||||
## Lifecycle management
|
||||
|
||||
### When to destroy
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const sandbox = getSandbox(env.Sandbox, sessionId);
|
||||
await sandbox.exec('npm run build');
|
||||
} finally {
|
||||
await sandbox.destroy(); // Clean up temporary sandboxes
|
||||
}
|
||||
```
|
||||
|
||||
**Destroy when**: Session ends, task completes, resources no longer needed
|
||||
|
||||
**Don't destroy**: Personal environments, long-running services
|
||||
|
||||
### Failure recovery
|
||||
|
||||
If container crashes or Durable Object is evicted (rare):
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await sandbox.exec('command');
|
||||
} catch (error) {
|
||||
if (error.message.includes('container') || error.message.includes('connection')) {
|
||||
await sandbox.exec('command'); // Retry - container recreates
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Name consistently** - Use clear, predictable naming schemes
|
||||
- **Clean up temporary sandboxes** - Always destroy when done
|
||||
- **Reuse long-lived sandboxes** - One per user is often sufficient
|
||||
- **Batch operations** - Combine commands: `npm install && npm test && npm build`
|
||||
- **Handle failures** - Design for container restarts
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Architecture](/sandbox/concepts/architecture/) - How sandboxes fit in the system
|
||||
- [Container runtime](/sandbox/concepts/containers/) - What runs inside sandboxes
|
||||
- [Session management](/sandbox/concepts/sessions/) - Advanced state isolation
|
||||
- [Sessions API](/sandbox/api/sessions/) - Programmatic lifecycle control
|
||||
178
src/content/docs/sandbox/concepts/security.mdx
Normal file
178
src/content/docs/sandbox/concepts/security.mdx
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
---
|
||||
title: Security model
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 6
|
||||
---
|
||||
|
||||
The Sandbox SDK is built on [Containers](/containers/), which run each sandbox in its own VM for strong isolation.
|
||||
|
||||
## Container isolation
|
||||
|
||||
Each sandbox runs in a separate VM, providing complete isolation:
|
||||
|
||||
- **Filesystem isolation** - Sandboxes cannot access other sandboxes' files
|
||||
- **Process isolation** - Processes in one sandbox cannot see or affect others
|
||||
- **Network isolation** - Sandboxes have separate network stacks
|
||||
- **Resource limits** - CPU, memory, and disk quotas are enforced per sandbox
|
||||
|
||||
For complete security details about the underlying container platform, see [Containers architecture](/containers/platform-details/architecture/).
|
||||
|
||||
## Within a sandbox
|
||||
|
||||
All code within a single sandbox shares resources:
|
||||
|
||||
- **Filesystem** - All processes see the same files
|
||||
- **Processes** - All sessions can see all processes
|
||||
- **Network** - Processes can communicate via localhost
|
||||
|
||||
For complete isolation, use separate sandboxes per user:
|
||||
|
||||
```typescript
|
||||
// Good - Each user in separate sandbox
|
||||
const userSandbox = getSandbox(env.Sandbox, `user-${userId}`);
|
||||
|
||||
// Bad - Users sharing one sandbox
|
||||
const shared = getSandbox(env.Sandbox, 'shared');
|
||||
// Users can read each other's files!
|
||||
```
|
||||
|
||||
## Input validation
|
||||
|
||||
### Command injection
|
||||
|
||||
Always validate user input before using it in commands:
|
||||
|
||||
```typescript
|
||||
// Dangerous - user input directly in command
|
||||
const filename = userInput;
|
||||
await sandbox.exec(`cat ${filename}`);
|
||||
// User could input: "file.txt; rm -rf /"
|
||||
|
||||
// Safe - validate input
|
||||
const filename = userInput.replace(/[^a-zA-Z0-9._-]/g, '');
|
||||
await sandbox.exec(`cat ${filename}`);
|
||||
|
||||
// Better - use file API
|
||||
await sandbox.writeFile('/tmp/input', userInput);
|
||||
await sandbox.exec('cat /tmp/input');
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Sandbox access
|
||||
|
||||
Sandbox IDs provide basic access control but aren't cryptographically secure. Add application-level authentication:
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const userId = await authenticate(request);
|
||||
if (!userId) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
|
||||
// User can only access their sandbox
|
||||
const sandbox = getSandbox(env.Sandbox, userId);
|
||||
return Response.json({ authorized: true });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Preview URLs
|
||||
|
||||
Preview URLs are public. Add authentication in your service:
|
||||
|
||||
```python
|
||||
from flask import Flask, request, abort
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def check_auth():
|
||||
token = request.headers.get('Authorization')
|
||||
if token != f"Bearer {os.environ['AUTH_TOKEN']}":
|
||||
abort(401)
|
||||
|
||||
@app.route('/api/data')
|
||||
def get_data():
|
||||
check_auth()
|
||||
return {'data': 'protected'}
|
||||
```
|
||||
|
||||
## Secrets management
|
||||
|
||||
Use environment variables, not hardcoded secrets:
|
||||
|
||||
```typescript
|
||||
// Bad - hardcoded in file
|
||||
await sandbox.writeFile('/workspace/config.js', `
|
||||
const API_KEY = 'sk_live_abc123';
|
||||
`);
|
||||
|
||||
// Good - use environment variables
|
||||
await sandbox.startProcess('node app.js', {
|
||||
env: {
|
||||
API_KEY: env.API_KEY, // From Worker environment binding
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Clean up temporary sensitive data:
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await sandbox.writeFile('/tmp/sensitive.txt', secretData);
|
||||
await sandbox.exec('python process.py /tmp/sensitive.txt');
|
||||
} finally {
|
||||
await sandbox.deleteFile('/tmp/sensitive.txt');
|
||||
}
|
||||
```
|
||||
|
||||
## What the SDK protects against
|
||||
|
||||
- Sandbox-to-sandbox access (VM isolation)
|
||||
- Resource exhaustion (enforced quotas)
|
||||
- Container escapes (VM-based isolation)
|
||||
|
||||
## What you must implement
|
||||
|
||||
- Authentication and authorization
|
||||
- Input validation and sanitization
|
||||
- Rate limiting
|
||||
- Application-level security (SQL injection, XSS, etc.)
|
||||
|
||||
## Best practices
|
||||
|
||||
**Use separate sandboxes for isolation**:
|
||||
```typescript
|
||||
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);
|
||||
```
|
||||
|
||||
**Validate all inputs**:
|
||||
```typescript
|
||||
const safe = input.replace(/[^a-zA-Z0-9._-]/g, '');
|
||||
await sandbox.exec(`command ${safe}`);
|
||||
```
|
||||
|
||||
**Use environment variables for secrets**:
|
||||
```typescript
|
||||
await sandbox.startProcess('node app.js', {
|
||||
env: { API_KEY: env.API_KEY }
|
||||
});
|
||||
```
|
||||
|
||||
**Clean up temporary resources**:
|
||||
```typescript
|
||||
try {
|
||||
const sandbox = getSandbox(env.Sandbox, sessionId);
|
||||
await sandbox.exec('npm test');
|
||||
} finally {
|
||||
await sandbox.destroy();
|
||||
}
|
||||
```
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Containers architecture](/containers/platform-details/architecture/) - Underlying platform security
|
||||
- [Sandbox lifecycle](/sandbox/concepts/sandboxes/) - Resource management
|
||||
153
src/content/docs/sandbox/concepts/sessions.mdx
Normal file
153
src/content/docs/sandbox/concepts/sessions.mdx
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
---
|
||||
title: Session management
|
||||
pcx_content_type: concept
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
Sessions are bash shell execution contexts within a sandbox. Think of them like terminal tabs or panes in the same container.
|
||||
|
||||
- **Sandbox** = A computer (container)
|
||||
- **Session** = A terminal shell session in that computer
|
||||
|
||||
## Default session
|
||||
|
||||
Every sandbox has a default session that maintains shell state across commands:
|
||||
|
||||
```typescript
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// These commands run in the default session
|
||||
await sandbox.exec("cd /app");
|
||||
await sandbox.exec("pwd"); // Output: /app
|
||||
|
||||
await sandbox.exec("export MY_VAR=hello");
|
||||
await sandbox.exec("echo $MY_VAR"); // Output: hello
|
||||
```
|
||||
|
||||
Shell state persists: working directory, environment variables, exported variables all carry over between commands.
|
||||
|
||||
## Creating sessions
|
||||
|
||||
Create additional sessions for isolated shell contexts:
|
||||
|
||||
```typescript
|
||||
const buildSession = await sandbox.createSession({
|
||||
name: "build",
|
||||
env: { NODE_ENV: "production" },
|
||||
cwd: "/build"
|
||||
});
|
||||
|
||||
const testSession = await sandbox.createSession({
|
||||
name: "test",
|
||||
env: { NODE_ENV: "test" },
|
||||
cwd: "/test"
|
||||
});
|
||||
|
||||
// Different shell contexts
|
||||
await buildSession.exec("npm run build");
|
||||
await testSession.exec("npm test");
|
||||
```
|
||||
|
||||
## What's isolated per session
|
||||
|
||||
Each session has its own:
|
||||
|
||||
**Shell environment**:
|
||||
```typescript
|
||||
await session1.exec("export MY_VAR=hello");
|
||||
await session2.exec("echo $MY_VAR"); // Empty - different shell
|
||||
```
|
||||
|
||||
**Working directory**:
|
||||
```typescript
|
||||
await session1.exec("cd /workspace/project1");
|
||||
await session2.exec("pwd"); // Different working directory
|
||||
```
|
||||
|
||||
**Environment variables** (set via `createSession` options):
|
||||
```typescript
|
||||
const session1 = await sandbox.createSession({
|
||||
env: { API_KEY: 'key-1' }
|
||||
});
|
||||
const session2 = await sandbox.createSession({
|
||||
env: { API_KEY: 'key-2' }
|
||||
});
|
||||
```
|
||||
|
||||
## What's shared
|
||||
|
||||
All sessions in a sandbox share:
|
||||
|
||||
**Filesystem**:
|
||||
```typescript
|
||||
await session1.writeFile('/workspace/file.txt', 'data');
|
||||
await session2.readFile('/workspace/file.txt'); // Can read it
|
||||
```
|
||||
|
||||
**Processes**:
|
||||
```typescript
|
||||
await session1.startProcess('node server.js');
|
||||
await session2.listProcesses(); // Sees the server
|
||||
```
|
||||
|
||||
**Ports**:
|
||||
```typescript
|
||||
await session1.exposePort(3000);
|
||||
await session2.getExposedPorts(); // Sees port 3000
|
||||
```
|
||||
|
||||
## When to use sessions
|
||||
|
||||
**Use sessions when**:
|
||||
- You need isolated shell state for different tasks
|
||||
- Running parallel operations with different environments
|
||||
- Keeping AI agent credentials separate from app runtime
|
||||
|
||||
**Example - separate dev and runtime environments**:
|
||||
```typescript
|
||||
// Phase 1: AI agent writes code (with API keys)
|
||||
const devSession = await sandbox.createSession({
|
||||
name: "dev",
|
||||
env: { ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY }
|
||||
});
|
||||
await devSession.exec('ai-tool "build a web server"');
|
||||
|
||||
// Phase 2: Run the code (without API keys)
|
||||
const appSession = await sandbox.createSession({
|
||||
name: "app",
|
||||
env: { PORT: "3000" }
|
||||
});
|
||||
await appSession.exec("node server.js");
|
||||
```
|
||||
|
||||
**Use separate sandboxes when**:
|
||||
- You need complete isolation (untrusted code)
|
||||
- Different users require fully separated environments
|
||||
- Independent resource allocation is needed
|
||||
|
||||
## Best practices
|
||||
|
||||
**Clean up temporary sessions**:
|
||||
```typescript
|
||||
try {
|
||||
const session = await sandbox.createSession({ name: 'temp' });
|
||||
await session.exec('command');
|
||||
} finally {
|
||||
await sandbox.deleteSession('temp');
|
||||
}
|
||||
```
|
||||
|
||||
**Sessions share filesystem**:
|
||||
```typescript
|
||||
// Bad - affects all sessions
|
||||
await session.exec('rm -rf /workspace/*');
|
||||
|
||||
// Use separate sandboxes for untrusted isolation
|
||||
const userSandbox = getSandbox(env.Sandbox, userId);
|
||||
```
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Sandbox lifecycle](/sandbox/concepts/sandboxes/) - Understanding sandbox management
|
||||
- [Sessions API](/sandbox/api/sessions/) - Complete session API reference
|
||||
73
src/content/docs/sandbox/configuration/dockerfile.mdx
Normal file
73
src/content/docs/sandbox/configuration/dockerfile.mdx
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
title: Dockerfile reference
|
||||
pcx_content_type: configuration
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
Customize the sandbox container image with your own packages, tools, and configurations by extending the base runtime image.
|
||||
|
||||
## Base image
|
||||
|
||||
The Sandbox SDK uses a Ubuntu-based Linux container with Python, Node.js (via Bun), and common development tools pre-installed:
|
||||
|
||||
```dockerfile
|
||||
FROM docker.io/cloudflare/sandbox:0.3.3
|
||||
```
|
||||
|
||||
:::note
|
||||
Always match the Docker image version to your npm package version. If you're using `@cloudflare/sandbox@0.3.3`, use `docker.io/cloudflare/sandbox:0.3.3` as your base image.
|
||||
:::
|
||||
|
||||
**What's included:**
|
||||
|
||||
- Ubuntu 22.04 LTS base
|
||||
- Python 3.11+ with pip
|
||||
- Bun (JavaScript/TypeScript runtime)
|
||||
- Git, curl, wget, and common CLI tools
|
||||
- Pre-installed Python packages: pandas, numpy, matplotlib
|
||||
- System libraries for most common use cases
|
||||
|
||||
## Creating a custom image
|
||||
|
||||
Create a `Dockerfile` in your project root:
|
||||
|
||||
```dockerfile title="Dockerfile"
|
||||
FROM docker.io/cloudflare/sandbox:0.3.3
|
||||
|
||||
# Install additional Python packages
|
||||
RUN pip install --no-cache-dir \
|
||||
scikit-learn==1.3.0 \
|
||||
tensorflow==2.13.0 \
|
||||
transformers==4.30.0
|
||||
|
||||
# Install Node.js packages globally
|
||||
RUN npm install -g typescript ts-node prettier
|
||||
|
||||
# Install system packages
|
||||
RUN apt-get update && apt-get install -y \
|
||||
postgresql-client \
|
||||
redis-tools \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
Update `wrangler.jsonc` to reference your Dockerfile:
|
||||
|
||||
```jsonc title="wrangler.jsonc"
|
||||
{
|
||||
"containers": [
|
||||
{
|
||||
"binding": "CONTAINER",
|
||||
"dockerfile": "./Dockerfile",
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
When you run `wrangler dev` or `wrangler deploy`, Wrangler automatically builds your Docker image and pushes it to Cloudflare's container registry. You don't need to manually build or publish images.
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Wrangler configuration](/sandbox/configuration/wrangler/) - Using custom images in wrangler.jsonc
|
||||
- [Docker documentation](https://docs.docker.com/reference/dockerfile/) - Complete Dockerfile syntax
|
||||
- [Container concepts](/sandbox/concepts/containers/) - Understanding the runtime environment
|
||||
197
src/content/docs/sandbox/configuration/environment-variables.mdx
Normal file
197
src/content/docs/sandbox/configuration/environment-variables.mdx
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
---
|
||||
title: Environment variables
|
||||
pcx_content_type: configuration
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
Pass configuration, secrets, and runtime settings to your sandboxes using environment variables.
|
||||
|
||||
### Command and process variables
|
||||
|
||||
Pass environment variables when executing commands or starting processes:
|
||||
|
||||
```typescript
|
||||
// Commands
|
||||
await sandbox.exec("node app.js", {
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
API_KEY: env.API_KEY, // Pass from Worker env
|
||||
PORT: "3000",
|
||||
},
|
||||
});
|
||||
|
||||
// Background processes (same syntax)
|
||||
await sandbox.startProcess("python server.py", {
|
||||
env: {
|
||||
DATABASE_URL: env.DATABASE_URL,
|
||||
SECRET_KEY: env.SECRET_KEY,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Session-level variables
|
||||
|
||||
Set environment variables for all commands in a session:
|
||||
|
||||
```typescript
|
||||
const session = await sandbox.createSession();
|
||||
|
||||
await session.setEnvVars({
|
||||
DATABASE_URL: env.DATABASE_URL,
|
||||
SECRET_KEY: env.SECRET_KEY,
|
||||
});
|
||||
|
||||
// All commands in this session have these vars
|
||||
await session.exec("python migrate.py");
|
||||
await session.exec("python seed.py");
|
||||
```
|
||||
|
||||
## Common patterns
|
||||
|
||||
### Pass Worker secrets to sandbox
|
||||
|
||||
Securely pass secrets from Worker environment:
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
Sandbox: DurableObjectNamespace;
|
||||
OPENAI_API_KEY: string; // Set with `wrangler secret put`
|
||||
DATABASE_URL: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const sandbox = getSandbox(env.Sandbox, "user-sandbox");
|
||||
|
||||
const result = await sandbox.exec("python analyze.py", {
|
||||
env: {
|
||||
OPENAI_API_KEY: env.OPENAI_API_KEY,
|
||||
DATABASE_URL: env.DATABASE_URL,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.json({ result });
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Default values with spreading
|
||||
|
||||
Combine default and command-specific variables:
|
||||
|
||||
```typescript
|
||||
const defaults = {
|
||||
NODE_ENV: env.ENVIRONMENT || "production",
|
||||
LOG_LEVEL: "info",
|
||||
TZ: "UTC",
|
||||
};
|
||||
|
||||
await sandbox.exec("npm start", {
|
||||
env: {
|
||||
...defaults,
|
||||
PORT: "3000", // Command-specific override
|
||||
API_KEY: env.API_KEY,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Environment variable precedence
|
||||
|
||||
When the same variable is set at multiple levels:
|
||||
|
||||
1. **Command-level** (highest) - Passed to `exec()` or `startProcess()`
|
||||
2. **Session-level** - Set with `setEnvVars()`
|
||||
3. **Container default** - Built into the Docker image
|
||||
4. **System default** (lowest) - Operating system defaults
|
||||
|
||||
Example:
|
||||
|
||||
```typescript
|
||||
// In Dockerfile: ENV NODE_ENV=development
|
||||
// In session: await sandbox.setEnvVars({ NODE_ENV: 'staging' });
|
||||
|
||||
// In command (overrides all):
|
||||
await sandbox.exec("node app.js", {
|
||||
env: { NODE_ENV: "production" }, // This wins
|
||||
});
|
||||
```
|
||||
|
||||
## Security best practices
|
||||
|
||||
### Never hardcode secrets
|
||||
|
||||
**Bad** - Secrets in code:
|
||||
|
||||
```typescript
|
||||
await sandbox.exec("python app.py", {
|
||||
env: {
|
||||
API_KEY: "sk-1234567890abcdef", // NEVER DO THIS
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Good** - Secrets from Worker environment:
|
||||
|
||||
```typescript
|
||||
await sandbox.exec("python app.py", {
|
||||
env: {
|
||||
API_KEY: env.API_KEY, // From Wrangler secret
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Set secrets with Wrangler:
|
||||
|
||||
```bash
|
||||
wrangler secret put API_KEY
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
List all environment variables:
|
||||
|
||||
```typescript
|
||||
const result = await sandbox.exec("env");
|
||||
console.log(result.stdout);
|
||||
```
|
||||
|
||||
Check specific variable:
|
||||
|
||||
```typescript
|
||||
const result = await sandbox.exec("echo $NODE_ENV");
|
||||
console.log("NODE_ENV:", result.stdout.trim());
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Variable not set
|
||||
|
||||
Verify the variable is being passed:
|
||||
|
||||
```typescript
|
||||
console.log("Worker env:", env.API_KEY ? "Set" : "Missing");
|
||||
|
||||
const result = await sandbox.exec("env | grep API_KEY", {
|
||||
env: { API_KEY: env.API_KEY },
|
||||
});
|
||||
console.log("Sandbox:", result.stdout);
|
||||
```
|
||||
|
||||
### Shell expansion issues
|
||||
|
||||
Use runtime-specific access instead of shell variables:
|
||||
|
||||
```typescript
|
||||
// Instead of: await sandbox.exec('echo $NODE_ENV')
|
||||
await sandbox.exec('node -e "console.log(process.env.NODE_ENV)"', {
|
||||
env: { NODE_ENV: "production" },
|
||||
});
|
||||
```
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Wrangler configuration](/sandbox/configuration/wrangler/) - Setting Worker-level environment
|
||||
- [Secrets](/workers/configuration/secrets/) - Managing sensitive data
|
||||
- [Sessions API](/sandbox/api/sessions/) - Session-level environment variables
|
||||
- [Security model](/sandbox/concepts/security/) - Understanding data isolation
|
||||
107
src/content/docs/sandbox/configuration/index.mdx
Normal file
107
src/content/docs/sandbox/configuration/index.mdx
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
title: Configuration
|
||||
pcx_content_type: navigation
|
||||
sidebar:
|
||||
order: 7
|
||||
---
|
||||
|
||||
import { LinkTitleCard, CardGrid } from "~/components";
|
||||
|
||||
Configure your Sandbox SDK deployment with Wrangler, customize container images, and manage environment variables.
|
||||
|
||||
<CardGrid>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Wrangler configuration"
|
||||
href="/sandbox/configuration/wrangler/"
|
||||
icon="document"
|
||||
>
|
||||
Configure Durable Objects bindings, container images, and Worker settings in wrangler.jsonc.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Dockerfile reference"
|
||||
href="/sandbox/configuration/dockerfile/"
|
||||
icon="wrench"
|
||||
>
|
||||
Customize the sandbox container image with your own packages, tools, and configurations.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Environment variables"
|
||||
href="/sandbox/configuration/environment-variables/"
|
||||
icon="gear"
|
||||
>
|
||||
Pass configuration and secrets to your sandboxes using environment variables.
|
||||
</LinkTitleCard>
|
||||
|
||||
</CardGrid>
|
||||
|
||||
## Quick reference
|
||||
|
||||
### Essential wrangler.jsonc settings
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "my-worker",
|
||||
"main": "src/index.ts",
|
||||
"compatibility_date": "2024-09-02",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{
|
||||
"name": "Sandbox",
|
||||
"class_name": "Sandbox",
|
||||
"script_name": "@cloudflare/sandbox"
|
||||
}
|
||||
]
|
||||
},
|
||||
"containers": [
|
||||
{
|
||||
"binding": "CONTAINER",
|
||||
"image": "ghcr.io/cloudflare/sandbox-runtime:latest"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Common Dockerfile customizations
|
||||
|
||||
```dockerfile
|
||||
FROM ghcr.io/cloudflare/sandbox-runtime:latest
|
||||
|
||||
# Install additional Python packages
|
||||
RUN pip install scikit-learn tensorflow pandas
|
||||
|
||||
# Install Node.js packages globally
|
||||
RUN npm install -g typescript ts-node
|
||||
|
||||
# Install system packages
|
||||
RUN apt-get update && apt-get install -y postgresql-client
|
||||
|
||||
# Add custom scripts
|
||||
COPY ./scripts /usr/local/bin/
|
||||
```
|
||||
|
||||
### Environment variables
|
||||
|
||||
```typescript
|
||||
// Pass to sandbox at creation
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// Configure environment for commands
|
||||
await sandbox.exec('node app.js', {
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
API_KEY: env.API_KEY,
|
||||
DATABASE_URL: env.DATABASE_URL
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Get Started guide](/sandbox/get-started/) - Initial setup walkthrough
|
||||
- [Wrangler documentation](/workers/wrangler/) - Complete Wrangler reference
|
||||
- [Docker documentation](https://docs.docker.com/engine/reference/builder/) - Dockerfile syntax
|
||||
- [Security model](/sandbox/concepts/security/) - Understanding environment isolation
|
||||
249
src/content/docs/sandbox/configuration/wrangler.mdx
Normal file
249
src/content/docs/sandbox/configuration/wrangler.mdx
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
---
|
||||
title: Wrangler configuration
|
||||
pcx_content_type: configuration
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
## Minimal configuration
|
||||
|
||||
The minimum required configuration for using Sandbox SDK:
|
||||
|
||||
```jsonc title="wrangler.jsonc"
|
||||
{
|
||||
"name": "my-sandbox-worker",
|
||||
"main": "src/index.ts",
|
||||
"compatibility_date": "2025-10-13",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
"containers": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"image": "docker.io/cloudflare/sandbox:0.3.3",
|
||||
},
|
||||
],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"name": "Sandbox",
|
||||
},
|
||||
],
|
||||
},
|
||||
"migrations": [
|
||||
{
|
||||
"new_sqlite_classes": ["Sandbox"],
|
||||
"tag": "v1",
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Required settings
|
||||
|
||||
### containers
|
||||
|
||||
Each container is backed by its own Durable Object. The container image contains your runtime environment.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"containers": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"image": "docker.io/cloudflare/sandbox:0.3.3",
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- **class_name** (string, required) - Must match the `class_name` of the Durable Object.
|
||||
- **image** (string, required) - The Docker image to use. Must match your npm package version.
|
||||
|
||||
For custom images, use a Dockerfile:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"containers": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"image": "./Dockerfile",
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
See [Dockerfile reference](/sandbox/configuration/dockerfile/) for customization.
|
||||
|
||||
### durable_objects.bindings
|
||||
|
||||
Bind the Sandbox Durable Object to your Worker:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"name": "Sandbox",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- **class_name** (string, required) - Must match the `class_name` of the container configuration.
|
||||
- **name** (string, required) - The binding name you'll use in your code. Conventionally `"Sandbox"`.
|
||||
|
||||
### migrations
|
||||
|
||||
Required for Durable Object initialization:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"migrations": [
|
||||
{
|
||||
"new_sqlite_classes": ["Sandbox"],
|
||||
"tag": "v1",
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
This tells Cloudflare to initialize the Sandbox Durable Object with SQLite storage.
|
||||
|
||||
## Optional settings
|
||||
|
||||
These settings are illustrative and not required for basic usage.
|
||||
|
||||
### Environment variables
|
||||
|
||||
Pass configuration to your Worker:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"vars": {
|
||||
"ENVIRONMENT": "production",
|
||||
"LOG_LEVEL": "info",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Access in your Worker:
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
console.log(`Running in ${env.ENVIRONMENT} mode`);
|
||||
// ...
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Secrets
|
||||
|
||||
Store sensitive values securely:
|
||||
|
||||
```bash
|
||||
# Set secrets via CLI (never commit these)
|
||||
wrangler secret put ANTHROPIC_API_KEY
|
||||
wrangler secret put GITHUB_TOKEN
|
||||
wrangler secret put DATABASE_URL
|
||||
```
|
||||
|
||||
Access like environment variables:
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
Sandbox: DurableObjectNamespace;
|
||||
ANTHROPIC_API_KEY: string;
|
||||
GITHUB_TOKEN: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Cron triggers
|
||||
|
||||
Run sandboxes on a schedule:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"triggers": {
|
||||
"crons": ["0 0 * * *"], // Daily at midnight
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async scheduled(event: ScheduledEvent, env: Env): Promise<void> {
|
||||
const sandbox = getSandbox(env.Sandbox, "scheduled-task");
|
||||
await sandbox.exec("python3 /workspace/daily-report.py");
|
||||
await sandbox.destroy();
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Binding not found
|
||||
|
||||
**Error**: `TypeError: env.Sandbox is undefined`
|
||||
|
||||
**Solution**: Ensure your `wrangler.jsonc` includes the Durable Objects binding:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"name": "Sandbox",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Container image pull fails
|
||||
|
||||
**Error**: `Failed to pull container image`
|
||||
|
||||
**Solution**: Ensure you're using the correct image version (must match npm package):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"containers": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"image": "docker.io/cloudflare/sandbox:0.3.3",
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Missing migrations
|
||||
|
||||
**Error**: Durable Object not initialized
|
||||
|
||||
**Solution**: Add migrations for the Sandbox class:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"migrations": [
|
||||
{
|
||||
"new_sqlite_classes": ["Sandbox"],
|
||||
"tag": "v1",
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Wrangler documentation](/workers/wrangler/) - Complete Wrangler reference
|
||||
- [Durable Objects setup](/durable-objects/get-started/) - DO-specific configuration
|
||||
- [Dockerfile reference](/sandbox/configuration/dockerfile/) - Custom container images
|
||||
- [Environment variables](/sandbox/configuration/environment-variables/) - Passing configuration to sandboxes
|
||||
- [Get Started guide](/sandbox/get-started/) - Initial setup walkthrough
|
||||
212
src/content/docs/sandbox/get-started.mdx
Normal file
212
src/content/docs/sandbox/get-started.mdx
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
---
|
||||
title: Getting started
|
||||
pcx_content_type: get-started
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
import { Render, PackageManagers, Steps, WranglerConfig } from "~/components";
|
||||
|
||||
Build your first application with Sandbox SDK - a secure code execution environment. In this guide, you'll create a Worker that can execute Python code and work with files in isolated containers.
|
||||
|
||||
:::note[What you're building]
|
||||
A simple API that can safely execute Python code and perform file operations in isolated sandbox environments.
|
||||
:::
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Render file="prereqs" product="workers" />
|
||||
|
||||
### Ensure Docker is running locally
|
||||
|
||||
Sandbox SDK uses [Docker](https://www.docker.com/) to build container images alongside your Worker. Docker must be running when you deploy or run locally.
|
||||
|
||||
Install Docker by following the [Docker Desktop installation guide](https://docs.docker.com/desktop/).
|
||||
|
||||
Verify Docker is running:
|
||||
|
||||
```sh
|
||||
docker info
|
||||
```
|
||||
|
||||
If Docker is not running, this command will hang or return "Cannot connect to the Docker daemon".
|
||||
|
||||
## 1. Create a new project
|
||||
|
||||
Create a new Sandbox SDK project:
|
||||
|
||||
<PackageManagers
|
||||
type="create"
|
||||
pkg="cloudflare@latest"
|
||||
args={"my-sandbox --template=cloudflare/sandbox-sdk/examples/minimal"}
|
||||
/>
|
||||
|
||||
This creates a `my-sandbox` directory with everything you need:
|
||||
|
||||
- `src/index.ts` - Worker with sandbox integration
|
||||
- `wrangler.jsonc` - Configuration for Workers and Containers
|
||||
- `Dockerfile` - Container environment definition
|
||||
|
||||
```sh
|
||||
cd my-sandbox
|
||||
```
|
||||
|
||||
## 2. Explore the template
|
||||
|
||||
The template provides a minimal Worker that demonstrates core sandbox capabilities:
|
||||
|
||||
```typescript
|
||||
import { getSandbox, proxyToSandbox, type Sandbox } from '@cloudflare/sandbox';
|
||||
|
||||
export { Sandbox } from '@cloudflare/sandbox';
|
||||
|
||||
type Env = {
|
||||
Sandbox: DurableObjectNamespace<Sandbox>;
|
||||
};
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
// Required for preview URLs (if you expose ports later)
|
||||
const proxyResponse = await proxyToSandbox(request, env);
|
||||
if (proxyResponse) return proxyResponse;
|
||||
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Get or create a sandbox instance
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// Execute Python code
|
||||
if (url.pathname === '/run') {
|
||||
const result = await sandbox.exec('python -c "print(2 + 2)"');
|
||||
return Response.json({
|
||||
output: result.stdout,
|
||||
success: result.success
|
||||
});
|
||||
}
|
||||
|
||||
// Work with files
|
||||
if (url.pathname === '/file') {
|
||||
await sandbox.writeFile('/workspace/hello.txt', 'Hello, Sandbox!');
|
||||
const file = await sandbox.readFile('/workspace/hello.txt');
|
||||
return Response.json({
|
||||
content: file.content
|
||||
});
|
||||
}
|
||||
|
||||
return new Response('Try /run or /file');
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**Key concepts**:
|
||||
|
||||
- `getSandbox()` - Gets or creates a sandbox instance by ID
|
||||
- `proxyToSandbox()` - Required at the top for preview URLs to work
|
||||
- `sandbox.exec()` - Execute commands and capture output
|
||||
- `sandbox.writeFile()` / `readFile()` - File operations
|
||||
|
||||
## 3. Test locally
|
||||
|
||||
Start the development server:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
:::note
|
||||
First run builds the Docker container (2-3 minutes). Subsequent runs are much faster due to caching.
|
||||
:::
|
||||
|
||||
Test the endpoints:
|
||||
|
||||
```sh
|
||||
# Execute Python code
|
||||
curl http://localhost:8787/run
|
||||
|
||||
# File operations
|
||||
curl http://localhost:8787/file
|
||||
```
|
||||
|
||||
You should see JSON responses with the command output and file contents.
|
||||
|
||||
## 4. Deploy to production
|
||||
|
||||
Deploy your Worker and container:
|
||||
|
||||
```sh
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Build your container image using Docker
|
||||
2. Push it to Cloudflare's Container Registry
|
||||
3. Deploy your Worker globally
|
||||
|
||||
:::caution[Wait for provisioning]
|
||||
After first deployment, wait 2-3 minutes before making requests. The Worker deploys immediately, but the container needs time to provision.
|
||||
:::
|
||||
|
||||
Check deployment status:
|
||||
|
||||
```sh
|
||||
npx wrangler containers list
|
||||
```
|
||||
|
||||
## 5. Test your deployment
|
||||
|
||||
Visit your Worker URL (shown in deploy output):
|
||||
|
||||
```sh
|
||||
# Replace with your actual URL
|
||||
curl https://my-sandbox.YOUR_SUBDOMAIN.workers.dev/run
|
||||
```
|
||||
|
||||
Your sandbox is now deployed.
|
||||
|
||||
## Understanding the configuration
|
||||
|
||||
Your `wrangler.jsonc` connects three pieces together:
|
||||
|
||||
<WranglerConfig>
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"containers": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"image": "./Dockerfile"
|
||||
}
|
||||
],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"name": "Sandbox"
|
||||
}
|
||||
]
|
||||
},
|
||||
"migrations": [
|
||||
{
|
||||
"new_sqlite_classes": ["Sandbox"],
|
||||
"tag": "v1"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</WranglerConfig>
|
||||
|
||||
- **containers** - Your Dockerfile defines the execution environment
|
||||
- **durable_objects** - Makes the `Sandbox` binding available in your Worker
|
||||
- **migrations** - Initializes Durable Object storage (required once)
|
||||
|
||||
For detailed configuration options including environment variables, secrets, and custom images, see the [Wrangler configuration reference](/sandbox/configuration/wrangler/).
|
||||
|
||||
## Next steps
|
||||
|
||||
Now that you have a working sandbox, explore more capabilities:
|
||||
|
||||
- [Execute commands](/sandbox/guides/execute-commands/) - Run shell commands and stream output
|
||||
- [Manage files](/sandbox/guides/manage-files/) - Work with files and directories
|
||||
- [Expose services](/sandbox/guides/expose-services/) - Get public URLs for services running in your sandbox
|
||||
- [API reference](/sandbox/api/) - Complete API documentation
|
||||
204
src/content/docs/sandbox/guides/background-processes.mdx
Normal file
204
src/content/docs/sandbox/guides/background-processes.mdx
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
---
|
||||
title: Run background processes
|
||||
pcx_content_type: how-to
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
This guide shows you how to start, monitor, and manage long-running background processes in the sandbox.
|
||||
|
||||
## When to use background processes
|
||||
|
||||
Use `startProcess()` instead of `exec()` when:
|
||||
|
||||
- **Running web servers** - HTTP servers, APIs, WebSocket servers
|
||||
- **Long-running services** - Database servers, caches, message queues
|
||||
- **Development servers** - Hot-reloading dev servers, watch modes
|
||||
- **Continuous monitoring** - Log watchers, health checkers
|
||||
- **Parallel execution** - Multiple services running simultaneously
|
||||
|
||||
Use `exec()` for:
|
||||
|
||||
- **One-time commands** - Installations, builds, data processing
|
||||
- **Quick scripts** - Simple operations that complete and exit
|
||||
|
||||
## Start a background process
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// Start a web server
|
||||
const server = await sandbox.startProcess('python -m http.server 8000');
|
||||
|
||||
console.log('Server started');
|
||||
console.log('Process ID:', server.id);
|
||||
console.log('PID:', server.pid);
|
||||
console.log('Status:', server.status); // 'running'
|
||||
|
||||
// Process runs in background - your code continues
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Configure process environment
|
||||
|
||||
Set working directory and environment variables:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const process = await sandbox.startProcess('node server.js', {
|
||||
cwd: '/workspace/api',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: '8080',
|
||||
API_KEY: env.API_KEY,
|
||||
DATABASE_URL: env.DATABASE_URL
|
||||
}
|
||||
});
|
||||
|
||||
console.log('API server started');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Monitor process status
|
||||
|
||||
List and check running processes:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const processes = await sandbox.listProcesses();
|
||||
|
||||
console.log(`Running ${processes.length} processes:`);
|
||||
|
||||
for (const proc of processes) {
|
||||
console.log(`${proc.id}: ${proc.command} (${proc.status})`);
|
||||
}
|
||||
|
||||
// Check if specific process is running
|
||||
const isRunning = processes.some(p => p.id === processId && p.status === 'running');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Monitor process logs
|
||||
|
||||
Stream logs in real-time to detect when a service is ready:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { parseSSEStream, type LogEvent } from '@cloudflare/sandbox';
|
||||
|
||||
const server = await sandbox.startProcess('node server.js');
|
||||
|
||||
// Stream logs
|
||||
const logStream = await sandbox.streamProcessLogs(server.id);
|
||||
|
||||
for await (const log of parseSSEStream<LogEvent>(logStream)) {
|
||||
console.log(log.data);
|
||||
|
||||
if (log.data.includes('Server listening')) {
|
||||
console.log('Server is ready!');
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
Or get accumulated logs:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const logs = await sandbox.getProcessLogs(server.id);
|
||||
console.log('Logs:', logs);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Stop processes
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Stop specific process
|
||||
await sandbox.killProcess(server.id);
|
||||
|
||||
// Force kill if needed
|
||||
await sandbox.killProcess(server.id, 'SIGKILL');
|
||||
|
||||
// Stop all processes
|
||||
await sandbox.killAllProcesses();
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Run multiple processes
|
||||
|
||||
Start services in sequence, waiting for dependencies:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { parseSSEStream, type LogEvent } from '@cloudflare/sandbox';
|
||||
|
||||
// Start database first
|
||||
const db = await sandbox.startProcess('redis-server');
|
||||
|
||||
// Wait for database to be ready
|
||||
const dbLogs = await sandbox.streamProcessLogs(db.id);
|
||||
for await (const log of parseSSEStream<LogEvent>(dbLogs)) {
|
||||
if (log.data.includes('Ready to accept connections')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now start API server (depends on database)
|
||||
const api = await sandbox.startProcess('node api-server.js', {
|
||||
env: { DATABASE_URL: 'redis://localhost:6379' }
|
||||
});
|
||||
|
||||
console.log('All services running');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Wait for readiness** - Stream logs to detect when services are ready
|
||||
- **Clean up** - Always stop processes when done
|
||||
- **Handle failures** - Monitor logs for errors and restart if needed
|
||||
- **Use try/finally** - Ensure cleanup happens even on errors
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Process exits immediately
|
||||
|
||||
Check logs to see why:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const process = await sandbox.startProcess('node server.js');
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
const processes = await sandbox.listProcesses();
|
||||
if (!processes.find(p => p.id === process.id)) {
|
||||
const logs = await sandbox.getProcessLogs(process.id);
|
||||
console.error('Process exited:', logs);
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Port already in use
|
||||
|
||||
Kill existing processes before starting:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.killAllProcesses();
|
||||
const server = await sandbox.startProcess('node server.js');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Commands API reference](/sandbox/api/commands/) - Complete process management API
|
||||
- [Execute commands guide](/sandbox/guides/execute-commands/) - One-time command execution
|
||||
- [Expose services guide](/sandbox/guides/expose-services/) - Make processes accessible
|
||||
- [Streaming output guide](/sandbox/guides/streaming-output/) - Monitor process output
|
||||
262
src/content/docs/sandbox/guides/code-execution.mdx
Normal file
262
src/content/docs/sandbox/guides/code-execution.mdx
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
---
|
||||
title: Use code interpreter
|
||||
pcx_content_type: how-to
|
||||
sidebar:
|
||||
order: 5
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
This guide shows you how to execute Python and JavaScript code with rich outputs using the Code Interpreter API.
|
||||
|
||||
## When to use code interpreter
|
||||
|
||||
Use the Code Interpreter API for **simple, direct code execution** with minimal setup:
|
||||
|
||||
- **Quick code execution** - Run Python/JS code without environment setup
|
||||
- **Rich outputs** - Get charts, tables, images, HTML automatically
|
||||
- **AI-generated code** - Execute LLM-generated code with structured results
|
||||
- **Persistent state** - Variables preserved between executions in the same context
|
||||
|
||||
Use `exec()` for **advanced or custom workflows**:
|
||||
|
||||
- **System operations** - Install packages, manage files, run builds
|
||||
- **Custom environments** - Configure specific versions, dependencies
|
||||
- **Shell commands** - Git operations, system utilities, complex pipelines
|
||||
- **Long-running processes** - Background services, servers
|
||||
|
||||
## Create an execution context
|
||||
|
||||
Code contexts maintain state between executions:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// Create a Python context
|
||||
const pythonContext = await sandbox.createCodeContext({
|
||||
language: 'python'
|
||||
});
|
||||
|
||||
console.log('Context ID:', pythonContext.id);
|
||||
console.log('Language:', pythonContext.language);
|
||||
|
||||
// Create a JavaScript context
|
||||
const jsContext = await sandbox.createCodeContext({
|
||||
language: 'javascript'
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Execute code
|
||||
|
||||
### Simple execution
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Create context
|
||||
const context = await sandbox.createCodeContext({
|
||||
language: 'python'
|
||||
});
|
||||
|
||||
// Execute code
|
||||
const result = await sandbox.runCode(context.id, `
|
||||
print("Hello from Code Interpreter!")
|
||||
result = 2 + 2
|
||||
print(f"2 + 2 = {result}")
|
||||
`);
|
||||
|
||||
console.log('Output:', result.output);
|
||||
console.log('Success:', result.success);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Persistent state
|
||||
|
||||
Variables and imports persist between executions in the same context:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const context = await sandbox.createCodeContext({
|
||||
language: 'python'
|
||||
});
|
||||
|
||||
// First execution - import and define variables
|
||||
await sandbox.runCode(context.id, `
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
data = [1, 2, 3, 4, 5]
|
||||
print("Data initialized")
|
||||
`);
|
||||
|
||||
// Second execution - use previously defined variables
|
||||
const result = await sandbox.runCode(context.id, `
|
||||
mean = np.mean(data)
|
||||
print(f"Mean: {mean}")
|
||||
`);
|
||||
|
||||
console.log(result.output); // "Mean: 3.0"
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Handle rich outputs
|
||||
|
||||
The code interpreter returns multiple output formats:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const result = await sandbox.runCode(context.id, `
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.plot([1, 2, 3], [1, 4, 9])
|
||||
plt.title('Simple Chart')
|
||||
plt.show()
|
||||
`);
|
||||
|
||||
// Check available formats
|
||||
console.log('Formats:', result.formats); // ['text', 'png']
|
||||
|
||||
// Access outputs
|
||||
if (result.outputs.png) {
|
||||
// Return as image
|
||||
return new Response(atob(result.outputs.png), {
|
||||
headers: { 'Content-Type': 'image/png' }
|
||||
});
|
||||
}
|
||||
|
||||
if (result.outputs.html) {
|
||||
// Return as HTML (pandas DataFrames)
|
||||
return new Response(result.outputs.html, {
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
});
|
||||
}
|
||||
|
||||
if (result.outputs.json) {
|
||||
// Return as JSON
|
||||
return Response.json(result.outputs.json);
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Stream execution output
|
||||
|
||||
For long-running code, stream output in real-time:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const context = await sandbox.createCodeContext({
|
||||
language: 'python'
|
||||
});
|
||||
|
||||
const result = await sandbox.runCode(
|
||||
context.id,
|
||||
`
|
||||
import time
|
||||
|
||||
for i in range(10):
|
||||
print(f"Processing item {i+1}/10...")
|
||||
time.sleep(0.5)
|
||||
|
||||
print("Done!")
|
||||
`,
|
||||
{
|
||||
stream: true,
|
||||
onOutput: (data) => {
|
||||
console.log('Output:', data);
|
||||
},
|
||||
onResult: (result) => {
|
||||
console.log('Result:', result);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Execute AI-generated code
|
||||
|
||||
Run LLM-generated code safely in a sandbox:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// 1. Generate code with Claude
|
||||
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': env.ANTHROPIC_API_KEY,
|
||||
'anthropic-version': '2023-06-01'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: 'claude-3-5-sonnet-20241022',
|
||||
max_tokens: 1024,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: 'Write Python code to calculate fibonacci sequence up to 100'
|
||||
}]
|
||||
})
|
||||
});
|
||||
|
||||
const { content } = await response.json();
|
||||
const code = content[0].text;
|
||||
|
||||
// 2. Execute in sandbox
|
||||
const context = await sandbox.createCodeContext({ language: 'python' });
|
||||
const result = await sandbox.runCode(context.id, code);
|
||||
|
||||
console.log('Generated code:', code);
|
||||
console.log('Output:', result.output);
|
||||
console.log('Success:', result.success);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Manage contexts
|
||||
|
||||
### List all contexts
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const contexts = await sandbox.listCodeContexts();
|
||||
|
||||
console.log(`${contexts.length} active contexts:`);
|
||||
|
||||
for (const ctx of contexts) {
|
||||
console.log(` ${ctx.id} (${ctx.language})`);
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Delete contexts
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Delete specific context
|
||||
await sandbox.deleteCodeContext(context.id);
|
||||
console.log('Context deleted');
|
||||
|
||||
// Clean up all contexts
|
||||
const contexts = await sandbox.listCodeContexts();
|
||||
for (const ctx of contexts) {
|
||||
await sandbox.deleteCodeContext(ctx.id);
|
||||
}
|
||||
console.log('All contexts deleted');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Clean up contexts** - Delete contexts when done to free resources
|
||||
- **Handle errors** - Always check `result.success` and `result.error`
|
||||
- **Stream long operations** - Use streaming for code that takes >2 seconds
|
||||
- **Validate AI code** - Review generated code before execution
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Code Interpreter API reference](/sandbox/api/interpreter/) - Complete API documentation
|
||||
- [AI code executor tutorial](/sandbox/tutorials/ai-code-executor/) - Build complete AI executor
|
||||
- [Execute commands guide](/sandbox/guides/execute-commands/) - Lower-level command execution
|
||||
171
src/content/docs/sandbox/guides/execute-commands.mdx
Normal file
171
src/content/docs/sandbox/guides/execute-commands.mdx
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
---
|
||||
title: Execute commands
|
||||
pcx_content_type: how-to
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
This guide shows you how to execute commands in the sandbox, handle output, and manage errors effectively.
|
||||
|
||||
## Choose the right method
|
||||
|
||||
The SDK provides two methods for command execution:
|
||||
|
||||
- **`exec()`** - Run a command and wait for complete result. Best for most use cases.
|
||||
- **`execStream()`** - Stream output in real-time. Best for long-running commands where you need immediate feedback.
|
||||
|
||||
## Execute basic commands
|
||||
|
||||
Use `exec()` for simple commands that complete quickly:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// Execute a single command
|
||||
const result = await sandbox.exec('python --version');
|
||||
|
||||
console.log(result.stdout); // "Python 3.11.0"
|
||||
console.log(result.exitCode); // 0
|
||||
console.log(result.success); // true
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Pass arguments safely
|
||||
|
||||
When passing user input or dynamic values, avoid string interpolation to prevent injection attacks:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Unsafe - vulnerable to injection
|
||||
const filename = userInput;
|
||||
await sandbox.exec(`cat ${filename}`);
|
||||
|
||||
// Safe - use proper escaping or validation
|
||||
const safeFilename = filename.replace(/[^a-zA-Z0-9_.-]/g, '');
|
||||
await sandbox.exec(`cat ${safeFilename}`);
|
||||
|
||||
// Better - write to file and execute
|
||||
await sandbox.writeFile('/tmp/input.txt', userInput);
|
||||
await sandbox.exec('python process.py /tmp/input.txt');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Handle errors
|
||||
|
||||
Commands can fail in two ways:
|
||||
|
||||
1. **Non-zero exit code** - Command ran but failed (result.success === false)
|
||||
2. **Execution error** - Command couldn't start (throws exception)
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
try {
|
||||
const result = await sandbox.exec('python analyze.py');
|
||||
|
||||
if (!result.success) {
|
||||
// Command failed (non-zero exit code)
|
||||
console.error('Analysis failed:', result.stderr);
|
||||
console.log('Exit code:', result.exitCode);
|
||||
|
||||
// Handle specific exit codes
|
||||
if (result.exitCode === 1) {
|
||||
throw new Error('Invalid input data');
|
||||
} else if (result.exitCode === 2) {
|
||||
throw new Error('Missing dependencies');
|
||||
}
|
||||
}
|
||||
|
||||
// Success - process output
|
||||
return JSON.parse(result.stdout);
|
||||
|
||||
} catch (error) {
|
||||
// Execution error (couldn't start command)
|
||||
console.error('Execution failed:', error.message);
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Execute shell commands
|
||||
|
||||
The sandbox supports shell features like pipes, redirects, and chaining:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Pipes and filters
|
||||
const result = await sandbox.exec('ls -la | grep ".py" | wc -l');
|
||||
console.log('Python files:', result.stdout.trim());
|
||||
|
||||
// Output redirection
|
||||
await sandbox.exec('python generate.py > output.txt 2> errors.txt');
|
||||
|
||||
// Multiple commands
|
||||
await sandbox.exec('cd /workspace && npm install && npm test');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Execute Python scripts
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Run inline Python
|
||||
const result = await sandbox.exec('python -c "print(sum([1, 2, 3, 4, 5]))"');
|
||||
console.log('Sum:', result.stdout.trim()); // "15"
|
||||
|
||||
// Run a script file
|
||||
await sandbox.writeFile('/workspace/analyze.py', `
|
||||
import sys
|
||||
print(f"Argument: {sys.argv[1]}")
|
||||
`);
|
||||
|
||||
await sandbox.exec('python /workspace/analyze.py data.csv');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Check exit codes** - Always verify `result.success` and `result.exitCode`
|
||||
- **Validate inputs** - Escape or validate user input to prevent injection
|
||||
- **Use streaming** - For long operations, use `execStream()` for real-time feedback
|
||||
- **Handle errors** - Check stderr for error details
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Command not found
|
||||
|
||||
Verify the command exists in the container:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const check = await sandbox.exec('which python3');
|
||||
if (!check.success) {
|
||||
console.error('python3 not found');
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Working directory issues
|
||||
|
||||
Use absolute paths or change directory:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Use absolute path
|
||||
await sandbox.exec('python /workspace/my-app/script.py');
|
||||
|
||||
// Or change directory
|
||||
await sandbox.exec('cd /workspace/my-app && python script.py');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Commands API reference](/sandbox/api/commands/) - Complete method documentation
|
||||
- [Background processes guide](/sandbox/guides/background-processes/) - Managing long-running processes
|
||||
- [Streaming output guide](/sandbox/guides/streaming-output/) - Advanced streaming patterns
|
||||
- [Code Interpreter guide](/sandbox/guides/code-execution/) - Higher-level code execution
|
||||
271
src/content/docs/sandbox/guides/expose-services.mdx
Normal file
271
src/content/docs/sandbox/guides/expose-services.mdx
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
---
|
||||
title: Expose services
|
||||
pcx_content_type: how-to
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
This guide shows you how to expose services running in your sandbox to the internet via preview URLs.
|
||||
|
||||
## When to expose ports
|
||||
|
||||
Expose ports when you need to:
|
||||
|
||||
- **Test web applications** - Preview frontend or backend apps
|
||||
- **Share demos** - Give others access to running applications
|
||||
- **Develop APIs** - Test endpoints from external tools
|
||||
- **Debug services** - Access internal services for troubleshooting
|
||||
- **Build dev environments** - Create shareable development workspaces
|
||||
|
||||
## Basic port exposure
|
||||
|
||||
The typical workflow is: start service → wait for ready → expose port → handle requests with `proxyToSandbox`.
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { getSandbox, proxyToSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// 1. Start a web server
|
||||
await sandbox.startProcess('python -m http.server 8000');
|
||||
|
||||
// 2. Wait for service to start
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// 3. Expose the port
|
||||
const exposed = await sandbox.exposePort(8000);
|
||||
|
||||
// 4. Preview URL is now available (public by default)
|
||||
console.log('Server accessible at:', exposed.exposedAt);
|
||||
// Returns: https://abc123-8000.sandbox.workers.dev
|
||||
|
||||
// 5. Handle preview URL requests in your Worker
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
// Proxy requests to the exposed port
|
||||
return proxyToSandbox(request, env.Sandbox, 'my-sandbox');
|
||||
}
|
||||
};
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
:::caution
|
||||
**Preview URLs are public by default.** Anyone with the URL can access your service. Add authentication if needed.
|
||||
:::
|
||||
|
||||
## Name your exposed ports
|
||||
|
||||
When exposing multiple ports, use names to stay organized:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Start and expose API server
|
||||
await sandbox.startProcess('node api.js', { env: { PORT: '8080' } });
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
const api = await sandbox.exposePort(8080, { name: 'api' });
|
||||
|
||||
// Start and expose frontend
|
||||
await sandbox.startProcess('npm run dev', { env: { PORT: '5173' } });
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
const frontend = await sandbox.exposePort(5173, { name: 'frontend' });
|
||||
|
||||
console.log('Services:');
|
||||
console.log('- API:', api.exposedAt);
|
||||
console.log('- Frontend:', frontend.exposedAt);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Wait for service readiness
|
||||
|
||||
Always verify a service is ready before exposing. Use a simple delay for most cases:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Start service
|
||||
await sandbox.startProcess('npm run dev', { env: { PORT: '8080' } });
|
||||
|
||||
// Wait 2-3 seconds
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Now expose
|
||||
await sandbox.exposePort(8080);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
For critical services, poll the health endpoint:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.startProcess('node api-server.js', { env: { PORT: '8080' } });
|
||||
|
||||
// Wait for health check
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
const check = await sandbox.exec('curl -f http://localhost:8080/health || echo "not ready"');
|
||||
if (check.stdout.includes('ok')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await sandbox.exposePort(8080);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Multiple services
|
||||
|
||||
Expose multiple ports for full-stack applications:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Start backend
|
||||
await sandbox.startProcess('node api/server.js', {
|
||||
env: { PORT: '8080' }
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Start frontend
|
||||
await sandbox.startProcess('npm run dev', {
|
||||
cwd: '/workspace/frontend',
|
||||
env: { PORT: '5173', API_URL: 'http://localhost:8080' }
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// Expose both
|
||||
const api = await sandbox.exposePort(8080, { name: 'api' });
|
||||
const frontend = await sandbox.exposePort(5173, { name: 'frontend' });
|
||||
|
||||
return Response.json({
|
||||
api: api.exposedAt,
|
||||
frontend: frontend.exposedAt
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Manage exposed ports
|
||||
|
||||
### List currently exposed ports
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const { ports, count } = await sandbox.getExposedPorts();
|
||||
|
||||
console.log(`${count} ports currently exposed:`);
|
||||
|
||||
for (const port of ports) {
|
||||
console.log(` Port ${port.port}: ${port.exposedAt}`);
|
||||
if (port.name) {
|
||||
console.log(` Name: ${port.name}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Unexpose ports
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Unexpose a single port
|
||||
await sandbox.unexposePort(8000);
|
||||
|
||||
// Unexpose multiple ports
|
||||
for (const port of [3000, 5173, 8080]) {
|
||||
await sandbox.unexposePort(port);
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Wait for readiness** - Don't expose ports immediately after starting processes
|
||||
- **Use named ports** - Easier to track when exposing multiple ports
|
||||
- **Clean up** - Unexpose ports when done to prevent abandoned URLs
|
||||
- **Add authentication** - Preview URLs are public; protect sensitive services
|
||||
|
||||
## Local development
|
||||
|
||||
When developing locally with `wrangler dev`, you must expose ports in your Dockerfile:
|
||||
|
||||
```dockerfile title="Dockerfile"
|
||||
FROM docker.io/cloudflare/sandbox:0.3.3
|
||||
|
||||
# Expose ports you plan to use
|
||||
EXPOSE 8000
|
||||
EXPOSE 8080
|
||||
EXPOSE 5173
|
||||
```
|
||||
|
||||
Update `wrangler.jsonc` to use your Dockerfile:
|
||||
|
||||
```jsonc title="wrangler.jsonc"
|
||||
{
|
||||
"containers": [
|
||||
{
|
||||
"class_name": "Sandbox",
|
||||
"image": "./Dockerfile"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
In production, all ports are available and controlled programmatically via `exposePort()` / `unexposePort()`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port 3000 is reserved
|
||||
|
||||
Port 3000 is used by the internal Bun server and cannot be exposed:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// ❌ This will fail
|
||||
await sandbox.exposePort(3000); // Error: Port 3000 is reserved
|
||||
|
||||
// ✅ Use a different port
|
||||
await sandbox.startProcess('node server.js', { env: { PORT: '8080' } });
|
||||
await sandbox.exposePort(8080);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Port not ready
|
||||
|
||||
Wait for the service to start before exposing:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.startProcess('npm run dev');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
await sandbox.exposePort(8080);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Port already exposed
|
||||
|
||||
Check before exposing to avoid errors:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const { ports } = await sandbox.getExposedPorts();
|
||||
if (!ports.some(p => p.port === 8080)) {
|
||||
await sandbox.exposePort(8080);
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Preview URL format
|
||||
|
||||
Preview URLs follow the pattern `https://{sandbox-id}-{port}.sandbox.workers.dev`:
|
||||
|
||||
- Port 8080: `https://abc123-8080.sandbox.workers.dev`
|
||||
- Port 5173: `https://abc123-5173.sandbox.workers.dev`
|
||||
|
||||
**Note**: Port 3000 is reserved for the internal Bun server and cannot be exposed.
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Ports API reference](/sandbox/api/ports/) - Complete port exposure API
|
||||
- [Background processes guide](/sandbox/guides/background-processes/) - Managing services
|
||||
- [Execute commands guide](/sandbox/guides/execute-commands/) - Starting services
|
||||
142
src/content/docs/sandbox/guides/git-workflows.mdx
Normal file
142
src/content/docs/sandbox/guides/git-workflows.mdx
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
---
|
||||
title: Work with Git
|
||||
pcx_content_type: how-to
|
||||
sidebar:
|
||||
order: 6
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
This guide shows you how to clone repositories, manage branches, and automate Git operations in the sandbox.
|
||||
|
||||
## Clone repositories
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// Basic clone
|
||||
await sandbox.gitCheckout('https://github.com/user/repo');
|
||||
|
||||
// Clone specific branch
|
||||
await sandbox.gitCheckout('https://github.com/user/repo', {
|
||||
branch: 'develop'
|
||||
});
|
||||
|
||||
// Shallow clone (faster for large repos)
|
||||
await sandbox.gitCheckout('https://github.com/user/large-repo', {
|
||||
depth: 1
|
||||
});
|
||||
|
||||
// Clone to specific directory
|
||||
await sandbox.gitCheckout('https://github.com/user/my-app', {
|
||||
targetDir: '/workspace/project'
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Clone private repositories
|
||||
|
||||
Use a personal access token in the URL:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const token = env.GITHUB_TOKEN;
|
||||
const repoUrl = `https://${token}@github.com/user/private-repo.git`;
|
||||
|
||||
await sandbox.gitCheckout(repoUrl);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Clone and build
|
||||
|
||||
Clone a repository and run build steps:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.gitCheckout('https://github.com/user/my-app');
|
||||
|
||||
const repoName = 'my-app';
|
||||
|
||||
// Install and build
|
||||
await sandbox.exec(`cd ${repoName} && npm install`);
|
||||
await sandbox.exec(`cd ${repoName} && npm run build`);
|
||||
|
||||
console.log('Build complete');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Work with branches
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.gitCheckout('https://github.com/user/repo');
|
||||
|
||||
// Switch branches
|
||||
await sandbox.exec('cd repo && git checkout feature-branch');
|
||||
|
||||
// Create new branch
|
||||
await sandbox.exec('cd repo && git checkout -b new-feature');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Make changes and commit
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.gitCheckout('https://github.com/user/repo');
|
||||
|
||||
// Modify a file
|
||||
const readme = await sandbox.readFile('/workspace/repo/README.md');
|
||||
await sandbox.writeFile('/workspace/repo/README.md', readme.content + '\n\n## New Section');
|
||||
|
||||
// Commit changes
|
||||
await sandbox.exec('cd repo && git config user.name "Sandbox Bot"');
|
||||
await sandbox.exec('cd repo && git config user.email "bot@example.com"');
|
||||
await sandbox.exec('cd repo && git add README.md');
|
||||
await sandbox.exec('cd repo && git commit -m "Update README"');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Use shallow clones** - Faster for large repos with `depth: 1`
|
||||
- **Store credentials securely** - Use environment variables for tokens
|
||||
- **Clean up** - Delete unused repositories to save space
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication fails
|
||||
|
||||
Verify your token is set:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
if (!env.GITHUB_TOKEN) {
|
||||
throw new Error('GITHUB_TOKEN not configured');
|
||||
}
|
||||
|
||||
const repoUrl = `https://${env.GITHUB_TOKEN}@github.com/user/private-repo.git`;
|
||||
await sandbox.gitCheckout(repoUrl);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Large repository timeout
|
||||
|
||||
Use shallow clone:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
await sandbox.gitCheckout('https://github.com/user/large-repo', {
|
||||
depth: 1
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Files API reference](/sandbox/api/files/) - File operations after cloning
|
||||
- [Execute commands guide](/sandbox/guides/execute-commands/) - Run git commands
|
||||
- [Manage files guide](/sandbox/guides/manage-files/) - Work with cloned files
|
||||
23
src/content/docs/sandbox/guides/index.mdx
Normal file
23
src/content/docs/sandbox/guides/index.mdx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
title: How-to guides
|
||||
pcx_content_type: navigation
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
These guides show you how to solve specific problems and implement features with the Sandbox SDK. Each guide focuses on a particular task and provides practical, production-ready solutions.
|
||||
|
||||
## Available guides
|
||||
|
||||
- [Execute commands](/sandbox/guides/execute-commands/) - Run commands with streaming output, error handling, and shell access
|
||||
- [Manage files](/sandbox/guides/manage-files/) - Read, write, organize, and synchronize files in the sandbox
|
||||
- [Run background processes](/sandbox/guides/background-processes/) - Start and manage long-running services and applications
|
||||
- [Expose services](/sandbox/guides/expose-services/) - Create preview URLs and expose ports for web services
|
||||
- [Use code interpreter](/sandbox/guides/code-execution/) - Execute Python and JavaScript code with rich outputs
|
||||
- [Work with Git](/sandbox/guides/git-workflows/) - Clone repositories, manage branches, and automate Git operations
|
||||
- [Stream output](/sandbox/guides/streaming-output/) - Handle real-time output from commands and processes
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Tutorials](/sandbox/tutorials/) - Step-by-step learning paths
|
||||
- [API reference](/sandbox/api/) - Complete method documentation
|
||||
175
src/content/docs/sandbox/guides/manage-files.mdx
Normal file
175
src/content/docs/sandbox/guides/manage-files.mdx
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
---
|
||||
title: Manage files
|
||||
pcx_content_type: how-to
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
This guide shows you how to read, write, organize, and synchronize files in the sandbox filesystem.
|
||||
|
||||
## Path conventions
|
||||
|
||||
File operations support both absolute and relative paths:
|
||||
|
||||
- `/workspace` - Default working directory for application files
|
||||
- `/tmp` - Temporary files (may be cleared)
|
||||
- `/home` - User home directory
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Absolute paths
|
||||
await sandbox.writeFile('/workspace/app.js', code);
|
||||
|
||||
// Relative paths (session-aware)
|
||||
const session = await sandbox.createSession();
|
||||
await session.exec('cd /workspace/my-project');
|
||||
await session.writeFile('app.js', code); // Writes to /workspace/my-project/app.js
|
||||
await session.writeFile('src/index.js', code); // Writes to /workspace/my-project/src/index.js
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Write files
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
// Write text file
|
||||
await sandbox.writeFile('/workspace/app.js', `console.log('Hello from sandbox!');`);
|
||||
|
||||
// Write JSON
|
||||
const config = { name: 'my-app', version: '1.0.0' };
|
||||
await sandbox.writeFile('/workspace/config.json', JSON.stringify(config, null, 2));
|
||||
|
||||
// Write binary file (base64)
|
||||
const buffer = await fetch(imageUrl).then(r => r.arrayBuffer());
|
||||
const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
||||
await sandbox.writeFile('/workspace/image.png', base64, { encoding: 'base64' });
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Read files
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Read text file
|
||||
const file = await sandbox.readFile('/workspace/app.js');
|
||||
console.log(file.content);
|
||||
|
||||
// Read and parse JSON
|
||||
const configFile = await sandbox.readFile('/workspace/config.json');
|
||||
const config = JSON.parse(configFile.content);
|
||||
|
||||
// Read binary file
|
||||
const imageFile = await sandbox.readFile('/workspace/image.png', { encoding: 'base64' });
|
||||
return new Response(atob(imageFile.content), {
|
||||
headers: { 'Content-Type': 'image/png' }
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Organize files
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Create directories
|
||||
await sandbox.mkdir('/workspace/src', { recursive: true });
|
||||
await sandbox.mkdir('/workspace/tests', { recursive: true });
|
||||
|
||||
// Rename file
|
||||
await sandbox.renameFile('/workspace/draft.txt', '/workspace/final.txt');
|
||||
|
||||
// Move file
|
||||
await sandbox.moveFile('/tmp/download.txt', '/workspace/data.txt');
|
||||
|
||||
// Delete file
|
||||
await sandbox.deleteFile('/workspace/temp.txt');
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Batch operations
|
||||
|
||||
Write multiple files in parallel:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const files = {
|
||||
'/workspace/src/app.js': 'console.log("app");',
|
||||
'/workspace/src/utils.js': 'console.log("utils");',
|
||||
'/workspace/README.md': '# My Project'
|
||||
};
|
||||
|
||||
await Promise.all(
|
||||
Object.entries(files).map(([path, content]) =>
|
||||
sandbox.writeFile(path, content)
|
||||
)
|
||||
);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Check if file exists
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
try {
|
||||
await sandbox.readFile('/workspace/config.json');
|
||||
console.log('File exists');
|
||||
} catch (error) {
|
||||
if (error.code === 'FILE_NOT_FOUND') {
|
||||
// Create default config
|
||||
await sandbox.writeFile('/workspace/config.json', '{}');
|
||||
}
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Use `/workspace`** - Default working directory for app files
|
||||
- **Use absolute paths** - Always use full paths like `/workspace/file.txt`
|
||||
- **Batch operations** - Use `Promise.all()` for multiple independent file writes
|
||||
- **Create parent directories** - Use `recursive: true` when creating nested paths
|
||||
- **Handle errors** - Check for `FILE_NOT_FOUND` errors gracefully
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Directory doesn't exist
|
||||
|
||||
Create parent directories first:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Create directory, then write file
|
||||
await sandbox.mkdir('/workspace/data', { recursive: true });
|
||||
await sandbox.writeFile('/workspace/data/file.txt', content);
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
### Binary file encoding
|
||||
|
||||
Use base64 for binary files:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Write binary
|
||||
await sandbox.writeFile('/workspace/image.png', base64Data, {
|
||||
encoding: 'base64'
|
||||
});
|
||||
|
||||
// Read binary
|
||||
const file = await sandbox.readFile('/workspace/image.png', {
|
||||
encoding: 'base64'
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Files API reference](/sandbox/api/files/) - Complete method documentation
|
||||
- [Execute commands guide](/sandbox/guides/execute-commands/) - Run file operations with commands
|
||||
- [Git workflows guide](/sandbox/guides/git-workflows/) - Clone and manage repositories
|
||||
- [Code Interpreter guide](/sandbox/guides/code-execution/) - Generate and execute code files
|
||||
167
src/content/docs/sandbox/guides/streaming-output.mdx
Normal file
167
src/content/docs/sandbox/guides/streaming-output.mdx
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
---
|
||||
title: Stream output
|
||||
pcx_content_type: how-to
|
||||
sidebar:
|
||||
order: 7
|
||||
---
|
||||
|
||||
import { TypeScriptExample } from "~/components";
|
||||
|
||||
This guide shows you how to handle real-time output from commands, processes, and code execution.
|
||||
|
||||
## When to use streaming
|
||||
|
||||
Use streaming when you need:
|
||||
|
||||
- **Real-time feedback** - Show progress as it happens
|
||||
- **Long-running operations** - Builds, tests, installations that take time
|
||||
- **Interactive applications** - Chat bots, code execution, live demos
|
||||
- **Large output** - Process output incrementally instead of all at once
|
||||
- **User experience** - Prevent users from waiting with no feedback
|
||||
|
||||
Use non-streaming (`exec()`) for:
|
||||
|
||||
- **Quick operations** - Commands that complete in seconds
|
||||
- **Small output** - When output fits easily in memory
|
||||
- **Post-processing** - When you need complete output before processing
|
||||
|
||||
## Stream command execution
|
||||
|
||||
Use `execStream()` to get real-time output:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { getSandbox, parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
|
||||
|
||||
const stream = await sandbox.execStream('npm run build');
|
||||
|
||||
for await (const event of parseSSEStream<ExecEvent>(stream)) {
|
||||
switch (event.type) {
|
||||
case 'stdout':
|
||||
console.log(event.data);
|
||||
break;
|
||||
|
||||
case 'stderr':
|
||||
console.error(event.data);
|
||||
break;
|
||||
|
||||
case 'complete':
|
||||
console.log('Exit code:', event.exitCode);
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
console.error('Failed:', event.error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Stream to client
|
||||
|
||||
Return streaming output to users via Server-Sent Events:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const sandbox = getSandbox(env.Sandbox, 'builder');
|
||||
|
||||
const stream = await sandbox.execStream('npm run build');
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
Client-side consumption:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
// Browser JavaScript
|
||||
const eventSource = new EventSource('/build');
|
||||
|
||||
eventSource.addEventListener('stdout', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log(data.data);
|
||||
});
|
||||
|
||||
eventSource.addEventListener('complete', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('Exit code:', data.exitCode);
|
||||
eventSource.close();
|
||||
});
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Stream process logs
|
||||
|
||||
Monitor background process output:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
import { parseSSEStream, type LogEvent } from '@cloudflare/sandbox';
|
||||
|
||||
const process = await sandbox.startProcess('node server.js');
|
||||
|
||||
const logStream = await sandbox.streamProcessLogs(process.id);
|
||||
|
||||
for await (const log of parseSSEStream<LogEvent>(logStream)) {
|
||||
console.log(log.data);
|
||||
|
||||
if (log.data.includes('Server listening')) {
|
||||
console.log('Server is ready');
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Handle errors
|
||||
|
||||
Check exit codes and handle stream errors:
|
||||
|
||||
<TypeScriptExample>
|
||||
```
|
||||
const stream = await sandbox.execStream('npm run build');
|
||||
|
||||
for await (const event of parseSSEStream<ExecEvent>(stream)) {
|
||||
switch (event.type) {
|
||||
case 'stdout':
|
||||
console.log(event.data);
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
throw new Error(`Build failed: ${event.error}`);
|
||||
|
||||
case 'complete':
|
||||
if (event.exitCode !== 0) {
|
||||
throw new Error(`Build failed with exit code ${event.exitCode}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
</TypeScriptExample>
|
||||
|
||||
## Best practices
|
||||
|
||||
- **Always consume streams** - Don't let streams hang unconsumed
|
||||
- **Handle all event types** - Process stdout, stderr, complete, and error events
|
||||
- **Check exit codes** - Non-zero exit codes indicate failure
|
||||
- **Provide feedback** - Show progress to users for long operations
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Commands API reference](/sandbox/api/commands/) - Complete streaming API
|
||||
- [Execute commands guide](/sandbox/guides/execute-commands/) - Command execution patterns
|
||||
- [Background processes guide](/sandbox/guides/background-processes/) - Process log streaming
|
||||
- [Code Interpreter guide](/sandbox/guides/code-execution/) - Stream code execution output
|
||||
241
src/content/docs/sandbox/index.mdx
Normal file
241
src/content/docs/sandbox/index.mdx
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
---
|
||||
title: Sandbox SDK (Beta)
|
||||
order: 0
|
||||
|
||||
pcx_content_type: overview
|
||||
sidebar:
|
||||
order: 1
|
||||
badge:
|
||||
text: Beta
|
||||
group:
|
||||
label: sandbox
|
||||
head:
|
||||
- tag: title
|
||||
content: Overview
|
||||
---
|
||||
|
||||
import {
|
||||
CardGrid,
|
||||
Description,
|
||||
Feature,
|
||||
LinkTitleCard,
|
||||
Plan,
|
||||
RelatedProduct,
|
||||
LinkButton,
|
||||
Tabs,
|
||||
TabItem,
|
||||
} from "~/components";
|
||||
|
||||
<Description>
|
||||
|
||||
Build secure, isolated code execution environments
|
||||
|
||||
</Description>
|
||||
|
||||
<Plan type="workers-paid" />
|
||||
|
||||
The Sandbox SDK enables you to run untrusted code securely in isolated environments. Built on [Containers](/containers/), Sandbox SDK provides a simple API for executing commands, managing files, running background processes, and exposing services — all from your [Workers](/workers/) applications.
|
||||
|
||||
Sandboxes are ideal for building AI agents that need to execute code, interactive development environments, data analysis platforms, CI/CD systems, and any application that needs secure code execution at the edge. Each sandbox runs in its own isolated container with a full Linux environment, providing strong security boundaries while maintaining performance.
|
||||
|
||||
With Sandbox, you can execute Python scripts, run Node.js applications, analyze data, compile code, and perform complex computations — all with a simple TypeScript API and no infrastructure to manage.
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Execute Commands">
|
||||
```typescript
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const sandbox = getSandbox(env.Sandbox, 'user-123');
|
||||
|
||||
// Execute a command and get the result
|
||||
const result = await sandbox.exec('python --version');
|
||||
|
||||
return Response.json({
|
||||
output: result.stdout,
|
||||
exitCode: result.exitCode,
|
||||
success: result.success
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="Code Interpreter">
|
||||
```typescript
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const sandbox = getSandbox(env.Sandbox, 'user-123');
|
||||
|
||||
// Create a Python execution context
|
||||
const ctx = await sandbox.createCodeContext({ language: 'python' });
|
||||
|
||||
// Execute Python code with automatic result capture
|
||||
const result = await sandbox.runCode(`
|
||||
import pandas as pd
|
||||
data = {'product': ['A', 'B', 'C'], 'sales': [100, 200, 150]}
|
||||
df = pd.DataFrame(data)
|
||||
df['sales'].sum() # Last expression is automatically returned
|
||||
`, { context: ctx });
|
||||
|
||||
return Response.json({
|
||||
result: result.results?.[0]?.text,
|
||||
logs: result.logs
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="File Operations">
|
||||
```typescript
|
||||
import { getSandbox } from '@cloudflare/sandbox';
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const sandbox = getSandbox(env.Sandbox, 'user-123');
|
||||
|
||||
// Create a project structure
|
||||
await sandbox.mkdir('/workspace/project/src', { recursive: true });
|
||||
|
||||
// Write files
|
||||
await sandbox.writeFile(
|
||||
'/workspace/project/package.json',
|
||||
JSON.stringify({ name: 'my-app', version: '1.0.0' })
|
||||
);
|
||||
|
||||
// Read a file back
|
||||
const content = await sandbox.readFile('/workspace/project/package.json');
|
||||
|
||||
return Response.json({ content });
|
||||
}
|
||||
};
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<LinkButton variant="primary" href="/sandbox/get-started/">
|
||||
Get started
|
||||
</LinkButton>
|
||||
<LinkButton variant="secondary" href="/sandbox/api/">
|
||||
API Reference
|
||||
</LinkButton>
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
<Feature header="Execute commands securely" href="/sandbox/guides/execute-commands/" cta="Learn about command execution">
|
||||
|
||||
Run shell commands, Python scripts, Node.js applications, and more in isolated containers with streaming output support and automatic timeout handling.
|
||||
|
||||
</Feature>
|
||||
|
||||
<Feature header="Manage files and processes" href="/sandbox/guides/manage-files/" cta="Learn about file operations">
|
||||
|
||||
Read, write, and manipulate files in the sandbox filesystem. Run background processes, monitor output, and manage long-running operations.
|
||||
|
||||
</Feature>
|
||||
|
||||
<Feature header="Expose services with preview URLs" href="/sandbox/guides/expose-services/" cta="Learn about preview URLs">
|
||||
|
||||
Expose HTTP services running in your sandbox with automatically generated preview URLs, perfect for interactive development environments and application hosting.
|
||||
|
||||
</Feature>
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
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.
|
||||
|
||||
### Data Analysis & Notebooks
|
||||
|
||||
Create interactive data analysis environments with Python, pandas, and visualization libraries. Build notebook-like experiences at the edge.
|
||||
|
||||
### Interactive Development Environments
|
||||
|
||||
Build cloud IDEs, coding playgrounds, and collaborative development tools with full Linux environments and preview URLs.
|
||||
|
||||
### CI/CD & Build Systems
|
||||
|
||||
Run tests, compile code, and execute build pipelines in isolated environments with parallel execution and streaming logs.
|
||||
|
||||
---
|
||||
|
||||
## Related products
|
||||
|
||||
<RelatedProduct header="Containers" href="/containers/" product="containers">
|
||||
|
||||
Serverless container runtime that powers Sandbox, enabling you to run any containerized workload on the edge.
|
||||
|
||||
</RelatedProduct>
|
||||
|
||||
<RelatedProduct header="Workers AI" href="/workers-ai/" product="workers-ai">
|
||||
|
||||
Run machine learning models and LLMs on the network. Combine with Sandbox for secure AI code execution workflows.
|
||||
|
||||
</RelatedProduct>
|
||||
|
||||
<RelatedProduct header="Durable Objects" href="/durable-objects/" product="durable-objects">
|
||||
|
||||
Stateful coordination layer that enables Sandbox to maintain persistent environments with strong consistency.
|
||||
|
||||
</RelatedProduct>
|
||||
|
||||
---
|
||||
|
||||
## More resources
|
||||
|
||||
<CardGrid>
|
||||
|
||||
<LinkTitleCard title="Examples" href="/sandbox/examples/" icon="pen">
|
||||
Explore complete examples including AI code execution, data analysis, and
|
||||
interactive environments.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Beta Information"
|
||||
href="/sandbox/platform/beta-info/"
|
||||
icon="seti:shell"
|
||||
>
|
||||
Learn about the Sandbox Beta, current status, and upcoming features.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard title="Limits" href="/sandbox/platform/limits/" icon="document">
|
||||
Understand Sandbox resource limits, quotas, and best practices for working
|
||||
within them.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Best Practices"
|
||||
href="/sandbox/best-practices/"
|
||||
icon="seti:lock"
|
||||
>
|
||||
Learn security best practices, performance optimization, and production
|
||||
deployment patterns.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="GitHub Repository"
|
||||
href="https://github.com/cloudflare/sandbox-sdk"
|
||||
icon="github"
|
||||
>
|
||||
View the SDK source code, report issues, and contribute to the project.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Developer Discord"
|
||||
href="https://discord.cloudflare.com"
|
||||
icon="discord"
|
||||
>
|
||||
Connect with the Workers community on Discord. Ask questions, share what
|
||||
you're building, and get help from other developers.
|
||||
</LinkTitleCard>
|
||||
|
||||
</CardGrid>
|
||||
223
src/content/docs/sandbox/tutorials/ai-code-executor.mdx
Normal file
223
src/content/docs/sandbox/tutorials/ai-code-executor.mdx
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
---
|
||||
title: Build an AI code executor
|
||||
pcx_content_type: tutorial
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
import { Render, PackageManagers } from "~/components";
|
||||
|
||||
Build an AI-powered code execution system using Sandbox SDK and Claude. Turn natural language questions into Python code, execute it securely, and return results.
|
||||
|
||||
**Time to complete:** 20 minutes
|
||||
|
||||
## What you'll build
|
||||
|
||||
An API that accepts questions like "What's the 100th Fibonacci number?", uses Claude to generate Python code, executes it in an isolated sandbox, and returns the results.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Render file="prereqs" product="workers" />
|
||||
|
||||
You'll also need:
|
||||
- An [Anthropic API key](https://console.anthropic.com/) for Claude
|
||||
- [Docker](https://www.docker.com/) running locally
|
||||
|
||||
## 1. Create your project
|
||||
|
||||
Create a new Sandbox SDK project:
|
||||
|
||||
<PackageManagers
|
||||
type="create"
|
||||
pkg="cloudflare@latest"
|
||||
args={"ai-code-executor --template=cloudflare/sandbox-sdk/examples/minimal"}
|
||||
/>
|
||||
|
||||
```sh
|
||||
cd ai-code-executor
|
||||
```
|
||||
|
||||
## 2. Install dependencies
|
||||
|
||||
Install the Anthropic SDK:
|
||||
|
||||
<PackageManagers pkg="@anthropic-ai/sdk" />
|
||||
|
||||
## 3. Build your code executor
|
||||
|
||||
Replace the contents of `src/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { getSandbox, type Sandbox } from '@cloudflare/sandbox';
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
export { Sandbox } from '@cloudflare/sandbox';
|
||||
|
||||
interface Env {
|
||||
Sandbox: DurableObjectNamespace<Sandbox>;
|
||||
ANTHROPIC_API_KEY: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
if (request.method !== 'POST' || new URL(request.url).pathname !== '/execute') {
|
||||
return new Response('POST /execute with { "question": "your question" }');
|
||||
}
|
||||
|
||||
try {
|
||||
const { question } = await request.json();
|
||||
|
||||
if (!question) {
|
||||
return Response.json({ error: 'Question is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Use Claude to generate Python code
|
||||
const anthropic = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });
|
||||
const codeGeneration = await anthropic.messages.create({
|
||||
model: 'claude-sonnet-4-5',
|
||||
max_tokens: 1024,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: `Generate Python code to answer: "${question}"
|
||||
|
||||
Requirements:
|
||||
- Use only Python standard library
|
||||
- Print the result using print()
|
||||
- Keep code simple and safe
|
||||
|
||||
Return ONLY the code, no explanations.`
|
||||
}],
|
||||
});
|
||||
|
||||
const generatedCode = codeGeneration.content[0]?.type === 'text'
|
||||
? codeGeneration.content[0].text
|
||||
: '';
|
||||
|
||||
if (!generatedCode) {
|
||||
return Response.json({ error: 'Failed to generate code' }, { status: 500 });
|
||||
}
|
||||
|
||||
// Execute the code in a sandbox
|
||||
const sandbox = getSandbox(env.Sandbox, 'demo-user');
|
||||
await sandbox.writeFile('/tmp/code.py', generatedCode);
|
||||
const result = await sandbox.exec('python /tmp/code.py');
|
||||
|
||||
return Response.json({
|
||||
success: result.success,
|
||||
question,
|
||||
code: generatedCode,
|
||||
output: result.stdout,
|
||||
error: result.stderr
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
return Response.json(
|
||||
{ error: 'Internal server error', message: error.message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
1. Receives a question via POST to `/execute`
|
||||
2. Uses Claude to generate Python code
|
||||
3. Writes code to `/tmp/code.py` in the sandbox
|
||||
4. Executes with `sandbox.exec('python /tmp/code.py')`
|
||||
5. Returns both the code and execution results
|
||||
|
||||
## 4. Set your Anthropic API key
|
||||
|
||||
Store your Anthropic API key as a secret:
|
||||
|
||||
```sh
|
||||
npx wrangler secret put ANTHROPIC_API_KEY
|
||||
```
|
||||
|
||||
Paste your API key from the [Anthropic Console](https://console.anthropic.com/) when prompted.
|
||||
|
||||
## 5. Test locally
|
||||
|
||||
Start the development server:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
:::note
|
||||
First run builds the Docker container (2-3 minutes). Subsequent runs are much faster.
|
||||
:::
|
||||
|
||||
Test with curl:
|
||||
|
||||
```sh
|
||||
curl -X POST http://localhost:8787/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"question": "What is the 10th Fibonacci number?"}'
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"question": "What is the 10th Fibonacci number?",
|
||||
"code": "def fibonacci(n):\n if n <= 1:\n return n\n return fibonacci(n-1) + fibonacci(n-2)\n\nprint(fibonacci(10))",
|
||||
"output": "55\n",
|
||||
"error": ""
|
||||
}
|
||||
```
|
||||
|
||||
## 6. Deploy
|
||||
|
||||
Deploy your Worker:
|
||||
|
||||
```sh
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
:::caution
|
||||
After first deployment, wait 2-3 minutes for container provisioning. Check status with `npx wrangler containers list`.
|
||||
:::
|
||||
|
||||
## 7. Test your deployment
|
||||
|
||||
Try different questions:
|
||||
|
||||
```sh
|
||||
# Factorial
|
||||
curl -X POST https://ai-code-executor.YOUR_SUBDOMAIN.workers.dev/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"question": "Calculate the factorial of 5"}'
|
||||
|
||||
# Statistics
|
||||
curl -X POST https://ai-code-executor.YOUR_SUBDOMAIN.workers.dev/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"question": "What is the mean of [10, 20, 30, 40, 50]?"}'
|
||||
|
||||
# String manipulation
|
||||
curl -X POST https://ai-code-executor.YOUR_SUBDOMAIN.workers.dev/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"question": "Reverse the string \"Hello World\""}'
|
||||
```
|
||||
|
||||
## What you built
|
||||
|
||||
You created an AI code execution system that:
|
||||
- Accepts natural language questions
|
||||
- Generates Python code with Claude
|
||||
- Executes code securely in isolated sandboxes
|
||||
- Returns results with error handling
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Analyze data with AI](/sandbox/tutorials/analyze-data-with-ai/) - Add pandas and matplotlib for data analysis
|
||||
- [Code Interpreter API](/sandbox/api/interpreter/) - Use the built-in code interpreter instead of exec
|
||||
- [Streaming output](/sandbox/guides/streaming-output/) - Show real-time execution progress
|
||||
- [API reference](/sandbox/api/) - Explore all available methods
|
||||
|
||||
## Related resources
|
||||
|
||||
- [Anthropic Claude documentation](https://docs.anthropic.com/)
|
||||
- [Workers AI](/workers-ai/) - Use Cloudflare's built-in models
|
||||
237
src/content/docs/sandbox/tutorials/analyze-data-with-ai.mdx
Normal file
237
src/content/docs/sandbox/tutorials/analyze-data-with-ai.mdx
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
---
|
||||
title: Analyze data with AI
|
||||
pcx_content_type: tutorial
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
import { Render, PackageManagers } from "~/components";
|
||||
|
||||
Build an AI-powered data analysis system that accepts CSV uploads, uses Claude to generate Python analysis code, executes it in sandboxes, and returns visualizations.
|
||||
|
||||
**Time to complete**: 25 minutes
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Render file="prereqs" product="workers" />
|
||||
|
||||
You'll also need:
|
||||
- An [Anthropic API key](https://console.anthropic.com/) for Claude
|
||||
- [Docker](https://www.docker.com/) running locally
|
||||
|
||||
## 1. Create your project
|
||||
|
||||
Create a new Sandbox SDK project:
|
||||
|
||||
<PackageManagers
|
||||
type="create"
|
||||
pkg="cloudflare@latest"
|
||||
args={"analyze-data --template=cloudflare/sandbox-sdk/examples/minimal"}
|
||||
/>
|
||||
|
||||
```sh
|
||||
cd analyze-data
|
||||
```
|
||||
|
||||
## 2. Install dependencies
|
||||
|
||||
<PackageManagers pkg="@anthropic-ai/sdk" />
|
||||
|
||||
## 3. Build the analysis handler
|
||||
|
||||
Replace `src/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { getSandbox, proxyToSandbox, type Sandbox } from '@cloudflare/sandbox';
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
export { Sandbox } from '@cloudflare/sandbox';
|
||||
|
||||
interface Env {
|
||||
Sandbox: DurableObjectNamespace<Sandbox>;
|
||||
ANTHROPIC_API_KEY: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const proxyResponse = await proxyToSandbox(request, env);
|
||||
if (proxyResponse) return proxyResponse;
|
||||
|
||||
if (request.method !== 'POST') {
|
||||
return Response.json({ error: 'POST CSV file and question' }, { status: 405 });
|
||||
}
|
||||
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
const csvFile = formData.get('file') as File;
|
||||
const question = formData.get('question') as string;
|
||||
|
||||
if (!csvFile || !question) {
|
||||
return Response.json({ error: 'Missing file or question' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Upload CSV to sandbox
|
||||
const sandbox = getSandbox(env.Sandbox, `analysis-${Date.now()}`);
|
||||
const csvPath = '/workspace/data.csv';
|
||||
await sandbox.writeFile(csvPath, new Uint8Array(await csvFile.arrayBuffer()));
|
||||
|
||||
// Analyze CSV structure
|
||||
const structure = await sandbox.exec(
|
||||
`python -c "import pandas as pd; df = pd.read_csv('${csvPath}'); print(f'Rows: {len(df)}'); print(f'Columns: {list(df.columns)[:5]}')"`
|
||||
);
|
||||
|
||||
if (!structure.success) {
|
||||
return Response.json({ error: 'Failed to read CSV', details: structure.stderr }, { status: 400 });
|
||||
}
|
||||
|
||||
// Generate analysis code with Claude
|
||||
const code = await generateAnalysisCode(env.ANTHROPIC_API_KEY, csvPath, question, structure.stdout);
|
||||
|
||||
// Write and execute the analysis code
|
||||
await sandbox.writeFile('/workspace/analyze.py', code);
|
||||
const result = await sandbox.exec('python /workspace/analyze.py');
|
||||
|
||||
if (!result.success) {
|
||||
return Response.json({ error: 'Analysis failed', details: result.stderr }, { status: 500 });
|
||||
}
|
||||
|
||||
// Check for generated chart
|
||||
let chart = null;
|
||||
try {
|
||||
const chartFile = await sandbox.readFile('/workspace/chart.png');
|
||||
const buffer = new Uint8Array(chartFile.content);
|
||||
chart = `data:image/png;base64,${btoa(String.fromCharCode(...buffer))}`;
|
||||
} catch {
|
||||
// No chart generated
|
||||
}
|
||||
|
||||
await sandbox.destroy();
|
||||
|
||||
return Response.json({
|
||||
success: true,
|
||||
output: result.stdout,
|
||||
chart,
|
||||
code
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
async function generateAnalysisCode(
|
||||
apiKey: string,
|
||||
csvPath: string,
|
||||
question: string,
|
||||
csvStructure: string
|
||||
): Promise<string> {
|
||||
const anthropic = new Anthropic({ apiKey });
|
||||
|
||||
const response = await anthropic.messages.create({
|
||||
model: 'claude-sonnet-4-5',
|
||||
max_tokens: 2048,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: `CSV at ${csvPath}:
|
||||
${csvStructure}
|
||||
|
||||
Question: "${question}"
|
||||
|
||||
Generate Python code that:
|
||||
- Reads CSV with pandas
|
||||
- Answers the question
|
||||
- Saves charts to /workspace/chart.png if helpful
|
||||
- Prints findings to stdout
|
||||
|
||||
Use pandas, numpy, matplotlib.`
|
||||
}],
|
||||
tools: [{
|
||||
name: 'generate_python_code',
|
||||
description: 'Generate Python code for data analysis',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
code: { type: 'string', description: 'Complete Python code' }
|
||||
},
|
||||
required: ['code']
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
for (const block of response.content) {
|
||||
if (block.type === 'tool_use' && block.name === 'generate_python_code') {
|
||||
return (block.input as { code: string }).code;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Failed to generate code');
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Set your API key
|
||||
|
||||
```sh
|
||||
npx wrangler secret put ANTHROPIC_API_KEY
|
||||
```
|
||||
|
||||
## 5. Test locally
|
||||
|
||||
Download a sample CSV:
|
||||
|
||||
```sh
|
||||
# Create a test CSV
|
||||
echo "year,rating,title
|
||||
2020,8.5,Movie A
|
||||
2021,7.2,Movie B
|
||||
2022,9.1,Movie C" > test.csv
|
||||
```
|
||||
|
||||
Start the dev server:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Test with curl:
|
||||
|
||||
```sh
|
||||
curl -X POST http://localhost:8787 \
|
||||
-F "file=@test.csv" \
|
||||
-F "question=What is the average rating by year?"
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"output": "Average ratings by year:\n2020: 8.5\n2021: 7.2\n2022: 9.1",
|
||||
"chart": "data:image/png;base64,...",
|
||||
"code": "import pandas as pd\nimport matplotlib.pyplot as plt\n..."
|
||||
}
|
||||
```
|
||||
|
||||
## 6. Deploy
|
||||
|
||||
```sh
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
:::caution
|
||||
Wait 2-3 minutes after first deployment for container provisioning.
|
||||
:::
|
||||
|
||||
## What you built
|
||||
|
||||
An AI data analysis system that:
|
||||
- Uploads CSV files to sandboxes
|
||||
- Uses Claude's tool calling to generate analysis code
|
||||
- Executes Python with pandas and matplotlib
|
||||
- Returns text output and visualizations
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Code Interpreter API](/sandbox/api/interpreter/) - Use the built-in code interpreter
|
||||
- [File operations](/sandbox/guides/manage-files/) - Advanced file handling
|
||||
- [Streaming output](/sandbox/guides/streaming-output/) - Real-time progress updates
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
---
|
||||
title: Automated testing pipeline
|
||||
pcx_content_type: tutorial
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
import { Render, PackageManagers } from "~/components";
|
||||
|
||||
Build a testing pipeline that clones Git repositories, installs dependencies, runs tests, and reports results.
|
||||
|
||||
**Time to complete**: 25 minutes
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Render file="prereqs" product="workers" />
|
||||
|
||||
You'll also need a GitHub repository with tests (public or private with access token).
|
||||
|
||||
## 1. Create your project
|
||||
|
||||
<PackageManagers
|
||||
type="create"
|
||||
pkg="cloudflare@latest"
|
||||
args={"test-pipeline --template=cloudflare/sandbox-sdk/examples/minimal"}
|
||||
/>
|
||||
|
||||
```sh
|
||||
cd test-pipeline
|
||||
```
|
||||
|
||||
## 2. Build the pipeline
|
||||
|
||||
Replace `src/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { getSandbox, proxyToSandbox, type Sandbox } from '@cloudflare/sandbox';
|
||||
|
||||
export { Sandbox } from '@cloudflare/sandbox';
|
||||
|
||||
interface Env {
|
||||
Sandbox: DurableObjectNamespace<Sandbox>;
|
||||
GITHUB_TOKEN?: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const proxyResponse = await proxyToSandbox(request, env);
|
||||
if (proxyResponse) return proxyResponse;
|
||||
|
||||
if (request.method !== 'POST') {
|
||||
return new Response('POST { "repoUrl": "https://github.com/owner/repo", "branch": "main" }');
|
||||
}
|
||||
|
||||
try {
|
||||
const { repoUrl, branch = 'main' } = await request.json();
|
||||
|
||||
if (!repoUrl) {
|
||||
return Response.json({ error: 'repoUrl required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, `test-${Date.now()}`);
|
||||
|
||||
try {
|
||||
// Clone repository
|
||||
let cloneUrl = repoUrl;
|
||||
if (env.GITHUB_TOKEN && repoUrl.includes('github.com')) {
|
||||
cloneUrl = repoUrl.replace('https://', `https://${env.GITHUB_TOKEN}@`);
|
||||
}
|
||||
|
||||
await sandbox.exec(`git clone --depth=1 --branch=${branch} ${cloneUrl} /workspace/repo`);
|
||||
|
||||
// Detect project type
|
||||
const projectType = await detectProjectType(sandbox);
|
||||
|
||||
// Install dependencies
|
||||
const installCmd = getInstallCommand(projectType);
|
||||
if (installCmd) {
|
||||
const installResult = await sandbox.exec(`cd /workspace/repo && ${installCmd}`);
|
||||
if (!installResult.success) {
|
||||
return Response.json({
|
||||
success: false,
|
||||
error: 'Install failed',
|
||||
output: installResult.stderr
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Run tests
|
||||
const testCmd = getTestCommand(projectType);
|
||||
const testResult = await sandbox.exec(`cd /workspace/repo && ${testCmd}`);
|
||||
|
||||
return Response.json({
|
||||
success: testResult.exitCode === 0,
|
||||
exitCode: testResult.exitCode,
|
||||
output: testResult.stdout,
|
||||
errors: testResult.stderr,
|
||||
projectType
|
||||
});
|
||||
|
||||
} finally {
|
||||
await sandbox.destroy();
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
async function detectProjectType(sandbox: any): Promise<string> {
|
||||
try {
|
||||
await sandbox.readFile('/workspace/repo/package.json');
|
||||
return 'nodejs';
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
await sandbox.readFile('/workspace/repo/requirements.txt');
|
||||
return 'python';
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
await sandbox.readFile('/workspace/repo/go.mod');
|
||||
return 'go';
|
||||
} catch {}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
function getInstallCommand(projectType: string): string {
|
||||
switch (projectType) {
|
||||
case 'nodejs': return 'npm install';
|
||||
case 'python': return 'pip install -r requirements.txt || pip install -e .';
|
||||
case 'go': return 'go mod download';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
function getTestCommand(projectType: string): string {
|
||||
switch (projectType) {
|
||||
case 'nodejs': return 'npm test';
|
||||
case 'python': return 'python -m pytest || python -m unittest discover';
|
||||
case 'go': return 'go test ./...';
|
||||
default: return 'echo "Unknown project type"';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Test locally
|
||||
|
||||
Start the dev server:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Test with a repository:
|
||||
|
||||
```sh
|
||||
curl -X POST http://localhost:8787 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"repoUrl": "https://github.com/sindresorhus/is-promise",
|
||||
"branch": "main"
|
||||
}'
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"exitCode": 0,
|
||||
"output": "...test output...",
|
||||
"projectType": "nodejs"
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Deploy
|
||||
|
||||
```sh
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
For private repositories, set your GitHub token:
|
||||
|
||||
```sh
|
||||
npx wrangler secret put GITHUB_TOKEN
|
||||
```
|
||||
|
||||
## What you built
|
||||
|
||||
An automated testing pipeline that:
|
||||
- Clones Git repositories
|
||||
- Detects project type (Node.js, Python, Go)
|
||||
- Installs dependencies automatically
|
||||
- Runs tests and reports results
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Streaming output](/sandbox/guides/streaming-output/) - Add real-time test output
|
||||
- [Background processes](/sandbox/guides/background-processes/) - Handle long-running tests
|
||||
- [Sessions API](/sandbox/api/sessions/) - Cache dependencies between runs
|
||||
242
src/content/docs/sandbox/tutorials/code-review-bot.mdx
Normal file
242
src/content/docs/sandbox/tutorials/code-review-bot.mdx
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
---
|
||||
title: Build a code review bot
|
||||
pcx_content_type: tutorial
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
import { Render, PackageManagers } from "~/components";
|
||||
|
||||
Build a GitHub bot that responds to pull requests, clones the repository in a sandbox, uses Claude to analyze code changes, and posts review comments.
|
||||
|
||||
**Time to complete**: 30 minutes
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Render file="prereqs" product="workers" />
|
||||
|
||||
You'll also need:
|
||||
- A [GitHub account](https://github.com/) and personal access token with repo permissions
|
||||
- An [Anthropic API key](https://console.anthropic.com/) for Claude
|
||||
- A GitHub repository for testing
|
||||
|
||||
## 1. Create your project
|
||||
|
||||
<PackageManagers
|
||||
type="create"
|
||||
pkg="cloudflare@latest"
|
||||
args={"code-review-bot --template=cloudflare/sandbox-sdk/examples/minimal"}
|
||||
/>
|
||||
|
||||
```sh
|
||||
cd code-review-bot
|
||||
```
|
||||
|
||||
## 2. Install dependencies
|
||||
|
||||
<PackageManagers pkg="@anthropic-ai/sdk @octokit/rest" />
|
||||
|
||||
## 3. Build the webhook handler
|
||||
|
||||
Replace `src/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { getSandbox, proxyToSandbox, type Sandbox } from '@cloudflare/sandbox';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
export { Sandbox } from '@cloudflare/sandbox';
|
||||
|
||||
interface Env {
|
||||
Sandbox: DurableObjectNamespace<Sandbox>;
|
||||
GITHUB_TOKEN: string;
|
||||
ANTHROPIC_API_KEY: string;
|
||||
WEBHOOK_SECRET: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const proxyResponse = await proxyToSandbox(request, env);
|
||||
if (proxyResponse) return proxyResponse;
|
||||
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === '/webhook' && request.method === 'POST') {
|
||||
const signature = request.headers.get('x-hub-signature-256');
|
||||
const body = await request.text();
|
||||
|
||||
// Verify webhook signature
|
||||
if (!signature || !(await verifySignature(body, signature, env.WEBHOOK_SECRET))) {
|
||||
return Response.json({ error: 'Invalid signature' }, { status: 401 });
|
||||
}
|
||||
|
||||
const event = request.headers.get('x-github-event');
|
||||
const payload = JSON.parse(body);
|
||||
|
||||
// Only handle opened PRs
|
||||
if (event === 'pull_request' && payload.action === 'opened') {
|
||||
reviewPullRequest(payload, env).catch(console.error);
|
||||
return Response.json({ message: 'Review started' });
|
||||
}
|
||||
|
||||
return Response.json({ message: 'Event ignored' });
|
||||
}
|
||||
|
||||
return new Response('Code Review Bot\n\nConfigure GitHub webhook to POST /webhook');
|
||||
},
|
||||
};
|
||||
|
||||
async function verifySignature(payload: string, signature: string, secret: string): Promise<boolean> {
|
||||
const encoder = new TextEncoder();
|
||||
const key = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
encoder.encode(secret),
|
||||
{ name: 'HMAC', hash: 'SHA-256' },
|
||||
false,
|
||||
['sign']
|
||||
);
|
||||
|
||||
const signatureBytes = await crypto.subtle.sign('HMAC', key, encoder.encode(payload));
|
||||
const expected = 'sha256=' + Array.from(new Uint8Array(signatureBytes))
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
|
||||
return signature === expected;
|
||||
}
|
||||
|
||||
async function reviewPullRequest(payload: any, env: Env): Promise<void> {
|
||||
const pr = payload.pull_request;
|
||||
const repo = payload.repository;
|
||||
const octokit = new Octokit({ auth: env.GITHUB_TOKEN });
|
||||
|
||||
// Post initial comment
|
||||
await octokit.issues.createComment({
|
||||
owner: repo.owner.login,
|
||||
repo: repo.name,
|
||||
issue_number: pr.number,
|
||||
body: 'Code review in progress...'
|
||||
});
|
||||
|
||||
const sandbox = getSandbox(env.Sandbox, `review-${pr.number}`);
|
||||
|
||||
try {
|
||||
// Clone repository
|
||||
const cloneUrl = `https://${env.GITHUB_TOKEN}@github.com/${repo.owner.login}/${repo.name}.git`;
|
||||
await sandbox.exec(`git clone --depth=1 --branch=${pr.head.ref} ${cloneUrl} /workspace/repo`);
|
||||
|
||||
// Get changed files
|
||||
const comparison = await octokit.repos.compareCommits({
|
||||
owner: repo.owner.login,
|
||||
repo: repo.name,
|
||||
base: pr.base.sha,
|
||||
head: pr.head.sha
|
||||
});
|
||||
|
||||
const files = [];
|
||||
for (const file of (comparison.data.files || []).slice(0, 5)) {
|
||||
if (file.status !== 'removed') {
|
||||
const content = await sandbox.readFile(`/workspace/repo/${file.filename}`);
|
||||
files.push({
|
||||
path: file.filename,
|
||||
patch: file.patch || '',
|
||||
content: content.content
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Generate review with Claude
|
||||
const anthropic = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });
|
||||
const response = await anthropic.messages.create({
|
||||
model: 'claude-sonnet-4-5',
|
||||
max_tokens: 2048,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: `Review this PR:
|
||||
|
||||
Title: ${pr.title}
|
||||
|
||||
Changed files:
|
||||
${files.map(f => `File: ${f.path}\nDiff:\n${f.patch}\n\nContent:\n${f.content.substring(0, 1000)}`).join('\n\n')}
|
||||
|
||||
Provide a brief code review focusing on bugs, security, and best practices.`
|
||||
}]
|
||||
});
|
||||
|
||||
const review = response.content[0]?.type === 'text' ? response.content[0].text : 'No review generated';
|
||||
|
||||
// Post review comment
|
||||
await octokit.issues.createComment({
|
||||
owner: repo.owner.login,
|
||||
repo: repo.name,
|
||||
issue_number: pr.number,
|
||||
body: `## Code Review\n\n${review}\n\n---\n*Generated by Claude*`
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
await octokit.issues.createComment({
|
||||
owner: repo.owner.login,
|
||||
repo: repo.name,
|
||||
issue_number: pr.number,
|
||||
body: `Review failed: ${error.message}`
|
||||
});
|
||||
} finally {
|
||||
await sandbox.destroy();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Set your secrets
|
||||
|
||||
```sh
|
||||
# GitHub token (needs repo permissions)
|
||||
npx wrangler secret put GITHUB_TOKEN
|
||||
|
||||
# Anthropic API key
|
||||
npx wrangler secret put ANTHROPIC_API_KEY
|
||||
|
||||
# Webhook secret (generate a random string)
|
||||
npx wrangler secret put WEBHOOK_SECRET
|
||||
```
|
||||
|
||||
## 5. Deploy
|
||||
|
||||
```sh
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
## 6. Configure GitHub webhook
|
||||
|
||||
1. Go to your repository **Settings** > **Webhooks** > **Add webhook**
|
||||
2. Set **Payload URL**: `https://code-review-bot.YOUR_SUBDOMAIN.workers.dev/webhook`
|
||||
3. Set **Content type**: `application/json`
|
||||
4. Set **Secret**: Same value you used for `WEBHOOK_SECRET`
|
||||
5. Select **Let me select individual events** → Check **Pull requests**
|
||||
6. Click **Add webhook**
|
||||
|
||||
## 7. Test with a pull request
|
||||
|
||||
Create a test PR:
|
||||
|
||||
```sh
|
||||
git checkout -b test-review
|
||||
echo "console.log('test');" > test.js
|
||||
git add test.js
|
||||
git commit -m "Add test file"
|
||||
git push origin test-review
|
||||
```
|
||||
|
||||
Open the PR on GitHub and watch for the bot's review comment!
|
||||
|
||||
## What you built
|
||||
|
||||
A GitHub code review bot that:
|
||||
- Receives webhook events from GitHub
|
||||
- Clones repositories in isolated sandboxes
|
||||
- Uses Claude to analyze code changes
|
||||
- Posts review comments automatically
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Git operations](/sandbox/api/files/#gitcheckout) - Advanced repository handling
|
||||
- [Sessions API](/sandbox/api/sessions/) - Manage long-running sandbox operations
|
||||
- [GitHub Apps](https://docs.github.com/en/apps) - Build a proper GitHub App
|
||||
71
src/content/docs/sandbox/tutorials/index.mdx
Normal file
71
src/content/docs/sandbox/tutorials/index.mdx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
title: Tutorials
|
||||
pcx_content_type: navigation
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
import { LinkTitleCard, CardGrid } from "~/components";
|
||||
|
||||
Learn how to build applications with Sandbox SDK through step-by-step tutorials. Each tutorial takes 20-30 minutes.
|
||||
|
||||
<CardGrid>
|
||||
|
||||
<LinkTitleCard
|
||||
title="AI code executor"
|
||||
href="/sandbox/tutorials/ai-code-executor/"
|
||||
icon="pen"
|
||||
>
|
||||
Use Claude to generate Python code from natural language and execute it securely in sandboxes.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Analyze data with AI"
|
||||
href="/sandbox/tutorials/analyze-data-with-ai/"
|
||||
icon="document"
|
||||
>
|
||||
Upload CSV files, generate analysis code with Claude, and return visualizations.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Code review bot"
|
||||
href="/sandbox/tutorials/code-review-bot/"
|
||||
icon="star"
|
||||
>
|
||||
Clone repositories, analyze code with Claude, and post review comments to GitHub PRs.
|
||||
</LinkTitleCard>
|
||||
|
||||
<LinkTitleCard
|
||||
title="Automated testing pipeline"
|
||||
href="/sandbox/tutorials/automated-testing-pipeline/"
|
||||
icon="checkmark"
|
||||
>
|
||||
Clone repositories, install dependencies, run tests, and report results.
|
||||
</LinkTitleCard>
|
||||
|
||||
</CardGrid>
|
||||
|
||||
## What you'll learn
|
||||
|
||||
These tutorials cover real-world applications:
|
||||
|
||||
- **AI Code Execution** - Integrate Claude with secure code execution
|
||||
- **Data Analysis** - Generate and run analysis code on uploaded datasets
|
||||
- **Code Review Automation** - Clone repositories and analyze code changes
|
||||
- **CI/CD Pipelines** - Automated testing workflows
|
||||
- **File Operations** - Work with files and directories
|
||||
- **Error Handling** - Validation and error management
|
||||
- **Deployment** - Deploy Workers with containers
|
||||
|
||||
## Before you start
|
||||
|
||||
All tutorials assume you have:
|
||||
|
||||
- Completed the [Get Started guide](/sandbox/get-started/)
|
||||
- Basic familiarity with [Workers](/workers/)
|
||||
- [Docker](https://www.docker.com/) installed and running
|
||||
|
||||
## Related resources
|
||||
|
||||
- [How-to guides](/sandbox/guides/) - Solve specific problems
|
||||
- [API reference](/sandbox/api/) - Complete SDK reference
|
||||
13
src/content/products/sandbox.yaml
Normal file
13
src/content/products/sandbox.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
name: Sandbox SDK
|
||||
|
||||
product:
|
||||
title: Sandbox SDK
|
||||
url: /sandbox/
|
||||
group: Developer platform
|
||||
additional_groups: [AI]
|
||||
tags: [AI]
|
||||
|
||||
meta:
|
||||
title: Cloudflare Sandbox SDK docs
|
||||
description: Build secure, isolated code execution environments
|
||||
author: "@cloudflare"
|
||||
1
src/icons/sandbox.svg
Normal file
1
src/icons/sandbox.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" stroke="currentColor" stroke-width="0" viewBox="0 0 512 512"><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M448 341.37V170.61A32 32 0 0 0 432.11 143l-152-88.46a47.94 47.94 0 0 0-48.24 0L79.89 143A32 32 0 0 0 64 170.61v170.76A32 32 0 0 0 79.89 369l152 88.46a48 48 0 0 0 48.24 0l152-88.46A32 32 0 0 0 448 341.37"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="m69 153.99 187 110 187-110m-187 310v-200"/></svg>
|
||||
|
After Width: | Height: | Size: 552 B |
Loading…
Add table
Reference in a new issue