@madarco/agentbox 0.14.0 → 0.16.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 (66) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/dist/{_cloud-attach-GUBB5RH2.js → _cloud-attach-5KJWOASL.js} +4 -4
  3. package/dist/{chunk-RSKG7AFU.js → chunk-3WCEB6RE.js} +2 -2
  4. package/dist/{chunk-XKH7NTT7.js → chunk-DBBUDKKB.js} +248 -5
  5. package/dist/chunk-DBBUDKKB.js.map +1 -0
  6. package/dist/{chunk-TCS5HXJX.js → chunk-GXJNJUEV.js} +1090 -527
  7. package/dist/chunk-GXJNJUEV.js.map +1 -0
  8. package/dist/{chunk-LDMYHWUS.js → chunk-NW2UZQV6.js} +10 -6
  9. package/dist/chunk-NW2UZQV6.js.map +1 -0
  10. package/dist/{chunk-TBSIJVSN.js → chunk-PIK47622.js} +37 -17
  11. package/dist/chunk-PIK47622.js.map +1 -0
  12. package/dist/{chunk-BKU34KYY.js → chunk-QXFNLKJJ.js} +9 -3
  13. package/dist/{chunk-BKU34KYY.js.map → chunk-QXFNLKJJ.js.map} +1 -1
  14. package/dist/{chunk-BYCLD6D6.js → chunk-SB4QTF2T.js} +98 -54
  15. package/dist/chunk-SB4QTF2T.js.map +1 -0
  16. package/dist/{chunk-VATTS2MR.js → chunk-SENASAU4.js} +10 -6
  17. package/dist/{chunk-VATTS2MR.js.map → chunk-SENASAU4.js.map} +1 -1
  18. package/dist/{dist-34RKQ74M.js → dist-4IQFJJQI.js} +5 -5
  19. package/dist/{dist-4DPOL5A7.js → dist-7YB7BMNG.js} +5 -5
  20. package/dist/{dist-3IMQNTTV.js → dist-SL2QSMBE.js} +5 -5
  21. package/dist/{dist-J2IHD5T7.js → dist-VHI5QOSQ.js} +6 -6
  22. package/dist/{dist-57M6ZA7H.js → dist-XC47DSCR.js} +5 -5
  23. package/dist/index.js +1043 -333
  24. package/dist/index.js.map +1 -1
  25. package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js → prepared-state-MQHD3M5F-2LANTRL7.js} +2 -2
  26. package/package.json +6 -5
  27. package/runtime/docker/Dockerfile.box +21 -2
  28. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +112 -29
  29. package/runtime/docker/packages/ctl/dist/bin.cjs +10353 -8575
  30. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup +5 -2
  31. package/runtime/docker/packages/sandbox-docker/scripts/linear-shim +181 -0
  32. package/runtime/docker/packages/sandbox-docker/scripts/ntn-shim +95 -0
  33. package/runtime/e2b/agentbox-checkpoint-cleanup +5 -2
  34. package/runtime/e2b/agentbox-setup-skill.md +112 -29
  35. package/runtime/e2b/ctl.cjs +10353 -8575
  36. package/runtime/e2b/linear-shim +181 -0
  37. package/runtime/e2b/ntn-shim +95 -0
  38. package/runtime/e2b/scripts/build-template.sh +13 -7
  39. package/runtime/hetzner/agentbox-checkpoint-cleanup +5 -2
  40. package/runtime/hetzner/agentbox-setup-skill.md +112 -29
  41. package/runtime/hetzner/ctl.cjs +10353 -8575
  42. package/runtime/hetzner/linear-shim +181 -0
  43. package/runtime/hetzner/ntn-shim +95 -0
  44. package/runtime/hetzner/scripts/install-box.sh +19 -9
  45. package/runtime/relay/bin.cjs +3707 -2828
  46. package/runtime/vercel/agentbox-checkpoint-cleanup +5 -2
  47. package/runtime/vercel/agentbox-setup-skill.md +112 -29
  48. package/runtime/vercel/ctl.cjs +10353 -8575
  49. package/runtime/vercel/linear-shim +181 -0
  50. package/runtime/vercel/ntn-shim +95 -0
  51. package/runtime/vercel/scripts/provision.sh +13 -7
  52. package/share/agentbox-setup/SKILL.md +112 -29
  53. package/share/host-skills/agentbox-info/SKILL.md +22 -2
  54. package/dist/chunk-BYCLD6D6.js.map +0 -1
  55. package/dist/chunk-LDMYHWUS.js.map +0 -1
  56. package/dist/chunk-TBSIJVSN.js.map +0 -1
  57. package/dist/chunk-TCS5HXJX.js.map +0 -1
  58. package/dist/chunk-XKH7NTT7.js.map +0 -1
  59. /package/dist/{_cloud-attach-GUBB5RH2.js.map → _cloud-attach-5KJWOASL.js.map} +0 -0
  60. /package/dist/{chunk-RSKG7AFU.js.map → chunk-3WCEB6RE.js.map} +0 -0
  61. /package/dist/{dist-34RKQ74M.js.map → dist-4IQFJJQI.js.map} +0 -0
  62. /package/dist/{dist-4DPOL5A7.js.map → dist-7YB7BMNG.js.map} +0 -0
  63. /package/dist/{dist-3IMQNTTV.js.map → dist-SL2QSMBE.js.map} +0 -0
  64. /package/dist/{dist-J2IHD5T7.js.map → dist-VHI5QOSQ.js.map} +0 -0
  65. /package/dist/{dist-57M6ZA7H.js.map → dist-XC47DSCR.js.map} +0 -0
  66. /package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js.map → prepared-state-MQHD3M5F-2LANTRL7.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -9,6 +9,114 @@ Entries are generated from the commit history with `/release-notes` and then
9
9
  hand-reviewed — they describe what changed for someone using the `agentbox`
10
10
  CLI, not the raw commits.
11
11
 
12
+ ## [0.16.0] - 2026-06-07
13
+
14
+ ### Added
15
+
16
+ - **Notion integration.** A box can now call Notion through the host's
17
+ authenticated `ntn` CLI without the Notion token ever entering the box. The
18
+ in-box `ntn`/`notion` shim proxies to the host relay: reads pass straight
19
+ through, writes (`pages create`/`pages update`) prompt for host approval.
20
+ `ntn api` is read-only — GET to any endpoint plus the read-only POSTs
21
+ `v1/search`, `v1/databases/<id>/query`, and `v1/data_sources/<id>/query`
22
+ (full JSON bodies via `-d '<json>'`); every other method/endpoint is refused.
23
+ Off by default; enable per project with
24
+ `agentbox config set --project integrations.notion.enabled true`. Shows up in
25
+ `agentbox doctor`.
26
+ - **Linear integration.** Same model for `@schpet/linear-cli`: read issues,
27
+ teams, and filtered queries plus a GraphQL **query** passthrough
28
+ (`linear api`); `mutation`/`subscription` are refused. `issue create`/
29
+ `update`/`comment add` prompt for host approval; `auth token` is hard-rejected
30
+ so the key stays on the host. Enable with `integrations.linear.enabled`.
31
+ - **`run_once:` tasks** in `agentbox.yaml` (renamed from `idempotent:`): a task
32
+ that runs only on a cold box and is skipped on warm boots, tracked by a
33
+ durable marker.
34
+ - **`agentbox.yaml` replacement engine** with an `{{AGENTBOX_AUTO_SECRET}}`
35
+ generator (stable per-project secret) and a new `agentbox render` command to
36
+ preview the resolved file. Replacements also apply to `carry:` targets.
37
+ - **Docker `image:` services.** Sidecar containers declared under `image:` now
38
+ take their `ports`/`env` nested under `image:` as well, keeping all
39
+ image-level config in one place.
40
+ - **Codex plugin marketplace.** AgentBox installs as a Codex plugin straight
41
+ from the repo (`codex plugin marketplace add madarco/agentbox`).
42
+
43
+ ### Fixed
44
+
45
+ - `carry:` and `agentbox cp` copy files via `docker exec tar` instead of
46
+ `docker cp`, fixing "read/write on closed pipe" failures into the
47
+ bind-mounted workspace and relative-path targets (e.g. `./backups/...`).
48
+ - `agentbox doctor` integration probes are time-bounded and stdin-isolated, so
49
+ doctor no longer hangs when a connector's auth check blocks; a timed-out
50
+ probe now reports a timeout rather than "not logged in".
51
+
52
+ ### Security
53
+
54
+ - The Notion `ntn api` gate is fail-closed: it refuses any unrecognized flag
55
+ rather than ignoring it, closing a bypass where ntn's value-consuming global
56
+ flags (`--workers-config-file`, `--env`) could shift the real request
57
+ endpoint past the read classification. Host-file (`--file`/`--input`) bodies
58
+ and `.`/`..` path segments are refused.
59
+
60
+ ## [0.15.0] - 2026-06-05
61
+
62
+ ### Breaking
63
+
64
+ - Carry and `cp` now share a single size cap. The `AGENTBOX_CARRY_MAX_BYTES`
65
+ env var is removed; both the `carry:` step and `agentbox cp` are governed by
66
+ the `box.cpMaxBytes` config key (default 100 MiB, up from carry's old 50 MiB).
67
+ Scripts that set `AGENTBOX_CARRY_MAX_BYTES` no longer have any effect — set
68
+ `box.cpMaxBytes` instead.
69
+
70
+ ### Added
71
+
72
+ - `queue.openIn` config key: when a background `-i` job's box becomes ready,
73
+ optionally open an attached terminal onto it — `split`, `window`, or `tab`
74
+ (default `none`, the previous behavior). Fires only when you submit from
75
+ inside tmux, cmux, or iTerm2.
76
+ - `agentbox cp` (and the `carry:` copy step) now stream the tar instead of
77
+ buffering it, so copies are no longer capped by Node's buffer limit (large
78
+ folders that silently failed with "tar: Write error" now work). Added a
79
+ repeatable `--exclude=<glob|name>` and `--no-default-excludes`; heavy
80
+ regenerable dirs (`.git`, `node_modules`, `dist`, `.next`, `target`, …) are
81
+ excluded by default. Copies larger than `box.cpMaxBytes` are blocked with a
82
+ du-style tree of the biggest folders and a suggested strategy unless `--yes`.
83
+ - `agentbox agent approvals` / `agentbox agent approve`: inspect and answer
84
+ relay host-action confirmations (git push, `cp`, `gh` PR writes, checkpoint)
85
+ from a host orchestrator, instead of hand-curling the loopback endpoint.
86
+ Prompt ids are content-derived, so a prompt that changed since you listed it
87
+ is refused rather than mis-answered. Adds an opt-in per-box
88
+ `box.autoApproveHostActions` (default off, audited) for unattended runs.
89
+ - The attach footer's `(...)` slot now shows aggregate box service status —
90
+ `starting N/M…` while services boot, `service error` on a crash/failed task,
91
+ `ready` once all are up (probe-aware: a `ready_when` service counts as up only
92
+ once its probe passes, so the footer no longer flashes `ready` early). Boxes
93
+ with no services fall back to the agent activity label.
94
+
95
+ ### Changed
96
+
97
+ - `queue.openIn` under cmux: `split` and `tab` queued jobs now open in the
98
+ workspace you submitted from (split targets the original pane, falling back to
99
+ the parent workspace) instead of always spawning a new top-level workspace.
100
+ `window` still opens a separate workspace.
101
+ - `agentbox config set queue.openIn` now warns that the feature only fires
102
+ inside tmux/cmux/iTerm2, and that cmux additionally needs `socketControlMode`
103
+ set to `automation`/`password` plus `cmux reload-config`.
104
+
105
+ ### Fixed
106
+
107
+ - The `carry:` block is now documented in the published `agentbox.yaml` JSON
108
+ schema, so editors and in-box agents that fetch the schema no longer see it as
109
+ invalid or undiscoverable.
110
+ - The stale default-checkpoint recreate prompt now fires for already-configured
111
+ projects too (it was skipped for them, silently booting old base layers), and
112
+ on recreate it reuses the existing `agentbox.yaml` instead of telling the agent
113
+ to regenerate a config that already exists.
114
+ - `agentbox cp` now enforces `box.cpMaxBytes` on single-file uploads, not just
115
+ directories.
116
+ - A supervisor screen-scrape safety net flips a stuck Claude `working` state to
117
+ `waiting` when its hooks miss a prompt (MCP dialogs, dropped notifications), so
118
+ `agent wait-for input-needed` reliably wakes.
119
+
12
120
  ## [0.14.0] - 2026-06-04
13
121
 
14
122
  ### Added
@@ -3,13 +3,13 @@ import {
3
3
  buildCloudAttachInnerCommand,
4
4
  cloudAgentAttach,
5
5
  cloudAgentStartDetached
6
- } from "./chunk-BYCLD6D6.js";
7
- import "./chunk-TCS5HXJX.js";
8
- import "./chunk-XKH7NTT7.js";
6
+ } from "./chunk-SB4QTF2T.js";
7
+ import "./chunk-GXJNJUEV.js";
8
+ import "./chunk-DBBUDKKB.js";
9
9
  import "./chunk-G3H2L3O2.js";
10
10
  export {
11
11
  buildCloudAttachInnerCommand,
12
12
  cloudAgentAttach,
13
13
  cloudAgentStartDetached
14
14
  };
15
- //# sourceMappingURL=_cloud-attach-GUBB5RH2.js.map
15
+ //# sourceMappingURL=_cloud-attach-5KJWOASL.js.map
@@ -7,7 +7,7 @@ import {
7
7
  readPreparedStateRaw,
8
8
  resolveContextFilesFrom,
9
9
  writePreparedStateRaw
10
- } from "./chunk-XKH7NTT7.js";
10
+ } from "./chunk-DBBUDKKB.js";
11
11
 
12
12
  // ../../packages/sandbox-daytona/dist/chunk-35HJOOGT.js
13
13
  import { existsSync } from "fs";
@@ -830,4 +830,4 @@ export {
830
830
  writePreparedDaytonaState,
831
831
  preparedMatches
832
832
  };
833
- //# sourceMappingURL=chunk-RSKG7AFU.js.map
833
+ //# sourceMappingURL=chunk-3WCEB6RE.js.map
@@ -1,5 +1,195 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // ../../packages/core/dist/index.js
4
+ import { randomBytes } from "crypto";
5
+ var claudeCodeLauncher = {
6
+ kind: "claude-code",
7
+ // claude treats its first positional argument as the seed user turn in
8
+ // interactive mode (`claude "<message>"`), so we slot the initial message
9
+ // ahead of any user-passed flags.
10
+ buildArgs(initialMessage, userArgs) {
11
+ if (!initialMessage) return [...userArgs];
12
+ return [initialMessage, ...userArgs];
13
+ }
14
+ };
15
+ var codexLauncher = {
16
+ kind: "codex",
17
+ buildArgs(initialMessage, userArgs) {
18
+ if (!initialMessage) return [...userArgs];
19
+ return [initialMessage, ...userArgs];
20
+ }
21
+ };
22
+ var opencodeLauncher = {
23
+ kind: "opencode",
24
+ buildArgs(initialMessage, userArgs) {
25
+ if (!initialMessage) return [...userArgs];
26
+ return [initialMessage, ...userArgs];
27
+ }
28
+ };
29
+ function resolveAgentLauncher(kind) {
30
+ if (kind === "claude-code") return claudeCodeLauncher;
31
+ if (kind === "codex") return codexLauncher;
32
+ if (kind === "opencode") return opencodeLauncher;
33
+ throw new Error(`unknown agent kind: ${String(kind)}`);
34
+ }
35
+ var PLACEHOLDER_KEYS = [
36
+ "AGENTBOX_BOX_NAME",
37
+ "AGENTBOX_BOX_ID",
38
+ "AGENTBOX_BOX_KIND",
39
+ "AGENTBOX_HOST_WORKSPACE",
40
+ "AGENTBOX_PROJECT_ROOT",
41
+ // Convenience: the portless host this box publishes (`<box-name>.localhost`).
42
+ // Derived from AGENTBOX_BOX_NAME when not set explicitly.
43
+ "AGENTBOX_BOX_HOST"
44
+ ];
45
+ var PLACEHOLDER_SET = new Set(PLACEHOLDER_KEYS);
46
+ var ReplaceError = class extends Error {
47
+ constructor(message) {
48
+ super(message);
49
+ this.name = "ReplaceError";
50
+ }
51
+ };
52
+ var PLACEHOLDER_RE = /\{\{\s*([A-Z0-9_]+)\s*\}\}/g;
53
+ function substitutePlaceholders(text, context, onWarn) {
54
+ return text.replace(PLACEHOLDER_RE, (match, name) => {
55
+ if (!PLACEHOLDER_SET.has(name)) return match;
56
+ const value = context[name];
57
+ if (value === void 0) {
58
+ onWarn?.(`placeholder {{${name}}} has no value in this context \u2014 left untouched`);
59
+ return match;
60
+ }
61
+ return value;
62
+ });
63
+ }
64
+ function applyReplacements(content, opts) {
65
+ let out = content;
66
+ if (opts.env) {
67
+ out = substitutePlaceholders(out, opts.context, opts.onWarn);
68
+ }
69
+ for (const rule of opts.rules ?? []) {
70
+ const to = substitutePlaceholders(rule.to, opts.context, opts.onWarn);
71
+ if (rule.regex) {
72
+ let re;
73
+ try {
74
+ re = new RegExp(rule.from, rule.flags ?? "g");
75
+ } catch (err) {
76
+ throw new ReplaceError(
77
+ `invalid regex "${rule.from}": ${err instanceof Error ? err.message : String(err)}`
78
+ );
79
+ }
80
+ out = out.replace(re, to);
81
+ } else {
82
+ out = out.split(rule.from).join(to);
83
+ }
84
+ }
85
+ return out;
86
+ }
87
+ function deriveBoxHost(ctx) {
88
+ if (ctx.AGENTBOX_BOX_HOST === void 0 && ctx.AGENTBOX_BOX_NAME !== void 0) {
89
+ ctx.AGENTBOX_BOX_HOST = `${ctx.AGENTBOX_BOX_NAME}.localhost`;
90
+ }
91
+ return ctx;
92
+ }
93
+ function isPlainObject(v) {
94
+ return typeof v === "object" && v !== null && !Array.isArray(v);
95
+ }
96
+ var RULE_KEYS = /* @__PURE__ */ new Set(["from", "to", "regex", "flags"]);
97
+ function parseReplaceRule(raw, where) {
98
+ if (!isPlainObject(raw)) {
99
+ throw new ReplaceError(`${where} must be a mapping with at least { from, to }`);
100
+ }
101
+ for (const key of Object.keys(raw)) {
102
+ if (!RULE_KEYS.has(key)) throw new ReplaceError(`${where} has unknown key "${key}"`);
103
+ }
104
+ if (typeof raw.from !== "string" || raw.from.length === 0) {
105
+ throw new ReplaceError(`${where}.from must be a non-empty string`);
106
+ }
107
+ if (typeof raw.to !== "string") {
108
+ throw new ReplaceError(`${where}.to must be a string`);
109
+ }
110
+ const rule = { from: raw.from, to: raw.to };
111
+ if (raw.regex !== void 0 && raw.regex !== null) {
112
+ if (typeof raw.regex !== "boolean") throw new ReplaceError(`${where}.regex must be a boolean`);
113
+ rule.regex = raw.regex;
114
+ }
115
+ if (raw.flags !== void 0 && raw.flags !== null) {
116
+ if (typeof raw.flags !== "string") throw new ReplaceError(`${where}.flags must be a string`);
117
+ rule.flags = raw.flags;
118
+ }
119
+ if (rule.regex) {
120
+ try {
121
+ new RegExp(rule.from, rule.flags ?? "g");
122
+ } catch (err) {
123
+ throw new ReplaceError(
124
+ `${where}.from is not a valid regex: ${err instanceof Error ? err.message : String(err)}`
125
+ );
126
+ }
127
+ }
128
+ return rule;
129
+ }
130
+ function parseReplaceRules(raw, where) {
131
+ if (raw === void 0 || raw === null) return [];
132
+ if (!Array.isArray(raw)) throw new ReplaceError(`${where} must be a list of rules`);
133
+ return raw.map((r, i) => parseReplaceRule(r, `${where}[${String(i)}]`));
134
+ }
135
+ function parseReplacements(raw) {
136
+ if (raw === void 0 || raw === null) return {};
137
+ if (!isPlainObject(raw)) {
138
+ throw new ReplaceError("replacements must be a mapping of name \u2192 rule list");
139
+ }
140
+ const out = {};
141
+ for (const [name, rules] of Object.entries(raw)) {
142
+ if (!/^[A-Za-z0-9_-]+$/.test(name)) {
143
+ throw new ReplaceError(`replacements.${name}: name must match [A-Za-z0-9_-]+`);
144
+ }
145
+ out[name] = parseReplaceRules(rules, `replacements.${name}`);
146
+ }
147
+ return out;
148
+ }
149
+ function resolveRuleRefs(refs, replacements, where) {
150
+ const out = [];
151
+ for (const name of refs) {
152
+ const set = replacements[name];
153
+ if (set === void 0) {
154
+ const known = Object.keys(replacements);
155
+ throw new ReplaceError(
156
+ `${where}: unknown replacements rule-set "${name}"` + (known.length > 0 ? ` (known: ${known.join(", ")})` : " (none declared)")
157
+ );
158
+ }
159
+ out.push(...set);
160
+ }
161
+ return out;
162
+ }
163
+ var UserFacingError = class extends Error {
164
+ constructor(message) {
165
+ super(message);
166
+ this.name = "UserFacingError";
167
+ }
168
+ };
169
+ var BoxNotFoundError = class extends Error {
170
+ constructor(query) {
171
+ super(`no agentbox matches "${query}"`);
172
+ this.query = query;
173
+ this.name = "BoxNotFoundError";
174
+ }
175
+ query;
176
+ };
177
+ var AmbiguousBoxError = class extends Error {
178
+ constructor(query, matches) {
179
+ const ids = matches.map((m) => m.id).join(", ");
180
+ super(`"${query}" matches multiple boxes: ${ids}`);
181
+ this.query = query;
182
+ this.matches = matches;
183
+ this.name = "AmbiguousBoxError";
184
+ }
185
+ query;
186
+ matches;
187
+ };
188
+ var BOX_ID_PREFIX = "b";
189
+ function generateBoxId() {
190
+ return `${BOX_ID_PREFIX}${randomBytes(4).toString("hex")}`;
191
+ }
192
+
3
193
  // ../../packages/sandbox-core/dist/index.js
4
194
  import { mkdir, open, readFile, rename, rm, stat, writeFile } from "fs/promises";
5
195
  import { homedir } from "os";
@@ -8,9 +198,12 @@ import { setTimeout as delay } from "timers/promises";
8
198
  import { execa } from "execa";
9
199
  import { readdir, stat as stat2 } from "fs/promises";
10
200
  import { join as join2 } from "path";
201
+ import { mkdtemp, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
202
+ import { tmpdir } from "os";
203
+ import { basename, join as join3 } from "path";
11
204
  import { createHash } from "crypto";
12
205
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
13
- import { readFile as readFile2 } from "fs/promises";
206
+ import { readFile as readFile3 } from "fs/promises";
14
207
  import { homedir as homedir2 } from "os";
15
208
  import { dirname as dirname2, resolve as pathResolve } from "path";
16
209
  var STATE_DIR = join(homedir(), ".agentbox");
@@ -238,6 +431,46 @@ var GitWorktreeError = class extends Error {
238
431
  function hostOpenCommand() {
239
432
  return process.platform === "linux" ? "xdg-open" : "open";
240
433
  }
434
+ function carryPlaceholderContext(ctx) {
435
+ const out = {};
436
+ if (ctx.name) out.AGENTBOX_BOX_NAME = ctx.name;
437
+ if (ctx.id) out.AGENTBOX_BOX_ID = ctx.id;
438
+ if (ctx.kind) out.AGENTBOX_BOX_KIND = ctx.kind;
439
+ if (ctx.hostWorkspace) out.AGENTBOX_HOST_WORKSPACE = ctx.hostWorkspace;
440
+ if (ctx.projectRoot) out.AGENTBOX_PROJECT_ROOT = ctx.projectRoot;
441
+ return deriveBoxHost(out);
442
+ }
443
+ function wantsRender(e) {
444
+ return e.kind === "file" && (!!e.replaceEnvs || (e.replace?.length ?? 0) > 0);
445
+ }
446
+ async function renderCarryEntries(entries, ctx, onLog) {
447
+ if (!entries.some(wantsRender)) return entries;
448
+ const context = carryPlaceholderContext(ctx);
449
+ const stage = await mkdtemp(join3(tmpdir(), "agentbox-carry-render-"));
450
+ const out = [];
451
+ for (const [i, entry] of entries.entries()) {
452
+ if (!wantsRender(entry)) {
453
+ out.push(entry);
454
+ continue;
455
+ }
456
+ const content = await readFile2(entry.absSrc, "utf8");
457
+ const rendered = applyReplacements(content, {
458
+ env: entry.replaceEnvs,
459
+ rules: entry.replace,
460
+ context,
461
+ onWarn: (msg) => onLog?.(`carry: ${entry.rawSrc}: ${msg}`)
462
+ });
463
+ const tmp = join3(stage, `${String(i)}-${basename(entry.absSrc)}`);
464
+ await writeFile2(tmp, rendered, "utf8");
465
+ out.push({ ...entry, absSrc: tmp, bytes: Buffer.byteLength(rendered) });
466
+ const what = [
467
+ ...entry.replaceEnvs ? ["env"] : [],
468
+ ...entry.replace?.length ? [`${String(entry.replace.length)} rule(s)`] : []
469
+ ].join("+");
470
+ onLog?.(`carry: rendered ${entry.rawSrc} (${what})`);
471
+ }
472
+ return out;
473
+ }
241
474
  function preparedStatePathFor(provider) {
242
475
  return pathResolve(homedir2(), ".agentbox", `${provider}-prepared.json`);
243
476
  }
@@ -259,7 +492,7 @@ function writePreparedStateRaw(provider, state) {
259
492
  renameSync(tmp, path);
260
493
  }
261
494
  async function sha256OfFile(path) {
262
- const buf = await readFile2(path);
495
+ const buf = await readFile3(path);
263
496
  return createHash("sha256").update(buf).digest("hex");
264
497
  }
265
498
  async function computeContextSha256(files) {
@@ -430,7 +663,7 @@ async function buildImage(opts = {}) {
430
663
  return ref;
431
664
  }
432
665
  async function pullOrBuild(ref, fingerprint, opts = {}) {
433
- const { writePreparedDockerState: writePreparedDockerState2 } = await import("./prepared-state-MQHD3M5F-Q27AZU53.js");
666
+ const { writePreparedDockerState: writePreparedDockerState2 } = await import("./prepared-state-MQHD3M5F-2LANTRL7.js");
434
667
  const registry = opts.registry ?? BOX_IMAGE_REGISTRY;
435
668
  const allowPull = opts.allowPull !== false;
436
669
  if (allowPull && registry && fingerprint) {
@@ -456,7 +689,7 @@ async function pullOrBuild(ref, fingerprint, opts = {}) {
456
689
  return { source: "built" };
457
690
  }
458
691
  async function ensureImage(ref = DEFAULT_BOX_IMAGE, opts = {}) {
459
- const { computeDockerContextFingerprint: computeDockerContextFingerprint2, readPreparedDockerState: readPreparedDockerState2, preparedMatches: preparedMatches2 } = await import("./prepared-state-MQHD3M5F-Q27AZU53.js");
692
+ const { computeDockerContextFingerprint: computeDockerContextFingerprint2, readPreparedDockerState: readPreparedDockerState2, preparedMatches: preparedMatches2 } = await import("./prepared-state-MQHD3M5F-2LANTRL7.js");
460
693
  const fingerprint = await computeDockerContextFingerprint2({
461
694
  contextDir: opts.contextDir
462
695
  });
@@ -526,6 +759,15 @@ function preparedMatches(state, current) {
526
759
  }
527
760
 
528
761
  export {
762
+ resolveAgentLauncher,
763
+ ReplaceError,
764
+ parseReplaceRules,
765
+ parseReplacements,
766
+ resolveRuleRefs,
767
+ UserFacingError,
768
+ BoxNotFoundError,
769
+ AmbiguousBoxError,
770
+ generateBoxId,
529
771
  STATE_DIR,
530
772
  STATE_FILE,
531
773
  readState,
@@ -540,6 +782,7 @@ export {
540
782
  pickFreshBranch,
541
783
  GitWorktreeError,
542
784
  hostOpenCommand,
785
+ renderCarryEntries,
543
786
  preparedStatePathFor,
544
787
  readPreparedStateRaw,
545
788
  writePreparedStateRaw,
@@ -563,4 +806,4 @@ export {
563
806
  writePreparedDockerState,
564
807
  preparedMatches
565
808
  };
566
- //# sourceMappingURL=chunk-XKH7NTT7.js.map
809
+ //# sourceMappingURL=chunk-DBBUDKKB.js.map