@cloudflare/sandbox 0.0.0-cecde0a → 0.0.0-d4bb3b7

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +314 -0
  2. package/Dockerfile +179 -69
  3. package/LICENSE +176 -0
  4. package/README.md +119 -315
  5. package/dist/index.d.ts +1953 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +3280 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +16 -7
  10. package/src/clients/base-client.ts +295 -0
  11. package/src/clients/command-client.ts +115 -0
  12. package/src/clients/file-client.ts +300 -0
  13. package/src/clients/git-client.ts +98 -0
  14. package/src/clients/index.ts +64 -0
  15. package/src/clients/interpreter-client.ts +333 -0
  16. package/src/clients/port-client.ts +105 -0
  17. package/src/clients/process-client.ts +180 -0
  18. package/src/clients/sandbox-client.ts +39 -0
  19. package/src/clients/types.ts +88 -0
  20. package/src/clients/utility-client.ts +156 -0
  21. package/src/errors/adapter.ts +238 -0
  22. package/src/errors/classes.ts +594 -0
  23. package/src/errors/index.ts +109 -0
  24. package/src/file-stream.ts +169 -0
  25. package/src/index.ts +98 -14
  26. package/src/interpreter.ts +168 -0
  27. package/src/request-handler.ts +94 -55
  28. package/src/sandbox.ts +938 -315
  29. package/src/security.ts +34 -28
  30. package/src/sse-parser.ts +8 -11
  31. package/src/version.ts +6 -0
  32. package/startup.sh +3 -0
  33. package/tests/base-client.test.ts +364 -0
  34. package/tests/command-client.test.ts +444 -0
  35. package/tests/file-client.test.ts +831 -0
  36. package/tests/file-stream.test.ts +310 -0
  37. package/tests/get-sandbox.test.ts +149 -0
  38. package/tests/git-client.test.ts +487 -0
  39. package/tests/port-client.test.ts +293 -0
  40. package/tests/process-client.test.ts +683 -0
  41. package/tests/request-handler.test.ts +292 -0
  42. package/tests/sandbox.test.ts +739 -0
  43. package/tests/sse-parser.test.ts +291 -0
  44. package/tests/utility-client.test.ts +339 -0
  45. package/tests/version.test.ts +16 -0
  46. package/tests/wrangler.jsonc +35 -0
  47. package/tsconfig.json +9 -1
  48. package/tsdown.config.ts +12 -0
  49. package/vitest.config.ts +31 -0
  50. package/container_src/handler/exec.ts +0 -337
  51. package/container_src/handler/file.ts +0 -844
  52. package/container_src/handler/git.ts +0 -182
  53. package/container_src/handler/ports.ts +0 -314
  54. package/container_src/handler/process.ts +0 -640
  55. package/container_src/index.ts +0 -361
  56. package/container_src/package.json +0 -9
  57. package/container_src/types.ts +0 -103
  58. package/src/client.ts +0 -1038
  59. package/src/types.ts +0 -386
package/LICENSE ADDED
@@ -0,0 +1,176 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
package/README.md CHANGED
@@ -1,365 +1,169 @@
1
- ## @cloudflare/sandbox
1
+ <img width="1362" height="450" alt="Image" src="https://github.com/user-attachments/assets/6f770ae3-0a14-4d2b-9aed-a304ee5446c5" />
2
2
 
3
- > **⚠️ Experimental** - This library is currently experimental and we're actively seeking feedback. Please try it out and let us know what you think!
3
+ # Cloudflare Sandbox SDK
4
4
 
5
- A library to spin up a sandboxed environment.
5
+ [![npm version](https://img.shields.io/npm/v/@cloudflare/sandbox.svg)](https://www.npmjs.com/package/@cloudflare/sandbox)
6
6
 
7
- First, setup your wrangler.json to use the sandbox:
7
+ **Build secure, isolated code execution environments on Cloudflare.**
8
8
 
9
- ```jsonc
10
- {
11
- // ...
12
- "containers": [
13
- {
14
- "class_name": "Sandbox",
15
- "image": "./node_modules/@cloudflare/sandbox/Dockerfile",
16
- "name": "sandbox"
17
- }
18
- ],
19
- "durable_objects": {
20
- "bindings": [
21
- {
22
- "class_name": "Sandbox",
23
- "name": "Sandbox"
24
- }
25
- ]
26
- },
27
- "migrations": [
28
- {
29
- "new_sqlite_classes": ["Sandbox"],
30
- "tag": "v1"
31
- }
32
- ]
33
- }
34
- ```
9
+ The Sandbox SDK lets you run untrusted code safely in isolated containers. Execute commands, manage files, run background processes, and expose services — all from your Workers applications.
35
10
 
36
- Then, export the Sandbox class in your worker:
11
+ Perfect for AI code execution, interactive development environments, data analysis platforms, CI/CD systems, and any application that needs secure code execution at the edge.
37
12
 
38
- ```ts
39
- export { Sandbox } from "@cloudflare/sandbox";
40
- ```
13
+ ## Getting Started
41
14
 
42
- You can then use the Sandbox class in your worker:
15
+ ### Prerequisites
43
16
 
44
- ```ts
45
- import { getSandbox } from "@cloudflare/sandbox";
17
+ 1. Install [Node.js](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) (version 16.17.0 or later)
18
+ 2. Ensure Docker is running locally
19
+ 3. For deploying to production, sign up for a [Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages)
46
20
 
47
- export default {
48
- async fetch(request: Request, env: Env) {
49
- const sandbox = getSandbox(env.Sandbox, "my-sandbox");
50
- const result = await sandbox.exec("ls -la");
51
- return Response.json(result);
52
- },
53
- };
54
- ```
21
+ ### 1. Create a new project
55
22
 
56
- ### Core Methods
23
+ Create a new Sandbox SDK project using the minimal template:
57
24
 
58
- #### Command Execution
59
- - `exec(command: string, options?: ExecOptions)`: Execute a command and return the complete result.
60
- - `execStream(command: string, options?: StreamOptions)`: Execute a command with real-time streaming (returns ReadableStream).
25
+ ```bash
26
+ npm create cloudflare@latest -- my-sandbox --template=cloudflare/sandbox-sdk/examples/minimal
27
+ cd my-sandbox
28
+ ```
61
29
 
62
- #### Process Management
63
- - `startProcess(command: string, options?: ProcessOptions)`: Start a background process.
64
- - `listProcesses()`: List all running processes.
65
- - `getProcess(id: string)`: Get details of a specific process.
66
- - `killProcess(id: string, signal?: string)`: Kill a specific process.
67
- - `killAllProcesses()`: Kill all running processes.
68
- - `streamProcessLogs(processId: string, options?: { signal?: AbortSignal })`: Stream logs from a running process (returns ReadableStream).
30
+ ### 2. Test locally
69
31
 
70
- #### File Operations
71
- - `gitCheckout(repoUrl: string, options: { branch?: string; targetDir?: string })`: Checkout a git repository.
72
- - `mkdir(path: string, options?: { recursive?: boolean })`: Create a directory.
73
- - `writeFile(path: string, content: string, options?: { encoding?: string })`: Write content to a file.
74
- - `readFile(path: string, options?: { encoding?: string })`: Read content from a file.
75
- - `deleteFile(path: string)`: Delete a file.
76
- - `renameFile(oldPath: string, newPath: string)`: Rename a file.
77
- - `moveFile(sourcePath: string, destinationPath: string)`: Move a file.
32
+ Start the development server:
78
33
 
79
- #### Port Management
80
- - `exposePort(port: number, options: { name?: string; hostname: string })`: Expose a port for external access.
81
- - `unexposePort(port: number)`: Unexpose a previously exposed port.
82
- - `getExposedPorts(hostname: string)`: List all exposed ports with their preview URLs.
34
+ ```bash
35
+ npm run dev
36
+ ```
83
37
 
84
- ### Beautiful AsyncIterable Streaming APIs
38
+ > **Note:** First run builds the Docker container (2-3 minutes). Subsequent runs are much faster.
85
39
 
86
- The SDK provides streaming methods that return `ReadableStream` for RPC compatibility, along with a `parseSSEStream` utility to convert them to typed AsyncIterables:
40
+ Test the endpoints:
87
41
 
88
- #### Stream Command Output
89
- ```typescript
90
- import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
91
-
92
- // Get the stream and convert to AsyncIterable
93
- const stream = await sandbox.execStream('npm run build');
94
- for await (const event of parseSSEStream<ExecEvent>(stream)) {
95
- switch (event.type) {
96
- case 'start':
97
- console.log(`Build started: ${event.command}`);
98
- break;
99
- case 'stdout':
100
- console.log(`[OUT] ${event.data}`);
101
- break;
102
- case 'stderr':
103
- console.error(`[ERR] ${event.data}`);
104
- break;
105
- case 'complete':
106
- console.log(`Build finished with exit code: ${event.exitCode}`);
107
- break;
108
- case 'error':
109
- console.error(`Build error: ${event.error}`);
110
- break;
111
- }
112
- }
113
- ```
42
+ ```bash
43
+ # Execute Python code
44
+ curl http://localhost:8787/run
114
45
 
115
- #### Stream Process Logs
116
- ```typescript
117
- import { parseSSEStream, type LogEvent } from '@cloudflare/sandbox';
118
-
119
- // Monitor background process logs
120
- const webServer = await sandbox.startProcess('node server.js');
121
-
122
- const logStream = await sandbox.streamProcessLogs(webServer.id);
123
- for await (const log of parseSSEStream<LogEvent>(logStream)) {
124
- if (log.type === 'stdout') {
125
- console.log(`Server: ${log.data}`);
126
- } else if (log.type === 'stderr' && log.data.includes('ERROR')) {
127
- // React to errors
128
- await handleError(log);
129
- } else if (log.type === 'exit') {
130
- console.log(`Server exited with code: ${log.exitCode}`);
131
- break;
132
- }
133
- }
46
+ # File operations
47
+ curl http://localhost:8787/file
134
48
  ```
135
49
 
136
- #### Why parseSSEStream?
50
+ ### 3. Deploy to production
137
51
 
138
- The streaming methods return `ReadableStream<Uint8Array>` to ensure compatibility across Durable Object RPC boundaries. The `parseSSEStream` utility converts these streams into typed AsyncIterables, giving you the best of both worlds:
52
+ Deploy your Worker and container:
139
53
 
140
- - **RPC Compatibility**: ReadableStream can be serialized across process boundaries
141
- - **Beautiful APIs**: AsyncIterable provides clean `for await` syntax with typed events
142
- - **Type Safety**: Full TypeScript support with `ExecEvent` and `LogEvent` types
54
+ ```bash
55
+ npx wrangler deploy
56
+ ```
143
57
 
144
- #### Advanced Examples
58
+ > **Wait for provisioning:** After first deployment, wait 2-3 minutes before making requests.
145
59
 
146
- ##### CI/CD Build System
147
- ```typescript
148
- import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
149
-
150
- export async function runBuild(env: Env, buildId: string) {
151
- const sandbox = getSandbox(env.Sandbox, buildId);
152
- const buildLog: string[] = [];
153
-
154
- try {
155
- const stream = await sandbox.execStream('npm run build');
156
- for await (const event of parseSSEStream<ExecEvent>(stream)) {
157
- buildLog.push(`[${event.type}] ${event.data || ''}`);
158
-
159
- if (event.type === 'complete') {
160
- await env.BUILDS.put(buildId, {
161
- status: event.exitCode === 0 ? 'success' : 'failed',
162
- exitCode: event.exitCode,
163
- logs: buildLog.join('\n'),
164
- duration: Date.now() - new Date(event.timestamp).getTime()
165
- });
166
- }
167
- }
168
- } catch (error) {
169
- await env.BUILDS.put(buildId, {
170
- status: 'error',
171
- error: error.message,
172
- logs: buildLog.join('\n')
173
- });
174
- }
175
- }
176
- ```
60
+ **📖 [View the complete getting started guide](https://developers.cloudflare.com/sandbox/get-started/)** for detailed instructions and explanations.
61
+
62
+ ## Quick API Example
177
63
 
178
- ##### System Monitoring
179
64
  ```typescript
180
- import { parseSSEStream, type LogEvent } from '@cloudflare/sandbox';
65
+ import { getSandbox, proxyToSandbox, type Sandbox } from '@cloudflare/sandbox';
66
+
67
+ export { Sandbox } from '@cloudflare/sandbox';
68
+
69
+ type Env = {
70
+ Sandbox: DurableObjectNamespace<Sandbox>;
71
+ };
181
72
 
182
73
  export default {
183
- async scheduled(controller: ScheduledController, env: Env) {
184
- const sandbox = getSandbox(env.Sandbox, 'monitor');
185
-
186
- // Monitor system logs
187
- const monitor = await sandbox.startProcess('journalctl -f');
188
-
189
- const logStream = await sandbox.streamProcessLogs(monitor.id);
190
- for await (const log of parseSSEStream<LogEvent>(logStream)) {
191
- if (log.type === 'stdout') {
192
- // Check for critical errors
193
- if (log.data.includes('CRITICAL')) {
194
- await env.ALERTS.send({
195
- severity: 'critical',
196
- message: log.data,
197
- timestamp: log.timestamp
198
- });
199
- }
200
-
201
- // Store logs
202
- await env.LOGS.put(`${log.timestamp}-${monitor.id}`, log.data);
203
- }
74
+ async fetch(request: Request, env: Env): Promise<Response> {
75
+ // Required for preview URLs
76
+ const proxyResponse = await proxyToSandbox(request, env);
77
+ if (proxyResponse) return proxyResponse;
78
+
79
+ const url = new URL(request.url);
80
+ const sandbox = getSandbox(env.Sandbox, 'my-sandbox');
81
+
82
+ // Execute Python code
83
+ if (url.pathname === '/run') {
84
+ const result = await sandbox.exec('python3 -c "print(2 + 2)"');
85
+ return Response.json({ output: result.stdout, success: result.success });
204
86
  }
205
- }
206
- }
207
- ```
208
87
 
209
- ##### Streaming to Frontend via SSE
210
- ```typescript
211
- // Worker endpoint that streams to frontend
212
- app.get('/api/build/:id/stream', async (req, env) => {
213
- const sandbox = getSandbox(env.Sandbox, req.params.id);
214
- const encoder = new TextEncoder();
215
-
216
- return new Response(
217
- new ReadableStream({
218
- async start(controller) {
219
- try {
220
- for await (const event of sandbox.execStream('npm run build')) {
221
- // Forward events to frontend as SSE
222
- const sseEvent = `data: ${JSON.stringify(event)}\n\n`;
223
- controller.enqueue(encoder.encode(sseEvent));
224
- }
225
- } catch (error) {
226
- const errorEvent = `data: ${JSON.stringify({
227
- type: 'error',
228
- error: error.message
229
- })}\n\n`;
230
- controller.enqueue(encoder.encode(errorEvent));
231
- } finally {
232
- controller.close();
233
- }
234
- }
235
- }),
236
- {
237
- headers: {
238
- 'Content-Type': 'text/event-stream',
239
- 'Cache-Control': 'no-cache'
240
- }
88
+ // Work with files
89
+ if (url.pathname === '/file') {
90
+ await sandbox.writeFile('/workspace/hello.txt', 'Hello, Sandbox!');
91
+ const file = await sandbox.readFile('/workspace/hello.txt');
92
+ return Response.json({ content: file.content });
241
93
  }
242
- );
243
- });
94
+
95
+ return new Response('Try /run or /file');
96
+ }
97
+ };
244
98
  ```
245
99
 
246
- ### Streaming Utilities
100
+ ## Documentation
247
101
 
248
- The SDK exports additional utilities for working with SSE streams:
102
+ **📖 [Full Documentation](https://developers.cloudflare.com/sandbox/)**
249
103
 
250
- #### `parseSSEStream`
251
- Converts a `ReadableStream<Uint8Array>` (from SSE endpoints) into a typed `AsyncIterable<T>`. This is the primary utility for consuming streams from the SDK.
104
+ - [Get Started Guide](https://developers.cloudflare.com/sandbox/get-started/) - Step-by-step tutorial
105
+ - [API Reference](https://developers.cloudflare.com/sandbox/api/) - Complete API docs
106
+ - [Guides](https://developers.cloudflare.com/sandbox/guides/) - Execute commands, manage files, expose services
107
+ - [Examples](https://developers.cloudflare.com/sandbox/tutorials/) - AI agents, data analysis, CI/CD pipelines
252
108
 
253
- ```typescript
254
- import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
109
+ ## Key Features
255
110
 
256
- const stream = await sandbox.execStream('npm build');
257
- for await (const event of parseSSEStream<ExecEvent>(stream)) {
258
- console.log(event);
259
- }
260
- ```
111
+ - **Secure Isolation** - Each sandbox runs in its own container
112
+ - **Edge-Native** - Runs on Cloudflare's global network
113
+ - **Code Interpreter** - Execute Python and JavaScript with rich outputs
114
+ - **File System Access** - Read, write, and manage files
115
+ - **Command Execution** - Run any command with streaming support
116
+ - **Preview URLs** - Expose services with public URLs
117
+ - **Git Integration** - Clone repositories directly
261
118
 
262
- #### `responseToAsyncIterable`
263
- Converts a `Response` object with SSE content directly to `AsyncIterable<T>`. Useful when fetching from external SSE endpoints.
119
+ ## Contributing
264
120
 
265
- ```typescript
266
- import { responseToAsyncIterable, type LogEvent } from '@cloudflare/sandbox';
121
+ We welcome contributions from the community! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on:
267
122
 
268
- // Fetch from an external SSE endpoint
269
- const response = await fetch('https://api.example.com/logs/stream', {
270
- headers: { 'Accept': 'text/event-stream' }
271
- });
123
+ - Setting up your development environment
124
+ - Creating pull requests
125
+ - Code style and testing requirements
272
126
 
273
- // Convert Response to typed AsyncIterable
274
- for await (const event of responseToAsyncIterable<LogEvent>(response)) {
275
- console.log(`[${event.type}] ${event.data}`);
276
- }
277
- ```
127
+ ## Development
278
128
 
279
- #### `asyncIterableToSSEStream`
280
- Converts an `AsyncIterable<T>` into an SSE-formatted `ReadableStream<Uint8Array>`. Perfect for Worker endpoints that need to transform or filter events before sending to clients.
129
+ This repository contains the SDK source code. Quick start:
281
130
 
282
- ```typescript
283
- import { getSandbox, parseSSEStream, asyncIterableToSSEStream, type LogEvent } from '@cloudflare/sandbox';
284
-
285
- export async function handleFilteredLogs(request: Request, env: Env) {
286
- const sandbox = getSandbox(env.SANDBOX);
287
-
288
- // Custom async generator that filters logs
289
- async function* filterLogs() {
290
- const stream = await sandbox.streamProcessLogs('web-server');
291
-
292
- for await (const log of parseSSEStream<LogEvent>(stream)) {
293
- // Only forward error logs to the client
294
- if (log.type === 'stderr' || log.data.includes('ERROR')) {
295
- yield log;
296
- }
297
- }
298
- }
131
+ ```bash
132
+ # Clone the repo
133
+ git clone https://github.com/cloudflare/sandbox-sdk
134
+ cd sandbox-sdk
299
135
 
300
- // Convert filtered AsyncIterable back to SSE stream for the response
301
- const sseStream = asyncIterableToSSEStream(filterLogs());
302
-
303
- return new Response(sseStream, {
304
- headers: {
305
- 'Content-Type': 'text/event-stream',
306
- 'Cache-Control': 'no-cache',
307
- }
308
- });
309
- }
310
- ```
136
+ # Install dependencies
137
+ npm install
311
138
 
312
- **Advanced Example - Merging Multiple Streams:**
313
- ```typescript
314
- async function* mergeBuilds(env: Env) {
315
- const sandbox1 = getSandbox(env.SANDBOX1);
316
- const sandbox2 = getSandbox(env.SANDBOX2);
317
-
318
- // Start builds in parallel
319
- const [stream1, stream2] = await Promise.all([
320
- sandbox1.execStream('npm run build:frontend'),
321
- sandbox2.execStream('npm run build:backend')
322
- ]);
323
-
324
- // Parse and merge events
325
- const frontend = parseSSEStream<ExecEvent>(stream1);
326
- const backend = parseSSEStream<ExecEvent>(stream2);
327
-
328
- // Merge with source identification
329
- for await (const event of frontend) {
330
- yield { ...event, source: 'frontend' };
331
- }
332
- for await (const event of backend) {
333
- yield { ...event, source: 'backend' };
334
- }
335
- }
139
+ # Run tests
140
+ npm test
141
+
142
+ # Build the project
143
+ npm run build
336
144
 
337
- // Convert merged stream to SSE for client
338
- const mergedSSE = asyncIterableToSSEStream(mergeBuilds(env));
145
+ # Type checking and linting
146
+ npm run check
339
147
  ```
340
148
 
341
- ### Cancellation Support
149
+ ## Examples
342
150
 
343
- Both streaming methods support cancellation via AbortSignal:
151
+ See the [examples directory](./examples) for complete working examples:
344
152
 
345
- ```typescript
346
- const controller = new AbortController();
347
-
348
- // Cancel after 30 seconds
349
- setTimeout(() => controller.abort(), 30000);
350
-
351
- try {
352
- for await (const event of sandbox.execStream('long-running-task', {
353
- signal: controller.signal
354
- })) {
355
- // Process events
356
- if (shouldCancel(event)) {
357
- controller.abort();
358
- }
359
- }
360
- } catch (error) {
361
- if (error.message.includes('aborted')) {
362
- console.log('Operation cancelled');
363
- }
364
- }
365
- ```
153
+ - [Minimal](./examples/minimal) - Basic sandbox setup
154
+ - [Code Interpreter](./examples/code-interpreter) - Use sandbox as an interpreter tool with gpt-oss
155
+
156
+ ## Status
157
+
158
+ **Beta** - The SDK is in active development. APIs may change before v1.0.
159
+
160
+ ## License
161
+
162
+ [Apache License 2.0](LICENSE)
163
+
164
+ ## Links
165
+
166
+ - [Documentation](https://developers.cloudflare.com/sandbox/)
167
+ - [GitHub Issues](https://github.com/cloudflare/sandbox-sdk/issues)
168
+ - [Developer Discord](https://discord.cloudflare.com)
169
+ - [Cloudflare Developers](https://twitter.com/CloudflareDev)