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.
@@ -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
- } from "./chunk-HMBWQSVN.js";
8
+ sleep,
9
+ suppressUnhandledRejection
10
+ } from "./chunk-O7HCJXKW.js";
9
11
  import {
10
12
  shellQuote,
11
13
  toShellCommand
12
- } from "./chunk-JFDP556Q.js";
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 "daytona";
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 "e2b";
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 "local-docker";
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 "modal";
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: this.options.provider?.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
- provider.unencryptedPorts = provider.unencryptedPorts?.includes(port) ? provider.unencryptedPorts : [...provider.unencryptedPorts ?? [], port];
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 "local-docker":
1728
+ case SandboxProvider.LocalDocker:
1354
1729
  return new LocalDockerSandboxAdapter(
1355
1730
  options
1356
1731
  );
1357
- case "modal":
1732
+ case SandboxProvider.Modal:
1358
1733
  return new ModalSandboxAdapter(
1359
1734
  options
1360
1735
  );
1361
- case "daytona":
1736
+ case SandboxProvider.Daytona:
1362
1737
  return new DaytonaSandboxAdapter(
1363
1738
  options
1364
1739
  );
1365
- case "e2b":
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-JFDP556Q.js";
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 "local-docker":
83
+ case SandboxProvider.LocalDocker:
81
84
  return buildLocalDockerImage(definition, options);
82
- case "modal":
85
+ case SandboxProvider.Modal:
83
86
  return buildModalImage(definition, options);
84
- case "daytona":
87
+ case SandboxProvider.Daytona:
85
88
  return buildDaytonaSnapshot(definition, options);
86
- case "e2b":
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 === "local-docker" || value === "modal" || value === "daytona" || value === "e2b";
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
  }
@@ -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 };
package/dist/enums.js ADDED
@@ -0,0 +1,8 @@
1
+ import {
2
+ AgentProvider,
3
+ SandboxProvider
4
+ } from "./chunk-2NKMDGYH.js";
5
+ export {
6
+ AgentProvider,
7
+ SandboxProvider
8
+ };