@madarco/agentbox 0.8.0 → 0.10.0

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +161 -0
  3. package/dist/{_cloud-attach-T727ZPRV.js → _cloud-attach-O6NYTLES.js} +4 -4
  4. package/dist/{chunk-67N47KUS.js → chunk-2GPORKYF.js} +349 -182
  5. package/dist/chunk-2GPORKYF.js.map +1 -0
  6. package/dist/{chunk-6OZDFNBF.js → chunk-7UIAO7PC.js} +401 -82
  7. package/dist/chunk-7UIAO7PC.js.map +1 -0
  8. package/dist/{chunk-BGK32PZE.js → chunk-KL36BRN4.js} +2 -2
  9. package/dist/chunk-KL36BRN4.js.map +1 -0
  10. package/dist/chunk-MTVI44DW.js +662 -0
  11. package/dist/chunk-MTVI44DW.js.map +1 -0
  12. package/dist/{chunk-FODMEHD3.js → chunk-R4O5WPHW.js} +705 -77
  13. package/dist/chunk-R4O5WPHW.js.map +1 -0
  14. package/dist/{dist-ZODPD2I6.js → dist-5FQGYRW5.js} +20 -10
  15. package/dist/dist-5FQGYRW5.js.map +1 -0
  16. package/dist/{dist-LOZBWMBF.js → dist-BQNX7RQE.js} +19 -3
  17. package/dist/dist-PZW3GWWU.js +874 -0
  18. package/dist/dist-PZW3GWWU.js.map +1 -0
  19. package/dist/{dist-L4LCG5SJ.js → dist-TMHSUVTP.js} +4 -4
  20. package/dist/index.js +2385 -842
  21. package/dist/index.js.map +1 -1
  22. package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js → prepared-state-CL4CWXQA-H5THETIM.js} +2 -2
  23. package/package.json +11 -7
  24. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +9 -8
  25. package/runtime/docker/packages/ctl/dist/bin.cjs +129 -31
  26. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  27. package/runtime/hetzner/agentbox-setup-skill.md +9 -8
  28. package/runtime/hetzner/agentbox-vnc-start +15 -1
  29. package/runtime/hetzner/ctl.cjs +129 -31
  30. package/runtime/relay/bin.cjs +260 -39
  31. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  32. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  33. package/runtime/vercel/agentbox-open +28 -0
  34. package/runtime/vercel/agentbox-setup-skill.md +197 -0
  35. package/runtime/vercel/agentbox-vnc-start +91 -0
  36. package/runtime/vercel/claude-managed-settings.json +115 -0
  37. package/runtime/vercel/ctl.cjs +23495 -0
  38. package/runtime/vercel/custom-system-CLAUDE.md +47 -0
  39. package/runtime/vercel/gh-shim +263 -0
  40. package/runtime/vercel/git-shim +131 -0
  41. package/runtime/vercel/scripts/provision.sh +314 -0
  42. package/share/agentbox-setup/SKILL.md +9 -8
  43. package/dist/chunk-67N47KUS.js.map +0 -1
  44. package/dist/chunk-6OZDFNBF.js.map +0 -1
  45. package/dist/chunk-BGK32PZE.js.map +0 -1
  46. package/dist/chunk-FODMEHD3.js.map +0 -1
  47. package/dist/dist-ZODPD2I6.js.map +0 -1
  48. /package/dist/{_cloud-attach-T727ZPRV.js.map → _cloud-attach-O6NYTLES.js.map} +0 -0
  49. /package/dist/{dist-LOZBWMBF.js.map → dist-BQNX7RQE.js.map} +0 -0
  50. /package/dist/{dist-L4LCG5SJ.js.map → dist-TMHSUVTP.js.map} +0 -0
  51. /package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js.map → prepared-state-CL4CWXQA-H5THETIM.js.map} +0 -0
@@ -18469,8 +18469,8 @@ var KEY_REGISTRY = [
18469
18469
  {
18470
18470
  key: "box.provider",
18471
18471
  type: "enum",
18472
- enumValues: ["docker", "daytona", "hetzner"],
18473
- description: "Sandbox backend new boxes are created on: local Docker containers, Daytona Cloud sandboxes, or Hetzner Cloud VPSes."
18472
+ enumValues: ["docker", "daytona", "hetzner", "vercel"],
18473
+ description: "Sandbox backend new boxes are created on: local Docker containers, Daytona Cloud sandboxes, Hetzner Cloud VPSes, or Vercel Sandboxes."
18474
18474
  },
18475
18475
  {
18476
18476
  key: "box.hostSnapshot",
@@ -18500,6 +18500,12 @@ var KEY_REGISTRY = [
18500
18500
  description: "Per-provider override of `box.defaultCheckpoint` for hetzner. Wins over the global when set; set via `agentbox checkpoint set-default --provider hetzner`.",
18501
18501
  advanced: true
18502
18502
  },
18503
+ {
18504
+ key: "box.defaultCheckpointVercel",
18505
+ type: "string",
18506
+ description: "Per-provider override of `box.defaultCheckpoint` for vercel. Wins over the global when set; set via `agentbox checkpoint set-default --provider vercel`.",
18507
+ advanced: true
18508
+ },
18503
18509
  {
18504
18510
  key: "checkpoint.maxLayers",
18505
18511
  type: "int",
@@ -18573,16 +18579,41 @@ var KEY_REGISTRY = [
18573
18579
  type: "int",
18574
18580
  description: "Cap git bundle history shipped to cloud sandboxes (daytona, hetzner). 0 = full history. Unset = adaptive default (last 200 commits; re-bundle at 100 if the bundle exceeds 20 MB). Ignored for docker (which bind-mounts .git/)."
18575
18581
  },
18582
+ {
18583
+ key: "box.vercelVcpus",
18584
+ type: "int",
18585
+ description: "vCPUs for new --provider vercel boxes (Vercel couples RAM at 2048 MB/vCPU). Default 2. Vercel only accepts specific counts (e.g. 1, 2, 4, 8) \u2014 an unsupported value fails create with a 400. Vercel-only; ignored by other providers."
18586
+ },
18587
+ {
18588
+ key: "box.vercelTimeoutMs",
18589
+ type: "int",
18590
+ description: "Max session length (ms) for new --provider vercel boxes before the VM auto-snapshots; persistent mode auto-resumes on the next call. Default 2700000 (45 min, the Hobby ceiling). Vercel-only."
18591
+ },
18592
+ {
18593
+ key: "box.vercelNetworkPolicy",
18594
+ type: "string",
18595
+ description: "Egress lock for new --provider vercel boxes: 'allow-all' (default, unset), 'deny-all', or a comma-separated domain allowlist (e.g. 'github.com,*.npmjs.org') that denies everything else. Vercel-only; ignored by other providers."
18596
+ },
18576
18597
  {
18577
18598
  key: "claude.sessionName",
18578
18599
  type: "string",
18579
18600
  description: "tmux session name for `agentbox claude`."
18580
18601
  },
18602
+ {
18603
+ key: "claude.dangerouslySkipPermissions",
18604
+ type: "bool",
18605
+ description: "Launch claude in new boxes with --dangerously-skip-permissions (auto-accept tool use). Safe because boxes are isolated; on by default. Override per-box with --no-dangerously-skip-permissions."
18606
+ },
18581
18607
  {
18582
18608
  key: "codex.sessionName",
18583
18609
  type: "string",
18584
18610
  description: "tmux session name for `agentbox codex`."
18585
18611
  },
18612
+ {
18613
+ key: "codex.dangerouslySkipPermissions",
18614
+ type: "bool",
18615
+ description: "Launch codex in new boxes with --dangerously-bypass-approvals-and-sandbox (never prompt for approval). Safe because boxes are isolated; on by default. Override per-box with --no-dangerously-skip-permissions."
18616
+ },
18586
18617
  {
18587
18618
  key: "opencode.sessionName",
18588
18619
  type: "string",
@@ -18690,6 +18721,21 @@ var KEY_REGISTRY = [
18690
18721
  type: "int",
18691
18722
  description: "Max number of simultaneously-running boxes (across providers) before background `-i` jobs queue up instead of starting immediately. Per-invocation override: `--max-running <n>`."
18692
18723
  },
18724
+ {
18725
+ key: "queue.maxWorking",
18726
+ type: "int",
18727
+ description: "Max agents actively working/thinking (quota-consuming) at once before background `-i` jobs queue. 0 = disabled (use the queue.maxConcurrent running-box gate). Counts all boxes, foreground + queued. Per-invocation override: `--max-working <n>`."
18728
+ },
18729
+ {
18730
+ key: "queue.idleGraceSeconds",
18731
+ type: "int",
18732
+ description: "Seconds an agent must stay non-working before it frees its working slot (debounce against brief idle flaps between turns). Only used when queue.maxWorking > 0."
18733
+ },
18734
+ {
18735
+ key: "cloud.useCurrentBranch",
18736
+ type: "bool",
18737
+ description: "On cloud providers (daytona/hetzner), start new boxes on the host's current branch instead of forking a new agentbox/<box-name> branch. Overridden by an explicit --use-branch / --from-branch."
18738
+ },
18693
18739
  {
18694
18740
  key: "maintenance.pruneProjectConfigs",
18695
18741
  type: "bool",
@@ -19119,6 +19165,23 @@ function isGhPrOp(value) {
19119
19165
  return GH_PR_OPS.includes(value);
19120
19166
  }
19121
19167
  var GH_PR_READ_ONLY_OPS = /* @__PURE__ */ new Set(["view", "list"]);
19168
+ function injectPrCreateHead(op, branch, args) {
19169
+ if (op !== "create") return args;
19170
+ if (!branch || branch === "HEAD") return args;
19171
+ if (hasHeadArg(args)) return args;
19172
+ return ["--head", branch, ...args];
19173
+ }
19174
+ function hasHeadArg(args) {
19175
+ return args.some((a2) => a2 === "--head" || a2.startsWith("--head=") || a2.startsWith("-H"));
19176
+ }
19177
+ function prCreateNeedsHead(op, args) {
19178
+ return op === "create" && !hasHeadArg(args);
19179
+ }
19180
+ var PR_CREATE_NO_HEAD_REFUSAL = {
19181
+ exitCode: 65,
19182
+ stdout: "",
19183
+ stderr: "gh pr create: refusing to run without --head \u2014 could not resolve this box's branch, and falling back to the host repo's checked-out branch would open a PR for the wrong branch. Ensure the box branch is pushed, or pass --head <branch> explicitly.\n"
19184
+ };
19122
19185
  var GH_RPC_TIMEOUT_MS = 12e4;
19123
19186
  var GH_READY_CACHE_TTL_MS = 6e4;
19124
19187
  var ghReadyCache;
@@ -19320,36 +19383,31 @@ var BoxStatusStore = class {
19320
19383
  async function resolveCloudBackend(name) {
19321
19384
  if (name === "daytona") {
19322
19385
  const pkg = "@agentbox/sandbox-daytona";
19323
- try {
19324
- const mod = await import(pkg);
19325
- return mod.daytonaBackend;
19326
- } catch (err) {
19327
- const msg = err instanceof Error ? err.message : String(err);
19328
- if (/cannot find module|MODULE_NOT_FOUND/i.test(msg)) {
19329
- throw new Error(
19330
- `relay: cannot load '${pkg}' at runtime \u2014 install it alongside @agentbox/relay (the @madarco/agentbox CLI normally provides this dependency). Original: ${msg}`
19331
- );
19332
- }
19333
- throw err;
19334
- }
19386
+ return loadCloudBackend(pkg, async () => (await import(pkg)).daytonaBackend);
19335
19387
  }
19336
19388
  if (name === "hetzner") {
19337
19389
  const pkg = "@agentbox/sandbox-hetzner";
19338
- try {
19339
- const mod = await import(pkg);
19340
- return mod.hetznerBackend;
19341
- } catch (err) {
19342
- const msg = err instanceof Error ? err.message : String(err);
19343
- if (/cannot find module|MODULE_NOT_FOUND/i.test(msg)) {
19344
- throw new Error(
19345
- `relay: cannot load '${pkg}' at runtime \u2014 install it alongside @agentbox/relay (the @madarco/agentbox CLI normally provides this dependency). Original: ${msg}`
19346
- );
19347
- }
19348
- throw err;
19349
- }
19390
+ return loadCloudBackend(pkg, async () => (await import(pkg)).hetznerBackend);
19391
+ }
19392
+ if (name === "vercel") {
19393
+ const pkg = "@agentbox/sandbox-vercel";
19394
+ return loadCloudBackend(pkg, async () => (await import(pkg)).vercelBackend);
19350
19395
  }
19351
19396
  throw new Error(`no host executor for cloud backend '${name}'`);
19352
19397
  }
19398
+ async function loadCloudBackend(pkg, load2) {
19399
+ try {
19400
+ return await load2();
19401
+ } catch (err) {
19402
+ const msg = err instanceof Error ? err.message : String(err);
19403
+ if (/cannot find module|MODULE_NOT_FOUND/i.test(msg)) {
19404
+ throw new Error(
19405
+ `relay: cannot load '${pkg}' at runtime \u2014 install it alongside @agentbox/relay (the @madarco/agentbox CLI normally provides this dependency). Original: ${msg}`
19406
+ );
19407
+ }
19408
+ throw err;
19409
+ }
19410
+ }
19353
19411
  async function refreshCloudPreviewUrl(backendName, boxId, port) {
19354
19412
  try {
19355
19413
  const backend = await resolveCloudBackend(backendName);
@@ -19477,7 +19535,20 @@ async function runGhPrRpc(action, deps) {
19477
19535
  }
19478
19536
  }
19479
19537
  }
19480
- return runHostGh(["pr", op, ...args], lookup.workspacePath);
19538
+ let finalArgs = args;
19539
+ if (op === "create" && !args.some((a2) => a2 === "--head" || a2.startsWith("--head="))) {
19540
+ const backend = await resolveCloudBackend(deps.backendName);
19541
+ const handle = { sandboxId: lookup.cloudSandboxId };
19542
+ const containerPath = params.path ?? "/workspace";
19543
+ const branchProbe = await backend.exec(
19544
+ handle,
19545
+ `git -C ${shellQuote(containerPath)} rev-parse --abbrev-ref HEAD`
19546
+ );
19547
+ const branch = branchProbe.exitCode === 0 ? (branchProbe.stdout ?? "").trim() : "";
19548
+ finalArgs = injectPrCreateHead(op, branch, args);
19549
+ }
19550
+ if (prCreateNeedsHead(op, finalArgs)) return PR_CREATE_NO_HEAD_REFUSAL;
19551
+ return runHostGh(["pr", op, ...finalArgs], lookup.workspacePath);
19481
19552
  }
19482
19553
  async function runBrowserOpenMirror(action, deps) {
19483
19554
  const params = action.params ?? {};
@@ -19596,6 +19667,18 @@ async function runCheckpointRpc(action, deps) {
19596
19667
  stderr: "relay: AGENTBOX_CLI_ENTRY not set; cannot run checkpoint host-side\n"
19597
19668
  };
19598
19669
  }
19670
+ if (deps.backendName === "vercel" && deps.prompts && deps.subscribers && deps.subscribers.forBox(deps.boxId).length > 0) {
19671
+ const verdict = await askPrompt(deps.prompts, deps.subscribers, deps.boxId, {
19672
+ kind: "confirm",
19673
+ message: `Create checkpoint on ${deps.boxName ?? deps.boxId}? The vercel box will stop and reboot.`,
19674
+ detail: params.name ? `checkpoint: ${params.name}` : "(auto-named)",
19675
+ defaultAnswer: "n",
19676
+ context: { command: "checkpoint create", argv: params.name ? [params.name] : [] }
19677
+ });
19678
+ if (verdict.answer !== "y") {
19679
+ return { exitCode: 10, stdout: "", stderr: "checkpoint denied by user\n" };
19680
+ }
19681
+ }
19599
19682
  const argv = [process.execPath, entry, "checkpoint", "create", deps.boxId];
19600
19683
  if (params.name) argv.push("--name", params.name);
19601
19684
  if (params.merged === true) argv.push("--merged");
@@ -19947,7 +20030,18 @@ function createRelayServer(opts) {
19947
20030
  const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "relay"}`);
19948
20031
  const route = `${req.method ?? "GET"} ${url.pathname}`;
19949
20032
  if (route === "GET /healthz") {
19950
- send(res, 200, { ok: true, boxes: registry.size(), events: events.size() });
20033
+ send(res, 200, {
20034
+ ok: true,
20035
+ boxes: registry.size(),
20036
+ events: events.size(),
20037
+ pid: process.pid,
20038
+ cliEntry: Boolean(process.env.AGENTBOX_CLI_ENTRY),
20039
+ // The spawning CLI's version/commit (inherited via env at spawn time).
20040
+ // `version` lets host-side ensureRelay reclaim a relay left over from a
20041
+ // different agentbox version; `commit` is observability-only.
20042
+ version: process.env.AGENTBOX_CLI_VERSION || void 0,
20043
+ commit: process.env.AGENTBOX_CLI_COMMIT || void 0
20044
+ });
19951
20045
  return;
19952
20046
  }
19953
20047
  if (url.pathname.startsWith("/bridge/")) {
@@ -20605,7 +20699,9 @@ async function handleGhPrRpc(op, reg, params, prompts, subscribers, hostInitiate
20605
20699
  return { exitCode: 10, stdout: "", stderr: "denied by user\n" };
20606
20700
  }
20607
20701
  }
20608
- return runHostGh(["pr", op, ...args], worktree.hostMainRepo);
20702
+ const finalArgs = injectPrCreateHead(op, worktree.branch, args);
20703
+ if (prCreateNeedsHead(op, finalArgs)) return PR_CREATE_NO_HEAD_REFUSAL;
20704
+ return runHostGh(["pr", op, ...finalArgs], worktree.hostMainRepo);
20609
20705
  }
20610
20706
  async function handleCpRpc(reg, method, params) {
20611
20707
  const entry = process.env.AGENTBOX_CLI_ENTRY;
@@ -21888,6 +21984,7 @@ var Supervisor = class extends import_node_events15.EventEmitter {
21888
21984
  super();
21889
21985
  this.opts = opts;
21890
21986
  this.relay = new RelayClient();
21987
+ this.webProxy = new WebProxy(opts.webProxyPort);
21891
21988
  }
21892
21989
  opts;
21893
21990
  units = /* @__PURE__ */ new Map();
@@ -21897,7 +21994,7 @@ var Supervisor = class extends import_node_events15.EventEmitter {
21897
21994
  scheduling = false;
21898
21995
  rescheduleDirty = false;
21899
21996
  relay;
21900
- webProxy = new WebProxy();
21997
+ webProxy;
21901
21998
  /** The relay client the supervisor pushes state events on. Shared with the
21902
21999
  * status reporter so both use the same fire-and-forget channel. */
21903
22000
  get relayClient() {
@@ -22824,7 +22921,8 @@ function resolveBoxRelayPort() {
22824
22921
  }
22825
22922
  var daemonCommand = new Command("daemon").description("Run the agentbox-ctl supervisor in the foreground").option("--socket <path>", "unix socket path", DEFAULT_SOCKET_PATH).option("--config <path>", "path to agentbox.yaml", DEFAULT_CONFIG_PATH).option("--log-dir <path>", "where per-service log files are written", DEFAULT_LOG_DIR).option("--workspace <path>", "cwd for service processes", "/workspace").action(async (opts) => {
22826
22923
  const cfg = await loadConfig(opts.config);
22827
- const sup = new Supervisor({ workspace: opts.workspace, logDir: opts.logDir });
22924
+ const webProxyPort = Number(process.env.AGENTBOX_WEB_PROXY_PORT) || void 0;
22925
+ const sup = new Supervisor({ workspace: opts.workspace, logDir: opts.logDir, webProxyPort });
22828
22926
  await sup.init(cfg);
22829
22927
  const reporter = new StatusReporter({
22830
22928
  supervisor: sup,