@madarco/agentbox 0.9.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 (36) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +161 -0
  3. package/dist/{_cloud-attach-ZXBCNWJX.js → _cloud-attach-O6NYTLES.js} +3 -3
  4. package/dist/{chunk-BXQMIEHC.js → chunk-2GPORKYF.js} +254 -162
  5. package/dist/chunk-2GPORKYF.js.map +1 -0
  6. package/dist/{chunk-NCJP5MTN.js → chunk-7UIAO7PC.js} +213 -51
  7. package/dist/chunk-7UIAO7PC.js.map +1 -0
  8. package/dist/{chunk-GU5LW4B5.js → chunk-R4O5WPHW.js} +374 -62
  9. package/dist/chunk-R4O5WPHW.js.map +1 -0
  10. package/dist/{dist-GDHP34ZK.js → dist-5FQGYRW5.js} +15 -3
  11. package/dist/dist-5FQGYRW5.js.map +1 -0
  12. package/dist/{dist-32EZBYG4.js → dist-BQNX7RQE.js} +12 -2
  13. package/dist/{dist-XML54CNB.js → dist-PZW3GWWU.js} +30 -5
  14. package/dist/dist-PZW3GWWU.js.map +1 -0
  15. package/dist/{dist-CX5CGVEB.js → dist-TMHSUVTP.js} +3 -3
  16. package/dist/index.js +1773 -526
  17. package/dist/index.js.map +1 -1
  18. package/package.json +9 -7
  19. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +9 -8
  20. package/runtime/docker/packages/ctl/dist/bin.cjs +32 -3
  21. package/runtime/hetzner/agentbox-setup-skill.md +9 -8
  22. package/runtime/hetzner/ctl.cjs +32 -3
  23. package/runtime/relay/bin.cjs +32 -3
  24. package/runtime/vercel/agentbox-setup-skill.md +9 -8
  25. package/runtime/vercel/ctl.cjs +32 -3
  26. package/runtime/vercel/custom-system-CLAUDE.md +1 -4
  27. package/runtime/vercel/scripts/provision.sh +40 -0
  28. package/share/agentbox-setup/SKILL.md +9 -8
  29. package/dist/chunk-BXQMIEHC.js.map +0 -1
  30. package/dist/chunk-GU5LW4B5.js.map +0 -1
  31. package/dist/chunk-NCJP5MTN.js.map +0 -1
  32. package/dist/dist-GDHP34ZK.js.map +0 -1
  33. package/dist/dist-XML54CNB.js.map +0 -1
  34. /package/dist/{_cloud-attach-ZXBCNWJX.js.map → _cloud-attach-O6NYTLES.js.map} +0 -0
  35. /package/dist/{dist-32EZBYG4.js.map → dist-BQNX7RQE.js.map} +0 -0
  36. /package/dist/{dist-CX5CGVEB.js.map → dist-TMHSUVTP.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madarco/agentbox",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Launch Claude Code, Codex, and other coding agents in isolated sandboxes",
5
5
  "license": "MIT",
6
6
  "author": "Marco D'Alia",
@@ -37,7 +37,8 @@
37
37
  "files": [
38
38
  "dist",
39
39
  "share",
40
- "runtime"
40
+ "runtime",
41
+ "CHANGELOG.md"
41
42
  ],
42
43
  "dependencies": {
43
44
  "@clack/prompts": "^0.9.0",
@@ -57,16 +58,16 @@
57
58
  "tsup": "^8.3.5",
58
59
  "typescript": "^5.7.2",
59
60
  "vitest": "^2.1.8",
61
+ "@agentbox/config": "0.0.0",
62
+ "@agentbox/ctl": "0.0.0",
60
63
  "@agentbox/sandbox-core": "0.0.0",
61
64
  "@agentbox/core": "0.0.0",
62
65
  "@agentbox/relay": "0.0.0",
63
- "@agentbox/ctl": "0.0.0",
64
- "@agentbox/config": "0.0.0",
65
66
  "@agentbox/sandbox-daytona": "0.0.0",
66
- "@agentbox/sandbox-vercel": "0.0.0",
67
- "@agentbox/sandbox-cloud": "0.0.0",
68
67
  "@agentbox/sandbox-docker": "0.0.0",
69
- "@agentbox/sandbox-hetzner": "0.0.0"
68
+ "@agentbox/sandbox-cloud": "0.0.0",
69
+ "@agentbox/sandbox-hetzner": "0.0.0",
70
+ "@agentbox/sandbox-vercel": "0.0.0"
70
71
  },
71
72
  "scripts": {
72
73
  "build": "tsup",
@@ -75,6 +76,7 @@
75
76
  "test": "vitest run",
76
77
  "typecheck": "tsc --noEmit",
77
78
  "clean": "rm -rf dist runtime .turbo",
79
+ "version": "git add CHANGELOG.md",
78
80
  "publish:patch": "npm version patch && git push --follow-tags",
79
81
  "publish:minor": "npm version minor && git push --follow-tags"
80
82
  }
@@ -164,14 +164,7 @@ services:
164
164
  - A service with `restart: never` and an autostart dependency will block the dependent forever after one failed run — usually a mistake.
165
165
  - `command:` is either a shell string (run via `bash -c`) or an argv array. Use the argv form if you need to avoid shell quoting.
166
166
 
167
- ## 8. Checkpoint the warm state (do this at the very end)
168
-
169
- Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
170
- Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
171
- This doesn't need to be confirmed by the user.
172
- It will pause the container for several seconds so warn the user about it and write Done when it's done.
173
-
174
- ## 9. Hand-off
167
+ ## 8. Hand-off
175
168
 
176
169
  Tell the user (verbatim):
177
170
 
@@ -187,6 +180,14 @@ Tell the user (verbatim):
187
180
  your box is ready, you can start more sessions with `agentbox claude`
188
181
  you can access the web app at https://<boxname>.localhost
189
182
 
183
+
184
+ ## 9. Checkpoint the warm state - DON't SKIP THIS STEP
185
+
186
+ Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
187
+ Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
188
+ On all providers except Vercel, this doesn't need to be confirmed by the user. It will pause the container for several seconds so warn the user about it and write Done when it's done.
189
+ On Vercel: this actually STOPS the sandbox, so warn the user about it. Also the system will ask confirmation.
190
+
190
191
  ## 10. Known issues
191
192
 
192
193
  - For Nextjs/Vite/Tasnstack projects, makes sure to forward also websocket for hot reload.
@@ -18599,11 +18599,21 @@ var KEY_REGISTRY = [
18599
18599
  type: "string",
18600
18600
  description: "tmux session name for `agentbox claude`."
18601
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
+ },
18602
18607
  {
18603
18608
  key: "codex.sessionName",
18604
18609
  type: "string",
18605
18610
  description: "tmux session name for `agentbox codex`."
18606
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
+ },
18607
18617
  {
18608
18618
  key: "opencode.sessionName",
18609
18619
  type: "string",
@@ -19657,6 +19667,18 @@ async function runCheckpointRpc(action, deps) {
19657
19667
  stderr: "relay: AGENTBOX_CLI_ENTRY not set; cannot run checkpoint host-side\n"
19658
19668
  };
19659
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
+ }
19660
19682
  const argv = [process.execPath, entry, "checkpoint", "create", deps.boxId];
19661
19683
  if (params.name) argv.push("--name", params.name);
19662
19684
  if (params.merged === true) argv.push("--merged");
@@ -20013,7 +20035,12 @@ function createRelayServer(opts) {
20013
20035
  boxes: registry.size(),
20014
20036
  events: events.size(),
20015
20037
  pid: process.pid,
20016
- cliEntry: Boolean(process.env.AGENTBOX_CLI_ENTRY)
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
20017
20044
  });
20018
20045
  return;
20019
20046
  }
@@ -21957,6 +21984,7 @@ var Supervisor = class extends import_node_events15.EventEmitter {
21957
21984
  super();
21958
21985
  this.opts = opts;
21959
21986
  this.relay = new RelayClient();
21987
+ this.webProxy = new WebProxy(opts.webProxyPort);
21960
21988
  }
21961
21989
  opts;
21962
21990
  units = /* @__PURE__ */ new Map();
@@ -21966,7 +21994,7 @@ var Supervisor = class extends import_node_events15.EventEmitter {
21966
21994
  scheduling = false;
21967
21995
  rescheduleDirty = false;
21968
21996
  relay;
21969
- webProxy = new WebProxy();
21997
+ webProxy;
21970
21998
  /** The relay client the supervisor pushes state events on. Shared with the
21971
21999
  * status reporter so both use the same fire-and-forget channel. */
21972
22000
  get relayClient() {
@@ -22893,7 +22921,8 @@ function resolveBoxRelayPort() {
22893
22921
  }
22894
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) => {
22895
22923
  const cfg = await loadConfig(opts.config);
22896
- 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 });
22897
22926
  await sup.init(cfg);
22898
22927
  const reporter = new StatusReporter({
22899
22928
  supervisor: sup,
@@ -164,14 +164,7 @@ services:
164
164
  - A service with `restart: never` and an autostart dependency will block the dependent forever after one failed run — usually a mistake.
165
165
  - `command:` is either a shell string (run via `bash -c`) or an argv array. Use the argv form if you need to avoid shell quoting.
166
166
 
167
- ## 8. Checkpoint the warm state (do this at the very end)
168
-
169
- Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
170
- Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
171
- This doesn't need to be confirmed by the user.
172
- It will pause the container for several seconds so warn the user about it and write Done when it's done.
173
-
174
- ## 9. Hand-off
167
+ ## 8. Hand-off
175
168
 
176
169
  Tell the user (verbatim):
177
170
 
@@ -187,6 +180,14 @@ Tell the user (verbatim):
187
180
  your box is ready, you can start more sessions with `agentbox claude`
188
181
  you can access the web app at https://<boxname>.localhost
189
182
 
183
+
184
+ ## 9. Checkpoint the warm state - DON't SKIP THIS STEP
185
+
186
+ Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
187
+ Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
188
+ On all providers except Vercel, this doesn't need to be confirmed by the user. It will pause the container for several seconds so warn the user about it and write Done when it's done.
189
+ On Vercel: this actually STOPS the sandbox, so warn the user about it. Also the system will ask confirmation.
190
+
190
191
  ## 10. Known issues
191
192
 
192
193
  - For Nextjs/Vite/Tasnstack projects, makes sure to forward also websocket for hot reload.
@@ -18599,11 +18599,21 @@ var KEY_REGISTRY = [
18599
18599
  type: "string",
18600
18600
  description: "tmux session name for `agentbox claude`."
18601
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
+ },
18602
18607
  {
18603
18608
  key: "codex.sessionName",
18604
18609
  type: "string",
18605
18610
  description: "tmux session name for `agentbox codex`."
18606
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
+ },
18607
18617
  {
18608
18618
  key: "opencode.sessionName",
18609
18619
  type: "string",
@@ -19657,6 +19667,18 @@ async function runCheckpointRpc(action, deps) {
19657
19667
  stderr: "relay: AGENTBOX_CLI_ENTRY not set; cannot run checkpoint host-side\n"
19658
19668
  };
19659
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
+ }
19660
19682
  const argv = [process.execPath, entry, "checkpoint", "create", deps.boxId];
19661
19683
  if (params.name) argv.push("--name", params.name);
19662
19684
  if (params.merged === true) argv.push("--merged");
@@ -20013,7 +20035,12 @@ function createRelayServer(opts) {
20013
20035
  boxes: registry.size(),
20014
20036
  events: events.size(),
20015
20037
  pid: process.pid,
20016
- cliEntry: Boolean(process.env.AGENTBOX_CLI_ENTRY)
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
20017
20044
  });
20018
20045
  return;
20019
20046
  }
@@ -21957,6 +21984,7 @@ var Supervisor = class extends import_node_events15.EventEmitter {
21957
21984
  super();
21958
21985
  this.opts = opts;
21959
21986
  this.relay = new RelayClient();
21987
+ this.webProxy = new WebProxy(opts.webProxyPort);
21960
21988
  }
21961
21989
  opts;
21962
21990
  units = /* @__PURE__ */ new Map();
@@ -21966,7 +21994,7 @@ var Supervisor = class extends import_node_events15.EventEmitter {
21966
21994
  scheduling = false;
21967
21995
  rescheduleDirty = false;
21968
21996
  relay;
21969
- webProxy = new WebProxy();
21997
+ webProxy;
21970
21998
  /** The relay client the supervisor pushes state events on. Shared with the
21971
21999
  * status reporter so both use the same fire-and-forget channel. */
21972
22000
  get relayClient() {
@@ -22893,7 +22921,8 @@ function resolveBoxRelayPort() {
22893
22921
  }
22894
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) => {
22895
22923
  const cfg = await loadConfig(opts.config);
22896
- 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 });
22897
22926
  await sup.init(cfg);
22898
22927
  const reporter = new StatusReporter({
22899
22928
  supervisor: sup,
@@ -18684,6 +18684,18 @@ async function runCheckpointRpc(action, deps) {
18684
18684
  stderr: "relay: AGENTBOX_CLI_ENTRY not set; cannot run checkpoint host-side\n"
18685
18685
  };
18686
18686
  }
18687
+ if (deps.backendName === "vercel" && deps.prompts && deps.subscribers && deps.subscribers.forBox(deps.boxId).length > 0) {
18688
+ const verdict = await askPrompt(deps.prompts, deps.subscribers, deps.boxId, {
18689
+ kind: "confirm",
18690
+ message: `Create checkpoint on ${deps.boxName ?? deps.boxId}? The vercel box will stop and reboot.`,
18691
+ detail: params.name ? `checkpoint: ${params.name}` : "(auto-named)",
18692
+ defaultAnswer: "n",
18693
+ context: { command: "checkpoint create", argv: params.name ? [params.name] : [] }
18694
+ });
18695
+ if (verdict.answer !== "y") {
18696
+ return { exitCode: 10, stdout: "", stderr: "checkpoint denied by user\n" };
18697
+ }
18698
+ }
18687
18699
  const argv = [process.execPath, entry, "checkpoint", "create", deps.boxId];
18688
18700
  if (params.name) argv.push("--name", params.name);
18689
18701
  if (params.merged === true) argv.push("--merged");
@@ -19300,7 +19312,12 @@ function createRelayServer(opts) {
19300
19312
  boxes: registry.size(),
19301
19313
  events: events.size(),
19302
19314
  pid: process.pid,
19303
- cliEntry: Boolean(process.env.AGENTBOX_CLI_ENTRY)
19315
+ cliEntry: Boolean(process.env.AGENTBOX_CLI_ENTRY),
19316
+ // The spawning CLI's version/commit (inherited via env at spawn time).
19317
+ // `version` lets host-side ensureRelay reclaim a relay left over from a
19318
+ // different agentbox version; `commit` is observability-only.
19319
+ version: process.env.AGENTBOX_CLI_VERSION || void 0,
19320
+ commit: process.env.AGENTBOX_CLI_COMMIT || void 0
19304
19321
  });
19305
19322
  return;
19306
19323
  }
@@ -20111,10 +20128,12 @@ var BUILT_IN_DEFAULTS = {
20111
20128
  maxLayers: 3
20112
20129
  },
20113
20130
  claude: {
20114
- sessionName: "claude"
20131
+ sessionName: "claude",
20132
+ dangerouslySkipPermissions: true
20115
20133
  },
20116
20134
  codex: {
20117
- sessionName: "codex"
20135
+ sessionName: "codex",
20136
+ dangerouslySkipPermissions: true
20118
20137
  },
20119
20138
  opencode: {
20120
20139
  sessionName: "opencode"
@@ -20302,11 +20321,21 @@ var KEY_REGISTRY = [
20302
20321
  type: "string",
20303
20322
  description: "tmux session name for `agentbox claude`."
20304
20323
  },
20324
+ {
20325
+ key: "claude.dangerouslySkipPermissions",
20326
+ type: "bool",
20327
+ 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."
20328
+ },
20305
20329
  {
20306
20330
  key: "codex.sessionName",
20307
20331
  type: "string",
20308
20332
  description: "tmux session name for `agentbox codex`."
20309
20333
  },
20334
+ {
20335
+ key: "codex.dangerouslySkipPermissions",
20336
+ type: "bool",
20337
+ 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."
20338
+ },
20310
20339
  {
20311
20340
  key: "opencode.sessionName",
20312
20341
  type: "string",
@@ -164,14 +164,7 @@ services:
164
164
  - A service with `restart: never` and an autostart dependency will block the dependent forever after one failed run — usually a mistake.
165
165
  - `command:` is either a shell string (run via `bash -c`) or an argv array. Use the argv form if you need to avoid shell quoting.
166
166
 
167
- ## 8. Checkpoint the warm state (do this at the very end)
168
-
169
- Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
170
- Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
171
- This doesn't need to be confirmed by the user.
172
- It will pause the container for several seconds so warn the user about it and write Done when it's done.
173
-
174
- ## 9. Hand-off
167
+ ## 8. Hand-off
175
168
 
176
169
  Tell the user (verbatim):
177
170
 
@@ -187,6 +180,14 @@ Tell the user (verbatim):
187
180
  your box is ready, you can start more sessions with `agentbox claude`
188
181
  you can access the web app at https://<boxname>.localhost
189
182
 
183
+
184
+ ## 9. Checkpoint the warm state - DON't SKIP THIS STEP
185
+
186
+ Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
187
+ Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
188
+ On all providers except Vercel, this doesn't need to be confirmed by the user. It will pause the container for several seconds so warn the user about it and write Done when it's done.
189
+ On Vercel: this actually STOPS the sandbox, so warn the user about it. Also the system will ask confirmation.
190
+
190
191
  ## 10. Known issues
191
192
 
192
193
  - For Nextjs/Vite/Tasnstack projects, makes sure to forward also websocket for hot reload.
@@ -18599,11 +18599,21 @@ var KEY_REGISTRY = [
18599
18599
  type: "string",
18600
18600
  description: "tmux session name for `agentbox claude`."
18601
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
+ },
18602
18607
  {
18603
18608
  key: "codex.sessionName",
18604
18609
  type: "string",
18605
18610
  description: "tmux session name for `agentbox codex`."
18606
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
+ },
18607
18617
  {
18608
18618
  key: "opencode.sessionName",
18609
18619
  type: "string",
@@ -19657,6 +19667,18 @@ async function runCheckpointRpc(action, deps) {
19657
19667
  stderr: "relay: AGENTBOX_CLI_ENTRY not set; cannot run checkpoint host-side\n"
19658
19668
  };
19659
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
+ }
19660
19682
  const argv = [process.execPath, entry, "checkpoint", "create", deps.boxId];
19661
19683
  if (params.name) argv.push("--name", params.name);
19662
19684
  if (params.merged === true) argv.push("--merged");
@@ -20013,7 +20035,12 @@ function createRelayServer(opts) {
20013
20035
  boxes: registry.size(),
20014
20036
  events: events.size(),
20015
20037
  pid: process.pid,
20016
- cliEntry: Boolean(process.env.AGENTBOX_CLI_ENTRY)
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
20017
20044
  });
20018
20045
  return;
20019
20046
  }
@@ -21957,6 +21984,7 @@ var Supervisor = class extends import_node_events15.EventEmitter {
21957
21984
  super();
21958
21985
  this.opts = opts;
21959
21986
  this.relay = new RelayClient();
21987
+ this.webProxy = new WebProxy(opts.webProxyPort);
21960
21988
  }
21961
21989
  opts;
21962
21990
  units = /* @__PURE__ */ new Map();
@@ -21966,7 +21994,7 @@ var Supervisor = class extends import_node_events15.EventEmitter {
21966
21994
  scheduling = false;
21967
21995
  rescheduleDirty = false;
21968
21996
  relay;
21969
- webProxy = new WebProxy();
21997
+ webProxy;
21970
21998
  /** The relay client the supervisor pushes state events on. Shared with the
21971
21999
  * status reporter so both use the same fire-and-forget channel. */
21972
22000
  get relayClient() {
@@ -22893,7 +22921,8 @@ function resolveBoxRelayPort() {
22893
22921
  }
22894
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) => {
22895
22923
  const cfg = await loadConfig(opts.config);
22896
- 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 });
22897
22926
  await sup.init(cfg);
22898
22927
  const reporter = new StatusReporter({
22899
22928
  supervisor: sup,
@@ -15,10 +15,7 @@ This box is **persistent**: stopping it captures a snapshot and resuming
15
15
  restarts from that snapshot, so the filesystem survives a pause. You can also
16
16
  save the current filesystem state for future boxes with
17
17
  `agentbox-ctl checkpoint --set-default`, but a checkpoint/snapshot STOPS the
18
- box, so use it only at the end of the setup wizard — otherwise rely on
19
- idempotent tasks in `agentbox.yaml`. NB: this `agentbox-ctl` command normally
20
- doesn't need user confirmation, but since it stops the box, ask for
21
- confirmation before running it.
18
+ box, so use it only at the end of the setup wizard.
22
19
 
23
20
  `/workspace` is a normal git checkout seeded from the host repo at create time.
24
21
  Because there is no host bind-mount, plain `git` inside the box only affects
@@ -245,6 +245,46 @@ step "Claude Code (native installer, run as vscode)"
245
245
  sudo -u vscode -H bash -lc 'curl -fsSL https://claude.ai/install.sh | bash -s stable'
246
246
  done_ "Claude Code (native installer, run as vscode)"
247
247
 
248
+ step "Chrome runtime libs (dnf)"
249
+ # agent-browser launches Chromium at AGENT_BROWSER_EXECUTABLE_PATH
250
+ # (/usr/local/bin/chromium, set in the login-shell shim above). Docker + hetzner
251
+ # bake that binary in; do the same here. These are the AL2023 (dnf) equivalents
252
+ # of the Ubuntu `t64` Chrome deps the other two providers apt-install — the
253
+ # Ubuntu package names don't exist on Amazon Linux 2023. Fail loud: a missing lib
254
+ # means a silently broken browser, not a convenience we can skip.
255
+ dnf install -y -q --allowerasing \
256
+ nss nspr atk at-spi2-atk at-spi2-core cups-libs \
257
+ libdrm libxkbcommon libXcomposite libXdamage libXfixes libXrandr \
258
+ libXext libX11 libxcb mesa-libgbm pango cairo alsa-lib \
259
+ liberation-fonts
260
+ done_ "Chrome runtime libs (dnf)"
261
+
262
+ step "playwright + Chromium download (as vscode)"
263
+ # Run the download as vscode so the cache lands under
264
+ # /home/vscode/.cache/ms-playwright. Resolve a stable symlink at
265
+ # /usr/local/bin/chromium so AGENT_BROWSER_EXECUTABLE_PATH stays predictable
266
+ # across Chromium revision bumps (mirrors hetzner install-box.sh).
267
+ npm install -g playwright 2>&1 | tail -3
268
+ sudo -u vscode -H bash -lc 'playwright install chromium'
269
+ CHROME_BIN="$(sudo -u vscode -H bash -lc 'ls /home/vscode/.cache/ms-playwright/chromium-*/chrome-linux*/chrome 2>/dev/null | sort | tail -1')"
270
+ if [ -z "$CHROME_BIN" ] || [ ! -x "$CHROME_BIN" ]; then
271
+ echo "provision.sh: could not resolve Playwright Chromium binary" >&2
272
+ exit 70
273
+ fi
274
+ # Fail loud if a shared lib is missing — this is where an incomplete AL2023 dep
275
+ # set surfaces at bake time instead of at first agent-browser launch. Capture
276
+ # ldd's output first (|| true): under `set -euo pipefail` a non-zero ldd exit
277
+ # would otherwise dominate the `ldd | grep` pipeline and make the missing-libs
278
+ # check a silent no-op even when 'not found' lines are present.
279
+ LDD_OUT="$(ldd "$CHROME_BIN" 2>&1 || true)"
280
+ if printf '%s\n' "$LDD_OUT" | grep -q 'not found'; then
281
+ echo "provision.sh: Chromium has unresolved shared libs:" >&2
282
+ printf '%s\n' "$LDD_OUT" | grep 'not found' >&2
283
+ exit 71
284
+ fi
285
+ ln -sf "$CHROME_BIN" /usr/local/bin/chromium
286
+ done_ "playwright + Chromium download (as vscode)"
287
+
248
288
  step "dnf cleanup"
249
289
  dnf clean all 2>/dev/null || true
250
290
  done_ "dnf cleanup"
@@ -164,14 +164,7 @@ services:
164
164
  - A service with `restart: never` and an autostart dependency will block the dependent forever after one failed run — usually a mistake.
165
165
  - `command:` is either a shell string (run via `bash -c`) or an argv array. Use the argv form if you need to avoid shell quoting.
166
166
 
167
- ## 8. Checkpoint the warm state (do this at the very end)
168
-
169
- Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
170
- Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
171
- This doesn't need to be confirmed by the user.
172
- It will pause the container for several seconds so warn the user about it and write Done when it's done.
173
-
174
- ## 9. Hand-off
167
+ ## 8. Hand-off
175
168
 
176
169
  Tell the user (verbatim):
177
170
 
@@ -187,6 +180,14 @@ Tell the user (verbatim):
187
180
  your box is ready, you can start more sessions with `agentbox claude`
188
181
  you can access the web app at https://<boxname>.localhost
189
182
 
183
+
184
+ ## 9. Checkpoint the warm state - DON't SKIP THIS STEP
185
+
186
+ Checkpoint (snapshot) this box writable layer: once the box is warmed up (deps installed, services ready), checkpoint it with `agentbox-ctl checkpoint --name setup --replace --set-default` so future boxes start ready.
187
+ Run this command exactly once. The `--name setup --replace` makes it idempotent — if it ever needs to run again it overwrites the existing `setup` checkpoint instead of stacking duplicates.
188
+ On all providers except Vercel, this doesn't need to be confirmed by the user. It will pause the container for several seconds so warn the user about it and write Done when it's done.
189
+ On Vercel: this actually STOPS the sandbox, so warn the user about it. Also the system will ask confirmation.
190
+
190
191
  ## 10. Known issues
191
192
 
192
193
  - For Nextjs/Vite/Tasnstack projects, makes sure to forward also websocket for hot reload.