@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.
- package/CHANGELOG.md +60 -0
- package/dist/{_cloud-attach-GUBB5RH2.js → _cloud-attach-R6TRWG5L.js} +3 -3
- package/dist/{chunk-BYCLD6D6.js → chunk-43Q5GWP6.js} +98 -54
- package/dist/chunk-43Q5GWP6.js.map +1 -0
- package/dist/{chunk-VATTS2MR.js → chunk-72CJTXN6.js} +2 -2
- package/dist/{chunk-TBSIJVSN.js → chunk-E7CHS7ZR.js} +21 -13
- package/dist/chunk-E7CHS7ZR.js.map +1 -0
- package/dist/{chunk-LDMYHWUS.js → chunk-MCOU6CZS.js} +2 -2
- package/dist/{chunk-TCS5HXJX.js → chunk-MLMFNN4T.js} +396 -324
- package/dist/chunk-MLMFNN4T.js.map +1 -0
- package/dist/{dist-J2IHD5T7.js → dist-AGTIA7AD.js} +3 -3
- package/dist/{dist-3IMQNTTV.js → dist-FIFEFKJ7.js} +3 -3
- package/dist/{dist-34RKQ74M.js → dist-JZ3XO6EB.js} +4 -4
- package/dist/{dist-4DPOL5A7.js → dist-OGJGZETZ.js} +2 -2
- package/dist/{dist-57M6ZA7H.js → dist-S4XR4ACV.js} +4 -4
- package/dist/index.js +868 -300
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +30 -0
- package/runtime/docker/packages/ctl/dist/bin.cjs +321 -27
- package/runtime/e2b/agentbox-setup-skill.md +30 -0
- package/runtime/e2b/ctl.cjs +321 -27
- package/runtime/hetzner/agentbox-setup-skill.md +30 -0
- package/runtime/hetzner/ctl.cjs +321 -27
- package/runtime/relay/bin.cjs +83 -5
- package/runtime/vercel/agentbox-setup-skill.md +30 -0
- package/runtime/vercel/ctl.cjs +321 -27
- package/share/agentbox-setup/SKILL.md +30 -0
- package/share/host-skills/agentbox-info/SKILL.md +21 -1
- package/dist/chunk-BYCLD6D6.js.map +0 -1
- package/dist/chunk-TBSIJVSN.js.map +0 -1
- package/dist/chunk-TCS5HXJX.js.map +0 -1
- /package/dist/{_cloud-attach-GUBB5RH2.js.map → _cloud-attach-R6TRWG5L.js.map} +0 -0
- /package/dist/{chunk-VATTS2MR.js.map → chunk-72CJTXN6.js.map} +0 -0
- /package/dist/{chunk-LDMYHWUS.js.map → chunk-MCOU6CZS.js.map} +0 -0
- /package/dist/{dist-J2IHD5T7.js.map → dist-AGTIA7AD.js.map} +0 -0
- /package/dist/{dist-3IMQNTTV.js.map → dist-FIFEFKJ7.js.map} +0 -0
- /package/dist/{dist-34RKQ74M.js.map → dist-JZ3XO6EB.js.map} +0 -0
- /package/dist/{dist-4DPOL5A7.js.map → dist-OGJGZETZ.js.map} +0 -0
- /package/dist/{dist-57M6ZA7H.js.map → dist-S4XR4ACV.js.map} +0 -0
package/runtime/relay/bin.cjs
CHANGED
|
@@ -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:
|
|
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
|
|
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.
|