@madarco/agentbox 0.15.0 → 0.17.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 +68 -0
- package/dist/{_cloud-attach-R6TRWG5L.js → _cloud-attach-SGNR6BXP.js} +5 -4
- package/dist/{chunk-BKU34KYY.js → chunk-3JXSW6PN.js} +42 -24
- package/dist/chunk-3JXSW6PN.js.map +1 -0
- package/dist/{chunk-RSKG7AFU.js → chunk-46NXCJ6Z.js} +45 -34
- package/dist/{chunk-RSKG7AFU.js.map → chunk-46NXCJ6Z.js.map} +1 -1
- package/dist/{chunk-43Q5GWP6.js → chunk-5AKAC27L.js} +7 -7
- package/dist/{chunk-XKH7NTT7.js → chunk-6QFPYU4Z.js} +248 -5
- package/dist/chunk-6QFPYU4Z.js.map +1 -0
- package/dist/{chunk-MLMFNN4T.js → chunk-HFQZJO73.js} +688 -197
- package/dist/chunk-HFQZJO73.js.map +1 -0
- package/dist/{chunk-E7CHS7ZR.js → chunk-KTDJ5JTE.js} +18 -6
- package/dist/chunk-KTDJ5JTE.js.map +1 -0
- package/dist/{chunk-72CJTXN6.js → chunk-RXPV3OIX.js} +80 -72
- package/dist/chunk-RXPV3OIX.js.map +1 -0
- package/dist/chunk-WJFZJZIM.js +24 -0
- package/dist/{chunk-MCOU6CZS.js → chunk-XWBFWUY2.js} +30 -22
- package/dist/chunk-XWBFWUY2.js.map +1 -0
- package/dist/{cloud-poller-SUNA6ZQC-2RG5WPRN.js → cloud-poller-SUNA6ZQC-7M5LJHHE.js} +2 -1
- package/dist/{dist-S4XR4ACV.js → dist-433ASGVG.js} +6 -5
- package/dist/{dist-S4XR4ACV.js.map → dist-433ASGVG.js.map} +1 -1
- package/dist/{dist-JZ3XO6EB.js → dist-BO2R55FX.js} +6 -5
- package/dist/{dist-JZ3XO6EB.js.map → dist-BO2R55FX.js.map} +1 -1
- package/dist/{dist-AGTIA7AD.js → dist-CNABE32V.js} +7 -6
- package/dist/{dist-AGTIA7AD.js.map → dist-CNABE32V.js.map} +1 -1
- package/dist/{dist-FIFEFKJ7.js → dist-TEKY3GFT.js} +6 -5
- package/dist/{dist-FIFEFKJ7.js.map → dist-TEKY3GFT.js.map} +1 -1
- package/dist/{dist-OGJGZETZ.js → dist-VAATGBAR.js} +6 -5
- package/dist/index.js +2426 -2094
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js → prepared-state-MQHD3M5F-OVABNV66.js} +3 -2
- package/dist/prepared-state-MQHD3M5F-OVABNV66.js.map +1 -0
- package/package.json +5 -4
- package/runtime/docker/Dockerfile.box +21 -2
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +82 -29
- package/runtime/docker/packages/ctl/dist/bin.cjs +10675 -9191
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup +5 -2
- package/runtime/docker/packages/sandbox-docker/scripts/linear-shim +181 -0
- package/runtime/docker/packages/sandbox-docker/scripts/ntn-shim +95 -0
- package/runtime/e2b/agentbox-checkpoint-cleanup +5 -2
- package/runtime/e2b/agentbox-setup-skill.md +82 -29
- package/runtime/e2b/ctl.cjs +10675 -9191
- package/runtime/e2b/linear-shim +181 -0
- package/runtime/e2b/ntn-shim +95 -0
- package/runtime/e2b/scripts/build-template.sh +13 -7
- package/runtime/hetzner/agentbox-checkpoint-cleanup +5 -2
- package/runtime/hetzner/agentbox-setup-skill.md +82 -29
- package/runtime/hetzner/ctl.cjs +10675 -9191
- package/runtime/hetzner/linear-shim +181 -0
- package/runtime/hetzner/ntn-shim +95 -0
- package/runtime/hetzner/scripts/install-box.sh +19 -9
- package/runtime/relay/bin.cjs +3696 -2895
- package/runtime/vercel/agentbox-checkpoint-cleanup +5 -2
- package/runtime/vercel/agentbox-setup-skill.md +82 -29
- package/runtime/vercel/ctl.cjs +10675 -9191
- package/runtime/vercel/linear-shim +181 -0
- package/runtime/vercel/ntn-shim +95 -0
- package/runtime/vercel/scripts/provision.sh +13 -7
- package/share/agentbox-setup/SKILL.md +82 -29
- package/share/host-skills/agentbox-info/SKILL.md +1 -1
- package/dist/chunk-72CJTXN6.js.map +0 -1
- package/dist/chunk-BKU34KYY.js.map +0 -1
- package/dist/chunk-E7CHS7ZR.js.map +0 -1
- package/dist/chunk-MCOU6CZS.js.map +0 -1
- package/dist/chunk-MLMFNN4T.js.map +0 -1
- package/dist/chunk-XKH7NTT7.js.map +0 -1
- /package/dist/{_cloud-attach-R6TRWG5L.js.map → _cloud-attach-SGNR6BXP.js.map} +0 -0
- /package/dist/{chunk-43Q5GWP6.js.map → chunk-5AKAC27L.js.map} +0 -0
- /package/dist/{cloud-poller-SUNA6ZQC-2RG5WPRN.js.map → chunk-WJFZJZIM.js.map} +0 -0
- /package/dist/{dist-OGJGZETZ.js.map → cloud-poller-SUNA6ZQC-7M5LJHHE.js.map} +0 -0
- /package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js.map → dist-VAATGBAR.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-
|
|
29
|
+
} from "./chunk-6QFPYU4Z.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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 && !
|
|
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 && !
|
|
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
|
-
|
|
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
|
|
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([
|
|
521
|
-
|
|
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}
|
|
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}
|
|
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
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
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
|
-
|
|
1568
|
-
if (!
|
|
1569
|
-
|
|
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
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
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
|
-
|
|
1995
|
+
cur[segs[segs.length - 1]] = value;
|
|
1753
1996
|
}
|
|
1754
1997
|
function unsetLeaf(doc, key) {
|
|
1755
|
-
const
|
|
1756
|
-
const
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2110
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
1926
2111
|
import { execa } from "execa";
|
|
1927
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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 (!
|
|
2751
|
+
if (!isPlainObject5(boxMap)) {
|
|
2295
2752
|
return { data: hostRoot, changed: false, addedKeys: [] };
|
|
2296
2753
|
}
|
|
2297
|
-
const base =
|
|
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
|
-
|
|
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 =
|
|
2778
|
+
const hostRoot = isPlainObject5(hostJson) ? hostJson : { plugins: {} };
|
|
2322
2779
|
return additiveMerge(
|
|
2323
2780
|
hostRoot,
|
|
2324
2781
|
boxJson,
|
|
2325
2782
|
prefix,
|
|
2326
|
-
(root) =>
|
|
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 (!
|
|
2789
|
+
if (!isPlainObject5(installedPluginsJson)) return keys;
|
|
2333
2790
|
const plugins = installedPluginsJson["plugins"];
|
|
2334
|
-
if (!
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
[
|
|
3151
|
-
|
|
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 (
|
|
3154
|
-
throw new Error(`
|
|
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=${
|
|
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}-${
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
|
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(
|
|
9451
|
+
await mkdir8(dirname4(target), { recursive: true });
|
|
8959
9452
|
await copyFile2(up.absSrc, target);
|
|
8960
9453
|
}
|
|
8961
|
-
tarballPath = join16(tmpdir4(), `agentbox-dynsync-${
|
|
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
|
-
|
|
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-
|
|
9752
|
+
//# sourceMappingURL=chunk-HFQZJO73.js.map
|