@madarco/agentbox 0.15.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 (65) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/{_cloud-attach-R6TRWG5L.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-MLMFNN4T.js → chunk-GXJNJUEV.js} +688 -197
  7. package/dist/chunk-GXJNJUEV.js.map +1 -0
  8. package/dist/{chunk-MCOU6CZS.js → chunk-NW2UZQV6.js} +10 -6
  9. package/dist/chunk-NW2UZQV6.js.map +1 -0
  10. package/dist/{chunk-E7CHS7ZR.js → chunk-PIK47622.js} +18 -6
  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-43Q5GWP6.js → chunk-SB4QTF2T.js} +7 -7
  15. package/dist/{chunk-72CJTXN6.js → chunk-SENASAU4.js} +10 -6
  16. package/dist/{chunk-72CJTXN6.js.map → chunk-SENASAU4.js.map} +1 -1
  17. package/dist/{dist-JZ3XO6EB.js → dist-4IQFJJQI.js} +5 -5
  18. package/dist/{dist-OGJGZETZ.js → dist-7YB7BMNG.js} +5 -5
  19. package/dist/{dist-FIFEFKJ7.js → dist-SL2QSMBE.js} +5 -5
  20. package/dist/{dist-AGTIA7AD.js → dist-VHI5QOSQ.js} +6 -6
  21. package/dist/{dist-S4XR4ACV.js → dist-XC47DSCR.js} +5 -5
  22. package/dist/index.js +210 -68
  23. package/dist/index.js.map +1 -1
  24. package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js → prepared-state-MQHD3M5F-2LANTRL7.js} +2 -2
  25. package/package.json +5 -4
  26. package/runtime/docker/Dockerfile.box +21 -2
  27. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +82 -29
  28. package/runtime/docker/packages/ctl/dist/bin.cjs +10675 -9191
  29. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup +5 -2
  30. package/runtime/docker/packages/sandbox-docker/scripts/linear-shim +181 -0
  31. package/runtime/docker/packages/sandbox-docker/scripts/ntn-shim +95 -0
  32. package/runtime/e2b/agentbox-checkpoint-cleanup +5 -2
  33. package/runtime/e2b/agentbox-setup-skill.md +82 -29
  34. package/runtime/e2b/ctl.cjs +10675 -9191
  35. package/runtime/e2b/linear-shim +181 -0
  36. package/runtime/e2b/ntn-shim +95 -0
  37. package/runtime/e2b/scripts/build-template.sh +13 -7
  38. package/runtime/hetzner/agentbox-checkpoint-cleanup +5 -2
  39. package/runtime/hetzner/agentbox-setup-skill.md +82 -29
  40. package/runtime/hetzner/ctl.cjs +10675 -9191
  41. package/runtime/hetzner/linear-shim +181 -0
  42. package/runtime/hetzner/ntn-shim +95 -0
  43. package/runtime/hetzner/scripts/install-box.sh +19 -9
  44. package/runtime/relay/bin.cjs +3696 -2895
  45. package/runtime/vercel/agentbox-checkpoint-cleanup +5 -2
  46. package/runtime/vercel/agentbox-setup-skill.md +82 -29
  47. package/runtime/vercel/ctl.cjs +10675 -9191
  48. package/runtime/vercel/linear-shim +181 -0
  49. package/runtime/vercel/ntn-shim +95 -0
  50. package/runtime/vercel/scripts/provision.sh +13 -7
  51. package/share/agentbox-setup/SKILL.md +82 -29
  52. package/share/host-skills/agentbox-info/SKILL.md +1 -1
  53. package/dist/chunk-E7CHS7ZR.js.map +0 -1
  54. package/dist/chunk-MCOU6CZS.js.map +0 -1
  55. package/dist/chunk-MLMFNN4T.js.map +0 -1
  56. package/dist/chunk-XKH7NTT7.js.map +0 -1
  57. /package/dist/{_cloud-attach-R6TRWG5L.js.map → _cloud-attach-5KJWOASL.js.map} +0 -0
  58. /package/dist/{chunk-RSKG7AFU.js.map → chunk-3WCEB6RE.js.map} +0 -0
  59. /package/dist/{chunk-43Q5GWP6.js.map → chunk-SB4QTF2T.js.map} +0 -0
  60. /package/dist/{dist-JZ3XO6EB.js.map → dist-4IQFJJQI.js.map} +0 -0
  61. /package/dist/{dist-OGJGZETZ.js.map → dist-7YB7BMNG.js.map} +0 -0
  62. /package/dist/{dist-FIFEFKJ7.js.map → dist-SL2QSMBE.js.map} +0 -0
  63. /package/dist/{dist-AGTIA7AD.js.map → dist-VHI5QOSQ.js.map} +0 -0
  64. /package/dist/{dist-S4XR4ACV.js.map → dist-XC47DSCR.js.map} +0 -0
  65. /package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js.map → prepared-state-MQHD3M5F-2LANTRL7.js.map} +0 -0
@@ -1,15 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ AmbiguousBoxError,
4
+ BoxNotFoundError,
3
5
  DEFAULT_BOX_IMAGE,
4
6
  GitWorktreeError,
7
+ ReplaceError,
5
8
  STATE_DIR,
6
9
  STATE_FILE,
7
10
  computeDockerContextFingerprint,
8
11
  detectGitRepos,
9
12
  ensureImage,
10
13
  findBox,
14
+ generateBoxId,
11
15
  hostOpenCommand,
12
16
  imageExists,
17
+ parseReplaceRules,
18
+ parseReplacements,
13
19
  pickFreshBranch,
14
20
  preparedMatches,
15
21
  pullOrBuild,
@@ -18,20 +24,22 @@ import {
18
24
  readState,
19
25
  recordBox,
20
26
  removeBoxRecord,
27
+ renderCarryEntries,
21
28
  reserveProjectIndex
22
- } from "./chunk-XKH7NTT7.js";
29
+ } from "./chunk-DBBUDKKB.js";
23
30
 
24
31
  // ../../packages/sandbox-docker/dist/index.js
25
32
  import { mkdir as mkdir7, stat as stat8 } from "fs/promises";
26
33
  import { homedir as homedir10 } from "os";
27
- import { basename as basename5, join as join12, resolve as resolve4 } from "path";
34
+ import { basename as basename6, join as join12, resolve as resolve4 } from "path";
28
35
  import { execa as execa15 } from "execa";
29
36
 
30
37
  // ../../packages/ctl/dist/index.js
31
- import { readFile } from "fs/promises";
32
- import { parse as parseYaml } from "yaml";
33
38
  import { readFile as readFile2 } from "fs/promises";
34
39
  import { parse as parseYaml2 } from "yaml";
40
+ import { parse as parseYaml } from "yaml";
41
+ import { readFile as readFile3 } from "fs/promises";
42
+ import { parse as parseYaml3 } from "yaml";
35
43
  var BOX_STATUS_EVENT = "box-status";
36
44
  function renderStatusTable(rows) {
37
45
  if (rows.length === 0) return "(no services configured)";
@@ -89,6 +97,20 @@ function truncate(s, n) {
89
97
  if (s.length <= n) return s;
90
98
  return s.slice(0, n - 1) + "\u2026";
91
99
  }
100
+ function isPlainObject(v) {
101
+ return typeof v === "object" && v !== null && !Array.isArray(v);
102
+ }
103
+ function parseReplacementsSection(text) {
104
+ let doc;
105
+ try {
106
+ doc = parseYaml(text);
107
+ } catch (err) {
108
+ throw new ReplaceError(`yaml parse error: ${err instanceof Error ? err.message : String(err)}`);
109
+ }
110
+ if (doc === null || doc === void 0) return {};
111
+ if (!isPlainObject(doc)) throw new ReplaceError("top-level config must be a mapping");
112
+ return parseReplacements(doc.replacements);
113
+ }
92
114
  var DEFAULT_BACKOFF = {
93
115
  initialMs: 500,
94
116
  maxMs: 3e4,
@@ -105,12 +127,12 @@ var ConfigError = class extends Error {
105
127
  this.name = "ConfigError";
106
128
  }
107
129
  };
108
- function isPlainObject(v) {
130
+ function isPlainObject2(v) {
109
131
  return typeof v === "object" && v !== null && !Array.isArray(v);
110
132
  }
111
133
  function parseEnv(raw, where) {
112
134
  if (raw === void 0 || raw === null) return void 0;
113
- if (!isPlainObject(raw)) {
135
+ if (!isPlainObject2(raw)) {
114
136
  throw new ConfigError(`${where}.env must be a mapping of string \u2192 string`);
115
137
  }
116
138
  const out = {};
@@ -152,7 +174,7 @@ function parseRestart(raw, where) {
152
174
  var BACKOFF_KEYS = /* @__PURE__ */ new Set(["initial_ms", "max_ms", "factor"]);
153
175
  function parseBackoff(raw, where) {
154
176
  if (raw === void 0) return { ...DEFAULT_BACKOFF };
155
- if (!isPlainObject(raw)) {
177
+ if (!isPlainObject2(raw)) {
156
178
  throw new ConfigError(`${where}.backoff must be a mapping`);
157
179
  }
158
180
  rejectUnknownKeys(raw, BACKOFF_KEYS, `${where}.backoff`);
@@ -234,7 +256,7 @@ var PROBE_KEYS = /* @__PURE__ */ new Set([
234
256
  ]);
235
257
  function parseReadyWhen(raw, where) {
236
258
  if (raw === void 0 || raw === null) return void 0;
237
- if (!isPlainObject(raw)) {
259
+ if (!isPlainObject2(raw)) {
238
260
  throw new ConfigError(`${where}.ready_when must be a mapping`);
239
261
  }
240
262
  rejectUnknownKeys(raw, PROBE_KEYS, `${where}.ready_when`);
@@ -330,12 +352,94 @@ var SERVICE_KEYS = /* @__PURE__ */ new Set([
330
352
  "needs",
331
353
  "ready_when",
332
354
  "expose",
333
- "ide"
355
+ "ide",
356
+ "image"
334
357
  ]);
358
+ var IMAGE_KEYS = /* @__PURE__ */ new Set(["name", "ports", "env", "args", "container_name"]);
359
+ function shQuote(s) {
360
+ if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(s)) return s;
361
+ return `'${s.replace(/'/g, `'\\''`)}'`;
362
+ }
363
+ function parsePorts(raw, where) {
364
+ if (raw === void 0 || raw === null) return void 0;
365
+ if (!Array.isArray(raw)) {
366
+ throw new ConfigError(`${where}.ports must be a list of "<host>:<container>" strings`);
367
+ }
368
+ const out = [];
369
+ for (const [i, v] of raw.entries()) {
370
+ const s = typeof v === "number" ? String(v) : v;
371
+ if (typeof s !== "string" || !/^\d+(:\d+)?$/.test(s.trim())) {
372
+ throw new ConfigError(
373
+ `${where}.ports[${String(i)}] must be "<host>" or "<host>:<container>" (got ${JSON.stringify(v)})`
374
+ );
375
+ }
376
+ out.push(s.trim());
377
+ }
378
+ return out.length > 0 ? out : void 0;
379
+ }
380
+ function parseArgs(raw, where) {
381
+ if (raw === void 0 || raw === null) return void 0;
382
+ if (typeof raw === "string") return raw.trim().length > 0 ? raw : void 0;
383
+ if (Array.isArray(raw)) {
384
+ const parts = [];
385
+ for (const [i, v] of raw.entries()) {
386
+ if (typeof v !== "string") throw new ConfigError(`${where}.args[${String(i)}] must be a string`);
387
+ parts.push(v);
388
+ }
389
+ const joined = parts.join(" ").trim();
390
+ return joined.length > 0 ? joined : void 0;
391
+ }
392
+ throw new ConfigError(`${where}.args must be a string or a list of strings`);
393
+ }
394
+ function synthesizeImageCommand(opts) {
395
+ const name = shQuote(opts.name);
396
+ const run = ["docker", "run", "--name", name];
397
+ for (const p of opts.ports ?? []) run.push("-p", shQuote(p));
398
+ for (const [k, v] of Object.entries(opts.env ?? {})) run.push("-e", `${k}=${shQuote(v)}`);
399
+ run.push(shQuote(opts.image));
400
+ let runLine = run.join(" ");
401
+ if (opts.args) runLine += ` ${opts.args}`;
402
+ return [
403
+ "set -e",
404
+ `if docker container inspect ${name} >/dev/null 2>&1; then`,
405
+ ` docker start ${name} >/dev/null`,
406
+ ` exec docker logs -f ${name}`,
407
+ "else",
408
+ ` exec ${runLine}`,
409
+ "fi"
410
+ ].join("\n");
411
+ }
412
+ function parseImage(raw, where, defaultName) {
413
+ if (typeof raw === "string") {
414
+ const name2 = raw.trim();
415
+ if (name2.length === 0) throw new ConfigError(`${where}.image must not be empty`);
416
+ return { name: name2, containerName: defaultName };
417
+ }
418
+ if (!isPlainObject2(raw)) {
419
+ throw new ConfigError(`${where}.image must be an image ref string or a mapping`);
420
+ }
421
+ rejectUnknownKeys(raw, IMAGE_KEYS, `${where}.image`);
422
+ const name = assertString(raw.name, `${where}.image.name`).trim();
423
+ if (name.length === 0) throw new ConfigError(`${where}.image.name must not be empty`);
424
+ const ports = parsePorts(raw.ports, `${where}.image`);
425
+ const args = parseArgs(raw.args, `${where}.image`);
426
+ const env = parseEnv(raw.env, `${where}.image`);
427
+ const containerName = raw.container_name === void 0 ? defaultName : assertString(raw.container_name, `${where}.image.container_name`).trim();
428
+ if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(containerName)) {
429
+ throw new ConfigError(
430
+ `${where}.image.container_name "${containerName}" is not a valid docker container name`
431
+ );
432
+ }
433
+ const out = { name, containerName };
434
+ if (ports !== void 0) out.ports = ports;
435
+ if (env !== void 0) out.env = env;
436
+ if (args !== void 0) out.args = args;
437
+ return out;
438
+ }
335
439
  var EXPOSE_KEYS = /* @__PURE__ */ new Set(["port", "as"]);
336
440
  function parseExpose(raw, where) {
337
441
  if (raw === void 0 || raw === null) return void 0;
338
- if (!isPlainObject(raw)) {
442
+ if (!isPlainObject2(raw)) {
339
443
  throw new ConfigError(`${where}.expose must be a mapping`);
340
444
  }
341
445
  rejectUnknownKeys(raw, EXPOSE_KEYS, `${where}.expose`);
@@ -359,25 +463,78 @@ function parsePortNumber(raw, where) {
359
463
  }
360
464
  function parseService(name, raw) {
361
465
  const where = `services.${name}`;
362
- if (!isPlainObject(raw)) {
466
+ if (!isPlainObject2(raw)) {
363
467
  throw new ConfigError(`${where} must be a mapping`);
364
468
  }
365
469
  rejectUnknownKeys(raw, SERVICE_KEYS, where);
366
- const command = parseCommand(raw.command, where);
470
+ const hasImage = raw.image !== void 0 && raw.image !== null;
471
+ const hasCommand = raw.command !== void 0 && raw.command !== null;
472
+ if (hasImage && hasCommand) {
473
+ throw new ConfigError(`${where} sets both command and image \u2014 use exactly one`);
474
+ }
475
+ if (!hasImage && !hasCommand) {
476
+ throw new ConfigError(`${where} must set either command or image`);
477
+ }
367
478
  const cwd = raw.cwd === void 0 ? void 0 : assertString(raw.cwd, `${where}.cwd`);
368
- const env = parseEnv(raw.env, where);
369
479
  const autostart = raw.autostart === void 0 ? true : assertBool(raw.autostart, `${where}.autostart`);
370
480
  const restart2 = parseRestart(raw.restart, where);
371
481
  const backoff = parseBackoff(raw.backoff, where);
372
482
  const needs = parseNeeds(raw.needs, `${where}.needs`);
373
483
  const readyWhen = parseReadyWhen(raw.ready_when, where);
374
484
  const expose = parseExpose(raw.expose, where);
485
+ if (hasImage) {
486
+ if (raw.env !== void 0) {
487
+ throw new ConfigError(`${where}.env is not valid for an image service \u2014 use image.env`);
488
+ }
489
+ const img = parseImage(raw.image, where, name);
490
+ const command2 = synthesizeImageCommand({
491
+ image: img.name,
492
+ name: img.containerName,
493
+ ports: img.ports,
494
+ env: img.env,
495
+ args: img.args
496
+ });
497
+ const spec = {
498
+ name,
499
+ command: command2,
500
+ cwd,
501
+ autostart,
502
+ restart: restart2,
503
+ backoff,
504
+ needs,
505
+ readyWhen,
506
+ expose,
507
+ image: img.name,
508
+ containerName: img.containerName
509
+ };
510
+ if (img.ports !== void 0) spec.ports = img.ports;
511
+ if (img.args !== void 0) spec.args = img.args;
512
+ return spec;
513
+ }
514
+ const command = parseCommand(raw.command, where);
515
+ const env = parseEnv(raw.env, where);
375
516
  return { name, command, cwd, env, autostart, restart: restart2, backoff, needs, readyWhen, expose };
376
517
  }
377
- var TASK_KEYS = /* @__PURE__ */ new Set(["command", "cwd", "env", "needs"]);
518
+ var TASK_KEYS = /* @__PURE__ */ new Set(["command", "cwd", "env", "needs", "run_once"]);
519
+ function parseRunOnce(raw, where) {
520
+ if (raw === void 0 || raw === null || raw === false) return void 0;
521
+ if (raw === true) return { kind: "marker" };
522
+ if (isPlainObject2(raw)) {
523
+ const keys = Object.keys(raw);
524
+ if (keys.length !== 1 || keys[0] !== "check") {
525
+ throw new ConfigError(`${where}.run_once object form must be exactly { check: <command> }`);
526
+ }
527
+ const check = raw.check;
528
+ if (typeof check !== "string" || check.trim().length === 0) {
529
+ throw new ConfigError(`${where}.run_once.check must be a non-empty command string`);
530
+ }
531
+ return { kind: "check", command: check };
532
+ }
533
+ throw new ConfigError(`${where}.run_once must be true or { check: <command> }`);
534
+ }
378
535
  function parseTask(name, raw) {
379
536
  const where = `tasks.${name}`;
380
- if (!isPlainObject(raw)) {
537
+ if (!isPlainObject2(raw)) {
381
538
  throw new ConfigError(`${where} must be a mapping`);
382
539
  }
383
540
  rejectUnknownKeys(raw, TASK_KEYS, where);
@@ -385,7 +542,10 @@ function parseTask(name, raw) {
385
542
  const cwd = raw.cwd === void 0 ? void 0 : assertString(raw.cwd, `${where}.cwd`);
386
543
  const env = parseEnv(raw.env, where);
387
544
  const needs = parseNeeds(raw.needs, `${where}.needs`);
388
- return { name, command, cwd, env, needs };
545
+ const runOnce = parseRunOnce(raw.run_once, where);
546
+ const spec = { name, command, cwd, env, needs };
547
+ if (runOnce !== void 0) spec.runOnce = runOnce;
548
+ return spec;
389
549
  }
390
550
  function assertString(raw, where) {
391
551
  if (typeof raw !== "string") throw new ConfigError(`${where} must be a string`);
@@ -395,7 +555,7 @@ function assertBool(raw, where) {
395
555
  if (typeof raw !== "boolean") throw new ConfigError(`${where} must be a boolean`);
396
556
  return raw;
397
557
  }
398
- var TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["services", "tasks", "ide", "defaults", "carry"]);
558
+ var TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["services", "tasks", "ide", "defaults", "carry", "replacements"]);
399
559
  function validateUnitGraph(tasks, services) {
400
560
  const names = /* @__PURE__ */ new Set();
401
561
  for (const t of tasks) {
@@ -449,19 +609,19 @@ function validateUnitGraph(tasks, services) {
449
609
  function parseConfig(text) {
450
610
  let doc;
451
611
  try {
452
- doc = parseYaml(text);
612
+ doc = parseYaml2(text);
453
613
  } catch (err) {
454
614
  throw new ConfigError(`yaml parse error: ${err instanceof Error ? err.message : String(err)}`);
455
615
  }
456
- if (doc === null || doc === void 0) return { services: [], tasks: [] };
457
- if (!isPlainObject(doc)) {
616
+ if (doc === null || doc === void 0) return { services: [], tasks: [], replacements: {} };
617
+ if (!isPlainObject2(doc)) {
458
618
  throw new ConfigError("top-level config must be a mapping");
459
619
  }
460
620
  rejectUnknownKeys(doc, TOP_LEVEL_KEYS, "(root)");
461
621
  const services = [];
462
622
  const servicesRaw = doc.services;
463
623
  if (servicesRaw !== void 0 && servicesRaw !== null) {
464
- if (!isPlainObject(servicesRaw)) {
624
+ if (!isPlainObject2(servicesRaw)) {
465
625
  throw new ConfigError("services must be a mapping of name \u2192 service");
466
626
  }
467
627
  for (const [name, raw] of Object.entries(servicesRaw)) {
@@ -474,7 +634,7 @@ function parseConfig(text) {
474
634
  const tasks = [];
475
635
  const tasksRaw = doc.tasks;
476
636
  if (tasksRaw !== void 0 && tasksRaw !== null) {
477
- if (!isPlainObject(tasksRaw)) {
637
+ if (!isPlainObject2(tasksRaw)) {
478
638
  throw new ConfigError("tasks must be a mapping of name \u2192 task");
479
639
  }
480
640
  for (const [name, raw] of Object.entries(tasksRaw)) {
@@ -484,10 +644,10 @@ function parseConfig(text) {
484
644
  tasks.push(parseTask(name, raw));
485
645
  }
486
646
  }
487
- if (doc.ide !== void 0 && doc.ide !== null && !isPlainObject(doc.ide)) {
647
+ if (doc.ide !== void 0 && doc.ide !== null && !isPlainObject2(doc.ide)) {
488
648
  throw new ConfigError("ide must be a mapping");
489
649
  }
490
- if (doc.defaults !== void 0 && doc.defaults !== null && !isPlainObject(doc.defaults)) {
650
+ if (doc.defaults !== void 0 && doc.defaults !== null && !isPlainObject2(doc.defaults)) {
491
651
  throw new ConfigError("defaults must be a mapping");
492
652
  }
493
653
  validateUnitGraph(tasks, services);
@@ -497,15 +657,21 @@ function parseConfig(text) {
497
657
  `at most one service may set expose: (got: ${exposed.map((s) => s.name).join(", ")})`
498
658
  );
499
659
  }
500
- return { services, tasks };
660
+ let replacements;
661
+ try {
662
+ replacements = parseReplacements(doc.replacements);
663
+ } catch (err) {
664
+ throw new ConfigError(err instanceof Error ? err.message : String(err));
665
+ }
666
+ return { services, tasks, replacements };
501
667
  }
502
668
  async function loadConfig(path) {
503
669
  let text;
504
670
  try {
505
- text = await readFile(path, "utf8");
671
+ text = await readFile2(path, "utf8");
506
672
  } catch (err) {
507
673
  if (err.code === "ENOENT") {
508
- return { services: [], tasks: [] };
674
+ return { services: [], tasks: [], replacements: {} };
509
675
  }
510
676
  throw err;
511
677
  }
@@ -517,16 +683,26 @@ var CarryConfigError = class extends Error {
517
683
  this.name = "CarryConfigError";
518
684
  }
519
685
  };
520
- var ITEM_KEYS = /* @__PURE__ */ new Set(["src", "dest", "mode", "user", "exclude", "optional"]);
521
- function parseExclude(raw, where) {
686
+ var ITEM_KEYS = /* @__PURE__ */ new Set([
687
+ "src",
688
+ "dest",
689
+ "mode",
690
+ "user",
691
+ "exclude",
692
+ "optional",
693
+ "replaceEnvs",
694
+ "replace",
695
+ "rules"
696
+ ]);
697
+ function parseStringList(raw, where, desc) {
522
698
  if (raw === void 0 || raw === null) return void 0;
523
699
  if (!Array.isArray(raw)) {
524
- throw new CarryConfigError(`${where}.exclude must be a list of glob/name strings`);
700
+ throw new CarryConfigError(`${where} must be a list of ${desc}`);
525
701
  }
526
702
  const out = [];
527
703
  for (const [i, v] of raw.entries()) {
528
704
  if (typeof v !== "string" || v.trim().length === 0) {
529
- throw new CarryConfigError(`${where}.exclude[${String(i)}] must be a non-empty string`);
705
+ throw new CarryConfigError(`${where}[${String(i)}] must be a non-empty string`);
530
706
  }
531
707
  out.push(v.trim());
532
708
  }
@@ -556,7 +732,7 @@ function parseUser(raw, where) {
556
732
  }
557
733
  return n;
558
734
  }
559
- function isPlainObject2(v) {
735
+ function isPlainObject3(v) {
560
736
  return typeof v === "object" && v !== null && !Array.isArray(v);
561
737
  }
562
738
  function assertSrcShape(src, where) {
@@ -660,7 +836,7 @@ function parseMapping(raw, where) {
660
836
  assertDestShape(dest, where);
661
837
  const mode = parseMode(raw.mode, where);
662
838
  const user = parseUser(raw.user, where);
663
- const exclude = parseExclude(raw.exclude, where);
839
+ const exclude = parseStringList(raw.exclude, `${where}.exclude`, "glob/name strings");
664
840
  let optional = false;
665
841
  if (raw.optional !== void 0 && raw.optional !== null) {
666
842
  if (typeof raw.optional !== "boolean") {
@@ -668,10 +844,30 @@ function parseMapping(raw, where) {
668
844
  }
669
845
  optional = raw.optional;
670
846
  }
847
+ let replaceEnvs;
848
+ if (raw.replaceEnvs !== void 0 && raw.replaceEnvs !== null) {
849
+ if (typeof raw.replaceEnvs !== "boolean") {
850
+ throw new CarryConfigError(`${where}.replaceEnvs must be a boolean`);
851
+ }
852
+ replaceEnvs = raw.replaceEnvs;
853
+ }
854
+ let replace;
855
+ if (raw.replace !== void 0 && raw.replace !== null) {
856
+ try {
857
+ const rules2 = parseReplaceRules(raw.replace, `${where}.replace`);
858
+ if (rules2.length > 0) replace = rules2;
859
+ } catch (err) {
860
+ throw new CarryConfigError(err instanceof Error ? err.message : String(err));
861
+ }
862
+ }
863
+ const rules = parseStringList(raw.rules, `${where}.rules`, "replacements rule-set names");
671
864
  const out = { src, dest, optional };
672
865
  if (mode !== void 0) out.mode = mode;
673
866
  if (user !== void 0) out.user = user;
674
867
  if (exclude !== void 0) out.exclude = exclude;
868
+ if (replaceEnvs !== void 0) out.replaceEnvs = replaceEnvs;
869
+ if (replace !== void 0) out.replace = replace;
870
+ if (rules !== void 0) out.rules = rules;
675
871
  return out;
676
872
  }
677
873
  function parseCarryRaw(raw) {
@@ -684,7 +880,7 @@ function parseCarryRaw(raw) {
684
880
  const where = `carry[${String(i)}]`;
685
881
  if (typeof item === "string") {
686
882
  out.push(parseShorthand(item, where));
687
- } else if (isPlainObject2(item)) {
883
+ } else if (isPlainObject3(item)) {
688
884
  out.push(parseMapping(item, where));
689
885
  } else {
690
886
  throw new CarryConfigError(`${where} must be a string or mapping`);
@@ -695,14 +891,14 @@ function parseCarryRaw(raw) {
695
891
  function parseCarrySection(text) {
696
892
  let doc;
697
893
  try {
698
- doc = parseYaml2(text);
894
+ doc = parseYaml3(text);
699
895
  } catch (err) {
700
896
  throw new CarryConfigError(
701
897
  `yaml parse error: ${err instanceof Error ? err.message : String(err)}`
702
898
  );
703
899
  }
704
900
  if (doc === null || doc === void 0) return [];
705
- if (!isPlainObject2(doc)) {
901
+ if (!isPlainObject3(doc)) {
706
902
  throw new CarryConfigError("top-level config must be a mapping");
707
903
  }
708
904
  return parseCarryRaw(doc.carry);
@@ -710,7 +906,7 @@ function parseCarrySection(text) {
710
906
  async function loadCarrySection(path) {
711
907
  let text;
712
908
  try {
713
- text = await readFile2(path, "utf8");
909
+ text = await readFile3(path, "utf8");
714
910
  } catch (err) {
715
911
  if (err.code === "ENOENT") return [];
716
912
  throw err;
@@ -719,12 +915,12 @@ async function loadCarrySection(path) {
719
915
  }
720
916
 
721
917
  // ../../packages/config/dist/index.js
722
- import { parse as parseYaml3 } from "yaml";
918
+ import { parse as parseYaml4 } from "yaml";
723
919
  import { createHash } from "crypto";
724
920
  import { realpath, stat } from "fs/promises";
725
921
  import { homedir } from "os";
726
922
  import { basename, dirname, join, resolve } from "path";
727
- import { readFile as readFile3 } from "fs/promises";
923
+ import { readFile } from "fs/promises";
728
924
  import { parse as parseYaml22 } from "yaml";
729
925
  import { mkdir, readFile as readFile22, rename, rm, stat as stat2, writeFile } from "fs/promises";
730
926
  import { dirname as dirname2, isAbsolute, join as join2 } from "path";
@@ -837,6 +1033,10 @@ var BUILT_IN_DEFAULTS = {
837
1033
  maintenance: {
838
1034
  pruneProjectConfigs: true,
839
1035
  pruneProjectConfigsEvery: 50
1036
+ },
1037
+ integrations: {
1038
+ notion: { enabled: false },
1039
+ linear: { enabled: false }
840
1040
  }
841
1041
  };
842
1042
  var KEY_REGISTRY = [
@@ -1223,6 +1423,16 @@ var KEY_REGISTRY = [
1223
1423
  key: "maintenance.pruneProjectConfigsEvery",
1224
1424
  type: "int",
1225
1425
  description: "Run the orphan project-config sweep every N successful `agentbox create`."
1426
+ },
1427
+ {
1428
+ key: "integrations.notion.enabled",
1429
+ type: "bool",
1430
+ description: 'Enable the in-box Notion integration shim (`ntn`/`notion` commands routed via the host relay). When false (default), the relay refuses dispatch with a clear "disabled" error and no host process is touched.'
1431
+ },
1432
+ {
1433
+ key: "integrations.linear.enabled",
1434
+ type: "bool",
1435
+ description: 'Enable the in-box Linear integration shim (`linear` commands routed via the host relay; backed by `@schpet/linear-cli`). When false (default), the relay refuses dispatch with a clear "disabled" error and no host process is touched.'
1226
1436
  }
1227
1437
  ];
1228
1438
  var REGISTRY_BY_KEY = new Map(KEY_REGISTRY.map((d) => [d.key, d]));
@@ -1235,7 +1445,7 @@ var UserConfigError = class extends Error {
1235
1445
  this.name = "UserConfigError";
1236
1446
  }
1237
1447
  };
1238
- function isPlainObject3(v) {
1448
+ function isPlainObject4(v) {
1239
1449
  return typeof v === "object" && v !== null && !Array.isArray(v);
1240
1450
  }
1241
1451
  var RENAMED_KEYS = /* @__PURE__ */ new Map([["box.snapshot", "box.hostSnapshot"]]);
@@ -1292,21 +1502,21 @@ function coerceTypedValue(raw, desc, where) {
1292
1502
  function parseUserConfig(text, where) {
1293
1503
  let doc;
1294
1504
  try {
1295
- doc = parseYaml3(text);
1505
+ doc = parseYaml4(text);
1296
1506
  } catch (err) {
1297
1507
  throw new UserConfigError(
1298
1508
  `${where}: yaml parse error: ${err instanceof Error ? err.message : String(err)}`
1299
1509
  );
1300
1510
  }
1301
1511
  if (doc === null || doc === void 0) return {};
1302
- if (!isPlainObject3(doc)) {
1512
+ if (!isPlainObject4(doc)) {
1303
1513
  throw new UserConfigError(`${where}: top-level must be a mapping`);
1304
1514
  }
1305
1515
  return parseUserConfigObject(doc, where);
1306
1516
  }
1307
1517
  function parseUserConfigObject(doc, where) {
1308
1518
  if (doc === null || doc === void 0) return {};
1309
- if (!isPlainObject3(doc)) {
1519
+ if (!isPlainObject4(doc)) {
1310
1520
  throw new UserConfigError(`${where}: must be a mapping`);
1311
1521
  }
1312
1522
  const out = {};
@@ -1327,32 +1537,50 @@ function parseUserConfigObject(doc, where) {
1327
1537
  );
1328
1538
  }
1329
1539
  if (branchRaw === null || branchRaw === void 0) continue;
1330
- if (!isPlainObject3(branchRaw)) {
1540
+ if (!isPlainObject4(branchRaw)) {
1331
1541
  throw new UserConfigError(`${where}.${branchName}: must be a mapping`);
1332
1542
  }
1333
- const branchOut = {};
1334
- for (const [leafName, leafRaw] of Object.entries(branchRaw)) {
1335
- const desc = branchSpec.leaves.get(leafName);
1336
- if (!desc) {
1337
- const renamedTo = RENAMED_KEYS.get(`${branchName}.${leafName}`);
1338
- if (renamedTo) {
1339
- throw new UserConfigError(
1340
- `${where}.${branchName}.${leafName} was renamed to ${renamedTo} \u2014 update your config`
1341
- );
1342
- }
1343
- throw new UserConfigError(
1344
- `${where}.${branchName}: unknown key "${leafName}" (known: ${[...branchSpec.leaves.keys()].join(", ")})`
1345
- );
1346
- }
1347
- if (leafRaw === void 0) continue;
1348
- branchOut[leafName] = coerceTypedValue(leafRaw, desc, `${where}.${desc.key}`);
1349
- }
1543
+ const branchOut = parseBranchObject(branchSpec, branchName, branchRaw, "", where);
1350
1544
  if (Object.keys(branchOut).length > 0) {
1351
1545
  out[branchName] = branchOut;
1352
1546
  }
1353
1547
  }
1354
1548
  return out;
1355
1549
  }
1550
+ function parseBranchObject(branchSpec, branchName, raw, qualifiedPrefix, where) {
1551
+ const out = {};
1552
+ for (const [name, value] of Object.entries(raw)) {
1553
+ if (value === void 0) continue;
1554
+ const qualified = qualifiedPrefix ? `${qualifiedPrefix}.${name}` : name;
1555
+ const desc = branchSpec.leaves.get(qualified);
1556
+ if (desc) {
1557
+ out[name] = coerceTypedValue(value, desc, `${where}.${desc.key}`);
1558
+ continue;
1559
+ }
1560
+ if (isPlainObject4(value) && branchHasLeafBelow(branchSpec, qualified)) {
1561
+ const sub = parseBranchObject(branchSpec, branchName, value, qualified, where);
1562
+ if (Object.keys(sub).length > 0) out[name] = sub;
1563
+ continue;
1564
+ }
1565
+ const renamedTo = RENAMED_KEYS.get(`${branchName}.${qualified}`);
1566
+ if (renamedTo) {
1567
+ throw new UserConfigError(
1568
+ `${where}.${branchName}.${qualified} was renamed to ${renamedTo} \u2014 update your config`
1569
+ );
1570
+ }
1571
+ throw new UserConfigError(
1572
+ `${where}.${branchName}: unknown key "${qualified}" (known: ${[...branchSpec.leaves.keys()].join(", ")})`
1573
+ );
1574
+ }
1575
+ return out;
1576
+ }
1577
+ function branchHasLeafBelow(branchSpec, prefix) {
1578
+ const needle = `${prefix}.`;
1579
+ for (const leaf of branchSpec.leaves.keys()) {
1580
+ if (leaf.startsWith(needle)) return true;
1581
+ }
1582
+ return false;
1583
+ }
1356
1584
  function coerceFromString(key, raw) {
1357
1585
  const desc = lookupKeyOrThrow(key);
1358
1586
  switch (desc.type) {
@@ -1468,7 +1696,7 @@ async function configPathFor(scope, cwd) {
1468
1696
  async function loadOptionalUserConfig(path) {
1469
1697
  let text;
1470
1698
  try {
1471
- text = await readFile3(path, "utf8");
1699
+ text = await readFile(path, "utf8");
1472
1700
  } catch (err) {
1473
1701
  if (err.code === "ENOENT") return {};
1474
1702
  throw err;
@@ -1479,7 +1707,7 @@ async function loadProjectAgentboxDefaults(workspacePath) {
1479
1707
  const path = workspaceConfigFile(workspacePath);
1480
1708
  let text;
1481
1709
  try {
1482
- text = await readFile3(path, "utf8");
1710
+ text = await readFile(path, "utf8");
1483
1711
  } catch (err) {
1484
1712
  if (err.code === "ENOENT") return {};
1485
1713
  throw err;
@@ -1559,14 +1787,26 @@ function mergeLayers(input) {
1559
1787
  return { effective, sources };
1560
1788
  }
1561
1789
  function readLeaf(obj, branch, leaf) {
1562
- const b = obj[branch];
1563
- if (b === void 0 || b === null || typeof b !== "object") return void 0;
1564
- return b[leaf];
1790
+ let cur = obj[branch];
1791
+ for (const seg of leaf.split(".")) {
1792
+ if (cur === void 0 || cur === null || typeof cur !== "object") return void 0;
1793
+ cur = cur[seg];
1794
+ }
1795
+ return cur;
1565
1796
  }
1566
1797
  function writeLeaf(obj, branch, leaf, value) {
1567
- const b = obj[branch];
1568
- if (!b) return;
1569
- b[leaf] = value;
1798
+ let cur = obj[branch];
1799
+ if (!cur) return;
1800
+ const segs = leaf.split(".");
1801
+ for (let i = 0; i < segs.length - 1; i++) {
1802
+ const seg = segs[i];
1803
+ const next = cur[seg];
1804
+ if (next === void 0 || next === null || typeof next !== "object") {
1805
+ cur[seg] = {};
1806
+ }
1807
+ cur = cur[seg];
1808
+ }
1809
+ cur[segs[segs.length - 1]] = value;
1570
1810
  }
1571
1811
  function resolveDefaultCheckpoint(cfg, provider) {
1572
1812
  const perProvider = provider === "daytona" ? cfg.box.defaultCheckpointDaytona : provider === "hetzner" ? cfg.box.defaultCheckpointHetzner : provider === "vercel" ? cfg.box.defaultCheckpointVercel : provider === "e2b" ? cfg.box.defaultCheckpointE2b : cfg.box.defaultCheckpointDocker;
@@ -1742,25 +1982,34 @@ function stampSchema(doc) {
1742
1982
  }
1743
1983
  }
1744
1984
  function setLeaf(doc, key, value) {
1745
- const idx = key.indexOf(".");
1746
- const branch = key.slice(0, idx);
1747
- const leaf = key.slice(idx + 1);
1748
- const root = doc;
1749
- if (!root[branch] || typeof root[branch] !== "object") {
1750
- root[branch] = {};
1985
+ const segs = key.split(".");
1986
+ let cur = doc;
1987
+ for (let i = 0; i < segs.length - 1; i++) {
1988
+ const seg = segs[i];
1989
+ const next = cur[seg];
1990
+ if (!next || typeof next !== "object") {
1991
+ cur[seg] = {};
1992
+ }
1993
+ cur = cur[seg];
1751
1994
  }
1752
- root[branch][leaf] = value;
1995
+ cur[segs[segs.length - 1]] = value;
1753
1996
  }
1754
1997
  function unsetLeaf(doc, key) {
1755
- const idx = key.indexOf(".");
1756
- const branch = key.slice(0, idx);
1757
- const leaf = key.slice(idx + 1);
1758
- const root = doc;
1759
- const b = root[branch];
1760
- if (!b || typeof b !== "object" || !(leaf in b)) return false;
1761
- delete b[leaf];
1762
- if (Object.keys(b).length === 0) {
1763
- delete root[branch];
1998
+ const segs = key.split(".");
1999
+ const path = [doc];
2000
+ for (let i = 0; i < segs.length - 1; i++) {
2001
+ const seg = segs[i];
2002
+ const next = path[path.length - 1][seg];
2003
+ if (!next || typeof next !== "object") return false;
2004
+ path.push(next);
2005
+ }
2006
+ const leafSeg = segs[segs.length - 1];
2007
+ const leafContainer = path[path.length - 1];
2008
+ if (!(leafSeg in leafContainer)) return false;
2009
+ delete leafContainer[leafSeg];
2010
+ for (let i = path.length - 1; i > 0; i--) {
2011
+ if (Object.keys(path[i]).length > 0) break;
2012
+ delete path[i - 1][segs[i - 1]];
1764
2013
  }
1765
2014
  return true;
1766
2015
  }
@@ -1803,17 +2052,17 @@ import { execa as execa2 } from "execa";
1803
2052
  import { mkdir as mkdir4, readFile as readFile5, readdir as readdir4, stat as stat4 } from "fs/promises";
1804
2053
  import { createHash as createHash3 } from "crypto";
1805
2054
  import { homedir as homedir2 } from "os";
1806
- import { join as join5 } from "path";
2055
+ import { basename as basename2, dirname as dirname22, join as join5 } from "path";
1807
2056
  import { execa as execa3 } from "execa";
1808
2057
  import { existsSync, mkdirSync, renameSync, statSync } from "fs";
1809
- import { basename as basename2, dirname as dirname3, posix, resolve as resolve2 } from "path";
2058
+ import { basename as basename3, dirname as dirname3, posix, resolve as resolve2 } from "path";
1810
2059
  import { execa as execa22 } from "execa";
1811
2060
  import { copyFile, mkdtemp, readdir as readdir22, readFile as readFile32, rm as rm3, stat as stat22, writeFile as writeFile3 } from "fs/promises";
1812
2061
  import { homedir as homedir22, tmpdir } from "os";
1813
- import { basename as basename3, join as join32, relative } from "path";
2062
+ import { basename as basename4, join as join32, relative } from "path";
1814
2063
  import { execa as execa5 } from "execa";
1815
2064
  import { chmod, mkdir as mkdir22, readFile as readFile23 } from "fs/promises";
1816
- import { basename as basename22, join as join22 } from "path";
2065
+ import { basename as basename32, join as join22 } from "path";
1817
2066
  import { execa as execa4 } from "execa";
1818
2067
  import { spawnSync as spawnSync2 } from "child_process";
1819
2068
  import { stat as stat42 } from "fs/promises";
@@ -1825,7 +2074,7 @@ import { stat as stat5 } from "fs/promises";
1825
2074
  import { homedir as homedir5 } from "os";
1826
2075
  import { join as join6 } from "path";
1827
2076
  import { execa as execa8 } from "execa";
1828
- import { randomBytes as randomBytes3 } from "crypto";
2077
+ import { randomBytes as randomBytes2 } from "crypto";
1829
2078
  import { createHash as createHash22 } from "crypto";
1830
2079
  import { readFile as readFile52 } from "fs/promises";
1831
2080
  import { join as join7 } from "path";
@@ -1835,77 +2084,13 @@ import { readFile as readFile6 } from "fs/promises";
1835
2084
  import { homedir as homedir6 } from "os";
1836
2085
  import { join as join8 } from "path";
1837
2086
  import { execa as execa10 } from "execa";
1838
-
1839
- // ../../packages/core/dist/index.js
1840
- import { randomBytes } from "crypto";
1841
- var claudeCodeLauncher = {
1842
- kind: "claude-code",
1843
- // claude treats its first positional argument as the seed user turn in
1844
- // interactive mode (`claude "<message>"`), so we slot the initial message
1845
- // ahead of any user-passed flags.
1846
- buildArgs(initialMessage, userArgs) {
1847
- if (!initialMessage) return [...userArgs];
1848
- return [initialMessage, ...userArgs];
1849
- }
1850
- };
1851
- var codexLauncher = {
1852
- kind: "codex",
1853
- buildArgs(initialMessage, userArgs) {
1854
- if (!initialMessage) return [...userArgs];
1855
- return [initialMessage, ...userArgs];
1856
- }
1857
- };
1858
- var opencodeLauncher = {
1859
- kind: "opencode",
1860
- buildArgs(initialMessage, userArgs) {
1861
- if (!initialMessage) return [...userArgs];
1862
- return [initialMessage, ...userArgs];
1863
- }
1864
- };
1865
- function resolveAgentLauncher(kind) {
1866
- if (kind === "claude-code") return claudeCodeLauncher;
1867
- if (kind === "codex") return codexLauncher;
1868
- if (kind === "opencode") return opencodeLauncher;
1869
- throw new Error(`unknown agent kind: ${String(kind)}`);
1870
- }
1871
- var UserFacingError = class extends Error {
1872
- constructor(message) {
1873
- super(message);
1874
- this.name = "UserFacingError";
1875
- }
1876
- };
1877
- var BoxNotFoundError = class extends Error {
1878
- constructor(query) {
1879
- super(`no agentbox matches "${query}"`);
1880
- this.query = query;
1881
- this.name = "BoxNotFoundError";
1882
- }
1883
- query;
1884
- };
1885
- var AmbiguousBoxError = class extends Error {
1886
- constructor(query, matches) {
1887
- const ids = matches.map((m) => m.id).join(", ");
1888
- super(`"${query}" matches multiple boxes: ${ids}`);
1889
- this.query = query;
1890
- this.matches = matches;
1891
- this.name = "AmbiguousBoxError";
1892
- }
1893
- query;
1894
- matches;
1895
- };
1896
- var BOX_ID_PREFIX = "b";
1897
- function generateBoxId() {
1898
- return `${BOX_ID_PREFIX}${randomBytes(4).toString("hex")}`;
1899
- }
1900
-
1901
- // ../../packages/sandbox-docker/dist/index.js
1902
2087
  import { execa as execa11 } from "execa";
1903
2088
  import { mkdir as mkdir42, readdir as readdir42, rm as rm32, stat as stat6 } from "fs/promises";
1904
2089
  import { homedir as homedir7, platform } from "os";
1905
2090
  import { join as join9, resolve as resolve22 } from "path";
1906
2091
  import { mkdir as mkdir5, mkdtemp as mkdtemp3, readFile as readFile7, readdir as readdir5, rm as rm4, writeFile as writeFile32 } from "fs/promises";
1907
2092
  import { homedir as homedir8, tmpdir as tmpdir3 } from "os";
1908
- import { basename as basename4, join as join10 } from "path";
2093
+ import { basename as basename5, join as join10 } from "path";
1909
2094
  import { execa as execa12 } from "execa";
1910
2095
  import { stat as stat7 } from "fs/promises";
1911
2096
  import { execa as execa13 } from "execa";
@@ -1917,14 +2102,286 @@ import { cp, mkdir as mkdir6, readFile as readFile8, readdir as readdir6, rename
1917
2102
  import { request as httpRequest } from "http";
1918
2103
  import { createRequire } from "module";
1919
2104
  import { homedir as homedir9 } from "os";
1920
- import { dirname as dirname22, join as join11, resolve as resolve3, sep } from "path";
2105
+ import { dirname as dirname32, join as join11, resolve as resolve3, sep } from "path";
1921
2106
  import { setTimeout as delay2 } from "timers/promises";
1922
2107
  import { fileURLToPath } from "url";
1923
2108
 
1924
2109
  // ../../packages/relay/dist/index.js
1925
- import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
2110
+ import { createHash as createHash2, randomBytes } from "crypto";
1926
2111
  import { execa } from "execa";
1927
- import { spawn as spawn3 } from "child_process";
2112
+
2113
+ // ../../packages/integrations/dist/index.js
2114
+ var linearConnector = {
2115
+ service: "linear",
2116
+ hostBin: "linear",
2117
+ detect: {
2118
+ versionArgs: ["--version"],
2119
+ authArgs: ["auth", "whoami"],
2120
+ installHint: "install @schpet/linear-cli: npm i -g @schpet/linear-cli",
2121
+ loginHint: "linear auth login"
2122
+ },
2123
+ ops: {
2124
+ whoami: {
2125
+ write: false,
2126
+ buildArgv: (args) => ["auth", "whoami", ...args]
2127
+ },
2128
+ "issue.list": {
2129
+ write: false,
2130
+ buildArgv: (args) => ["issue", "list", ...args]
2131
+ },
2132
+ "issue.mine": {
2133
+ // The v2-native read for "issues assigned to me" — the README directs
2134
+ // users here in place of the older `issue list --me`. Listed as a
2135
+ // separate op so the shim doesn't reject the canonical form.
2136
+ write: false,
2137
+ buildArgv: (args) => ["issue", "mine", ...args]
2138
+ },
2139
+ "issue.view": {
2140
+ write: false,
2141
+ buildArgv: (args) => ["issue", "view", ...args]
2142
+ },
2143
+ "issue.query": {
2144
+ write: false,
2145
+ buildArgv: (args) => ["issue", "query", ...args]
2146
+ },
2147
+ "team.list": {
2148
+ write: false,
2149
+ buildArgv: (args) => ["team", "list", ...args]
2150
+ },
2151
+ api: {
2152
+ write: false,
2153
+ buildArgv: (args) => ["api", ...args],
2154
+ refuseCall: refuseGraphqlNonQuery
2155
+ },
2156
+ "issue.create": {
2157
+ write: true,
2158
+ buildArgv: (args) => ["issue", "create", ...args]
2159
+ },
2160
+ "issue.update": {
2161
+ write: true,
2162
+ buildArgv: (args) => ["issue", "update", ...args]
2163
+ },
2164
+ "issue.comment": {
2165
+ // Maps to `linear issue comment add` — `@schpet/linear-cli` v2 uses
2166
+ // `add` (not `create`); `add`'s sibling subcommands are `list`,
2167
+ // `update`, `delete`.
2168
+ write: true,
2169
+ buildArgv: (args) => ["issue", "comment", "add", ...args]
2170
+ }
2171
+ }
2172
+ };
2173
+ function refuseGraphqlNonQuery(args) {
2174
+ const refuse = (reason) => ({
2175
+ exitCode: 65,
2176
+ stderr: `linear api: ${reason}
2177
+ `
2178
+ });
2179
+ for (let i = 0; i < args.length; i++) {
2180
+ const arg = args[i] ?? "";
2181
+ if (arg === "--input" || arg.startsWith("--input=")) {
2182
+ return refuse("'--input' (stdin/file body) isn't supported through the relay");
2183
+ }
2184
+ if (arg === "--variable") {
2185
+ const next = args[i + 1] ?? "";
2186
+ if (variableValueIsFileLoad(next)) {
2187
+ return refuse(
2188
+ "'--variable key=@<path>' (host-file load) isn't supported through the relay"
2189
+ );
2190
+ }
2191
+ if (!next.startsWith("--")) i++;
2192
+ continue;
2193
+ }
2194
+ if (arg.startsWith("--variable=")) {
2195
+ if (variableValueIsFileLoad(arg.slice("--variable=".length))) {
2196
+ return refuse(
2197
+ "'--variable=key=@<path>' (host-file load) isn't supported through the relay"
2198
+ );
2199
+ }
2200
+ continue;
2201
+ }
2202
+ if (arg === "--variables-json") {
2203
+ const next = args[i + 1] ?? "";
2204
+ if (!next.startsWith("--")) i++;
2205
+ continue;
2206
+ }
2207
+ if (arg.startsWith("--variables-json=")) {
2208
+ continue;
2209
+ }
2210
+ if (arg.startsWith("--")) continue;
2211
+ const op = firstGraphqlOperationKeyword(arg);
2212
+ if (op === "mutation" || op === "subscription") {
2213
+ return refuse(
2214
+ `only GraphQL queries are proxied (use issue.create / issue.update / issue.comment for writes); detected operation '${op}'`
2215
+ );
2216
+ }
2217
+ if (op === "unparseable") {
2218
+ return refuse(
2219
+ `couldn't classify positional argv ${JSON.stringify(arg)} as a GraphQL operation (expected 'query', 'mutation', 'subscription', or '{')`
2220
+ );
2221
+ }
2222
+ }
2223
+ return null;
2224
+ }
2225
+ function variableValueIsFileLoad(value) {
2226
+ if (value.startsWith("@")) return true;
2227
+ return value.includes("=@");
2228
+ }
2229
+ function firstGraphqlOperationKeyword(source) {
2230
+ let i = 0;
2231
+ const n = source.length;
2232
+ while (i < n) {
2233
+ const c = source[i];
2234
+ if (/\s/.test(c) || c === "," || c === "\uFEFF") {
2235
+ i++;
2236
+ continue;
2237
+ }
2238
+ if (c === "#") {
2239
+ while (i < n && source[i] !== "\n") i++;
2240
+ continue;
2241
+ }
2242
+ break;
2243
+ }
2244
+ if (i >= n) return null;
2245
+ if (source[i] === "{") return "anonymous";
2246
+ let j = i;
2247
+ while (j < n) {
2248
+ const c = source[j];
2249
+ if (c >= "a" && c <= "z" || c >= "A" && c <= "Z") {
2250
+ j++;
2251
+ } else {
2252
+ break;
2253
+ }
2254
+ }
2255
+ if (j === i) return "unparseable";
2256
+ return source.slice(i, j).toLowerCase();
2257
+ }
2258
+ var notionConnector = {
2259
+ service: "notion",
2260
+ hostBin: "ntn",
2261
+ detect: {
2262
+ versionArgs: ["--version"],
2263
+ authArgs: ["api", "v1/users/me"],
2264
+ installHint: "install ntn: https://developers.notion.com/reference/notion-cli",
2265
+ loginHint: "ntn login"
2266
+ },
2267
+ ops: {
2268
+ whoami: {
2269
+ write: false,
2270
+ buildArgv: (args) => ["whoami", ...args]
2271
+ },
2272
+ api: {
2273
+ write: false,
2274
+ buildArgv: (args) => ["api", ...args],
2275
+ refuseCall: refuseUnsafeApiCall
2276
+ },
2277
+ "page.create": {
2278
+ write: true,
2279
+ buildArgv: (args) => ["pages", "create", ...args]
2280
+ },
2281
+ "page.update": {
2282
+ write: true,
2283
+ buildArgv: (args) => ["pages", "update", ...args]
2284
+ }
2285
+ }
2286
+ };
2287
+ var READ_POST_ENDPOINTS = [
2288
+ /^\/?v1\/search$/,
2289
+ /^\/?v1\/databases\/[^/?]+\/query$/,
2290
+ /^\/?v1\/data_sources\/[^/?]+\/query$/
2291
+ ];
2292
+ var SAFE_API_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2293
+ "--spec",
2294
+ "--docs",
2295
+ "-h",
2296
+ "--help",
2297
+ "-v",
2298
+ "--verbose",
2299
+ "-V",
2300
+ "--version"
2301
+ ]);
2302
+ function refuseUnsafeApiCall(args) {
2303
+ const refuse = (reason) => ({
2304
+ exitCode: 65,
2305
+ stderr: `notion api: ${reason}
2306
+ `
2307
+ });
2308
+ let explicitMethod = null;
2309
+ let hasBody = false;
2310
+ let endpoint = null;
2311
+ for (let i = 0; i < args.length; i++) {
2312
+ const arg = args[i] ?? "";
2313
+ if (arg === "-X" || arg === "--method") {
2314
+ explicitMethod = args[i + 1] ?? "";
2315
+ i++;
2316
+ continue;
2317
+ }
2318
+ if (arg.startsWith("--method=")) {
2319
+ explicitMethod = arg.slice("--method=".length);
2320
+ continue;
2321
+ }
2322
+ if (arg.startsWith("-X") && arg.length > 2) {
2323
+ explicitMethod = arg.slice(2).replace(/^=/, "");
2324
+ continue;
2325
+ }
2326
+ if (arg === "-d" || arg === "--data") {
2327
+ hasBody = true;
2328
+ i++;
2329
+ continue;
2330
+ }
2331
+ if (arg.startsWith("--data=") || arg.startsWith("-d") && arg.length > 2) {
2332
+ hasBody = true;
2333
+ continue;
2334
+ }
2335
+ if (arg === "--file" || arg.startsWith("--file=")) {
2336
+ return refuse("'--file' (host-file upload) isn't supported through the relay");
2337
+ }
2338
+ if (arg === "--input" || arg.startsWith("--input=")) {
2339
+ return refuse("'--input' (stdin/file body) isn't supported through the relay; use -d <JSON>");
2340
+ }
2341
+ if (arg === "--notion-version") {
2342
+ i++;
2343
+ continue;
2344
+ }
2345
+ if (arg.startsWith("--notion-version=")) continue;
2346
+ if (arg.startsWith("-")) {
2347
+ if (SAFE_API_BOOLEAN_FLAGS.has(arg)) continue;
2348
+ return refuse(
2349
+ `unsupported option '${arg}' (read-only api accepts a path, inline inputs, -d <JSON>, -X GET/POST, --notion-version, --spec, --docs)`
2350
+ );
2351
+ }
2352
+ if (endpoint === null) {
2353
+ endpoint = arg;
2354
+ continue;
2355
+ }
2356
+ if (isBodyAssignment(arg)) hasBody = true;
2357
+ }
2358
+ const method = (explicitMethod && explicitMethod.length > 0 ? explicitMethod : hasBody ? "POST" : "GET").toUpperCase();
2359
+ if (method === "GET") return null;
2360
+ if (method !== "POST") {
2361
+ return refuse(
2362
+ `only GET and read-only POST are proxied (use page.create / page.update for writes); detected method '${method}'`
2363
+ );
2364
+ }
2365
+ if (endpoint === null) {
2366
+ return refuse("could not determine the API endpoint for a non-GET call");
2367
+ }
2368
+ if (endpoint.split("/").some((seg) => seg === "." || seg === "..")) {
2369
+ return refuse(`path traversal segments ('.' / '..') are not allowed in '${endpoint}'`);
2370
+ }
2371
+ if (READ_POST_ENDPOINTS.some((re) => re.test(endpoint))) return null;
2372
+ return refuse(
2373
+ `POST is only proxied for read endpoints (v1/search, v1/databases/{id}/query, v1/data_sources/{id}/query); '${endpoint}' is not one (use page.create / page.update for writes)`
2374
+ );
2375
+ }
2376
+ function isBodyAssignment(token) {
2377
+ if (token.includes(":=")) return true;
2378
+ if (token.includes("==")) return false;
2379
+ return token.includes("=");
2380
+ }
2381
+ var ALL_CONNECTORS = [notionConnector, linearConnector];
2382
+
2383
+ // ../../packages/relay/dist/index.js
2384
+ import { spawn as spawn4 } from "child_process";
1928
2385
  import {
1929
2386
  mkdir as mkdir2,
1930
2387
  readdir as readdir2,
@@ -2114,7 +2571,7 @@ async function uncachedBoxStateCount() {
2114
2571
  }
2115
2572
  function inspectDockerState(containerName) {
2116
2573
  return new Promise((resolveP) => {
2117
- const child = spawn3("docker", ["inspect", "--format", "{{.State.Status}}", containerName], {
2574
+ const child = spawn4("docker", ["inspect", "--format", "{{.State.Status}}", containerName], {
2118
2575
  stdio: ["ignore", "pipe", "pipe"]
2119
2576
  });
2120
2577
  let out = "";
@@ -2158,7 +2615,7 @@ import { execa as execa18 } from "execa";
2158
2615
  import { createHash as createHash32 } from "crypto";
2159
2616
  import { copyFile as copyFile2, mkdir as mkdir8, mkdtemp as mkdtemp4, readdir as readdir8, readFile as readFile9, rm as rm7, stat as stat10 } from "fs/promises";
2160
2617
  import { homedir as homedir12, tmpdir as tmpdir4 } from "os";
2161
- import { basename as basename6, dirname as dirname32, join as join16 } from "path";
2618
+ import { basename as basename7, dirname as dirname4, join as join16 } from "path";
2162
2619
  import { execa as execa19 } from "execa";
2163
2620
  function isHostPathHookCommand(command, hostHome) {
2164
2621
  if (typeof command !== "string" || command.length === 0) return false;
@@ -2285,16 +2742,16 @@ function rewritePluginPaths(value, hostPluginsPrefix) {
2285
2742
  }
2286
2743
  return value;
2287
2744
  }
2288
- function isPlainObject4(v) {
2745
+ function isPlainObject5(v) {
2289
2746
  return v !== null && typeof v === "object" && !Array.isArray(v);
2290
2747
  }
2291
2748
  function additiveMerge(hostRoot, boxRoot, hostPluginsPrefix, selectMap, withMap) {
2292
2749
  const hostMap = selectMap(hostRoot);
2293
2750
  const boxMap = selectMap(boxRoot);
2294
- if (!isPlainObject4(boxMap)) {
2751
+ if (!isPlainObject5(boxMap)) {
2295
2752
  return { data: hostRoot, changed: false, addedKeys: [] };
2296
2753
  }
2297
- const base = isPlainObject4(hostMap) ? { ...hostMap } : {};
2754
+ const base = isPlainObject5(hostMap) ? { ...hostMap } : {};
2298
2755
  const addedKeys = [];
2299
2756
  for (const [key, value] of Object.entries(boxMap)) {
2300
2757
  if (Object.prototype.hasOwnProperty.call(base, key)) continue;
@@ -2309,7 +2766,7 @@ function additiveMerge(hostRoot, boxRoot, hostPluginsPrefix, selectMap, withMap)
2309
2766
  function mergeKnownMarketplaces(hostJson, boxJson, opts) {
2310
2767
  const prefix = `${opts.hostHome}/.claude/plugins/`;
2311
2768
  return additiveMerge(
2312
- isPlainObject4(hostJson) ? hostJson : {},
2769
+ isPlainObject5(hostJson) ? hostJson : {},
2313
2770
  boxJson,
2314
2771
  prefix,
2315
2772
  (root) => root,
@@ -2318,24 +2775,24 @@ function mergeKnownMarketplaces(hostJson, boxJson, opts) {
2318
2775
  }
2319
2776
  function mergeInstalledPlugins(hostJson, boxJson, opts) {
2320
2777
  const prefix = `${opts.hostHome}/.claude/plugins/`;
2321
- const hostRoot = isPlainObject4(hostJson) ? hostJson : { plugins: {} };
2778
+ const hostRoot = isPlainObject5(hostJson) ? hostJson : { plugins: {} };
2322
2779
  return additiveMerge(
2323
2780
  hostRoot,
2324
2781
  boxJson,
2325
2782
  prefix,
2326
- (root) => isPlainObject4(root) ? root["plugins"] : void 0,
2783
+ (root) => isPlainObject5(root) ? root["plugins"] : void 0,
2327
2784
  (host, merged) => ({ ...host, plugins: merged })
2328
2785
  );
2329
2786
  }
2330
2787
  function referencedPluginVersionKeys(installedPluginsJson) {
2331
2788
  const keys = /* @__PURE__ */ new Set();
2332
- if (!isPlainObject4(installedPluginsJson)) return keys;
2789
+ if (!isPlainObject5(installedPluginsJson)) return keys;
2333
2790
  const plugins = installedPluginsJson["plugins"];
2334
- if (!isPlainObject4(plugins)) return keys;
2791
+ if (!isPlainObject5(plugins)) return keys;
2335
2792
  for (const entries of Object.values(plugins)) {
2336
2793
  if (!Array.isArray(entries)) continue;
2337
2794
  for (const entry of entries) {
2338
- if (!isPlainObject4(entry)) continue;
2795
+ if (!isPlainObject5(entry)) continue;
2339
2796
  const installPath = entry["installPath"];
2340
2797
  if (typeof installPath !== "string") continue;
2341
2798
  const segments = installPath.split("/").filter((s) => s.length > 0);
@@ -2585,7 +3042,7 @@ async function streamTarPipe(producerFile, producerArgs, consumerFile, consumerA
2585
3042
  async function uploadToBox(box, hostSrc, boxDst, exclude) {
2586
3043
  const srcAbs = resolve2(hostSrc);
2587
3044
  if (!existsSync(srcAbs)) throw new Error(`source not found: ${hostSrc}`);
2588
- const srcBasename = basename2(srcAbs);
3045
+ const srcBasename = basename3(srcAbs);
2589
3046
  const srcParent = dirname3(srcAbs);
2590
3047
  let boxParent;
2591
3048
  let finalName;
@@ -2666,7 +3123,7 @@ async function downloadFromBox(box, boxSrc, hostDst, exclude) {
2666
3123
  finalName = srcBasename;
2667
3124
  } else {
2668
3125
  hostParent = dirname3(dstAbs);
2669
- finalName = basename2(dstAbs);
3126
+ finalName = basename3(dstAbs);
2670
3127
  }
2671
3128
  mkdirSync(hostParent, { recursive: true });
2672
3129
  const finalPath = posix.join(hostParent, finalName);
@@ -3145,13 +3602,44 @@ async function copyOneEntry(container, entry) {
3145
3602
  throw new Error(`mkdir -p ${parentDir} failed: ${String(mkdir9.stderr).slice(0, 300)}`);
3146
3603
  }
3147
3604
  if (entry.kind === "file") {
3148
- const cp2 = await execa3(
3605
+ const srcDir = dirname22(entry.absSrc);
3606
+ const srcBase = basename2(entry.absSrc);
3607
+ const destBase = boxDest.slice(boxDest.lastIndexOf("/") + 1);
3608
+ const [packed, extract] = await streamTarPipe(
3609
+ "tar",
3610
+ ["-C", srcDir, "-cf", "-", srcBase],
3149
3611
  "docker",
3150
- ["cp", entry.absSrc, `${container}:${boxDest}`],
3151
- { reject: false }
3612
+ [
3613
+ "exec",
3614
+ "-i",
3615
+ "--user",
3616
+ "0:0",
3617
+ container,
3618
+ "tar",
3619
+ "-xf",
3620
+ "-",
3621
+ "-C",
3622
+ parentDir,
3623
+ "--no-same-permissions",
3624
+ "--no-same-owner",
3625
+ "-m"
3626
+ ]
3152
3627
  );
3153
- if (cp2.exitCode !== 0) {
3154
- throw new Error(`docker cp failed: ${String(cp2.stderr).slice(0, 300)}`);
3628
+ if (packed.exitCode !== 0) {
3629
+ throw new Error(`tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
3630
+ }
3631
+ if (extract.exitCode !== 0) {
3632
+ throw new Error(`tar extract failed: ${String(extract.stderr).slice(0, 300)}`);
3633
+ }
3634
+ if (srcBase !== destBase) {
3635
+ const mv = await execa3(
3636
+ "docker",
3637
+ ["exec", "--user", "0:0", container, "mv", "-f", `${parentDir}/${srcBase}`, boxDest],
3638
+ { reject: false }
3639
+ );
3640
+ if (mv.exitCode !== 0) {
3641
+ throw new Error(`rename to ${boxDest} failed: ${String(mv.stderr).slice(0, 300)}`);
3642
+ }
3155
3643
  }
3156
3644
  } else {
3157
3645
  const excludeArgs = (entry.exclude ?? []).map((p) => `--exclude=${p}`);
@@ -3265,7 +3753,7 @@ async function extractVolumeAuthToBackup(opts) {
3265
3753
  "-e",
3266
3754
  // Pass the destination filename via env so the path isn't interpolated
3267
3755
  // into the script string (keeps the docker arg list static + injection-safe).
3268
- `DEST=${basename22(opts.backupFile)}`,
3756
+ `DEST=${basename32(opts.backupFile)}`,
3269
3757
  opts.image,
3270
3758
  "sh",
3271
3759
  "-c",
@@ -3385,7 +3873,7 @@ function emptyResult(warnings = []) {
3385
3873
  }, warnings };
3386
3874
  }
3387
3875
  async function tarballFromDir(stageDir, agent) {
3388
- const tarballPath = join32(tmpdir(), `agentbox-${agent}-${basename3(stageDir)}.tar.gz`);
3876
+ const tarballPath = join32(tmpdir(), `agentbox-${agent}-${basename4(stageDir)}.tar.gz`);
3389
3877
  await execa5("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
3390
3878
  env: { ...process.env, COPYFILE_DISABLE: "1" }
3391
3879
  });
@@ -4262,14 +4750,14 @@ var ClaudeSessionError = class extends Error {
4262
4750
  this.name = "ClaudeSessionError";
4263
4751
  }
4264
4752
  };
4265
- function shQuote(arg) {
4753
+ function shQuote2(arg) {
4266
4754
  if (arg.length === 0) return `''`;
4267
4755
  if (/^[A-Za-z0-9_\-./=:@%+,]+$/.test(arg)) return arg;
4268
4756
  return `'${arg.replace(/'/g, `'\\''`)}'`;
4269
4757
  }
4270
4758
  async function startClaudeSession(opts) {
4271
4759
  const sessionName = opts.sessionName ?? DEFAULT_CLAUDE_SESSION;
4272
- const cmd = ["claude", ...opts.claudeArgs].map(shQuote).join(" ");
4760
+ const cmd = ["claude", ...opts.claudeArgs].map(shQuote2).join(" ");
4273
4761
  const term = process.env["TERM"] ?? "xterm-256color";
4274
4762
  const envFlags = ["-e", `TERM=${term}`];
4275
4763
  for (const k of FORWARDED_ENV_KEYS) {
@@ -4685,7 +5173,7 @@ async function pathExists3(p) {
4685
5173
  return false;
4686
5174
  }
4687
5175
  }
4688
- function shQuote2(arg) {
5176
+ function shQuote22(arg) {
4689
5177
  if (arg.length === 0) return `''`;
4690
5178
  if (/^[A-Za-z0-9_\-./=:@%+,]+$/.test(arg)) return arg;
4691
5179
  return `'${arg.replace(/'/g, `'\\''`)}'`;
@@ -4795,7 +5283,7 @@ ${(install.stdout ?? "").toString().slice(-600)}`
4795
5283
  var CODEX_AGENTBOX_FLAGS = ["--enable", "hooks", "--dangerously-bypass-hook-trust"];
4796
5284
  async function startCodexSession(opts) {
4797
5285
  const sessionName = opts.sessionName ?? DEFAULT_CODEX_SESSION;
4798
- const cmd = ["codex", ...CODEX_AGENTBOX_FLAGS, ...opts.codexArgs].map(shQuote2).join(" ");
5286
+ const cmd = ["codex", ...CODEX_AGENTBOX_FLAGS, ...opts.codexArgs].map(shQuote22).join(" ");
4799
5287
  const term = process.env["TERM"] ?? "xterm-256color";
4800
5288
  const envFlags = ["-e", `TERM=${term}`];
4801
5289
  for (const k of CODEX_FORWARDED_ENV_KEYS) {
@@ -5403,7 +5891,7 @@ async function launchVncDaemon(container, timeoutMs = 5e3) {
5403
5891
  }
5404
5892
  var VNC_PASSWORD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
5405
5893
  function generateVncPassword() {
5406
- const bytes = randomBytes3(8);
5894
+ const bytes = randomBytes2(8);
5407
5895
  let out = "";
5408
5896
  for (let i = 0; i < 8; i++) {
5409
5897
  out += VNC_PASSWORD_ALPHABET[bytes[i] % VNC_PASSWORD_ALPHABET.length];
@@ -6106,7 +6594,7 @@ async function createSnapshot(opts) {
6106
6594
  var CHECKPOINTS_ROOT = join10(homedir8(), ".agentbox", "checkpoints");
6107
6595
  var CHECKPOINT_IMAGE_PREFIX = "agentbox-ckpt-";
6108
6596
  function checkpointImageTag(projectRoot, name) {
6109
- const mnemonic = sanitizeMnemonic(basename4(projectRoot));
6597
+ const mnemonic = sanitizeMnemonic(basename5(projectRoot));
6110
6598
  return `${CHECKPOINT_IMAGE_PREFIX}${hashProjectPath(projectRoot)}_${mnemonic}:${name}`;
6111
6599
  }
6112
6600
  function projectCheckpointsDir(projectRoot) {
@@ -6603,7 +7091,7 @@ async function spawnRelay(relayBin, cliEntry, log) {
6603
7091
  function resolveRelayBin() {
6604
7092
  const override = process.env.AGENTBOX_RELAY_BIN;
6605
7093
  if (override && existsSync3(override)) return override;
6606
- const here = dirname22(fileURLToPath(import.meta.url));
7094
+ const here = dirname32(fileURLToPath(import.meta.url));
6607
7095
  const candidates = [
6608
7096
  resolve3(here, "..", "runtime", "relay", "bin.cjs"),
6609
7097
  resolve3(here, "..", "..", "relay", "dist", "bin.cjs"),
@@ -6621,7 +7109,7 @@ function resolveRelayBin() {
6621
7109
  function resolveCliEntry() {
6622
7110
  const override = process.env.AGENTBOX_CLI_ENTRY;
6623
7111
  if (override && existsSync3(override)) return override;
6624
- const here = dirname22(fileURLToPath(import.meta.url));
7112
+ const here = dirname32(fileURLToPath(import.meta.url));
6625
7113
  const candidates = [
6626
7114
  // Bundled CLI (dev + published): this module IS bundled into the CLI
6627
7115
  // entry, so the entry is index.js next to this file.
@@ -6637,7 +7125,7 @@ function resolveCliEntry() {
6637
7125
  async function stageRelayHome(version, log) {
6638
7126
  if (!version || version === "0.0.0-dev") return null;
6639
7127
  if (process.env.AGENTBOX_RELAY_BIN || process.env.AGENTBOX_CLI_ENTRY) return null;
6640
- const cliRoot = findCliRoot(dirname22(fileURLToPath(import.meta.url)));
7128
+ const cliRoot = findCliRoot(dirname32(fileURLToPath(import.meta.url)));
6641
7129
  if (cliRoot === null) return null;
6642
7130
  const homeDir = join11(RELAY_HOME_DIR, version);
6643
7131
  const stagedEntry = join11(homeDir, "dist", "index.js");
@@ -7145,7 +7633,7 @@ function persistableLimits(lim) {
7145
7633
  return Object.keys(out).length > 0 ? out : void 0;
7146
7634
  }
7147
7635
  function sanitizeBasename(workspacePath) {
7148
- const raw = basename5(resolve4(workspacePath));
7636
+ const raw = basename6(resolve4(workspacePath));
7149
7637
  return raw.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "").slice(0, 30).replace(/[-._]+$/, "");
7150
7638
  }
7151
7639
  function defaultBoxName(workspacePath, id) {
@@ -7716,9 +8204,14 @@ async function createBox(opts) {
7716
8204
  let carrySummary;
7717
8205
  if (opts.carry && opts.carry.length > 0) {
7718
8206
  log(`carry: copying ${String(opts.carry.length)} host path(s) into the box`);
8207
+ const entries = await renderCarryEntries(
8208
+ opts.carry,
8209
+ { name, id, kind: "docker", hostWorkspace: workspace, projectRoot: opts.projectRoot },
8210
+ log
8211
+ );
7719
8212
  const result = await copyCarryPathsToBox({
7720
8213
  container: containerName,
7721
- entries: opts.carry,
8214
+ entries,
7722
8215
  onLog: log
7723
8216
  });
7724
8217
  log(`carry: copied ${String(result.copied)}/${String(opts.carry.length)} entry/entries`);
@@ -8955,10 +9448,10 @@ async function stageDynamicSyncTarball(uploads) {
8955
9448
  try {
8956
9449
  for (const up of uploads) {
8957
9450
  const target = join16(stageDir, up.set, up.rel);
8958
- await mkdir8(dirname32(target), { recursive: true });
9451
+ await mkdir8(dirname4(target), { recursive: true });
8959
9452
  await copyFile2(up.absSrc, target);
8960
9453
  }
8961
- tarballPath = join16(tmpdir4(), `agentbox-dynsync-${basename6(stageDir)}.tar.gz`);
9454
+ tarballPath = join16(tmpdir4(), `agentbox-dynsync-${basename7(stageDir)}.tar.gz`);
8962
9455
  await execa19("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
8963
9456
  env: { ...process.env, COPYFILE_DISABLE: "1" }
8964
9457
  });
@@ -9023,12 +9516,10 @@ export {
9023
9516
  renderStatusTable,
9024
9517
  renderTaskTable,
9025
9518
  renderPortsTable,
9519
+ parseReplacementsSection,
9520
+ parseCarrySection,
9026
9521
  loadCarrySection,
9027
- resolveAgentLauncher,
9028
- UserFacingError,
9029
- BoxNotFoundError,
9030
- AmbiguousBoxError,
9031
- generateBoxId,
9522
+ ALL_CONNECTORS,
9032
9523
  DEFAULT_RELAY_PORT,
9033
9524
  RELAY_CONTAINER_NAME,
9034
9525
  RELAY_NETWORK_NAME,
@@ -9258,4 +9749,4 @@ export {
9258
9749
  browserSessionActive,
9259
9750
  ensureBoxBrowser
9260
9751
  };
9261
- //# sourceMappingURL=chunk-MLMFNN4T.js.map
9752
+ //# sourceMappingURL=chunk-GXJNJUEV.js.map