@cloudflare/sandbox 0.0.0-1a42464 โ 0.0.0-1be7d53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +153 -0
- package/Dockerfile +43 -19
- package/README.md +356 -19
- package/container_src/bun.lock +76 -0
- package/container_src/circuit-breaker.ts +121 -0
- package/container_src/control-process.ts +784 -0
- package/container_src/handler/exec.ts +99 -251
- package/container_src/handler/file.ts +253 -640
- package/container_src/handler/git.ts +28 -80
- package/container_src/handler/process.ts +443 -515
- package/container_src/handler/session.ts +92 -0
- package/container_src/index.ts +363 -123
- package/container_src/interpreter-service.ts +276 -0
- package/container_src/isolation.ts +1213 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/runtime/executors/javascript/node_executor.ts +123 -0
- package/container_src/runtime/executors/python/ipython_executor.py +338 -0
- package/container_src/runtime/executors/typescript/ts_executor.ts +138 -0
- package/container_src/runtime/process-pool.ts +464 -0
- package/container_src/shell-escape.ts +42 -0
- package/container_src/startup.sh +11 -0
- package/container_src/types.ts +42 -14
- package/package.json +2 -2
- package/src/client.ts +244 -234
- package/src/errors.ts +219 -0
- package/src/file-stream.ts +162 -0
- package/src/index.ts +76 -15
- package/src/interpreter-client.ts +352 -0
- package/src/interpreter-types.ts +390 -0
- package/src/interpreter.ts +150 -0
- package/src/sandbox.ts +511 -401
- package/src/types.ts +209 -24
package/README.md
CHANGED
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
- [Basic Setup](#basic-setup)
|
|
18
18
|
- [๐ API Reference](#api-reference)
|
|
19
19
|
- [Core Methods](#core-methods)
|
|
20
|
+
- [๐งช Code Interpreter](#code-interpreter)
|
|
21
|
+
- [Code Execution](#code-execution)
|
|
22
|
+
- [Rich Outputs](#rich-outputs)
|
|
23
|
+
- [Output Formats](#output-formats)
|
|
20
24
|
- [๐ Port Forwarding](#port-forwarding)
|
|
21
25
|
- [Utility Methods](#utility-methods)
|
|
22
26
|
- [๐ก Examples](#examples)
|
|
@@ -24,6 +28,7 @@
|
|
|
24
28
|
- [Build and Test Code](#build-and-test-code)
|
|
25
29
|
- [Interactive Development Environment](#interactive-development-environment)
|
|
26
30
|
- [Expose Services with Preview URLs](#expose-services-with-preview-urls)
|
|
31
|
+
- [Data Analysis with Code Interpreter](#data-analysis-with-code-interpreter)
|
|
27
32
|
- [๐๏ธ Architecture](#architecture)
|
|
28
33
|
- [๐ ๏ธ Advanced Usage](#advanced-usage)
|
|
29
34
|
- [AsyncIterable Streaming Support](#asynciterable-streaming-support)
|
|
@@ -45,11 +50,15 @@ The Cloudflare Sandbox SDK enables you to run isolated code environments directl
|
|
|
45
50
|
- **๐ Secure Isolation**: Each sandbox runs in its own container with full process isolation
|
|
46
51
|
- **โก Edge-Native**: Runs on Cloudflare's global network for low latency worldwide
|
|
47
52
|
- **๐ File System Access**: Read, write, and manage files within the sandbox
|
|
53
|
+
- **๐ผ๏ธ Binary File Support**: Automatic MIME type detection and base64 encoding for images, PDFs, and other binary files
|
|
48
54
|
- **๐ง Command Execution**: Run any command or process inside the container
|
|
49
55
|
- **๐ Preview URLs**: Expose services running in your sandbox via public URLs
|
|
50
56
|
- **๐ Git Integration**: Clone repositories directly into sandboxes
|
|
51
|
-
- **๐ Streaming Support**: Real-time output streaming for long-running commands
|
|
57
|
+
- **๐ Streaming Support**: Real-time output streaming for long-running commands and file transfers
|
|
52
58
|
- **๐ฎ Session Management**: Maintain state across multiple operations
|
|
59
|
+
- **๐งช Code Interpreter**: Execute Python and JavaScript with rich outputs (charts, tables, formatted data)
|
|
60
|
+
- **๐ Multi-Language Support**: Persistent execution contexts for Python and JavaScript/TypeScript
|
|
61
|
+
- **๐จ Rich MIME Types**: Automatic processing of images, HTML, charts, and structured data
|
|
53
62
|
|
|
54
63
|
<h2 id="quick-start">๐ Quick Start</h2>
|
|
55
64
|
|
|
@@ -64,12 +73,10 @@ npm install @cloudflare/sandbox
|
|
|
64
73
|
1. **Create a Dockerfile** (temporary requirement, will be removed in future releases):
|
|
65
74
|
|
|
66
75
|
```dockerfile
|
|
67
|
-
FROM docker.io/cloudflare/sandbox:0.
|
|
76
|
+
FROM docker.io/cloudflare/sandbox:0.3.6
|
|
68
77
|
|
|
78
|
+
# Expose the ports you want to expose
|
|
69
79
|
EXPOSE 3000
|
|
70
|
-
|
|
71
|
-
# Run the same command as the original image
|
|
72
|
-
CMD ["bun", "index.ts"]
|
|
73
80
|
```
|
|
74
81
|
|
|
75
82
|
2. **Configure wrangler.json**:
|
|
@@ -83,7 +90,7 @@ CMD ["bun", "index.ts"]
|
|
|
83
90
|
{
|
|
84
91
|
"class_name": "Sandbox",
|
|
85
92
|
"image": "./Dockerfile",
|
|
86
|
-
"max_instances":
|
|
93
|
+
"max_instances": 20
|
|
87
94
|
}
|
|
88
95
|
],
|
|
89
96
|
"durable_objects": {
|
|
@@ -178,16 +185,62 @@ for await (const log of parseSSEStream<LogEvent>(logStream)) {
|
|
|
178
185
|
Write content to a file.
|
|
179
186
|
|
|
180
187
|
```typescript
|
|
181
|
-
await sandbox.writeFile("/app.js", "console.log('Hello!');");
|
|
188
|
+
await sandbox.writeFile("/workspace/app.js", "console.log('Hello!');");
|
|
182
189
|
```
|
|
183
190
|
|
|
184
191
|
#### `readFile(path, options?)`
|
|
185
192
|
|
|
186
|
-
Read a file from the sandbox.
|
|
193
|
+
Read a file from the sandbox with automatic binary detection.
|
|
187
194
|
|
|
188
195
|
```typescript
|
|
196
|
+
// Read text files
|
|
189
197
|
const file = await sandbox.readFile("/package.json");
|
|
190
|
-
console.log(file.content);
|
|
198
|
+
console.log(file.content); // UTF-8 text content
|
|
199
|
+
|
|
200
|
+
// Read binary files - automatically detected and base64 encoded
|
|
201
|
+
const image = await sandbox.readFile("/workspace/chart.png");
|
|
202
|
+
console.log(image.mimeType); // "image/png"
|
|
203
|
+
console.log(image.isBinary); // true
|
|
204
|
+
console.log(image.encoding); // "base64"
|
|
205
|
+
console.log(image.size); // File size in bytes
|
|
206
|
+
|
|
207
|
+
// Use the base64 content directly in data URLs
|
|
208
|
+
const dataUrl = `data:${image.mimeType};base64,${image.content}`;
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### `readFileStream(path)`
|
|
212
|
+
|
|
213
|
+
Stream large files efficiently with automatic chunking and encoding.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { streamFile, collectFile } from '@cloudflare/sandbox';
|
|
217
|
+
|
|
218
|
+
// Stream a large file
|
|
219
|
+
const stream = await sandbox.readFileStream("/large-video.mp4");
|
|
220
|
+
|
|
221
|
+
// Option 1: Process chunks as they arrive
|
|
222
|
+
for await (const chunk of streamFile(stream)) {
|
|
223
|
+
if (chunk instanceof Uint8Array) {
|
|
224
|
+
// Binary chunk - already decoded from base64
|
|
225
|
+
console.log(`Received ${chunk.byteLength} bytes`);
|
|
226
|
+
// Process binary data...
|
|
227
|
+
} else {
|
|
228
|
+
// Text chunk
|
|
229
|
+
console.log('Text:', chunk);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Option 2: Collect entire file into memory
|
|
234
|
+
const { content, metadata } = await collectFile(stream);
|
|
235
|
+
console.log(`MIME: ${metadata.mimeType}, Size: ${metadata.size} bytes`);
|
|
236
|
+
|
|
237
|
+
if (content instanceof Uint8Array) {
|
|
238
|
+
// Binary file - ready to save or process
|
|
239
|
+
await writeToStorage(content);
|
|
240
|
+
} else {
|
|
241
|
+
// Text file
|
|
242
|
+
console.log('Content:', content);
|
|
243
|
+
}
|
|
191
244
|
```
|
|
192
245
|
|
|
193
246
|
#### `gitCheckout(repoUrl, options?)`
|
|
@@ -235,7 +288,8 @@ console.log(result.stdout); // "production"
|
|
|
235
288
|
#### File System Methods
|
|
236
289
|
|
|
237
290
|
- `writeFile(path, content, options?)` - Write content to a file
|
|
238
|
-
- `readFile(path, options?)` - Read a file
|
|
291
|
+
- `readFile(path, options?)` - Read a file with automatic binary detection and base64 encoding
|
|
292
|
+
- `readFileStream(path)` - Stream large files efficiently with chunking
|
|
239
293
|
- `mkdir(path, options?)` - Create a directory
|
|
240
294
|
- `deleteFile(path)` - Delete a file
|
|
241
295
|
- `renameFile(oldPath, newPath)` - Rename a file
|
|
@@ -248,6 +302,131 @@ console.log(result.stdout); // "production"
|
|
|
248
302
|
- `unexposePort(port)` - Remove port exposure
|
|
249
303
|
- `getExposedPorts()` - List all exposed ports with their URLs
|
|
250
304
|
|
|
305
|
+
#### Session Methods
|
|
306
|
+
|
|
307
|
+
- `createSession(options)` - Create an isolated execution session
|
|
308
|
+
- `name`: Session identifier
|
|
309
|
+
- `env`: Environment variables for this session
|
|
310
|
+
- `cwd`: Working directory
|
|
311
|
+
- `isolation`: Enable PID namespace isolation (requires CAP_SYS_ADMIN)
|
|
312
|
+
|
|
313
|
+
<h2 id="code-interpreter">๐งช Code Interpreter</h2>
|
|
314
|
+
|
|
315
|
+
The Sandbox SDK includes powerful code interpreter capabilities, allowing you to execute Python and JavaScript code with rich outputs including charts, tables, and formatted data.
|
|
316
|
+
|
|
317
|
+
### Code Execution
|
|
318
|
+
|
|
319
|
+
#### `createCodeContext(options?)`
|
|
320
|
+
|
|
321
|
+
Creates a new code execution context with persistent state.
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// Create a Python context
|
|
325
|
+
const pythonCtx = await sandbox.createCodeContext({ language: 'python' });
|
|
326
|
+
|
|
327
|
+
// Create a JavaScript context
|
|
328
|
+
const jsCtx = await sandbox.createCodeContext({ language: 'javascript' });
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Options:**
|
|
332
|
+
- `language`: Programming language (`'python'` | `'javascript'` | `'typescript'`)
|
|
333
|
+
- `cwd`: Working directory (default: `/workspace`)
|
|
334
|
+
- `envVars`: Environment variables for the context
|
|
335
|
+
|
|
336
|
+
#### `runCode(code, options?)`
|
|
337
|
+
|
|
338
|
+
Executes code with optional streaming callbacks.
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
// Simple execution
|
|
342
|
+
const execution = await sandbox.runCode('print("Hello World")', {
|
|
343
|
+
context: pythonCtx
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// With streaming callbacks
|
|
347
|
+
await sandbox.runCode(`
|
|
348
|
+
for i in range(5):
|
|
349
|
+
print(f"Step {i}")
|
|
350
|
+
time.sleep(1)
|
|
351
|
+
`, {
|
|
352
|
+
context: pythonCtx,
|
|
353
|
+
onStdout: (output) => console.log('Real-time:', output.text),
|
|
354
|
+
onResult: (result) => console.log('Result:', result)
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Options:**
|
|
359
|
+
- `context`: Context to run the code in
|
|
360
|
+
- `language`: Language if no context provided
|
|
361
|
+
- `onStdout`: Callback for stdout output
|
|
362
|
+
- `onStderr`: Callback for stderr output
|
|
363
|
+
- `onResult`: Callback for execution results
|
|
364
|
+
- `onError`: Callback for errors
|
|
365
|
+
|
|
366
|
+
#### `runCodeStream(code, options?)`
|
|
367
|
+
|
|
368
|
+
Returns a streaming response for real-time processing.
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const stream = await sandbox.runCodeStream('import time; [print(i) for i in range(10)]');
|
|
372
|
+
// Process the stream as needed
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Rich Outputs
|
|
376
|
+
|
|
377
|
+
The code interpreter automatically detects and processes various output types:
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
// Data visualization
|
|
381
|
+
const execution = await sandbox.runCode(`
|
|
382
|
+
import matplotlib.pyplot as plt
|
|
383
|
+
import numpy as np
|
|
384
|
+
|
|
385
|
+
x = np.linspace(0, 10, 100)
|
|
386
|
+
y = np.sin(x)
|
|
387
|
+
plt.plot(x, y)
|
|
388
|
+
plt.title('Sine Wave')
|
|
389
|
+
plt.show()
|
|
390
|
+
`, {
|
|
391
|
+
context: pythonCtx,
|
|
392
|
+
onResult: (result) => {
|
|
393
|
+
if (result.png) {
|
|
394
|
+
// Base64 encoded PNG image
|
|
395
|
+
console.log('Chart generated!');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// HTML tables with pandas
|
|
401
|
+
const tableExecution = await sandbox.runCode(`
|
|
402
|
+
import pandas as pd
|
|
403
|
+
df = pd.DataFrame({
|
|
404
|
+
'name': ['Alice', 'Bob', 'Charlie'],
|
|
405
|
+
'score': [92, 88, 95]
|
|
406
|
+
})
|
|
407
|
+
df
|
|
408
|
+
`, { context: pythonCtx });
|
|
409
|
+
|
|
410
|
+
// Access HTML table in execution.results[0].html
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Output Formats
|
|
414
|
+
|
|
415
|
+
Results can include multiple formats:
|
|
416
|
+
- `text`: Plain text representation
|
|
417
|
+
- `html`: HTML (often pandas DataFrames)
|
|
418
|
+
- `png`/`jpeg`: Base64 encoded images
|
|
419
|
+
- `svg`: Vector graphics
|
|
420
|
+
- `json`: Structured data
|
|
421
|
+
- `chart`: Parsed chart information
|
|
422
|
+
|
|
423
|
+
Check available formats with `result.formats()`.
|
|
424
|
+
|
|
425
|
+
#### Additional Code Interpreter Methods
|
|
426
|
+
|
|
427
|
+
- `listCodeContexts()` - List all active code contexts
|
|
428
|
+
- `deleteCodeContext(contextId)` - Delete a specific context
|
|
429
|
+
|
|
251
430
|
<h2 id="port-forwarding">๐ Port Forwarding</h2>
|
|
252
431
|
|
|
253
432
|
The SDK automatically handles preview URL routing for exposed ports. Just add one line to your worker:
|
|
@@ -284,7 +463,7 @@ The SDK handles:
|
|
|
284
463
|
|
|
285
464
|
```dockerfile
|
|
286
465
|
# In your Dockerfile (only needed for local dev)
|
|
287
|
-
FROM
|
|
466
|
+
FROM docker.io/cloudflare/sandbox:0.1.3
|
|
288
467
|
|
|
289
468
|
# Expose the ports you'll be using
|
|
290
469
|
EXPOSE 3000 # For a web server
|
|
@@ -316,7 +495,7 @@ const sandbox = getSandbox(env.Sandbox, "node-app");
|
|
|
316
495
|
|
|
317
496
|
// Write a simple Express server
|
|
318
497
|
await sandbox.writeFile(
|
|
319
|
-
"/app.js",
|
|
498
|
+
"/workspace/app.js",
|
|
320
499
|
`
|
|
321
500
|
const express = require('express');
|
|
322
501
|
const app = express();
|
|
@@ -406,14 +585,100 @@ console.log(`Service available at: ${preview.url}`);
|
|
|
406
585
|
// See the example in examples/basic/src/index.ts for the routing implementation.
|
|
407
586
|
```
|
|
408
587
|
|
|
588
|
+
### Data Analysis with Code Interpreter
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
const sandbox = getSandbox(env.Sandbox, "analysis");
|
|
592
|
+
|
|
593
|
+
// Create a Python context for data analysis
|
|
594
|
+
const pythonCtx = await sandbox.createCodeContext({ language: 'python' });
|
|
595
|
+
|
|
596
|
+
// Load and analyze data
|
|
597
|
+
const analysis = await sandbox.runCode(`
|
|
598
|
+
import pandas as pd
|
|
599
|
+
import matplotlib.pyplot as plt
|
|
600
|
+
|
|
601
|
+
# Create sample data
|
|
602
|
+
data = {
|
|
603
|
+
'Month': ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
|
|
604
|
+
'Sales': [10000, 12000, 15000, 14000, 18000],
|
|
605
|
+
'Profit': [2000, 2500, 3200, 2800, 4000]
|
|
606
|
+
}
|
|
607
|
+
df = pd.DataFrame(data)
|
|
608
|
+
|
|
609
|
+
# Display summary statistics
|
|
610
|
+
print("Sales Summary:")
|
|
611
|
+
print(df.describe())
|
|
612
|
+
|
|
613
|
+
# Create visualization
|
|
614
|
+
plt.figure(figsize=(10, 6))
|
|
615
|
+
plt.subplot(1, 2, 1)
|
|
616
|
+
plt.bar(df['Month'], df['Sales'])
|
|
617
|
+
plt.title('Monthly Sales')
|
|
618
|
+
plt.xlabel('Month')
|
|
619
|
+
plt.ylabel('Sales ($)')
|
|
620
|
+
|
|
621
|
+
plt.subplot(1, 2, 2)
|
|
622
|
+
plt.plot(df['Month'], df['Profit'], marker='o', color='green')
|
|
623
|
+
plt.title('Monthly Profit')
|
|
624
|
+
plt.xlabel('Month')
|
|
625
|
+
plt.ylabel('Profit ($)')
|
|
626
|
+
|
|
627
|
+
plt.tight_layout()
|
|
628
|
+
plt.show()
|
|
629
|
+
|
|
630
|
+
# Return the data as JSON
|
|
631
|
+
df.to_dict('records')
|
|
632
|
+
`, {
|
|
633
|
+
context: pythonCtx,
|
|
634
|
+
onResult: (result) => {
|
|
635
|
+
if (result.png) {
|
|
636
|
+
// Handle the chart image
|
|
637
|
+
console.log('Chart generated:', result.png.substring(0, 50) + '...');
|
|
638
|
+
}
|
|
639
|
+
if (result.json) {
|
|
640
|
+
// Handle the structured data
|
|
641
|
+
console.log('Data:', result.json);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// Multi-language workflow: Process in Python, analyze in JavaScript
|
|
647
|
+
await sandbox.runCode(`
|
|
648
|
+
# Save processed data
|
|
649
|
+
df.to_json('/tmp/sales_data.json', orient='records')
|
|
650
|
+
`, { context: pythonCtx });
|
|
651
|
+
|
|
652
|
+
const jsCtx = await sandbox.createCodeContext({ language: 'javascript' });
|
|
653
|
+
const jsAnalysis = await sandbox.runCode(`
|
|
654
|
+
const fs = require('fs');
|
|
655
|
+
const data = JSON.parse(fs.readFileSync('/tmp/sales_data.json', 'utf8'));
|
|
656
|
+
|
|
657
|
+
// Calculate growth rate
|
|
658
|
+
const growth = data.map((curr, idx) => {
|
|
659
|
+
if (idx === 0) return { ...curr, growth: 0 };
|
|
660
|
+
const prev = data[idx - 1];
|
|
661
|
+
return {
|
|
662
|
+
...curr,
|
|
663
|
+
growth: ((curr.Sales - prev.Sales) / prev.Sales * 100).toFixed(2) + '%'
|
|
664
|
+
};
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
console.log('Growth Analysis:', growth);
|
|
668
|
+
growth;
|
|
669
|
+
`, { context: jsCtx });
|
|
670
|
+
```
|
|
671
|
+
|
|
409
672
|
<h2 id="architecture">๐๏ธ Architecture</h2>
|
|
410
673
|
|
|
411
674
|
The SDK leverages Cloudflare's infrastructure:
|
|
412
675
|
|
|
413
676
|
- **Durable Objects**: Manages sandbox lifecycle and state
|
|
414
|
-
- **Containers**: Provides isolated execution environments
|
|
677
|
+
- **Containers**: Provides isolated execution environments with Jupyter kernels
|
|
415
678
|
- **Workers**: Handles HTTP routing and API interface
|
|
416
679
|
- **Edge Network**: Enables global distribution and low latency
|
|
680
|
+
- **Jupyter Integration**: Python (IPython) and JavaScript (TSLab) kernels for code execution
|
|
681
|
+
- **MIME Processing**: Automatic detection and handling of rich output formats
|
|
417
682
|
|
|
418
683
|
<h2 id="advanced-usage">๐ ๏ธ Advanced Usage</h2>
|
|
419
684
|
|
|
@@ -448,10 +713,15 @@ for await (const event of parseSSEStream<ExecEvent>(stream)) {
|
|
|
448
713
|
|
|
449
714
|
The SDK exports utilities for working with Server-Sent Event streams:
|
|
450
715
|
|
|
716
|
+
**Command Execution:**
|
|
451
717
|
- **`parseSSEStream<T>(stream)`** - Convert ReadableStream to typed AsyncIterable
|
|
452
718
|
- **`responseToAsyncIterable<T>(response)`** - Convert SSE Response to AsyncIterable
|
|
453
719
|
- **`asyncIterableToSSEStream<T>(iterable)`** - Convert AsyncIterable back to SSE stream
|
|
454
720
|
|
|
721
|
+
**File Streaming:**
|
|
722
|
+
- **`streamFile(stream, signal?)`** - Convert file SSE stream to AsyncIterable with automatic base64 decoding
|
|
723
|
+
- **`collectFile(stream, signal?)`** - Collect entire file from stream into memory
|
|
724
|
+
|
|
455
725
|
#### Advanced Streaming Examples
|
|
456
726
|
|
|
457
727
|
**CI/CD Build System:**
|
|
@@ -494,17 +764,71 @@ for await (const log of parseSSEStream<LogEvent>(logStream)) {
|
|
|
494
764
|
|
|
495
765
|
### Session Management
|
|
496
766
|
|
|
497
|
-
|
|
767
|
+
The SDK provides two approaches for managing execution context:
|
|
768
|
+
|
|
769
|
+
#### Implicit Sessions (Recommended)
|
|
770
|
+
|
|
771
|
+
Each sandbox maintains its own persistent session automatically:
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
const sandbox = getSandbox(env.Sandbox, "my-app");
|
|
775
|
+
|
|
776
|
+
// These commands share state (pwd, env vars, etc.)
|
|
777
|
+
await sandbox.exec("cd /app");
|
|
778
|
+
await sandbox.exec("pwd"); // Output: /app
|
|
779
|
+
await sandbox.exec("export MY_VAR=hello");
|
|
780
|
+
await sandbox.exec("echo $MY_VAR"); // Output: hello
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
#### Explicit Sessions for Advanced Use Cases
|
|
784
|
+
|
|
785
|
+
Create isolated execution contexts within the same sandbox:
|
|
498
786
|
|
|
499
787
|
```typescript
|
|
500
|
-
const
|
|
788
|
+
const sandbox = getSandbox(env.Sandbox, "multi-env");
|
|
789
|
+
|
|
790
|
+
// Create independent sessions with different environments
|
|
791
|
+
const buildSession = await sandbox.createSession({
|
|
792
|
+
name: "build",
|
|
793
|
+
env: { NODE_ENV: "production" },
|
|
794
|
+
cwd: "/build"
|
|
795
|
+
});
|
|
501
796
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
797
|
+
const testSession = await sandbox.createSession({
|
|
798
|
+
name: "test",
|
|
799
|
+
env: { NODE_ENV: "test" },
|
|
800
|
+
cwd: "/test"
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
// Run commands in parallel with different contexts
|
|
804
|
+
await Promise.all([
|
|
805
|
+
buildSession.exec("npm run build"),
|
|
806
|
+
testSession.exec("npm test")
|
|
807
|
+
]);
|
|
506
808
|
```
|
|
507
809
|
|
|
810
|
+
#### Security with AI Agents
|
|
811
|
+
|
|
812
|
+
When using AI coding agents, separate development from execution:
|
|
813
|
+
|
|
814
|
+
```typescript
|
|
815
|
+
// Phase 1: AI agent writes code (with API keys)
|
|
816
|
+
const devSession = await sandbox.createSession({
|
|
817
|
+
name: "ai-development",
|
|
818
|
+
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY }
|
|
819
|
+
});
|
|
820
|
+
await devSession.exec('opencode "build a web server"');
|
|
821
|
+
|
|
822
|
+
// Phase 2: Run the generated code (without API keys)
|
|
823
|
+
const appSession = await sandbox.createSession({
|
|
824
|
+
name: "app-runtime",
|
|
825
|
+
env: { PORT: "3000" } // Only app-specific vars
|
|
826
|
+
});
|
|
827
|
+
await appSession.exec("node server.js");
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
> **Best Practice**: Keep AI agent credentials separate from your application runtime to prevent accidental exposure of API keys.
|
|
831
|
+
|
|
508
832
|
<h2 id="debugging">๐ Debugging</h2>
|
|
509
833
|
|
|
510
834
|
Enable verbose logging:
|
|
@@ -523,6 +847,9 @@ sandbox.client.onCommandComplete = (success, code) =>
|
|
|
523
847
|
- Maximum container runtime is limited by Durable Object constraints
|
|
524
848
|
- WebSocket support for preview URLs coming soon
|
|
525
849
|
- Some system calls may be restricted in the container environment
|
|
850
|
+
- Code interpreter has no internet access (sandbox restriction)
|
|
851
|
+
- Some Python/JavaScript packages may not be pre-installed
|
|
852
|
+
- Resource limits apply to code execution (CPU, memory)
|
|
526
853
|
|
|
527
854
|
<h2 id="contributing">๐ค Contributing</h2>
|
|
528
855
|
|
|
@@ -536,11 +863,21 @@ cd sandbox-sdk
|
|
|
536
863
|
# Install dependencies
|
|
537
864
|
npm install
|
|
538
865
|
|
|
866
|
+
# Install Bun (if not already installed)
|
|
867
|
+
# Visit https://bun.sh for installation instructions
|
|
868
|
+
curl -fsSL https://bun.sh/install | bash
|
|
869
|
+
|
|
870
|
+
# Install container dependencies (required for TypeScript checking)
|
|
871
|
+
cd packages/sandbox/container_src && bun install && cd -
|
|
872
|
+
|
|
539
873
|
# Run tests
|
|
540
874
|
npm test
|
|
541
875
|
|
|
542
876
|
# Build the project
|
|
543
877
|
npm run build
|
|
878
|
+
|
|
879
|
+
# Run type checking and linting
|
|
880
|
+
npm run check
|
|
544
881
|
```
|
|
545
882
|
|
|
546
883
|
<h2 id="license">๐ License</h2>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"workspaces": {
|
|
4
|
+
"": {
|
|
5
|
+
"name": "sandbox-server",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"esbuild": "^0.21.5",
|
|
8
|
+
"uuid": "^9.0.1",
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/node": "^20.0.0",
|
|
12
|
+
"@types/uuid": "^9.0.7",
|
|
13
|
+
"typescript": "^5.3.0",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
"packages": {
|
|
18
|
+
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
|
19
|
+
|
|
20
|
+
"@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
|
21
|
+
|
|
22
|
+
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
|
|
23
|
+
|
|
24
|
+
"@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
|
|
25
|
+
|
|
26
|
+
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
|
|
27
|
+
|
|
28
|
+
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
|
|
29
|
+
|
|
30
|
+
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
|
|
31
|
+
|
|
32
|
+
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
|
|
33
|
+
|
|
34
|
+
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
|
|
35
|
+
|
|
36
|
+
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
|
|
37
|
+
|
|
38
|
+
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
|
|
39
|
+
|
|
40
|
+
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
|
|
41
|
+
|
|
42
|
+
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
|
|
43
|
+
|
|
44
|
+
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
|
|
45
|
+
|
|
46
|
+
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
|
|
47
|
+
|
|
48
|
+
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
|
|
49
|
+
|
|
50
|
+
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
|
|
51
|
+
|
|
52
|
+
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
|
|
53
|
+
|
|
54
|
+
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
|
|
55
|
+
|
|
56
|
+
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
|
|
57
|
+
|
|
58
|
+
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
|
|
59
|
+
|
|
60
|
+
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
|
|
61
|
+
|
|
62
|
+
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
|
63
|
+
|
|
64
|
+
"@types/node": ["@types/node@20.19.16", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-VS6TTONVdgwJwtJr7U+ghEjpfmQdqehLLpg/iMYGOd1+ilaFjdBJwFuPggJ4EAYPDCzWfDUHoIxyVnu+tOWVuQ=="],
|
|
65
|
+
|
|
66
|
+
"@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="],
|
|
67
|
+
|
|
68
|
+
"esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
|
69
|
+
|
|
70
|
+
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
|
71
|
+
|
|
72
|
+
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
|
73
|
+
|
|
74
|
+
"uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
|
75
|
+
}
|
|
76
|
+
}
|