@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.
- package/CHANGELOG.md +314 -0
- package/Dockerfile +179 -69
- package/LICENSE +176 -0
- package/README.md +119 -315
- package/dist/index.d.ts +1953 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3280 -0
- package/dist/index.js.map +1 -0
- package/package.json +16 -7
- package/src/clients/base-client.ts +295 -0
- package/src/clients/command-client.ts +115 -0
- package/src/clients/file-client.ts +300 -0
- package/src/clients/git-client.ts +98 -0
- package/src/clients/index.ts +64 -0
- package/src/clients/interpreter-client.ts +333 -0
- package/src/clients/port-client.ts +105 -0
- package/src/clients/process-client.ts +180 -0
- package/src/clients/sandbox-client.ts +39 -0
- package/src/clients/types.ts +88 -0
- package/src/clients/utility-client.ts +156 -0
- package/src/errors/adapter.ts +238 -0
- package/src/errors/classes.ts +594 -0
- package/src/errors/index.ts +109 -0
- package/src/file-stream.ts +169 -0
- package/src/index.ts +98 -14
- package/src/interpreter.ts +168 -0
- package/src/request-handler.ts +94 -55
- package/src/sandbox.ts +938 -315
- package/src/security.ts +34 -28
- package/src/sse-parser.ts +8 -11
- package/src/version.ts +6 -0
- package/startup.sh +3 -0
- package/tests/base-client.test.ts +364 -0
- package/tests/command-client.test.ts +444 -0
- package/tests/file-client.test.ts +831 -0
- package/tests/file-stream.test.ts +310 -0
- package/tests/get-sandbox.test.ts +149 -0
- package/tests/git-client.test.ts +487 -0
- package/tests/port-client.test.ts +293 -0
- package/tests/process-client.test.ts +683 -0
- package/tests/request-handler.test.ts +292 -0
- package/tests/sandbox.test.ts +739 -0
- package/tests/sse-parser.test.ts +291 -0
- package/tests/utility-client.test.ts +339 -0
- package/tests/version.test.ts +16 -0
- package/tests/wrangler.jsonc +35 -0
- package/tsconfig.json +9 -1
- package/tsdown.config.ts +12 -0
- package/vitest.config.ts +31 -0
- package/container_src/handler/exec.ts +0 -337
- package/container_src/handler/file.ts +0 -844
- package/container_src/handler/git.ts +0 -182
- package/container_src/handler/ports.ts +0 -314
- package/container_src/handler/process.ts +0 -640
- package/container_src/index.ts +0 -361
- package/container_src/package.json +0 -9
- package/container_src/types.ts +0 -103
- package/src/client.ts +0 -1038
- 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
|
-
|
|
1
|
+
<img width="1362" height="450" alt="Image" src="https://github.com/user-attachments/assets/6f770ae3-0a14-4d2b-9aed-a304ee5446c5" />
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Cloudflare Sandbox SDK
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@cloudflare/sandbox)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Build secure, isolated code execution environments on Cloudflare.**
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
export { Sandbox } from "@cloudflare/sandbox";
|
|
40
|
-
```
|
|
13
|
+
## Getting Started
|
|
41
14
|
|
|
42
|
-
|
|
15
|
+
### Prerequisites
|
|
43
16
|
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
Create a new Sandbox SDK project using the minimal template:
|
|
57
24
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
25
|
+
```bash
|
|
26
|
+
npm create cloudflare@latest -- my-sandbox --template=cloudflare/sandbox-sdk/examples/minimal
|
|
27
|
+
cd my-sandbox
|
|
28
|
+
```
|
|
61
29
|
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- `getExposedPorts(hostname: string)`: List all exposed ports with their preview URLs.
|
|
34
|
+
```bash
|
|
35
|
+
npm run dev
|
|
36
|
+
```
|
|
83
37
|
|
|
84
|
-
|
|
38
|
+
> **Note:** First run builds the Docker container (2-3 minutes). Subsequent runs are much faster.
|
|
85
39
|
|
|
86
|
-
|
|
40
|
+
Test the endpoints:
|
|
87
41
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
116
|
-
|
|
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
|
-
|
|
50
|
+
### 3. Deploy to production
|
|
137
51
|
|
|
138
|
-
|
|
52
|
+
Deploy your Worker and container:
|
|
139
53
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
54
|
+
```bash
|
|
55
|
+
npx wrangler deploy
|
|
56
|
+
```
|
|
143
57
|
|
|
144
|
-
|
|
58
|
+
> **Wait for provisioning:** After first deployment, wait 2-3 minutes before making requests.
|
|
145
59
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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 {
|
|
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
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
100
|
+
## Documentation
|
|
247
101
|
|
|
248
|
-
|
|
102
|
+
**📖 [Full Documentation](https://developers.cloudflare.com/sandbox/)**
|
|
249
103
|
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
254
|
-
import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
|
|
109
|
+
## Key Features
|
|
255
110
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
263
|
-
Converts a `Response` object with SSE content directly to `AsyncIterable<T>`. Useful when fetching from external SSE endpoints.
|
|
119
|
+
## Contributing
|
|
264
120
|
|
|
265
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
});
|
|
123
|
+
- Setting up your development environment
|
|
124
|
+
- Creating pull requests
|
|
125
|
+
- Code style and testing requirements
|
|
272
126
|
|
|
273
|
-
|
|
274
|
-
for await (const event of responseToAsyncIterable<LogEvent>(response)) {
|
|
275
|
-
console.log(`[${event.type}] ${event.data}`);
|
|
276
|
-
}
|
|
277
|
-
```
|
|
127
|
+
## Development
|
|
278
128
|
|
|
279
|
-
|
|
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
|
-
```
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
301
|
-
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
338
|
-
|
|
145
|
+
# Type checking and linting
|
|
146
|
+
npm run check
|
|
339
147
|
```
|
|
340
148
|
|
|
341
|
-
|
|
149
|
+
## Examples
|
|
342
150
|
|
|
343
|
-
|
|
151
|
+
See the [examples directory](./examples) for complete working examples:
|
|
344
152
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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)
|