@cloudflare/sandbox 0.0.0-f5fcd52 → 0.0.0-fd5ec7f

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 CHANGED
@@ -1,5 +1,37 @@
1
1
  # @cloudflare/sandbox
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#32](https://github.com/cloudflare/sandbox-sdk/pull/32) [`1a42464`](https://github.com/cloudflare/sandbox-sdk/commit/1a4246479369c5d0160705caf192aa1816540d52) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Bring back package README
8
+
9
+ ## 0.1.2
10
+
11
+ ### Patch Changes
12
+
13
+ - [#30](https://github.com/cloudflare/sandbox-sdk/pull/30) [`30e5c25`](https://github.com/cloudflare/sandbox-sdk/commit/30e5c25cf7d4b07f9049724206c531e2d5d29d5c) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Remove actions timeout
14
+
15
+ - [#29](https://github.com/cloudflare/sandbox-sdk/pull/29) [`d78508f`](https://github.com/cloudflare/sandbox-sdk/commit/d78508f7287a59e0423edd2999c2c83e9e34ccfd) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Create multi-platform Docker image and switch to Cloudflare official repo
16
+
17
+ ## 0.1.1
18
+
19
+ ### Patch Changes
20
+
21
+ - [`157dde9`](https://github.com/cloudflare/sandbox-sdk/commit/157dde9b1f23e9bb6f3e9c3f0514b639a8813897) Thanks [@threepointone](https://github.com/threepointone)! - update deps
22
+
23
+ - [`a04f6b6`](https://github.com/cloudflare/sandbox-sdk/commit/a04f6b6c0b2ef9e3ce0851b53769f1c10d8c6de6) Thanks [@threepointone](https://github.com/threepointone)! - trigger a build with updated deps
24
+
25
+ ## 0.1.0
26
+
27
+ ### Minor Changes
28
+
29
+ - [#24](https://github.com/cloudflare/sandbox-sdk/pull/24) [`cecde0a`](https://github.com/cloudflare/sandbox-sdk/commit/cecde0a7530a87deffd8562fb8b01d66ee80ee19) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Redesign command execution API
30
+
31
+ ### Patch Changes
32
+
33
+ - [#22](https://github.com/cloudflare/sandbox-sdk/pull/22) [`f5fcd52`](https://github.com/cloudflare/sandbox-sdk/commit/f5fcd52025d1f7958a374e69d75e3fc590275f3f) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Allow setting env variables dynamically and remove command restrictions
34
+
3
35
  ## 0.0.9
4
36
 
5
37
  ### Patch Changes
package/Dockerfile CHANGED
@@ -1,4 +1,5 @@
1
1
  # Sandbox base image with development tools, Python, Node.js, and Bun
2
+ FROM oven/bun:latest AS bun-source
2
3
  FROM ubuntu:22.04
3
4
 
4
5
  # Prevent interactive prompts during package installation
@@ -50,41 +51,23 @@ RUN apt-get update && apt-get install -y ca-certificates curl gnupg \
50
51
  && apt-get install -y nodejs \
51
52
  && rm -rf /var/lib/apt/lists/*
52
53
 
53
- # Install Bun using the official installation script
54
- RUN curl -fsSL https://bun.sh/install | bash \
55
- && mv /root/.bun/bin/bun /usr/local/bin/bun \
56
- && mv /root/.bun/bin/bunx /usr/local/bin/bunx \
57
- && rm -rf /root/.bun
54
+ # Install Bun from official image (avoids architecture compatibility issues)
55
+ COPY --from=bun-source /usr/local/bin/bun /usr/local/bin/bun
56
+ COPY --from=bun-source /usr/local/bin/bunx /usr/local/bin/bunx
58
57
 
59
- # Install global npm packages as root
60
- RUN npm install -g yarn pnpm
61
-
62
- # Create a non-root user for running applications
63
- RUN useradd -m -s /bin/bash sandbox \
64
- && echo "sandbox ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
65
58
 
66
59
  # Set up working directory
67
60
  WORKDIR /app
68
61
 
69
- # Set ownership of the app directory to sandbox user
70
- RUN chown -R sandbox:sandbox /app
71
-
72
- # Set environment variables
73
- ENV PATH="/home/sandbox/.local/bin:${PATH}"
74
-
75
- # Switch to non-root user
76
- USER sandbox
77
-
78
62
  # Verify installations
79
63
  RUN python3 --version && \
80
64
  node --version && \
81
65
  npm --version && \
82
- bun --version && \
83
- yarn --version && \
84
- pnpm --version
66
+ bun --version
67
+
85
68
 
86
69
  # Copy container source files
87
- COPY --chown=sandbox:sandbox container_src/* ./
70
+ COPY container_src/ ./
88
71
 
89
72
  # Expose the application port
90
73
  EXPOSE 3000
package/README.md CHANGED
@@ -1,10 +1,78 @@
1
- ## @cloudflare/sandbox
1
+ <div align="center">
2
+ <h1>📦 Cloudflare Sandbox SDK</h1>
3
+ <h3><strong>Run sandboxed code environments on Cloudflare's edge network</strong></h3>
4
+ <p>
5
+ <a href="https://www.npmjs.com/package/@cloudflare/sandbox"><img src="https://img.shields.io/npm/v/@cloudflare/sandbox.svg" alt="npm version"></a>
6
+ <a href="https://github.com/cloudflare/sandbox-sdk"><img src="https://img.shields.io/badge/status-experimental-orange.svg" alt="status"></a>
7
+ </p>
8
+ </div>
2
9
 
3
- > **⚠️ Experimental** - This library is currently experimental and we're actively seeking feedback. Please try it out and let us know what you think!
10
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
11
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
4
12
 
5
- A library to spin up a sandboxed environment.
13
+ - [✨ Overview](#overview)
14
+ - [🎯 Features](#features)
15
+ - [🚀 Quick Start](#quick-start)
16
+ - [Installation](#installation)
17
+ - [Basic Setup](#basic-setup)
18
+ - [📚 API Reference](#api-reference)
19
+ - [Core Methods](#core-methods)
20
+ - [🌐 Port Forwarding](#port-forwarding)
21
+ - [Utility Methods](#utility-methods)
22
+ - [💡 Examples](#examples)
23
+ - [Run a Node.js App](#run-a-nodejs-app)
24
+ - [Build and Test Code](#build-and-test-code)
25
+ - [Interactive Development Environment](#interactive-development-environment)
26
+ - [Expose Services with Preview URLs](#expose-services-with-preview-urls)
27
+ - [🏗️ Architecture](#architecture)
28
+ - [🛠️ Advanced Usage](#advanced-usage)
29
+ - [AsyncIterable Streaming Support](#asynciterable-streaming-support)
30
+ - [Session Management](#session-management)
31
+ - [🔍 Debugging](#debugging)
32
+ - [🚧 Known Limitations](#known-limitations)
33
+ - [🤝 Contributing](#contributing)
34
+ - [📄 License](#license)
35
+ - [🙌 Acknowledgments](#acknowledgments)
6
36
 
7
- First, setup your wrangler.json to use the sandbox:
37
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
38
+
39
+ <h2 id="overview">✨ Overview</h2>
40
+
41
+ The Cloudflare Sandbox SDK enables you to run isolated code environments directly on Cloudflare's edge network using Durable Objects and the Cloudflare Containers. Execute commands, manage files, run services, and expose them via public URLs - all within secure, sandboxed containers.
42
+
43
+ <h2 id="features">🎯 Features</h2>
44
+
45
+ - **🔒 Secure Isolation**: Each sandbox runs in its own container with full process isolation
46
+ - **⚡ Edge-Native**: Runs on Cloudflare's global network for low latency worldwide
47
+ - **📁 File System Access**: Read, write, and manage files within the sandbox
48
+ - **🔧 Command Execution**: Run any command or process inside the container
49
+ - **🌐 Preview URLs**: Expose services running in your sandbox via public URLs
50
+ - **🔄 Git Integration**: Clone repositories directly into sandboxes
51
+ - **🚀 Streaming Support**: Real-time output streaming for long-running commands
52
+ - **🎮 Session Management**: Maintain state across multiple operations
53
+
54
+ <h2 id="quick-start">🚀 Quick Start</h2>
55
+
56
+ ### Installation
57
+
58
+ ```bash
59
+ npm install @cloudflare/sandbox
60
+ ```
61
+
62
+ ### Basic Setup
63
+
64
+ 1. **Create a Dockerfile** (temporary requirement, will be removed in future releases):
65
+
66
+ ```dockerfile
67
+ FROM docker.io/cloudflare/sandbox:0.1.3
68
+
69
+ # Expose the ports you want to expose
70
+ EXPOSE 3000
71
+ ```
72
+
73
+ 2. **Configure wrangler.json**:
74
+
75
+ > **NOTE**: In an upcoming release, this step will be removed entirely and you can reference a single Docker image published by us directly in your wrangler configuration below.
8
76
 
9
77
  ```jsonc
10
78
  {
@@ -12,8 +80,8 @@ First, setup your wrangler.json to use the sandbox:
12
80
  "containers": [
13
81
  {
14
82
  "class_name": "Sandbox",
15
- "image": "./node_modules/@cloudflare/sandbox/Dockerfile",
16
- "name": "sandbox"
83
+ "image": "./Dockerfile",
84
+ "max_instances": 1
17
85
  }
18
86
  ],
19
87
  "durable_objects": {
@@ -33,33 +101,460 @@ First, setup your wrangler.json to use the sandbox:
33
101
  }
34
102
  ```
35
103
 
36
- Then, export the Sandbox class in your worker:
104
+ 3. **Create your Worker**:
105
+
106
+ ```typescript
107
+ import { getSandbox } from "@cloudflare/sandbox";
37
108
 
38
- ```ts
109
+ // Export the Sandbox class in your Worker
39
110
  export { Sandbox } from "@cloudflare/sandbox";
111
+
112
+ export default {
113
+ async fetch(request: Request, env: Env) {
114
+ const sandbox = getSandbox(env.Sandbox, "my-sandbox");
115
+
116
+ // Execute a command
117
+ const result = await sandbox.exec("echo 'Hello from the edge!'");
118
+ return new Response(result.stdout);
119
+ },
120
+ };
40
121
  ```
41
122
 
42
- You can then use the Sandbox class in your worker:
123
+ <h2 id="api-reference">📚 API Reference</h2>
43
124
 
44
- ```ts
45
- import { getSandbox } from "@cloudflare/sandbox";
125
+ ### Core Methods
126
+
127
+ #### Command Execution
128
+
129
+ **`exec(command, options?)`** - Enhanced command execution that always returns results
130
+
131
+ ```typescript
132
+ // Simple execution
133
+ const result = await sandbox.exec("npm install express");
134
+ console.log(result.stdout, result.exitCode);
135
+
136
+ // With streaming callbacks
137
+ const result = await sandbox.exec("npm run build", {
138
+ stream: true,
139
+ onOutput: (stream, data) => console.log(`[${stream}] ${data}`)
140
+ });
141
+ ```
142
+
143
+ **`execStream(command, options?)`** - Dedicated streaming method returning SSE stream
144
+
145
+ ```typescript
146
+ import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
147
+
148
+ const stream = await sandbox.execStream("npm run test");
149
+ for await (const event of parseSSEStream<ExecEvent>(stream)) {
150
+ switch (event.type) {
151
+ case 'stdout':
152
+ console.log(`Test output: ${event.data}`);
153
+ break;
154
+ case 'complete':
155
+ console.log(`Tests ${event.exitCode === 0 ? 'passed' : 'failed'}`);
156
+ break;
157
+ }
158
+ }
159
+ ```
160
+
161
+ **`startProcess(command, options?)`** - Start background processes with lifecycle management
162
+
163
+ ```typescript
164
+ const process = await sandbox.startProcess("node server.js");
165
+ console.log(`Started process ${process.id} with PID ${process.pid}`);
166
+
167
+ // Monitor the process
168
+ const logStream = await sandbox.streamProcessLogs(process.id);
169
+ for await (const log of parseSSEStream<LogEvent>(logStream)) {
170
+ console.log(`Server: ${log.data}`);
171
+ }
172
+ ```
173
+
174
+ #### `writeFile(path, content, options?)`
175
+
176
+ Write content to a file.
177
+
178
+ ```typescript
179
+ await sandbox.writeFile("/app.js", "console.log('Hello!');");
180
+ ```
181
+
182
+ #### `readFile(path, options?)`
183
+
184
+ Read a file from the sandbox.
185
+
186
+ ```typescript
187
+ const file = await sandbox.readFile("/package.json");
188
+ console.log(file.content);
189
+ ```
190
+
191
+ #### `gitCheckout(repoUrl, options?)`
192
+
193
+ Clone a git repository.
194
+
195
+ ```typescript
196
+ await sandbox.gitCheckout("https://github.com/user/repo", {
197
+ branch: "main",
198
+ targetDir: "my-project",
199
+ });
200
+ ```
201
+
202
+ #### `setEnvVars(envVars)`
203
+
204
+ Set environment variables dynamically in the sandbox.
205
+
206
+ > **Important**: This method must be called immediately after `getSandbox()` and before any other operations. Once a sandbox instance starts up, environment variables cannot be changed
207
+ for that instance.
208
+
209
+ ```typescript
210
+ const sandbox = getSandbox(env.Sandbox, "my-sandbox");
211
+
212
+ // Set environment variables FIRST, before any other operations
213
+ await sandbox.setEnvVars({
214
+ NODE_ENV: "production",
215
+ API_KEY: "your-api-key",
216
+ DATABASE_URL: "postgresql://localhost:5432/mydb"
217
+ });
218
+
219
+ // Now you can run commands - environment variables are available
220
+ const result = await sandbox.exec("echo $NODE_ENV");
221
+ console.log(result.stdout); // "production"
222
+ ```
223
+
224
+ #### Process Management
225
+
226
+ - `listProcesses()` - List all running processes
227
+ - `getProcess(id)` - Get detailed process status
228
+ - `killProcess(id, signal?)` - Terminate specific processes
229
+ - `killAllProcesses()` - Kill all processes
230
+ - `streamProcessLogs(id, options?)` - Stream logs from running processes
231
+ - `getProcessLogs(id)` - Get accumulated process output
232
+
233
+ #### File System Methods
234
+
235
+ - `writeFile(path, content, options?)` - Write content to a file
236
+ - `readFile(path, options?)` - Read a file from the sandbox
237
+ - `mkdir(path, options?)` - Create a directory
238
+ - `deleteFile(path)` - Delete a file
239
+ - `renameFile(oldPath, newPath)` - Rename a file
240
+ - `moveFile(sourcePath, destinationPath)` - Move a file
241
+ - `gitCheckout(repoUrl, options?)` - Clone git repositories
242
+
243
+ #### Network Methods
244
+
245
+ - `exposePort(port, options?)` - Expose a port and get a public URL
246
+ - `unexposePort(port)` - Remove port exposure
247
+ - `getExposedPorts()` - List all exposed ports with their URLs
248
+
249
+ <h2 id="port-forwarding">🌐 Port Forwarding</h2>
250
+
251
+ The SDK automatically handles preview URL routing for exposed ports. Just add one line to your worker:
252
+
253
+ ```typescript
254
+ import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";
46
255
 
47
256
  export default {
48
- async fetch(request: Request, env: Env) {
49
- const sandbox = getSandbox(env.Sandbox, "my-sandbox");
50
- return sandbox.exec("ls", ["-la"]);
257
+ async fetch(request, env) {
258
+ // Route requests to exposed container ports via their preview URLs
259
+ const proxyResponse = await proxyToSandbox(request, env);
260
+ if (proxyResponse) return proxyResponse;
261
+
262
+ // Your custom routes here
263
+ // ...
51
264
  },
52
265
  };
53
266
  ```
54
267
 
55
- ### Methods:
268
+ When you expose a port, the SDK returns a preview URL that automatically routes to your service:
269
+
270
+ ```typescript
271
+ const preview = await sandbox.exposePort(3000);
272
+ console.log(preview.url); // https://3000-sandbox-id.your-worker.dev
273
+ ```
274
+
275
+ The SDK handles:
276
+
277
+ - Subdomain routing (`3000-sandbox-id.domain.com`) for both production and local development
278
+ - All localhost variants (127.0.0.1, ::1, etc.)
279
+ - Request forwarding with proper headers
280
+
281
+ > **Important for Local Development**: When developing locally with `wrangler dev`, you must explicitly expose ports in your Dockerfile using the `EXPOSE` instruction. This is **only required for local development** - in production, all container ports are automatically accessible.
282
+
283
+ ```dockerfile
284
+ # In your Dockerfile (only needed for local dev)
285
+ FROM docker.io/cloudflare/sandbox:0.1.3
286
+
287
+ # Expose the ports you'll be using
288
+ EXPOSE 3000 # For a web server
289
+ EXPOSE 8080 # For an API server
290
+ EXPOSE 3001 # For any additional services
291
+
292
+ # Your container setup...
293
+ ```
294
+
295
+ Without the `EXPOSE` instruction in local development, you'll see this error:
296
+
297
+ ```
298
+ connect(): Connection refused: container port not found. Make sure you exposed the port in your container definition.
299
+ ```
300
+
301
+ For more details, see the [Cloudflare Containers local development guide](https://developers.cloudflare.com/containers/local-dev/#exposing-ports).
302
+
303
+ ### Utility Methods
304
+
305
+ - `ping()` - Health check for the sandbox
306
+ - `containerFetch(request)` - Direct container communication
307
+
308
+ <h2 id="examples">💡 Examples</h2>
309
+
310
+ ### Run a Node.js App
311
+
312
+ ```typescript
313
+ const sandbox = getSandbox(env.Sandbox, "node-app");
314
+
315
+ // Write a simple Express server
316
+ await sandbox.writeFile(
317
+ "/app.js",
318
+ `
319
+ const express = require('express');
320
+ const app = express();
321
+
322
+ app.get('/', (req, res) => {
323
+ res.json({ message: 'Hello from Cloudflare!' });
324
+ });
325
+
326
+ app.listen(3000);
327
+ `
328
+ );
329
+
330
+ // Install dependencies and start the server
331
+ await sandbox.exec("npm init -y");
332
+ await sandbox.exec("npm install express");
333
+ const server = await sandbox.startProcess("node app.js");
334
+
335
+ // Expose it to the internet
336
+ const preview = await sandbox.exposePort(3000);
337
+ console.log(`API available at: ${preview.url}`);
338
+ ```
339
+
340
+ ### Build and Test Code
341
+
342
+ ```typescript
343
+ const sandbox = getSandbox(env.Sandbox, "test-env");
344
+
345
+ // Clone a repository
346
+ await sandbox.gitCheckout("https://github.com/user/project");
347
+
348
+ // Run tests
349
+ const testResult = await sandbox.exec("npm test");
350
+
351
+ // Build the project
352
+ const buildResult = await sandbox.exec("npm run build");
353
+
354
+ return new Response(
355
+ JSON.stringify({
356
+ tests: testResult.exitCode === 0 ? "passed" : "failed",
357
+ build: buildResult.exitCode === 0 ? "success" : "failed",
358
+ output: testResult.stdout,
359
+ })
360
+ );
361
+ ```
362
+
363
+ ### Interactive Development Environment
364
+
365
+ ```typescript
366
+ // Create a development sandbox with hot reload
367
+ const sandbox = getSandbox(env.Sandbox, "dev-env");
368
+
369
+ // Set up the project
370
+ await sandbox.gitCheckout("https://github.com/user/my-app");
371
+ await sandbox.exec("npm install");
372
+
373
+ // Start dev server
374
+ const devServer = await sandbox.startProcess("npm run dev");
375
+
376
+ // Expose the dev server
377
+ const preview = await sandbox.exposePort(3000, { name: "dev-server" });
378
+
379
+ // Make changes and see them live!
380
+ await sandbox.writeFile("/src/App.jsx", updatedCode);
381
+ ```
382
+
383
+ ### Expose Services with Preview URLs
384
+
385
+ ```typescript
386
+ // Create and start a web server
387
+ await sandbox.writeFile(
388
+ "/server.js",
389
+ `Bun.serve({
390
+ port: 8080,
391
+ fetch(req) {
392
+ return new Response("Hello from sandbox!");
393
+ }
394
+ });`
395
+ );
396
+
397
+ const server = await sandbox.startProcess("bun run /server.js");
398
+
399
+ // Expose the port - returns a public URL
400
+ const preview = await sandbox.exposePort(8080);
401
+ console.log(`Service available at: ${preview.url}`);
402
+
403
+ // Note: Your Worker needs to handle preview URL routing.
404
+ // See the example in examples/basic/src/index.ts for the routing implementation.
405
+ ```
406
+
407
+ <h2 id="architecture">🏗️ Architecture</h2>
408
+
409
+ The SDK leverages Cloudflare's infrastructure:
410
+
411
+ - **Durable Objects**: Manages sandbox lifecycle and state
412
+ - **Containers**: Provides isolated execution environments
413
+ - **Workers**: Handles HTTP routing and API interface
414
+ - **Edge Network**: Enables global distribution and low latency
415
+
416
+ <h2 id="advanced-usage">🛠️ Advanced Usage</h2>
417
+
418
+ ### AsyncIterable Streaming Support
419
+
420
+ The SDK provides powerful streaming capabilities with typed AsyncIterable support:
421
+
422
+ ```typescript
423
+ import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
424
+
425
+ // Stream command execution
426
+ const stream = await sandbox.execStream('npm run build');
427
+ for await (const event of parseSSEStream<ExecEvent>(stream)) {
428
+ switch (event.type) {
429
+ case 'start':
430
+ console.log(`Build started: ${event.command}`);
431
+ break;
432
+ case 'stdout':
433
+ console.log(`Build: ${event.data}`);
434
+ break;
435
+ case 'complete':
436
+ console.log(`Exit code: ${event.exitCode}`);
437
+ break;
438
+ case 'error':
439
+ console.error(`Error: ${event.error}`);
440
+ break;
441
+ }
442
+ }
443
+ ```
444
+
445
+ #### Streaming Utilities
446
+
447
+ The SDK exports utilities for working with Server-Sent Event streams:
448
+
449
+ - **`parseSSEStream<T>(stream)`** - Convert ReadableStream to typed AsyncIterable
450
+ - **`responseToAsyncIterable<T>(response)`** - Convert SSE Response to AsyncIterable
451
+ - **`asyncIterableToSSEStream<T>(iterable)`** - Convert AsyncIterable back to SSE stream
452
+
453
+ #### Advanced Streaming Examples
454
+
455
+ **CI/CD Build System:**
456
+ ```typescript
457
+ export async function runBuild(env: Env, buildId: string) {
458
+ const sandbox = getSandbox(env.SANDBOX, buildId);
459
+ const stream = await sandbox.execStream('npm run build');
460
+
461
+ for await (const event of parseSSEStream<ExecEvent>(stream)) {
462
+ switch (event.type) {
463
+ case 'start':
464
+ await env.BUILDS.put(buildId, { status: 'running' });
465
+ break;
466
+ case 'complete':
467
+ await env.BUILDS.put(buildId, {
468
+ status: event.exitCode === 0 ? 'success' : 'failed',
469
+ exitCode: event.exitCode
470
+ });
471
+ break;
472
+ }
473
+ }
474
+ }
475
+ ```
476
+
477
+ **System Monitoring:**
478
+ ```typescript
479
+ const monitor = await sandbox.startProcess('tail -f /var/log/system.log');
480
+ const logStream = await sandbox.streamProcessLogs(monitor.id);
481
+
482
+ for await (const log of parseSSEStream<LogEvent>(logStream)) {
483
+ if (log.type === 'stdout' && log.data.includes('ERROR')) {
484
+ await env.ALERTS.send({
485
+ severity: 'high',
486
+ message: log.data,
487
+ timestamp: log.timestamp
488
+ });
489
+ }
490
+ }
491
+ ```
492
+
493
+ ### Session Management
494
+
495
+ Maintain context across commands:
496
+
497
+ ```typescript
498
+ const sessionId = crypto.randomUUID();
499
+
500
+ // Commands in the same session share working directory
501
+ await sandbox.exec("cd /app", { sessionId });
502
+ await sandbox.exec("npm install", { sessionId });
503
+ const app = await sandbox.startProcess("npm start", { sessionId });
504
+ ```
505
+
506
+ <h2 id="debugging">🔍 Debugging</h2>
507
+
508
+ Enable verbose logging:
509
+
510
+ ```typescript
511
+ const sandbox = getSandbox(env.Sandbox, "debug-sandbox");
512
+ sandbox.client.onCommandStart = (cmd, args) =>
513
+ console.log(`Starting: ${cmd} ${args.join(" ")}`);
514
+ sandbox.client.onOutput = (stream, data) => console.log(`[${stream}] ${data}`);
515
+ sandbox.client.onCommandComplete = (success, code) =>
516
+ console.log(`Completed: ${success} (${code})`);
517
+ ```
518
+
519
+ <h2 id="known-limitations">🚧 Known Limitations</h2>
520
+
521
+ - Maximum container runtime is limited by Durable Object constraints
522
+ - WebSocket support for preview URLs coming soon
523
+ - Some system calls may be restricted in the container environment
524
+
525
+ <h2 id="contributing">🤝 Contributing</h2>
526
+
527
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
528
+
529
+ ```bash
530
+ # Clone the repo
531
+ git clone https://github.com/cloudflare/sandbox-sdk
532
+ cd sandbox-sdk
533
+
534
+ # Install dependencies
535
+ npm install
536
+
537
+ # Run tests
538
+ npm test
539
+
540
+ # Build the project
541
+ npm run build
542
+ ```
543
+
544
+ <h2 id="license">📄 License</h2>
545
+
546
+ [MIT License](LICENSE)
547
+
548
+ <h2 id="acknowledgments">🙌 Acknowledgments</h2>
549
+
550
+ Built with ❤️ by the Cloudflare team. Special thanks to all early adopters and contributors.
551
+
552
+ ---
56
553
 
57
- - `exec(command: string, args: string[], options?: { stream?: boolean })`: Execute a command in the sandbox.
58
- - `gitCheckout(repoUrl: string, options: { branch?: string; targetDir?: string; stream?: boolean })`: Checkout a git repository in the sandbox.
59
- - `mkdir(path: string, options: { recursive?: boolean; stream?: boolean })`: Create a directory in the sandbox.
60
- - `writeFile(path: string, content: string, options: { encoding?: string; stream?: boolean })`: Write content to a file in the sandbox.
61
- - `readFile(path: string, options: { encoding?: string; stream?: boolean })`: Read content from a file in the sandbox.
62
- - `deleteFile(path: string, options?: { stream?: boolean })`: Delete a file from the sandbox.
63
- - `renameFile(oldPath: string, newPath: string, options?: { stream?: boolean })`: Rename a file in the sandbox.
64
- - `moveFile(sourcePath: string, destinationPath: string, options?: { stream?: boolean })`: Move a file from one location to another in the sandbox.
65
- - `ping()`: Ping the sandbox.
554
+ <div align="center">
555
+ <p>
556
+ <a href="https://github.com/cloudflare/sandbox-sdk/issues">Issues</a>
557
+ <a href="https://discord.gg/cloudflaredev">Discord</a>
558
+ <a href="https://twitter.com/CloudflareDev">Twitter</a>
559
+ </p>
560
+ </div>