agentbox-sdk 0.1.0 → 0.1.1
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/README.md +29 -14
- package/dist/{Sandbox-DTprxRZf.d.ts → Sandbox-BQX-sWzs.d.ts} +49 -6
- package/dist/agents/index.d.ts +24 -4
- package/dist/agents/index.js +13 -4
- package/dist/chunk-2NKMDGYH.js +18 -0
- package/dist/{chunk-BW43ESRM.js → chunk-G27423WX.js} +293 -309
- package/dist/{chunk-HMBWQSVN.js → chunk-O7HCJXKW.js} +28 -1
- package/dist/{chunk-QRQFQTGH.js → chunk-X7AWPYDK.js} +401 -13
- package/dist/cli.js +16 -8
- package/dist/enums.d.ts +25 -0
- package/dist/enums.js +8 -0
- package/dist/events/index.d.ts +4 -2
- package/dist/index.d.ts +5 -3
- package/dist/index.js +15 -4
- package/dist/sandboxes/index.d.ts +12 -2
- package/dist/sandboxes/index.js +7 -3
- package/dist/{types-BwcoN0n-.d.ts → types-Et22oPap.d.ts} +3 -2
- package/package.json +9 -2
- /package/dist/{chunk-JFDP556Q.js → chunk-NSJM57Z4.js} +0 -0
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentProvider
|
|
3
|
+
} from "./chunk-2NKMDGYH.js";
|
|
4
|
+
|
|
5
|
+
// src/agents/ports.ts
|
|
6
|
+
var AGENT_RESERVED_PORTS = {
|
|
7
|
+
[AgentProvider.ClaudeCode]: [43180],
|
|
8
|
+
[AgentProvider.Codex]: [43181],
|
|
9
|
+
[AgentProvider.OpenCode]: [4096]
|
|
10
|
+
};
|
|
11
|
+
function collectAllAgentReservedPorts() {
|
|
12
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13
|
+
for (const ports of Object.values(AGENT_RESERVED_PORTS)) {
|
|
14
|
+
for (const port of ports) {
|
|
15
|
+
seen.add(port);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return Array.from(seen);
|
|
19
|
+
}
|
|
20
|
+
|
|
1
21
|
// src/shared/errors.ts
|
|
2
22
|
var AgentBoxError = class extends Error {
|
|
3
23
|
code;
|
|
@@ -24,6 +44,10 @@ function asError(error) {
|
|
|
24
44
|
}
|
|
25
45
|
return new Error(typeof error === "string" ? error : JSON.stringify(error));
|
|
26
46
|
}
|
|
47
|
+
function suppressUnhandledRejection(promise) {
|
|
48
|
+
promise.catch(() => void 0);
|
|
49
|
+
return promise;
|
|
50
|
+
}
|
|
27
51
|
|
|
28
52
|
// src/shared/async-queue.ts
|
|
29
53
|
var AsyncQueue = class {
|
|
@@ -193,6 +217,7 @@ export {
|
|
|
193
217
|
AgentBoxError,
|
|
194
218
|
UnsupportedProviderError,
|
|
195
219
|
asError,
|
|
220
|
+
suppressUnhandledRejection,
|
|
196
221
|
AsyncQueue,
|
|
197
222
|
sleep,
|
|
198
223
|
getAvailablePort,
|
|
@@ -200,5 +225,7 @@ export {
|
|
|
200
225
|
readStreamAsText,
|
|
201
226
|
pipeReadableStream,
|
|
202
227
|
readNodeStream,
|
|
203
|
-
linesFromTextChunks
|
|
228
|
+
linesFromTextChunks,
|
|
229
|
+
AGENT_RESERVED_PORTS,
|
|
230
|
+
collectAllAgentReservedPorts
|
|
204
231
|
};
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AsyncQueue,
|
|
3
3
|
UnsupportedProviderError,
|
|
4
|
+
collectAllAgentReservedPorts,
|
|
4
5
|
pipeReadableStream,
|
|
5
6
|
readNodeStream,
|
|
6
7
|
readStreamAsText,
|
|
7
|
-
sleep
|
|
8
|
-
|
|
8
|
+
sleep,
|
|
9
|
+
suppressUnhandledRejection
|
|
10
|
+
} from "./chunk-O7HCJXKW.js";
|
|
9
11
|
import {
|
|
10
12
|
shellQuote,
|
|
11
13
|
toShellCommand
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-NSJM57Z4.js";
|
|
15
|
+
import {
|
|
16
|
+
SandboxProvider
|
|
17
|
+
} from "./chunk-2NKMDGYH.js";
|
|
13
18
|
|
|
14
19
|
// src/sandboxes/git.ts
|
|
15
20
|
function encodeExtraHeader(name, value) {
|
|
@@ -59,6 +64,19 @@ var SandboxAdapter = class {
|
|
|
59
64
|
this.options = options;
|
|
60
65
|
this.baseEnv = { ...options.env ?? {} };
|
|
61
66
|
}
|
|
67
|
+
async uploadFile(_content, _targetPath) {
|
|
68
|
+
void _content;
|
|
69
|
+
void _targetPath;
|
|
70
|
+
throw new Error(
|
|
71
|
+
`uploadFile is not supported by the ${this.provider} provider.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
async downloadFile(_sourcePath) {
|
|
75
|
+
void _sourcePath;
|
|
76
|
+
throw new Error(
|
|
77
|
+
`downloadFile is not supported by the ${this.provider} provider.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
62
80
|
async ensureProvisioned() {
|
|
63
81
|
if (this.provisioned) {
|
|
64
82
|
return;
|
|
@@ -79,6 +97,14 @@ var SandboxAdapter = class {
|
|
|
79
97
|
get workingDir() {
|
|
80
98
|
return this.options.workingDir ?? "/workspace";
|
|
81
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Headers that callers should attach to HTTP / WebSocket requests they make
|
|
102
|
+
* against this sandbox's preview URL. Default is empty; providers like
|
|
103
|
+
* Vercel override this to inject Deployment Protection bypass tokens.
|
|
104
|
+
*/
|
|
105
|
+
get previewHeaders() {
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
82
108
|
getMergedEnv(extra) {
|
|
83
109
|
return {
|
|
84
110
|
...this.baseEnv,
|
|
@@ -127,7 +153,7 @@ var DaytonaSandboxAdapter = class extends SandboxAdapter {
|
|
|
127
153
|
});
|
|
128
154
|
}
|
|
129
155
|
get provider() {
|
|
130
|
-
return
|
|
156
|
+
return SandboxProvider.Daytona;
|
|
131
157
|
}
|
|
132
158
|
get raw() {
|
|
133
159
|
return {
|
|
@@ -288,6 +314,7 @@ var DaytonaSandboxAdapter = class extends SandboxAdapter {
|
|
|
288
314
|
queue.fail(error);
|
|
289
315
|
throw error;
|
|
290
316
|
});
|
|
317
|
+
suppressUnhandledRejection(completion);
|
|
291
318
|
return {
|
|
292
319
|
id: commandId,
|
|
293
320
|
raw: { sessionId, commandId },
|
|
@@ -382,7 +409,7 @@ async function loadE2bModule() {
|
|
|
382
409
|
var E2bSandboxAdapter = class extends SandboxAdapter {
|
|
383
410
|
sandbox;
|
|
384
411
|
get provider() {
|
|
385
|
-
return
|
|
412
|
+
return SandboxProvider.E2B;
|
|
386
413
|
}
|
|
387
414
|
get raw() {
|
|
388
415
|
return {
|
|
@@ -520,6 +547,7 @@ var E2bSandboxAdapter = class extends SandboxAdapter {
|
|
|
520
547
|
queue.fail(error);
|
|
521
548
|
throw error;
|
|
522
549
|
});
|
|
550
|
+
suppressUnhandledRejection(completion);
|
|
523
551
|
return {
|
|
524
552
|
id: String(handle.pid),
|
|
525
553
|
raw: handle,
|
|
@@ -585,6 +613,7 @@ var E2bSandboxAdapter = class extends SandboxAdapter {
|
|
|
585
613
|
queue.fail(error);
|
|
586
614
|
throw error;
|
|
587
615
|
});
|
|
616
|
+
suppressUnhandledRejection(completion);
|
|
588
617
|
return {
|
|
589
618
|
id: String(handle.pid),
|
|
590
619
|
raw: handle,
|
|
@@ -762,7 +791,7 @@ var LocalDockerSandboxAdapter = class extends SandboxAdapter {
|
|
|
762
791
|
client = new Docker();
|
|
763
792
|
container;
|
|
764
793
|
get provider() {
|
|
765
|
-
return
|
|
794
|
+
return SandboxProvider.LocalDocker;
|
|
766
795
|
}
|
|
767
796
|
get raw() {
|
|
768
797
|
return {
|
|
@@ -921,6 +950,7 @@ var LocalDockerSandboxAdapter = class extends SandboxAdapter {
|
|
|
921
950
|
queue.fail(error);
|
|
922
951
|
throw error;
|
|
923
952
|
});
|
|
953
|
+
suppressUnhandledRejection(completion);
|
|
924
954
|
return {
|
|
925
955
|
id: exec.id,
|
|
926
956
|
raw: { exec, stream },
|
|
@@ -1115,7 +1145,7 @@ var ModalSandboxAdapter = class extends SandboxAdapter {
|
|
|
1115
1145
|
});
|
|
1116
1146
|
}
|
|
1117
1147
|
get provider() {
|
|
1118
|
-
return
|
|
1148
|
+
return SandboxProvider.Modal;
|
|
1119
1149
|
}
|
|
1120
1150
|
get raw() {
|
|
1121
1151
|
return {
|
|
@@ -1139,6 +1169,7 @@ var ModalSandboxAdapter = class extends SandboxAdapter {
|
|
|
1139
1169
|
});
|
|
1140
1170
|
const image = await this.resolveModalImage();
|
|
1141
1171
|
const resources = resolveSandboxResources(this.options.resources);
|
|
1172
|
+
const unencryptedPorts = this.resolveDefaultUnencryptedPorts();
|
|
1142
1173
|
const sandbox = await this.client.sandboxes.create(app, image, {
|
|
1143
1174
|
cpu: resources?.cpu,
|
|
1144
1175
|
memoryMiB: resources?.memoryMiB,
|
|
@@ -1148,12 +1179,34 @@ var ModalSandboxAdapter = class extends SandboxAdapter {
|
|
|
1148
1179
|
command: this.options.provider?.command ?? ["sleep", "infinity"],
|
|
1149
1180
|
env: this.getMergedEnv(),
|
|
1150
1181
|
encryptedPorts: this.options.provider?.encryptedPorts,
|
|
1151
|
-
unencryptedPorts
|
|
1182
|
+
unencryptedPorts,
|
|
1152
1183
|
verbose: this.options.provider?.verbose
|
|
1153
1184
|
});
|
|
1154
1185
|
await sandbox.setTags(this.getTags());
|
|
1155
1186
|
this.sandbox = sandbox;
|
|
1156
1187
|
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Modal requires ports to be declared at sandbox creation time — a running
|
|
1190
|
+
* sandbox cannot gain new tunnels. To make `openPort` work predictably
|
|
1191
|
+
* across providers, we pre-declare all well-known agent-harness ports on
|
|
1192
|
+
* every Modal sandbox we create, unless the caller has explicitly pinned
|
|
1193
|
+
* them to a specific (possibly empty) list.
|
|
1194
|
+
*/
|
|
1195
|
+
resolveDefaultUnencryptedPorts() {
|
|
1196
|
+
const declared = this.options.provider?.unencryptedPorts;
|
|
1197
|
+
const encrypted = new Set(this.options.provider?.encryptedPorts ?? []);
|
|
1198
|
+
const reserved = collectAllAgentReservedPorts().filter(
|
|
1199
|
+
(port) => !encrypted.has(port)
|
|
1200
|
+
);
|
|
1201
|
+
if (declared === void 0) {
|
|
1202
|
+
return reserved.length > 0 ? reserved : void 0;
|
|
1203
|
+
}
|
|
1204
|
+
const merged = new Set(declared);
|
|
1205
|
+
for (const port of reserved) {
|
|
1206
|
+
merged.add(port);
|
|
1207
|
+
}
|
|
1208
|
+
return Array.from(merged);
|
|
1209
|
+
}
|
|
1157
1210
|
async run(command, options) {
|
|
1158
1211
|
await this.ensureProvisioned();
|
|
1159
1212
|
const sandbox = this.requireSandbox();
|
|
@@ -1230,6 +1283,7 @@ var ModalSandboxAdapter = class extends SandboxAdapter {
|
|
|
1230
1283
|
queue.fail(error);
|
|
1231
1284
|
throw error;
|
|
1232
1285
|
});
|
|
1286
|
+
suppressUnhandledRejection(completion);
|
|
1233
1287
|
return {
|
|
1234
1288
|
id: `${sandbox.sandboxId}:${Date.now()}`,
|
|
1235
1289
|
raw: process2,
|
|
@@ -1284,7 +1338,24 @@ var ModalSandboxAdapter = class extends SandboxAdapter {
|
|
|
1284
1338
|
if (provider.encryptedPorts?.includes(port)) {
|
|
1285
1339
|
return;
|
|
1286
1340
|
}
|
|
1287
|
-
|
|
1341
|
+
const alreadyDeclared = provider.unencryptedPorts?.includes(port) ?? false;
|
|
1342
|
+
if (!alreadyDeclared) {
|
|
1343
|
+
provider.unencryptedPorts = [...provider.unencryptedPorts ?? [], port];
|
|
1344
|
+
}
|
|
1345
|
+
if (!this.sandbox) {
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
try {
|
|
1349
|
+
const tunnels = await this.sandbox.tunnels();
|
|
1350
|
+
if (tunnels[port]) {
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
} catch {
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
throw new Error(
|
|
1357
|
+
`Modal sandbox is already running and cannot expose port ${port} dynamically. Declare it at creation time via \`provider.unencryptedPorts\` (e.g. \`provider: { unencryptedPorts: [${port}] }\`) or use \`AGENT_RESERVED_PORTS\` / \`collectAllAgentReservedPorts()\` from agentbox-sdk to pre-declare the agent harness ports.`
|
|
1358
|
+
);
|
|
1288
1359
|
}
|
|
1289
1360
|
async getPreviewLink(port) {
|
|
1290
1361
|
await this.ensureProvisioned();
|
|
@@ -1347,22 +1418,330 @@ var ModalSandboxAdapter = class extends SandboxAdapter {
|
|
|
1347
1418
|
}
|
|
1348
1419
|
};
|
|
1349
1420
|
|
|
1421
|
+
// src/sandboxes/providers/vercel.ts
|
|
1422
|
+
import { Sandbox as VercelSandbox } from "@vercel/sandbox";
|
|
1423
|
+
function pickFirstTag(tags) {
|
|
1424
|
+
if (!tags) return void 0;
|
|
1425
|
+
const entries = Object.entries(tags);
|
|
1426
|
+
if (entries.length === 0) return void 0;
|
|
1427
|
+
const [key, value] = entries[0];
|
|
1428
|
+
return { [key]: value };
|
|
1429
|
+
}
|
|
1430
|
+
function matchesAllTags(candidateTags, required) {
|
|
1431
|
+
return Object.entries(required).every(
|
|
1432
|
+
([key, value]) => candidateTags?.[key] === value
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
function describeVercelApiError(error, action) {
|
|
1436
|
+
if (error && typeof error === "object" && "response" in error && error.response instanceof Response) {
|
|
1437
|
+
const apiError = error;
|
|
1438
|
+
const status = apiError.response.status;
|
|
1439
|
+
const body = apiError.json !== void 0 ? JSON.stringify(apiError.json) : apiError.text ?? "";
|
|
1440
|
+
return new Error(
|
|
1441
|
+
`Vercel ${action} failed with HTTP ${status}: ${body || apiError.message}`
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
1445
|
+
}
|
|
1446
|
+
async function wrapVercelApiError(action, fn) {
|
|
1447
|
+
try {
|
|
1448
|
+
return await fn();
|
|
1449
|
+
} catch (error) {
|
|
1450
|
+
throw describeVercelApiError(error, action);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
function buildTimeoutSignal(timeoutMs) {
|
|
1454
|
+
if (!timeoutMs || timeoutMs <= 0) return void 0;
|
|
1455
|
+
return AbortSignal.timeout(timeoutMs);
|
|
1456
|
+
}
|
|
1457
|
+
function getCredentials(options) {
|
|
1458
|
+
const token = options.provider?.token ?? process.env.VERCEL_TOKEN;
|
|
1459
|
+
const teamId = options.provider?.teamId ?? process.env.VERCEL_TEAM_ID;
|
|
1460
|
+
const projectId = options.provider?.projectId ?? process.env.VERCEL_PROJECT_ID;
|
|
1461
|
+
if (token && teamId && projectId) {
|
|
1462
|
+
return { token, teamId, projectId };
|
|
1463
|
+
}
|
|
1464
|
+
return {};
|
|
1465
|
+
}
|
|
1466
|
+
var VercelSandboxAdapter = class extends SandboxAdapter {
|
|
1467
|
+
sandbox;
|
|
1468
|
+
get provider() {
|
|
1469
|
+
return SandboxProvider.Vercel;
|
|
1470
|
+
}
|
|
1471
|
+
get raw() {
|
|
1472
|
+
return { sandbox: this.sandbox };
|
|
1473
|
+
}
|
|
1474
|
+
get id() {
|
|
1475
|
+
return this.sandbox?.name;
|
|
1476
|
+
}
|
|
1477
|
+
get workingDir() {
|
|
1478
|
+
return this.options.workingDir ?? "/vercel/sandbox";
|
|
1479
|
+
}
|
|
1480
|
+
get previewHeaders() {
|
|
1481
|
+
const token = this.options.provider?.protectionBypass;
|
|
1482
|
+
return token ? { "x-vercel-protection-bypass": token } : {};
|
|
1483
|
+
}
|
|
1484
|
+
async provision() {
|
|
1485
|
+
const existing = await this.findExistingSandbox();
|
|
1486
|
+
if (existing) {
|
|
1487
|
+
this.sandbox = existing;
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
const credentials = getCredentials(this.options);
|
|
1491
|
+
const provider = this.options.provider;
|
|
1492
|
+
const snapshotId = provider?.snapshotId;
|
|
1493
|
+
const timeout = provider?.timeoutMs ?? 12e4;
|
|
1494
|
+
const runtime = provider?.runtime ?? "node24";
|
|
1495
|
+
const resources = resolveSandboxResources(this.options.resources);
|
|
1496
|
+
const vcpus = resources?.cpu ? { resources: { vcpus: resources.cpu } } : {};
|
|
1497
|
+
const base = {
|
|
1498
|
+
...credentials,
|
|
1499
|
+
timeout,
|
|
1500
|
+
env: this.getMergedEnv(),
|
|
1501
|
+
...vcpus,
|
|
1502
|
+
tags: this.getTags(),
|
|
1503
|
+
...provider?.ports?.length ? { ports: provider.ports } : {}
|
|
1504
|
+
};
|
|
1505
|
+
const sandbox = await wrapVercelApiError("create sandbox", () => {
|
|
1506
|
+
if (snapshotId) {
|
|
1507
|
+
return VercelSandbox.create({
|
|
1508
|
+
...base,
|
|
1509
|
+
source: { type: "snapshot", snapshotId }
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
if (provider?.gitSource) {
|
|
1513
|
+
const git = provider.gitSource;
|
|
1514
|
+
const source = {
|
|
1515
|
+
type: "git",
|
|
1516
|
+
url: git.url,
|
|
1517
|
+
depth: git.depth,
|
|
1518
|
+
revision: git.revision,
|
|
1519
|
+
...git.username && git.password ? { username: git.username, password: git.password } : {}
|
|
1520
|
+
};
|
|
1521
|
+
return VercelSandbox.create({ ...base, runtime, source });
|
|
1522
|
+
}
|
|
1523
|
+
return VercelSandbox.create({ ...base, runtime });
|
|
1524
|
+
});
|
|
1525
|
+
this.sandbox = sandbox;
|
|
1526
|
+
if (this.workingDir !== "/vercel/sandbox") {
|
|
1527
|
+
await wrapVercelApiError(
|
|
1528
|
+
"create working directory",
|
|
1529
|
+
() => sandbox.runCommand({
|
|
1530
|
+
cmd: "mkdir",
|
|
1531
|
+
args: ["-p", this.workingDir],
|
|
1532
|
+
sudo: true
|
|
1533
|
+
})
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
async run(command, options) {
|
|
1538
|
+
await this.ensureProvisioned();
|
|
1539
|
+
const sandbox = this.requireSandbox();
|
|
1540
|
+
const signal = buildTimeoutSignal(options?.timeoutMs);
|
|
1541
|
+
const result = await wrapVercelApiError(
|
|
1542
|
+
"run command",
|
|
1543
|
+
() => sandbox.runCommand({
|
|
1544
|
+
cmd: "sh",
|
|
1545
|
+
args: ["-lc", toShellCommand(command)],
|
|
1546
|
+
cwd: options?.cwd ?? this.workingDir,
|
|
1547
|
+
env: this.getMergedEnv(options?.env),
|
|
1548
|
+
...signal ? { signal } : {}
|
|
1549
|
+
})
|
|
1550
|
+
);
|
|
1551
|
+
const [stdout, stderr] = await Promise.all([
|
|
1552
|
+
result.stdout(),
|
|
1553
|
+
result.stderr()
|
|
1554
|
+
]);
|
|
1555
|
+
return {
|
|
1556
|
+
exitCode: result.exitCode,
|
|
1557
|
+
stdout,
|
|
1558
|
+
stderr,
|
|
1559
|
+
combinedOutput: `${stdout}${stderr}`,
|
|
1560
|
+
raw: result
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
async runAsync(command, options) {
|
|
1564
|
+
await this.ensureProvisioned();
|
|
1565
|
+
const sandbox = this.requireSandbox();
|
|
1566
|
+
const signal = buildTimeoutSignal(options?.timeoutMs);
|
|
1567
|
+
const cmd = await wrapVercelApiError(
|
|
1568
|
+
"start async command",
|
|
1569
|
+
() => sandbox.runCommand({
|
|
1570
|
+
cmd: "sh",
|
|
1571
|
+
args: ["-lc", toShellCommand(command)],
|
|
1572
|
+
cwd: options?.cwd ?? this.workingDir,
|
|
1573
|
+
env: this.getMergedEnv(options?.env),
|
|
1574
|
+
detached: true,
|
|
1575
|
+
...signal ? { signal } : {}
|
|
1576
|
+
})
|
|
1577
|
+
);
|
|
1578
|
+
const queue = new AsyncQueue();
|
|
1579
|
+
let stdout = "";
|
|
1580
|
+
let stderr = "";
|
|
1581
|
+
const completion = (async () => {
|
|
1582
|
+
for await (const log of cmd.logs()) {
|
|
1583
|
+
if (log.stream === "stdout") {
|
|
1584
|
+
stdout += log.data;
|
|
1585
|
+
queue.push({
|
|
1586
|
+
type: "stdout",
|
|
1587
|
+
chunk: log.data,
|
|
1588
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1589
|
+
});
|
|
1590
|
+
} else {
|
|
1591
|
+
stderr += log.data;
|
|
1592
|
+
queue.push({
|
|
1593
|
+
type: "stderr",
|
|
1594
|
+
chunk: log.data,
|
|
1595
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
const finished2 = await cmd.wait();
|
|
1600
|
+
const exitCode = finished2.exitCode;
|
|
1601
|
+
queue.push({
|
|
1602
|
+
type: "exit",
|
|
1603
|
+
exitCode,
|
|
1604
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1605
|
+
});
|
|
1606
|
+
queue.finish();
|
|
1607
|
+
return {
|
|
1608
|
+
exitCode,
|
|
1609
|
+
stdout,
|
|
1610
|
+
stderr,
|
|
1611
|
+
combinedOutput: `${stdout}${stderr}`,
|
|
1612
|
+
raw: finished2
|
|
1613
|
+
};
|
|
1614
|
+
})().catch((error) => {
|
|
1615
|
+
queue.fail(error);
|
|
1616
|
+
throw error;
|
|
1617
|
+
});
|
|
1618
|
+
suppressUnhandledRejection(completion);
|
|
1619
|
+
return {
|
|
1620
|
+
id: cmd.cmdId,
|
|
1621
|
+
raw: cmd,
|
|
1622
|
+
wait: () => completion,
|
|
1623
|
+
kill: async () => {
|
|
1624
|
+
await cmd.kill();
|
|
1625
|
+
},
|
|
1626
|
+
[Symbol.asyncIterator]: () => queue[Symbol.asyncIterator]()
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
async list(options) {
|
|
1630
|
+
const filterTags = options?.tags ?? this.getTags();
|
|
1631
|
+
const sandboxes = await this.listSandboxesByTags(filterTags);
|
|
1632
|
+
return sandboxes.map(
|
|
1633
|
+
(s) => ({
|
|
1634
|
+
provider: this.provider,
|
|
1635
|
+
id: s.name,
|
|
1636
|
+
state: s.status,
|
|
1637
|
+
tags: s.tags ?? {},
|
|
1638
|
+
createdAt: new Date(s.createdAt).toISOString(),
|
|
1639
|
+
raw: s
|
|
1640
|
+
})
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
async snapshot() {
|
|
1644
|
+
await this.ensureProvisioned();
|
|
1645
|
+
const sandbox = this.requireSandbox();
|
|
1646
|
+
const snap = await wrapVercelApiError(
|
|
1647
|
+
"snapshot sandbox",
|
|
1648
|
+
() => sandbox.snapshot()
|
|
1649
|
+
);
|
|
1650
|
+
return snap.snapshotId;
|
|
1651
|
+
}
|
|
1652
|
+
async stop() {
|
|
1653
|
+
const sandbox = this.sandbox;
|
|
1654
|
+
if (!sandbox) {
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
await wrapVercelApiError("stop sandbox", () => sandbox.stop());
|
|
1658
|
+
this.sandbox = void 0;
|
|
1659
|
+
}
|
|
1660
|
+
async delete() {
|
|
1661
|
+
await this.stop();
|
|
1662
|
+
}
|
|
1663
|
+
async openPort(_port) {
|
|
1664
|
+
void _port;
|
|
1665
|
+
}
|
|
1666
|
+
async getPreviewLink(port) {
|
|
1667
|
+
await this.ensureProvisioned();
|
|
1668
|
+
const sandbox = this.requireSandbox();
|
|
1669
|
+
return sandbox.domain(port);
|
|
1670
|
+
}
|
|
1671
|
+
async uploadFile(content, targetPath) {
|
|
1672
|
+
await this.ensureProvisioned();
|
|
1673
|
+
const sandbox = this.requireSandbox();
|
|
1674
|
+
const data = typeof content === "string" ? content : new Uint8Array(content);
|
|
1675
|
+
await sandbox.writeFiles([{ path: targetPath, content: data }]);
|
|
1676
|
+
}
|
|
1677
|
+
async downloadFile(sourcePath) {
|
|
1678
|
+
await this.ensureProvisioned();
|
|
1679
|
+
const sandbox = this.requireSandbox();
|
|
1680
|
+
const result = await sandbox.readFileToBuffer({ path: sourcePath });
|
|
1681
|
+
if (!result) {
|
|
1682
|
+
throw new Error(`File not found in Vercel sandbox: ${sourcePath}`);
|
|
1683
|
+
}
|
|
1684
|
+
return result;
|
|
1685
|
+
}
|
|
1686
|
+
getTags() {
|
|
1687
|
+
return {
|
|
1688
|
+
"agentbox.provider": this.provider,
|
|
1689
|
+
...this.options.tags ?? {}
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
async listSandboxesByTags(tags) {
|
|
1693
|
+
const credentials = getCredentials(this.options);
|
|
1694
|
+
const result = await wrapVercelApiError(
|
|
1695
|
+
"list sandboxes",
|
|
1696
|
+
() => VercelSandbox.list({
|
|
1697
|
+
...credentials,
|
|
1698
|
+
tags: pickFirstTag(tags)
|
|
1699
|
+
})
|
|
1700
|
+
);
|
|
1701
|
+
return result.sandboxes.filter(
|
|
1702
|
+
(s) => matchesAllTags(s.tags, tags)
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
async findExistingSandbox() {
|
|
1706
|
+
const credentials = getCredentials(this.options);
|
|
1707
|
+
const sandboxes = await this.listSandboxesByTags(this.getTags());
|
|
1708
|
+
const match = sandboxes.find((s) => s.status === "running");
|
|
1709
|
+
if (!match) {
|
|
1710
|
+
return void 0;
|
|
1711
|
+
}
|
|
1712
|
+
return wrapVercelApiError(
|
|
1713
|
+
"get sandbox",
|
|
1714
|
+
() => VercelSandbox.get({ ...credentials, name: match.name })
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
requireSandbox() {
|
|
1718
|
+
if (!this.sandbox) {
|
|
1719
|
+
throw new Error("Vercel sandbox has not been provisioned.");
|
|
1720
|
+
}
|
|
1721
|
+
return this.sandbox;
|
|
1722
|
+
}
|
|
1723
|
+
};
|
|
1724
|
+
|
|
1350
1725
|
// src/sandboxes/Sandbox.ts
|
|
1351
1726
|
function createSandboxAdapter(provider, options) {
|
|
1352
1727
|
switch (provider) {
|
|
1353
|
-
case
|
|
1728
|
+
case SandboxProvider.LocalDocker:
|
|
1354
1729
|
return new LocalDockerSandboxAdapter(
|
|
1355
1730
|
options
|
|
1356
1731
|
);
|
|
1357
|
-
case
|
|
1732
|
+
case SandboxProvider.Modal:
|
|
1358
1733
|
return new ModalSandboxAdapter(
|
|
1359
1734
|
options
|
|
1360
1735
|
);
|
|
1361
|
-
case
|
|
1736
|
+
case SandboxProvider.Daytona:
|
|
1362
1737
|
return new DaytonaSandboxAdapter(
|
|
1363
1738
|
options
|
|
1364
1739
|
);
|
|
1365
|
-
case
|
|
1740
|
+
case SandboxProvider.Vercel:
|
|
1741
|
+
return new VercelSandboxAdapter(
|
|
1742
|
+
options
|
|
1743
|
+
);
|
|
1744
|
+
case SandboxProvider.E2B:
|
|
1366
1745
|
return new E2bSandboxAdapter(
|
|
1367
1746
|
options
|
|
1368
1747
|
);
|
|
@@ -1427,6 +1806,15 @@ var Sandbox = class {
|
|
|
1427
1806
|
async getPreviewLink(port) {
|
|
1428
1807
|
return this.adapter.getPreviewLink(port);
|
|
1429
1808
|
}
|
|
1809
|
+
get previewHeaders() {
|
|
1810
|
+
return this.adapter.previewHeaders;
|
|
1811
|
+
}
|
|
1812
|
+
async uploadFile(content, targetPath) {
|
|
1813
|
+
return this.adapter.uploadFile(content, targetPath);
|
|
1814
|
+
}
|
|
1815
|
+
async downloadFile(sourcePath) {
|
|
1816
|
+
return this.adapter.downloadFile(sourcePath);
|
|
1817
|
+
}
|
|
1430
1818
|
};
|
|
1431
1819
|
|
|
1432
1820
|
export {
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
toShellCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NSJM57Z4.js";
|
|
5
|
+
import {
|
|
6
|
+
SandboxProvider
|
|
7
|
+
} from "./chunk-2NKMDGYH.js";
|
|
5
8
|
|
|
6
9
|
// src/sandbox-images/build.ts
|
|
7
10
|
import { existsSync } from "fs";
|
|
@@ -77,14 +80,18 @@ async function buildSandboxImage(options) {
|
|
|
77
80
|
options.cwd
|
|
78
81
|
);
|
|
79
82
|
switch (options.provider) {
|
|
80
|
-
case
|
|
83
|
+
case SandboxProvider.LocalDocker:
|
|
81
84
|
return buildLocalDockerImage(definition, options);
|
|
82
|
-
case
|
|
85
|
+
case SandboxProvider.Modal:
|
|
83
86
|
return buildModalImage(definition, options);
|
|
84
|
-
case
|
|
87
|
+
case SandboxProvider.Daytona:
|
|
85
88
|
return buildDaytonaSnapshot(definition, options);
|
|
86
|
-
case
|
|
89
|
+
case SandboxProvider.E2B:
|
|
87
90
|
return buildE2bTemplate(definition, options);
|
|
91
|
+
default:
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Image building is not supported for provider "${options.provider}". Vercel sandboxes use runtime snapshots instead \u2014 see Sandbox.snapshot().`
|
|
94
|
+
);
|
|
88
95
|
}
|
|
89
96
|
}
|
|
90
97
|
async function loadSandboxImageDefinition(preset, file, cwd = process.cwd()) {
|
|
@@ -371,7 +378,7 @@ function parseOptions(args) {
|
|
|
371
378
|
return options;
|
|
372
379
|
}
|
|
373
380
|
function isSandboxProviderName(value) {
|
|
374
|
-
return value
|
|
381
|
+
return Object.values(SandboxProvider).includes(value);
|
|
375
382
|
}
|
|
376
383
|
function isBuiltInImageName(value) {
|
|
377
384
|
return value === "browser-agent" || value === "computer-use";
|
|
@@ -380,8 +387,8 @@ function printHelp() {
|
|
|
380
387
|
process.stdout.write(`agentbox
|
|
381
388
|
|
|
382
389
|
Usage:
|
|
383
|
-
agentbox image build --provider <local-docker|modal|daytona|e2b> --preset <browser-agent|computer-use>
|
|
384
|
-
agentbox image build --provider <local-docker|modal|daytona|e2b> --file <path>
|
|
390
|
+
agentbox image build --provider <local-docker|modal|daytona|vercel|e2b> --preset <browser-agent|computer-use>
|
|
391
|
+
agentbox image build --provider <local-docker|modal|daytona|vercel|e2b> --file <path>
|
|
385
392
|
|
|
386
393
|
Options:
|
|
387
394
|
--image-name <name> Override the built artifact name
|
|
@@ -390,6 +397,7 @@ Options:
|
|
|
390
397
|
Environment:
|
|
391
398
|
Modal: MODAL_TOKEN_ID, MODAL_TOKEN_SECRET, MODAL_ENVIRONMENT?, MODAL_ENDPOINT?
|
|
392
399
|
Daytona: DAYTONA_API_KEY or DAYTONA_JWT_TOKEN, DAYTONA_ORGANIZATION_ID?, DAYTONA_API_URL?, DAYTONA_TARGET?
|
|
400
|
+
Vercel: VERCEL_TOKEN, VERCEL_TEAM_ID, VERCEL_PROJECT_ID
|
|
393
401
|
E2B: E2B_API_KEY, E2B_DOMAIN?, E2B_ACCESS_TOKEN?
|
|
394
402
|
`);
|
|
395
403
|
}
|
package/dist/enums.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure value-level provider enums.
|
|
3
|
+
*
|
|
4
|
+
* This file MUST NOT import anything from the rest of the SDK. It is used as
|
|
5
|
+
* a standalone tsup entry (`agentbox-sdk/enums`) so that client-side bundles
|
|
6
|
+
* in Next.js/browser contexts can reference `AgentProvider`/`SandboxProvider`
|
|
7
|
+
* without pulling in server-only modules like `net`, `crypto`, or the sandbox
|
|
8
|
+
* provider adapters.
|
|
9
|
+
*/
|
|
10
|
+
declare const AgentProvider: {
|
|
11
|
+
readonly ClaudeCode: "claude-code";
|
|
12
|
+
readonly OpenCode: "opencode";
|
|
13
|
+
readonly Codex: "codex";
|
|
14
|
+
};
|
|
15
|
+
type AgentProvider = (typeof AgentProvider)[keyof typeof AgentProvider];
|
|
16
|
+
declare const SandboxProvider: {
|
|
17
|
+
readonly LocalDocker: "local-docker";
|
|
18
|
+
readonly Modal: "modal";
|
|
19
|
+
readonly Daytona: "daytona";
|
|
20
|
+
readonly Vercel: "vercel";
|
|
21
|
+
readonly E2B: "e2b";
|
|
22
|
+
};
|
|
23
|
+
type SandboxProvider = (typeof SandboxProvider)[keyof typeof SandboxProvider];
|
|
24
|
+
|
|
25
|
+
export { AgentProvider, SandboxProvider };
|