@madarco/agentbox 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +108 -0
- package/dist/{_cloud-attach-GUBB5RH2.js → _cloud-attach-5KJWOASL.js} +4 -4
- package/dist/{chunk-RSKG7AFU.js → chunk-3WCEB6RE.js} +2 -2
- package/dist/{chunk-XKH7NTT7.js → chunk-DBBUDKKB.js} +248 -5
- package/dist/chunk-DBBUDKKB.js.map +1 -0
- package/dist/{chunk-TCS5HXJX.js → chunk-GXJNJUEV.js} +1090 -527
- package/dist/chunk-GXJNJUEV.js.map +1 -0
- package/dist/{chunk-LDMYHWUS.js → chunk-NW2UZQV6.js} +10 -6
- package/dist/chunk-NW2UZQV6.js.map +1 -0
- package/dist/{chunk-TBSIJVSN.js → chunk-PIK47622.js} +37 -17
- package/dist/chunk-PIK47622.js.map +1 -0
- package/dist/{chunk-BKU34KYY.js → chunk-QXFNLKJJ.js} +9 -3
- package/dist/{chunk-BKU34KYY.js.map → chunk-QXFNLKJJ.js.map} +1 -1
- package/dist/{chunk-BYCLD6D6.js → chunk-SB4QTF2T.js} +98 -54
- package/dist/chunk-SB4QTF2T.js.map +1 -0
- package/dist/{chunk-VATTS2MR.js → chunk-SENASAU4.js} +10 -6
- package/dist/{chunk-VATTS2MR.js.map → chunk-SENASAU4.js.map} +1 -1
- package/dist/{dist-34RKQ74M.js → dist-4IQFJJQI.js} +5 -5
- package/dist/{dist-4DPOL5A7.js → dist-7YB7BMNG.js} +5 -5
- package/dist/{dist-3IMQNTTV.js → dist-SL2QSMBE.js} +5 -5
- package/dist/{dist-J2IHD5T7.js → dist-VHI5QOSQ.js} +6 -6
- package/dist/{dist-57M6ZA7H.js → dist-XC47DSCR.js} +5 -5
- package/dist/index.js +1043 -333
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-MQHD3M5F-Q27AZU53.js → prepared-state-MQHD3M5F-2LANTRL7.js} +2 -2
- package/package.json +6 -5
- package/runtime/docker/Dockerfile.box +21 -2
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +112 -29
- package/runtime/docker/packages/ctl/dist/bin.cjs +10353 -8575
- 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 +112 -29
- package/runtime/e2b/ctl.cjs +10353 -8575
- 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 +112 -29
- package/runtime/hetzner/ctl.cjs +10353 -8575
- 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 +3707 -2828
- package/runtime/vercel/agentbox-checkpoint-cleanup +5 -2
- package/runtime/vercel/agentbox-setup-skill.md +112 -29
- package/runtime/vercel/ctl.cjs +10353 -8575
- 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 +112 -29
- package/share/host-skills/agentbox-info/SKILL.md +22 -2
- package/dist/chunk-BYCLD6D6.js.map +0 -1
- package/dist/chunk-LDMYHWUS.js.map +0 -1
- package/dist/chunk-TBSIJVSN.js.map +0 -1
- package/dist/chunk-TCS5HXJX.js.map +0 -1
- package/dist/chunk-XKH7NTT7.js.map +0 -1
- /package/dist/{_cloud-attach-GUBB5RH2.js.map → _cloud-attach-5KJWOASL.js.map} +0 -0
- /package/dist/{chunk-RSKG7AFU.js.map → chunk-3WCEB6RE.js.map} +0 -0
- /package/dist/{dist-34RKQ74M.js.map → dist-4IQFJJQI.js.map} +0 -0
- /package/dist/{dist-4DPOL5A7.js.map → dist-7YB7BMNG.js.map} +0 -0
- /package/dist/{dist-3IMQNTTV.js.map → dist-SL2QSMBE.js.map} +0 -0
- /package/dist/{dist-J2IHD5T7.js.map → dist-VHI5QOSQ.js.map} +0 -0
- /package/dist/{dist-57M6ZA7H.js.map → dist-XC47DSCR.js.map} +0 -0
- /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-
|
|
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
|
|
28
|
-
import { execa as
|
|
34
|
+
import { basename as basename6, join as join12, resolve as resolve4 } from "path";
|
|
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,7 +683,31 @@ var CarryConfigError = class extends Error {
|
|
|
517
683
|
this.name = "CarryConfigError";
|
|
518
684
|
}
|
|
519
685
|
};
|
|
520
|
-
var ITEM_KEYS = /* @__PURE__ */ new Set([
|
|
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) {
|
|
698
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
699
|
+
if (!Array.isArray(raw)) {
|
|
700
|
+
throw new CarryConfigError(`${where} must be a list of ${desc}`);
|
|
701
|
+
}
|
|
702
|
+
const out = [];
|
|
703
|
+
for (const [i, v] of raw.entries()) {
|
|
704
|
+
if (typeof v !== "string" || v.trim().length === 0) {
|
|
705
|
+
throw new CarryConfigError(`${where}[${String(i)}] must be a non-empty string`);
|
|
706
|
+
}
|
|
707
|
+
out.push(v.trim());
|
|
708
|
+
}
|
|
709
|
+
return out.length > 0 ? out : void 0;
|
|
710
|
+
}
|
|
521
711
|
function parseUser(raw, where) {
|
|
522
712
|
if (raw === void 0 || raw === null) return void 0;
|
|
523
713
|
let n;
|
|
@@ -542,7 +732,7 @@ function parseUser(raw, where) {
|
|
|
542
732
|
}
|
|
543
733
|
return n;
|
|
544
734
|
}
|
|
545
|
-
function
|
|
735
|
+
function isPlainObject3(v) {
|
|
546
736
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
547
737
|
}
|
|
548
738
|
function assertSrcShape(src, where) {
|
|
@@ -646,6 +836,7 @@ function parseMapping(raw, where) {
|
|
|
646
836
|
assertDestShape(dest, where);
|
|
647
837
|
const mode = parseMode(raw.mode, where);
|
|
648
838
|
const user = parseUser(raw.user, where);
|
|
839
|
+
const exclude = parseStringList(raw.exclude, `${where}.exclude`, "glob/name strings");
|
|
649
840
|
let optional = false;
|
|
650
841
|
if (raw.optional !== void 0 && raw.optional !== null) {
|
|
651
842
|
if (typeof raw.optional !== "boolean") {
|
|
@@ -653,9 +844,30 @@ function parseMapping(raw, where) {
|
|
|
653
844
|
}
|
|
654
845
|
optional = raw.optional;
|
|
655
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");
|
|
656
864
|
const out = { src, dest, optional };
|
|
657
865
|
if (mode !== void 0) out.mode = mode;
|
|
658
866
|
if (user !== void 0) out.user = user;
|
|
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;
|
|
659
871
|
return out;
|
|
660
872
|
}
|
|
661
873
|
function parseCarryRaw(raw) {
|
|
@@ -668,7 +880,7 @@ function parseCarryRaw(raw) {
|
|
|
668
880
|
const where = `carry[${String(i)}]`;
|
|
669
881
|
if (typeof item === "string") {
|
|
670
882
|
out.push(parseShorthand(item, where));
|
|
671
|
-
} else if (
|
|
883
|
+
} else if (isPlainObject3(item)) {
|
|
672
884
|
out.push(parseMapping(item, where));
|
|
673
885
|
} else {
|
|
674
886
|
throw new CarryConfigError(`${where} must be a string or mapping`);
|
|
@@ -679,14 +891,14 @@ function parseCarryRaw(raw) {
|
|
|
679
891
|
function parseCarrySection(text) {
|
|
680
892
|
let doc;
|
|
681
893
|
try {
|
|
682
|
-
doc =
|
|
894
|
+
doc = parseYaml3(text);
|
|
683
895
|
} catch (err) {
|
|
684
896
|
throw new CarryConfigError(
|
|
685
897
|
`yaml parse error: ${err instanceof Error ? err.message : String(err)}`
|
|
686
898
|
);
|
|
687
899
|
}
|
|
688
900
|
if (doc === null || doc === void 0) return [];
|
|
689
|
-
if (!
|
|
901
|
+
if (!isPlainObject3(doc)) {
|
|
690
902
|
throw new CarryConfigError("top-level config must be a mapping");
|
|
691
903
|
}
|
|
692
904
|
return parseCarryRaw(doc.carry);
|
|
@@ -694,7 +906,7 @@ function parseCarrySection(text) {
|
|
|
694
906
|
async function loadCarrySection(path) {
|
|
695
907
|
let text;
|
|
696
908
|
try {
|
|
697
|
-
text = await
|
|
909
|
+
text = await readFile3(path, "utf8");
|
|
698
910
|
} catch (err) {
|
|
699
911
|
if (err.code === "ENOENT") return [];
|
|
700
912
|
throw err;
|
|
@@ -702,27 +914,13 @@ async function loadCarrySection(path) {
|
|
|
702
914
|
return parseCarrySection(text);
|
|
703
915
|
}
|
|
704
916
|
|
|
705
|
-
// ../../packages/sandbox-docker/dist/index.js
|
|
706
|
-
import { spawnSync } from "child_process";
|
|
707
|
-
import { mkdir as mkdir3, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile42, realpath as realpath2, rm as rm2, stat as stat3, writeFile as writeFile22 } from "fs/promises";
|
|
708
|
-
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
709
|
-
import { isAbsolute as isAbsolute2, join as join4, relative as relative2 } from "path";
|
|
710
|
-
import { setTimeout as delay } from "timers/promises";
|
|
711
|
-
import { execa as execa5 } from "execa";
|
|
712
|
-
import { execa as execa2 } from "execa";
|
|
713
|
-
import { mkdir as mkdir4, readFile as readFile5, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
714
|
-
import { createHash as createHash3 } from "crypto";
|
|
715
|
-
import { homedir as homedir2 } from "os";
|
|
716
|
-
import { join as join5 } from "path";
|
|
717
|
-
import { execa as execa22 } from "execa";
|
|
718
|
-
|
|
719
917
|
// ../../packages/config/dist/index.js
|
|
720
|
-
import { parse as
|
|
918
|
+
import { parse as parseYaml4 } from "yaml";
|
|
721
919
|
import { createHash } from "crypto";
|
|
722
920
|
import { realpath, stat } from "fs/promises";
|
|
723
921
|
import { homedir } from "os";
|
|
724
922
|
import { basename, dirname, join, resolve } from "path";
|
|
725
|
-
import { readFile
|
|
923
|
+
import { readFile } from "fs/promises";
|
|
726
924
|
import { parse as parseYaml22 } from "yaml";
|
|
727
925
|
import { mkdir, readFile as readFile22, rename, rm, stat as stat2, writeFile } from "fs/promises";
|
|
728
926
|
import { dirname as dirname2, isAbsolute, join as join2 } from "path";
|
|
@@ -748,6 +946,7 @@ var BUILT_IN_DEFAULTS = {
|
|
|
748
946
|
withEnv: false,
|
|
749
947
|
resyncOnStart: true,
|
|
750
948
|
vnc: true,
|
|
949
|
+
autoApproveHostActions: false,
|
|
751
950
|
isolateClaudeConfig: false,
|
|
752
951
|
isolateCodexConfig: false,
|
|
753
952
|
isolateOpencodeConfig: false,
|
|
@@ -768,7 +967,8 @@ var BUILT_IN_DEFAULTS = {
|
|
|
768
967
|
bundleDepth: void 0,
|
|
769
968
|
vercelVcpus: 2,
|
|
770
969
|
vercelTimeoutMs: 27e5,
|
|
771
|
-
vercelNetworkPolicy: ""
|
|
970
|
+
vercelNetworkPolicy: "",
|
|
971
|
+
cpMaxBytes: 100 * 1024 * 1024
|
|
772
972
|
},
|
|
773
973
|
checkpoint: {
|
|
774
974
|
maxLayers: 3
|
|
@@ -824,7 +1024,8 @@ var BUILT_IN_DEFAULTS = {
|
|
|
824
1024
|
enabled: true,
|
|
825
1025
|
maxConcurrent: 5,
|
|
826
1026
|
maxWorking: 0,
|
|
827
|
-
idleGraceSeconds: 15
|
|
1027
|
+
idleGraceSeconds: 15,
|
|
1028
|
+
openIn: "none"
|
|
828
1029
|
},
|
|
829
1030
|
cloud: {
|
|
830
1031
|
useCurrentBranch: false
|
|
@@ -832,6 +1033,10 @@ var BUILT_IN_DEFAULTS = {
|
|
|
832
1033
|
maintenance: {
|
|
833
1034
|
pruneProjectConfigs: true,
|
|
834
1035
|
pruneProjectConfigsEvery: 50
|
|
1036
|
+
},
|
|
1037
|
+
integrations: {
|
|
1038
|
+
notion: { enabled: false },
|
|
1039
|
+
linear: { enabled: false }
|
|
835
1040
|
}
|
|
836
1041
|
};
|
|
837
1042
|
var KEY_REGISTRY = [
|
|
@@ -942,6 +1147,11 @@ var KEY_REGISTRY = [
|
|
|
942
1147
|
type: "bool",
|
|
943
1148
|
description: "Run the per-box Xvnc + noVNC stack."
|
|
944
1149
|
},
|
|
1150
|
+
{
|
|
1151
|
+
key: "box.autoApproveHostActions",
|
|
1152
|
+
type: "bool",
|
|
1153
|
+
description: "Auto-approve host-action confirmations (git push, cp host<->box, gh PR writes, checkpoint) for this box without an interactive prompt. Off by default; intended for unattended orchestration of trusted boxes. Each auto-approval is recorded as a relay event (visible in `agentbox agent` / the dashboard)."
|
|
1154
|
+
},
|
|
945
1155
|
{
|
|
946
1156
|
key: "box.isolateClaudeConfig",
|
|
947
1157
|
type: "bool",
|
|
@@ -1040,6 +1250,12 @@ var KEY_REGISTRY = [
|
|
|
1040
1250
|
type: "int",
|
|
1041
1251
|
description: "Max session length (ms) for new --provider vercel boxes before the VM auto-snapshots; persistent mode auto-resumes on the next call. Default 2700000 (45 min, the Hobby ceiling). Vercel-only."
|
|
1042
1252
|
},
|
|
1253
|
+
{
|
|
1254
|
+
key: "box.cpMaxBytes",
|
|
1255
|
+
type: "int",
|
|
1256
|
+
description: "Max bytes a single host\u2192box copy may transfer after excludes, shared by `agentbox cp` (blocked with a size breakdown unless --yes) and each `carry:` entry (rejected at resolve time). Default 104857600 (100 MiB).",
|
|
1257
|
+
advanced: true
|
|
1258
|
+
},
|
|
1043
1259
|
{
|
|
1044
1260
|
key: "box.vercelNetworkPolicy",
|
|
1045
1261
|
type: "string",
|
|
@@ -1187,6 +1403,12 @@ var KEY_REGISTRY = [
|
|
|
1187
1403
|
type: "int",
|
|
1188
1404
|
description: "Seconds an agent must stay non-working before it frees its working slot (debounce against brief idle flaps between turns). Only used when queue.maxWorking > 0."
|
|
1189
1405
|
},
|
|
1406
|
+
{
|
|
1407
|
+
key: "queue.openIn",
|
|
1408
|
+
type: "enum",
|
|
1409
|
+
enumValues: ["none", "split", "window", "tab"],
|
|
1410
|
+
description: "When a background `-i` job finishes creating its box, where the host relay opens an attached terminal onto it: `none` (default \u2014 open nothing, just queue), `split`, `window`, or `tab`. Honored only when the submitting shell runs inside tmux, cmux, or iTerm2 (the targeting is captured at submit time). Under cmux, `split` splits the pane you submitted from (falling back to the parent workspace, then a new workspace), `tab` adds a tab in the parent workspace, and `window` opens a separate workspace; iTerm2 opens relative to the frontmost window. Unlike `attach.openIn` there is no `same` mode \u2014 the box is created asynchronously, so it is always a fresh terminal."
|
|
1411
|
+
},
|
|
1190
1412
|
{
|
|
1191
1413
|
key: "cloud.useCurrentBranch",
|
|
1192
1414
|
type: "bool",
|
|
@@ -1201,6 +1423,16 @@ var KEY_REGISTRY = [
|
|
|
1201
1423
|
key: "maintenance.pruneProjectConfigsEvery",
|
|
1202
1424
|
type: "int",
|
|
1203
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.'
|
|
1204
1436
|
}
|
|
1205
1437
|
];
|
|
1206
1438
|
var REGISTRY_BY_KEY = new Map(KEY_REGISTRY.map((d) => [d.key, d]));
|
|
@@ -1213,7 +1445,7 @@ var UserConfigError = class extends Error {
|
|
|
1213
1445
|
this.name = "UserConfigError";
|
|
1214
1446
|
}
|
|
1215
1447
|
};
|
|
1216
|
-
function
|
|
1448
|
+
function isPlainObject4(v) {
|
|
1217
1449
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1218
1450
|
}
|
|
1219
1451
|
var RENAMED_KEYS = /* @__PURE__ */ new Map([["box.snapshot", "box.hostSnapshot"]]);
|
|
@@ -1270,21 +1502,21 @@ function coerceTypedValue(raw, desc, where) {
|
|
|
1270
1502
|
function parseUserConfig(text, where) {
|
|
1271
1503
|
let doc;
|
|
1272
1504
|
try {
|
|
1273
|
-
doc =
|
|
1505
|
+
doc = parseYaml4(text);
|
|
1274
1506
|
} catch (err) {
|
|
1275
1507
|
throw new UserConfigError(
|
|
1276
1508
|
`${where}: yaml parse error: ${err instanceof Error ? err.message : String(err)}`
|
|
1277
1509
|
);
|
|
1278
1510
|
}
|
|
1279
1511
|
if (doc === null || doc === void 0) return {};
|
|
1280
|
-
if (!
|
|
1512
|
+
if (!isPlainObject4(doc)) {
|
|
1281
1513
|
throw new UserConfigError(`${where}: top-level must be a mapping`);
|
|
1282
1514
|
}
|
|
1283
1515
|
return parseUserConfigObject(doc, where);
|
|
1284
1516
|
}
|
|
1285
1517
|
function parseUserConfigObject(doc, where) {
|
|
1286
1518
|
if (doc === null || doc === void 0) return {};
|
|
1287
|
-
if (!
|
|
1519
|
+
if (!isPlainObject4(doc)) {
|
|
1288
1520
|
throw new UserConfigError(`${where}: must be a mapping`);
|
|
1289
1521
|
}
|
|
1290
1522
|
const out = {};
|
|
@@ -1305,32 +1537,50 @@ function parseUserConfigObject(doc, where) {
|
|
|
1305
1537
|
);
|
|
1306
1538
|
}
|
|
1307
1539
|
if (branchRaw === null || branchRaw === void 0) continue;
|
|
1308
|
-
if (!
|
|
1540
|
+
if (!isPlainObject4(branchRaw)) {
|
|
1309
1541
|
throw new UserConfigError(`${where}.${branchName}: must be a mapping`);
|
|
1310
1542
|
}
|
|
1311
|
-
const branchOut =
|
|
1312
|
-
for (const [leafName, leafRaw] of Object.entries(branchRaw)) {
|
|
1313
|
-
const desc = branchSpec.leaves.get(leafName);
|
|
1314
|
-
if (!desc) {
|
|
1315
|
-
const renamedTo = RENAMED_KEYS.get(`${branchName}.${leafName}`);
|
|
1316
|
-
if (renamedTo) {
|
|
1317
|
-
throw new UserConfigError(
|
|
1318
|
-
`${where}.${branchName}.${leafName} was renamed to ${renamedTo} \u2014 update your config`
|
|
1319
|
-
);
|
|
1320
|
-
}
|
|
1321
|
-
throw new UserConfigError(
|
|
1322
|
-
`${where}.${branchName}: unknown key "${leafName}" (known: ${[...branchSpec.leaves.keys()].join(", ")})`
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
if (leafRaw === void 0) continue;
|
|
1326
|
-
branchOut[leafName] = coerceTypedValue(leafRaw, desc, `${where}.${desc.key}`);
|
|
1327
|
-
}
|
|
1543
|
+
const branchOut = parseBranchObject(branchSpec, branchName, branchRaw, "", where);
|
|
1328
1544
|
if (Object.keys(branchOut).length > 0) {
|
|
1329
1545
|
out[branchName] = branchOut;
|
|
1330
1546
|
}
|
|
1331
1547
|
}
|
|
1332
1548
|
return out;
|
|
1333
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
|
+
}
|
|
1334
1584
|
function coerceFromString(key, raw) {
|
|
1335
1585
|
const desc = lookupKeyOrThrow(key);
|
|
1336
1586
|
switch (desc.type) {
|
|
@@ -1446,7 +1696,7 @@ async function configPathFor(scope, cwd) {
|
|
|
1446
1696
|
async function loadOptionalUserConfig(path) {
|
|
1447
1697
|
let text;
|
|
1448
1698
|
try {
|
|
1449
|
-
text = await
|
|
1699
|
+
text = await readFile(path, "utf8");
|
|
1450
1700
|
} catch (err) {
|
|
1451
1701
|
if (err.code === "ENOENT") return {};
|
|
1452
1702
|
throw err;
|
|
@@ -1457,7 +1707,7 @@ async function loadProjectAgentboxDefaults(workspacePath) {
|
|
|
1457
1707
|
const path = workspaceConfigFile(workspacePath);
|
|
1458
1708
|
let text;
|
|
1459
1709
|
try {
|
|
1460
|
-
text = await
|
|
1710
|
+
text = await readFile(path, "utf8");
|
|
1461
1711
|
} catch (err) {
|
|
1462
1712
|
if (err.code === "ENOENT") return {};
|
|
1463
1713
|
throw err;
|
|
@@ -1537,14 +1787,26 @@ function mergeLayers(input) {
|
|
|
1537
1787
|
return { effective, sources };
|
|
1538
1788
|
}
|
|
1539
1789
|
function readLeaf(obj, branch, leaf) {
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
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;
|
|
1543
1796
|
}
|
|
1544
1797
|
function writeLeaf(obj, branch, leaf, value) {
|
|
1545
|
-
|
|
1546
|
-
if (!
|
|
1547
|
-
|
|
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;
|
|
1548
1810
|
}
|
|
1549
1811
|
function resolveDefaultCheckpoint(cfg, provider) {
|
|
1550
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;
|
|
@@ -1720,25 +1982,34 @@ function stampSchema(doc) {
|
|
|
1720
1982
|
}
|
|
1721
1983
|
}
|
|
1722
1984
|
function setLeaf(doc, key, value) {
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
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];
|
|
1729
1994
|
}
|
|
1730
|
-
|
|
1995
|
+
cur[segs[segs.length - 1]] = value;
|
|
1731
1996
|
}
|
|
1732
1997
|
function unsetLeaf(doc, key) {
|
|
1733
|
-
const
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
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]];
|
|
1742
2013
|
}
|
|
1743
2014
|
return true;
|
|
1744
2015
|
}
|
|
@@ -1771,123 +2042,346 @@ async function touchProjectMeta(absPath) {
|
|
|
1771
2042
|
}
|
|
1772
2043
|
|
|
1773
2044
|
// ../../packages/sandbox-docker/dist/index.js
|
|
2045
|
+
import { spawnSync } from "child_process";
|
|
2046
|
+
import { mkdir as mkdir3, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile42, realpath as realpath2, rm as rm2, stat as stat3, writeFile as writeFile22 } from "fs/promises";
|
|
2047
|
+
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
2048
|
+
import { isAbsolute as isAbsolute2, join as join4, relative as relative2 } from "path";
|
|
2049
|
+
import { setTimeout as delay } from "timers/promises";
|
|
2050
|
+
import { execa as execa6 } from "execa";
|
|
2051
|
+
import { execa as execa2 } from "execa";
|
|
2052
|
+
import { mkdir as mkdir4, readFile as readFile5, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
2053
|
+
import { createHash as createHash3 } from "crypto";
|
|
2054
|
+
import { homedir as homedir2 } from "os";
|
|
2055
|
+
import { basename as basename2, dirname as dirname22, join as join5 } from "path";
|
|
2056
|
+
import { execa as execa3 } from "execa";
|
|
2057
|
+
import { existsSync, mkdirSync, renameSync, statSync } from "fs";
|
|
2058
|
+
import { basename as basename3, dirname as dirname3, posix, resolve as resolve2 } from "path";
|
|
2059
|
+
import { execa as execa22 } from "execa";
|
|
1774
2060
|
import { copyFile, mkdtemp, readdir as readdir22, readFile as readFile32, rm as rm3, stat as stat22, writeFile as writeFile3 } from "fs/promises";
|
|
1775
2061
|
import { homedir as homedir22, tmpdir } from "os";
|
|
1776
|
-
import { basename as
|
|
1777
|
-
import { execa as
|
|
2062
|
+
import { basename as basename4, join as join32, relative } from "path";
|
|
2063
|
+
import { execa as execa5 } from "execa";
|
|
1778
2064
|
import { chmod, mkdir as mkdir22, readFile as readFile23 } from "fs/promises";
|
|
1779
|
-
import { basename as
|
|
1780
|
-
import { execa as
|
|
2065
|
+
import { basename as basename32, join as join22 } from "path";
|
|
2066
|
+
import { execa as execa4 } from "execa";
|
|
1781
2067
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1782
2068
|
import { stat as stat42 } from "fs/promises";
|
|
1783
2069
|
import { homedir as homedir4 } from "os";
|
|
1784
2070
|
import { join as join52 } from "path";
|
|
1785
|
-
import { execa as
|
|
2071
|
+
import { execa as execa7 } from "execa";
|
|
1786
2072
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1787
2073
|
import { stat as stat5 } from "fs/promises";
|
|
1788
2074
|
import { homedir as homedir5 } from "os";
|
|
1789
2075
|
import { join as join6 } from "path";
|
|
1790
|
-
import { execa as
|
|
1791
|
-
import { randomBytes as
|
|
2076
|
+
import { execa as execa8 } from "execa";
|
|
2077
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
1792
2078
|
import { createHash as createHash22 } from "crypto";
|
|
1793
2079
|
import { readFile as readFile52 } from "fs/promises";
|
|
1794
2080
|
import { join as join7 } from "path";
|
|
1795
|
-
import { execa as
|
|
1796
|
-
import { existsSync } from "fs";
|
|
2081
|
+
import { execa as execa9 } from "execa";
|
|
2082
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1797
2083
|
import { readFile as readFile6 } from "fs/promises";
|
|
1798
2084
|
import { homedir as homedir6 } from "os";
|
|
1799
2085
|
import { join as join8 } from "path";
|
|
1800
|
-
import { execa as execa9 } from "execa";
|
|
1801
|
-
|
|
1802
|
-
// ../../packages/core/dist/index.js
|
|
1803
|
-
import { randomBytes } from "crypto";
|
|
1804
|
-
var claudeCodeLauncher = {
|
|
1805
|
-
kind: "claude-code",
|
|
1806
|
-
// claude treats its first positional argument as the seed user turn in
|
|
1807
|
-
// interactive mode (`claude "<message>"`), so we slot the initial message
|
|
1808
|
-
// ahead of any user-passed flags.
|
|
1809
|
-
buildArgs(initialMessage, userArgs) {
|
|
1810
|
-
if (!initialMessage) return [...userArgs];
|
|
1811
|
-
return [initialMessage, ...userArgs];
|
|
1812
|
-
}
|
|
1813
|
-
};
|
|
1814
|
-
var codexLauncher = {
|
|
1815
|
-
kind: "codex",
|
|
1816
|
-
buildArgs(initialMessage, userArgs) {
|
|
1817
|
-
if (!initialMessage) return [...userArgs];
|
|
1818
|
-
return [initialMessage, ...userArgs];
|
|
1819
|
-
}
|
|
1820
|
-
};
|
|
1821
|
-
var opencodeLauncher = {
|
|
1822
|
-
kind: "opencode",
|
|
1823
|
-
buildArgs(initialMessage, userArgs) {
|
|
1824
|
-
if (!initialMessage) return [...userArgs];
|
|
1825
|
-
return [initialMessage, ...userArgs];
|
|
1826
|
-
}
|
|
1827
|
-
};
|
|
1828
|
-
function resolveAgentLauncher(kind) {
|
|
1829
|
-
if (kind === "claude-code") return claudeCodeLauncher;
|
|
1830
|
-
if (kind === "codex") return codexLauncher;
|
|
1831
|
-
if (kind === "opencode") return opencodeLauncher;
|
|
1832
|
-
throw new Error(`unknown agent kind: ${String(kind)}`);
|
|
1833
|
-
}
|
|
1834
|
-
var UserFacingError = class extends Error {
|
|
1835
|
-
constructor(message) {
|
|
1836
|
-
super(message);
|
|
1837
|
-
this.name = "UserFacingError";
|
|
1838
|
-
}
|
|
1839
|
-
};
|
|
1840
|
-
var BoxNotFoundError = class extends Error {
|
|
1841
|
-
constructor(query) {
|
|
1842
|
-
super(`no agentbox matches "${query}"`);
|
|
1843
|
-
this.query = query;
|
|
1844
|
-
this.name = "BoxNotFoundError";
|
|
1845
|
-
}
|
|
1846
|
-
query;
|
|
1847
|
-
};
|
|
1848
|
-
var AmbiguousBoxError = class extends Error {
|
|
1849
|
-
constructor(query, matches) {
|
|
1850
|
-
const ids = matches.map((m) => m.id).join(", ");
|
|
1851
|
-
super(`"${query}" matches multiple boxes: ${ids}`);
|
|
1852
|
-
this.query = query;
|
|
1853
|
-
this.matches = matches;
|
|
1854
|
-
this.name = "AmbiguousBoxError";
|
|
1855
|
-
}
|
|
1856
|
-
query;
|
|
1857
|
-
matches;
|
|
1858
|
-
};
|
|
1859
|
-
var BOX_ID_PREFIX = "b";
|
|
1860
|
-
function generateBoxId() {
|
|
1861
|
-
return `${BOX_ID_PREFIX}${randomBytes(4).toString("hex")}`;
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
// ../../packages/sandbox-docker/dist/index.js
|
|
1865
2086
|
import { execa as execa10 } from "execa";
|
|
2087
|
+
import { execa as execa11 } from "execa";
|
|
1866
2088
|
import { mkdir as mkdir42, readdir as readdir42, rm as rm32, stat as stat6 } from "fs/promises";
|
|
1867
2089
|
import { homedir as homedir7, platform } from "os";
|
|
1868
|
-
import { join as join9, resolve as
|
|
2090
|
+
import { join as join9, resolve as resolve22 } from "path";
|
|
1869
2091
|
import { mkdir as mkdir5, mkdtemp as mkdtemp3, readFile as readFile7, readdir as readdir5, rm as rm4, writeFile as writeFile32 } from "fs/promises";
|
|
1870
2092
|
import { homedir as homedir8, tmpdir as tmpdir3 } from "os";
|
|
1871
|
-
import { basename as
|
|
1872
|
-
import { execa as execa11 } from "execa";
|
|
1873
|
-
import { stat as stat7 } from "fs/promises";
|
|
2093
|
+
import { basename as basename5, join as join10 } from "path";
|
|
1874
2094
|
import { execa as execa12 } from "execa";
|
|
2095
|
+
import { stat as stat7 } from "fs/promises";
|
|
1875
2096
|
import { execa as execa13 } from "execa";
|
|
2097
|
+
import { execa as execa14 } from "execa";
|
|
1876
2098
|
import { spawn } from "child_process";
|
|
1877
2099
|
import { randomBytes as randomBytes22 } from "crypto";
|
|
1878
|
-
import { existsSync as
|
|
2100
|
+
import { existsSync as existsSync3, openSync } from "fs";
|
|
1879
2101
|
import { cp, mkdir as mkdir6, readFile as readFile8, readdir as readdir6, rename as rename3, rm as rm5, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
|
|
1880
2102
|
import { request as httpRequest } from "http";
|
|
1881
2103
|
import { createRequire } from "module";
|
|
1882
2104
|
import { homedir as homedir9 } from "os";
|
|
1883
|
-
import { dirname as
|
|
2105
|
+
import { dirname as dirname32, join as join11, resolve as resolve3, sep } from "path";
|
|
1884
2106
|
import { setTimeout as delay2 } from "timers/promises";
|
|
1885
2107
|
import { fileURLToPath } from "url";
|
|
1886
2108
|
|
|
1887
2109
|
// ../../packages/relay/dist/index.js
|
|
1888
|
-
import { createHash as createHash2, randomBytes
|
|
2110
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
1889
2111
|
import { execa } from "execa";
|
|
1890
|
-
|
|
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";
|
|
1891
2385
|
import {
|
|
1892
2386
|
mkdir as mkdir2,
|
|
1893
2387
|
readdir as readdir2,
|
|
@@ -2077,7 +2571,7 @@ async function uncachedBoxStateCount() {
|
|
|
2077
2571
|
}
|
|
2078
2572
|
function inspectDockerState(containerName) {
|
|
2079
2573
|
return new Promise((resolveP) => {
|
|
2080
|
-
const child =
|
|
2574
|
+
const child = spawn4("docker", ["inspect", "--format", "{{.State.Status}}", containerName], {
|
|
2081
2575
|
stdio: ["ignore", "pipe", "pipe"]
|
|
2082
2576
|
});
|
|
2083
2577
|
let out = "";
|
|
@@ -2110,21 +2604,18 @@ function queueLogPath(id) {
|
|
|
2110
2604
|
}
|
|
2111
2605
|
|
|
2112
2606
|
// ../../packages/sandbox-docker/dist/index.js
|
|
2113
|
-
import { execa as
|
|
2607
|
+
import { execa as execa17 } from "execa";
|
|
2114
2608
|
import { readdir as readdir7, rm as rm6, stat as stat9 } from "fs/promises";
|
|
2115
2609
|
import { join as join14 } from "path";
|
|
2116
|
-
import { execa as
|
|
2610
|
+
import { execa as execa16 } from "execa";
|
|
2117
2611
|
import { join as join13 } from "path";
|
|
2118
2612
|
import { homedir as homedir11 } from "os";
|
|
2119
2613
|
import { join as join15 } from "path";
|
|
2120
|
-
import { execa as execa17 } from "execa";
|
|
2121
|
-
import { existsSync as existsSync3, mkdirSync, renameSync, statSync } from "fs";
|
|
2122
|
-
import { basename as basename5, dirname as dirname22, posix, resolve as resolve4 } from "path";
|
|
2123
2614
|
import { execa as execa18 } from "execa";
|
|
2124
2615
|
import { createHash as createHash32 } from "crypto";
|
|
2125
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";
|
|
2126
2617
|
import { homedir as homedir12, tmpdir as tmpdir4 } from "os";
|
|
2127
|
-
import { basename as
|
|
2618
|
+
import { basename as basename7, dirname as dirname4, join as join16 } from "path";
|
|
2128
2619
|
import { execa as execa19 } from "execa";
|
|
2129
2620
|
function isHostPathHookCommand(command, hostHome) {
|
|
2130
2621
|
if (typeof command !== "string" || command.length === 0) return false;
|
|
@@ -2251,16 +2742,16 @@ function rewritePluginPaths(value, hostPluginsPrefix) {
|
|
|
2251
2742
|
}
|
|
2252
2743
|
return value;
|
|
2253
2744
|
}
|
|
2254
|
-
function
|
|
2745
|
+
function isPlainObject5(v) {
|
|
2255
2746
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
2256
2747
|
}
|
|
2257
2748
|
function additiveMerge(hostRoot, boxRoot, hostPluginsPrefix, selectMap, withMap) {
|
|
2258
2749
|
const hostMap = selectMap(hostRoot);
|
|
2259
2750
|
const boxMap = selectMap(boxRoot);
|
|
2260
|
-
if (!
|
|
2751
|
+
if (!isPlainObject5(boxMap)) {
|
|
2261
2752
|
return { data: hostRoot, changed: false, addedKeys: [] };
|
|
2262
2753
|
}
|
|
2263
|
-
const base =
|
|
2754
|
+
const base = isPlainObject5(hostMap) ? { ...hostMap } : {};
|
|
2264
2755
|
const addedKeys = [];
|
|
2265
2756
|
for (const [key, value] of Object.entries(boxMap)) {
|
|
2266
2757
|
if (Object.prototype.hasOwnProperty.call(base, key)) continue;
|
|
@@ -2275,7 +2766,7 @@ function additiveMerge(hostRoot, boxRoot, hostPluginsPrefix, selectMap, withMap)
|
|
|
2275
2766
|
function mergeKnownMarketplaces(hostJson, boxJson, opts) {
|
|
2276
2767
|
const prefix = `${opts.hostHome}/.claude/plugins/`;
|
|
2277
2768
|
return additiveMerge(
|
|
2278
|
-
|
|
2769
|
+
isPlainObject5(hostJson) ? hostJson : {},
|
|
2279
2770
|
boxJson,
|
|
2280
2771
|
prefix,
|
|
2281
2772
|
(root) => root,
|
|
@@ -2284,24 +2775,24 @@ function mergeKnownMarketplaces(hostJson, boxJson, opts) {
|
|
|
2284
2775
|
}
|
|
2285
2776
|
function mergeInstalledPlugins(hostJson, boxJson, opts) {
|
|
2286
2777
|
const prefix = `${opts.hostHome}/.claude/plugins/`;
|
|
2287
|
-
const hostRoot =
|
|
2778
|
+
const hostRoot = isPlainObject5(hostJson) ? hostJson : { plugins: {} };
|
|
2288
2779
|
return additiveMerge(
|
|
2289
2780
|
hostRoot,
|
|
2290
2781
|
boxJson,
|
|
2291
2782
|
prefix,
|
|
2292
|
-
(root) =>
|
|
2783
|
+
(root) => isPlainObject5(root) ? root["plugins"] : void 0,
|
|
2293
2784
|
(host, merged) => ({ ...host, plugins: merged })
|
|
2294
2785
|
);
|
|
2295
2786
|
}
|
|
2296
2787
|
function referencedPluginVersionKeys(installedPluginsJson) {
|
|
2297
2788
|
const keys = /* @__PURE__ */ new Set();
|
|
2298
|
-
if (!
|
|
2789
|
+
if (!isPlainObject5(installedPluginsJson)) return keys;
|
|
2299
2790
|
const plugins = installedPluginsJson["plugins"];
|
|
2300
|
-
if (!
|
|
2791
|
+
if (!isPlainObject5(plugins)) return keys;
|
|
2301
2792
|
for (const entries of Object.values(plugins)) {
|
|
2302
2793
|
if (!Array.isArray(entries)) continue;
|
|
2303
2794
|
for (const entry of entries) {
|
|
2304
|
-
if (!
|
|
2795
|
+
if (!isPlainObject5(entry)) continue;
|
|
2305
2796
|
const installPath = entry["installPath"];
|
|
2306
2797
|
if (typeof installPath !== "string") continue;
|
|
2307
2798
|
const segments = installPath.split("/").filter((s) => s.length > 0);
|
|
@@ -2483,48 +2974,181 @@ async function inspectContainer(name) {
|
|
|
2483
2974
|
} catch {
|
|
2484
2975
|
return null;
|
|
2485
2976
|
}
|
|
2486
|
-
}
|
|
2487
|
-
async function inspectVolumeMountpoint(name) {
|
|
2488
|
-
const result = await execa2("docker", ["volume", "inspect", "--format", "{{.Mountpoint}}", name], {
|
|
2489
|
-
reject: false
|
|
2490
|
-
});
|
|
2491
|
-
if (result.exitCode !== 0) return null;
|
|
2492
|
-
return (result.stdout ?? "").trim() || null;
|
|
2493
|
-
}
|
|
2494
|
-
var AGENTBOX_PREFIX = "agentbox-";
|
|
2495
|
-
async function listAgentboxContainers() {
|
|
2496
|
-
const result = await execa2(
|
|
2977
|
+
}
|
|
2978
|
+
async function inspectVolumeMountpoint(name) {
|
|
2979
|
+
const result = await execa2("docker", ["volume", "inspect", "--format", "{{.Mountpoint}}", name], {
|
|
2980
|
+
reject: false
|
|
2981
|
+
});
|
|
2982
|
+
if (result.exitCode !== 0) return null;
|
|
2983
|
+
return (result.stdout ?? "").trim() || null;
|
|
2984
|
+
}
|
|
2985
|
+
var AGENTBOX_PREFIX = "agentbox-";
|
|
2986
|
+
async function listAgentboxContainers() {
|
|
2987
|
+
const result = await execa2(
|
|
2988
|
+
"docker",
|
|
2989
|
+
["ps", "-a", "--filter", `name=^${AGENTBOX_PREFIX}`, "--format", "{{.Names}}"],
|
|
2990
|
+
{ reject: false }
|
|
2991
|
+
);
|
|
2992
|
+
if (result.exitCode !== 0) return [];
|
|
2993
|
+
return (result.stdout ?? "").split("\n").map((s) => s.trim()).filter((s) => s.startsWith(AGENTBOX_PREFIX));
|
|
2994
|
+
}
|
|
2995
|
+
async function publishedHostPort(container, containerPort) {
|
|
2996
|
+
const result = await execa2("docker", ["port", container, `${String(containerPort)}/tcp`], {
|
|
2997
|
+
reject: false
|
|
2998
|
+
});
|
|
2999
|
+
if (result.exitCode !== 0) return null;
|
|
3000
|
+
const first = (result.stdout ?? "").split("\n")[0]?.trim();
|
|
3001
|
+
if (!first) return null;
|
|
3002
|
+
const m = /:(\d+)$/.exec(first);
|
|
3003
|
+
return m ? Number(m[1]) : null;
|
|
3004
|
+
}
|
|
3005
|
+
async function listAgentboxVolumes() {
|
|
3006
|
+
const result = await execa2(
|
|
3007
|
+
"docker",
|
|
3008
|
+
["volume", "ls", "--filter", `name=^${AGENTBOX_PREFIX}`, "--format", "{{.Name}}"],
|
|
3009
|
+
{ reject: false }
|
|
3010
|
+
);
|
|
3011
|
+
if (result.exitCode !== 0) return [];
|
|
3012
|
+
return (result.stdout ?? "").split("\n").map((s) => s.trim()).filter((s) => s.startsWith(AGENTBOX_PREFIX));
|
|
3013
|
+
}
|
|
3014
|
+
function posixDirname(p) {
|
|
3015
|
+
return posix.dirname(p) || "/";
|
|
3016
|
+
}
|
|
3017
|
+
function asText(s) {
|
|
3018
|
+
if (s === void 0) return "";
|
|
3019
|
+
if (typeof s === "string") return s;
|
|
3020
|
+
return Buffer.from(s).toString("utf8");
|
|
3021
|
+
}
|
|
3022
|
+
function tarExcludeArgs(exclude) {
|
|
3023
|
+
return (exclude ?? []).map((p) => `--exclude=${p}`);
|
|
3024
|
+
}
|
|
3025
|
+
async function streamTarPipe(producerFile, producerArgs, consumerFile, consumerArgs, producerEnv) {
|
|
3026
|
+
const producer = execa22(producerFile, producerArgs, {
|
|
3027
|
+
reject: false,
|
|
3028
|
+
buffer: { stdout: false },
|
|
3029
|
+
...producerEnv ? { env: producerEnv } : {}
|
|
3030
|
+
});
|
|
3031
|
+
const consumer = execa22(consumerFile, consumerArgs, {
|
|
3032
|
+
reject: false,
|
|
3033
|
+
buffer: { stdout: false }
|
|
3034
|
+
});
|
|
3035
|
+
producer.stdout?.pipe(consumer.stdin);
|
|
3036
|
+
const [p, c] = await Promise.all([producer, consumer]);
|
|
3037
|
+
return [
|
|
3038
|
+
{ exitCode: p.exitCode, stderr: p.stderr },
|
|
3039
|
+
{ exitCode: c.exitCode, stderr: c.stderr }
|
|
3040
|
+
];
|
|
3041
|
+
}
|
|
3042
|
+
async function uploadToBox(box, hostSrc, boxDst, exclude) {
|
|
3043
|
+
const srcAbs = resolve2(hostSrc);
|
|
3044
|
+
if (!existsSync(srcAbs)) throw new Error(`source not found: ${hostSrc}`);
|
|
3045
|
+
const srcBasename = basename3(srcAbs);
|
|
3046
|
+
const srcParent = dirname3(srcAbs);
|
|
3047
|
+
let boxParent;
|
|
3048
|
+
let finalName;
|
|
3049
|
+
if (boxDst.endsWith("/")) {
|
|
3050
|
+
boxParent = boxDst.replace(/\/+$/, "") || "/";
|
|
3051
|
+
finalName = srcBasename;
|
|
3052
|
+
} else {
|
|
3053
|
+
const isDir2 = await execa22(
|
|
3054
|
+
"docker",
|
|
3055
|
+
["exec", box.container, "test", "-d", boxDst],
|
|
3056
|
+
{ reject: false }
|
|
3057
|
+
);
|
|
3058
|
+
if (isDir2.exitCode === 0) {
|
|
3059
|
+
boxParent = boxDst.replace(/\/+$/, "") || "/";
|
|
3060
|
+
finalName = srcBasename;
|
|
3061
|
+
} else {
|
|
3062
|
+
boxParent = posixDirname(boxDst);
|
|
3063
|
+
finalName = posix.basename(boxDst);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
const finalPath = boxParent === "/" ? `/${finalName}` : `${boxParent}/${finalName}`;
|
|
3067
|
+
const mk = await execa22(
|
|
3068
|
+
"docker",
|
|
3069
|
+
["exec", "--user", "root", box.container, "mkdir", "-p", boxParent],
|
|
3070
|
+
{ reject: false }
|
|
3071
|
+
);
|
|
3072
|
+
if (mk.exitCode !== 0) {
|
|
3073
|
+
throw new Error(`mkdir -p ${boxParent} in box failed: ${asText(mk.stderr).slice(0, 300)}`);
|
|
3074
|
+
}
|
|
3075
|
+
const [packed, extracted] = await streamTarPipe(
|
|
3076
|
+
"tar",
|
|
3077
|
+
["-C", srcParent, "-cf", "-", ...tarExcludeArgs(exclude), srcBasename],
|
|
3078
|
+
"docker",
|
|
3079
|
+
["exec", "-i", "--user", "root", box.container, "tar", "-xf", "-", "-C", boxParent],
|
|
3080
|
+
{ ...process.env, COPYFILE_DISABLE: "1" }
|
|
3081
|
+
);
|
|
3082
|
+
if (packed.exitCode !== 0) {
|
|
3083
|
+
throw new Error(`tar pack failed: ${asText(packed.stderr).slice(0, 300)}`);
|
|
3084
|
+
}
|
|
3085
|
+
if (extracted.exitCode !== 0) {
|
|
3086
|
+
throw new Error(`tar extract in box failed: ${asText(extracted.stderr).slice(0, 300)}`);
|
|
3087
|
+
}
|
|
3088
|
+
if (finalName !== srcBasename) {
|
|
3089
|
+
const initial = boxParent === "/" ? `/${srcBasename}` : `${boxParent}/${srcBasename}`;
|
|
3090
|
+
const mv = await execa22(
|
|
3091
|
+
"docker",
|
|
3092
|
+
["exec", "--user", "root", box.container, "mv", initial, finalPath],
|
|
3093
|
+
{ reject: false }
|
|
3094
|
+
);
|
|
3095
|
+
if (mv.exitCode !== 0) {
|
|
3096
|
+
throw new Error(
|
|
3097
|
+
`rename ${initial} -> ${finalPath} in box failed: ${asText(mv.stderr).slice(0, 300)}`
|
|
3098
|
+
);
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
const chown = await execa22(
|
|
2497
3102
|
"docker",
|
|
2498
|
-
["
|
|
3103
|
+
["exec", "--user", "root", box.container, "chown", "-R", "1000:1000", finalPath],
|
|
2499
3104
|
{ reject: false }
|
|
2500
3105
|
);
|
|
2501
|
-
if (
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
}
|
|
2508
|
-
if (result.exitCode !== 0) return null;
|
|
2509
|
-
const first = (result.stdout ?? "").split("\n")[0]?.trim();
|
|
2510
|
-
if (!first) return null;
|
|
2511
|
-
const m = /:(\d+)$/.exec(first);
|
|
2512
|
-
return m ? Number(m[1]) : null;
|
|
3106
|
+
if (chown.exitCode !== 0) {
|
|
3107
|
+
return {
|
|
3108
|
+
finalPath,
|
|
3109
|
+
warn: `chown ${finalPath} to vscode (uid 1000) failed; ownership inside the box may be root.`
|
|
3110
|
+
};
|
|
3111
|
+
}
|
|
3112
|
+
return { finalPath };
|
|
2513
3113
|
}
|
|
2514
|
-
async function
|
|
2515
|
-
const
|
|
3114
|
+
async function downloadFromBox(box, boxSrc, hostDst, exclude) {
|
|
3115
|
+
const srcBasename = posix.basename(boxSrc);
|
|
3116
|
+
const srcParent = posixDirname(boxSrc);
|
|
3117
|
+
const dstAbs = resolve2(hostDst);
|
|
3118
|
+
let hostParent;
|
|
3119
|
+
let finalName;
|
|
3120
|
+
const dstExists = existsSync(dstAbs);
|
|
3121
|
+
if (hostDst.endsWith("/") || dstExists && statSync(dstAbs).isDirectory()) {
|
|
3122
|
+
hostParent = dstAbs;
|
|
3123
|
+
finalName = srcBasename;
|
|
3124
|
+
} else {
|
|
3125
|
+
hostParent = dirname3(dstAbs);
|
|
3126
|
+
finalName = basename3(dstAbs);
|
|
3127
|
+
}
|
|
3128
|
+
mkdirSync(hostParent, { recursive: true });
|
|
3129
|
+
const finalPath = posix.join(hostParent, finalName);
|
|
3130
|
+
const [packed, extracted] = await streamTarPipe(
|
|
2516
3131
|
"docker",
|
|
2517
|
-
["
|
|
2518
|
-
|
|
3132
|
+
["exec", box.container, "tar", "-C", srcParent, "-cf", "-", ...tarExcludeArgs(exclude), srcBasename],
|
|
3133
|
+
"tar",
|
|
3134
|
+
["-xf", "-", "-C", hostParent]
|
|
2519
3135
|
);
|
|
2520
|
-
if (
|
|
2521
|
-
|
|
3136
|
+
if (packed.exitCode !== 0) {
|
|
3137
|
+
throw new Error(`tar pack in box failed: ${asText(packed.stderr).slice(0, 300)}`);
|
|
3138
|
+
}
|
|
3139
|
+
if (extracted.exitCode !== 0) {
|
|
3140
|
+
throw new Error(`tar extract on host failed: ${asText(extracted.stderr).slice(0, 300)}`);
|
|
3141
|
+
}
|
|
3142
|
+
if (finalName !== srcBasename) {
|
|
3143
|
+
renameSync(posix.join(hostParent, srcBasename), finalPath);
|
|
3144
|
+
}
|
|
3145
|
+
return { finalPath };
|
|
2522
3146
|
}
|
|
2523
3147
|
var CONTAINER_EXPORT_MERGED = "/host-export";
|
|
2524
3148
|
var cachedEngine = null;
|
|
2525
3149
|
async function detectEngine() {
|
|
2526
3150
|
if (cachedEngine !== null) return cachedEngine;
|
|
2527
|
-
const result = await
|
|
3151
|
+
const result = await execa3("docker", ["info", "--format", "{{.OperatingSystem}}"], {
|
|
2528
3152
|
reject: false
|
|
2529
3153
|
});
|
|
2530
3154
|
const os = (result.stdout ?? "").trim().toLowerCase();
|
|
@@ -2537,7 +3161,7 @@ function setEngineOverride(engine) {
|
|
|
2537
3161
|
cachedEngine = engine;
|
|
2538
3162
|
}
|
|
2539
3163
|
async function getDockerContext() {
|
|
2540
|
-
const result = await
|
|
3164
|
+
const result = await execa3("docker", ["context", "show"], { reject: false });
|
|
2541
3165
|
if (result.exitCode !== 0) return void 0;
|
|
2542
3166
|
const ctx = (result.stdout ?? "").trim();
|
|
2543
3167
|
return ctx.length > 0 ? ctx : void 0;
|
|
@@ -2597,7 +3221,7 @@ async function refreshExport(record, opts = {}) {
|
|
|
2597
3221
|
return { hostPath: paths.mergedExport, copied: true, usedFallback: false };
|
|
2598
3222
|
}
|
|
2599
3223
|
const excludes = excludeNodeModules ? ["--exclude=node_modules"] : [];
|
|
2600
|
-
const result = await
|
|
3224
|
+
const result = await execa3(
|
|
2601
3225
|
"docker",
|
|
2602
3226
|
["exec", "--user", "root", record.container, "tar", "-cf", "-", ...excludes, "-C", "/workspace", "."],
|
|
2603
3227
|
{ reject: false, encoding: "buffer" }
|
|
@@ -2609,7 +3233,7 @@ async function refreshExport(record, opts = {}) {
|
|
|
2609
3233
|
typeof result.stderr === "string" ? result.stderr : result.stderr.toString("utf8")
|
|
2610
3234
|
);
|
|
2611
3235
|
}
|
|
2612
|
-
const extract = await
|
|
3236
|
+
const extract = await execa3("tar", ["-xf", "-", "-C", paths.mergedExport], {
|
|
2613
3237
|
input: result.stdout,
|
|
2614
3238
|
reject: false
|
|
2615
3239
|
});
|
|
@@ -2704,7 +3328,7 @@ function buildHostEnvFindArgs(patterns) {
|
|
|
2704
3328
|
async function copyHostEnvFilesToBox(opts) {
|
|
2705
3329
|
const log = opts.onLog ?? (() => {
|
|
2706
3330
|
});
|
|
2707
|
-
const found = await
|
|
3331
|
+
const found = await execa3("find", buildHostEnvFindArgs(opts.patterns).slice(1), {
|
|
2708
3332
|
cwd: opts.workspaceDir,
|
|
2709
3333
|
reject: false
|
|
2710
3334
|
});
|
|
@@ -2714,7 +3338,7 @@ async function copyHostEnvFilesToBox(opts) {
|
|
|
2714
3338
|
}
|
|
2715
3339
|
const list = String(found.stdout).split("\0").filter((p) => p.length > 0);
|
|
2716
3340
|
if (list.length === 0) return { copied: 0 };
|
|
2717
|
-
const packed = await
|
|
3341
|
+
const packed = await execa3("tar", ["-C", opts.workspaceDir, "--null", "-T", "-", "-cf", "-"], {
|
|
2718
3342
|
input: list.join("\0"),
|
|
2719
3343
|
encoding: "buffer",
|
|
2720
3344
|
reject: false
|
|
@@ -2723,7 +3347,7 @@ async function copyHostEnvFilesToBox(opts) {
|
|
|
2723
3347
|
log(`warning: env-file tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
|
|
2724
3348
|
return { copied: 0 };
|
|
2725
3349
|
}
|
|
2726
|
-
const extract = await
|
|
3350
|
+
const extract = await execa3(
|
|
2727
3351
|
"docker",
|
|
2728
3352
|
["exec", "-i", "--user", "1000:1000", opts.container, "tar", "-xf", "-", "-C", "/workspace"],
|
|
2729
3353
|
{ input: packed.stdout, reject: false }
|
|
@@ -2736,7 +3360,7 @@ async function copyHostEnvFilesToBox(opts) {
|
|
|
2736
3360
|
}
|
|
2737
3361
|
async function scanHostEnvFiles(workspaceDir, patterns) {
|
|
2738
3362
|
if (patterns.length === 0) return [];
|
|
2739
|
-
const found = await
|
|
3363
|
+
const found = await execa3("find", buildHostEnvFindArgs(patterns).slice(1), {
|
|
2740
3364
|
cwd: workspaceDir,
|
|
2741
3365
|
reject: false
|
|
2742
3366
|
});
|
|
@@ -2748,7 +3372,7 @@ async function copyHostFilesToBox(opts) {
|
|
|
2748
3372
|
});
|
|
2749
3373
|
const list = opts.files.map((p) => p.replace(/^\.\//, "")).filter((p) => p.length > 0);
|
|
2750
3374
|
if (list.length === 0) return { copied: 0 };
|
|
2751
|
-
const packed = await
|
|
3375
|
+
const packed = await execa3("tar", ["-C", opts.workspaceDir, "--null", "-T", "-", "-cf", "-"], {
|
|
2752
3376
|
input: list.join("\0"),
|
|
2753
3377
|
encoding: "buffer",
|
|
2754
3378
|
reject: false
|
|
@@ -2757,7 +3381,7 @@ async function copyHostFilesToBox(opts) {
|
|
|
2757
3381
|
log(`warning: env-file tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
|
|
2758
3382
|
return { copied: 0 };
|
|
2759
3383
|
}
|
|
2760
|
-
const extract = await
|
|
3384
|
+
const extract = await execa3(
|
|
2761
3385
|
"docker",
|
|
2762
3386
|
["exec", "-i", "--user", "1000:1000", opts.container, "tar", "-xf", "-", "-C", "/workspace"],
|
|
2763
3387
|
{ input: packed.stdout, reject: false }
|
|
@@ -2831,7 +3455,7 @@ async function pullToHost(record, opts = {}) {
|
|
|
2831
3455
|
}
|
|
2832
3456
|
const src = `${scratchDir}/`;
|
|
2833
3457
|
const dst = `${record.workspacePath}/`;
|
|
2834
|
-
const dry = await
|
|
3458
|
+
const dry = await execa3("rsync", [...baseArgs, "--dry-run", "-i", src, dst], {
|
|
2835
3459
|
reject: false,
|
|
2836
3460
|
input: fileList !== null ? fileList : void 0
|
|
2837
3461
|
});
|
|
@@ -2842,7 +3466,7 @@ async function pullToHost(record, opts = {}) {
|
|
|
2842
3466
|
if (opts.dryRun) {
|
|
2843
3467
|
return { hostPath: record.workspacePath, changes, applied: false, usedGitignore };
|
|
2844
3468
|
}
|
|
2845
|
-
const real = await
|
|
3469
|
+
const real = await execa3("rsync", [...baseArgs, src, dst], {
|
|
2846
3470
|
reject: false,
|
|
2847
3471
|
input: fileList !== null ? fileList : void 0
|
|
2848
3472
|
});
|
|
@@ -2867,7 +3491,7 @@ async function openInFinder(record, opts) {
|
|
|
2867
3491
|
usedFallback = refreshed.usedFallback;
|
|
2868
3492
|
}
|
|
2869
3493
|
if (!opts.noOpen) {
|
|
2870
|
-
const opened = await
|
|
3494
|
+
const opened = await execa3(hostOpenCommand(), [hostPath], { reject: false });
|
|
2871
3495
|
if (opened.exitCode !== 0) {
|
|
2872
3496
|
throw new ExportError(`open ${hostPath} failed`, opened.stdout, opened.stderr);
|
|
2873
3497
|
}
|
|
@@ -2890,12 +3514,14 @@ async function carrySourceHash(entry) {
|
|
|
2890
3514
|
if (entry.kind === "file") {
|
|
2891
3515
|
return createHash3("sha256").update(await readFile5(entry.absSrc)).digest("hex");
|
|
2892
3516
|
}
|
|
3517
|
+
const exclude = entry.exclude ?? [];
|
|
2893
3518
|
const h = createHash3("sha256");
|
|
2894
3519
|
const walk = async (dir, rel) => {
|
|
2895
3520
|
const names = (await readdir4(dir)).sort();
|
|
2896
3521
|
for (const name of names) {
|
|
2897
|
-
const abs = join5(dir, name);
|
|
2898
3522
|
const relPath = rel ? `${rel}/${name}` : name;
|
|
3523
|
+
if (carryRelExcluded(relPath, exclude)) continue;
|
|
3524
|
+
const abs = join5(dir, name);
|
|
2899
3525
|
const st = await stat4(abs);
|
|
2900
3526
|
if (st.isDirectory()) {
|
|
2901
3527
|
h.update(`d\0${relPath}
|
|
@@ -2914,6 +3540,24 @@ async function carrySourceHash(entry) {
|
|
|
2914
3540
|
return void 0;
|
|
2915
3541
|
}
|
|
2916
3542
|
}
|
|
3543
|
+
function carryRelExcluded(relPath, patterns) {
|
|
3544
|
+
if (patterns.length === 0) return false;
|
|
3545
|
+
const segs = relPath.split("/");
|
|
3546
|
+
for (const p of patterns) {
|
|
3547
|
+
const isGlob = p.includes("*") || p.includes("?");
|
|
3548
|
+
if (!isGlob) {
|
|
3549
|
+
if (segs.includes(p)) return true;
|
|
3550
|
+
} else if (p.startsWith("*/") && !/[*?/]/.test(p.slice(2))) {
|
|
3551
|
+
if (segs.includes(p.slice(2))) return true;
|
|
3552
|
+
} else {
|
|
3553
|
+
const re = new RegExp(
|
|
3554
|
+
`^${p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
|
|
3555
|
+
);
|
|
3556
|
+
if (re.test(relPath)) return true;
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
return false;
|
|
3560
|
+
}
|
|
2917
3561
|
async function copyCarryPathsToBox(opts) {
|
|
2918
3562
|
const log = opts.onLog ?? (() => {
|
|
2919
3563
|
});
|
|
@@ -2949,7 +3593,7 @@ async function copyOneEntry(container, entry) {
|
|
|
2949
3593
|
const boxDest = entry.absDest.startsWith("~/") ? `${BOX_HOME}/${entry.absDest.slice(2)}` : entry.absDest;
|
|
2950
3594
|
const boxDestParent = boxDest.endsWith("/") ? boxDest.slice(0, -1) : boxDest;
|
|
2951
3595
|
const parentDir = entry.kind === "dir" ? boxDestParent : dirnameUnix(boxDestParent);
|
|
2952
|
-
const mkdir9 = await
|
|
3596
|
+
const mkdir9 = await execa3(
|
|
2953
3597
|
"docker",
|
|
2954
3598
|
["exec", "--user", "0:0", container, "mkdir", "-p", parentDir],
|
|
2955
3599
|
{ reject: false }
|
|
@@ -2958,24 +3602,50 @@ async function copyOneEntry(container, entry) {
|
|
|
2958
3602
|
throw new Error(`mkdir -p ${parentDir} failed: ${String(mkdir9.stderr).slice(0, 300)}`);
|
|
2959
3603
|
}
|
|
2960
3604
|
if (entry.kind === "file") {
|
|
2961
|
-
const
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
);
|
|
2966
|
-
if (cp2.exitCode !== 0) {
|
|
2967
|
-
throw new Error(`docker cp failed: ${String(cp2.stderr).slice(0, 300)}`);
|
|
2968
|
-
}
|
|
2969
|
-
} else {
|
|
2970
|
-
const packed = await execa22(
|
|
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(
|
|
2971
3609
|
"tar",
|
|
2972
|
-
["-C",
|
|
2973
|
-
|
|
3610
|
+
["-C", srcDir, "-cf", "-", srcBase],
|
|
3611
|
+
"docker",
|
|
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
|
+
]
|
|
2974
3627
|
);
|
|
2975
3628
|
if (packed.exitCode !== 0) {
|
|
2976
3629
|
throw new Error(`tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
|
|
2977
3630
|
}
|
|
2978
|
-
|
|
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
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
} else {
|
|
3645
|
+
const excludeArgs = (entry.exclude ?? []).map((p) => `--exclude=${p}`);
|
|
3646
|
+
const [packed, extract] = await streamTarPipe(
|
|
3647
|
+
"tar",
|
|
3648
|
+
["-C", entry.absSrc, "-cf", "-", ...excludeArgs, "."],
|
|
2979
3649
|
"docker",
|
|
2980
3650
|
[
|
|
2981
3651
|
"exec",
|
|
@@ -2991,16 +3661,18 @@ async function copyOneEntry(container, entry) {
|
|
|
2991
3661
|
"--no-same-permissions",
|
|
2992
3662
|
"--no-same-owner",
|
|
2993
3663
|
"-m"
|
|
2994
|
-
]
|
|
2995
|
-
{ input: packed.stdout, reject: false }
|
|
3664
|
+
]
|
|
2996
3665
|
);
|
|
3666
|
+
if (packed.exitCode !== 0) {
|
|
3667
|
+
throw new Error(`tar pack failed: ${String(packed.stderr).slice(0, 300)}`);
|
|
3668
|
+
}
|
|
2997
3669
|
if (extract.exitCode !== 0) {
|
|
2998
3670
|
throw new Error(`tar extract failed: ${String(extract.stderr).slice(0, 300)}`);
|
|
2999
3671
|
}
|
|
3000
3672
|
}
|
|
3001
3673
|
if (entry.mode !== void 0) {
|
|
3002
3674
|
const modeStr = entry.mode.toString(8).padStart(4, "0");
|
|
3003
|
-
const chmod2 = await
|
|
3675
|
+
const chmod2 = await execa3(
|
|
3004
3676
|
"docker",
|
|
3005
3677
|
["exec", "--user", "0:0", container, "chmod", "-R", modeStr, boxDest],
|
|
3006
3678
|
{ reject: false }
|
|
@@ -3010,7 +3682,7 @@ async function copyOneEntry(container, entry) {
|
|
|
3010
3682
|
}
|
|
3011
3683
|
}
|
|
3012
3684
|
const uid = entry.user ?? 1e3;
|
|
3013
|
-
const chown = await
|
|
3685
|
+
const chown = await execa3(
|
|
3014
3686
|
"docker",
|
|
3015
3687
|
["exec", "--user", "0:0", container, "chown", "-R", `${String(uid)}:${String(uid)}`, boxDest],
|
|
3016
3688
|
{ reject: false }
|
|
@@ -3021,7 +3693,7 @@ async function copyOneEntry(container, entry) {
|
|
|
3021
3693
|
if (boxDest.startsWith(BOX_HOME + "/") && dirnameUnix(boxDest) !== BOX_HOME) {
|
|
3022
3694
|
const safeDest = boxDest.replace(/'/g, `'\\''`);
|
|
3023
3695
|
const script = `set -e; parent="$(dirname '${safeDest}')"; while [ "$parent" != "${BOX_HOME}" ] && [ "$parent" != "/" ]; do chown ${String(uid)}:${String(uid)} "$parent"; parent="$(dirname "$parent")"; done`;
|
|
3024
|
-
const chownParents = await
|
|
3696
|
+
const chownParents = await execa3(
|
|
3025
3697
|
"docker",
|
|
3026
3698
|
["exec", "--user", "0:0", container, "bash", "-c", script],
|
|
3027
3699
|
{ reject: false }
|
|
@@ -3069,7 +3741,7 @@ async function extractVolumeAuthToBackup(opts) {
|
|
|
3069
3741
|
try {
|
|
3070
3742
|
await mkdir22(STATE_DIR, { recursive: true });
|
|
3071
3743
|
const script = 'COPIED=no; if [ -s /dst/auth.json ]; then cp -a /dst/auth.json "/host-state/$DEST" && COPIED=yes; fi; echo "COPIED=$COPIED"';
|
|
3072
|
-
const { stdout } = await
|
|
3744
|
+
const { stdout } = await execa4("docker", [
|
|
3073
3745
|
"run",
|
|
3074
3746
|
"--rm",
|
|
3075
3747
|
"--user",
|
|
@@ -3081,7 +3753,7 @@ async function extractVolumeAuthToBackup(opts) {
|
|
|
3081
3753
|
"-e",
|
|
3082
3754
|
// Pass the destination filename via env so the path isn't interpolated
|
|
3083
3755
|
// into the script string (keeps the docker arg list static + injection-safe).
|
|
3084
|
-
`DEST=${
|
|
3756
|
+
`DEST=${basename32(opts.backupFile)}`,
|
|
3085
3757
|
opts.image,
|
|
3086
3758
|
"sh",
|
|
3087
3759
|
"-c",
|
|
@@ -3133,7 +3805,7 @@ echo "EXTRACTED=$EXTRACTED SEEDED=$SEEDED VOLREAL=$VOL_REAL"
|
|
|
3133
3805
|
async function syncClaudeCredentials(spec, opts) {
|
|
3134
3806
|
try {
|
|
3135
3807
|
await mkdir22(STATE_DIR, { recursive: true });
|
|
3136
|
-
const { stdout } = await
|
|
3808
|
+
const { stdout } = await execa4("docker", [
|
|
3137
3809
|
"run",
|
|
3138
3810
|
"--rm",
|
|
3139
3811
|
"--user",
|
|
@@ -3201,8 +3873,8 @@ function emptyResult(warnings = []) {
|
|
|
3201
3873
|
}, warnings };
|
|
3202
3874
|
}
|
|
3203
3875
|
async function tarballFromDir(stageDir, agent) {
|
|
3204
|
-
const tarballPath = join32(tmpdir(), `agentbox-${agent}-${
|
|
3205
|
-
await
|
|
3876
|
+
const tarballPath = join32(tmpdir(), `agentbox-${agent}-${basename4(stageDir)}.tar.gz`);
|
|
3877
|
+
await execa5("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
|
|
3206
3878
|
env: { ...process.env, COPYFILE_DISABLE: "1" }
|
|
3207
3879
|
});
|
|
3208
3880
|
return tarballPath;
|
|
@@ -3347,7 +4019,7 @@ async function stageClaudeStaticForUpload(opts = {}) {
|
|
|
3347
4019
|
...CLAUDE_RUNTIME_EXCLUDES.map((p) => `--exclude=${p}`),
|
|
3348
4020
|
...broken.map((r) => `--exclude=/${r}`)
|
|
3349
4021
|
];
|
|
3350
|
-
await
|
|
4022
|
+
await execa5("rsync", [
|
|
3351
4023
|
"-a",
|
|
3352
4024
|
"--copy-unsafe-links",
|
|
3353
4025
|
...excludes,
|
|
@@ -3436,7 +4108,7 @@ async function stageCodexStaticForUpload(opts = {}) {
|
|
|
3436
4108
|
let tarballPath = null;
|
|
3437
4109
|
try {
|
|
3438
4110
|
const codexBroken = await findBrokenSymlinks(hostCodex);
|
|
3439
|
-
await
|
|
4111
|
+
await execa5("rsync", [
|
|
3440
4112
|
"-a",
|
|
3441
4113
|
"-L",
|
|
3442
4114
|
...codexBroken.map((r) => `--exclude=/${r}`),
|
|
@@ -3492,7 +4164,7 @@ async function stageOpencodeStaticForUpload(opts = {}) {
|
|
|
3492
4164
|
try {
|
|
3493
4165
|
if (hasData) {
|
|
3494
4166
|
const dataBroken = await findBrokenSymlinks(hostData);
|
|
3495
|
-
await
|
|
4167
|
+
await execa5("rsync", [
|
|
3496
4168
|
"-a",
|
|
3497
4169
|
"-L",
|
|
3498
4170
|
...dataBroken.map((r) => `--exclude=/${r}`),
|
|
@@ -3505,7 +4177,7 @@ async function stageOpencodeStaticForUpload(opts = {}) {
|
|
|
3505
4177
|
if (hasConfig) {
|
|
3506
4178
|
const configStage = join32(stageDir, "config");
|
|
3507
4179
|
const cfgBroken = await findBrokenSymlinks(hostConfig);
|
|
3508
|
-
await
|
|
4180
|
+
await execa5("rsync", [
|
|
3509
4181
|
"-a",
|
|
3510
4182
|
"-L",
|
|
3511
4183
|
...cfgBroken.map((r) => `--exclude=/${r}`),
|
|
@@ -3563,7 +4235,7 @@ async function pathExists2(p) {
|
|
|
3563
4235
|
}
|
|
3564
4236
|
}
|
|
3565
4237
|
async function volumeHasClaudeJson(volume, image) {
|
|
3566
|
-
const res = await
|
|
4238
|
+
const res = await execa6(
|
|
3567
4239
|
"docker",
|
|
3568
4240
|
["run", "--rm", "-v", `${volume}:/dst`, image, "sh", "-c", "test -e /dst/_claude.json"],
|
|
3569
4241
|
{ reject: false }
|
|
@@ -3733,7 +4405,7 @@ async function ensureClaudeVolume(spec, opts) {
|
|
|
3733
4405
|
// linux/amd64 node_modules on the next `agentbox claude`.
|
|
3734
4406
|
`{ [ ! -f /dst/.agentbox-cleaned-nm-v1 ] && find /dst -name node_modules -type d -prune -exec rm -rf {} + && touch /dst/.agentbox-cleaned-nm-v1; true; } && rsync ${rsyncFlags} /src-claude/ /dst/ && { [ -f /src-claude-json ] && cp -a /src-claude-json /dst/_claude.json; true; } && { [ -f /src-filter/settings.json ] && cp -a /src-filter/settings.json /dst/settings.json; true; } && { [ -f /src-filter/_claude.json ] && cp -a /src-filter/_claude.json /dst/_claude.json; true; } && { [ -d /dst/plugins ] && [ -n "$HOST_HOME" ] && find /dst/plugins -maxdepth 1 -type f -name "*.json" -exec sed -i "s|$HOST_HOME/.claude/plugins/|/home/vscode/.claude/plugins/|g" {} +; true; }` + memoryRekeyStep + " && chown -R 1000:1000 /dst"
|
|
3735
4407
|
);
|
|
3736
|
-
await
|
|
4408
|
+
await execa6("docker", args);
|
|
3737
4409
|
} finally {
|
|
3738
4410
|
await rm2(filterDir, { recursive: true, force: true });
|
|
3739
4411
|
}
|
|
@@ -3748,7 +4420,7 @@ async function ensureClaudeVolume(spec, opts) {
|
|
|
3748
4420
|
}
|
|
3749
4421
|
async function seedSetupSkillIntoVolume(volume, image) {
|
|
3750
4422
|
try {
|
|
3751
|
-
const { stdout } = await
|
|
4423
|
+
const { stdout } = await execa6("docker", [
|
|
3752
4424
|
"run",
|
|
3753
4425
|
"--rm",
|
|
3754
4426
|
"--user",
|
|
@@ -3913,7 +4585,7 @@ async function resolveClaudeCacheLiveOnHost(volume) {
|
|
|
3913
4585
|
return orbstackVolumePath(volume, "plugins", "cache");
|
|
3914
4586
|
}
|
|
3915
4587
|
async function readBoxReferencedPluginKeys(container) {
|
|
3916
|
-
const res = await
|
|
4588
|
+
const res = await execa6(
|
|
3917
4589
|
"docker",
|
|
3918
4590
|
[
|
|
3919
4591
|
"exec",
|
|
@@ -4031,7 +4703,7 @@ while IFS= read -r dir; do
|
|
|
4031
4703
|
done < "$WORK/dirs"
|
|
4032
4704
|
rm -rf "$WORK"
|
|
4033
4705
|
`;
|
|
4034
|
-
const result = await
|
|
4706
|
+
const result = await execa6(
|
|
4035
4707
|
"docker",
|
|
4036
4708
|
["exec", "--user", CONTAINER_USER, container, "sh", "-c", script],
|
|
4037
4709
|
{ reject: false }
|
|
@@ -4078,21 +4750,21 @@ var ClaudeSessionError = class extends Error {
|
|
|
4078
4750
|
this.name = "ClaudeSessionError";
|
|
4079
4751
|
}
|
|
4080
4752
|
};
|
|
4081
|
-
function
|
|
4753
|
+
function shQuote2(arg) {
|
|
4082
4754
|
if (arg.length === 0) return `''`;
|
|
4083
4755
|
if (/^[A-Za-z0-9_\-./=:@%+,]+$/.test(arg)) return arg;
|
|
4084
4756
|
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
4085
4757
|
}
|
|
4086
4758
|
async function startClaudeSession(opts) {
|
|
4087
4759
|
const sessionName = opts.sessionName ?? DEFAULT_CLAUDE_SESSION;
|
|
4088
|
-
const cmd = ["claude", ...opts.claudeArgs].map(
|
|
4760
|
+
const cmd = ["claude", ...opts.claudeArgs].map(shQuote2).join(" ");
|
|
4089
4761
|
const term = process.env["TERM"] ?? "xterm-256color";
|
|
4090
4762
|
const envFlags = ["-e", `TERM=${term}`];
|
|
4091
4763
|
for (const k of FORWARDED_ENV_KEYS) {
|
|
4092
4764
|
const v = process.env[k];
|
|
4093
4765
|
if (typeof v === "string" && v.length > 0) envFlags.push("-e", `${k}=${v}`);
|
|
4094
4766
|
}
|
|
4095
|
-
const result = await
|
|
4767
|
+
const result = await execa6(
|
|
4096
4768
|
"docker",
|
|
4097
4769
|
[
|
|
4098
4770
|
"exec",
|
|
@@ -4183,7 +4855,7 @@ function buildDashboardAttachArgv(container, sessionName) {
|
|
|
4183
4855
|
async function waitForTmuxPaneContent(container, sessionName, timeoutMs = 2e4) {
|
|
4184
4856
|
const deadline = Date.now() + timeoutMs;
|
|
4185
4857
|
while (Date.now() < deadline) {
|
|
4186
|
-
const res = await
|
|
4858
|
+
const res = await execa6(
|
|
4187
4859
|
"docker",
|
|
4188
4860
|
["exec", "--user", CONTAINER_USER, container, "tmux", "capture-pane", "-p", "-t", sessionName],
|
|
4189
4861
|
{ reject: false }
|
|
@@ -4251,7 +4923,7 @@ async function warmUpClaudeCredentials(volume, image, opts = {}) {
|
|
|
4251
4923
|
const SLEEP_MS = 5e3;
|
|
4252
4924
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
4253
4925
|
opts.onProgress?.(`checking credentials... ${attempt}/${MAX_ATTEMPTS}`);
|
|
4254
|
-
const res = await
|
|
4926
|
+
const res = await execa6(
|
|
4255
4927
|
"docker",
|
|
4256
4928
|
[
|
|
4257
4929
|
"run",
|
|
@@ -4293,7 +4965,7 @@ function attachClaudeSession(container, sessionName, reattachRef) {
|
|
|
4293
4965
|
}
|
|
4294
4966
|
async function claudeSessionInfo(container, sessionName) {
|
|
4295
4967
|
const name = sessionName ?? DEFAULT_CLAUDE_SESSION;
|
|
4296
|
-
const has = await
|
|
4968
|
+
const has = await execa6(
|
|
4297
4969
|
"docker",
|
|
4298
4970
|
["exec", "--user", CONTAINER_USER, container, "tmux", "has-session", "-t", name],
|
|
4299
4971
|
{ reject: false }
|
|
@@ -4301,7 +4973,7 @@ async function claudeSessionInfo(container, sessionName) {
|
|
|
4301
4973
|
if (has.exitCode !== 0) {
|
|
4302
4974
|
return { running: false, sessionName: name, startedAt: null };
|
|
4303
4975
|
}
|
|
4304
|
-
const ts = await
|
|
4976
|
+
const ts = await execa6(
|
|
4305
4977
|
"docker",
|
|
4306
4978
|
[
|
|
4307
4979
|
"exec",
|
|
@@ -4367,7 +5039,7 @@ async function pullClaudeExtras(spec, opts) {
|
|
|
4367
5039
|
' printf "\\n";',
|
|
4368
5040
|
"done"
|
|
4369
5041
|
].join(" ");
|
|
4370
|
-
const inv = await
|
|
5042
|
+
const inv = await execa6(
|
|
4371
5043
|
"docker",
|
|
4372
5044
|
["run", "--rm", "--user", "0", "-v", `${spec.volume}:/src:ro`, opts.image, "sh", "-c", inventoryScript],
|
|
4373
5045
|
{ reject: false }
|
|
@@ -4440,7 +5112,7 @@ async function pullClaudeExtras(spec, opts) {
|
|
|
4440
5112
|
const parent = dest.slice(0, dest.lastIndexOf("/"));
|
|
4441
5113
|
return `mkdir -p '${parent}' && rsync -a --ignore-existing --exclude=node_modules '${src}/' '${dest}/'`;
|
|
4442
5114
|
});
|
|
4443
|
-
const apply = await
|
|
5115
|
+
const apply = await execa6(
|
|
4444
5116
|
"docker",
|
|
4445
5117
|
[
|
|
4446
5118
|
"run",
|
|
@@ -4501,7 +5173,7 @@ async function pathExists3(p) {
|
|
|
4501
5173
|
return false;
|
|
4502
5174
|
}
|
|
4503
5175
|
}
|
|
4504
|
-
function
|
|
5176
|
+
function shQuote22(arg) {
|
|
4505
5177
|
if (arg.length === 0) return `''`;
|
|
4506
5178
|
if (/^[A-Za-z0-9_\-./=:@%+,]+$/.test(arg)) return arg;
|
|
4507
5179
|
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
@@ -4513,7 +5185,7 @@ async function ensureCodexVolume(spec, opts) {
|
|
|
4513
5185
|
const hostCodex = join52(homedir4(), ".codex");
|
|
4514
5186
|
const willSync = opts.syncFromHost && await pathExists3(hostCodex);
|
|
4515
5187
|
if (willSync) {
|
|
4516
|
-
await
|
|
5188
|
+
await execa7("docker", [
|
|
4517
5189
|
"run",
|
|
4518
5190
|
"--rm",
|
|
4519
5191
|
"--user",
|
|
@@ -4531,7 +5203,7 @@ async function ensureCodexVolume(spec, opts) {
|
|
|
4531
5203
|
]);
|
|
4532
5204
|
return { created, synced: true };
|
|
4533
5205
|
}
|
|
4534
|
-
await
|
|
5206
|
+
await execa7(
|
|
4535
5207
|
"docker",
|
|
4536
5208
|
[
|
|
4537
5209
|
"run",
|
|
@@ -4551,7 +5223,7 @@ async function ensureCodexVolume(spec, opts) {
|
|
|
4551
5223
|
}
|
|
4552
5224
|
async function seedCodexHooks(volume, image) {
|
|
4553
5225
|
try {
|
|
4554
|
-
const { stdout } = await
|
|
5226
|
+
const { stdout } = await execa7("docker", [
|
|
4555
5227
|
"run",
|
|
4556
5228
|
"--rm",
|
|
4557
5229
|
"--user",
|
|
@@ -4588,14 +5260,14 @@ var CodexSessionError = class extends Error {
|
|
|
4588
5260
|
}
|
|
4589
5261
|
};
|
|
4590
5262
|
async function ensureCodexInstalled(container, opts = {}) {
|
|
4591
|
-
const probe = await
|
|
5263
|
+
const probe = await execa7(
|
|
4592
5264
|
"docker",
|
|
4593
5265
|
["exec", "--user", CONTAINER_USER, container, "sh", "-c", "command -v codex"],
|
|
4594
5266
|
{ reject: false }
|
|
4595
5267
|
);
|
|
4596
5268
|
if (probe.exitCode === 0) return { installed: false };
|
|
4597
5269
|
opts.onProgress?.("installing codex (absent from this box image)");
|
|
4598
|
-
const install = await
|
|
5270
|
+
const install = await execa7(
|
|
4599
5271
|
"docker",
|
|
4600
5272
|
["exec", "--user", "root", container, "bash", "-lc", "npm install -g @openai/codex 2>&1"],
|
|
4601
5273
|
{ reject: false }
|
|
@@ -4611,14 +5283,14 @@ ${(install.stdout ?? "").toString().slice(-600)}`
|
|
|
4611
5283
|
var CODEX_AGENTBOX_FLAGS = ["--enable", "hooks", "--dangerously-bypass-hook-trust"];
|
|
4612
5284
|
async function startCodexSession(opts) {
|
|
4613
5285
|
const sessionName = opts.sessionName ?? DEFAULT_CODEX_SESSION;
|
|
4614
|
-
const cmd = ["codex", ...CODEX_AGENTBOX_FLAGS, ...opts.codexArgs].map(
|
|
5286
|
+
const cmd = ["codex", ...CODEX_AGENTBOX_FLAGS, ...opts.codexArgs].map(shQuote22).join(" ");
|
|
4615
5287
|
const term = process.env["TERM"] ?? "xterm-256color";
|
|
4616
5288
|
const envFlags = ["-e", `TERM=${term}`];
|
|
4617
5289
|
for (const k of CODEX_FORWARDED_ENV_KEYS) {
|
|
4618
5290
|
const v = process.env[k];
|
|
4619
5291
|
if (typeof v === "string" && v.length > 0) envFlags.push("-e", `${k}=${v}`);
|
|
4620
5292
|
}
|
|
4621
|
-
const result = await
|
|
5293
|
+
const result = await execa7(
|
|
4622
5294
|
"docker",
|
|
4623
5295
|
[
|
|
4624
5296
|
"exec",
|
|
@@ -4700,7 +5372,7 @@ function runInteractiveCodexLogin(dockerArgv) {
|
|
|
4700
5372
|
return { exitCode: child.status ?? 1 };
|
|
4701
5373
|
}
|
|
4702
5374
|
async function volumeHasCodexAuth(volume, image) {
|
|
4703
|
-
const res = await
|
|
5375
|
+
const res = await execa7(
|
|
4704
5376
|
"docker",
|
|
4705
5377
|
["run", "--rm", "-v", `${volume}:/dst`, image, "sh", "-c", "test -e /dst/auth.json"],
|
|
4706
5378
|
{ reject: false }
|
|
@@ -4709,7 +5381,7 @@ async function volumeHasCodexAuth(volume, image) {
|
|
|
4709
5381
|
}
|
|
4710
5382
|
async function codexSessionInfo(container, sessionName) {
|
|
4711
5383
|
const name = sessionName ?? DEFAULT_CODEX_SESSION;
|
|
4712
|
-
const has = await
|
|
5384
|
+
const has = await execa7(
|
|
4713
5385
|
"docker",
|
|
4714
5386
|
["exec", "--user", CONTAINER_USER, container, "tmux", "has-session", "-t", name],
|
|
4715
5387
|
{ reject: false }
|
|
@@ -4717,7 +5389,7 @@ async function codexSessionInfo(container, sessionName) {
|
|
|
4717
5389
|
if (has.exitCode !== 0) {
|
|
4718
5390
|
return { running: false, sessionName: name, startedAt: null };
|
|
4719
5391
|
}
|
|
4720
|
-
const ts = await
|
|
5392
|
+
const ts = await execa7(
|
|
4721
5393
|
"docker",
|
|
4722
5394
|
[
|
|
4723
5395
|
"exec",
|
|
@@ -4743,7 +5415,7 @@ async function codexSessionInfo(container, sessionName) {
|
|
|
4743
5415
|
var CODEX_PULL_ITEMS = ["config.toml", "auth.json", "prompts"];
|
|
4744
5416
|
async function pullCodexConfig(spec, opts) {
|
|
4745
5417
|
const hostCodex = join52(homedir4(), ".codex");
|
|
4746
|
-
const inv = await
|
|
5418
|
+
const inv = await execa7(
|
|
4747
5419
|
"docker",
|
|
4748
5420
|
[
|
|
4749
5421
|
"run",
|
|
@@ -4777,7 +5449,7 @@ async function pullCodexConfig(spec, opts) {
|
|
|
4777
5449
|
const uid = process.getuid?.() ?? 0;
|
|
4778
5450
|
const gid = process.getgid?.() ?? 0;
|
|
4779
5451
|
const cmds = newItems.map((it) => `cp -a '/src/${it}' '/dst/${it}'`);
|
|
4780
|
-
const apply = await
|
|
5452
|
+
const apply = await execa7(
|
|
4781
5453
|
"docker",
|
|
4782
5454
|
[
|
|
4783
5455
|
"run",
|
|
@@ -4859,10 +5531,10 @@ async function ensureOpencodeVolume(spec, opts) {
|
|
|
4859
5531
|
}
|
|
4860
5532
|
steps.push("chown -R 1000:1000 /dst");
|
|
4861
5533
|
args.push(opts.image, "sh", "-c", steps.join(" && "));
|
|
4862
|
-
await
|
|
5534
|
+
await execa8("docker", args);
|
|
4863
5535
|
return { created, synced: true };
|
|
4864
5536
|
}
|
|
4865
|
-
await
|
|
5537
|
+
await execa8(
|
|
4866
5538
|
"docker",
|
|
4867
5539
|
[
|
|
4868
5540
|
"run",
|
|
@@ -4882,7 +5554,7 @@ async function ensureOpencodeVolume(spec, opts) {
|
|
|
4882
5554
|
}
|
|
4883
5555
|
async function seedOpencodePlugin(volume, image) {
|
|
4884
5556
|
try {
|
|
4885
|
-
const { stdout } = await
|
|
5557
|
+
const { stdout } = await execa8("docker", [
|
|
4886
5558
|
"run",
|
|
4887
5559
|
"--rm",
|
|
4888
5560
|
"--user",
|
|
@@ -4930,14 +5602,14 @@ var OpencodeSessionError = class extends Error {
|
|
|
4930
5602
|
}
|
|
4931
5603
|
};
|
|
4932
5604
|
async function ensureOpencodeInstalled(container, opts = {}) {
|
|
4933
|
-
const probe = await
|
|
5605
|
+
const probe = await execa8(
|
|
4934
5606
|
"docker",
|
|
4935
5607
|
["exec", "--user", CONTAINER_USER, container, "sh", "-c", "command -v opencode"],
|
|
4936
5608
|
{ reject: false }
|
|
4937
5609
|
);
|
|
4938
5610
|
if (probe.exitCode === 0) return { installed: false };
|
|
4939
5611
|
opts.onProgress?.("installing opencode (absent from this box image)");
|
|
4940
|
-
const install = await
|
|
5612
|
+
const install = await execa8(
|
|
4941
5613
|
"docker",
|
|
4942
5614
|
["exec", "--user", "root", container, "bash", "-lc", "npm install -g opencode-ai 2>&1"],
|
|
4943
5615
|
{ reject: false }
|
|
@@ -4959,7 +5631,7 @@ async function startOpencodeSession(opts) {
|
|
|
4959
5631
|
const v = process.env[k];
|
|
4960
5632
|
if (typeof v === "string" && v.length > 0) envFlags.push("-e", `${k}=${v}`);
|
|
4961
5633
|
}
|
|
4962
|
-
const result = await
|
|
5634
|
+
const result = await execa8(
|
|
4963
5635
|
"docker",
|
|
4964
5636
|
[
|
|
4965
5637
|
"exec",
|
|
@@ -5045,7 +5717,7 @@ function runInteractiveOpencodeLogin(dockerArgv) {
|
|
|
5045
5717
|
return { exitCode: child.status ?? 1 };
|
|
5046
5718
|
}
|
|
5047
5719
|
async function volumeHasOpencodeAuth(volume, image) {
|
|
5048
|
-
const res = await
|
|
5720
|
+
const res = await execa8(
|
|
5049
5721
|
"docker",
|
|
5050
5722
|
["run", "--rm", "-v", `${volume}:/dst`, image, "sh", "-c", "test -e /dst/auth.json"],
|
|
5051
5723
|
{ reject: false }
|
|
@@ -5054,7 +5726,7 @@ async function volumeHasOpencodeAuth(volume, image) {
|
|
|
5054
5726
|
}
|
|
5055
5727
|
async function opencodeSessionInfo(container, sessionName) {
|
|
5056
5728
|
const name = sessionName ?? DEFAULT_OPENCODE_SESSION;
|
|
5057
|
-
const has = await
|
|
5729
|
+
const has = await execa8(
|
|
5058
5730
|
"docker",
|
|
5059
5731
|
["exec", "--user", CONTAINER_USER, container, "tmux", "has-session", "-t", name],
|
|
5060
5732
|
{ reject: false }
|
|
@@ -5062,7 +5734,7 @@ async function opencodeSessionInfo(container, sessionName) {
|
|
|
5062
5734
|
if (has.exitCode !== 0) {
|
|
5063
5735
|
return { running: false, sessionName: name, startedAt: null };
|
|
5064
5736
|
}
|
|
5065
|
-
const ts = await
|
|
5737
|
+
const ts = await execa8(
|
|
5066
5738
|
"docker",
|
|
5067
5739
|
[
|
|
5068
5740
|
"exec",
|
|
@@ -5100,7 +5772,7 @@ var OPENCODE_PULL_CONFIG_ITEMS = [
|
|
|
5100
5772
|
async function pullOpencodeConfig(spec, opts) {
|
|
5101
5773
|
const hostData = join6(homedir5(), ".local", "share", "opencode");
|
|
5102
5774
|
const hostConfig = join6(homedir5(), ".config", "opencode");
|
|
5103
|
-
const inv = await
|
|
5775
|
+
const inv = await execa8(
|
|
5104
5776
|
"docker",
|
|
5105
5777
|
[
|
|
5106
5778
|
"run",
|
|
@@ -5142,7 +5814,7 @@ async function pullOpencodeConfig(spec, opts) {
|
|
|
5142
5814
|
const cmds = newItems.map(
|
|
5143
5815
|
(i) => `cp -a '${i.src}' '${i.hostDst === "data" ? "/dst-data" : "/dst-config"}/${i.name}'`
|
|
5144
5816
|
);
|
|
5145
|
-
const apply = await
|
|
5817
|
+
const apply = await execa8(
|
|
5146
5818
|
"docker",
|
|
5147
5819
|
[
|
|
5148
5820
|
"run",
|
|
@@ -5219,7 +5891,7 @@ async function launchVncDaemon(container, timeoutMs = 5e3) {
|
|
|
5219
5891
|
}
|
|
5220
5892
|
var VNC_PASSWORD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
5221
5893
|
function generateVncPassword() {
|
|
5222
|
-
const bytes =
|
|
5894
|
+
const bytes = randomBytes2(8);
|
|
5223
5895
|
let out = "";
|
|
5224
5896
|
for (let i = 0; i < 8; i++) {
|
|
5225
5897
|
out += VNC_PASSWORD_ALPHABET[bytes[i] % VNC_PASSWORD_ALPHABET.length];
|
|
@@ -5253,9 +5925,9 @@ function gitWorktreePathFor(branch) {
|
|
|
5253
5925
|
return `${WORKTREE_ROOT}/${fsSafeBranch(branch)}`;
|
|
5254
5926
|
}
|
|
5255
5927
|
async function collectRepoCarryOver(repo, branch, containerPath, gitWorktreePath) {
|
|
5256
|
-
const stash = await
|
|
5928
|
+
const stash = await execa9("git", ["-C", repo.hostMainRepo, "stash", "create"], { reject: false });
|
|
5257
5929
|
const stashSha = stash.exitCode === 0 ? stash.stdout.trim() || null : null;
|
|
5258
|
-
const untracked = await
|
|
5930
|
+
const untracked = await execa9(
|
|
5259
5931
|
"git",
|
|
5260
5932
|
["-C", repo.hostMainRepo, "ls-files", "--others", "--exclude-standard", "-z"],
|
|
5261
5933
|
{ reject: false }
|
|
@@ -5272,7 +5944,7 @@ async function collectRepoCarryOver(repo, branch, containerPath, gitWorktreePath
|
|
|
5272
5944
|
};
|
|
5273
5945
|
}
|
|
5274
5946
|
async function dexec(container, argv, user = "vscode", cwd = "/") {
|
|
5275
|
-
const r = await
|
|
5947
|
+
const r = await execa9(
|
|
5276
5948
|
"docker",
|
|
5277
5949
|
["exec", "-w", cwd, "--user", user, container, ...argv],
|
|
5278
5950
|
{ reject: false }
|
|
@@ -5304,7 +5976,7 @@ async function bindWorktrees(container, binds, onLog) {
|
|
|
5304
5976
|
(a, b) => a.kind === "root" && b.kind !== "root" ? -1 : a.kind !== "root" && b.kind === "root" ? 1 : 0
|
|
5305
5977
|
);
|
|
5306
5978
|
for (const b of ordered) {
|
|
5307
|
-
await
|
|
5979
|
+
await execa9(
|
|
5308
5980
|
"docker",
|
|
5309
5981
|
["exec", "-w", "/", "--user", "root", container, "sh", "-c", `mountpoint -q ${b.containerPath} && umount ${b.containerPath} || true`],
|
|
5310
5982
|
{ reject: false }
|
|
@@ -5326,7 +5998,7 @@ async function seedWorkspace(opts) {
|
|
|
5326
5998
|
const wt = r.gitWorktreePath;
|
|
5327
5999
|
const baseRef = r.repo.kind === "root" ? opts.fromBranch ?? "HEAD" : "HEAD";
|
|
5328
6000
|
const addArgs = r.reuseBranch ? ["worktree", "add", wt, r.branch] : ["worktree", "add", "-b", r.branch, wt, baseRef];
|
|
5329
|
-
const add = await
|
|
6001
|
+
const add = await execa9(
|
|
5330
6002
|
"docker",
|
|
5331
6003
|
["exec", "--user", "vscode", opts.container, "git", "-C", main, ...addArgs],
|
|
5332
6004
|
{ reject: false }
|
|
@@ -5337,7 +6009,7 @@ async function seedWorkspace(opts) {
|
|
|
5337
6009
|
);
|
|
5338
6010
|
}
|
|
5339
6011
|
log(`worktree ${wt} on branch ${r.branch} (host main ${main})`);
|
|
5340
|
-
await
|
|
6012
|
+
await execa9(
|
|
5341
6013
|
"docker",
|
|
5342
6014
|
[
|
|
5343
6015
|
"exec",
|
|
@@ -5353,7 +6025,7 @@ async function seedWorkspace(opts) {
|
|
|
5353
6025
|
],
|
|
5354
6026
|
{ reject: false }
|
|
5355
6027
|
);
|
|
5356
|
-
await
|
|
6028
|
+
await execa9(
|
|
5357
6029
|
"docker",
|
|
5358
6030
|
[
|
|
5359
6031
|
"exec",
|
|
@@ -5383,7 +6055,7 @@ async function seedWorkspace(opts) {
|
|
|
5383
6055
|
for (const r of opts.repos) {
|
|
5384
6056
|
const ct = r.containerPath;
|
|
5385
6057
|
if (r.stashSha) {
|
|
5386
|
-
const withIndex = await
|
|
6058
|
+
const withIndex = await execa9(
|
|
5387
6059
|
"docker",
|
|
5388
6060
|
[
|
|
5389
6061
|
"exec",
|
|
@@ -5401,7 +6073,7 @@ async function seedWorkspace(opts) {
|
|
|
5401
6073
|
{ reject: false }
|
|
5402
6074
|
);
|
|
5403
6075
|
if (withIndex.exitCode !== 0) {
|
|
5404
|
-
const noIndex = await
|
|
6076
|
+
const noIndex = await execa9(
|
|
5405
6077
|
"docker",
|
|
5406
6078
|
[
|
|
5407
6079
|
"exec",
|
|
@@ -5429,7 +6101,7 @@ async function seedWorkspace(opts) {
|
|
|
5429
6101
|
}
|
|
5430
6102
|
}
|
|
5431
6103
|
if (r.untrackedNul.length > 0) {
|
|
5432
|
-
const tarOut = await
|
|
6104
|
+
const tarOut = await execa9("tar", ["-C", r.hostSource, "--null", "-T", "-", "-cf", "-"], {
|
|
5433
6105
|
input: r.untrackedNul.replace(/\0$/, ""),
|
|
5434
6106
|
encoding: "buffer",
|
|
5435
6107
|
reject: false
|
|
@@ -5438,7 +6110,7 @@ async function seedWorkspace(opts) {
|
|
|
5438
6110
|
log(`warning: tar of untracked files for ${r.repo.hostMainRepo} failed: ${tarOut.stderr}`);
|
|
5439
6111
|
continue;
|
|
5440
6112
|
}
|
|
5441
|
-
const tarIn = await
|
|
6113
|
+
const tarIn = await execa9(
|
|
5442
6114
|
"docker",
|
|
5443
6115
|
["exec", "-i", "--user", "vscode", opts.container, "tar", "-C", ct, "-xf", "-"],
|
|
5444
6116
|
{ input: tarOut.stdout, reject: false }
|
|
@@ -5491,7 +6163,7 @@ async function regenerateRestoredWorktrees(opts) {
|
|
|
5491
6163
|
for (const p of opts.plans) {
|
|
5492
6164
|
const baseRef = p.kind === "root" ? opts.fromBranch ?? "HEAD" : "HEAD";
|
|
5493
6165
|
const metaDir = `${p.hostMainRepo}/.git/worktrees/${fsSafeBranch(p.freshBranch)}`;
|
|
5494
|
-
const r = await
|
|
6166
|
+
const r = await execa9(
|
|
5495
6167
|
"docker",
|
|
5496
6168
|
[
|
|
5497
6169
|
"exec",
|
|
@@ -5524,14 +6196,14 @@ async function regenerateRestoredWorktrees(opts) {
|
|
|
5524
6196
|
async function seedWorkspaceFromDir(opts) {
|
|
5525
6197
|
const log = opts.onLog ?? (() => {
|
|
5526
6198
|
});
|
|
5527
|
-
const tarOut = await
|
|
6199
|
+
const tarOut = await execa9("tar", ["-C", opts.hostSource, "-cf", "-", "."], {
|
|
5528
6200
|
encoding: "buffer",
|
|
5529
6201
|
reject: false
|
|
5530
6202
|
});
|
|
5531
6203
|
if (tarOut.exitCode !== 0) {
|
|
5532
6204
|
throw new GitWorktreeError(`tar of ${opts.hostSource} failed: ${tarOut.stderr}`);
|
|
5533
6205
|
}
|
|
5534
|
-
const tarIn = await
|
|
6206
|
+
const tarIn = await execa9(
|
|
5535
6207
|
"docker",
|
|
5536
6208
|
["exec", "-i", "--user", "1000:1000", opts.container, "tar", "-C", "/workspace", "-xf", "-"],
|
|
5537
6209
|
{ input: tarOut.stdout, reject: false }
|
|
@@ -5542,13 +6214,13 @@ async function seedWorkspaceFromDir(opts) {
|
|
|
5542
6214
|
log(`seeded /workspace from ${opts.hostSource}`);
|
|
5543
6215
|
}
|
|
5544
6216
|
async function removeInBoxWorktree(args) {
|
|
5545
|
-
const remove = await
|
|
6217
|
+
const remove = await execa9(
|
|
5546
6218
|
"git",
|
|
5547
6219
|
["-C", args.hostMainRepo, "worktree", "remove", "--force", args.gitWorktreePath],
|
|
5548
6220
|
{ reject: false }
|
|
5549
6221
|
);
|
|
5550
6222
|
if (remove.exitCode === 0) return;
|
|
5551
|
-
await
|
|
6223
|
+
await execa9("git", ["-C", args.hostMainRepo, "worktree", "prune"], { reject: false });
|
|
5552
6224
|
}
|
|
5553
6225
|
function ctParent(p) {
|
|
5554
6226
|
const i = p.lastIndexOf("/");
|
|
@@ -5579,20 +6251,20 @@ async function resyncWorkspaceFromHost(opts) {
|
|
|
5579
6251
|
const hostMain = w.hostMainRepo;
|
|
5580
6252
|
const boxBranch = w.branch;
|
|
5581
6253
|
const res = { containerPath: ct, mergeConflicts: [], overlaySkipped: [] };
|
|
5582
|
-
const hostBranchProbe = await
|
|
6254
|
+
const hostBranchProbe = await execa9(
|
|
5583
6255
|
"git",
|
|
5584
6256
|
["-C", hostMain, "symbolic-ref", "--short", "-q", "HEAD"],
|
|
5585
6257
|
{ reject: false }
|
|
5586
6258
|
);
|
|
5587
|
-
const hostRef = hostBranchProbe.exitCode === 0 && hostBranchProbe.stdout.trim() ? hostBranchProbe.stdout.trim() : (await
|
|
6259
|
+
const hostRef = hostBranchProbe.exitCode === 0 && hostBranchProbe.stdout.trim() ? hostBranchProbe.stdout.trim() : (await execa9("git", ["-C", hostMain, "rev-parse", "HEAD"], { reject: false })).stdout.trim();
|
|
5588
6260
|
if (!hostRef) {
|
|
5589
6261
|
log(`resync: ${ct}: could not resolve host ref; skipping`);
|
|
5590
6262
|
results.push(res);
|
|
5591
6263
|
continue;
|
|
5592
6264
|
}
|
|
5593
|
-
const stash = await
|
|
6265
|
+
const stash = await execa9("git", ["-C", hostMain, "stash", "create"], { reject: false });
|
|
5594
6266
|
const hostStashSha = stash.exitCode === 0 ? stash.stdout.trim() || null : null;
|
|
5595
|
-
const untracked = await
|
|
6267
|
+
const untracked = await execa9(
|
|
5596
6268
|
"git",
|
|
5597
6269
|
["-C", hostMain, "ls-files", "--others", "--exclude-standard", "-z"],
|
|
5598
6270
|
{ reject: false }
|
|
@@ -5662,7 +6334,7 @@ async function resyncWorkspaceFromHost(opts) {
|
|
|
5662
6334
|
}
|
|
5663
6335
|
}
|
|
5664
6336
|
if (hostUntracked.length > 0) {
|
|
5665
|
-
const probe = await
|
|
6337
|
+
const probe = await execa9(
|
|
5666
6338
|
"docker",
|
|
5667
6339
|
[
|
|
5668
6340
|
"exec",
|
|
@@ -5709,13 +6381,13 @@ async function resyncWorkspaceFromHost(opts) {
|
|
|
5709
6381
|
log(`resync: ${ct}: ${String(identical)} untracked host file(s) already identical in box (no-op)`);
|
|
5710
6382
|
}
|
|
5711
6383
|
if (toCopy.length > 0) {
|
|
5712
|
-
const tarOut = await
|
|
6384
|
+
const tarOut = await execa9("tar", ["-C", hostMain, "--null", "-T", "-", "-cf", "-"], {
|
|
5713
6385
|
input: toCopy.join("\0"),
|
|
5714
6386
|
encoding: "buffer",
|
|
5715
6387
|
reject: false
|
|
5716
6388
|
});
|
|
5717
6389
|
if (tarOut.exitCode === 0) {
|
|
5718
|
-
await
|
|
6390
|
+
await execa9(
|
|
5719
6391
|
"docker",
|
|
5720
6392
|
["exec", "-i", "--user", "vscode", opts.container, "tar", "-C", ct, "-xf", "-"],
|
|
5721
6393
|
{ input: tarOut.stdout, reject: false }
|
|
@@ -5738,7 +6410,7 @@ var cached = null;
|
|
|
5738
6410
|
async function detectPortless() {
|
|
5739
6411
|
if (cached !== null) return cached;
|
|
5740
6412
|
try {
|
|
5741
|
-
const ver = await
|
|
6413
|
+
const ver = await execa10(PORTLESS_BIN, SUB_VERSION, { reject: false });
|
|
5742
6414
|
if (ver.exitCode !== 0) {
|
|
5743
6415
|
cached = { installed: false, proxyRunning: false };
|
|
5744
6416
|
return cached;
|
|
@@ -5758,7 +6430,7 @@ function resetPortlessCache() {
|
|
|
5758
6430
|
}
|
|
5759
6431
|
async function portlessAlias(name, port) {
|
|
5760
6432
|
try {
|
|
5761
|
-
const r = await
|
|
6433
|
+
const r = await execa10(PORTLESS_BIN, [SUB_ALIAS, name, String(port)], { reject: false });
|
|
5762
6434
|
return r.exitCode === 0;
|
|
5763
6435
|
} catch {
|
|
5764
6436
|
return false;
|
|
@@ -5766,7 +6438,7 @@ async function portlessAlias(name, port) {
|
|
|
5766
6438
|
}
|
|
5767
6439
|
async function portlessUnalias(name) {
|
|
5768
6440
|
try {
|
|
5769
|
-
const r = await
|
|
6441
|
+
const r = await execa10(PORTLESS_BIN, [SUB_ALIAS, SUB_ALIAS_REMOVE, name], { reject: false });
|
|
5770
6442
|
return r.exitCode === 0;
|
|
5771
6443
|
} catch {
|
|
5772
6444
|
return false;
|
|
@@ -5775,7 +6447,7 @@ async function portlessUnalias(name) {
|
|
|
5775
6447
|
async function portlessGetUrl(name) {
|
|
5776
6448
|
const fallback = `https://${name}.localhost`;
|
|
5777
6449
|
try {
|
|
5778
|
-
const r = await
|
|
6450
|
+
const r = await execa10(PORTLESS_BIN, [SUB_GET, name], { reject: false });
|
|
5779
6451
|
const out = (r.stdout ?? "").trim();
|
|
5780
6452
|
if (r.exitCode === 0 && /^https?:\/\//.test(out)) return out;
|
|
5781
6453
|
} catch {
|
|
@@ -5796,7 +6468,7 @@ function portlessBrowserEnv(boxName, opts) {
|
|
|
5796
6468
|
}
|
|
5797
6469
|
async function installPortless() {
|
|
5798
6470
|
try {
|
|
5799
|
-
const r = await
|
|
6471
|
+
const r = await execa10("npm", ["install", "-g", "portless"], { reject: false });
|
|
5800
6472
|
return r.exitCode === 0;
|
|
5801
6473
|
} catch {
|
|
5802
6474
|
return false;
|
|
@@ -5804,7 +6476,7 @@ async function installPortless() {
|
|
|
5804
6476
|
}
|
|
5805
6477
|
async function startPortlessProxy() {
|
|
5806
6478
|
try {
|
|
5807
|
-
const r = await
|
|
6479
|
+
const r = await execa10(
|
|
5808
6480
|
PORTLESS_BIN,
|
|
5809
6481
|
["proxy", "start", "--no-tls", "-p", String(PORTLESS_PROXY_PORT)],
|
|
5810
6482
|
{ reject: false }
|
|
@@ -5851,14 +6523,14 @@ async function resolvePortlessHostStateDir(override) {
|
|
|
5851
6523
|
const live = await findLivePortlessStateDir();
|
|
5852
6524
|
if (live) return live;
|
|
5853
6525
|
const home = join8(homedir6(), ".portless");
|
|
5854
|
-
if (
|
|
5855
|
-
if (
|
|
6526
|
+
if (existsSync2(home)) return home;
|
|
6527
|
+
if (existsSync2("/tmp/portless")) return "/tmp/portless";
|
|
5856
6528
|
return home;
|
|
5857
6529
|
}
|
|
5858
6530
|
async function isProxyRunning() {
|
|
5859
6531
|
if (await findLivePortlessStateDir() !== null) return true;
|
|
5860
6532
|
try {
|
|
5861
|
-
const r = await
|
|
6533
|
+
const r = await execa10("pgrep", ["-f", "portless proxy"], { reject: false });
|
|
5862
6534
|
return r.exitCode === 0 && (r.stdout ?? "").trim().length > 0;
|
|
5863
6535
|
} catch {
|
|
5864
6536
|
return false;
|
|
@@ -5909,12 +6581,12 @@ async function findExcludedDirs(root, excluded = EXCLUDE_DIRS) {
|
|
|
5909
6581
|
return matches;
|
|
5910
6582
|
}
|
|
5911
6583
|
async function createSnapshot(opts) {
|
|
5912
|
-
const source =
|
|
5913
|
-
const destination =
|
|
6584
|
+
const source = resolve22(opts.source);
|
|
6585
|
+
const destination = resolve22(opts.destination);
|
|
5914
6586
|
const excluded = opts.excluded ?? EXCLUDE_DIRS;
|
|
5915
6587
|
await mkdir42(SNAPSHOTS_ROOT, { recursive: true });
|
|
5916
6588
|
const cpArgs = platform() === "darwin" ? ["-cR"] : ["-R"];
|
|
5917
|
-
await
|
|
6589
|
+
await execa11("cp", [...cpArgs, `${source}/`, destination]);
|
|
5918
6590
|
const toPrune = await findExcludedDirs(destination, excluded);
|
|
5919
6591
|
await Promise.all(toPrune.map((p) => rm32(p, { recursive: true, force: true })));
|
|
5920
6592
|
return { destination, prunedPaths: toPrune };
|
|
@@ -5922,7 +6594,7 @@ async function createSnapshot(opts) {
|
|
|
5922
6594
|
var CHECKPOINTS_ROOT = join10(homedir8(), ".agentbox", "checkpoints");
|
|
5923
6595
|
var CHECKPOINT_IMAGE_PREFIX = "agentbox-ckpt-";
|
|
5924
6596
|
function checkpointImageTag(projectRoot, name) {
|
|
5925
|
-
const mnemonic = sanitizeMnemonic(
|
|
6597
|
+
const mnemonic = sanitizeMnemonic(basename5(projectRoot));
|
|
5926
6598
|
return `${CHECKPOINT_IMAGE_PREFIX}${hashProjectPath(projectRoot)}_${mnemonic}:${name}`;
|
|
5927
6599
|
}
|
|
5928
6600
|
function projectCheckpointsDir(projectRoot) {
|
|
@@ -6039,7 +6711,7 @@ async function runCleanup(container, log) {
|
|
|
6039
6711
|
}
|
|
6040
6712
|
}
|
|
6041
6713
|
async function inspectImageConfig(imageRef) {
|
|
6042
|
-
const r = await
|
|
6714
|
+
const r = await execa12("docker", ["image", "inspect", imageRef], { reject: false });
|
|
6043
6715
|
if (r.exitCode !== 0) {
|
|
6044
6716
|
throw new CheckpointError(`docker image inspect ${imageRef} failed`, r.stdout, r.stderr);
|
|
6045
6717
|
}
|
|
@@ -6115,14 +6787,14 @@ async function createCheckpoint(opts) {
|
|
|
6115
6787
|
await runCleanup(box.container, log);
|
|
6116
6788
|
if (type === "layered") {
|
|
6117
6789
|
log(`docker commit ${box.container} -> ${tag} (layered)`);
|
|
6118
|
-
const r = await
|
|
6790
|
+
const r = await execa12("docker", ["commit", box.container, tag], { reject: false });
|
|
6119
6791
|
if (r.exitCode !== 0) {
|
|
6120
6792
|
throw new CheckpointError(`docker commit failed for ${box.container}`, r.stdout, r.stderr);
|
|
6121
6793
|
}
|
|
6122
6794
|
} else {
|
|
6123
6795
|
log(`docker commit ${box.container} -> <intermediate> (flattened path)`);
|
|
6124
6796
|
const intermediate = `${tag}-intermediate`;
|
|
6125
|
-
const commit = await
|
|
6797
|
+
const commit = await execa12("docker", ["commit", box.container, intermediate], {
|
|
6126
6798
|
reject: false
|
|
6127
6799
|
});
|
|
6128
6800
|
if (commit.exitCode !== 0) {
|
|
@@ -6167,7 +6839,7 @@ async function createCheckpoint(opts) {
|
|
|
6167
6839
|
}
|
|
6168
6840
|
async function flattenImage(sourceTag, destTag, log) {
|
|
6169
6841
|
const tmpName = `agentbox-flatten-${Date.now().toString(36)}`;
|
|
6170
|
-
const create = await
|
|
6842
|
+
const create = await execa12(
|
|
6171
6843
|
"docker",
|
|
6172
6844
|
["create", "--name", tmpName, sourceTag, "sleep", "0"],
|
|
6173
6845
|
{ reject: false }
|
|
@@ -6179,7 +6851,7 @@ async function flattenImage(sourceTag, destTag, log) {
|
|
|
6179
6851
|
try {
|
|
6180
6852
|
const rootfsPath = join10(scratch, "rootfs.tar");
|
|
6181
6853
|
log(`exporting rootfs of ${sourceTag} to ${rootfsPath}`);
|
|
6182
|
-
const exp = await
|
|
6854
|
+
const exp = await execa12("docker", ["export", "-o", rootfsPath, tmpName], { reject: false });
|
|
6183
6855
|
if (exp.exitCode !== 0) {
|
|
6184
6856
|
throw new CheckpointError(`docker export failed`, exp.stdout, exp.stderr);
|
|
6185
6857
|
}
|
|
@@ -6192,7 +6864,7 @@ async function flattenImage(sourceTag, destTag, log) {
|
|
|
6192
6864
|
];
|
|
6193
6865
|
await writeFile32(join10(scratch, "Dockerfile"), lines.join("\n") + "\n", "utf8");
|
|
6194
6866
|
log(`building flattened ${destTag} from rootfs.tar (FROM scratch)`);
|
|
6195
|
-
const build = await
|
|
6867
|
+
const build = await execa12(
|
|
6196
6868
|
"docker",
|
|
6197
6869
|
["build", "-t", destTag, "-f", join10(scratch, "Dockerfile"), scratch],
|
|
6198
6870
|
{ reject: false }
|
|
@@ -6201,7 +6873,7 @@ async function flattenImage(sourceTag, destTag, log) {
|
|
|
6201
6873
|
throw new CheckpointError(`flatten docker build failed`, build.stdout, build.stderr);
|
|
6202
6874
|
}
|
|
6203
6875
|
} finally {
|
|
6204
|
-
await
|
|
6876
|
+
await execa12("docker", ["rm", "-f", tmpName], { reject: false });
|
|
6205
6877
|
await rm4(scratch, { recursive: true, force: true });
|
|
6206
6878
|
}
|
|
6207
6879
|
}
|
|
@@ -6245,7 +6917,7 @@ async function pathExists5(p) {
|
|
|
6245
6917
|
}
|
|
6246
6918
|
async function writeBoxEnvFile(container, env) {
|
|
6247
6919
|
const body = formatBoxEnvBody(env);
|
|
6248
|
-
const result = await
|
|
6920
|
+
const result = await execa13(
|
|
6249
6921
|
"docker",
|
|
6250
6922
|
["exec", "--user", "root", "-i", container, "sh", "-c", "umask 022 && cat > /etc/agentbox/box.env"],
|
|
6251
6923
|
{ input: body, reject: false }
|
|
@@ -6269,7 +6941,7 @@ function shellSingleQuote(s) {
|
|
|
6269
6941
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
6270
6942
|
}
|
|
6271
6943
|
async function ensureHomeOwnedByVscode(container) {
|
|
6272
|
-
await
|
|
6944
|
+
await execa14(
|
|
6273
6945
|
"docker",
|
|
6274
6946
|
[
|
|
6275
6947
|
"exec",
|
|
@@ -6418,16 +7090,16 @@ async function spawnRelay(relayBin, cliEntry, log) {
|
|
|
6418
7090
|
}
|
|
6419
7091
|
function resolveRelayBin() {
|
|
6420
7092
|
const override = process.env.AGENTBOX_RELAY_BIN;
|
|
6421
|
-
if (override &&
|
|
6422
|
-
const here =
|
|
7093
|
+
if (override && existsSync3(override)) return override;
|
|
7094
|
+
const here = dirname32(fileURLToPath(import.meta.url));
|
|
6423
7095
|
const candidates = [
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
7096
|
+
resolve3(here, "..", "runtime", "relay", "bin.cjs"),
|
|
7097
|
+
resolve3(here, "..", "..", "relay", "dist", "bin.cjs"),
|
|
7098
|
+
resolve3(here, "..", "..", "..", "@agentbox", "relay", "dist", "bin.cjs"),
|
|
7099
|
+
resolve3(here, "..", "..", "node_modules", "@agentbox", "relay", "dist", "bin.cjs")
|
|
6428
7100
|
];
|
|
6429
7101
|
for (const c of candidates) {
|
|
6430
|
-
if (
|
|
7102
|
+
if (existsSync3(c)) return c;
|
|
6431
7103
|
}
|
|
6432
7104
|
throw new Error(
|
|
6433
7105
|
`could not locate @agentbox/relay bin; tried:
|
|
@@ -6436,29 +7108,29 @@ function resolveRelayBin() {
|
|
|
6436
7108
|
}
|
|
6437
7109
|
function resolveCliEntry() {
|
|
6438
7110
|
const override = process.env.AGENTBOX_CLI_ENTRY;
|
|
6439
|
-
if (override &&
|
|
6440
|
-
const here =
|
|
7111
|
+
if (override && existsSync3(override)) return override;
|
|
7112
|
+
const here = dirname32(fileURLToPath(import.meta.url));
|
|
6441
7113
|
const candidates = [
|
|
6442
7114
|
// Bundled CLI (dev + published): this module IS bundled into the CLI
|
|
6443
7115
|
// entry, so the entry is index.js next to this file.
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
7116
|
+
resolve3(here, "index.js"),
|
|
7117
|
+
resolve3(here, "..", "..", "..", "apps", "cli", "dist", "index.js"),
|
|
7118
|
+
resolve3(here, "..", "..", "..", "..", "dist", "index.js")
|
|
6447
7119
|
];
|
|
6448
7120
|
for (const c of candidates) {
|
|
6449
|
-
if (
|
|
7121
|
+
if (existsSync3(c)) return c;
|
|
6450
7122
|
}
|
|
6451
7123
|
return null;
|
|
6452
7124
|
}
|
|
6453
7125
|
async function stageRelayHome(version, log) {
|
|
6454
7126
|
if (!version || version === "0.0.0-dev") return null;
|
|
6455
7127
|
if (process.env.AGENTBOX_RELAY_BIN || process.env.AGENTBOX_CLI_ENTRY) return null;
|
|
6456
|
-
const cliRoot = findCliRoot(
|
|
7128
|
+
const cliRoot = findCliRoot(dirname32(fileURLToPath(import.meta.url)));
|
|
6457
7129
|
if (cliRoot === null) return null;
|
|
6458
7130
|
const homeDir = join11(RELAY_HOME_DIR, version);
|
|
6459
7131
|
const stagedEntry = join11(homeDir, "dist", "index.js");
|
|
6460
7132
|
const stagedBin = join11(homeDir, "runtime", "relay", "bin.cjs");
|
|
6461
|
-
if (
|
|
7133
|
+
if (existsSync3(stagedEntry) && existsSync3(stagedBin)) {
|
|
6462
7134
|
return { relayBin: stagedBin, cliEntry: stagedEntry };
|
|
6463
7135
|
}
|
|
6464
7136
|
const nodeModules = resolveDepRoot(join11(cliRoot, "dist", "index.js"));
|
|
@@ -6470,7 +7142,7 @@ async function stageRelayHome(version, log) {
|
|
|
6470
7142
|
await mkdir6(tmpDir, { recursive: true });
|
|
6471
7143
|
for (const sub of ["dist", "runtime", "share"]) {
|
|
6472
7144
|
const src = join11(cliRoot, sub);
|
|
6473
|
-
if (
|
|
7145
|
+
if (existsSync3(src)) await cp(src, join11(tmpDir, sub), { recursive: true });
|
|
6474
7146
|
}
|
|
6475
7147
|
await cp(nodeModules, join11(tmpDir, "node_modules"), { recursive: true, dereference: true });
|
|
6476
7148
|
await rm5(homeDir, { recursive: true, force: true });
|
|
@@ -6478,7 +7150,7 @@ async function stageRelayHome(version, log) {
|
|
|
6478
7150
|
} catch (err) {
|
|
6479
7151
|
await rm5(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
6480
7152
|
});
|
|
6481
|
-
if (
|
|
7153
|
+
if (existsSync3(stagedEntry) && existsSync3(stagedBin)) {
|
|
6482
7154
|
return { relayBin: stagedBin, cliEntry: stagedEntry };
|
|
6483
7155
|
}
|
|
6484
7156
|
log(`relay home staging failed (${err instanceof Error ? err.message : String(err)}); using bundle paths`);
|
|
@@ -6490,8 +7162,8 @@ async function stageRelayHome(version, log) {
|
|
|
6490
7162
|
return { relayBin: stagedBin, cliEntry: stagedEntry };
|
|
6491
7163
|
}
|
|
6492
7164
|
function findCliRoot(moduleDir) {
|
|
6493
|
-
for (const root of [
|
|
6494
|
-
if (
|
|
7165
|
+
for (const root of [resolve3(moduleDir, ".."), resolve3(moduleDir, "..", "..")]) {
|
|
7166
|
+
if (existsSync3(join11(root, "dist", "index.js")) && existsSync3(join11(root, "runtime", "relay", "bin.cjs"))) {
|
|
6495
7167
|
return root;
|
|
6496
7168
|
}
|
|
6497
7169
|
}
|
|
@@ -6506,7 +7178,7 @@ function resolveDepRoot(fromFile) {
|
|
|
6506
7178
|
const idx = main.lastIndexOf(marker);
|
|
6507
7179
|
if (idx === -1) return null;
|
|
6508
7180
|
const nm = main.slice(0, idx + marker.length - 1);
|
|
6509
|
-
return
|
|
7181
|
+
return existsSync3(join11(nm, "commander")) ? nm : null;
|
|
6510
7182
|
} catch {
|
|
6511
7183
|
return null;
|
|
6512
7184
|
}
|
|
@@ -6667,7 +7339,8 @@ async function registerBoxWithRelay(args) {
|
|
|
6667
7339
|
worktrees,
|
|
6668
7340
|
previewUrl: args.previewUrl,
|
|
6669
7341
|
previewToken: args.previewToken,
|
|
6670
|
-
bridgeToken: args.bridgeToken
|
|
7342
|
+
bridgeToken: args.bridgeToken,
|
|
7343
|
+
autoApproveHostActions: args.autoApproveHostActions
|
|
6671
7344
|
});
|
|
6672
7345
|
}
|
|
6673
7346
|
async function forgetBoxFromRelay(boxId) {
|
|
@@ -6808,7 +7481,8 @@ async function rehydrateRelayRegistry(boxes) {
|
|
|
6808
7481
|
worktrees: b.gitWorktrees,
|
|
6809
7482
|
previewUrl: b.relayPreviewUrl,
|
|
6810
7483
|
previewToken: b.relayPreviewToken,
|
|
6811
|
-
bridgeToken: b.bridgeToken
|
|
7484
|
+
bridgeToken: b.bridgeToken,
|
|
7485
|
+
autoApproveHostActions: b.autoApproveHostActions
|
|
6812
7486
|
});
|
|
6813
7487
|
} catch {
|
|
6814
7488
|
}
|
|
@@ -6959,7 +7633,7 @@ function persistableLimits(lim) {
|
|
|
6959
7633
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
6960
7634
|
}
|
|
6961
7635
|
function sanitizeBasename(workspacePath) {
|
|
6962
|
-
const raw =
|
|
7636
|
+
const raw = basename6(resolve4(workspacePath));
|
|
6963
7637
|
return raw.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "").slice(0, 30).replace(/[-._]+$/, "");
|
|
6964
7638
|
}
|
|
6965
7639
|
function defaultBoxName(workspacePath, id) {
|
|
@@ -6990,7 +7664,7 @@ async function buildIdentityMounts() {
|
|
|
6990
7664
|
async function createBox(opts) {
|
|
6991
7665
|
const log = opts.onLog ?? (() => {
|
|
6992
7666
|
});
|
|
6993
|
-
const workspace =
|
|
7667
|
+
const workspace = resolve4(opts.workspacePath);
|
|
6994
7668
|
if (!await pathExists6(workspace)) {
|
|
6995
7669
|
throw new Error(`workspace does not exist: ${workspace}`);
|
|
6996
7670
|
}
|
|
@@ -7283,6 +7957,7 @@ async function createBox(opts) {
|
|
|
7283
7957
|
}
|
|
7284
7958
|
}
|
|
7285
7959
|
for (const v of extraVolumes) log(`mounting agent dir: ${v}`);
|
|
7960
|
+
const autoApproveHostActions = (await loadEffectiveConfig(opts.projectRoot ?? workspace)).effective.box.autoApproveHostActions;
|
|
7286
7961
|
const relayToken = generateRelayToken();
|
|
7287
7962
|
if (relayUp) {
|
|
7288
7963
|
try {
|
|
@@ -7293,7 +7968,8 @@ async function createBox(opts) {
|
|
|
7293
7968
|
containerName,
|
|
7294
7969
|
createdAt,
|
|
7295
7970
|
projectIndex,
|
|
7296
|
-
worktrees: gitWorktreeRecords
|
|
7971
|
+
worktrees: gitWorktreeRecords,
|
|
7972
|
+
autoApproveHostActions
|
|
7297
7973
|
});
|
|
7298
7974
|
log(`registered box token with relay`);
|
|
7299
7975
|
} catch (err) {
|
|
@@ -7359,6 +8035,7 @@ async function createBox(opts) {
|
|
|
7359
8035
|
gitWorktrees: gitWorktreeRecords.length > 0 ? gitWorktreeRecords : void 0,
|
|
7360
8036
|
withPlaywright: opts.withPlaywright ? true : void 0,
|
|
7361
8037
|
withEnv: opts.withEnv ? true : void 0,
|
|
8038
|
+
autoApproveHostActions: autoApproveHostActions ? true : void 0,
|
|
7362
8039
|
vncEnabled: vncEnabled ? true : void 0,
|
|
7363
8040
|
vncContainerPort: vncEnabled ? VNC_CONTAINER_PORT : void 0,
|
|
7364
8041
|
vncPassword,
|
|
@@ -7416,7 +8093,7 @@ async function createBox(opts) {
|
|
|
7416
8093
|
} catch (err) {
|
|
7417
8094
|
if (opts.useBranch !== void 0) {
|
|
7418
8095
|
log(`seedWorkspace failed for --use-branch ${opts.useBranch}; cleaning up the box`);
|
|
7419
|
-
await
|
|
8096
|
+
await execa15("docker", ["rm", "-f", containerName], { reject: false });
|
|
7420
8097
|
await removeBoxRecord(id);
|
|
7421
8098
|
for (const w of gitWorktreeRecords) {
|
|
7422
8099
|
await removeInBoxWorktree({
|
|
@@ -7479,7 +8156,7 @@ async function createBox(opts) {
|
|
|
7479
8156
|
else log(`agentbox-ctl daemon did not become reachable: ${ctl.reason}`);
|
|
7480
8157
|
if (opts.withPlaywright) {
|
|
7481
8158
|
log("installing @playwright/cli@latest (--with-playwright)");
|
|
7482
|
-
const result = await
|
|
8159
|
+
const result = await execa15(
|
|
7483
8160
|
"docker",
|
|
7484
8161
|
[
|
|
7485
8162
|
"exec",
|
|
@@ -7527,9 +8204,14 @@ async function createBox(opts) {
|
|
|
7527
8204
|
let carrySummary;
|
|
7528
8205
|
if (opts.carry && opts.carry.length > 0) {
|
|
7529
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
|
+
);
|
|
7530
8212
|
const result = await copyCarryPathsToBox({
|
|
7531
8213
|
container: containerName,
|
|
7532
|
-
entries
|
|
8214
|
+
entries,
|
|
7533
8215
|
onLog: log
|
|
7534
8216
|
});
|
|
7535
8217
|
log(`carry: copied ${String(result.copied)}/${String(opts.carry.length)} entry/entries`);
|
|
@@ -7653,7 +8335,7 @@ function parseShellSessionList(stdout) {
|
|
|
7653
8335
|
return out;
|
|
7654
8336
|
}
|
|
7655
8337
|
async function listShellSessions(container, user) {
|
|
7656
|
-
const res = await
|
|
8338
|
+
const res = await execa16(
|
|
7657
8339
|
"docker",
|
|
7658
8340
|
[
|
|
7659
8341
|
"exec",
|
|
@@ -7677,7 +8359,7 @@ async function startShellSession(opts) {
|
|
|
7677
8359
|
const login = opts.login !== false;
|
|
7678
8360
|
const term = process.env["TERM"] ?? "xterm-256color";
|
|
7679
8361
|
const cmd = login ? "bash -l" : "bash";
|
|
7680
|
-
const result = await
|
|
8362
|
+
const result = await execa16(
|
|
7681
8363
|
"docker",
|
|
7682
8364
|
[
|
|
7683
8365
|
"exec",
|
|
@@ -7726,7 +8408,7 @@ function buildShellSessionAttachArgv(container, sessionName, user) {
|
|
|
7726
8408
|
}
|
|
7727
8409
|
async function shellSessionInfo(container, sessionName, user) {
|
|
7728
8410
|
const name = sessionName ?? DEFAULT_SHELL_SESSION;
|
|
7729
|
-
const has = await
|
|
8411
|
+
const has = await execa16(
|
|
7730
8412
|
"docker",
|
|
7731
8413
|
["exec", "--user", user ?? CONTAINER_USER, container, "tmux", "has-session", "-t", name],
|
|
7732
8414
|
{ reject: false }
|
|
@@ -7734,7 +8416,7 @@ async function shellSessionInfo(container, sessionName, user) {
|
|
|
7734
8416
|
return { running: has.exitCode === 0, sessionName: name };
|
|
7735
8417
|
}
|
|
7736
8418
|
async function killShellSession(container, sessionName, user) {
|
|
7737
|
-
const res = await
|
|
8419
|
+
const res = await execa16(
|
|
7738
8420
|
"docker",
|
|
7739
8421
|
[
|
|
7740
8422
|
"exec",
|
|
@@ -8017,7 +8699,8 @@ async function startBox(idOrName) {
|
|
|
8017
8699
|
containerName: box.container,
|
|
8018
8700
|
createdAt: box.createdAt,
|
|
8019
8701
|
projectIndex: box.projectIndex,
|
|
8020
|
-
worktrees: box.gitWorktrees
|
|
8702
|
+
worktrees: box.gitWorktrees,
|
|
8703
|
+
autoApproveHostActions: box.autoApproveHostActions
|
|
8021
8704
|
});
|
|
8022
8705
|
} catch {
|
|
8023
8706
|
}
|
|
@@ -8036,7 +8719,7 @@ async function getBoxHostPaths(idOrName) {
|
|
|
8036
8719
|
}
|
|
8037
8720
|
async function dirSizeBytes(path) {
|
|
8038
8721
|
try {
|
|
8039
|
-
const result = await
|
|
8722
|
+
const result = await execa17("du", ["-sk", path], { reject: false });
|
|
8040
8723
|
if (result.exitCode !== 0) return null;
|
|
8041
8724
|
const sizeKb = Number.parseInt((result.stdout ?? "").split(/\s+/)[0] ?? "", 10);
|
|
8042
8725
|
if (Number.isNaN(sizeKb)) return null;
|
|
@@ -8181,7 +8864,7 @@ async function listBoxDirs() {
|
|
|
8181
8864
|
}
|
|
8182
8865
|
}
|
|
8183
8866
|
async function listCheckpointImageTags() {
|
|
8184
|
-
const r = await
|
|
8867
|
+
const r = await execa17(
|
|
8185
8868
|
"docker",
|
|
8186
8869
|
["image", "ls", "--format", "{{.Repository}}:{{.Tag}}", `${CHECKPOINT_IMAGE_PREFIX}*`],
|
|
8187
8870
|
{ reject: false }
|
|
@@ -8289,7 +8972,7 @@ async function pruneBoxes(opts = {}) {
|
|
|
8289
8972
|
} catch {
|
|
8290
8973
|
}
|
|
8291
8974
|
try {
|
|
8292
|
-
await
|
|
8975
|
+
await execa17("docker", ["image", "rm", RELAY_IMAGE_REF], { reject: false });
|
|
8293
8976
|
} catch {
|
|
8294
8977
|
}
|
|
8295
8978
|
try {
|
|
@@ -8351,7 +9034,7 @@ function splitPair(raw) {
|
|
|
8351
9034
|
return [parts[0].trim(), parts[1].trim()];
|
|
8352
9035
|
}
|
|
8353
9036
|
async function duBytes(path) {
|
|
8354
|
-
const result = await
|
|
9037
|
+
const result = await execa18("du", ["-sk", path], { reject: false });
|
|
8355
9038
|
if (result.exitCode !== 0) return null;
|
|
8356
9039
|
const kb = Number.parseInt((result.stdout ?? "").split(/\s+/)[0] ?? "", 10);
|
|
8357
9040
|
return Number.isNaN(kb) ? null : kb * 1024;
|
|
@@ -8364,7 +9047,7 @@ async function volumeSizeBytes(name) {
|
|
|
8364
9047
|
const sz = await duBytes(live);
|
|
8365
9048
|
if (sz !== null) return sz;
|
|
8366
9049
|
}
|
|
8367
|
-
const df = await
|
|
9050
|
+
const df = await execa18(
|
|
8368
9051
|
"docker",
|
|
8369
9052
|
["system", "df", "-v", "--format", "{{json .Volumes}}"],
|
|
8370
9053
|
{ reject: false }
|
|
@@ -8385,7 +9068,7 @@ async function volumeSizeBytes(name) {
|
|
|
8385
9068
|
return null;
|
|
8386
9069
|
}
|
|
8387
9070
|
async function imageBytes(tag) {
|
|
8388
|
-
const r = await
|
|
9071
|
+
const r = await execa18("docker", ["image", "inspect", tag, "--format", "{{.Size}}"], {
|
|
8389
9072
|
reject: false
|
|
8390
9073
|
});
|
|
8391
9074
|
if (r.exitCode !== 0) return null;
|
|
@@ -8396,7 +9079,7 @@ async function projectCheckpointImageBytes(projectRoot, name) {
|
|
|
8396
9079
|
return imageBytes(checkpointImageTag(projectRoot, name));
|
|
8397
9080
|
}
|
|
8398
9081
|
async function allCheckpointImagesBytes() {
|
|
8399
|
-
const r = await
|
|
9082
|
+
const r = await execa18(
|
|
8400
9083
|
"docker",
|
|
8401
9084
|
[
|
|
8402
9085
|
"image",
|
|
@@ -8448,7 +9131,7 @@ function reconcileLimits(persisted, dockerJson) {
|
|
|
8448
9131
|
};
|
|
8449
9132
|
}
|
|
8450
9133
|
async function containerWritableBytes(container) {
|
|
8451
|
-
const r = await
|
|
9134
|
+
const r = await execa18(
|
|
8452
9135
|
"docker",
|
|
8453
9136
|
["ps", "-a", "--filter", `name=^${container}$`, "--format", "{{.Size}}", "--size"],
|
|
8454
9137
|
{ reject: false }
|
|
@@ -8495,7 +9178,7 @@ async function boxResourceStats(record) {
|
|
|
8495
9178
|
if (await inspectContainerStatus(record.container) !== "running") {
|
|
8496
9179
|
return base;
|
|
8497
9180
|
}
|
|
8498
|
-
const proc = await
|
|
9181
|
+
const proc = await execa18(
|
|
8499
9182
|
"docker",
|
|
8500
9183
|
["stats", "--no-stream", "--format", "{{json .}}", record.container],
|
|
8501
9184
|
{ reject: false }
|
|
@@ -8530,125 +9213,6 @@ async function boxResourceStats(record) {
|
|
|
8530
9213
|
blockWriteBytes: blkPair ? parseDockerSize(blkPair[1]) : null
|
|
8531
9214
|
};
|
|
8532
9215
|
}
|
|
8533
|
-
function posixDirname(p) {
|
|
8534
|
-
return posix.dirname(p) || "/";
|
|
8535
|
-
}
|
|
8536
|
-
function asText(s) {
|
|
8537
|
-
if (s === void 0) return "";
|
|
8538
|
-
if (typeof s === "string") return s;
|
|
8539
|
-
return Buffer.from(s).toString("utf8");
|
|
8540
|
-
}
|
|
8541
|
-
async function uploadToBox(box, hostSrc, boxDst) {
|
|
8542
|
-
const srcAbs = resolve4(hostSrc);
|
|
8543
|
-
if (!existsSync3(srcAbs)) throw new Error(`source not found: ${hostSrc}`);
|
|
8544
|
-
const srcBasename = basename5(srcAbs);
|
|
8545
|
-
const srcParent = dirname22(srcAbs);
|
|
8546
|
-
let boxParent;
|
|
8547
|
-
let finalName;
|
|
8548
|
-
if (boxDst.endsWith("/")) {
|
|
8549
|
-
boxParent = boxDst.replace(/\/+$/, "") || "/";
|
|
8550
|
-
finalName = srcBasename;
|
|
8551
|
-
} else {
|
|
8552
|
-
const isDir2 = await execa18(
|
|
8553
|
-
"docker",
|
|
8554
|
-
["exec", box.container, "test", "-d", boxDst],
|
|
8555
|
-
{ reject: false }
|
|
8556
|
-
);
|
|
8557
|
-
if (isDir2.exitCode === 0) {
|
|
8558
|
-
boxParent = boxDst.replace(/\/+$/, "") || "/";
|
|
8559
|
-
finalName = srcBasename;
|
|
8560
|
-
} else {
|
|
8561
|
-
boxParent = posixDirname(boxDst);
|
|
8562
|
-
finalName = posix.basename(boxDst);
|
|
8563
|
-
}
|
|
8564
|
-
}
|
|
8565
|
-
const finalPath = boxParent === "/" ? `/${finalName}` : `${boxParent}/${finalName}`;
|
|
8566
|
-
const mk = await execa18(
|
|
8567
|
-
"docker",
|
|
8568
|
-
["exec", "--user", "root", box.container, "mkdir", "-p", boxParent],
|
|
8569
|
-
{ reject: false }
|
|
8570
|
-
);
|
|
8571
|
-
if (mk.exitCode !== 0) {
|
|
8572
|
-
throw new Error(`mkdir -p ${boxParent} in box failed: ${asText(mk.stderr).slice(0, 300)}`);
|
|
8573
|
-
}
|
|
8574
|
-
const packed = await execa18("tar", ["-C", srcParent, "-cf", "-", srcBasename], {
|
|
8575
|
-
encoding: "buffer",
|
|
8576
|
-
reject: false,
|
|
8577
|
-
env: { ...process.env, COPYFILE_DISABLE: "1" }
|
|
8578
|
-
});
|
|
8579
|
-
if (packed.exitCode !== 0) {
|
|
8580
|
-
throw new Error(`tar pack failed: ${asText(packed.stderr).slice(0, 300)}`);
|
|
8581
|
-
}
|
|
8582
|
-
const extract = await execa18(
|
|
8583
|
-
"docker",
|
|
8584
|
-
["exec", "-i", "--user", "root", box.container, "tar", "-xf", "-", "-C", boxParent],
|
|
8585
|
-
{ input: packed.stdout, reject: false }
|
|
8586
|
-
);
|
|
8587
|
-
if (extract.exitCode !== 0) {
|
|
8588
|
-
throw new Error(`tar extract in box failed: ${asText(extract.stderr).slice(0, 300)}`);
|
|
8589
|
-
}
|
|
8590
|
-
if (finalName !== srcBasename) {
|
|
8591
|
-
const initial = boxParent === "/" ? `/${srcBasename}` : `${boxParent}/${srcBasename}`;
|
|
8592
|
-
const mv = await execa18(
|
|
8593
|
-
"docker",
|
|
8594
|
-
["exec", "--user", "root", box.container, "mv", initial, finalPath],
|
|
8595
|
-
{ reject: false }
|
|
8596
|
-
);
|
|
8597
|
-
if (mv.exitCode !== 0) {
|
|
8598
|
-
throw new Error(
|
|
8599
|
-
`rename ${initial} -> ${finalPath} in box failed: ${asText(mv.stderr).slice(0, 300)}`
|
|
8600
|
-
);
|
|
8601
|
-
}
|
|
8602
|
-
}
|
|
8603
|
-
const chown = await execa18(
|
|
8604
|
-
"docker",
|
|
8605
|
-
["exec", "--user", "root", box.container, "chown", "-R", "1000:1000", finalPath],
|
|
8606
|
-
{ reject: false }
|
|
8607
|
-
);
|
|
8608
|
-
if (chown.exitCode !== 0) {
|
|
8609
|
-
return {
|
|
8610
|
-
finalPath,
|
|
8611
|
-
warn: `chown ${finalPath} to vscode (uid 1000) failed; ownership inside the box may be root.`
|
|
8612
|
-
};
|
|
8613
|
-
}
|
|
8614
|
-
return { finalPath };
|
|
8615
|
-
}
|
|
8616
|
-
async function downloadFromBox(box, boxSrc, hostDst) {
|
|
8617
|
-
const srcBasename = posix.basename(boxSrc);
|
|
8618
|
-
const srcParent = posixDirname(boxSrc);
|
|
8619
|
-
const dstAbs = resolve4(hostDst);
|
|
8620
|
-
let hostParent;
|
|
8621
|
-
let finalName;
|
|
8622
|
-
const dstExists = existsSync3(dstAbs);
|
|
8623
|
-
if (hostDst.endsWith("/") || dstExists && statSync(dstAbs).isDirectory()) {
|
|
8624
|
-
hostParent = dstAbs;
|
|
8625
|
-
finalName = srcBasename;
|
|
8626
|
-
} else {
|
|
8627
|
-
hostParent = dirname22(dstAbs);
|
|
8628
|
-
finalName = basename5(dstAbs);
|
|
8629
|
-
}
|
|
8630
|
-
mkdirSync(hostParent, { recursive: true });
|
|
8631
|
-
const finalPath = posix.join(hostParent, finalName);
|
|
8632
|
-
const packed = await execa18(
|
|
8633
|
-
"docker",
|
|
8634
|
-
["exec", box.container, "tar", "-C", srcParent, "-cf", "-", srcBasename],
|
|
8635
|
-
{ encoding: "buffer", reject: false }
|
|
8636
|
-
);
|
|
8637
|
-
if (packed.exitCode !== 0) {
|
|
8638
|
-
throw new Error(`tar pack in box failed: ${asText(packed.stderr).slice(0, 300)}`);
|
|
8639
|
-
}
|
|
8640
|
-
const extract = await execa18("tar", ["-xf", "-", "-C", hostParent], {
|
|
8641
|
-
input: packed.stdout,
|
|
8642
|
-
reject: false
|
|
8643
|
-
});
|
|
8644
|
-
if (extract.exitCode !== 0) {
|
|
8645
|
-
throw new Error(`tar extract on host failed: ${asText(extract.stderr).slice(0, 300)}`);
|
|
8646
|
-
}
|
|
8647
|
-
if (finalName !== srcBasename) {
|
|
8648
|
-
renameSync(posix.join(hostParent, srcBasename), finalPath);
|
|
8649
|
-
}
|
|
8650
|
-
return { finalPath };
|
|
8651
|
-
}
|
|
8652
9216
|
var dockerProvider = {
|
|
8653
9217
|
name: "docker",
|
|
8654
9218
|
async create(req) {
|
|
@@ -8724,12 +9288,12 @@ var dockerProvider = {
|
|
|
8724
9288
|
const r = await execInBox(box.container, argv, opts?.user ? { user: opts.user } : {});
|
|
8725
9289
|
return { exitCode: r.exitCode, stdout: r.stdout, stderr: r.stderr };
|
|
8726
9290
|
},
|
|
8727
|
-
async uploadPath(box, hostSrc, boxDst) {
|
|
8728
|
-
const r = await uploadToBox(box, hostSrc, boxDst);
|
|
9291
|
+
async uploadPath(box, hostSrc, boxDst, exclude) {
|
|
9292
|
+
const r = await uploadToBox(box, hostSrc, boxDst, exclude);
|
|
8729
9293
|
return { finalPath: r.finalPath };
|
|
8730
9294
|
},
|
|
8731
|
-
async downloadPath(box, boxSrc, hostDst) {
|
|
8732
|
-
const r = await downloadFromBox(box, boxSrc, hostDst);
|
|
9295
|
+
async downloadPath(box, boxSrc, hostDst, exclude) {
|
|
9296
|
+
const r = await downloadFromBox(box, boxSrc, hostDst, exclude);
|
|
8733
9297
|
return { finalPath: r.finalPath };
|
|
8734
9298
|
},
|
|
8735
9299
|
async resolveUrl(box, opts) {
|
|
@@ -8884,10 +9448,10 @@ async function stageDynamicSyncTarball(uploads) {
|
|
|
8884
9448
|
try {
|
|
8885
9449
|
for (const up of uploads) {
|
|
8886
9450
|
const target = join16(stageDir, up.set, up.rel);
|
|
8887
|
-
await mkdir8(
|
|
9451
|
+
await mkdir8(dirname4(target), { recursive: true });
|
|
8888
9452
|
await copyFile2(up.absSrc, target);
|
|
8889
9453
|
}
|
|
8890
|
-
tarballPath = join16(tmpdir4(), `agentbox-dynsync-${
|
|
9454
|
+
tarballPath = join16(tmpdir4(), `agentbox-dynsync-${basename7(stageDir)}.tar.gz`);
|
|
8891
9455
|
await execa19("tar", ["-czf", tarballPath, "-C", stageDir, "."], {
|
|
8892
9456
|
env: { ...process.env, COPYFILE_DISABLE: "1" }
|
|
8893
9457
|
});
|
|
@@ -8928,6 +9492,7 @@ async function ensureBoxBrowser(container, timeoutMs = 8e3, targetUrl = "about:b
|
|
|
8928
9492
|
}
|
|
8929
9493
|
|
|
8930
9494
|
export {
|
|
9495
|
+
BUILT_IN_DEFAULTS,
|
|
8931
9496
|
KEY_REGISTRY,
|
|
8932
9497
|
lookupKey,
|
|
8933
9498
|
UserConfigError,
|
|
@@ -8951,12 +9516,10 @@ export {
|
|
|
8951
9516
|
renderStatusTable,
|
|
8952
9517
|
renderTaskTable,
|
|
8953
9518
|
renderPortsTable,
|
|
9519
|
+
parseReplacementsSection,
|
|
9520
|
+
parseCarrySection,
|
|
8954
9521
|
loadCarrySection,
|
|
8955
|
-
|
|
8956
|
-
UserFacingError,
|
|
8957
|
-
BoxNotFoundError,
|
|
8958
|
-
AmbiguousBoxError,
|
|
8959
|
-
generateBoxId,
|
|
9522
|
+
ALL_CONNECTORS,
|
|
8960
9523
|
DEFAULT_RELAY_PORT,
|
|
8961
9524
|
RELAY_CONTAINER_NAME,
|
|
8962
9525
|
RELAY_NETWORK_NAME,
|
|
@@ -8974,6 +9537,8 @@ export {
|
|
|
8974
9537
|
execInBox,
|
|
8975
9538
|
removeImage,
|
|
8976
9539
|
volumeExists,
|
|
9540
|
+
uploadToBox,
|
|
9541
|
+
downloadFromBox,
|
|
8977
9542
|
CONTAINER_EXPORT_MERGED,
|
|
8978
9543
|
detectEngine,
|
|
8979
9544
|
setEngineOverride,
|
|
@@ -9174,8 +9739,6 @@ export {
|
|
|
9174
9739
|
allCheckpointImagesBytes,
|
|
9175
9740
|
agentboxHomeBytes,
|
|
9176
9741
|
boxResourceStats,
|
|
9177
|
-
uploadToBox,
|
|
9178
|
-
downloadFromBox,
|
|
9179
9742
|
dockerProvider,
|
|
9180
9743
|
BOX_WORKFLOWS_DIR,
|
|
9181
9744
|
BOX_DYNAMIC_SYNC_MANIFEST,
|
|
@@ -9186,4 +9749,4 @@ export {
|
|
|
9186
9749
|
browserSessionActive,
|
|
9187
9750
|
ensureBoxBrowser
|
|
9188
9751
|
};
|
|
9189
|
-
//# sourceMappingURL=chunk-
|
|
9752
|
+
//# sourceMappingURL=chunk-GXJNJUEV.js.map
|