sbox-sdk 0.0.2
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/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/adapter/index.d.ts +22 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +16 -0
- package/dist/agent-tools/index.d.ts +13 -0
- package/dist/agent-tools/index.d.ts.map +1 -0
- package/dist/agent-tools/index.js +9 -0
- package/dist/agent-tools/policy.d.ts +48 -0
- package/dist/agent-tools/policy.d.ts.map +1 -0
- package/dist/agent-tools/policy.js +51 -0
- package/dist/agent-tools/registry.d.ts +9 -0
- package/dist/agent-tools/registry.d.ts.map +1 -0
- package/dist/agent-tools/registry.js +412 -0
- package/dist/agent-tools/result.d.ts +32 -0
- package/dist/agent-tools/result.d.ts.map +1 -0
- package/dist/agent-tools/result.js +14 -0
- package/dist/agent-tools/types.d.ts +76 -0
- package/dist/agent-tools/types.d.ts.map +1 -0
- package/dist/agent-tools/types.js +1 -0
- package/dist/ai/index.d.ts +36 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +40 -0
- package/dist/ai-sdk/index.d.ts +31 -0
- package/dist/ai-sdk/index.d.ts.map +1 -0
- package/dist/ai-sdk/index.js +80 -0
- package/dist/anthropic/index.d.ts +42 -0
- package/dist/anthropic/index.d.ts.map +1 -0
- package/dist/anthropic/index.js +64 -0
- package/dist/aws-lambda/index.d.ts +87 -0
- package/dist/aws-lambda/index.d.ts.map +1 -0
- package/dist/aws-lambda/index.js +290 -0
- package/dist/beam/index.d.ts +92 -0
- package/dist/beam/index.d.ts.map +1 -0
- package/dist/beam/index.js +222 -0
- package/dist/blaxel/index.d.ts +125 -0
- package/dist/blaxel/index.d.ts.map +1 -0
- package/dist/blaxel/index.js +220 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +249 -0
- package/dist/cloudflare/index.d.ts +64 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +259 -0
- package/dist/codesandbox/index.d.ts +100 -0
- package/dist/codesandbox/index.d.ts.map +1 -0
- package/dist/codesandbox/index.js +227 -0
- package/dist/conformance/index.d.ts +20 -0
- package/dist/conformance/index.d.ts.map +1 -0
- package/dist/conformance/index.js +189 -0
- package/dist/daytona/index.d.ts +64 -0
- package/dist/daytona/index.d.ts.map +1 -0
- package/dist/daytona/index.js +258 -0
- package/dist/e2b/index.d.ts +63 -0
- package/dist/e2b/index.d.ts.map +1 -0
- package/dist/e2b/index.js +411 -0
- package/dist/fly/index.d.ts +75 -0
- package/dist/fly/index.d.ts.map +1 -0
- package/dist/fly/index.js +222 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/internal/capabilities.d.ts +57 -0
- package/dist/internal/capabilities.d.ts.map +1 -0
- package/dist/internal/capabilities.js +68 -0
- package/dist/internal/client.d.ts +9 -0
- package/dist/internal/client.d.ts.map +1 -0
- package/dist/internal/client.js +126 -0
- package/dist/internal/encoding.d.ts +8 -0
- package/dist/internal/encoding.d.ts.map +1 -0
- package/dist/internal/encoding.js +20 -0
- package/dist/internal/errors.d.ts +45 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +79 -0
- package/dist/internal/exec.d.ts +19 -0
- package/dist/internal/exec.d.ts.map +1 -0
- package/dist/internal/exec.js +208 -0
- package/dist/internal/plugin.d.ts +38 -0
- package/dist/internal/plugin.d.ts.map +1 -0
- package/dist/internal/plugin.js +1 -0
- package/dist/internal/runtime.d.ts +8 -0
- package/dist/internal/runtime.d.ts.map +1 -0
- package/dist/internal/runtime.js +21 -0
- package/dist/internal/sandbox.d.ts +12 -0
- package/dist/internal/sandbox.d.ts.map +1 -0
- package/dist/internal/sandbox.js +438 -0
- package/dist/internal/shell.d.ts +36 -0
- package/dist/internal/shell.d.ts.map +1 -0
- package/dist/internal/shell.js +88 -0
- package/dist/internal/stream.d.ts +15 -0
- package/dist/internal/stream.d.ts.map +1 -0
- package/dist/internal/stream.js +58 -0
- package/dist/internal/types.d.ts +381 -0
- package/dist/internal/types.d.ts.map +1 -0
- package/dist/internal/types.js +1 -0
- package/dist/langchain/index.d.ts +25 -0
- package/dist/langchain/index.d.ts.map +1 -0
- package/dist/langchain/index.js +61 -0
- package/dist/mastra/index.d.ts +43 -0
- package/dist/mastra/index.d.ts.map +1 -0
- package/dist/mastra/index.js +69 -0
- package/dist/memory/index.d.ts +57 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +573 -0
- package/dist/modal/index.d.ts +67 -0
- package/dist/modal/index.d.ts.map +1 -0
- package/dist/modal/index.js +223 -0
- package/dist/morph/index.d.ts +91 -0
- package/dist/morph/index.d.ts.map +1 -0
- package/dist/morph/index.js +221 -0
- package/dist/northflank/index.d.ts +74 -0
- package/dist/northflank/index.d.ts.map +1 -0
- package/dist/northflank/index.js +265 -0
- package/dist/openai/index.d.ts +25 -0
- package/dist/openai/index.d.ts.map +1 -0
- package/dist/openai/index.js +71 -0
- package/dist/railway/index.d.ts +109 -0
- package/dist/railway/index.d.ts.map +1 -0
- package/dist/railway/index.js +219 -0
- package/dist/runloop/index.d.ts +69 -0
- package/dist/runloop/index.d.ts.map +1 -0
- package/dist/runloop/index.js +226 -0
- package/dist/testing/index.d.ts +44 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +61 -0
- package/dist/vercel/index.d.ts +63 -0
- package/dist/vercel/index.d.ts.map +1 -0
- package/dist/vercel/index.js +241 -0
- package/package.json +252 -0
- package/src/aws-lambda/runner/Dockerfile +15 -0
- package/src/aws-lambda/runner/README.md +59 -0
- package/src/aws-lambda/runner/server.mjs +91 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `sbox` CLI — diagnostics + a one-shot runner for the unified sandbox SDK.
|
|
4
|
+
*
|
|
5
|
+
* Every command lazy-imports only the provider it touches (and the vendor SDK
|
|
6
|
+
* only when a sandbox is actually created), so the published `bin` never bundles
|
|
7
|
+
* all provider SDKs. `caps` and `doctor` are fully offline; `exec` and `list`
|
|
8
|
+
* talk to the real provider using credentials from the environment.
|
|
9
|
+
*/
|
|
10
|
+
import { createRequire } from "node:module";
|
|
11
|
+
/** Registry of the network-backed adapters the CLI can drive by id. */
|
|
12
|
+
const PROVIDERS = {
|
|
13
|
+
e2b: { caps: "E2B_CAPS", factory: "e2b", pkg: "@e2b/code-interpreter" },
|
|
14
|
+
vercel: { caps: "VERCEL_CAPS", factory: "vercel", pkg: "@vercel/sandbox" },
|
|
15
|
+
cloudflare: {
|
|
16
|
+
caps: "CLOUDFLARE_CAPS",
|
|
17
|
+
factory: "cloudflare",
|
|
18
|
+
pkg: "@cloudflare/sandbox",
|
|
19
|
+
},
|
|
20
|
+
daytona: { caps: "DAYTONA_CAPS", factory: "daytona", pkg: "@daytonaio/sdk" },
|
|
21
|
+
modal: { caps: "MODAL_CAPS", factory: "modal", pkg: "modal" },
|
|
22
|
+
fly: { caps: "FLY_CAPS", factory: "fly", pkg: null },
|
|
23
|
+
"aws-lambda": {
|
|
24
|
+
caps: "AWS_LAMBDA_CAPS",
|
|
25
|
+
factory: "awsLambda",
|
|
26
|
+
pkg: "@aws-sdk/client-lambda-microvms",
|
|
27
|
+
},
|
|
28
|
+
northflank: {
|
|
29
|
+
caps: "NORTHFLANK_CAPS",
|
|
30
|
+
factory: "northflank",
|
|
31
|
+
pkg: "@northflank/js-client",
|
|
32
|
+
},
|
|
33
|
+
runloop: {
|
|
34
|
+
caps: "RUNLOOP_CAPS",
|
|
35
|
+
factory: "runloop",
|
|
36
|
+
pkg: "@runloop/api-client",
|
|
37
|
+
},
|
|
38
|
+
codesandbox: {
|
|
39
|
+
caps: "CODESANDBOX_CAPS",
|
|
40
|
+
factory: "codesandbox",
|
|
41
|
+
pkg: "@codesandbox/sdk",
|
|
42
|
+
},
|
|
43
|
+
morph: { caps: "MORPH_CAPS", factory: "morph", pkg: "morphcloud" },
|
|
44
|
+
blaxel: { caps: "BLAXEL_CAPS", factory: "blaxel", pkg: "@blaxel/core" },
|
|
45
|
+
beam: { caps: "BEAM_CAPS", factory: "beam", pkg: "@beamcloud/beam-js" },
|
|
46
|
+
railway: { caps: "RAILWAY_CAPS", factory: "railway", pkg: "railway" },
|
|
47
|
+
};
|
|
48
|
+
const LEVEL_SYMBOL = {
|
|
49
|
+
emulated: "◐",
|
|
50
|
+
native: "●",
|
|
51
|
+
unsupported: "○",
|
|
52
|
+
};
|
|
53
|
+
function write(s) {
|
|
54
|
+
process.stdout.write(`${s}\n`);
|
|
55
|
+
}
|
|
56
|
+
function writeErr(s) {
|
|
57
|
+
process.stderr.write(`${s}\n`);
|
|
58
|
+
}
|
|
59
|
+
function unknownProvider(id) {
|
|
60
|
+
writeErr(`sbox: unknown provider '${id}'. Known: ${Object.keys(PROVIDERS).join(", ")}`);
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
/** Import a built adapter module by id (cheap — vendor SDK stays lazy). */
|
|
64
|
+
async function importProvider(id) {
|
|
65
|
+
return (await import(`./${id}/index.js`));
|
|
66
|
+
}
|
|
67
|
+
function printHelp() {
|
|
68
|
+
write([
|
|
69
|
+
"sbox — one unified CLI for agent sandbox providers",
|
|
70
|
+
"",
|
|
71
|
+
"Usage:",
|
|
72
|
+
" sbox caps <provider> print a provider's capability matrix",
|
|
73
|
+
" sbox doctor [<provider>] validate node + provider SDK setup",
|
|
74
|
+
" sbox exec <provider> -- <cmd> run one command in a fresh sandbox",
|
|
75
|
+
" sbox list <provider> list a provider's live sandboxes",
|
|
76
|
+
"",
|
|
77
|
+
`Providers: ${Object.keys(PROVIDERS).join(", ")}`,
|
|
78
|
+
"",
|
|
79
|
+
"Credentials for exec/list are read from the environment (see each",
|
|
80
|
+
"provider's docs). caps and doctor run fully offline.",
|
|
81
|
+
].join("\n"));
|
|
82
|
+
}
|
|
83
|
+
async function cmdCaps(id) {
|
|
84
|
+
if (!id) {
|
|
85
|
+
writeErr("sbox: caps needs a provider, e.g. `sbox caps e2b`");
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
const meta = PROVIDERS[id];
|
|
89
|
+
if (!meta) {
|
|
90
|
+
return unknownProvider(id);
|
|
91
|
+
}
|
|
92
|
+
const mod = await importProvider(id);
|
|
93
|
+
const map = mod[meta.caps];
|
|
94
|
+
if (!map) {
|
|
95
|
+
writeErr(`sbox: '${id}' does not export ${meta.caps}`);
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
const names = Object.keys(map).toSorted();
|
|
99
|
+
const width = Math.max(...names.map((n) => n.length));
|
|
100
|
+
const counts = {
|
|
101
|
+
emulated: 0,
|
|
102
|
+
native: 0,
|
|
103
|
+
unsupported: 0,
|
|
104
|
+
};
|
|
105
|
+
write(`${id} capabilities\n`);
|
|
106
|
+
for (const name of names) {
|
|
107
|
+
const level = map[name];
|
|
108
|
+
if (!level) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
counts[level]++;
|
|
112
|
+
write(` ${LEVEL_SYMBOL[level]} ${name.padEnd(width)} ${level}`);
|
|
113
|
+
}
|
|
114
|
+
write(`\n ${counts.native} native · ${counts.emulated} emulated · ${counts.unsupported} unsupported`);
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
async function cmdDoctor(id) {
|
|
118
|
+
let ok = true;
|
|
119
|
+
write("sbox doctor\n");
|
|
120
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
121
|
+
const nodeOk = major >= 20;
|
|
122
|
+
ok &&= nodeOk;
|
|
123
|
+
write(` ${nodeOk ? "✔" : "✖"} node ${process.versions.node}${nodeOk ? "" : " (requires >= 20)"}`);
|
|
124
|
+
if (id) {
|
|
125
|
+
const meta = PROVIDERS[id];
|
|
126
|
+
if (!meta) {
|
|
127
|
+
return unknownProvider(id);
|
|
128
|
+
}
|
|
129
|
+
if (meta.pkg) {
|
|
130
|
+
const req = createRequire(import.meta.url);
|
|
131
|
+
let installed = true;
|
|
132
|
+
try {
|
|
133
|
+
req.resolve(meta.pkg);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
installed = false;
|
|
137
|
+
}
|
|
138
|
+
ok &&= installed;
|
|
139
|
+
write(installed
|
|
140
|
+
? ` ✔ provider SDK '${meta.pkg}' is installed`
|
|
141
|
+
: ` ✖ provider SDK '${meta.pkg}' is missing — run: npm install ${meta.pkg}`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
write(` ✔ ${id} needs no extra SDK (uses the platform REST API)`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
write(`\n ${ok ? "all checks passed" : "some checks failed"}`);
|
|
148
|
+
return ok ? 0 : 1;
|
|
149
|
+
}
|
|
150
|
+
async function withProvider(id, fn) {
|
|
151
|
+
const meta = PROVIDERS[id];
|
|
152
|
+
if (!meta) {
|
|
153
|
+
return unknownProvider(id);
|
|
154
|
+
}
|
|
155
|
+
const mod = await importProvider(id);
|
|
156
|
+
const factory = mod[meta.factory];
|
|
157
|
+
if (!factory) {
|
|
158
|
+
writeErr(`sbox: '${id}' does not export ${meta.factory}`);
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
return await fn(factory({}));
|
|
162
|
+
}
|
|
163
|
+
async function cmdExec(id, command) {
|
|
164
|
+
if (!(id && command)) {
|
|
165
|
+
writeErr("sbox: usage: sbox exec <provider> -- <cmd>");
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
const { createSandboxClient } = await import("./index.js");
|
|
169
|
+
return await withProvider(id, async (provider) => {
|
|
170
|
+
const client = createSandboxClient({ provider });
|
|
171
|
+
try {
|
|
172
|
+
const sandbox = await client.create();
|
|
173
|
+
try {
|
|
174
|
+
const res = await sandbox.commands.run(command);
|
|
175
|
+
if (res.stdout) {
|
|
176
|
+
process.stdout.write(res.stdout);
|
|
177
|
+
}
|
|
178
|
+
if (res.stderr) {
|
|
179
|
+
process.stderr.write(res.stderr);
|
|
180
|
+
}
|
|
181
|
+
return res.exitCode ?? 0;
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
await sandbox.destroy();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
await client.dispose();
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async function cmdList(id) {
|
|
193
|
+
if (!id) {
|
|
194
|
+
writeErr("sbox: list needs a provider, e.g. `sbox list e2b`");
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
197
|
+
const { createSandboxClient } = await import("./index.js");
|
|
198
|
+
return await withProvider(id, async (provider) => {
|
|
199
|
+
const client = createSandboxClient({ provider });
|
|
200
|
+
try {
|
|
201
|
+
let n = 0;
|
|
202
|
+
for await (const info of client.list()) {
|
|
203
|
+
n++;
|
|
204
|
+
const state = info.state ? ` [${info.state}]` : "";
|
|
205
|
+
write(`${info.id}${state}`);
|
|
206
|
+
}
|
|
207
|
+
if (n === 0) {
|
|
208
|
+
write("(no sandboxes)");
|
|
209
|
+
}
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
await client.dispose();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async function main(argv) {
|
|
218
|
+
const [cmd, ...rest] = argv;
|
|
219
|
+
if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
220
|
+
printHelp();
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
switch (cmd) {
|
|
224
|
+
case "caps":
|
|
225
|
+
return await cmdCaps(rest[0]);
|
|
226
|
+
case "doctor": {
|
|
227
|
+
const flag = rest.indexOf("--provider");
|
|
228
|
+
const id = flag === -1 ? rest[0] : rest[flag + 1];
|
|
229
|
+
return await cmdDoctor(id);
|
|
230
|
+
}
|
|
231
|
+
case "exec": {
|
|
232
|
+
const sep = rest.indexOf("--");
|
|
233
|
+
const command = (sep === -1 ? rest.slice(1) : rest.slice(sep + 1)).join(" ");
|
|
234
|
+
return await cmdExec(rest[0], command);
|
|
235
|
+
}
|
|
236
|
+
case "list":
|
|
237
|
+
return await cmdList(rest[0]);
|
|
238
|
+
default:
|
|
239
|
+
writeErr(`sbox: unknown command '${cmd}'. Try \`sbox help\`.`);
|
|
240
|
+
return 1;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
main(process.argv.slice(2))
|
|
244
|
+
.then((code) => process.exit(code))
|
|
245
|
+
.catch((err) => {
|
|
246
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
247
|
+
writeErr(`sbox: ${message}`);
|
|
248
|
+
process.exit(1);
|
|
249
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Sandbox as CFSandbox } from "@cloudflare/sandbox";
|
|
2
|
+
import type { SandboxProvider } from "../adapter/index.js";
|
|
3
|
+
export interface CloudflareOptions {
|
|
4
|
+
/** The Durable Object namespace binding for your exported Sandbox class. */
|
|
5
|
+
binding: unknown;
|
|
6
|
+
/** Your Worker's domain — required to build preview URLs for exposed ports. */
|
|
7
|
+
hostname?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const CLOUDFLARE_CAPS: {
|
|
10
|
+
readonly background: "unsupported";
|
|
11
|
+
readonly codeInterpreter: "native";
|
|
12
|
+
readonly egressControl: "unsupported";
|
|
13
|
+
readonly exposePort: "native";
|
|
14
|
+
readonly filesUpload: "native";
|
|
15
|
+
readonly filesWatch: "unsupported";
|
|
16
|
+
readonly fork: "unsupported";
|
|
17
|
+
readonly gpu: "unsupported";
|
|
18
|
+
readonly killProcess: "unsupported";
|
|
19
|
+
readonly list: "unsupported";
|
|
20
|
+
readonly metrics: "unsupported";
|
|
21
|
+
readonly pause: "unsupported";
|
|
22
|
+
readonly privatePreview: "unsupported";
|
|
23
|
+
readonly proxiedFetch: "native";
|
|
24
|
+
readonly pty: "unsupported";
|
|
25
|
+
readonly region: "unsupported";
|
|
26
|
+
readonly secretsVault: "unsupported";
|
|
27
|
+
readonly setTimeout: "native";
|
|
28
|
+
readonly snapshot: "unsupported";
|
|
29
|
+
readonly ssh: "unsupported";
|
|
30
|
+
readonly statefulKernel: "native";
|
|
31
|
+
readonly stdin: "unsupported";
|
|
32
|
+
readonly stop: "unsupported";
|
|
33
|
+
readonly streaming: "native";
|
|
34
|
+
readonly volumes: "unsupported";
|
|
35
|
+
};
|
|
36
|
+
export type CloudflareCaps = typeof CLOUDFLARE_CAPS;
|
|
37
|
+
export declare const cloudflare: (opts: CloudflareOptions) => SandboxProvider<{
|
|
38
|
+
readonly background: "unsupported";
|
|
39
|
+
readonly codeInterpreter: "native";
|
|
40
|
+
readonly egressControl: "unsupported";
|
|
41
|
+
readonly exposePort: "native";
|
|
42
|
+
readonly filesUpload: "native";
|
|
43
|
+
readonly filesWatch: "unsupported";
|
|
44
|
+
readonly fork: "unsupported";
|
|
45
|
+
readonly gpu: "unsupported";
|
|
46
|
+
readonly killProcess: "unsupported";
|
|
47
|
+
readonly list: "unsupported";
|
|
48
|
+
readonly metrics: "unsupported";
|
|
49
|
+
readonly pause: "unsupported";
|
|
50
|
+
readonly privatePreview: "unsupported";
|
|
51
|
+
readonly proxiedFetch: "native";
|
|
52
|
+
readonly pty: "unsupported";
|
|
53
|
+
readonly region: "unsupported";
|
|
54
|
+
readonly secretsVault: "unsupported";
|
|
55
|
+
readonly setTimeout: "native";
|
|
56
|
+
readonly snapshot: "unsupported";
|
|
57
|
+
readonly ssh: "unsupported";
|
|
58
|
+
readonly statefulKernel: "native";
|
|
59
|
+
readonly stdin: "unsupported";
|
|
60
|
+
readonly stop: "unsupported";
|
|
61
|
+
readonly streaming: "native";
|
|
62
|
+
readonly volumes: "unsupported";
|
|
63
|
+
}, CFSandbox<unknown>>;
|
|
64
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cloudflare/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAwBhE,OAAO,KAAK,EAaV,eAAe,EAEhB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BM,CAAC;AAEnC,MAAM,MAAM,cAAc,GAAG,OAAO,eAAe,CAAC;AAwEpD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;sBAoPrB,CAAC"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sbox-sdk/cloudflare` — adapter for `@cloudflare/sandbox`. Unlike API-key
|
|
3
|
+
* providers, Cloudflare sandboxes run INSIDE a Cloudflare Worker against a
|
|
4
|
+
* Durable Object / Container binding. So the factory takes the binding, and
|
|
5
|
+
* `create()` is get-or-create (no imperative create; ids are names). This is the
|
|
6
|
+
* edge-portability stress test: the core stays fetch-only, no `node:` imports.
|
|
7
|
+
*
|
|
8
|
+
* Usage (inside a Worker):
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createSandboxClient } from "sbox-sdk";
|
|
11
|
+
* import { cloudflare } from "sbox-sdk/cloudflare";
|
|
12
|
+
* const client = createSandboxClient({ provider: cloudflare({ binding: env.Sandbox, hostname: "example.com" }) });
|
|
13
|
+
* ```
|
|
14
|
+
* Requires the optional peer dependency `@cloudflare/sandbox`, and you must
|
|
15
|
+
* `export { Sandbox } from "@cloudflare/sandbox"` from your Worker entry.
|
|
16
|
+
*/
|
|
17
|
+
import { AsyncQueue, defineProvider, NotSupportedError, SandboxError, } from "../adapter/index.js";
|
|
18
|
+
export const CLOUDFLARE_CAPS = {
|
|
19
|
+
background: "unsupported",
|
|
20
|
+
codeInterpreter: "native",
|
|
21
|
+
egressControl: "unsupported",
|
|
22
|
+
exposePort: "native",
|
|
23
|
+
filesUpload: "native",
|
|
24
|
+
filesWatch: "unsupported",
|
|
25
|
+
fork: "unsupported",
|
|
26
|
+
gpu: "unsupported",
|
|
27
|
+
killProcess: "unsupported",
|
|
28
|
+
list: "unsupported",
|
|
29
|
+
metrics: "unsupported",
|
|
30
|
+
pause: "unsupported",
|
|
31
|
+
privatePreview: "unsupported",
|
|
32
|
+
proxiedFetch: "native",
|
|
33
|
+
pty: "unsupported",
|
|
34
|
+
region: "unsupported",
|
|
35
|
+
secretsVault: "unsupported",
|
|
36
|
+
setTimeout: "native",
|
|
37
|
+
snapshot: "unsupported",
|
|
38
|
+
ssh: "unsupported",
|
|
39
|
+
statefulKernel: "native",
|
|
40
|
+
stdin: "unsupported",
|
|
41
|
+
stop: "unsupported",
|
|
42
|
+
streaming: "native",
|
|
43
|
+
volumes: "unsupported",
|
|
44
|
+
};
|
|
45
|
+
const CLOUDFLARE_FLAGS = {
|
|
46
|
+
exitCodeNative: true,
|
|
47
|
+
perCommandEnvCwd: true,
|
|
48
|
+
preservesDiskOnStop: false,
|
|
49
|
+
preservesMemoryOnPause: false,
|
|
50
|
+
previewModel: "wildcardDNS",
|
|
51
|
+
};
|
|
52
|
+
let cached = null;
|
|
53
|
+
async function loadCF() {
|
|
54
|
+
if (!cached) {
|
|
55
|
+
cached = (await import("@cloudflare/sandbox"));
|
|
56
|
+
}
|
|
57
|
+
return cached;
|
|
58
|
+
}
|
|
59
|
+
function base64ToBytes(b64) {
|
|
60
|
+
const bin = atob(b64);
|
|
61
|
+
const out = new Uint8Array(bin.length);
|
|
62
|
+
for (let i = 0; i < bin.length; i++) {
|
|
63
|
+
out[i] = bin.codePointAt(i) ?? 0;
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
function mapResult(r) {
|
|
68
|
+
const rr = r;
|
|
69
|
+
const mime = {};
|
|
70
|
+
const put = (key, mt) => {
|
|
71
|
+
const v = rr[key];
|
|
72
|
+
if (typeof v === "string") {
|
|
73
|
+
mime[mt] = v;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
put("text", "text/plain");
|
|
77
|
+
put("html", "text/html");
|
|
78
|
+
put("png", "image/png");
|
|
79
|
+
put("jpeg", "image/jpeg");
|
|
80
|
+
put("svg", "image/svg+xml");
|
|
81
|
+
put("markdown", "text/markdown");
|
|
82
|
+
return { mime, text: typeof rr.text === "string" ? rr.text : undefined };
|
|
83
|
+
}
|
|
84
|
+
function mapCFError(e) {
|
|
85
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
86
|
+
if (/not ?found/i.test(msg)) {
|
|
87
|
+
return new SandboxError("NotFound", msg, {
|
|
88
|
+
cause: e,
|
|
89
|
+
provider: "cloudflare",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (/unauthor|forbidden/i.test(msg)) {
|
|
93
|
+
return new SandboxError("Unauthorized", msg, {
|
|
94
|
+
cause: e,
|
|
95
|
+
provider: "cloudflare",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (/timeout|timed out/i.test(msg)) {
|
|
99
|
+
return new SandboxError("Timeout", msg, {
|
|
100
|
+
cause: e,
|
|
101
|
+
provider: "cloudflare",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
export const cloudflare = defineProvider((opts) => {
|
|
107
|
+
const getStub = async (id) => {
|
|
108
|
+
const mod = await loadCF();
|
|
109
|
+
const getSandbox = mod.getSandbox;
|
|
110
|
+
return getSandbox(opts.binding, id);
|
|
111
|
+
};
|
|
112
|
+
const makeHandle = (stub, id, defaultEnv) => {
|
|
113
|
+
const s = stub;
|
|
114
|
+
const contexts = new Map();
|
|
115
|
+
return {
|
|
116
|
+
async createContext(options) {
|
|
117
|
+
const c = await s.createCodeContext({ language: options.language });
|
|
118
|
+
contexts.set(c.id, c);
|
|
119
|
+
return { id: c.id, language: options.language ?? "python" };
|
|
120
|
+
},
|
|
121
|
+
async destroy() {
|
|
122
|
+
await s.destroy();
|
|
123
|
+
},
|
|
124
|
+
exec(cmd, options) {
|
|
125
|
+
const queue = new AsyncQueue();
|
|
126
|
+
void s
|
|
127
|
+
.exec(cmd, {
|
|
128
|
+
cwd: options.cwd,
|
|
129
|
+
env: { ...defaultEnv, ...options.env },
|
|
130
|
+
onOutput: (stream, data) => queue.push({ type: stream, data }),
|
|
131
|
+
stream: true,
|
|
132
|
+
timeout: options.timeoutMs,
|
|
133
|
+
})
|
|
134
|
+
.then((res) => {
|
|
135
|
+
queue.push({ exitCode: res.exitCode, type: "exit" });
|
|
136
|
+
queue.close();
|
|
137
|
+
})
|
|
138
|
+
.catch((error) => queue.fail(mapCFError(error) ?? SandboxError.wrap(error, "cloudflare")));
|
|
139
|
+
return {
|
|
140
|
+
pid: Promise.resolve(""),
|
|
141
|
+
async kill() {
|
|
142
|
+
/* buffered exec has no kill handle */
|
|
143
|
+
},
|
|
144
|
+
[Symbol.asyncIterator]: () => queue.iterator(),
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
exposePort(port) {
|
|
148
|
+
if (!opts.hostname) {
|
|
149
|
+
throw new NotSupportedError("cloudflare", "ports.expose (set `hostname` in cloudflare({...}) options)");
|
|
150
|
+
}
|
|
151
|
+
return s
|
|
152
|
+
.exposePort(port, { hostname: opts.hostname })
|
|
153
|
+
.then((r) => ({ port: r.port, url: r.url }));
|
|
154
|
+
},
|
|
155
|
+
getInfo() {
|
|
156
|
+
return {
|
|
157
|
+
id,
|
|
158
|
+
metadata: {},
|
|
159
|
+
provider: "cloudflare",
|
|
160
|
+
raw: stub,
|
|
161
|
+
state: "running",
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
id,
|
|
165
|
+
async listDir(path) {
|
|
166
|
+
const r = await s.listFiles(path);
|
|
167
|
+
return r.files.map((f) => ({
|
|
168
|
+
name: f.name,
|
|
169
|
+
path: f.path ?? `${path}/${f.name}`,
|
|
170
|
+
type: f.isDirectory || f.type === "directory" || f.type === "dir"
|
|
171
|
+
? "dir"
|
|
172
|
+
: "file",
|
|
173
|
+
}));
|
|
174
|
+
},
|
|
175
|
+
async listPorts() {
|
|
176
|
+
if (!opts.hostname) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
const ports = await s.getExposedPorts(opts.hostname);
|
|
180
|
+
return ports.map((p) => ({ port: p.port, url: p.url }));
|
|
181
|
+
},
|
|
182
|
+
async mkdir(path, recursive) {
|
|
183
|
+
await s.mkdir(path, { recursive });
|
|
184
|
+
},
|
|
185
|
+
proxyFetch(port, path, init) {
|
|
186
|
+
const url = `http://localhost:${port}${path ?? "/"}`;
|
|
187
|
+
return s.containerFetch(url, init ?? {}, port);
|
|
188
|
+
},
|
|
189
|
+
raw: stub,
|
|
190
|
+
async readFile(path) {
|
|
191
|
+
const r = await s.readFile(path);
|
|
192
|
+
return r.encoding === "base64"
|
|
193
|
+
? base64ToBytes(r.content)
|
|
194
|
+
: new TextEncoder().encode(r.content);
|
|
195
|
+
},
|
|
196
|
+
async remove(path) {
|
|
197
|
+
await s.deleteFile(path);
|
|
198
|
+
},
|
|
199
|
+
async rename(from, to) {
|
|
200
|
+
await s.renameFile(from, to);
|
|
201
|
+
},
|
|
202
|
+
async runCode(code, options) {
|
|
203
|
+
const realContext = options.context
|
|
204
|
+
? contexts.get(options.context.id)
|
|
205
|
+
: undefined;
|
|
206
|
+
const exec = await s.runCode(code, {
|
|
207
|
+
context: realContext,
|
|
208
|
+
language: options.language,
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
error: exec.error
|
|
212
|
+
? {
|
|
213
|
+
name: exec.error.name,
|
|
214
|
+
value: exec.error.value,
|
|
215
|
+
traceback: exec.error.traceback,
|
|
216
|
+
}
|
|
217
|
+
: undefined,
|
|
218
|
+
logs: {
|
|
219
|
+
stderr: exec.logs?.stderr ?? [],
|
|
220
|
+
stdout: exec.logs?.stdout ?? [],
|
|
221
|
+
},
|
|
222
|
+
results: (exec.results ?? []).map(mapResult),
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
async setTimeout(ttlMs) {
|
|
226
|
+
await s.setSleepAfter(ttlMs);
|
|
227
|
+
},
|
|
228
|
+
async unexposePort(port) {
|
|
229
|
+
await s.unexposePort(port);
|
|
230
|
+
},
|
|
231
|
+
async writeFile(path, data) {
|
|
232
|
+
const stream = new ReadableStream({
|
|
233
|
+
start(controller) {
|
|
234
|
+
controller.enqueue(data);
|
|
235
|
+
controller.close();
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
await s.writeFile(path, stream);
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
const provider = {
|
|
243
|
+
name: "cloudflare",
|
|
244
|
+
capabilities: CLOUDFLARE_CAPS,
|
|
245
|
+
flags: CLOUDFLARE_FLAGS,
|
|
246
|
+
mapError: mapCFError,
|
|
247
|
+
async create(spec, ctx) {
|
|
248
|
+
// get-or-create: the id IS the name. Use idempotencyKey so retries hit
|
|
249
|
+
// the same Durable Object instead of orphaning a new one.
|
|
250
|
+
const id = spec.name ?? ctx.idempotencyKey ?? globalThis.crypto.randomUUID();
|
|
251
|
+
return makeHandle(await getStub(id), id, spec.env);
|
|
252
|
+
},
|
|
253
|
+
async connect(id) {
|
|
254
|
+
return makeHandle(await getStub(id), id);
|
|
255
|
+
},
|
|
256
|
+
// NOTE: no `list` — Durable Objects are not enumerable (caps.list = unsupported).
|
|
257
|
+
};
|
|
258
|
+
return provider;
|
|
259
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { SandboxProvider } from "../adapter/index.js";
|
|
2
|
+
export interface CodeSandboxOptions {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
/** Default template/sandbox id to fork from when `spec.template` is unset. */
|
|
5
|
+
templateId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const CODESANDBOX_CAPS: {
|
|
8
|
+
readonly background: "unsupported";
|
|
9
|
+
readonly codeInterpreter: "unsupported";
|
|
10
|
+
readonly egressControl: "unsupported";
|
|
11
|
+
readonly exposePort: "native";
|
|
12
|
+
readonly filesUpload: "native";
|
|
13
|
+
readonly filesWatch: "unsupported";
|
|
14
|
+
readonly fork: "unsupported";
|
|
15
|
+
readonly gpu: "unsupported";
|
|
16
|
+
readonly killProcess: "unsupported";
|
|
17
|
+
readonly list: "native";
|
|
18
|
+
readonly metrics: "unsupported";
|
|
19
|
+
readonly pause: "native";
|
|
20
|
+
readonly privatePreview: "native";
|
|
21
|
+
readonly proxiedFetch: "unsupported";
|
|
22
|
+
readonly pty: "unsupported";
|
|
23
|
+
readonly region: "unsupported";
|
|
24
|
+
readonly secretsVault: "unsupported";
|
|
25
|
+
readonly setTimeout: "native";
|
|
26
|
+
readonly snapshot: "unsupported";
|
|
27
|
+
readonly ssh: "unsupported";
|
|
28
|
+
readonly statefulKernel: "unsupported";
|
|
29
|
+
readonly stdin: "unsupported";
|
|
30
|
+
readonly stop: "unsupported";
|
|
31
|
+
readonly streaming: "emulated";
|
|
32
|
+
readonly volumes: "unsupported";
|
|
33
|
+
};
|
|
34
|
+
export type CodeSandboxCaps = typeof CODESANDBOX_CAPS;
|
|
35
|
+
interface CsbFsEntry {
|
|
36
|
+
name: string;
|
|
37
|
+
type?: string;
|
|
38
|
+
isSymlink?: boolean;
|
|
39
|
+
}
|
|
40
|
+
interface CsbStat {
|
|
41
|
+
type?: string;
|
|
42
|
+
size?: number;
|
|
43
|
+
mtime?: number;
|
|
44
|
+
}
|
|
45
|
+
interface CsbClient {
|
|
46
|
+
commands: {
|
|
47
|
+
run(cmd: string, opts?: {
|
|
48
|
+
cwd?: string;
|
|
49
|
+
env?: Record<string, string>;
|
|
50
|
+
}): Promise<string>;
|
|
51
|
+
};
|
|
52
|
+
fs: {
|
|
53
|
+
readFile(path: string): Promise<Uint8Array>;
|
|
54
|
+
writeFile(path: string, data: Uint8Array, opts?: {
|
|
55
|
+
create?: boolean;
|
|
56
|
+
overwrite?: boolean;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
readdir(path: string): Promise<CsbFsEntry[]>;
|
|
59
|
+
stat(path: string): Promise<CsbStat>;
|
|
60
|
+
rename(from: string, to: string): Promise<void>;
|
|
61
|
+
remove(path: string, recursive?: boolean): Promise<void>;
|
|
62
|
+
};
|
|
63
|
+
hosts: {
|
|
64
|
+
getUrl(port: number, protocol?: string): string;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
interface CsbSandbox {
|
|
68
|
+
id: string;
|
|
69
|
+
connect(): Promise<CsbClient>;
|
|
70
|
+
updateHibernationTimeout(seconds: number): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
export declare const codesandbox: (opts: CodeSandboxOptions) => SandboxProvider<{
|
|
73
|
+
readonly background: "unsupported";
|
|
74
|
+
readonly codeInterpreter: "unsupported";
|
|
75
|
+
readonly egressControl: "unsupported";
|
|
76
|
+
readonly exposePort: "native";
|
|
77
|
+
readonly filesUpload: "native";
|
|
78
|
+
readonly filesWatch: "unsupported";
|
|
79
|
+
readonly fork: "unsupported";
|
|
80
|
+
readonly gpu: "unsupported";
|
|
81
|
+
readonly killProcess: "unsupported";
|
|
82
|
+
readonly list: "native";
|
|
83
|
+
readonly metrics: "unsupported";
|
|
84
|
+
readonly pause: "native";
|
|
85
|
+
readonly privatePreview: "native";
|
|
86
|
+
readonly proxiedFetch: "unsupported";
|
|
87
|
+
readonly pty: "unsupported";
|
|
88
|
+
readonly region: "unsupported";
|
|
89
|
+
readonly secretsVault: "unsupported";
|
|
90
|
+
readonly setTimeout: "native";
|
|
91
|
+
readonly snapshot: "unsupported";
|
|
92
|
+
readonly ssh: "unsupported";
|
|
93
|
+
readonly statefulKernel: "unsupported";
|
|
94
|
+
readonly stdin: "unsupported";
|
|
95
|
+
readonly stop: "unsupported";
|
|
96
|
+
readonly streaming: "emulated";
|
|
97
|
+
readonly volumes: "unsupported";
|
|
98
|
+
}, CsbSandbox>;
|
|
99
|
+
export {};
|
|
100
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/codesandbox/index.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAWV,eAAe,EAGhB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BK,CAAC;AAEnC,MAAM,MAAM,eAAe,GAAG,OAAO,gBAAgB,CAAC;AAetD,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AACD,UAAU,OAAO;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,UAAU,SAAS;IACjB,QAAQ,EAAE;QACR,GAAG,CACD,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;SAAE,GACpD,OAAO,CAAC,MAAM,CAAC,CAAC;KACpB,CAAC;IACF,EAAE,EAAE;QACF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QAC5C,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,OAAO,CAAC;YAAC,SAAS,CAAC,EAAE,OAAO,CAAA;SAAE,GAC/C,OAAO,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC1D,CAAC;IACF,KAAK,EAAE;QAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CAC5D;AACD,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9B,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAkDD,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;cAiKtB,CAAC"}
|