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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vercel/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAchE,OAAO,KAAK,EAYV,eAAe,EAGhB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BU,CAAC;AAEnC,MAAM,MAAM,UAAU,GAAG,OAAO,WAAW,CAAC;AAgE5C,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6MlB,CAAC"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sbox-sdk/vercel` — adapter for `@vercel/sandbox` (ephemeral microVMs).
|
|
3
|
+
* Exercises the exec-emulation surface: no PTY, no fs watch, no code interpreter.
|
|
4
|
+
* Node-only (uses Buffer for binary fs). Requires the optional peer dependency
|
|
5
|
+
* `@vercel/sandbox`; auth via a Vercel token + team/project ids (OIDC or PAT).
|
|
6
|
+
*/
|
|
7
|
+
import { AsyncQueue, defineProvider, numExit, SandboxError, } from "../adapter/index.js";
|
|
8
|
+
export const VERCEL_CAPS = {
|
|
9
|
+
background: "native",
|
|
10
|
+
codeInterpreter: "unsupported",
|
|
11
|
+
egressControl: "unsupported",
|
|
12
|
+
exposePort: "native",
|
|
13
|
+
filesUpload: "native",
|
|
14
|
+
filesWatch: "unsupported",
|
|
15
|
+
fork: "unsupported",
|
|
16
|
+
gpu: "unsupported",
|
|
17
|
+
killProcess: "unsupported",
|
|
18
|
+
list: "unsupported",
|
|
19
|
+
metrics: "unsupported",
|
|
20
|
+
pause: "unsupported",
|
|
21
|
+
privatePreview: "unsupported",
|
|
22
|
+
proxiedFetch: "unsupported",
|
|
23
|
+
pty: "unsupported",
|
|
24
|
+
region: "unsupported",
|
|
25
|
+
secretsVault: "unsupported",
|
|
26
|
+
setTimeout: "unsupported",
|
|
27
|
+
snapshot: "unsupported",
|
|
28
|
+
ssh: "unsupported",
|
|
29
|
+
statefulKernel: "unsupported",
|
|
30
|
+
stdin: "unsupported",
|
|
31
|
+
stop: "unsupported",
|
|
32
|
+
streaming: "native",
|
|
33
|
+
volumes: "unsupported",
|
|
34
|
+
};
|
|
35
|
+
const VERCEL_FLAGS = {
|
|
36
|
+
exitCodeNative: true,
|
|
37
|
+
perCommandEnvCwd: true,
|
|
38
|
+
preservesDiskOnStop: true, // stop() auto-snapshots the filesystem
|
|
39
|
+
preservesMemoryOnPause: false,
|
|
40
|
+
previewModel: "declaredPorts",
|
|
41
|
+
};
|
|
42
|
+
let cached = null;
|
|
43
|
+
async function loadVercel() {
|
|
44
|
+
if (!cached) {
|
|
45
|
+
cached = (await import("@vercel/sandbox"));
|
|
46
|
+
}
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
function mapState(status) {
|
|
50
|
+
switch (status) {
|
|
51
|
+
case "running": {
|
|
52
|
+
return "running";
|
|
53
|
+
}
|
|
54
|
+
case "stopped":
|
|
55
|
+
case "stopping": {
|
|
56
|
+
return "stopped";
|
|
57
|
+
}
|
|
58
|
+
case "pending":
|
|
59
|
+
case "initializing": {
|
|
60
|
+
return "creating";
|
|
61
|
+
}
|
|
62
|
+
case "failed": {
|
|
63
|
+
return "error";
|
|
64
|
+
}
|
|
65
|
+
default: {
|
|
66
|
+
return "unknown";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function mapVercelError(e) {
|
|
71
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
72
|
+
if (/not ?found/i.test(msg)) {
|
|
73
|
+
return new SandboxError("NotFound", msg, { cause: e, provider: "vercel" });
|
|
74
|
+
}
|
|
75
|
+
if (/unauthor|forbidden|token/i.test(msg)) {
|
|
76
|
+
return new SandboxError("Unauthorized", msg, {
|
|
77
|
+
cause: e,
|
|
78
|
+
provider: "vercel",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (/timeout|timed out/i.test(msg)) {
|
|
82
|
+
return new SandboxError("Timeout", msg, { cause: e, provider: "vercel" });
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
export const vercel = defineProvider((opts) => {
|
|
87
|
+
const creds = {
|
|
88
|
+
projectId: opts.projectId,
|
|
89
|
+
teamId: opts.teamId,
|
|
90
|
+
token: opts.token,
|
|
91
|
+
};
|
|
92
|
+
const makeHandle = (sb) => {
|
|
93
|
+
const v = sb;
|
|
94
|
+
const drive = (queue, cmdP) => {
|
|
95
|
+
void (async () => {
|
|
96
|
+
let cmd;
|
|
97
|
+
try {
|
|
98
|
+
cmd = await cmdP;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
queue.fail(mapVercelError(error) ?? SandboxError.wrap(error, "vercel"));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
for await (const log of cmd.logs()) {
|
|
106
|
+
queue.push({
|
|
107
|
+
data: log.data,
|
|
108
|
+
type: log.stream === "stderr" ? "stderr" : "stdout",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const fin = await cmd.wait();
|
|
112
|
+
queue.push({ exitCode: fin.exitCode, type: "exit" });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
queue.push({ exitCode: numExit(error), type: "exit" });
|
|
116
|
+
}
|
|
117
|
+
queue.close();
|
|
118
|
+
})();
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
id: v.sandboxId,
|
|
122
|
+
raw: sb,
|
|
123
|
+
getInfo() {
|
|
124
|
+
return {
|
|
125
|
+
id: v.sandboxId,
|
|
126
|
+
metadata: {},
|
|
127
|
+
provider: "vercel",
|
|
128
|
+
raw: sb,
|
|
129
|
+
state: mapState(v.status),
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
async destroy() {
|
|
133
|
+
await v.stop();
|
|
134
|
+
},
|
|
135
|
+
exec(cmd, options) {
|
|
136
|
+
const queue = new AsyncQueue();
|
|
137
|
+
const cmdP = v.runCommand({
|
|
138
|
+
args: ["-c", cmd],
|
|
139
|
+
cmd: "sh",
|
|
140
|
+
cwd: options.cwd,
|
|
141
|
+
detached: true,
|
|
142
|
+
env: options.env,
|
|
143
|
+
});
|
|
144
|
+
drive(queue, cmdP);
|
|
145
|
+
return {
|
|
146
|
+
pid: cmdP.then((c) => c.cmdId).catch(() => ""),
|
|
147
|
+
async kill() {
|
|
148
|
+
/* no kill handle exposed by the SDK */
|
|
149
|
+
},
|
|
150
|
+
[Symbol.asyncIterator]: () => queue.iterator(),
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
async spawn(cmd, options) {
|
|
154
|
+
const c = await v.runCommand({
|
|
155
|
+
args: ["-c", cmd],
|
|
156
|
+
cmd: "sh",
|
|
157
|
+
cwd: options.cwd,
|
|
158
|
+
detached: true,
|
|
159
|
+
env: options.env,
|
|
160
|
+
});
|
|
161
|
+
const queue = new AsyncQueue();
|
|
162
|
+
drive(queue, Promise.resolve(c));
|
|
163
|
+
return {
|
|
164
|
+
id: c.cmdId,
|
|
165
|
+
pid: Promise.resolve(c.cmdId),
|
|
166
|
+
async kill() {
|
|
167
|
+
/* no kill handle exposed by the SDK */
|
|
168
|
+
},
|
|
169
|
+
[Symbol.asyncIterator]: () => queue.iterator(),
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
async readFile(path) {
|
|
173
|
+
const buf = await v.fs.readFileToBuffer({ path });
|
|
174
|
+
if (!buf) {
|
|
175
|
+
throw new SandboxError("NotFound", `no such file: '${path}'`, {
|
|
176
|
+
provider: "vercel",
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return new Uint8Array(buf);
|
|
180
|
+
},
|
|
181
|
+
async writeFile(path, data) {
|
|
182
|
+
await v.fs.writeFiles([{ content: data, path }]);
|
|
183
|
+
},
|
|
184
|
+
async listDir(path) {
|
|
185
|
+
const entries = await v.fs.readdir(path, { withFileTypes: true });
|
|
186
|
+
return entries.map((d) => ({
|
|
187
|
+
name: d.name,
|
|
188
|
+
path: `${path.replace(/\/$/, "")}/${d.name}`,
|
|
189
|
+
type: d.isDirectory() ? "dir" : "file",
|
|
190
|
+
}));
|
|
191
|
+
},
|
|
192
|
+
async mkdir(path, recursive) {
|
|
193
|
+
await v.fs.mkdir(path, { recursive });
|
|
194
|
+
},
|
|
195
|
+
async remove(path, recursive) {
|
|
196
|
+
await v.fs.rm(path, { force: true, recursive });
|
|
197
|
+
},
|
|
198
|
+
async stat(path) {
|
|
199
|
+
const s = await v.fs.stat(path);
|
|
200
|
+
return {
|
|
201
|
+
mtime: s.mtime,
|
|
202
|
+
path,
|
|
203
|
+
size: s.size,
|
|
204
|
+
type: s.isDirectory() ? "dir" : "file",
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
exposePort(port) {
|
|
208
|
+
return { port, url: v.domain(port) };
|
|
209
|
+
},
|
|
210
|
+
// NOTE: rename is omitted -> the core polyfills it via exec(`mv`).
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
const provider = {
|
|
214
|
+
capabilities: VERCEL_CAPS,
|
|
215
|
+
async connect(id) {
|
|
216
|
+
const { Sandbox } = await loadVercel();
|
|
217
|
+
const sb = await Sandbox.get({ ...creds, sandboxId: id });
|
|
218
|
+
return makeHandle(sb);
|
|
219
|
+
},
|
|
220
|
+
async create(spec) {
|
|
221
|
+
const { Sandbox } = await loadVercel();
|
|
222
|
+
const sb = await Sandbox.create({
|
|
223
|
+
...creds,
|
|
224
|
+
env: spec.env,
|
|
225
|
+
name: spec.name,
|
|
226
|
+
ports: spec.ports,
|
|
227
|
+
resources: spec.resources?.vcpus
|
|
228
|
+
? { vcpus: spec.resources.vcpus }
|
|
229
|
+
: undefined,
|
|
230
|
+
runtime: spec.template,
|
|
231
|
+
signal: spec.signal,
|
|
232
|
+
timeout: spec.ttlMs,
|
|
233
|
+
});
|
|
234
|
+
return makeHandle(sb);
|
|
235
|
+
},
|
|
236
|
+
flags: VERCEL_FLAGS,
|
|
237
|
+
mapError: mapVercelError,
|
|
238
|
+
name: "vercel",
|
|
239
|
+
};
|
|
240
|
+
return provider;
|
|
241
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sbox-sdk",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "One unified SDK for agent sandbox providers (E2B, Vercel, Cloudflare, Daytona, Modal, and more) — swap the adapter import, keep your code.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent",
|
|
7
|
+
"ai",
|
|
8
|
+
"cloudflare-sandbox",
|
|
9
|
+
"code-interpreter",
|
|
10
|
+
"daytona",
|
|
11
|
+
"e2b",
|
|
12
|
+
"sandbox",
|
|
13
|
+
"vercel-sandbox"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"bin": {
|
|
17
|
+
"sbox": "dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src/aws-lambda/runner",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./adapter": {
|
|
33
|
+
"types": "./dist/adapter/index.d.ts",
|
|
34
|
+
"import": "./dist/adapter/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./testing": {
|
|
37
|
+
"types": "./dist/testing/index.d.ts",
|
|
38
|
+
"import": "./dist/testing/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./conformance": {
|
|
41
|
+
"types": "./dist/conformance/index.d.ts",
|
|
42
|
+
"import": "./dist/conformance/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./memory": {
|
|
45
|
+
"types": "./dist/memory/index.d.ts",
|
|
46
|
+
"import": "./dist/memory/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./agent-tools": {
|
|
49
|
+
"types": "./dist/agent-tools/index.d.ts",
|
|
50
|
+
"import": "./dist/agent-tools/index.js"
|
|
51
|
+
},
|
|
52
|
+
"./ai": {
|
|
53
|
+
"types": "./dist/ai/index.d.ts",
|
|
54
|
+
"import": "./dist/ai/index.js"
|
|
55
|
+
},
|
|
56
|
+
"./ai-sdk": {
|
|
57
|
+
"types": "./dist/ai-sdk/index.d.ts",
|
|
58
|
+
"import": "./dist/ai-sdk/index.js"
|
|
59
|
+
},
|
|
60
|
+
"./mastra": {
|
|
61
|
+
"types": "./dist/mastra/index.d.ts",
|
|
62
|
+
"import": "./dist/mastra/index.js"
|
|
63
|
+
},
|
|
64
|
+
"./openai": {
|
|
65
|
+
"types": "./dist/openai/index.d.ts",
|
|
66
|
+
"import": "./dist/openai/index.js"
|
|
67
|
+
},
|
|
68
|
+
"./anthropic": {
|
|
69
|
+
"types": "./dist/anthropic/index.d.ts",
|
|
70
|
+
"import": "./dist/anthropic/index.js"
|
|
71
|
+
},
|
|
72
|
+
"./langchain": {
|
|
73
|
+
"types": "./dist/langchain/index.d.ts",
|
|
74
|
+
"import": "./dist/langchain/index.js"
|
|
75
|
+
},
|
|
76
|
+
"./e2b": {
|
|
77
|
+
"types": "./dist/e2b/index.d.ts",
|
|
78
|
+
"import": "./dist/e2b/index.js"
|
|
79
|
+
},
|
|
80
|
+
"./cloudflare": {
|
|
81
|
+
"types": "./dist/cloudflare/index.d.ts",
|
|
82
|
+
"import": "./dist/cloudflare/index.js"
|
|
83
|
+
},
|
|
84
|
+
"./vercel": {
|
|
85
|
+
"types": "./dist/vercel/index.d.ts",
|
|
86
|
+
"import": "./dist/vercel/index.js"
|
|
87
|
+
},
|
|
88
|
+
"./daytona": {
|
|
89
|
+
"types": "./dist/daytona/index.d.ts",
|
|
90
|
+
"import": "./dist/daytona/index.js"
|
|
91
|
+
},
|
|
92
|
+
"./modal": {
|
|
93
|
+
"types": "./dist/modal/index.d.ts",
|
|
94
|
+
"import": "./dist/modal/index.js"
|
|
95
|
+
},
|
|
96
|
+
"./fly": {
|
|
97
|
+
"types": "./dist/fly/index.d.ts",
|
|
98
|
+
"import": "./dist/fly/index.js"
|
|
99
|
+
},
|
|
100
|
+
"./aws-lambda": {
|
|
101
|
+
"types": "./dist/aws-lambda/index.d.ts",
|
|
102
|
+
"import": "./dist/aws-lambda/index.js"
|
|
103
|
+
},
|
|
104
|
+
"./northflank": {
|
|
105
|
+
"types": "./dist/northflank/index.d.ts",
|
|
106
|
+
"import": "./dist/northflank/index.js"
|
|
107
|
+
},
|
|
108
|
+
"./runloop": {
|
|
109
|
+
"types": "./dist/runloop/index.d.ts",
|
|
110
|
+
"import": "./dist/runloop/index.js"
|
|
111
|
+
},
|
|
112
|
+
"./codesandbox": {
|
|
113
|
+
"types": "./dist/codesandbox/index.d.ts",
|
|
114
|
+
"import": "./dist/codesandbox/index.js"
|
|
115
|
+
},
|
|
116
|
+
"./morph": {
|
|
117
|
+
"types": "./dist/morph/index.d.ts",
|
|
118
|
+
"import": "./dist/morph/index.js"
|
|
119
|
+
},
|
|
120
|
+
"./blaxel": {
|
|
121
|
+
"types": "./dist/blaxel/index.d.ts",
|
|
122
|
+
"import": "./dist/blaxel/index.js"
|
|
123
|
+
},
|
|
124
|
+
"./beam": {
|
|
125
|
+
"types": "./dist/beam/index.d.ts",
|
|
126
|
+
"import": "./dist/beam/index.js"
|
|
127
|
+
},
|
|
128
|
+
"./railway": {
|
|
129
|
+
"types": "./dist/railway/index.d.ts",
|
|
130
|
+
"import": "./dist/railway/index.js"
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"scripts": {
|
|
134
|
+
"build": "rm -rf dist && tsc -p tsconfig.json && chmod +x dist/cli.js",
|
|
135
|
+
"check-types": "tsgo -p tsconfig.json --noEmit",
|
|
136
|
+
"types": "tsgo -p tsconfig.json --noEmit",
|
|
137
|
+
"test": "vitest run",
|
|
138
|
+
"test:watch": "vitest",
|
|
139
|
+
"prepack": "pnpm run build"
|
|
140
|
+
},
|
|
141
|
+
"devDependencies": {
|
|
142
|
+
"@anthropic-ai/sdk": "^0.69.0",
|
|
143
|
+
"@aws-sdk/client-lambda": "^3.1075.0",
|
|
144
|
+
"@aws-sdk/client-lambda-microvms": "^3.1075.0",
|
|
145
|
+
"@aws-sdk/client-s3": "^3.1075.0",
|
|
146
|
+
"@beamcloud/beam-js": "*",
|
|
147
|
+
"@blaxel/core": "*",
|
|
148
|
+
"@cloudflare/sandbox": "^0.12.1",
|
|
149
|
+
"@codesandbox/sdk": "*",
|
|
150
|
+
"@daytonaio/sdk": "^0.191.0",
|
|
151
|
+
"@e2b/code-interpreter": "^2.6.1",
|
|
152
|
+
"@langchain/core": "^0.3.0",
|
|
153
|
+
"@mastra/core": "^1.46.0",
|
|
154
|
+
"@northflank/js-client": "*",
|
|
155
|
+
"@openai/agents": "^0.12.0",
|
|
156
|
+
"@runloop/api-client": "*",
|
|
157
|
+
"@sbox-sdk/config": "workspace:*",
|
|
158
|
+
"@types/node": "^25.9.1",
|
|
159
|
+
"@typescript/native-preview": "7.0.0-dev.20260624.1",
|
|
160
|
+
"@vercel/sandbox": "^2.2.1",
|
|
161
|
+
"ai": "^5.0.0",
|
|
162
|
+
"modal": "^0.8.1",
|
|
163
|
+
"morphcloud": "*",
|
|
164
|
+
"railway": "*",
|
|
165
|
+
"typescript": "5.9.2",
|
|
166
|
+
"vitest": "^3.2.4",
|
|
167
|
+
"zod": "^4.0.0"
|
|
168
|
+
},
|
|
169
|
+
"peerDependencies": {
|
|
170
|
+
"@anthropic-ai/sdk": ">=0.40.0",
|
|
171
|
+
"@aws-sdk/client-lambda-microvms": "*",
|
|
172
|
+
"@beamcloud/beam-js": "*",
|
|
173
|
+
"@blaxel/core": "*",
|
|
174
|
+
"@cloudflare/sandbox": "*",
|
|
175
|
+
"@codesandbox/sdk": "*",
|
|
176
|
+
"@daytonaio/sdk": "*",
|
|
177
|
+
"@e2b/code-interpreter": "*",
|
|
178
|
+
"@langchain/core": ">=0.3.0",
|
|
179
|
+
"@mastra/core": "^1.0.0",
|
|
180
|
+
"@northflank/js-client": "*",
|
|
181
|
+
"@openai/agents": ">=0.12.0",
|
|
182
|
+
"@runloop/api-client": "*",
|
|
183
|
+
"@vercel/sandbox": "*",
|
|
184
|
+
"ai": "^5.0.0 || ^6.0.0",
|
|
185
|
+
"modal": "*",
|
|
186
|
+
"morphcloud": "*",
|
|
187
|
+
"railway": "*",
|
|
188
|
+
"zod": "^3.25.0 || ^4.0.0"
|
|
189
|
+
},
|
|
190
|
+
"peerDependenciesMeta": {
|
|
191
|
+
"@aws-sdk/client-lambda-microvms": {
|
|
192
|
+
"optional": true
|
|
193
|
+
},
|
|
194
|
+
"@beamcloud/beam-js": {
|
|
195
|
+
"optional": true
|
|
196
|
+
},
|
|
197
|
+
"@blaxel/core": {
|
|
198
|
+
"optional": true
|
|
199
|
+
},
|
|
200
|
+
"@cloudflare/sandbox": {
|
|
201
|
+
"optional": true
|
|
202
|
+
},
|
|
203
|
+
"@codesandbox/sdk": {
|
|
204
|
+
"optional": true
|
|
205
|
+
},
|
|
206
|
+
"@northflank/js-client": {
|
|
207
|
+
"optional": true
|
|
208
|
+
},
|
|
209
|
+
"@runloop/api-client": {
|
|
210
|
+
"optional": true
|
|
211
|
+
},
|
|
212
|
+
"morphcloud": {
|
|
213
|
+
"optional": true
|
|
214
|
+
},
|
|
215
|
+
"railway": {
|
|
216
|
+
"optional": true
|
|
217
|
+
},
|
|
218
|
+
"@daytonaio/sdk": {
|
|
219
|
+
"optional": true
|
|
220
|
+
},
|
|
221
|
+
"@e2b/code-interpreter": {
|
|
222
|
+
"optional": true
|
|
223
|
+
},
|
|
224
|
+
"modal": {
|
|
225
|
+
"optional": true
|
|
226
|
+
},
|
|
227
|
+
"@vercel/sandbox": {
|
|
228
|
+
"optional": true
|
|
229
|
+
},
|
|
230
|
+
"zod": {
|
|
231
|
+
"optional": true
|
|
232
|
+
},
|
|
233
|
+
"ai": {
|
|
234
|
+
"optional": true
|
|
235
|
+
},
|
|
236
|
+
"@mastra/core": {
|
|
237
|
+
"optional": true
|
|
238
|
+
},
|
|
239
|
+
"@openai/agents": {
|
|
240
|
+
"optional": true
|
|
241
|
+
},
|
|
242
|
+
"@anthropic-ai/sdk": {
|
|
243
|
+
"optional": true
|
|
244
|
+
},
|
|
245
|
+
"@langchain/core": {
|
|
246
|
+
"optional": true
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
"engines": {
|
|
250
|
+
"node": ">=20.0.0"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Reference MicroVM image for the sbox-sdk AWS Lambda MicroVMs adapter.
|
|
2
|
+
# Build -> zip with this Dockerfile + server.mjs -> upload to S3 ->
|
|
3
|
+
# CreateMicrovmImage (see ./README.md). The runner runs INSIDE the isolated
|
|
4
|
+
# MicroVM; executing arbitrary commands is its purpose (that is what the sandbox
|
|
5
|
+
# is for), and it is never exposed on the host.
|
|
6
|
+
FROM public.ecr.aws/docker/library/node:22-slim
|
|
7
|
+
|
|
8
|
+
WORKDIR /opt/sbox
|
|
9
|
+
COPY server.mjs .
|
|
10
|
+
|
|
11
|
+
# The MicroVM endpoint routes to port 8080 by default.
|
|
12
|
+
ENV PORT=8080
|
|
13
|
+
EXPOSE 8080
|
|
14
|
+
|
|
15
|
+
CMD ["node", "/opt/sbox/server.mjs"]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# sbox-sdk runner for AWS Lambda MicroVMs
|
|
2
|
+
|
|
3
|
+
AWS Lambda MicroVMs give you isolation + lifecycle + a dedicated HTTPS endpoint,
|
|
4
|
+
but **the exec/filesystem protocol is defined by your image** — AWS only defines
|
|
5
|
+
lifecycle hooks. This folder is a reference "runner" the `sbox-sdk/aws-lambda`
|
|
6
|
+
adapter talks to. Bake it into your MicroVM image once.
|
|
7
|
+
|
|
8
|
+
## Protocol the adapter expects
|
|
9
|
+
|
|
10
|
+
Served on port `8080` (override with the adapter's `port` option):
|
|
11
|
+
|
|
12
|
+
| Method & path | Body | Response |
|
|
13
|
+
| --------------------------------------------------------------------- | --------------------------------- | ------------------------------------ |
|
|
14
|
+
| `POST /sbox/exec` | `{ cmd, cwd?, env?, timeoutMs? }` | `{ stdout, stderr, exitCode }` |
|
|
15
|
+
| `POST /sbox/fs/read` | `{ path }` | `{ contentBase64 }` (404 if missing) |
|
|
16
|
+
| `POST /sbox/fs/write` | `{ path, contentBase64 }` | `{ ok: true }` |
|
|
17
|
+
| `GET /sbox/health` | — | `200` |
|
|
18
|
+
| `POST /aws/lambda-microvms/runtime/v1/{run,resume,suspend,terminate}` | — | `200` |
|
|
19
|
+
|
|
20
|
+
The `/run` hook must return `200` before traffic flows — the reference
|
|
21
|
+
`server.mjs` does this. Directory ops (`ls`, `mkdir`, `mv`, `stat`) are not
|
|
22
|
+
separate routes: the core polyfills them through `/sbox/exec`.
|
|
23
|
+
|
|
24
|
+
> Security: the runner runs **inside the isolated MicroVM** and executes
|
|
25
|
+
> arbitrary commands on purpose — that is the sandbox. Never run it on a host.
|
|
26
|
+
|
|
27
|
+
## Build & register the image
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
zip -j runner.zip Dockerfile server.mjs
|
|
31
|
+
aws s3 cp runner.zip s3://YOUR_BUCKET/runner.zip
|
|
32
|
+
|
|
33
|
+
aws lambda-microvms create-microvm-image \
|
|
34
|
+
--image-name sbox-runner \
|
|
35
|
+
--source '{"s3":{"bucket":"YOUR_BUCKET","key":"runner.zip"}}'
|
|
36
|
+
# -> note the returned image ARN
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Use it
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { createSandboxClient } from "sbox-sdk";
|
|
43
|
+
import { awsLambda } from "sbox-sdk/aws-lambda";
|
|
44
|
+
|
|
45
|
+
const client = createSandboxClient({
|
|
46
|
+
provider: awsLambda({
|
|
47
|
+
imageIdentifier:
|
|
48
|
+
"arn:aws:lambda:us-east-1:123456789012:microvm-image:sbox-runner",
|
|
49
|
+
region: "us-east-1",
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const sandbox = await client.create();
|
|
54
|
+
const res = await sandbox.commands.run("echo hello"); // res.stdout === "hello\n"
|
|
55
|
+
await sandbox.files.write("/tmp/data.txt", "hi");
|
|
56
|
+
await sandbox.pause(); // SuspendMicrovm (memory + disk preserved up to 8h)
|
|
57
|
+
await sandbox.resume(); // ResumeMicrovm
|
|
58
|
+
await sandbox.destroy(); // TerminateMicrovm
|
|
59
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Reference "runner" for sbox-sdk's AWS Lambda MicroVMs adapter.
|
|
2
|
+
// Bake this into your MicroVM image (see Dockerfile). It exposes the small HTTP
|
|
3
|
+
// protocol the adapter calls over the microVM's dedicated endpoint, plus the
|
|
4
|
+
// required AWS lifecycle hooks. No external deps — Node stdlib only.
|
|
5
|
+
//
|
|
6
|
+
// Protocol (POST JSON unless noted), served on PORT (default 8080):
|
|
7
|
+
// POST /sbox/exec { cmd, cwd?, env?, timeoutMs? } -> { stdout, stderr, exitCode }
|
|
8
|
+
// POST /sbox/fs/read { path } -> { contentBase64 } (404 if missing)
|
|
9
|
+
// POST /sbox/fs/write { path, contentBase64 } -> { ok: true }
|
|
10
|
+
// GET /sbox/health -> 200
|
|
11
|
+
// POST /aws/lambda-microvms/runtime/v1/{run,resume,suspend,terminate} -> 200
|
|
12
|
+
|
|
13
|
+
import { exec } from "node:child_process";
|
|
14
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
15
|
+
import { createServer } from "node:http";
|
|
16
|
+
import { dirname } from "node:path";
|
|
17
|
+
|
|
18
|
+
const PORT = Number(process.env.PORT ?? 8080);
|
|
19
|
+
|
|
20
|
+
const readJson = (req) =>
|
|
21
|
+
new Promise((resolve, reject) => {
|
|
22
|
+
let body = "";
|
|
23
|
+
req.on("data", (c) => (body += c));
|
|
24
|
+
req.on("end", () => {
|
|
25
|
+
try {
|
|
26
|
+
resolve(body ? JSON.parse(body) : {});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
reject(error);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
req.on("error", reject);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const send = (res, status, obj) => {
|
|
35
|
+
res.writeHead(status, { "content-type": "application/json" });
|
|
36
|
+
res.end(obj === undefined ? "" : JSON.stringify(obj));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const runCmd = (cmd, cwd, env, timeoutMs) =>
|
|
40
|
+
new Promise((resolve) => {
|
|
41
|
+
exec(
|
|
42
|
+
cmd,
|
|
43
|
+
{
|
|
44
|
+
cwd,
|
|
45
|
+
env: { ...process.env, ...env },
|
|
46
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
47
|
+
timeout: timeoutMs ?? 0,
|
|
48
|
+
},
|
|
49
|
+
(err, stdout, stderr) => {
|
|
50
|
+
const exitCode =
|
|
51
|
+
err && typeof err.code === "number" ? err.code : err ? 1 : 0;
|
|
52
|
+
resolve({ exitCode, stderr: String(stderr), stdout: String(stdout) });
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
createServer(async (req, res) => {
|
|
58
|
+
try {
|
|
59
|
+
const url = req.url ?? "/";
|
|
60
|
+
if (req.method === "GET" && url === "/sbox/health") {
|
|
61
|
+
return send(res, 200, { ok: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (url.startsWith("/aws/lambda-microvms/runtime/v1/")) {
|
|
65
|
+
return send(res, 200, { ok: true });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (req.method === "POST" && url === "/sbox/exec") {
|
|
69
|
+
const { cmd, cwd, env, timeoutMs } = await readJson(req);
|
|
70
|
+
return send(res, 200, await runCmd(cmd, cwd, env, timeoutMs));
|
|
71
|
+
}
|
|
72
|
+
if (req.method === "POST" && url === "/sbox/fs/read") {
|
|
73
|
+
const { path } = await readJson(req);
|
|
74
|
+
try {
|
|
75
|
+
const buf = await readFile(path);
|
|
76
|
+
return send(res, 200, { contentBase64: buf.toString("base64") });
|
|
77
|
+
} catch {
|
|
78
|
+
return send(res, 404, { error: "not found" });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (req.method === "POST" && url === "/sbox/fs/write") {
|
|
82
|
+
const { path, contentBase64 } = await readJson(req);
|
|
83
|
+
await mkdir(dirname(path), { recursive: true });
|
|
84
|
+
await writeFile(path, Buffer.from(contentBase64, "base64"));
|
|
85
|
+
return send(res, 200, { ok: true });
|
|
86
|
+
}
|
|
87
|
+
return send(res, 404, { error: "unknown route" });
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return send(res, 500, { error: String(error?.message ?? error) });
|
|
90
|
+
}
|
|
91
|
+
}).listen(PORT, () => console.log(`[sbox-runner] listening on :${PORT}`));
|