@madarco/agentbox 0.14.0 → 0.15.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 (40) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/{_cloud-attach-GUBB5RH2.js → _cloud-attach-R6TRWG5L.js} +3 -3
  3. package/dist/{chunk-BYCLD6D6.js → chunk-43Q5GWP6.js} +98 -54
  4. package/dist/chunk-43Q5GWP6.js.map +1 -0
  5. package/dist/{chunk-VATTS2MR.js → chunk-72CJTXN6.js} +2 -2
  6. package/dist/{chunk-TBSIJVSN.js → chunk-E7CHS7ZR.js} +21 -13
  7. package/dist/chunk-E7CHS7ZR.js.map +1 -0
  8. package/dist/{chunk-LDMYHWUS.js → chunk-MCOU6CZS.js} +2 -2
  9. package/dist/{chunk-TCS5HXJX.js → chunk-MLMFNN4T.js} +396 -324
  10. package/dist/chunk-MLMFNN4T.js.map +1 -0
  11. package/dist/{dist-J2IHD5T7.js → dist-AGTIA7AD.js} +3 -3
  12. package/dist/{dist-3IMQNTTV.js → dist-FIFEFKJ7.js} +3 -3
  13. package/dist/{dist-34RKQ74M.js → dist-JZ3XO6EB.js} +4 -4
  14. package/dist/{dist-4DPOL5A7.js → dist-OGJGZETZ.js} +2 -2
  15. package/dist/{dist-57M6ZA7H.js → dist-S4XR4ACV.js} +4 -4
  16. package/dist/index.js +868 -300
  17. package/dist/index.js.map +1 -1
  18. package/package.json +5 -5
  19. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +30 -0
  20. package/runtime/docker/packages/ctl/dist/bin.cjs +321 -27
  21. package/runtime/e2b/agentbox-setup-skill.md +30 -0
  22. package/runtime/e2b/ctl.cjs +321 -27
  23. package/runtime/hetzner/agentbox-setup-skill.md +30 -0
  24. package/runtime/hetzner/ctl.cjs +321 -27
  25. package/runtime/relay/bin.cjs +83 -5
  26. package/runtime/vercel/agentbox-setup-skill.md +30 -0
  27. package/runtime/vercel/ctl.cjs +321 -27
  28. package/share/agentbox-setup/SKILL.md +30 -0
  29. package/share/host-skills/agentbox-info/SKILL.md +21 -1
  30. package/dist/chunk-BYCLD6D6.js.map +0 -1
  31. package/dist/chunk-TBSIJVSN.js.map +0 -1
  32. package/dist/chunk-TCS5HXJX.js.map +0 -1
  33. /package/dist/{_cloud-attach-GUBB5RH2.js.map → _cloud-attach-R6TRWG5L.js.map} +0 -0
  34. /package/dist/{chunk-VATTS2MR.js.map → chunk-72CJTXN6.js.map} +0 -0
  35. /package/dist/{chunk-LDMYHWUS.js.map → chunk-MCOU6CZS.js.map} +0 -0
  36. /package/dist/{dist-J2IHD5T7.js.map → dist-AGTIA7AD.js.map} +0 -0
  37. /package/dist/{dist-3IMQNTTV.js.map → dist-FIFEFKJ7.js.map} +0 -0
  38. /package/dist/{dist-34RKQ74M.js.map → dist-JZ3XO6EB.js.map} +0 -0
  39. /package/dist/{dist-4DPOL5A7.js.map → dist-OGJGZETZ.js.map} +0 -0
  40. /package/dist/{dist-57M6ZA7H.js.map → dist-S4XR4ACV.js.map} +0 -0
@@ -18367,6 +18367,21 @@ var HostInitiatedTokens = class {
18367
18367
  var import_node_crypto2 = require("crypto");
18368
18368
  var PendingPrompts = class {
18369
18369
  entries = /* @__PURE__ */ new Map();
18370
+ autoApprove = null;
18371
+ /** Install the per-box auto-approve policy (relay server, once at startup). */
18372
+ setAutoApprovePolicy(policy) {
18373
+ this.autoApprove = policy;
18374
+ }
18375
+ /**
18376
+ * True when this box opted into `box.autoApproveHostActions`. Records the
18377
+ * bypass to the audit sink as a side effect so the caller short-circuits
18378
+ * with a trail. Returns false when no policy is installed.
18379
+ */
18380
+ consumeAutoApprove(boxId, params) {
18381
+ if (!this.autoApprove || !this.autoApprove.shouldAutoApprove(boxId)) return false;
18382
+ this.autoApprove.audit(boxId, params);
18383
+ return true;
18384
+ }
18370
18385
  add(boxId, ev) {
18371
18386
  return new Promise((resolve2) => {
18372
18387
  this.entries.set(ev.id, {
@@ -18453,6 +18468,9 @@ async function askPrompt(prompts, subscribers, boxId, params, opts) {
18453
18468
  if (process.env.AGENTBOX_PROMPT === "off") {
18454
18469
  return { answer: "y" };
18455
18470
  }
18471
+ if (prompts.consumeAutoApprove(boxId, params)) {
18472
+ return { answer: "y" };
18473
+ }
18456
18474
  const ev = { id: (0, import_node_crypto2.randomUUID)(), ...params };
18457
18475
  const promise = prompts.add(boxId, ev);
18458
18476
  subscribers.broadcast(boxId, "prompt-ask", ev);
@@ -19430,6 +19448,21 @@ function createRelayServer(opts) {
19430
19448
  const prompts = new PendingPrompts();
19431
19449
  const subscribers = new PromptSubscribers();
19432
19450
  const notices = new BoxNotices(subscribers);
19451
+ prompts.setAutoApprovePolicy({
19452
+ shouldAutoApprove: (boxId) => registry.get(boxId)?.autoApproveHostActions === true,
19453
+ audit: (boxId, params) => {
19454
+ events.append({
19455
+ boxId,
19456
+ type: "host-action-auto-approved",
19457
+ payload: {
19458
+ command: params.context?.command,
19459
+ argv: params.context?.argv,
19460
+ message: params.message
19461
+ }
19462
+ });
19463
+ log(`auto-approved host action for ${boxId}: ${params.context?.command ?? params.message}`);
19464
+ }
19465
+ });
19433
19466
  const hostInitiatedTokens = new HostInitiatedTokens();
19434
19467
  let queuePoke = null;
19435
19468
  const host = opts.host ?? "0.0.0.0";
@@ -19617,11 +19650,22 @@ function createRelayServer(opts) {
19617
19650
  send(res, 400, { error: "cp.* requires {boxPath, hostPath} strings" });
19618
19651
  return;
19619
19652
  }
19653
+ if (params.exclude !== void 0 && (!Array.isArray(params.exclude) || params.exclude.some((p) => typeof p !== "string"))) {
19654
+ send(res, 400, { error: "cp.* exclude must be an array of strings" });
19655
+ return;
19656
+ }
19620
19657
  const direction = body.method === "cp.toHost" ? "box -> host" : "host -> box";
19658
+ const pathDetail = body.method === "cp.toHost" ? `${params.boxPath} -> ${params.hostPath}` : `${params.hostPath} -> ${params.boxPath}`;
19659
+ const detailParts = [pathDetail];
19660
+ if (params.exclude && params.exclude.length > 0) {
19661
+ detailParts.push(`exclude: ${params.exclude.join(", ")}`);
19662
+ }
19663
+ if (params.defaultExcludes === false) detailParts.push("(default excludes off)");
19664
+ if (params.yes) detailParts.push("(over size limit \u2014 confirmed)");
19621
19665
  const verdict = await askPrompt(prompts, subscribers, reg.boxId, {
19622
19666
  kind: "confirm",
19623
19667
  message: `Allow cp (${direction}) on ${reg.name}?`,
19624
- detail: body.method === "cp.toHost" ? `${params.boxPath} -> ${params.hostPath}` : `${params.hostPath} -> ${params.boxPath}`,
19668
+ detail: detailParts.join("\n"),
19625
19669
  defaultAnswer: "n",
19626
19670
  context: {
19627
19671
  command: body.method,
@@ -19787,7 +19831,8 @@ function createRelayServer(opts) {
19787
19831
  worktrees,
19788
19832
  previewUrl: typeof body.previewUrl === "string" && body.previewUrl.length > 0 ? body.previewUrl : void 0,
19789
19833
  previewToken: typeof body.previewToken === "string" && body.previewToken.length > 0 ? body.previewToken : void 0,
19790
- bridgeToken: typeof body.bridgeToken === "string" && body.bridgeToken.length > 0 ? body.bridgeToken : void 0
19834
+ bridgeToken: typeof body.bridgeToken === "string" && body.bridgeToken.length > 0 ? body.bridgeToken : void 0,
19835
+ autoApproveHostActions: body.autoApproveHostActions === true
19791
19836
  };
19792
19837
  registry.register(reg);
19793
19838
  log(
@@ -19895,6 +19940,15 @@ function createRelayServer(opts) {
19895
19940
  send(res, 200, { boxes: redacted });
19896
19941
  return;
19897
19942
  }
19943
+ if (route === "GET /admin/prompts") {
19944
+ const boxId = url.searchParams.get("boxId") ?? "";
19945
+ if (boxId.length === 0) {
19946
+ send(res, 400, { error: "missing boxId query param" });
19947
+ return;
19948
+ }
19949
+ send(res, 200, { prompts: prompts.forBox(boxId) });
19950
+ return;
19951
+ }
19898
19952
  if (route === "GET /admin/prompts/stream") {
19899
19953
  const boxId = url.searchParams.get("boxId") ?? "";
19900
19954
  if (boxId.length === 0) {
@@ -20212,7 +20266,11 @@ async function handleCpRpc(reg, method, params) {
20212
20266
  };
20213
20267
  }
20214
20268
  const boxRef = `${reg.name}:${params.boxPath}`;
20215
- const argv = method === "cp.toHost" ? [process.execPath, entry, "cp", boxRef, params.hostPath] : [process.execPath, entry, "cp", params.hostPath, boxRef];
20269
+ const flags = [];
20270
+ for (const pat of params.exclude ?? []) flags.push("--exclude", pat);
20271
+ if (params.defaultExcludes === false) flags.push("--no-default-excludes");
20272
+ if (params.yes) flags.push("--yes");
20273
+ const argv = method === "cp.toHost" ? [process.execPath, entry, "cp", boxRef, params.hostPath, ...flags] : [process.execPath, entry, "cp", params.hostPath, boxRef, ...flags];
20216
20274
  return runHostCommand(argv, CP_RPC_TIMEOUT_MS);
20217
20275
  }
20218
20276
  async function handleDownloadRpc(reg, kind) {
@@ -20340,6 +20398,7 @@ var BUILT_IN_DEFAULTS = {
20340
20398
  withEnv: false,
20341
20399
  resyncOnStart: true,
20342
20400
  vnc: true,
20401
+ autoApproveHostActions: false,
20343
20402
  isolateClaudeConfig: false,
20344
20403
  isolateCodexConfig: false,
20345
20404
  isolateOpencodeConfig: false,
@@ -20360,7 +20419,8 @@ var BUILT_IN_DEFAULTS = {
20360
20419
  bundleDepth: void 0,
20361
20420
  vercelVcpus: 2,
20362
20421
  vercelTimeoutMs: 27e5,
20363
- vercelNetworkPolicy: ""
20422
+ vercelNetworkPolicy: "",
20423
+ cpMaxBytes: 100 * 1024 * 1024
20364
20424
  },
20365
20425
  checkpoint: {
20366
20426
  maxLayers: 3
@@ -20416,7 +20476,8 @@ var BUILT_IN_DEFAULTS = {
20416
20476
  enabled: true,
20417
20477
  maxConcurrent: 5,
20418
20478
  maxWorking: 0,
20419
- idleGraceSeconds: 15
20479
+ idleGraceSeconds: 15,
20480
+ openIn: "none"
20420
20481
  },
20421
20482
  cloud: {
20422
20483
  useCurrentBranch: false
@@ -20534,6 +20595,11 @@ var KEY_REGISTRY = [
20534
20595
  type: "bool",
20535
20596
  description: "Run the per-box Xvnc + noVNC stack."
20536
20597
  },
20598
+ {
20599
+ key: "box.autoApproveHostActions",
20600
+ type: "bool",
20601
+ description: "Auto-approve host-action confirmations (git push, cp host<->box, gh PR writes, checkpoint) for this box without an interactive prompt. Off by default; intended for unattended orchestration of trusted boxes. Each auto-approval is recorded as a relay event (visible in `agentbox agent` / the dashboard)."
20602
+ },
20537
20603
  {
20538
20604
  key: "box.isolateClaudeConfig",
20539
20605
  type: "bool",
@@ -20632,6 +20698,12 @@ var KEY_REGISTRY = [
20632
20698
  type: "int",
20633
20699
  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."
20634
20700
  },
20701
+ {
20702
+ key: "box.cpMaxBytes",
20703
+ type: "int",
20704
+ description: "Max bytes a single host\u2192box copy may transfer after excludes, shared by `agentbox cp` (blocked with a size breakdown unless --yes) and each `carry:` entry (rejected at resolve time). Default 104857600 (100 MiB).",
20705
+ advanced: true
20706
+ },
20635
20707
  {
20636
20708
  key: "box.vercelNetworkPolicy",
20637
20709
  type: "string",
@@ -20779,6 +20851,12 @@ var KEY_REGISTRY = [
20779
20851
  type: "int",
20780
20852
  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."
20781
20853
  },
20854
+ {
20855
+ key: "queue.openIn",
20856
+ type: "enum",
20857
+ enumValues: ["none", "split", "window", "tab"],
20858
+ description: "When a background `-i` job finishes creating its box, where the host relay opens an attached terminal onto it: `none` (default \u2014 open nothing, just queue), `split`, `window`, or `tab`. Honored only when the submitting shell runs inside tmux, cmux, or iTerm2 (the targeting is captured at submit time). Under cmux, `split` splits the pane you submitted from (falling back to the parent workspace, then a new workspace), `tab` adds a tab in the parent workspace, and `window` opens a separate workspace; iTerm2 opens relative to the frontmost window. Unlike `attach.openIn` there is no `same` mode \u2014 the box is created asynchronously, so it is always a fresh terminal."
20859
+ },
20782
20860
  {
20783
20861
  key: "cloud.useCurrentBranch",
20784
20862
  type: "bool",
@@ -192,6 +192,36 @@ services:
192
192
  factor: 2
193
193
  ```
194
194
 
195
+ ## 6b. Bringing extra host files/folders into the box
196
+
197
+ Two ways to copy host files in (both COPY — never a live mount, so the box can't
198
+ write back to the host):
199
+
200
+ - **`carry:` block** (declarative, in `agentbox.yaml`) — for files/dirs every box
201
+ should get at create time. Each entry is `{ src, dest }` with optional `mode`,
202
+ `user`, `optional`, and `exclude:` (a list of tar globs / bare dir names to drop
203
+ when copying a directory). Heavy regenerable dirs (`.git`, `node_modules`, `bin`,
204
+ `obj`, `packages`, `dist`, `.next`, `target`) are dropped by default; `exclude:`
205
+ is additive. Each carry entry is capped at `box.cpMaxBytes` (default 100 MiB
206
+ after excludes) — the same limit `agentbox cp` enforces.
207
+ - **`agentbox-ctl cp fromHost <hostPath> <boxPath>`** (ad-hoc, from inside the box)
208
+ — for a one-off copy. Prompts the user on the host to approve.
209
+
210
+ **The per-copy size limit (important for large/legacy folders).** A single copy is
211
+ blocked above `box.cpMaxBytes` (default **100 MB**) *after* default excludes, so it
212
+ fails loud instead of silently hanging. When blocked you get a `du`-style tree of
213
+ the biggest remaining folders/subfolders. To get under the limit, EITHER:
214
+
215
+ - **drop what the box can regenerate** (the default excludes already remove
216
+ `node_modules`/`.git`/build output; add more with `--exclude=<glob-or-name>`), OR
217
+ - **copy the heavy folders one at a time** so each copy is under the limit, OR
218
+ - pass `--yes` to copy the whole thing anyway (only when you really need it all).
219
+
220
+ Example: a 2.4 GB legacy folder is mostly `packages/` (NuGet) + `.git`; those are
221
+ excluded by default, and what's left can be split:
222
+ `agentbox-ctl cp fromHost ../legacy/src /workspace/legacy/src` then
223
+ `... cp fromHost ../legacy/Database /workspace/legacy/Database`.
224
+
195
225
  ## 7. Validate before handing off
196
226
 
197
227
  - check with `agentbox-ctl reload` and then `agentbox-ctl status` that everything is running as expected.