@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
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
clipboardCaptureAvailable,
|
|
9
9
|
cloudAgentAttach,
|
|
10
10
|
cloudAgentStartDetached,
|
|
11
|
+
cmuxBinary,
|
|
11
12
|
createMenuLines,
|
|
12
13
|
detectHostTerminal,
|
|
13
14
|
getProvider,
|
|
@@ -26,10 +27,11 @@ import {
|
|
|
26
27
|
runWrappedAttach,
|
|
27
28
|
setTerminalTitle,
|
|
28
29
|
sidebarLines,
|
|
30
|
+
spawnInNewTerminal,
|
|
29
31
|
statusLine,
|
|
30
32
|
stripTitleGlyph,
|
|
31
33
|
subscribePrompts
|
|
32
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-SB4QTF2T.js";
|
|
33
35
|
import {
|
|
34
36
|
daytonaBackend,
|
|
35
37
|
ensureDaytonaCredentials,
|
|
@@ -37,7 +39,7 @@ import {
|
|
|
37
39
|
readDaytonaCredStatus,
|
|
38
40
|
readPreparedDaytonaState,
|
|
39
41
|
secretsPath
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-3WCEB6RE.js";
|
|
41
43
|
import {
|
|
42
44
|
detectEgressIp,
|
|
43
45
|
ensureHetznerCredentials,
|
|
@@ -48,7 +50,7 @@ import {
|
|
|
48
50
|
readPreparedState,
|
|
49
51
|
secretsPath as secretsPath2,
|
|
50
52
|
syncFirewallSource
|
|
51
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-QXFNLKJJ.js";
|
|
52
54
|
import {
|
|
53
55
|
detectSbx,
|
|
54
56
|
ensureVercelCredentials,
|
|
@@ -57,14 +59,14 @@ import {
|
|
|
57
59
|
readPreparedState as readPreparedState2,
|
|
58
60
|
readVercelCredStatus,
|
|
59
61
|
secretsPath as secretsPath3
|
|
60
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-SENASAU4.js";
|
|
61
63
|
import {
|
|
62
64
|
ensureE2bCredentials,
|
|
63
65
|
maskKey as maskKey4,
|
|
64
66
|
readE2bCredStatus,
|
|
65
67
|
readPreparedState as readPreparedState3,
|
|
66
68
|
secretsPath as secretsPath4
|
|
67
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-NW2UZQV6.js";
|
|
68
70
|
import {
|
|
69
71
|
agentSpecsForCloud,
|
|
70
72
|
currentCloudBaseFingerprint,
|
|
@@ -74,11 +76,11 @@ import {
|
|
|
74
76
|
probeCloudCheckpoint,
|
|
75
77
|
resolveCloudCheckpoint,
|
|
76
78
|
seedAgentVolumesIfFresh
|
|
77
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-PIK47622.js";
|
|
78
80
|
import {
|
|
79
|
-
|
|
81
|
+
ALL_CONNECTORS,
|
|
80
82
|
BOX_STATUS_EVENT,
|
|
81
|
-
|
|
83
|
+
BUILT_IN_DEFAULTS,
|
|
82
84
|
CODEX_CREDENTIALS_BACKUP_FILE,
|
|
83
85
|
ClaudeSessionError,
|
|
84
86
|
CodexSessionError,
|
|
@@ -96,7 +98,6 @@ import {
|
|
|
96
98
|
SHARED_CODEX_VOLUME,
|
|
97
99
|
SHARED_OPENCODE_VOLUME,
|
|
98
100
|
UserConfigError,
|
|
99
|
-
UserFacingError,
|
|
100
101
|
agentboxHomeBytes,
|
|
101
102
|
allCheckpointImagesBytes,
|
|
102
103
|
allocateShellSessionName,
|
|
@@ -165,6 +166,8 @@ import {
|
|
|
165
166
|
mintHostInitiatedToken,
|
|
166
167
|
openBoxInFinder,
|
|
167
168
|
opencodeSessionInfo,
|
|
169
|
+
parseCarrySection,
|
|
170
|
+
parseReplacementsSection,
|
|
168
171
|
parseShellSessionList,
|
|
169
172
|
pauseBox,
|
|
170
173
|
portlessGetUrl,
|
|
@@ -190,7 +193,6 @@ import {
|
|
|
190
193
|
renderStatusTable,
|
|
191
194
|
renderTaskTable,
|
|
192
195
|
resetPortlessCache,
|
|
193
|
-
resolveAgentLauncher,
|
|
194
196
|
resolveBoxImage,
|
|
195
197
|
resolveBoxSize,
|
|
196
198
|
resolveCheckpoint,
|
|
@@ -229,10 +231,13 @@ import {
|
|
|
229
231
|
waitForTmuxPaneContent,
|
|
230
232
|
warmUpClaudeCredentials,
|
|
231
233
|
writeJob
|
|
232
|
-
} from "./chunk-
|
|
234
|
+
} from "./chunk-GXJNJUEV.js";
|
|
233
235
|
import {
|
|
236
|
+
AmbiguousBoxError,
|
|
237
|
+
BoxNotFoundError,
|
|
234
238
|
DEFAULT_BOX_IMAGE,
|
|
235
239
|
STATE_DIR,
|
|
240
|
+
UserFacingError,
|
|
236
241
|
computeDockerContextFingerprint,
|
|
237
242
|
ensureImage,
|
|
238
243
|
hostOpenCommand,
|
|
@@ -241,13 +246,15 @@ import {
|
|
|
241
246
|
readPreparedDockerState,
|
|
242
247
|
readState,
|
|
243
248
|
recordBox,
|
|
244
|
-
|
|
245
|
-
|
|
249
|
+
resolveAgentLauncher,
|
|
250
|
+
resolveBoxRef,
|
|
251
|
+
resolveRuleRefs
|
|
252
|
+
} from "./chunk-DBBUDKKB.js";
|
|
246
253
|
import "./chunk-G3H2L3O2.js";
|
|
247
254
|
|
|
248
255
|
// src/version.ts
|
|
249
|
-
var AGENTBOX_VERSION = true ? "0.
|
|
250
|
-
var AGENTBOX_COMMIT = true ? "
|
|
256
|
+
var AGENTBOX_VERSION = true ? "0.16.0" : "0.0.0-dev";
|
|
257
|
+
var AGENTBOX_COMMIT = true ? "de3c2792d" : "dev";
|
|
251
258
|
|
|
252
259
|
// src/index.ts
|
|
253
260
|
import { Command as Command48 } from "commander";
|
|
@@ -430,6 +437,178 @@ function derivedAgentState(claude) {
|
|
|
430
437
|
return claude.state;
|
|
431
438
|
}
|
|
432
439
|
|
|
440
|
+
// src/lib/agent-answer.ts
|
|
441
|
+
import { createHash } from "crypto";
|
|
442
|
+
var TUI_ID_PREFIX = "tui";
|
|
443
|
+
function inTuiKind(claude) {
|
|
444
|
+
if (claude.state === "working" || claude.state === "compacting") return null;
|
|
445
|
+
if (claude.plan !== void 0) return "plan";
|
|
446
|
+
if (claude.question !== void 0) return "question";
|
|
447
|
+
if (claude.state === "waiting") return "permission";
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
function digestForBlock(claude, kind) {
|
|
451
|
+
let material;
|
|
452
|
+
if (kind === "plan") {
|
|
453
|
+
material = `plan|${claude.plan?.capturedAt ?? ""}|${claude.plan?.plan ?? ""}`;
|
|
454
|
+
} else if (kind === "question") {
|
|
455
|
+
material = `question|${claude.question?.capturedAt ?? ""}|${JSON.stringify(claude.question?.questions ?? [])}`;
|
|
456
|
+
} else {
|
|
457
|
+
material = `permission|${claude.updatedAt ?? ""}`;
|
|
458
|
+
}
|
|
459
|
+
return createHash("sha256").update(material).digest("hex").slice(0, 12);
|
|
460
|
+
}
|
|
461
|
+
function mintTuiId(boxId, claude) {
|
|
462
|
+
const kind = inTuiKind(claude);
|
|
463
|
+
if (kind === null) return null;
|
|
464
|
+
return { id: `${TUI_ID_PREFIX}:${boxId}:${kind}:${digestForBlock(claude, kind)}`, kind };
|
|
465
|
+
}
|
|
466
|
+
function parseTuiId(id) {
|
|
467
|
+
const parts = id.split(":");
|
|
468
|
+
if (parts.length !== 4 || parts[0] !== TUI_ID_PREFIX) return null;
|
|
469
|
+
const [, boxId, kind, digest] = parts;
|
|
470
|
+
if (!boxId || !digest) return null;
|
|
471
|
+
if (kind !== "plan" && kind !== "question" && kind !== "permission") return null;
|
|
472
|
+
return { boxId, kind, digest };
|
|
473
|
+
}
|
|
474
|
+
function isTuiId(id) {
|
|
475
|
+
return id.startsWith(`${TUI_ID_PREFIX}:`);
|
|
476
|
+
}
|
|
477
|
+
var OPTION_ENTER_DELAY_MS = 150;
|
|
478
|
+
function answerKeystrokes(_agent, _kind, decision) {
|
|
479
|
+
if (decision.deny === true) {
|
|
480
|
+
return [{ type: "key", value: "Escape" }];
|
|
481
|
+
}
|
|
482
|
+
if (decision.option !== void 0) {
|
|
483
|
+
return [
|
|
484
|
+
{ type: "literal", value: String(decision.option) },
|
|
485
|
+
{ type: "delay", ms: OPTION_ENTER_DELAY_MS },
|
|
486
|
+
{ type: "key", value: "Enter" }
|
|
487
|
+
];
|
|
488
|
+
}
|
|
489
|
+
return [{ type: "key", value: "Enter" }];
|
|
490
|
+
}
|
|
491
|
+
function resolveQuestionOption(claude, raw) {
|
|
492
|
+
const options = claude.question?.questions?.[0]?.options ?? [];
|
|
493
|
+
const asNum = Number.parseInt(raw, 10);
|
|
494
|
+
if (Number.isFinite(asNum) && String(asNum) === raw.trim()) {
|
|
495
|
+
return asNum >= 1 && asNum <= options.length ? asNum : null;
|
|
496
|
+
}
|
|
497
|
+
const needle = raw.trim().toLowerCase();
|
|
498
|
+
const exact = options.findIndex((o) => o.label.toLowerCase() === needle);
|
|
499
|
+
if (exact !== -1) return exact + 1;
|
|
500
|
+
const prefix = options.findIndex((o) => o.label.toLowerCase().startsWith(needle));
|
|
501
|
+
return prefix !== -1 ? prefix + 1 : null;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// src/lib/drive/tmux.ts
|
|
505
|
+
var TMUX_USER = "vscode";
|
|
506
|
+
async function captureSession(provider, box, session, opts = {}) {
|
|
507
|
+
const argv2 = ["tmux", "capture-pane", opts.ansi ? "-pe" : "-p", "-t", session];
|
|
508
|
+
if (opts.rows) {
|
|
509
|
+
argv2.push("-S", String(opts.rows.from), "-E", String(opts.rows.to));
|
|
510
|
+
}
|
|
511
|
+
const res = await provider.exec(box, argv2, { user: TMUX_USER });
|
|
512
|
+
if (res.exitCode !== 0) {
|
|
513
|
+
throw new Error(failure("capture-pane", session, res.stderr || res.stdout));
|
|
514
|
+
}
|
|
515
|
+
return res.stdout.replace(/\n$/, "");
|
|
516
|
+
}
|
|
517
|
+
async function paneInfo(provider, box, session) {
|
|
518
|
+
const fmt = "#{pane_width},#{pane_height},#{cursor_x},#{cursor_y}";
|
|
519
|
+
const res = await provider.exec(box, ["tmux", "display-message", "-p", "-t", session, fmt], {
|
|
520
|
+
user: TMUX_USER
|
|
521
|
+
});
|
|
522
|
+
if (res.exitCode !== 0) {
|
|
523
|
+
throw new Error(failure("display-message", session, res.stderr || res.stdout));
|
|
524
|
+
}
|
|
525
|
+
const m = /^(\d+),(\d+),(\d+),(\d+)/.exec(res.stdout.trim());
|
|
526
|
+
if (!m) throw new Error(`tmux display-message returned unexpected output: ${res.stdout}`);
|
|
527
|
+
return {
|
|
528
|
+
cols: Number(m[1]),
|
|
529
|
+
rows: Number(m[2]),
|
|
530
|
+
cursor: { x: Number(m[3]), y: Number(m[4]) }
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
async function sendLiteral(provider, box, session, literal) {
|
|
534
|
+
if (literal.length === 0) return;
|
|
535
|
+
const res = await provider.exec(box, ["tmux", "send-keys", "-t", session, "-l", "--", literal], {
|
|
536
|
+
user: TMUX_USER
|
|
537
|
+
});
|
|
538
|
+
if (res.exitCode !== 0) {
|
|
539
|
+
throw new Error(failure("send-keys -l", session, res.stderr || res.stdout));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
async function sendKey(provider, box, session, key) {
|
|
543
|
+
const res = await provider.exec(box, ["tmux", "send-keys", "-t", session, key], {
|
|
544
|
+
user: TMUX_USER
|
|
545
|
+
});
|
|
546
|
+
if (res.exitCode !== 0) {
|
|
547
|
+
throw new Error(failure("send-keys", session, res.stderr || res.stdout));
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async function resizeWindow(provider, box, session, cols, rows) {
|
|
551
|
+
const res = await provider.exec(
|
|
552
|
+
box,
|
|
553
|
+
["tmux", "resize-window", "-t", session, "-x", String(cols), "-y", String(rows)],
|
|
554
|
+
{ user: TMUX_USER }
|
|
555
|
+
);
|
|
556
|
+
if (res.exitCode !== 0) {
|
|
557
|
+
throw new Error(failure("resize-window", session, res.stderr || res.stdout));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async function listSessions(provider, box) {
|
|
561
|
+
const res = await provider.exec(
|
|
562
|
+
box,
|
|
563
|
+
["tmux", "list-sessions", "-F", "#{session_name}"],
|
|
564
|
+
{ user: TMUX_USER }
|
|
565
|
+
);
|
|
566
|
+
if (res.exitCode !== 0) return [];
|
|
567
|
+
return res.stdout.split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
568
|
+
}
|
|
569
|
+
function failure(op, session, detail) {
|
|
570
|
+
const tail = detail.trim();
|
|
571
|
+
return `tmux ${op} failed for session '${session}'${tail ? `: ${tail}` : ""}`;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/lib/drive/session.ts
|
|
575
|
+
var AGENT_SESSION_PRIORITY = ["claude", "codex", "opencode"];
|
|
576
|
+
async function resolveDriveSession(provider, box, explicit) {
|
|
577
|
+
if (await provider.probeState(box) === "paused") {
|
|
578
|
+
process.stderr.write(`drive: box ${box.name} is paused; unpausing
|
|
579
|
+
`);
|
|
580
|
+
await provider.resume(box);
|
|
581
|
+
}
|
|
582
|
+
const sessions = await listSessions(provider, box);
|
|
583
|
+
if (explicit !== void 0 && explicit !== "") {
|
|
584
|
+
if (!sessions.includes(explicit)) {
|
|
585
|
+
throw new SessionNotFoundError(explicit, sessions);
|
|
586
|
+
}
|
|
587
|
+
return { name: explicit, available: sessions };
|
|
588
|
+
}
|
|
589
|
+
for (const candidate of AGENT_SESSION_PRIORITY) {
|
|
590
|
+
if (sessions.includes(candidate)) {
|
|
591
|
+
return { name: candidate, available: sessions };
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (sessions.length === 1 && sessions[0]) {
|
|
595
|
+
return { name: sessions[0], available: sessions };
|
|
596
|
+
}
|
|
597
|
+
throw new SessionNotFoundError(void 0, sessions);
|
|
598
|
+
}
|
|
599
|
+
var SessionNotFoundError = class extends Error {
|
|
600
|
+
wanted;
|
|
601
|
+
available;
|
|
602
|
+
constructor(wanted, available) {
|
|
603
|
+
const head = wanted ? `no tmux session '${wanted}' in this box` : "no agent tmux session running in this box";
|
|
604
|
+
const tail = available.length ? ` (running: ${available.join(", ")})` : " (tmux server not running or no sessions)";
|
|
605
|
+
super(head + tail);
|
|
606
|
+
this.name = "SessionNotFoundError";
|
|
607
|
+
this.wanted = wanted;
|
|
608
|
+
this.available = available;
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
433
612
|
// src/lib/wait/events.ts
|
|
434
613
|
var POLL_INTERVAL_MS = 500;
|
|
435
614
|
var WaitTimeoutError = class extends Error {
|
|
@@ -606,9 +785,209 @@ var agentGetPlanQuestionCommand = new Command("get-plan-question").description("
|
|
|
606
785
|
handleLifecycleError(err);
|
|
607
786
|
}
|
|
608
787
|
});
|
|
788
|
+
var agentApprovalsCommand = new Command("approvals").description(
|
|
789
|
+
"List everything a box is blocked on: relay host-action approvals (git push, cp host<->box, gh PR writes, checkpoint) AND the agent's in-TUI prompts (plan approval, question, tool permission). Each row carries an id to pass to `agent approve`."
|
|
790
|
+
).argument("[box]", "box ref (default: only box in this project)").option("--json", "emit the pending approvals as a JSON array").option(
|
|
791
|
+
"--wait <ms>",
|
|
792
|
+
"block until at least one approval is pending (or this wall-clock cap elapses), then print"
|
|
793
|
+
).action(async (boxRef, opts) => {
|
|
794
|
+
try {
|
|
795
|
+
const box = await resolveBoxOrExit(boxRef);
|
|
796
|
+
const relayUrl = (await ensureRelay()).hostUrl;
|
|
797
|
+
const waitMs = opts.wait !== void 0 ? parsePositiveInt(opts.wait, "--wait") : void 0;
|
|
798
|
+
let rows = await gatherApprovals(relayUrl, box);
|
|
799
|
+
if (waitMs !== void 0 && rows.length === 0) {
|
|
800
|
+
const start = Date.now();
|
|
801
|
+
while (rows.length === 0 && Date.now() - start < waitMs) {
|
|
802
|
+
await sleep2(Math.min(500, waitMs - (Date.now() - start)));
|
|
803
|
+
rows = await gatherApprovals(relayUrl, box);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (opts.json === true) {
|
|
807
|
+
process.stdout.write(JSON.stringify(rows) + "\n");
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (rows.length === 0) {
|
|
811
|
+
log3.info("nothing pending for this box (no relay approvals, agent not parked on a prompt)");
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
for (const row2 of rows) {
|
|
815
|
+
process.stdout.write(approvalDisplay(row2) + "\n");
|
|
816
|
+
}
|
|
817
|
+
} catch (err) {
|
|
818
|
+
handleLifecycleError(err);
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
var agentApproveCommand = new Command("approve").description(
|
|
822
|
+
"Answer a pending approval by id (see `agent approvals`). The id is a safety token: you answer the exact prompt you inspected, and if a different one has since taken its place the approve is refused. Works for both relay host-action approvals and the agent's in-TUI prompts (plan / question / tool permission). Approves by default; --deny rejects."
|
|
823
|
+
).argument("<id>", "approval id from `agent approvals` (relay UUID or a tui:... id)").option("--deny", "reject instead of approving").option("--cancel", "relay approvals only: dismiss (treated as denied; marks it cancelled)").option(
|
|
824
|
+
"--option <n|label>",
|
|
825
|
+
"in-TUI question/permission: pick this 1-based option (or match its label) instead of the default"
|
|
826
|
+
).action(async (id, opts) => {
|
|
827
|
+
try {
|
|
828
|
+
if (isTuiId(id)) {
|
|
829
|
+
await approveInTui(id, opts);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
await approveRelay(id, opts);
|
|
833
|
+
} catch (err) {
|
|
834
|
+
handleLifecycleError(err);
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
async function approveRelay(id, opts) {
|
|
838
|
+
const relayUrl = (await ensureRelay()).hostUrl;
|
|
839
|
+
const cancelled = opts.cancel === true;
|
|
840
|
+
const answer = opts.deny === true || cancelled ? "n" : "y";
|
|
841
|
+
const url = new URL("/admin/prompts/answer", relayUrl);
|
|
842
|
+
const res = await fetch(url, {
|
|
843
|
+
method: "POST",
|
|
844
|
+
headers: { "Content-Type": "application/json" },
|
|
845
|
+
body: JSON.stringify({ id, answer, cancelled: cancelled || void 0 })
|
|
846
|
+
});
|
|
847
|
+
if (res.status === 204) {
|
|
848
|
+
log3.success(`approval ${id}: ${answer === "y" ? "approved" : "denied"}`);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (res.status === 404) {
|
|
852
|
+
log3.info(`approval ${id} already resolved (or expired)`);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
log3.error(`relay /admin/prompts/answer: HTTP ${String(res.status)}`);
|
|
856
|
+
process.exit(1);
|
|
857
|
+
}
|
|
858
|
+
async function approveInTui(id, opts) {
|
|
859
|
+
const parsed = parseTuiId(id);
|
|
860
|
+
if (!parsed) {
|
|
861
|
+
log3.error(`malformed in-TUI approval id: ${id}`);
|
|
862
|
+
process.exit(2);
|
|
863
|
+
}
|
|
864
|
+
if (opts.cancel === true) {
|
|
865
|
+
log3.error("--cancel applies to relay approvals only; use --deny for in-TUI prompts");
|
|
866
|
+
process.exit(2);
|
|
867
|
+
}
|
|
868
|
+
const box = await resolveBoxOrExit(parsed.boxId);
|
|
869
|
+
const status = await readBoxStatus(box);
|
|
870
|
+
const claude = status?.claude;
|
|
871
|
+
const current = claude ? mintTuiId(box.id, claude) : null;
|
|
872
|
+
if (!current || current.id !== id) {
|
|
873
|
+
log3.error(
|
|
874
|
+
`approval ${id} is no longer the pending prompt for ${box.name} (it changed or was answered) \u2014 re-run \`agentbox agent approvals ${box.name}\``
|
|
875
|
+
);
|
|
876
|
+
process.exit(1);
|
|
877
|
+
}
|
|
878
|
+
let option;
|
|
879
|
+
if (opts.option !== void 0) {
|
|
880
|
+
if (parsed.kind === "question" && claude) {
|
|
881
|
+
const resolved = resolveQuestionOption(claude, opts.option);
|
|
882
|
+
if (resolved === null) {
|
|
883
|
+
const labels = (claude.question?.questions?.[0]?.options ?? []).map((o) => o.label);
|
|
884
|
+
log3.error(`--option '${opts.option}' did not match an option (have: ${labels.join(" | ")})`);
|
|
885
|
+
process.exit(2);
|
|
886
|
+
}
|
|
887
|
+
option = resolved;
|
|
888
|
+
} else {
|
|
889
|
+
const n = Number.parseInt(opts.option, 10);
|
|
890
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
891
|
+
log3.error(`--option must be a 1-based number for a ${parsed.kind} prompt (got: ${opts.option})`);
|
|
892
|
+
process.exit(2);
|
|
893
|
+
}
|
|
894
|
+
option = n;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
const provider = await providerForBox(box);
|
|
898
|
+
const session = await resolveDriveSession(provider, box, void 0);
|
|
899
|
+
const agent = agentKindForSession(session.name);
|
|
900
|
+
const steps = answerKeystrokes(agent, parsed.kind, { option, deny: opts.deny });
|
|
901
|
+
await runAnswerSteps(provider, box, session.name, steps);
|
|
902
|
+
const verb = opts.deny === true ? "denied" : option !== void 0 ? `answered (option ${String(option)})` : "approved";
|
|
903
|
+
log3.success(`${parsed.kind} prompt on ${box.name}: ${verb}`);
|
|
904
|
+
}
|
|
905
|
+
function agentKindForSession(session) {
|
|
906
|
+
if (session === "codex") return "codex";
|
|
907
|
+
if (session === "opencode") return "opencode";
|
|
908
|
+
return "claude";
|
|
909
|
+
}
|
|
910
|
+
async function runAnswerSteps(provider, box, session, steps) {
|
|
911
|
+
for (const step of steps) {
|
|
912
|
+
if (step.type === "literal") await sendLiteral(provider, box, session, step.value);
|
|
913
|
+
else if (step.type === "key") await sendKey(provider, box, session, step.value);
|
|
914
|
+
else await sleep2(step.ms);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
609
917
|
agentCommand.addCommand(agentStateCommand);
|
|
610
918
|
agentCommand.addCommand(agentWaitForCommand);
|
|
611
919
|
agentCommand.addCommand(agentGetPlanQuestionCommand);
|
|
920
|
+
agentCommand.addCommand(agentApprovalsCommand);
|
|
921
|
+
agentCommand.addCommand(agentApproveCommand);
|
|
922
|
+
async function gatherApprovals(relayUrl, box) {
|
|
923
|
+
const rows = [];
|
|
924
|
+
const relay = await fetchRelayApprovals(relayUrl, box.id);
|
|
925
|
+
for (const ev of relay) {
|
|
926
|
+
rows.push({
|
|
927
|
+
id: ev.id,
|
|
928
|
+
kind: "host-action",
|
|
929
|
+
command: ev.context?.command,
|
|
930
|
+
argv: ev.context?.argv,
|
|
931
|
+
cwd: ev.context?.cwd,
|
|
932
|
+
message: ev.message,
|
|
933
|
+
detail: ev.detail,
|
|
934
|
+
defaultAnswer: ev.defaultAnswer
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
const claude = (await readBoxStatus(box))?.claude;
|
|
938
|
+
const tui = claude ? mintTuiId(box.id, claude) : null;
|
|
939
|
+
if (claude && tui) {
|
|
940
|
+
if (tui.kind === "plan") {
|
|
941
|
+
rows.push({ id: tui.id, kind: "plan", message: "Approve plan?", plan: claude.plan?.plan ?? "" });
|
|
942
|
+
} else if (tui.kind === "question") {
|
|
943
|
+
const q = claude.question?.questions?.[0];
|
|
944
|
+
rows.push({
|
|
945
|
+
id: tui.id,
|
|
946
|
+
kind: "question",
|
|
947
|
+
message: q?.question ?? "Answer question?",
|
|
948
|
+
options: (q?.options ?? []).map((o) => o.label)
|
|
949
|
+
});
|
|
950
|
+
} else {
|
|
951
|
+
rows.push({
|
|
952
|
+
id: tui.id,
|
|
953
|
+
kind: "permission",
|
|
954
|
+
message: "Tool-permission prompt (screen-driven; inspect with `agentbox drive snapshot`)",
|
|
955
|
+
state: claude.state
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return rows;
|
|
960
|
+
}
|
|
961
|
+
async function fetchRelayApprovals(relayUrl, boxId) {
|
|
962
|
+
const url = new URL("/admin/prompts", relayUrl);
|
|
963
|
+
url.searchParams.set("boxId", boxId);
|
|
964
|
+
const res = await fetch(url);
|
|
965
|
+
if (!res.ok) throw new Error(`relay /admin/prompts: HTTP ${String(res.status)}`);
|
|
966
|
+
const body = await res.json();
|
|
967
|
+
return body.prompts ?? [];
|
|
968
|
+
}
|
|
969
|
+
function approvalDisplay(row2) {
|
|
970
|
+
if (row2.kind === "host-action") {
|
|
971
|
+
const cmd = row2.command ?? row2.message;
|
|
972
|
+
const argv2 = row2.argv?.length ? ` ${row2.argv.join(" ")}` : "";
|
|
973
|
+
const detail = row2.detail ? ` (${row2.detail})` : "";
|
|
974
|
+
return `${row2.id} [host-action] ${cmd}${argv2}${detail}`;
|
|
975
|
+
}
|
|
976
|
+
if (row2.kind === "plan") {
|
|
977
|
+
return `${row2.id} [plan] ${firstLine(row2.plan)}`;
|
|
978
|
+
}
|
|
979
|
+
if (row2.kind === "question") {
|
|
980
|
+
return `${row2.id} [question] ${row2.message} {${row2.options.join(" | ")}}`;
|
|
981
|
+
}
|
|
982
|
+
return `${row2.id} [permission] ${row2.message}`;
|
|
983
|
+
}
|
|
984
|
+
function firstLine(s) {
|
|
985
|
+
const line = s.split("\n", 1)[0] ?? "";
|
|
986
|
+
return line.length > 100 ? line.slice(0, 99) + "\u2026" : line;
|
|
987
|
+
}
|
|
988
|
+
function sleep2(ms) {
|
|
989
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
990
|
+
}
|
|
612
991
|
function emitMatch(claude, asJson) {
|
|
613
992
|
if (asJson) {
|
|
614
993
|
process.stdout.write(JSON.stringify(claude) + "\n");
|
|
@@ -727,25 +1106,149 @@ function buildPromptArgs(agentKind, prompt, userArgs) {
|
|
|
727
1106
|
}
|
|
728
1107
|
|
|
729
1108
|
// src/lib/carry-resync.ts
|
|
730
|
-
import { join as
|
|
1109
|
+
import { join as join5 } from "path";
|
|
731
1110
|
|
|
732
1111
|
// src/lib/carry-resolve.ts
|
|
733
|
-
import { realpath, stat as
|
|
1112
|
+
import { realpath, stat as stat3 } from "fs/promises";
|
|
734
1113
|
import { homedir as homedir2 } from "os";
|
|
735
|
-
import { isAbsolute, join as
|
|
736
|
-
|
|
1114
|
+
import { isAbsolute, join as join4, normalize, relative, resolve } from "path";
|
|
1115
|
+
|
|
1116
|
+
// src/lib/dir-breakdown.ts
|
|
1117
|
+
import { readdir, stat as stat2 } from "fs/promises";
|
|
1118
|
+
import { join as join3 } from "path";
|
|
1119
|
+
var DEFAULT_CP_EXCLUDES = [
|
|
1120
|
+
".git",
|
|
1121
|
+
"node_modules",
|
|
1122
|
+
"bin",
|
|
1123
|
+
"obj",
|
|
1124
|
+
"packages",
|
|
1125
|
+
"dist",
|
|
1126
|
+
".next",
|
|
1127
|
+
"target"
|
|
1128
|
+
];
|
|
1129
|
+
function isBareName(token) {
|
|
1130
|
+
return !token.includes("/") && !token.includes("*") && !token.includes("?");
|
|
1131
|
+
}
|
|
1132
|
+
function toTarExcludes(tokens) {
|
|
1133
|
+
const out = [];
|
|
1134
|
+
for (const t of tokens) {
|
|
1135
|
+
if (isBareName(t)) {
|
|
1136
|
+
out.push(`*/${t}`, t);
|
|
1137
|
+
} else {
|
|
1138
|
+
out.push(t);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
return out;
|
|
1142
|
+
}
|
|
1143
|
+
function effectiveExcludes(userTokens, useDefaults) {
|
|
1144
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1145
|
+
const out = [];
|
|
1146
|
+
const add = (t) => {
|
|
1147
|
+
if (t && !seen.has(t)) {
|
|
1148
|
+
seen.add(t);
|
|
1149
|
+
out.push(t);
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
if (useDefaults) DEFAULT_CP_EXCLUDES.forEach(add);
|
|
1153
|
+
userTokens.forEach(add);
|
|
1154
|
+
return out;
|
|
1155
|
+
}
|
|
1156
|
+
function globToRegExp(glob) {
|
|
1157
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1158
|
+
return new RegExp(`^${escaped}$`);
|
|
1159
|
+
}
|
|
1160
|
+
function isPathExcluded(relPath, tokens) {
|
|
1161
|
+
const segs = relPath.split("/");
|
|
1162
|
+
for (const t of tokens) {
|
|
1163
|
+
if (isBareName(t)) {
|
|
1164
|
+
if (segs.includes(t)) return true;
|
|
1165
|
+
} else if (globToRegExp(t).test(relPath)) {
|
|
1166
|
+
return true;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return false;
|
|
1170
|
+
}
|
|
1171
|
+
async function buildNode(abs, rel, tokens, seen) {
|
|
1172
|
+
let bytes = 0;
|
|
1173
|
+
const children = [];
|
|
1174
|
+
let entries;
|
|
1175
|
+
try {
|
|
1176
|
+
entries = await readdir(abs);
|
|
1177
|
+
} catch {
|
|
1178
|
+
return { path: rel || ".", bytes: 0, children };
|
|
1179
|
+
}
|
|
1180
|
+
for (const name of entries) {
|
|
1181
|
+
const childRel = rel ? `${rel}/${name}` : name;
|
|
1182
|
+
if (isPathExcluded(childRel, tokens)) continue;
|
|
1183
|
+
const full = join3(abs, name);
|
|
1184
|
+
let st;
|
|
1185
|
+
try {
|
|
1186
|
+
st = await stat2(full);
|
|
1187
|
+
} catch {
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
const key = `${String(st.dev)}:${String(st.ino)}`;
|
|
1191
|
+
if (seen.has(key)) continue;
|
|
1192
|
+
seen.add(key);
|
|
1193
|
+
if (st.isDirectory()) {
|
|
1194
|
+
const child = await buildNode(full, childRel, tokens, seen);
|
|
1195
|
+
bytes += child.bytes;
|
|
1196
|
+
children.push(child);
|
|
1197
|
+
} else if (st.isFile()) {
|
|
1198
|
+
bytes += st.size;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
return { path: rel || ".", bytes, children };
|
|
1202
|
+
}
|
|
1203
|
+
async function measureCopy(absSrc, tokens, opts = {}) {
|
|
1204
|
+
const st = await stat2(absSrc);
|
|
1205
|
+
if (!st.isDirectory()) {
|
|
1206
|
+
return { totalBytes: st.size, isDir: false, treeLines: [], topChildren: [] };
|
|
1207
|
+
}
|
|
1208
|
+
const root = await buildNode(absSrc, "", tokens, /* @__PURE__ */ new Set());
|
|
1209
|
+
const maxDepth = opts.maxDepth ?? 3;
|
|
1210
|
+
const floor = opts.floorBytes ?? 10 * 1024 * 1024;
|
|
1211
|
+
const perLevel = opts.perLevel ?? 8;
|
|
1212
|
+
const lines = [];
|
|
1213
|
+
const render = (node, depth, label) => {
|
|
1214
|
+
const pad4 = " ".repeat(depth);
|
|
1215
|
+
lines.push(` ${fmtBytes(node.bytes).padStart(8)} ${pad4}${label}`);
|
|
1216
|
+
if (depth >= maxDepth) return;
|
|
1217
|
+
const kids = [...node.children].sort((a, b) => b.bytes - a.bytes).filter((c) => c.bytes >= floor).slice(0, perLevel);
|
|
1218
|
+
for (const kid of kids) {
|
|
1219
|
+
render(kid, depth + 1, `./${kid.path}`);
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
render(root, 0, "./");
|
|
1223
|
+
const topChildren = [...root.children].sort((a, b) => b.bytes - a.bytes).map((c) => ({ path: c.path, bytes: c.bytes }));
|
|
1224
|
+
return { totalBytes: root.bytes, isDir: true, treeLines: lines, topChildren };
|
|
1225
|
+
}
|
|
1226
|
+
function fmtBytes(n) {
|
|
1227
|
+
if (n < 1024) return `${String(n)} B`;
|
|
1228
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
1229
|
+
let v = n / 1024;
|
|
1230
|
+
let i = 0;
|
|
1231
|
+
while (v >= 1024 && i < units.length - 1) {
|
|
1232
|
+
v /= 1024;
|
|
1233
|
+
i += 1;
|
|
1234
|
+
}
|
|
1235
|
+
return `${v >= 10 || Number.isInteger(v) ? v.toFixed(0) : v.toFixed(1)} ${units[i]}`;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// src/lib/carry-resolve.ts
|
|
737
1239
|
var DENYLIST_DEST_PREFIXES = ["/proc", "/sys", "/dev"];
|
|
738
1240
|
var DENYLIST_DEST_EXACT = /* @__PURE__ */ new Set(["/etc/passwd", "/etc/shadow"]);
|
|
739
1241
|
async function resolveCarry(items, opts) {
|
|
740
1242
|
const home = opts.homeDir ?? homedir2();
|
|
741
|
-
const cap = opts.maxBytes ??
|
|
1243
|
+
const cap = opts.maxBytes ?? BUILT_IN_DEFAULTS.box.cpMaxBytes;
|
|
742
1244
|
const projectRoot = opts.projectRoot;
|
|
1245
|
+
const replacements = opts.replacements ?? {};
|
|
743
1246
|
const entries = [];
|
|
744
1247
|
const errors = [];
|
|
745
1248
|
for (const [i, item] of items.entries()) {
|
|
746
1249
|
const where = `carry[${String(i)}]`;
|
|
747
1250
|
try {
|
|
748
|
-
const entry = await resolveOne(item, { projectRoot, home, cap, where });
|
|
1251
|
+
const entry = await resolveOne(item, { projectRoot, home, cap, where, replacements });
|
|
749
1252
|
entries.push(entry);
|
|
750
1253
|
} catch (err) {
|
|
751
1254
|
errors.push(err instanceof Error ? err.message : String(err));
|
|
@@ -753,13 +1256,6 @@ async function resolveCarry(items, opts) {
|
|
|
753
1256
|
}
|
|
754
1257
|
return { entries, errors };
|
|
755
1258
|
}
|
|
756
|
-
function readMaxBytesFromEnv() {
|
|
757
|
-
const raw = process.env.AGENTBOX_CARRY_MAX_BYTES;
|
|
758
|
-
if (!raw) return void 0;
|
|
759
|
-
const n = Number(raw);
|
|
760
|
-
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
761
|
-
return Math.floor(n);
|
|
762
|
-
}
|
|
763
1259
|
async function resolveOne(item, ctx) {
|
|
764
1260
|
const absSrc = expandHostPath(item.src, ctx);
|
|
765
1261
|
if (containsDotDot(absSrc)) {
|
|
@@ -770,9 +1266,18 @@ async function resolveOne(item, ctx) {
|
|
|
770
1266
|
const rawSrc = item.src;
|
|
771
1267
|
const rawDest = item.dest;
|
|
772
1268
|
const absDest = item.dest;
|
|
1269
|
+
const hasReplaceOpts = !!(item.replaceEnvs || item.replace || item.rules);
|
|
1270
|
+
const replaceRules = [
|
|
1271
|
+
...resolveRuleRefs(item.rules ?? [], ctx.replacements, `${ctx.where}.rules`),
|
|
1272
|
+
...item.replace ?? []
|
|
1273
|
+
];
|
|
1274
|
+
const replaceFields = {
|
|
1275
|
+
...item.replaceEnvs ? { replaceEnvs: true } : {},
|
|
1276
|
+
...replaceRules.length > 0 ? { replace: replaceRules } : {}
|
|
1277
|
+
};
|
|
773
1278
|
let st;
|
|
774
1279
|
try {
|
|
775
|
-
st = await
|
|
1280
|
+
st = await stat3(absSrc);
|
|
776
1281
|
} catch (err) {
|
|
777
1282
|
if (err.code === "ENOENT") {
|
|
778
1283
|
if (optional) {
|
|
@@ -806,10 +1311,17 @@ async function resolveOne(item, ctx) {
|
|
|
806
1311
|
} catch {
|
|
807
1312
|
}
|
|
808
1313
|
if (st.isDirectory()) {
|
|
809
|
-
|
|
1314
|
+
if (hasReplaceOpts) {
|
|
1315
|
+
throw new Error(
|
|
1316
|
+
`${ctx.where}: replaceEnvs/replace/rules are file-only (src "${absSrc}" is a directory)`
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
const tokens = effectiveExcludes(item.exclude ?? [], true);
|
|
1320
|
+
const tarPatterns = toTarExcludes(tokens);
|
|
1321
|
+
const bytes = await dirSizeCapped(absSrc, ctx.cap, tokens);
|
|
810
1322
|
if (bytes > ctx.cap) {
|
|
811
1323
|
throw new Error(
|
|
812
|
-
`${ctx.where}: dir "${absSrc}" exceeds ${String(ctx.cap)} bytes (
|
|
1324
|
+
`${ctx.where}: dir "${absSrc}" exceeds ${String(ctx.cap)} bytes after excludes (add carry exclude: patterns, raise box.cpMaxBytes, or narrow the path)`
|
|
813
1325
|
);
|
|
814
1326
|
}
|
|
815
1327
|
return {
|
|
@@ -821,6 +1333,7 @@ async function resolveOne(item, ctx) {
|
|
|
821
1333
|
bytes,
|
|
822
1334
|
...item.mode !== void 0 ? { mode: item.mode } : {},
|
|
823
1335
|
...item.user !== void 0 ? { user: item.user } : {},
|
|
1336
|
+
...tarPatterns.length > 0 ? { exclude: tarPatterns } : {},
|
|
824
1337
|
optional,
|
|
825
1338
|
...symlinkInfo ? { symlinkInfo } : {}
|
|
826
1339
|
};
|
|
@@ -828,7 +1341,7 @@ async function resolveOne(item, ctx) {
|
|
|
828
1341
|
if (st.isFile()) {
|
|
829
1342
|
if (st.size > ctx.cap) {
|
|
830
1343
|
throw new Error(
|
|
831
|
-
`${ctx.where}: file "${absSrc}" is ${String(st.size)} bytes, exceeds cap ${String(ctx.cap)} (
|
|
1344
|
+
`${ctx.where}: file "${absSrc}" is ${String(st.size)} bytes, exceeds cap ${String(ctx.cap)} (raise box.cpMaxBytes)`
|
|
832
1345
|
);
|
|
833
1346
|
}
|
|
834
1347
|
return {
|
|
@@ -841,7 +1354,8 @@ async function resolveOne(item, ctx) {
|
|
|
841
1354
|
...item.mode !== void 0 ? { mode: item.mode } : {},
|
|
842
1355
|
...item.user !== void 0 ? { user: item.user } : {},
|
|
843
1356
|
optional,
|
|
844
|
-
...symlinkInfo ? { symlinkInfo } : {}
|
|
1357
|
+
...symlinkInfo ? { symlinkInfo } : {},
|
|
1358
|
+
...replaceFields
|
|
845
1359
|
};
|
|
846
1360
|
}
|
|
847
1361
|
throw new Error(`${ctx.where}: src "${absSrc}" is neither a regular file nor a directory`);
|
|
@@ -893,24 +1407,26 @@ async function realpathSafe(p) {
|
|
|
893
1407
|
return resolve(p);
|
|
894
1408
|
}
|
|
895
1409
|
}
|
|
896
|
-
async function dirSizeCapped(dir, cap) {
|
|
1410
|
+
async function dirSizeCapped(dir, cap, exclude = []) {
|
|
897
1411
|
let total = 0;
|
|
898
1412
|
const seen = /* @__PURE__ */ new Set();
|
|
899
1413
|
async function walk(d) {
|
|
900
1414
|
if (total > cap) return;
|
|
901
|
-
const { readdir:
|
|
1415
|
+
const { readdir: readdir4 } = await import("fs/promises");
|
|
902
1416
|
let names;
|
|
903
1417
|
try {
|
|
904
|
-
names = await
|
|
1418
|
+
names = await readdir4(d);
|
|
905
1419
|
} catch {
|
|
906
1420
|
return;
|
|
907
1421
|
}
|
|
908
1422
|
for (const name of names) {
|
|
909
1423
|
if (total > cap) return;
|
|
910
|
-
const full =
|
|
1424
|
+
const full = join4(d, name);
|
|
1425
|
+
const rel = relative(dir, full).split(/[\\/]/).join("/");
|
|
1426
|
+
if (exclude.length > 0 && isPathExcluded(rel, exclude)) continue;
|
|
911
1427
|
let st;
|
|
912
1428
|
try {
|
|
913
|
-
st = await
|
|
1429
|
+
st = await stat3(full);
|
|
914
1430
|
} catch {
|
|
915
1431
|
continue;
|
|
916
1432
|
}
|
|
@@ -935,9 +1451,13 @@ async function resyncCarryFiles(args) {
|
|
|
935
1451
|
});
|
|
936
1452
|
const prior = args.box.carry?.entries ?? [];
|
|
937
1453
|
if (prior.length === 0) return { recopied: 0, skippedNew: 0 };
|
|
938
|
-
const items = await loadCarrySection(
|
|
1454
|
+
const items = await loadCarrySection(join5(args.projectRoot, "agentbox.yaml"));
|
|
939
1455
|
if (items.length === 0) return { recopied: 0, skippedNew: 0 };
|
|
940
|
-
const
|
|
1456
|
+
const cfg = await loadEffectiveConfig(args.projectRoot);
|
|
1457
|
+
const resolved = await resolveCarry(items, {
|
|
1458
|
+
projectRoot: args.projectRoot,
|
|
1459
|
+
maxBytes: cfg.effective.box.cpMaxBytes
|
|
1460
|
+
});
|
|
941
1461
|
if (resolved.errors.length > 0) {
|
|
942
1462
|
log47(`carry: resync skipped (resolve errors: ${resolved.errors.length})`);
|
|
943
1463
|
return { recopied: 0, skippedNew: 0 };
|
|
@@ -1072,6 +1592,7 @@ async function submitQueueJob(input) {
|
|
|
1072
1592
|
createOpts: input.createOpts,
|
|
1073
1593
|
maxConcurrent: ceiling,
|
|
1074
1594
|
...maxWorking !== void 0 ? { maxWorking } : {},
|
|
1595
|
+
...input.openTerminal !== void 0 ? { openTerminal: input.openTerminal } : {},
|
|
1075
1596
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1076
1597
|
logPath: queueLogPath(id)
|
|
1077
1598
|
};
|
|
@@ -1126,6 +1647,58 @@ function postEnqueue(id) {
|
|
|
1126
1647
|
});
|
|
1127
1648
|
}
|
|
1128
1649
|
|
|
1650
|
+
// src/terminal/queue-open.ts
|
|
1651
|
+
function captureOpenTerminalContext(mode, env = process.env, cwd = process.cwd()) {
|
|
1652
|
+
if (mode === "none") return void 0;
|
|
1653
|
+
const host = detectHostTerminal(env);
|
|
1654
|
+
if (host === "unknown") return void 0;
|
|
1655
|
+
const base = { host, mode, cwd };
|
|
1656
|
+
if (host === "tmux") {
|
|
1657
|
+
return { ...base, host, tmuxSocket: env["TMUX"], tmuxPane: env["TMUX_PANE"] };
|
|
1658
|
+
}
|
|
1659
|
+
if (host === "cmux") {
|
|
1660
|
+
return {
|
|
1661
|
+
...base,
|
|
1662
|
+
host,
|
|
1663
|
+
cmuxSocket: env["CMUX_SOCKET_PATH"],
|
|
1664
|
+
cmuxBundledCli: cmuxBinary(env),
|
|
1665
|
+
cmuxSurfaceId: env["CMUX_SURFACE_ID"],
|
|
1666
|
+
cmuxWorkspaceId: env["CMUX_WORKSPACE_ID"]
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
return { ...base, host };
|
|
1670
|
+
}
|
|
1671
|
+
function spawnQueuedOpenTerminal(ctx, argv2, title) {
|
|
1672
|
+
if (ctx.host === "tmux") {
|
|
1673
|
+
return spawnInNewTerminal({
|
|
1674
|
+
host: "tmux",
|
|
1675
|
+
mode: ctx.mode,
|
|
1676
|
+
argv: argv2,
|
|
1677
|
+
cwd: ctx.cwd,
|
|
1678
|
+
title,
|
|
1679
|
+
env: ctx.tmuxSocket ? { ...process.env, TMUX: ctx.tmuxSocket } : process.env,
|
|
1680
|
+
tmuxTarget: ctx.tmuxPane
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
if (ctx.host === "cmux") {
|
|
1684
|
+
const env = { ...process.env };
|
|
1685
|
+
if (ctx.cmuxSocket) env["CMUX_SOCKET_PATH"] = ctx.cmuxSocket;
|
|
1686
|
+
if (ctx.cmuxBundledCli) env["CMUX_BUNDLED_CLI_PATH"] = ctx.cmuxBundledCli;
|
|
1687
|
+
return spawnInNewTerminal({
|
|
1688
|
+
host: "cmux",
|
|
1689
|
+
mode: ctx.mode,
|
|
1690
|
+
argv: argv2,
|
|
1691
|
+
cwd: ctx.cwd,
|
|
1692
|
+
title,
|
|
1693
|
+
env,
|
|
1694
|
+
cmuxTargetSurface: ctx.cmuxSurfaceId,
|
|
1695
|
+
cmuxTargetWorkspace: ctx.cmuxWorkspaceId,
|
|
1696
|
+
cmuxWorkspaceFallback: true
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
return spawnInNewTerminal({ host: "iterm2", mode: ctx.mode, argv: argv2, cwd: ctx.cwd, title });
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1129
1702
|
// src/commands/_attach-in.ts
|
|
1130
1703
|
var VALUES = ["split", "window", "tab", "same"];
|
|
1131
1704
|
var ATTACH_IN_HELP = "where to open the attached session: split | window | tab | same (default from attach.openIn, built-in: split). Only effective when running inside tmux or iTerm2; falls back to inline attach otherwise.";
|
|
@@ -1366,14 +1939,15 @@ async function cloudAgentCreate(args) {
|
|
|
1366
1939
|
}
|
|
1367
1940
|
|
|
1368
1941
|
// src/lib/carry-gate.ts
|
|
1369
|
-
import {
|
|
1942
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1943
|
+
import { join as join6 } from "path";
|
|
1370
1944
|
import { log as log5 } from "@clack/prompts";
|
|
1371
1945
|
|
|
1372
1946
|
// src/carry-prompt.ts
|
|
1373
1947
|
import { isCancel, log as log4, select } from "@clack/prompts";
|
|
1374
1948
|
|
|
1375
1949
|
// src/fmt.ts
|
|
1376
|
-
function
|
|
1950
|
+
function fmtBytes2(n) {
|
|
1377
1951
|
if (n === null || n === void 0) return "n/a";
|
|
1378
1952
|
if (n < 1024) return `${String(n)} B`;
|
|
1379
1953
|
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KiB`;
|
|
@@ -1449,7 +2023,7 @@ function printSummary(entries) {
|
|
|
1449
2023
|
if (e.mode !== void 0) flags.push(`mode ${e.mode.toString(8).padStart(4, "0")}`);
|
|
1450
2024
|
if (e.user !== void 0 && e.user !== 1e3) flags.push(`user ${String(e.user)}`);
|
|
1451
2025
|
if (e.symlinkInfo === "outside-home") flags.push("symlink \u2192 outside $HOME!");
|
|
1452
|
-
const size = e.kind === "missing" ? "\u2014" :
|
|
2026
|
+
const size = e.kind === "missing" ? "\u2014" : fmtBytes2(e.bytes ?? 0);
|
|
1453
2027
|
rows.push(
|
|
1454
2028
|
` ${pad(e.rawSrc, srcW)} \u2192 ${pad(e.rawDest, destW)} ${pad(size, 9)} ${flags.join(", ")}`
|
|
1455
2029
|
);
|
|
@@ -1465,10 +2039,22 @@ function pad(s, w) {
|
|
|
1465
2039
|
async function runCarryGate(args) {
|
|
1466
2040
|
const emit = args.onLog ?? (() => {
|
|
1467
2041
|
});
|
|
1468
|
-
const yamlPath =
|
|
1469
|
-
|
|
2042
|
+
const yamlPath = join6(args.projectRoot, "agentbox.yaml");
|
|
2043
|
+
let yamlText = "";
|
|
2044
|
+
try {
|
|
2045
|
+
yamlText = await readFile2(yamlPath, "utf8");
|
|
2046
|
+
} catch (err) {
|
|
2047
|
+
if (err.code !== "ENOENT") throw err;
|
|
2048
|
+
}
|
|
2049
|
+
const items = parseCarrySection(yamlText);
|
|
1470
2050
|
if (items.length === 0) return { decision: "approve", entries: [] };
|
|
1471
|
-
const
|
|
2051
|
+
const cfg = await loadEffectiveConfig(args.projectRoot);
|
|
2052
|
+
const replacements = parseReplacementsSection(yamlText);
|
|
2053
|
+
const resolved = await resolveCarry(items, {
|
|
2054
|
+
projectRoot: args.projectRoot,
|
|
2055
|
+
maxBytes: cfg.effective.box.cpMaxBytes,
|
|
2056
|
+
replacements
|
|
2057
|
+
});
|
|
1472
2058
|
if (resolved.errors.length > 0) {
|
|
1473
2059
|
const msg = ["carry: refused to proceed:", ...resolved.errors.map((e) => ` - ${e}`)].join("\n");
|
|
1474
2060
|
throw new Error(msg);
|
|
@@ -1513,10 +2099,10 @@ async function runQueuedCarryGate(args) {
|
|
|
1513
2099
|
}
|
|
1514
2100
|
|
|
1515
2101
|
// src/session-teleport/claude.ts
|
|
1516
|
-
import { mkdir, mkdtemp, readdir, readFile as
|
|
2102
|
+
import { mkdir, mkdtemp, readdir as readdir2, readFile as readFile3, stat as stat4, writeFile } from "fs/promises";
|
|
1517
2103
|
import { existsSync } from "fs";
|
|
1518
2104
|
import { homedir as homedir4, tmpdir } from "os";
|
|
1519
|
-
import { join as
|
|
2105
|
+
import { join as join7 } from "path";
|
|
1520
2106
|
|
|
1521
2107
|
// src/session-teleport/cwd-encoding.ts
|
|
1522
2108
|
function encodeClaudeProjectsDir(absPath) {
|
|
@@ -1537,7 +2123,7 @@ var TeleportError = class extends Error {
|
|
|
1537
2123
|
var BOX_CLAUDE_PROJECTS_DIR = `/home/vscode/.claude/projects/${BOX_WORKSPACE_ENCODED}`;
|
|
1538
2124
|
async function resolveClaudeTeleport(opts) {
|
|
1539
2125
|
const hostHome = opts.hostHome ?? homedir4();
|
|
1540
|
-
const projectDir =
|
|
2126
|
+
const projectDir = join7(
|
|
1541
2127
|
hostHome,
|
|
1542
2128
|
".claude",
|
|
1543
2129
|
"projects",
|
|
@@ -1550,8 +2136,8 @@ async function resolveClaudeTeleport(opts) {
|
|
|
1550
2136
|
}
|
|
1551
2137
|
const sessionPath = await pickSessionFile(projectDir, opts.mode);
|
|
1552
2138
|
const sessionId = sessionPath.replace(/\.jsonl$/u, "").split("/").pop() ?? "";
|
|
1553
|
-
const stage = await mkdtemp(
|
|
1554
|
-
const stagedFile =
|
|
2139
|
+
const stage = await mkdtemp(join7(tmpdir(), "agentbox-teleport-claude-"));
|
|
2140
|
+
const stagedFile = join7(stage, `${sessionId}.jsonl`);
|
|
1555
2141
|
await rewriteSessionFile(sessionPath, stagedFile, opts.hostCwd);
|
|
1556
2142
|
opts.log?.(`teleport: claude session ${sessionId} staged for upload`);
|
|
1557
2143
|
return {
|
|
@@ -1565,7 +2151,7 @@ async function resolveClaudeTeleport(opts) {
|
|
|
1565
2151
|
}
|
|
1566
2152
|
async function pickSessionFile(projectDir, mode) {
|
|
1567
2153
|
if (mode.kind === "resume") {
|
|
1568
|
-
const target =
|
|
2154
|
+
const target = join7(projectDir, `${mode.id}.jsonl`);
|
|
1569
2155
|
if (!existsSync(target)) {
|
|
1570
2156
|
throw new TeleportError(
|
|
1571
2157
|
`Claude session "${mode.id}" not found in ${projectDir}. List available sessions with: ls "${projectDir}"`
|
|
@@ -1573,7 +2159,7 @@ async function pickSessionFile(projectDir, mode) {
|
|
|
1573
2159
|
}
|
|
1574
2160
|
return target;
|
|
1575
2161
|
}
|
|
1576
|
-
const entries = await
|
|
2162
|
+
const entries = await readdir2(projectDir);
|
|
1577
2163
|
const jsonl = entries.filter((e) => e.endsWith(".jsonl"));
|
|
1578
2164
|
if (jsonl.length === 0) {
|
|
1579
2165
|
throw new TeleportError(
|
|
@@ -1582,8 +2168,8 @@ async function pickSessionFile(projectDir, mode) {
|
|
|
1582
2168
|
}
|
|
1583
2169
|
const stats = await Promise.all(
|
|
1584
2170
|
jsonl.map(async (name) => {
|
|
1585
|
-
const full =
|
|
1586
|
-
const s = await
|
|
2171
|
+
const full = join7(projectDir, name);
|
|
2172
|
+
const s = await stat4(full);
|
|
1587
2173
|
return { full, mtimeMs: s.mtimeMs };
|
|
1588
2174
|
})
|
|
1589
2175
|
);
|
|
@@ -1591,7 +2177,7 @@ async function pickSessionFile(projectDir, mode) {
|
|
|
1591
2177
|
return stats[0].full;
|
|
1592
2178
|
}
|
|
1593
2179
|
async function rewriteSessionFile(src, dst, hostCwd) {
|
|
1594
|
-
const raw = await
|
|
2180
|
+
const raw = await readFile3(src, "utf8");
|
|
1595
2181
|
const lines = raw.split("\n");
|
|
1596
2182
|
const out = [];
|
|
1597
2183
|
for (const line of lines) {
|
|
@@ -1616,18 +2202,18 @@ async function rewriteSessionFile(src, dst, hostCwd) {
|
|
|
1616
2202
|
}
|
|
1617
2203
|
out.push(line);
|
|
1618
2204
|
}
|
|
1619
|
-
await mkdir(
|
|
2205
|
+
await mkdir(join7(dst, ".."), { recursive: true });
|
|
1620
2206
|
await writeFile(dst, out.join("\n"), "utf8");
|
|
1621
2207
|
}
|
|
1622
2208
|
|
|
1623
2209
|
// src/session-teleport/codex.ts
|
|
1624
|
-
import { mkdtemp as mkdtemp2, readdir as
|
|
2210
|
+
import { mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile4, stat as stat5, writeFile as writeFile2 } from "fs/promises";
|
|
1625
2211
|
import { existsSync as existsSync2 } from "fs";
|
|
1626
2212
|
import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
|
|
1627
|
-
import { join as
|
|
2213
|
+
import { join as join8 } from "path";
|
|
1628
2214
|
async function resolveCodexTeleport(opts) {
|
|
1629
2215
|
const hostHome = opts.hostHome ?? homedir5();
|
|
1630
|
-
const sessionsRoot =
|
|
2216
|
+
const sessionsRoot = join8(hostHome, ".codex", "sessions");
|
|
1631
2217
|
if (!existsSync2(sessionsRoot)) {
|
|
1632
2218
|
throw new TeleportError(
|
|
1633
2219
|
`no Codex session history found on the host (expected at ${sessionsRoot}). Run \`codex\` at least once before using -c / --resume.`
|
|
@@ -1669,8 +2255,8 @@ async function resolveCodexTeleport(opts) {
|
|
|
1669
2255
|
matching.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1670
2256
|
picked = matching[0];
|
|
1671
2257
|
}
|
|
1672
|
-
const stage = await mkdtemp2(
|
|
1673
|
-
const stagedFile =
|
|
2258
|
+
const stage = await mkdtemp2(join8(tmpdir2(), "agentbox-teleport-codex-"));
|
|
2259
|
+
const stagedFile = join8(stage, posixBasename(picked.relPath));
|
|
1674
2260
|
await rewriteCodexSession(picked.hostPath, stagedFile, opts.hostCwd);
|
|
1675
2261
|
opts.log?.(`teleport: codex session ${picked.uuid} staged for upload`);
|
|
1676
2262
|
const boxParentDir = `/home/vscode/.codex/sessions/${posixDirname(picked.relPath)}`;
|
|
@@ -1696,24 +2282,24 @@ async function listCodexSessions(sessionsRoot) {
|
|
|
1696
2282
|
const years = await safeReaddir(sessionsRoot);
|
|
1697
2283
|
for (const y of years) {
|
|
1698
2284
|
if (!/^\d{4}$/u.test(y)) continue;
|
|
1699
|
-
const yDir =
|
|
2285
|
+
const yDir = join8(sessionsRoot, y);
|
|
1700
2286
|
const months = await safeReaddir(yDir);
|
|
1701
2287
|
for (const m of months) {
|
|
1702
2288
|
if (!/^\d{2}$/u.test(m)) continue;
|
|
1703
|
-
const mDir =
|
|
2289
|
+
const mDir = join8(yDir, m);
|
|
1704
2290
|
const days = await safeReaddir(mDir);
|
|
1705
2291
|
for (const d of days) {
|
|
1706
2292
|
if (!/^\d{2}$/u.test(d)) continue;
|
|
1707
|
-
const dDir =
|
|
2293
|
+
const dDir = join8(mDir, d);
|
|
1708
2294
|
const files = await safeReaddir(dDir);
|
|
1709
2295
|
for (const name of files) {
|
|
1710
2296
|
if (!name.startsWith("rollout-") || !name.endsWith(".jsonl")) continue;
|
|
1711
2297
|
const uuid = extractCodexUuid(name);
|
|
1712
2298
|
if (uuid === null) continue;
|
|
1713
|
-
const hostPath =
|
|
2299
|
+
const hostPath = join8(dDir, name);
|
|
1714
2300
|
let mtimeMs = 0;
|
|
1715
2301
|
try {
|
|
1716
|
-
mtimeMs = (await
|
|
2302
|
+
mtimeMs = (await stat5(hostPath)).mtimeMs;
|
|
1717
2303
|
} catch {
|
|
1718
2304
|
continue;
|
|
1719
2305
|
}
|
|
@@ -1733,7 +2319,7 @@ async function listCodexSessions(sessionsRoot) {
|
|
|
1733
2319
|
}
|
|
1734
2320
|
async function safeReaddir(dir) {
|
|
1735
2321
|
try {
|
|
1736
|
-
return await
|
|
2322
|
+
return await readdir3(dir);
|
|
1737
2323
|
} catch {
|
|
1738
2324
|
return [];
|
|
1739
2325
|
}
|
|
@@ -1745,16 +2331,16 @@ function extractCodexUuid(filename) {
|
|
|
1745
2331
|
return m ? m[1] : null;
|
|
1746
2332
|
}
|
|
1747
2333
|
async function peekCodexCwd(file) {
|
|
1748
|
-
let
|
|
2334
|
+
let firstLine3;
|
|
1749
2335
|
try {
|
|
1750
|
-
const buf = await
|
|
2336
|
+
const buf = await readFile4(file, "utf8");
|
|
1751
2337
|
const nl = buf.indexOf("\n");
|
|
1752
|
-
|
|
2338
|
+
firstLine3 = nl === -1 ? buf : buf.slice(0, nl);
|
|
1753
2339
|
} catch {
|
|
1754
2340
|
return null;
|
|
1755
2341
|
}
|
|
1756
2342
|
try {
|
|
1757
|
-
const parsed = JSON.parse(
|
|
2343
|
+
const parsed = JSON.parse(firstLine3);
|
|
1758
2344
|
if (parsed.type === "session_meta" && typeof parsed.payload?.cwd === "string") {
|
|
1759
2345
|
return parsed.payload.cwd;
|
|
1760
2346
|
}
|
|
@@ -1763,7 +2349,7 @@ async function peekCodexCwd(file) {
|
|
|
1763
2349
|
return null;
|
|
1764
2350
|
}
|
|
1765
2351
|
async function rewriteCodexSession(src, dst, hostCwd) {
|
|
1766
|
-
const raw = await
|
|
2352
|
+
const raw = await readFile4(src, "utf8");
|
|
1767
2353
|
const lines = raw.split("\n");
|
|
1768
2354
|
const out = [];
|
|
1769
2355
|
for (const line of lines) {
|
|
@@ -1833,14 +2419,14 @@ async function uploadTeleport(input) {
|
|
|
1833
2419
|
}
|
|
1834
2420
|
|
|
1835
2421
|
// src/session-teleport/plan.ts
|
|
1836
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
2422
|
+
import { mkdtemp as mkdtemp3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
1837
2423
|
import { existsSync as existsSync3 } from "fs";
|
|
1838
2424
|
import { homedir as homedir6, tmpdir as tmpdir3 } from "os";
|
|
1839
|
-
import { basename, isAbsolute as isAbsolute2, join as
|
|
2425
|
+
import { basename, isAbsolute as isAbsolute2, join as join9, resolve as resolve2 } from "path";
|
|
1840
2426
|
var BOX_CLAUDE_PLANS_DIR = "/home/vscode/.claude/plans";
|
|
1841
2427
|
function expandHome(p, hostHome) {
|
|
1842
2428
|
if (p === "~") return hostHome;
|
|
1843
|
-
if (p.startsWith("~/")) return
|
|
2429
|
+
if (p.startsWith("~/")) return join9(hostHome, p.slice(2));
|
|
1844
2430
|
return isAbsolute2(p) ? p : resolve2(p);
|
|
1845
2431
|
}
|
|
1846
2432
|
async function resolvePlanTeleport(opts) {
|
|
@@ -1852,9 +2438,9 @@ async function resolvePlanTeleport(opts) {
|
|
|
1852
2438
|
);
|
|
1853
2439
|
}
|
|
1854
2440
|
const name = basename(planFile);
|
|
1855
|
-
const stage = await mkdtemp3(
|
|
1856
|
-
const stagedFile =
|
|
1857
|
-
const raw = await
|
|
2441
|
+
const stage = await mkdtemp3(join9(tmpdir3(), "agentbox-teleport-plan-"));
|
|
2442
|
+
const stagedFile = join9(stage, name);
|
|
2443
|
+
const raw = await readFile5(planFile, "utf8");
|
|
1858
2444
|
const absCwd = opts.hostCwd ? resolve2(expandHome(opts.hostCwd, hostHome)) : "";
|
|
1859
2445
|
const rewritten = absCwd ? raw.split(absCwd).join(BOX_WORKSPACE) : raw;
|
|
1860
2446
|
await writeFile3(stagedFile, rewritten, "utf8");
|
|
@@ -1873,14 +2459,14 @@ async function resolvePlanTeleport(opts) {
|
|
|
1873
2459
|
import { log as log6 } from "@clack/prompts";
|
|
1874
2460
|
import { existsSync as existsSync4, mkdirSync, writeFileSync } from "fs";
|
|
1875
2461
|
import { homedir as homedir7 } from "os";
|
|
1876
|
-
import { join as
|
|
2462
|
+
import { join as join10 } from "path";
|
|
1877
2463
|
function maybeShowInstallHint() {
|
|
1878
2464
|
try {
|
|
1879
|
-
const skill =
|
|
2465
|
+
const skill = join10(homedir7(), ".claude", "skills", "agentbox", "SKILL.md");
|
|
1880
2466
|
if (existsSync4(skill)) return;
|
|
1881
|
-
const marker =
|
|
2467
|
+
const marker = join10(homedir7(), ".agentbox", "install-hint-shown");
|
|
1882
2468
|
if (existsSync4(marker)) return;
|
|
1883
|
-
mkdirSync(
|
|
2469
|
+
mkdirSync(join10(homedir7(), ".agentbox"), { recursive: true });
|
|
1884
2470
|
writeFileSync(marker, "");
|
|
1885
2471
|
log6.info("tip: run 'agentbox install' to enable the /agentbox fork command in host Claude");
|
|
1886
2472
|
} catch {
|
|
@@ -1890,18 +2476,18 @@ function maybeShowInstallHint() {
|
|
|
1890
2476
|
// src/lib/log-file.ts
|
|
1891
2477
|
import { closeSync, mkdirSync as mkdirSync2, openSync, renameSync, symlinkSync, unlinkSync, writeFileSync as writeFileSync2, writeSync } from "fs";
|
|
1892
2478
|
import { homedir as homedir8 } from "os";
|
|
1893
|
-
import { join as
|
|
2479
|
+
import { join as join11 } from "path";
|
|
1894
2480
|
function stateDir() {
|
|
1895
|
-
return process.env.AGENTBOX_HOME ??
|
|
2481
|
+
return process.env.AGENTBOX_HOME ?? join11(homedir8(), ".agentbox");
|
|
1896
2482
|
}
|
|
1897
2483
|
function logsDir() {
|
|
1898
|
-
return
|
|
2484
|
+
return join11(stateDir(), "logs");
|
|
1899
2485
|
}
|
|
1900
2486
|
function openCommandLog(command) {
|
|
1901
2487
|
const dir = logsDir();
|
|
1902
2488
|
mkdirSync2(dir, { recursive: true });
|
|
1903
|
-
const path =
|
|
1904
|
-
const prev =
|
|
2489
|
+
const path = join11(dir, `${command}.log`);
|
|
2490
|
+
const prev = join11(dir, `${command}.log.prev`);
|
|
1905
2491
|
try {
|
|
1906
2492
|
renameSync(path, prev);
|
|
1907
2493
|
} catch {
|
|
@@ -1961,7 +2547,7 @@ function openCommandLog(command) {
|
|
|
1961
2547
|
};
|
|
1962
2548
|
}
|
|
1963
2549
|
function updateLatestSymlink(dir, target) {
|
|
1964
|
-
const link =
|
|
2550
|
+
const link = join11(dir, "latest.log");
|
|
1965
2551
|
try {
|
|
1966
2552
|
unlinkSync(link);
|
|
1967
2553
|
} catch {
|
|
@@ -2075,13 +2661,13 @@ import { basename as basename2 } from "path";
|
|
|
2075
2661
|
async function cloudBackendForProvider(provider) {
|
|
2076
2662
|
switch (provider) {
|
|
2077
2663
|
case "daytona":
|
|
2078
|
-
return (await import("./dist-
|
|
2664
|
+
return (await import("./dist-SL2QSMBE.js")).daytonaBackend;
|
|
2079
2665
|
case "hetzner":
|
|
2080
|
-
return (await import("./dist-
|
|
2666
|
+
return (await import("./dist-VHI5QOSQ.js")).hetznerBackend;
|
|
2081
2667
|
case "vercel":
|
|
2082
|
-
return (await import("./dist-
|
|
2668
|
+
return (await import("./dist-XC47DSCR.js")).vercelBackend;
|
|
2083
2669
|
case "e2b":
|
|
2084
|
-
return (await import("./dist-
|
|
2670
|
+
return (await import("./dist-4IQFJJQI.js")).e2bBackend;
|
|
2085
2671
|
default:
|
|
2086
2672
|
return null;
|
|
2087
2673
|
}
|
|
@@ -2089,13 +2675,13 @@ async function cloudBackendForProvider(provider) {
|
|
|
2089
2675
|
async function currentCloudBaseFingerprintLive(provider) {
|
|
2090
2676
|
switch (provider) {
|
|
2091
2677
|
case "daytona":
|
|
2092
|
-
return (await import("./dist-
|
|
2678
|
+
return (await import("./dist-SL2QSMBE.js")).currentDaytonaBaseFingerprintLive();
|
|
2093
2679
|
case "hetzner":
|
|
2094
|
-
return (await import("./dist-
|
|
2680
|
+
return (await import("./dist-VHI5QOSQ.js")).currentHetznerBaseFingerprintLive();
|
|
2095
2681
|
case "vercel":
|
|
2096
|
-
return (await import("./dist-
|
|
2682
|
+
return (await import("./dist-XC47DSCR.js")).currentVercelBaseFingerprintLive();
|
|
2097
2683
|
case "e2b":
|
|
2098
|
-
return (await import("./dist-
|
|
2684
|
+
return (await import("./dist-4IQFJJQI.js")).currentE2bBaseFingerprintLive();
|
|
2099
2685
|
default:
|
|
2100
2686
|
return void 0;
|
|
2101
2687
|
}
|
|
@@ -2173,18 +2759,30 @@ async function evaluateBaseFreshness(provider) {
|
|
|
2173
2759
|
|
|
2174
2760
|
// src/wizard.ts
|
|
2175
2761
|
var IN_BOX_SETUP_GUIDE_PATH = "/usr/local/share/agentbox/setup-guide.md";
|
|
2176
|
-
function buildSetupInitialPrompt(workspace) {
|
|
2762
|
+
function buildSetupInitialPrompt(workspace, hasAgentboxYaml = false) {
|
|
2177
2763
|
const name = basename2(workspace);
|
|
2764
|
+
if (hasAgentboxYaml) {
|
|
2765
|
+
return `The user reopened the agentbox sandbox for "${name}". The workspace already has a /workspace/agentbox.yaml, but the previous snapshot was stale so the box was rebuilt from a fresh base image \u2014 its dependencies and system setup are NOT installed yet. Please run the /agentbox-setup skill (or read ${IN_BOX_SETUP_GUIDE_PATH} if the skill is not loaded), then reuse the existing /workspace/agentbox.yaml: verify the declared tasks and services still fit the project, (re)install dependencies and any system packages the setup needs on this fresh base, and update agentbox.yaml only if something is missing or wrong \u2014 do not regenerate it from scratch. Then run \`agentbox-ctl reload\` from inside the box so the already-running supervisor re-applies the config and immediately runs the declared tasks and autostarts the services (no box restart needed). When the box is warm, capture it with \`agentbox checkpoint --set-default\` so future boxes skip this rebuild. Finally, summarise what you verified and changed, and remind the user how to land any agentbox.yaml change on the host (commit through the bind-mounted .git, or "agentbox download env" on the host).`;
|
|
2766
|
+
}
|
|
2178
2767
|
return `The user just opened a new agentbox sandbox for "${name}" but the workspace has no agentbox.yaml yet. Please run the /agentbox-setup skill (or read ${IN_BOX_SETUP_GUIDE_PATH} if the skill is not loaded), then explore /workspace and propose an agentbox.yaml. Save the file to /workspace/agentbox.yaml. Then run \`agentbox-ctl reload\` from inside the box so the already-running supervisor applies the new config and immediately runs the declared tasks and autostarts the services (no box restart needed). When done, summarise what services and tasks you declared, and remind the user how to land the file on the host (commit through the bind-mounted .git, or "agentbox download env" on the host).`;
|
|
2179
2768
|
}
|
|
2180
2769
|
var WIZARD_AUTOLAUNCH_ENV = "AGENTBOX_WIZARD_AUTOLAUNCH";
|
|
2181
2770
|
var WIZARD_ENV_FILES_ENV = "AGENTBOX_WIZARD_ENV_FILES";
|
|
2771
|
+
var WIZARD_RECREATE_ENV = "AGENTBOX_WIZARD_RECREATE";
|
|
2182
2772
|
var WIZARD_ENV_SCAN_PATTERNS = DEFAULT_ENV_PATTERNS.filter((p) => p !== "agentbox.yaml");
|
|
2183
2773
|
async function maybeRunSetupWizard(args) {
|
|
2184
2774
|
if (process.env[WIZARD_AUTOLAUNCH_ENV] === "1") {
|
|
2185
2775
|
if (args.command !== "claude") return { action: "proceed" };
|
|
2186
2776
|
const envFiles = parseEnvFilesFromEnv(process.env[WIZARD_ENV_FILES_ENV]);
|
|
2187
2777
|
const proj2 = await findProjectRoot(args.workspace);
|
|
2778
|
+
if (process.env[WIZARD_RECREATE_ENV] === "1") {
|
|
2779
|
+
return {
|
|
2780
|
+
action: "launch-with-prompt",
|
|
2781
|
+
initialPrompt: buildSetupInitialPrompt(proj2.root, proj2.hasAgentboxYaml),
|
|
2782
|
+
envFilesToImport: envFiles,
|
|
2783
|
+
discardCheckpoint: true
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2188
2786
|
return nonInteractiveOutcome(args, proj2, await checkpointStatus(args, proj2.root), envFiles);
|
|
2189
2787
|
}
|
|
2190
2788
|
const proj = await findProjectRoot(args.workspace);
|
|
@@ -2218,13 +2816,6 @@ async function maybeRunSetupWizard(args) {
|
|
|
2218
2816
|
};
|
|
2219
2817
|
}
|
|
2220
2818
|
}
|
|
2221
|
-
if (proj.hasAgentboxYaml) {
|
|
2222
|
-
return { action: "proceed", discardCheckpoint: discardOnMissing(status, fromDefault) };
|
|
2223
|
-
}
|
|
2224
|
-
if (!rebuildBase && (status.state === "fresh" || status.state === "stale" && !fromDefault)) {
|
|
2225
|
-
log8.info(`starting from checkpoint "${args.checkpointRef}"; skipping agentbox.yaml setup`);
|
|
2226
|
-
return { action: "proceed" };
|
|
2227
|
-
}
|
|
2228
2819
|
let discardCheckpoint = rebuildBase;
|
|
2229
2820
|
let recreateChosen = rebuildBase;
|
|
2230
2821
|
if (!rebuildBase && status.state === "stale" && fromDefault) {
|
|
@@ -2237,7 +2828,15 @@ async function maybeRunSetupWizard(args) {
|
|
|
2237
2828
|
}
|
|
2238
2829
|
discardCheckpoint = true;
|
|
2239
2830
|
recreateChosen = true;
|
|
2240
|
-
}
|
|
2831
|
+
}
|
|
2832
|
+
if (proj.hasAgentboxYaml && !recreateChosen) {
|
|
2833
|
+
return { action: "proceed", discardCheckpoint: discardOnMissing(status, fromDefault) };
|
|
2834
|
+
}
|
|
2835
|
+
if (!rebuildBase && !recreateChosen && (status.state === "fresh" || status.state === "stale" && !fromDefault)) {
|
|
2836
|
+
log8.info(`starting from checkpoint "${args.checkpointRef}"; skipping agentbox.yaml setup`);
|
|
2837
|
+
return { action: "proceed" };
|
|
2838
|
+
}
|
|
2839
|
+
if (!rebuildBase && !recreateChosen && status.state === "missing") {
|
|
2241
2840
|
discardCheckpoint = discardOnMissing(status, fromDefault) ?? false;
|
|
2242
2841
|
}
|
|
2243
2842
|
let envFilesToImport;
|
|
@@ -2274,15 +2873,17 @@ async function maybeRunSetupWizard(args) {
|
|
|
2274
2873
|
action: "switch-to-claude",
|
|
2275
2874
|
envFilesToImport,
|
|
2276
2875
|
discardCheckpoint: discardCheckpoint || void 0,
|
|
2277
|
-
rebuildBase: rebuildBase || void 0
|
|
2876
|
+
rebuildBase: rebuildBase || void 0,
|
|
2877
|
+
recreate: recreateChosen || void 0
|
|
2278
2878
|
};
|
|
2279
2879
|
}
|
|
2280
2880
|
return {
|
|
2281
2881
|
action: "launch-with-prompt",
|
|
2282
|
-
initialPrompt: buildSetupInitialPrompt(proj.root),
|
|
2882
|
+
initialPrompt: buildSetupInitialPrompt(proj.root, proj.hasAgentboxYaml),
|
|
2283
2883
|
envFilesToImport,
|
|
2284
2884
|
discardCheckpoint: discardCheckpoint || void 0,
|
|
2285
|
-
rebuildBase: rebuildBase || void 0
|
|
2885
|
+
rebuildBase: rebuildBase || void 0,
|
|
2886
|
+
recreate: recreateChosen || void 0
|
|
2286
2887
|
};
|
|
2287
2888
|
}
|
|
2288
2889
|
function rebuildMinutesFor(provider) {
|
|
@@ -2292,9 +2893,9 @@ function rebuildMinutesFor(provider) {
|
|
|
2292
2893
|
case "daytona":
|
|
2293
2894
|
return "7";
|
|
2294
2895
|
case "vercel":
|
|
2295
|
-
return "
|
|
2896
|
+
return "5-10";
|
|
2296
2897
|
case "hetzner":
|
|
2297
|
-
return "
|
|
2898
|
+
return "7-10";
|
|
2298
2899
|
default:
|
|
2299
2900
|
return "1";
|
|
2300
2901
|
}
|
|
@@ -2323,7 +2924,7 @@ function nonInteractiveOutcome(args, proj, status, envFilesToImport) {
|
|
|
2323
2924
|
if (args.command === "claude") {
|
|
2324
2925
|
return {
|
|
2325
2926
|
action: "launch-with-prompt",
|
|
2326
|
-
initialPrompt: buildSetupInitialPrompt(proj.root),
|
|
2927
|
+
initialPrompt: buildSetupInitialPrompt(proj.root, proj.hasAgentboxYaml),
|
|
2327
2928
|
envFilesToImport,
|
|
2328
2929
|
discardCheckpoint
|
|
2329
2930
|
};
|
|
@@ -2419,7 +3020,7 @@ async function renderDocker(status) {
|
|
|
2419
3020
|
}
|
|
2420
3021
|
async function daytonaStatus() {
|
|
2421
3022
|
try {
|
|
2422
|
-
const mod = await import("./dist-
|
|
3023
|
+
const mod = await import("./dist-SL2QSMBE.js");
|
|
2423
3024
|
return await mod.getDaytonaStatus();
|
|
2424
3025
|
} catch (err) {
|
|
2425
3026
|
return {
|
|
@@ -2430,7 +3031,7 @@ async function daytonaStatus() {
|
|
|
2430
3031
|
}
|
|
2431
3032
|
async function e2bStatus() {
|
|
2432
3033
|
try {
|
|
2433
|
-
const mod = await import("./dist-
|
|
3034
|
+
const mod = await import("./dist-4IQFJJQI.js");
|
|
2434
3035
|
const cred = mod.readE2bCredStatus();
|
|
2435
3036
|
if (cred.auth === "none") {
|
|
2436
3037
|
return { configured: false, reason: "not configured \u2014 run `agentbox e2b login`" };
|
|
@@ -2944,7 +3545,8 @@ var claudeCommand = new Command3("claude").description("Create a sandboxed box a
|
|
|
2944
3545
|
agentArgs: claudeArgs,
|
|
2945
3546
|
createOpts: { ...pickCreateOpts(opts), carry: carryForQueue },
|
|
2946
3547
|
maxRunningOverride,
|
|
2947
|
-
maxWorkingOverride
|
|
3548
|
+
maxWorkingOverride,
|
|
3549
|
+
openTerminal: captureOpenTerminalContext(cfg.effective.queue.openIn)
|
|
2948
3550
|
});
|
|
2949
3551
|
outro(
|
|
2950
3552
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
@@ -3572,13 +4174,13 @@ var CLOUD_BACKENDS = ["daytona", "hetzner", "vercel", "e2b"];
|
|
|
3572
4174
|
async function cloudProviderFor(backend) {
|
|
3573
4175
|
switch (backend) {
|
|
3574
4176
|
case "daytona":
|
|
3575
|
-
return (await import("./dist-
|
|
4177
|
+
return (await import("./dist-SL2QSMBE.js")).daytonaProvider;
|
|
3576
4178
|
case "hetzner":
|
|
3577
|
-
return (await import("./dist-
|
|
4179
|
+
return (await import("./dist-VHI5QOSQ.js")).hetznerProvider;
|
|
3578
4180
|
case "vercel":
|
|
3579
|
-
return (await import("./dist-
|
|
4181
|
+
return (await import("./dist-XC47DSCR.js")).vercelProvider;
|
|
3580
4182
|
case "e2b":
|
|
3581
|
-
return (await import("./dist-
|
|
4183
|
+
return (await import("./dist-4IQFJJQI.js")).e2bProvider;
|
|
3582
4184
|
}
|
|
3583
4185
|
}
|
|
3584
4186
|
var CHECKPOINT_NOTICE = "Checkpoint in progress \u2014 the box will be unresponsive for a moment";
|
|
@@ -3948,9 +4550,9 @@ import { Command as Command5, InvalidArgumentError } from "commander";
|
|
|
3948
4550
|
// src/ssh-config.ts
|
|
3949
4551
|
import { promises as fs } from "fs";
|
|
3950
4552
|
import { homedir as homedir9 } from "os";
|
|
3951
|
-
import { join as
|
|
4553
|
+
import { join as join12 } from "path";
|
|
3952
4554
|
function sshConfigPath() {
|
|
3953
|
-
return
|
|
4555
|
+
return join12(homedir9(), ".ssh", "config");
|
|
3954
4556
|
}
|
|
3955
4557
|
function beginMarker(alias) {
|
|
3956
4558
|
return `# BEGIN agentbox cloud box ${alias}`;
|
|
@@ -4001,7 +4603,7 @@ function buildBlock(opts) {
|
|
|
4001
4603
|
}
|
|
4002
4604
|
async function writeAgentboxSshAlias(opts) {
|
|
4003
4605
|
const path = sshConfigPath();
|
|
4004
|
-
await fs.mkdir(
|
|
4606
|
+
await fs.mkdir(join12(homedir9(), ".ssh"), { recursive: true, mode: 448 });
|
|
4005
4607
|
const existing = await readConfig();
|
|
4006
4608
|
const stripped = stripBlock(existing, opts.alias);
|
|
4007
4609
|
const separator = stripped.length === 0 || stripped.endsWith("\n") ? "" : "\n";
|
|
@@ -4231,10 +4833,10 @@ async function launchOne(flavor, folderUri) {
|
|
|
4231
4833
|
return { code: fallback, flavor, via: "open" };
|
|
4232
4834
|
}
|
|
4233
4835
|
function spawnCommand(cmd, args) {
|
|
4234
|
-
return new Promise((
|
|
4836
|
+
return new Promise((resolve5) => {
|
|
4235
4837
|
const child = spawn(cmd, args, { stdio: "ignore" });
|
|
4236
|
-
child.once("error", () =>
|
|
4237
|
-
child.once("exit", (code) =>
|
|
4838
|
+
child.once("error", () => resolve5(127));
|
|
4839
|
+
child.once("exit", (code) => resolve5(code ?? -1));
|
|
4238
4840
|
});
|
|
4239
4841
|
}
|
|
4240
4842
|
async function fetchServiceNamesDocker(container) {
|
|
@@ -4253,7 +4855,7 @@ async function fetchServiceNamesDocker(container) {
|
|
|
4253
4855
|
// src/commands/codex.ts
|
|
4254
4856
|
import { access } from "fs/promises";
|
|
4255
4857
|
import { homedir as homedir10 } from "os";
|
|
4256
|
-
import { join as
|
|
4858
|
+
import { join as join13 } from "path";
|
|
4257
4859
|
import { confirm as confirm5, intro as intro3, isCancel as isCancel6, log as log13, outro as outro2, spinner as spinner5 } from "@clack/prompts";
|
|
4258
4860
|
import { Command as Command6 } from "commander";
|
|
4259
4861
|
function reattachRef2(r) {
|
|
@@ -4350,7 +4952,7 @@ async function maybeRunCodexLogin(args) {
|
|
|
4350
4952
|
}
|
|
4351
4953
|
async function cloudCodexCredAvailable(env = process.env) {
|
|
4352
4954
|
if ((env["OPENAI_API_KEY"] ?? "").length > 0) return true;
|
|
4353
|
-
for (const p of [CODEX_CREDENTIALS_BACKUP_FILE,
|
|
4955
|
+
for (const p of [CODEX_CREDENTIALS_BACKUP_FILE, join13(homedir10(), ".codex", "auth.json")]) {
|
|
4354
4956
|
try {
|
|
4355
4957
|
await access(p);
|
|
4356
4958
|
return true;
|
|
@@ -4518,7 +5120,8 @@ var codexCommand = new Command6("codex").description("Create a sandboxed box and
|
|
|
4518
5120
|
agentArgs: codexArgs,
|
|
4519
5121
|
createOpts: { ...pickCodexCreateOpts(opts), carry: carryForQueue },
|
|
4520
5122
|
maxRunningOverride,
|
|
4521
|
-
maxWorkingOverride
|
|
5123
|
+
maxWorkingOverride,
|
|
5124
|
+
openTerminal: captureOpenTerminalContext(cfg.effective.queue.openIn)
|
|
4522
5125
|
});
|
|
4523
5126
|
outro2(
|
|
4524
5127
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
@@ -5008,7 +5611,7 @@ codexCommand.addCommand(codexLoginCommand);
|
|
|
5008
5611
|
// src/commands/opencode.ts
|
|
5009
5612
|
import { access as access2 } from "fs/promises";
|
|
5010
5613
|
import { homedir as homedir11 } from "os";
|
|
5011
|
-
import { join as
|
|
5614
|
+
import { join as join14 } from "path";
|
|
5012
5615
|
import { confirm as confirm6, intro as intro4, isCancel as isCancel7, log as log14, outro as outro3, spinner as spinner6 } from "@clack/prompts";
|
|
5013
5616
|
import { Command as Command7 } from "commander";
|
|
5014
5617
|
function reattachRef3(r) {
|
|
@@ -5107,7 +5710,7 @@ async function cloudOpencodeCredAvailable(env = process.env) {
|
|
|
5107
5710
|
for (const k of OPENCODE_FORWARDED_ENV_KEYS) {
|
|
5108
5711
|
if ((env[k] ?? "").length > 0) return true;
|
|
5109
5712
|
}
|
|
5110
|
-
for (const p of [OPENCODE_CREDENTIALS_BACKUP_FILE,
|
|
5713
|
+
for (const p of [OPENCODE_CREDENTIALS_BACKUP_FILE, join14(homedir11(), ".local", "share", "opencode", "auth.json")]) {
|
|
5111
5714
|
try {
|
|
5112
5715
|
await access2(p);
|
|
5113
5716
|
return true;
|
|
@@ -5257,7 +5860,8 @@ var opencodeCommand = new Command7("opencode").description("Create a sandboxed b
|
|
|
5257
5860
|
agentArgs: opencodeArgs,
|
|
5258
5861
|
createOpts: { ...pickOpencodeCreateOpts(opts), carry: carryForQueue },
|
|
5259
5862
|
maxRunningOverride,
|
|
5260
|
-
maxWorkingOverride
|
|
5863
|
+
maxWorkingOverride,
|
|
5864
|
+
openTerminal: captureOpenTerminalContext(cfg.effective.queue.openIn)
|
|
5261
5865
|
});
|
|
5262
5866
|
outro3(
|
|
5263
5867
|
`job ${result.job.id} queued (${String(result.runningCount)}/${String(result.maxConcurrent)} running); log: ${result.job.logPath}`
|
|
@@ -5653,18 +6257,19 @@ function fail(message) {
|
|
|
5653
6257
|
`);
|
|
5654
6258
|
process.exit(1);
|
|
5655
6259
|
}
|
|
6260
|
+
function walkKey(obj, key) {
|
|
6261
|
+
let cur = obj;
|
|
6262
|
+
for (const seg of key.split(".")) {
|
|
6263
|
+
if (cur === void 0 || cur === null || typeof cur !== "object") return void 0;
|
|
6264
|
+
cur = cur[seg];
|
|
6265
|
+
}
|
|
6266
|
+
return cur;
|
|
6267
|
+
}
|
|
5656
6268
|
function leafValue(loaded, key) {
|
|
5657
|
-
|
|
5658
|
-
const branch = key.slice(0, idx);
|
|
5659
|
-
const leaf = key.slice(idx + 1);
|
|
5660
|
-
return loaded.effective[branch]?.[leaf];
|
|
6269
|
+
return walkKey(loaded.effective, key);
|
|
5661
6270
|
}
|
|
5662
6271
|
function rawLeafFromValues(values, key) {
|
|
5663
|
-
|
|
5664
|
-
const idx = key.indexOf(".");
|
|
5665
|
-
const b = values[key.slice(0, idx)];
|
|
5666
|
-
if (!b || typeof b !== "object") return void 0;
|
|
5667
|
-
return b[key.slice(idx + 1)];
|
|
6272
|
+
return walkKey(values, key);
|
|
5668
6273
|
}
|
|
5669
6274
|
function describeSource(source, loaded) {
|
|
5670
6275
|
switch (source) {
|
|
@@ -5756,11 +6361,19 @@ var setCommand = new Command8("set").description("Set a config key in the global
|
|
|
5756
6361
|
} else {
|
|
5757
6362
|
process.stdout.write(`${key} = ${fmtValue(r.coerced)} (wrote ${r.path})
|
|
5758
6363
|
`);
|
|
6364
|
+
warnOnSet(key, r.coerced);
|
|
5759
6365
|
}
|
|
5760
6366
|
} catch (err) {
|
|
5761
6367
|
handleError(err);
|
|
5762
6368
|
}
|
|
5763
6369
|
});
|
|
6370
|
+
function warnOnSet(key, value) {
|
|
6371
|
+
if (key === "queue.openIn" && value !== "none") {
|
|
6372
|
+
process.stderr.write(
|
|
6373
|
+
"note: queue.openIn only opens a terminal when you submit the `-i` job from inside tmux, cmux, or iTerm2.\n cmux: the box is opened by the relay's queue worker, a cmux-external process, so cmux's default\n `socketControlMode: cmuxOnly` blocks it. Set `socketControlMode` to `automation` (or `password`)\n in ~/.config/cmux/cmux.json and run `cmux reload-config` for cmux opens to work.\n"
|
|
6374
|
+
);
|
|
6375
|
+
}
|
|
6376
|
+
}
|
|
5764
6377
|
var unsetCommand = new Command8("unset").description("Remove a config key from the global or per-project file (default: --project)").argument("<key>", "dot-path key (e.g. box.hostSnapshot)").option("--global", "edit ~/.agentbox/config.yaml").option("--project", "edit ~/.agentbox/projects/<hash>/config.yaml (default)").action(async (key, opts) => {
|
|
5765
6378
|
const scope = resolveWriteScope(opts);
|
|
5766
6379
|
try {
|
|
@@ -5898,8 +6511,51 @@ function handleError(err) {
|
|
|
5898
6511
|
var configCommand = new Command8("config").description("Read / write layered config (global, per-project, workspace `defaults:` block)").addCommand(getCommand).addCommand(setCommand).addCommand(unsetCommand).addCommand(listCommand).addCommand(pathCommand).addCommand(editCommand).addCommand(listProjectsCommand);
|
|
5899
6512
|
|
|
5900
6513
|
// src/commands/cp.ts
|
|
6514
|
+
import { resolve as resolve3 } from "path";
|
|
5901
6515
|
import { log as log15 } from "@clack/prompts";
|
|
5902
6516
|
import { Command as Command9 } from "commander";
|
|
6517
|
+
function collectExclude(val, acc) {
|
|
6518
|
+
acc.push(val);
|
|
6519
|
+
return acc;
|
|
6520
|
+
}
|
|
6521
|
+
async function guardUploadSize(hostSrc, boxDst, opts) {
|
|
6522
|
+
const tokens = effectiveExcludes(opts.exclude, opts.defaultExcludes);
|
|
6523
|
+
const tarPatterns = toTarExcludes(tokens);
|
|
6524
|
+
const cfg = await loadEffectiveConfig(process.cwd());
|
|
6525
|
+
const maxBytes = cfg.effective.box.cpMaxBytes;
|
|
6526
|
+
const absSrc = resolve3(hostSrc);
|
|
6527
|
+
const measured = await measureCopy(absSrc, tokens);
|
|
6528
|
+
if (opts.yes || measured.totalBytes <= maxBytes) {
|
|
6529
|
+
return tarPatterns;
|
|
6530
|
+
}
|
|
6531
|
+
const dropped = measured.isDir && opts.defaultExcludes ? ` after default excludes (${effectiveExcludes([], true).join(", ")})` : "";
|
|
6532
|
+
const lines = [
|
|
6533
|
+
`${hostSrc} is ${fmtBytes(measured.totalBytes)}${dropped}, over the ${fmtBytes(maxBytes)} per-copy limit.`
|
|
6534
|
+
];
|
|
6535
|
+
if (measured.isDir) {
|
|
6536
|
+
lines.push(
|
|
6537
|
+
"Biggest remaining folders/subfolders:",
|
|
6538
|
+
...measured.treeLines,
|
|
6539
|
+
"Each copy must be under the limit. To proceed, EITHER:",
|
|
6540
|
+
" - copy the heavy folders one at a time, e.g.:"
|
|
6541
|
+
);
|
|
6542
|
+
for (const child of measured.topChildren.slice(0, 3)) {
|
|
6543
|
+
lines.push(` agentbox cp ${hostSrc}/${child.path} ${boxDst}`);
|
|
6544
|
+
}
|
|
6545
|
+
lines.push(
|
|
6546
|
+
" - or drop what you do not need:",
|
|
6547
|
+
` agentbox cp ${hostSrc} ${boxDst} --exclude=<dir>`,
|
|
6548
|
+
" - or copy the whole thing anyway:",
|
|
6549
|
+
` agentbox cp ${hostSrc} ${boxDst} --yes`
|
|
6550
|
+
);
|
|
6551
|
+
} else {
|
|
6552
|
+
lines.push(
|
|
6553
|
+
"A single file cannot be split or trimmed. To copy it anyway:",
|
|
6554
|
+
` agentbox cp ${hostSrc} ${boxDst} --yes`
|
|
6555
|
+
);
|
|
6556
|
+
}
|
|
6557
|
+
throw new Error(lines.join("\n"));
|
|
6558
|
+
}
|
|
5903
6559
|
function parseBoxArg(arg) {
|
|
5904
6560
|
const idx = arg.indexOf(":");
|
|
5905
6561
|
if (idx === -1) return null;
|
|
@@ -5944,7 +6600,15 @@ function parseArgs(src, dst) {
|
|
|
5944
6600
|
var cpCommand = new Command9("cp").description("Copy files between host and box (like `docker cp`; direction picked by `name:` prefix)").argument("<src>", "`box:/path` (download) or host path (upload)").argument(
|
|
5945
6601
|
"[dst]",
|
|
5946
6602
|
"`box:/path` (upload) or host path (download); defaults to cwd when downloading"
|
|
5947
|
-
).
|
|
6603
|
+
).option(
|
|
6604
|
+
"--exclude <pattern>",
|
|
6605
|
+
'exclude paths matching <pattern> (repeatable; tar glob like "*/foo" or a bare dir name)',
|
|
6606
|
+
collectExclude,
|
|
6607
|
+
[]
|
|
6608
|
+
).option(
|
|
6609
|
+
"--no-default-excludes",
|
|
6610
|
+
`keep the heavy dirs cp drops by default (${effectiveExcludes([], true).join(", ")})`
|
|
6611
|
+
).option("-y, --yes", "copy even if the source is over the box.cpMaxBytes size limit").addHelpText(
|
|
5948
6612
|
"after",
|
|
5949
6613
|
[
|
|
5950
6614
|
"",
|
|
@@ -5952,27 +6616,30 @@ var cpCommand = new Command9("cp").description("Copy files between host and box
|
|
|
5952
6616
|
" agentbox cp mybox:/etc/foo ./foo # download (host path optional)",
|
|
5953
6617
|
" agentbox cp mybox:/workspace/.env # download into cwd",
|
|
5954
6618
|
" agentbox cp ./local.txt mybox:/workspace/ # upload (host path required)",
|
|
5955
|
-
" agentbox cp ./dir mybox:/workspace/ # upload directory (recursive)"
|
|
6619
|
+
" agentbox cp ./dir mybox:/workspace/ # upload directory (recursive)",
|
|
6620
|
+
' agentbox cp ./dir mybox:/workspace/ --exclude=.git --exclude="*/cache"'
|
|
5956
6621
|
].join("\n")
|
|
5957
|
-
).action(async (src, dst) => {
|
|
6622
|
+
).action(async (src, dst, opts) => {
|
|
5958
6623
|
try {
|
|
5959
6624
|
const parsed = parseArgs(src, dst);
|
|
5960
6625
|
const box = await resolveBoxOrExit(parsed.boxRef);
|
|
5961
6626
|
const isCloud = (box.provider ?? "docker") !== "docker";
|
|
6627
|
+
const tarPatterns = parsed.direction === "upload" ? await guardUploadSize(parsed.hostPath, `${parsed.boxRef}:${parsed.boxPath}`, opts) : toTarExcludes(effectiveExcludes(opts.exclude, opts.defaultExcludes));
|
|
5962
6628
|
if (isCloud) {
|
|
5963
6629
|
const provider = await providerForBox(box);
|
|
5964
6630
|
if (!provider.uploadPath || !provider.downloadPath) {
|
|
5965
6631
|
throw new Error(`provider '${provider.name}' does not support cp`);
|
|
5966
6632
|
}
|
|
5967
6633
|
if (parsed.direction === "upload") {
|
|
5968
|
-
const result = await provider.uploadPath(box, parsed.hostPath, parsed.boxPath);
|
|
6634
|
+
const result = await provider.uploadPath(box, parsed.hostPath, parsed.boxPath, tarPatterns);
|
|
5969
6635
|
process.stdout.write(`copied to ${box.name}:${result.finalPath}
|
|
5970
6636
|
`);
|
|
5971
6637
|
} else {
|
|
5972
6638
|
const result = await provider.downloadPath(
|
|
5973
6639
|
box,
|
|
5974
6640
|
parsed.boxPath,
|
|
5975
|
-
parsed.hostPath ?? process.cwd()
|
|
6641
|
+
parsed.hostPath ?? process.cwd(),
|
|
6642
|
+
tarPatterns
|
|
5976
6643
|
);
|
|
5977
6644
|
process.stdout.write(`copied to ${result.finalPath}
|
|
5978
6645
|
`);
|
|
@@ -5990,7 +6657,7 @@ var cpCommand = new Command9("cp").description("Copy files between host and box
|
|
|
5990
6657
|
throw new Error(`box ${box.name} has no container; was it destroyed?`);
|
|
5991
6658
|
}
|
|
5992
6659
|
if (parsed.direction === "upload") {
|
|
5993
|
-
const result = await uploadToBox(box, parsed.hostPath, parsed.boxPath);
|
|
6660
|
+
const result = await uploadToBox(box, parsed.hostPath, parsed.boxPath, tarPatterns);
|
|
5994
6661
|
if (result.warn) {
|
|
5995
6662
|
log15.warn(`copied to ${box.name}:${result.finalPath}, but ${result.warn}`);
|
|
5996
6663
|
} else {
|
|
@@ -5998,7 +6665,12 @@ var cpCommand = new Command9("cp").description("Copy files between host and box
|
|
|
5998
6665
|
`);
|
|
5999
6666
|
}
|
|
6000
6667
|
} else {
|
|
6001
|
-
const result = await downloadFromBox(
|
|
6668
|
+
const result = await downloadFromBox(
|
|
6669
|
+
box,
|
|
6670
|
+
parsed.boxPath,
|
|
6671
|
+
parsed.hostPath ?? process.cwd(),
|
|
6672
|
+
tarPatterns
|
|
6673
|
+
);
|
|
6002
6674
|
process.stdout.write(`copied to ${result.finalPath}
|
|
6003
6675
|
`);
|
|
6004
6676
|
}
|
|
@@ -6189,12 +6861,14 @@ var createCommand = new Command10("create").description(
|
|
|
6189
6861
|
const effectiveCheckpointRef = wiz.discardCheckpoint ? void 0 : checkpointRef;
|
|
6190
6862
|
if (wiz.action === "switch-to-claude" && isDocker) {
|
|
6191
6863
|
process.env[WIZARD_AUTOLAUNCH_ENV] = "1";
|
|
6864
|
+
if (wiz.recreate) process.env[WIZARD_RECREATE_ENV] = "1";
|
|
6192
6865
|
const serialized = serializeEnvFilesForEnv(wiz.envFilesToImport);
|
|
6193
6866
|
if (serialized !== void 0) process.env[WIZARD_ENV_FILES_ENV] = serialized;
|
|
6194
6867
|
try {
|
|
6195
6868
|
await claudeCommand.parseAsync(passthroughFlags(opts), { from: "user" });
|
|
6196
6869
|
} finally {
|
|
6197
6870
|
delete process.env[WIZARD_AUTOLAUNCH_ENV];
|
|
6871
|
+
delete process.env[WIZARD_RECREATE_ENV];
|
|
6198
6872
|
delete process.env[WIZARD_ENV_FILES_ENV];
|
|
6199
6873
|
}
|
|
6200
6874
|
return;
|
|
@@ -6320,7 +6994,7 @@ var createCommand = new Command10("create").description(
|
|
|
6320
6994
|
}
|
|
6321
6995
|
outro4("done");
|
|
6322
6996
|
if (attachClaudeAfter) {
|
|
6323
|
-
const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-
|
|
6997
|
+
const { cloudAgentAttach: cloudAgentAttach2 } = await import("./_cloud-attach-5KJWOASL.js");
|
|
6324
6998
|
await cloudAgentAttach2({
|
|
6325
6999
|
box: result.record,
|
|
6326
7000
|
binary: "claude",
|
|
@@ -6953,8 +7627,8 @@ var Compositor = class {
|
|
|
6953
7627
|
this.drawChrome();
|
|
6954
7628
|
this.scheduleRender();
|
|
6955
7629
|
this.pollTimer = setInterval(() => void this.poll(), POLL_MS);
|
|
6956
|
-
await new Promise((
|
|
6957
|
-
this.resolveDone =
|
|
7630
|
+
await new Promise((resolve5) => {
|
|
7631
|
+
this.resolveDone = resolve5;
|
|
6958
7632
|
});
|
|
6959
7633
|
}
|
|
6960
7634
|
async refreshBoxes() {
|
|
@@ -9332,114 +10006,6 @@ function resolveToken(raw) {
|
|
|
9332
10006
|
return `<${raw}>`;
|
|
9333
10007
|
}
|
|
9334
10008
|
|
|
9335
|
-
// src/lib/drive/tmux.ts
|
|
9336
|
-
var TMUX_USER = "vscode";
|
|
9337
|
-
async function captureSession(provider, box, session, opts = {}) {
|
|
9338
|
-
const argv2 = ["tmux", "capture-pane", opts.ansi ? "-pe" : "-p", "-t", session];
|
|
9339
|
-
if (opts.rows) {
|
|
9340
|
-
argv2.push("-S", String(opts.rows.from), "-E", String(opts.rows.to));
|
|
9341
|
-
}
|
|
9342
|
-
const res = await provider.exec(box, argv2, { user: TMUX_USER });
|
|
9343
|
-
if (res.exitCode !== 0) {
|
|
9344
|
-
throw new Error(failure("capture-pane", session, res.stderr || res.stdout));
|
|
9345
|
-
}
|
|
9346
|
-
return res.stdout.replace(/\n$/, "");
|
|
9347
|
-
}
|
|
9348
|
-
async function paneInfo(provider, box, session) {
|
|
9349
|
-
const fmt = "#{pane_width},#{pane_height},#{cursor_x},#{cursor_y}";
|
|
9350
|
-
const res = await provider.exec(box, ["tmux", "display-message", "-p", "-t", session, fmt], {
|
|
9351
|
-
user: TMUX_USER
|
|
9352
|
-
});
|
|
9353
|
-
if (res.exitCode !== 0) {
|
|
9354
|
-
throw new Error(failure("display-message", session, res.stderr || res.stdout));
|
|
9355
|
-
}
|
|
9356
|
-
const m = /^(\d+),(\d+),(\d+),(\d+)/.exec(res.stdout.trim());
|
|
9357
|
-
if (!m) throw new Error(`tmux display-message returned unexpected output: ${res.stdout}`);
|
|
9358
|
-
return {
|
|
9359
|
-
cols: Number(m[1]),
|
|
9360
|
-
rows: Number(m[2]),
|
|
9361
|
-
cursor: { x: Number(m[3]), y: Number(m[4]) }
|
|
9362
|
-
};
|
|
9363
|
-
}
|
|
9364
|
-
async function sendLiteral(provider, box, session, literal) {
|
|
9365
|
-
if (literal.length === 0) return;
|
|
9366
|
-
const res = await provider.exec(box, ["tmux", "send-keys", "-t", session, "-l", "--", literal], {
|
|
9367
|
-
user: TMUX_USER
|
|
9368
|
-
});
|
|
9369
|
-
if (res.exitCode !== 0) {
|
|
9370
|
-
throw new Error(failure("send-keys -l", session, res.stderr || res.stdout));
|
|
9371
|
-
}
|
|
9372
|
-
}
|
|
9373
|
-
async function sendKey(provider, box, session, key) {
|
|
9374
|
-
const res = await provider.exec(box, ["tmux", "send-keys", "-t", session, key], {
|
|
9375
|
-
user: TMUX_USER
|
|
9376
|
-
});
|
|
9377
|
-
if (res.exitCode !== 0) {
|
|
9378
|
-
throw new Error(failure("send-keys", session, res.stderr || res.stdout));
|
|
9379
|
-
}
|
|
9380
|
-
}
|
|
9381
|
-
async function resizeWindow(provider, box, session, cols, rows) {
|
|
9382
|
-
const res = await provider.exec(
|
|
9383
|
-
box,
|
|
9384
|
-
["tmux", "resize-window", "-t", session, "-x", String(cols), "-y", String(rows)],
|
|
9385
|
-
{ user: TMUX_USER }
|
|
9386
|
-
);
|
|
9387
|
-
if (res.exitCode !== 0) {
|
|
9388
|
-
throw new Error(failure("resize-window", session, res.stderr || res.stdout));
|
|
9389
|
-
}
|
|
9390
|
-
}
|
|
9391
|
-
async function listSessions(provider, box) {
|
|
9392
|
-
const res = await provider.exec(
|
|
9393
|
-
box,
|
|
9394
|
-
["tmux", "list-sessions", "-F", "#{session_name}"],
|
|
9395
|
-
{ user: TMUX_USER }
|
|
9396
|
-
);
|
|
9397
|
-
if (res.exitCode !== 0) return [];
|
|
9398
|
-
return res.stdout.split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9399
|
-
}
|
|
9400
|
-
function failure(op, session, detail) {
|
|
9401
|
-
const tail = detail.trim();
|
|
9402
|
-
return `tmux ${op} failed for session '${session}'${tail ? `: ${tail}` : ""}`;
|
|
9403
|
-
}
|
|
9404
|
-
|
|
9405
|
-
// src/lib/drive/session.ts
|
|
9406
|
-
var AGENT_SESSION_PRIORITY = ["claude", "codex", "opencode"];
|
|
9407
|
-
async function resolveDriveSession(provider, box, explicit) {
|
|
9408
|
-
if (await provider.probeState(box) === "paused") {
|
|
9409
|
-
process.stderr.write(`drive: box ${box.name} is paused; unpausing
|
|
9410
|
-
`);
|
|
9411
|
-
await provider.resume(box);
|
|
9412
|
-
}
|
|
9413
|
-
const sessions = await listSessions(provider, box);
|
|
9414
|
-
if (explicit !== void 0 && explicit !== "") {
|
|
9415
|
-
if (!sessions.includes(explicit)) {
|
|
9416
|
-
throw new SessionNotFoundError(explicit, sessions);
|
|
9417
|
-
}
|
|
9418
|
-
return { name: explicit, available: sessions };
|
|
9419
|
-
}
|
|
9420
|
-
for (const candidate of AGENT_SESSION_PRIORITY) {
|
|
9421
|
-
if (sessions.includes(candidate)) {
|
|
9422
|
-
return { name: candidate, available: sessions };
|
|
9423
|
-
}
|
|
9424
|
-
}
|
|
9425
|
-
if (sessions.length === 1 && sessions[0]) {
|
|
9426
|
-
return { name: sessions[0], available: sessions };
|
|
9427
|
-
}
|
|
9428
|
-
throw new SessionNotFoundError(void 0, sessions);
|
|
9429
|
-
}
|
|
9430
|
-
var SessionNotFoundError = class extends Error {
|
|
9431
|
-
wanted;
|
|
9432
|
-
available;
|
|
9433
|
-
constructor(wanted, available) {
|
|
9434
|
-
const head = wanted ? `no tmux session '${wanted}' in this box` : "no agent tmux session running in this box";
|
|
9435
|
-
const tail = available.length ? ` (running: ${available.join(", ")})` : " (tmux server not running or no sessions)";
|
|
9436
|
-
super(head + tail);
|
|
9437
|
-
this.name = "SessionNotFoundError";
|
|
9438
|
-
this.wanted = wanted;
|
|
9439
|
-
this.available = available;
|
|
9440
|
-
}
|
|
9441
|
-
};
|
|
9442
|
-
|
|
9443
10009
|
// src/commands/drive.ts
|
|
9444
10010
|
var PROMPT_ENTER_DELAY_MS = 200;
|
|
9445
10011
|
var POLL_INTERVAL_MS2 = 250;
|
|
@@ -9507,7 +10073,7 @@ var drivePromptCommand = new Command24("prompt").description('Type text into the
|
|
|
9507
10073
|
const session = await resolveDriveSession(provider, box, opts.session);
|
|
9508
10074
|
const delay = opts.delay !== void 0 ? parsePositiveInt2(opts.delay, "--delay") : PROMPT_ENTER_DELAY_MS;
|
|
9509
10075
|
await sendLiteral(provider, box, session.name, text);
|
|
9510
|
-
if (delay > 0) await
|
|
10076
|
+
if (delay > 0) await sleep3(delay);
|
|
9511
10077
|
await sendKey(provider, box, session.name, "Enter");
|
|
9512
10078
|
} catch (err) {
|
|
9513
10079
|
handleDriveError(err);
|
|
@@ -9533,7 +10099,7 @@ var driveWaitCommand = new Command24("wait").description("Block until --text app
|
|
|
9533
10099
|
}
|
|
9534
10100
|
return;
|
|
9535
10101
|
}
|
|
9536
|
-
await
|
|
10102
|
+
await sleep3(POLL_INTERVAL_MS2);
|
|
9537
10103
|
}
|
|
9538
10104
|
const elapsedMs = Date.now() - start;
|
|
9539
10105
|
if (opts.json === true) {
|
|
@@ -9593,7 +10159,7 @@ function parsePositiveInt2(raw, label) {
|
|
|
9593
10159
|
}
|
|
9594
10160
|
return n;
|
|
9595
10161
|
}
|
|
9596
|
-
function
|
|
10162
|
+
function sleep3(ms) {
|
|
9597
10163
|
return new Promise((r) => setTimeout(r, ms));
|
|
9598
10164
|
}
|
|
9599
10165
|
|
|
@@ -9602,7 +10168,7 @@ import { log as log30 } from "@clack/prompts";
|
|
|
9602
10168
|
import { Command as Command25 } from "commander";
|
|
9603
10169
|
import { existsSync as existsSync5, readdirSync, statSync } from "fs";
|
|
9604
10170
|
import { homedir as homedir12 } from "os";
|
|
9605
|
-
import { join as
|
|
10171
|
+
import { join as join15 } from "path";
|
|
9606
10172
|
var FORK_AGENTS = ["claude", "codex", "opencode"];
|
|
9607
10173
|
var AGENT_COMMAND = {
|
|
9608
10174
|
claude: claudeCommand,
|
|
@@ -9622,12 +10188,12 @@ function resolveSessionArgs(agent, opts) {
|
|
|
9622
10188
|
}
|
|
9623
10189
|
if (opts.session) return ["--resume", opts.session];
|
|
9624
10190
|
if (agent === "codex") return ["--continue"];
|
|
9625
|
-
const dir =
|
|
10191
|
+
const dir = join15(homedir12(), ".claude", "projects", encodeClaudeProjectsDir(opts.workspace));
|
|
9626
10192
|
if (!existsSync5(dir)) return ["--continue"];
|
|
9627
10193
|
const now = Date.now();
|
|
9628
10194
|
const recent = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
9629
10195
|
try {
|
|
9630
|
-
return statSync(
|
|
10196
|
+
return statSync(join15(dir, f)).mtimeMs;
|
|
9631
10197
|
} catch {
|
|
9632
10198
|
return 0;
|
|
9633
10199
|
}
|
|
@@ -9738,13 +10304,13 @@ import {
|
|
|
9738
10304
|
writeFileSync as writeFileSync5
|
|
9739
10305
|
} from "fs";
|
|
9740
10306
|
import { homedir as homedir16 } from "os";
|
|
9741
|
-
import { dirname as dirname3, join as
|
|
10307
|
+
import { dirname as dirname3, join as join19, resolve as resolve4, sep } from "path";
|
|
9742
10308
|
import { fileURLToPath } from "url";
|
|
9743
10309
|
|
|
9744
10310
|
// src/lib/doctor-checks.ts
|
|
9745
10311
|
import { accessSync, constants as fsConstants, mkdirSync as mkdirSync3 } from "fs";
|
|
9746
10312
|
import { homedir as homedir13 } from "os";
|
|
9747
|
-
import { join as
|
|
10313
|
+
import { join as join16 } from "path";
|
|
9748
10314
|
import { execa as execa3 } from "execa";
|
|
9749
10315
|
var ALL_PROVIDERS = ["docker", "daytona", "hetzner", "vercel", "e2b"];
|
|
9750
10316
|
var NODE_MIN_MAJOR = 20;
|
|
@@ -9764,12 +10330,12 @@ function parseNodeMajorMinor(v) {
|
|
|
9764
10330
|
if (!m) return [0, 0];
|
|
9765
10331
|
return [Number(m[1]), Number(m[2])];
|
|
9766
10332
|
}
|
|
9767
|
-
function
|
|
10333
|
+
function firstLine2(s) {
|
|
9768
10334
|
const i = s.indexOf("\n");
|
|
9769
10335
|
return i === -1 ? s : s.slice(0, i);
|
|
9770
10336
|
}
|
|
9771
10337
|
function errSummary(err) {
|
|
9772
|
-
return err instanceof Error ?
|
|
10338
|
+
return err instanceof Error ? firstLine2(err.message) : String(err);
|
|
9773
10339
|
}
|
|
9774
10340
|
function checkNode() {
|
|
9775
10341
|
const v = process.versions.node;
|
|
@@ -9792,7 +10358,7 @@ function checkPlatform() {
|
|
|
9792
10358
|
};
|
|
9793
10359
|
}
|
|
9794
10360
|
function checkAgentboxHome() {
|
|
9795
|
-
const dir =
|
|
10361
|
+
const dir = join16(homedir13(), ".agentbox");
|
|
9796
10362
|
try {
|
|
9797
10363
|
mkdirSync3(dir, { recursive: true });
|
|
9798
10364
|
accessSync(dir, fsConstants.W_OK);
|
|
@@ -9864,7 +10430,7 @@ async function dockerChecks() {
|
|
|
9864
10430
|
];
|
|
9865
10431
|
}
|
|
9866
10432
|
const daemonRes = { label: "docker daemon", status: "ok", detail: "reachable" };
|
|
9867
|
-
const mod = await import("./dist-
|
|
10433
|
+
const mod = await import("./dist-7YB7BMNG.js");
|
|
9868
10434
|
let imgRes;
|
|
9869
10435
|
try {
|
|
9870
10436
|
const img = await mod.imageInfo(mod.DEFAULT_BOX_IMAGE);
|
|
@@ -9895,7 +10461,7 @@ async function dockerChecks() {
|
|
|
9895
10461
|
}
|
|
9896
10462
|
async function daytonaChecks() {
|
|
9897
10463
|
try {
|
|
9898
|
-
const mod = await import("./dist-
|
|
10464
|
+
const mod = await import("./dist-SL2QSMBE.js");
|
|
9899
10465
|
const status = await mod.getDaytonaStatus();
|
|
9900
10466
|
if (!status.configured) {
|
|
9901
10467
|
return [
|
|
@@ -9931,7 +10497,7 @@ async function daytonaChecks() {
|
|
|
9931
10497
|
}
|
|
9932
10498
|
async function hetznerChecks() {
|
|
9933
10499
|
try {
|
|
9934
|
-
const mod = await import("./dist-
|
|
10500
|
+
const mod = await import("./dist-VHI5QOSQ.js");
|
|
9935
10501
|
const cred = mod.readHetznerCredStatus();
|
|
9936
10502
|
const credRes = cred.source === "none" ? {
|
|
9937
10503
|
label: "credentials",
|
|
@@ -9963,7 +10529,7 @@ async function hetznerChecks() {
|
|
|
9963
10529
|
}
|
|
9964
10530
|
async function vercelChecks() {
|
|
9965
10531
|
try {
|
|
9966
|
-
const mod = await import("./dist-
|
|
10532
|
+
const mod = await import("./dist-XC47DSCR.js");
|
|
9967
10533
|
const cred = mod.readVercelCredStatus();
|
|
9968
10534
|
const credRes = cred.auth === "none" ? {
|
|
9969
10535
|
label: "credentials",
|
|
@@ -9999,7 +10565,7 @@ async function vercelChecks() {
|
|
|
9999
10565
|
}
|
|
10000
10566
|
async function e2bChecks() {
|
|
10001
10567
|
try {
|
|
10002
|
-
const mod = await import("./dist-
|
|
10568
|
+
const mod = await import("./dist-4IQFJJQI.js");
|
|
10003
10569
|
const cred = mod.readE2bCredStatus();
|
|
10004
10570
|
const credRes = cred.auth === "none" ? {
|
|
10005
10571
|
label: "credentials",
|
|
@@ -10033,6 +10599,103 @@ async function e2bChecks() {
|
|
|
10033
10599
|
];
|
|
10034
10600
|
}
|
|
10035
10601
|
}
|
|
10602
|
+
var INTEGRATION_PROBE_TIMEOUT_MS = 1e4;
|
|
10603
|
+
async function probeIntegrationBin(bin, args) {
|
|
10604
|
+
try {
|
|
10605
|
+
const r = await execa3(bin, [...args], {
|
|
10606
|
+
reject: false,
|
|
10607
|
+
timeout: INTEGRATION_PROBE_TIMEOUT_MS,
|
|
10608
|
+
stdin: "ignore"
|
|
10609
|
+
});
|
|
10610
|
+
const code = r.code;
|
|
10611
|
+
if (code === "ENOENT") {
|
|
10612
|
+
return { exitCode: 127, stdout: "", stderr: r.stderr ?? "", missing: true };
|
|
10613
|
+
}
|
|
10614
|
+
if (r.timedOut) {
|
|
10615
|
+
return {
|
|
10616
|
+
exitCode: 124,
|
|
10617
|
+
stdout: "",
|
|
10618
|
+
stderr: `timed out after ${String(INTEGRATION_PROBE_TIMEOUT_MS)}ms`,
|
|
10619
|
+
missing: false
|
|
10620
|
+
};
|
|
10621
|
+
}
|
|
10622
|
+
return {
|
|
10623
|
+
exitCode: r.exitCode ?? 1,
|
|
10624
|
+
stdout: typeof r.stdout === "string" ? r.stdout : "",
|
|
10625
|
+
stderr: typeof r.stderr === "string" ? r.stderr : "",
|
|
10626
|
+
missing: false
|
|
10627
|
+
};
|
|
10628
|
+
} catch (err) {
|
|
10629
|
+
const code = err.code;
|
|
10630
|
+
return {
|
|
10631
|
+
exitCode: code === "ENOENT" ? 127 : 1,
|
|
10632
|
+
stdout: "",
|
|
10633
|
+
stderr: errSummary(err),
|
|
10634
|
+
missing: code === "ENOENT"
|
|
10635
|
+
};
|
|
10636
|
+
}
|
|
10637
|
+
}
|
|
10638
|
+
async function integrationsChecks(loader = loadEffectiveConfig) {
|
|
10639
|
+
let cfg;
|
|
10640
|
+
try {
|
|
10641
|
+
cfg = await loader(process.cwd());
|
|
10642
|
+
} catch {
|
|
10643
|
+
cfg = { effective: {} };
|
|
10644
|
+
}
|
|
10645
|
+
return Promise.all(
|
|
10646
|
+
ALL_CONNECTORS.map((connector) => checkOneIntegration(connector, cfg.effective.integrations))
|
|
10647
|
+
);
|
|
10648
|
+
}
|
|
10649
|
+
async function checkOneIntegration(connector, integrations) {
|
|
10650
|
+
const svc = connector.service;
|
|
10651
|
+
const enabled = integrations?.[svc]?.enabled === true;
|
|
10652
|
+
if (!enabled) {
|
|
10653
|
+
return {
|
|
10654
|
+
label: svc,
|
|
10655
|
+
status: "info",
|
|
10656
|
+
detail: "disabled",
|
|
10657
|
+
hint: `enable with \`agentbox config set --project integrations.${svc}.enabled true\``
|
|
10658
|
+
};
|
|
10659
|
+
}
|
|
10660
|
+
const version = await probeIntegrationBin(connector.hostBin, connector.detect.versionArgs);
|
|
10661
|
+
if (version.missing || version.exitCode === 127) {
|
|
10662
|
+
return {
|
|
10663
|
+
label: svc,
|
|
10664
|
+
status: "warn",
|
|
10665
|
+
detail: `${connector.hostBin} not installed`,
|
|
10666
|
+
hint: connector.detect.installHint ?? `install the ${svc} CLI (\`${connector.hostBin}\`) on the host`
|
|
10667
|
+
};
|
|
10668
|
+
}
|
|
10669
|
+
if (version.exitCode !== 0) {
|
|
10670
|
+
const tail = firstLine2((version.stderr || version.stdout).trim());
|
|
10671
|
+
return {
|
|
10672
|
+
label: svc,
|
|
10673
|
+
status: "warn",
|
|
10674
|
+
detail: `${connector.hostBin} ${connector.detect.versionArgs.join(" ")} failed${tail ? `: ${tail}` : ""}`
|
|
10675
|
+
};
|
|
10676
|
+
}
|
|
10677
|
+
const versionLine = firstLine2((version.stdout || version.stderr).trim()) || connector.hostBin;
|
|
10678
|
+
if (!connector.detect.authArgs || connector.detect.authArgs.length === 0) {
|
|
10679
|
+
return { label: svc, status: "ok", detail: versionLine };
|
|
10680
|
+
}
|
|
10681
|
+
const auth = await probeIntegrationBin(connector.hostBin, connector.detect.authArgs);
|
|
10682
|
+
if (auth.exitCode === 124) {
|
|
10683
|
+
return {
|
|
10684
|
+
label: svc,
|
|
10685
|
+
status: "warn",
|
|
10686
|
+
detail: `auth check timed out after ${String(INTEGRATION_PROBE_TIMEOUT_MS / 1e3)}s`
|
|
10687
|
+
};
|
|
10688
|
+
}
|
|
10689
|
+
if (auth.exitCode !== 0) {
|
|
10690
|
+
return {
|
|
10691
|
+
label: svc,
|
|
10692
|
+
status: "warn",
|
|
10693
|
+
detail: "not logged in",
|
|
10694
|
+
hint: connector.detect.loginHint ?? `run \`${connector.hostBin} login\``
|
|
10695
|
+
};
|
|
10696
|
+
}
|
|
10697
|
+
return { label: svc, status: "ok", detail: `${versionLine} \xB7 authed` };
|
|
10698
|
+
}
|
|
10036
10699
|
async function runProviderChecks(name) {
|
|
10037
10700
|
let results;
|
|
10038
10701
|
switch (name) {
|
|
@@ -10057,7 +10720,8 @@ async function runProviderChecks(name) {
|
|
|
10057
10720
|
async function runAllChecks() {
|
|
10058
10721
|
const sys = { title: "system", results: await runSystemChecks() };
|
|
10059
10722
|
const providerGroups = await Promise.all(ALL_PROVIDERS.map((n) => runProviderChecks(n)));
|
|
10060
|
-
|
|
10723
|
+
const integrations = { title: "integrations", results: await integrationsChecks() };
|
|
10724
|
+
return [sys, ...providerGroups, integrations];
|
|
10061
10725
|
}
|
|
10062
10726
|
function worstInResults(results) {
|
|
10063
10727
|
let worst = "ok";
|
|
@@ -10083,6 +10747,12 @@ function summaryToken(group) {
|
|
|
10083
10747
|
if (worst === "warn") return "system warn";
|
|
10084
10748
|
return "system ok";
|
|
10085
10749
|
}
|
|
10750
|
+
if (group.title === "integrations") {
|
|
10751
|
+
if (worst === "fail") return "integrations FAIL";
|
|
10752
|
+
if (worst === "warn") return "integrations check";
|
|
10753
|
+
const anyEnabled = group.results.some((r) => r.status === "ok");
|
|
10754
|
+
return anyEnabled ? "integrations ready" : "integrations off";
|
|
10755
|
+
}
|
|
10086
10756
|
if (worst === "fail") return `${group.title} FAIL`;
|
|
10087
10757
|
if (worst === "warn") {
|
|
10088
10758
|
const cred = group.results.find((r) => r.label === "credentials");
|
|
@@ -10094,12 +10764,13 @@ function summaryToken(group) {
|
|
|
10094
10764
|
var C_GREEN = "\x1B[32m";
|
|
10095
10765
|
var C_YELLOW = "\x1B[33m";
|
|
10096
10766
|
var C_RED = "\x1B[31m";
|
|
10767
|
+
var C_DIM = "\x1B[2m";
|
|
10097
10768
|
var C_RESET = "\x1B[0m";
|
|
10098
10769
|
var COLOR = !process.env.NO_COLOR;
|
|
10099
10770
|
function statusMarker(s) {
|
|
10100
|
-
const glyph = s === "ok" ? "\u2713" : s === "warn" ? "\u26A0" : "\u2717";
|
|
10771
|
+
const glyph = s === "ok" ? "\u2713" : s === "info" ? "\xB7" : s === "warn" ? "\u26A0" : "\u2717";
|
|
10101
10772
|
if (!COLOR) return glyph;
|
|
10102
|
-
const color = s === "ok" ? C_GREEN : s === "warn" ? C_YELLOW : C_RED;
|
|
10773
|
+
const color = s === "ok" ? C_GREEN : s === "info" ? C_DIM : s === "warn" ? C_YELLOW : C_RED;
|
|
10103
10774
|
return `${color}${glyph}${C_RESET}`;
|
|
10104
10775
|
}
|
|
10105
10776
|
function formatCompact(groups) {
|
|
@@ -10110,6 +10781,7 @@ function pad3(s, width) {
|
|
|
10110
10781
|
}
|
|
10111
10782
|
function statusBadge(s) {
|
|
10112
10783
|
if (s === "ok") return "[ ok ]";
|
|
10784
|
+
if (s === "info") return "[info]";
|
|
10113
10785
|
if (s === "warn") return "[warn]";
|
|
10114
10786
|
return "[FAIL]";
|
|
10115
10787
|
}
|
|
@@ -10130,10 +10802,10 @@ function formatDetailed(groups) {
|
|
|
10130
10802
|
// src/lib/first-run.ts
|
|
10131
10803
|
import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
10132
10804
|
import { homedir as homedir14 } from "os";
|
|
10133
|
-
import { dirname, join as
|
|
10805
|
+
import { dirname, join as join17 } from "path";
|
|
10134
10806
|
var MARKER_VERSION = 1;
|
|
10135
10807
|
function setupMarkerPath() {
|
|
10136
|
-
return
|
|
10808
|
+
return join17(homedir14(), ".agentbox", "setup-complete.json");
|
|
10137
10809
|
}
|
|
10138
10810
|
function isFirstRun() {
|
|
10139
10811
|
return !existsSync6(setupMarkerPath());
|
|
@@ -10154,12 +10826,12 @@ import { intro as intro6, log as log31, note as note2, outro as outro5 } from "@
|
|
|
10154
10826
|
import { Command as Command26 } from "commander";
|
|
10155
10827
|
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
10156
10828
|
import { homedir as homedir15 } from "os";
|
|
10157
|
-
import { dirname as dirname2, join as
|
|
10829
|
+
import { dirname as dirname2, join as join18 } from "path";
|
|
10158
10830
|
var CONTROL_ID = "agentbox";
|
|
10159
10831
|
function cmuxDockPath(env = process.env) {
|
|
10160
10832
|
const xdg = env["XDG_CONFIG_HOME"];
|
|
10161
|
-
const base = xdg && xdg.length > 0 ? xdg :
|
|
10162
|
-
return
|
|
10833
|
+
const base = xdg && xdg.length > 0 ? xdg : join18(homedir15(), ".config");
|
|
10834
|
+
return join18(base, "cmux", "dock.json");
|
|
10163
10835
|
}
|
|
10164
10836
|
function upsertAgentboxControl(doc, opts) {
|
|
10165
10837
|
const controls = Array.isArray(doc.controls) ? doc.controls : [];
|
|
@@ -10251,7 +10923,7 @@ var SYNC_END2 = "\x1B[?2026l";
|
|
|
10251
10923
|
var HIDE_CURSOR = "\x1B[?25l";
|
|
10252
10924
|
var SHOW_CURSOR = "\x1B[?25h";
|
|
10253
10925
|
var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
10254
|
-
var
|
|
10926
|
+
var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
10255
10927
|
function shineColor(dist) {
|
|
10256
10928
|
const d = Math.abs(dist);
|
|
10257
10929
|
if (d === 0) return 231;
|
|
@@ -10301,11 +10973,11 @@ ${HIDE_CURSOR}`);
|
|
|
10301
10973
|
statusLine2(spin) + "\x1B[3A\r" + // back up to the logo's first row
|
|
10302
10974
|
SYNC_END2;
|
|
10303
10975
|
process.stdout.write(frame);
|
|
10304
|
-
await
|
|
10976
|
+
await sleep4(frameMs);
|
|
10305
10977
|
}
|
|
10306
10978
|
process.stdout.write(SYNC_BEGIN2 + `\x1B[38;5;39m${LOGO_L1}
|
|
10307
10979
|
${LOGO_L2}\x1B[0m` + SYNC_END2);
|
|
10308
|
-
await
|
|
10980
|
+
await sleep4(250);
|
|
10309
10981
|
process.stdout.write(SYNC_BEGIN2 + "\n\x1B[2K\n\x1B[2K" + SHOW_CURSOR + SYNC_END2);
|
|
10310
10982
|
process.removeListener("exit", restoreCursor);
|
|
10311
10983
|
process.removeListener("SIGINT", onSigint);
|
|
@@ -10313,30 +10985,30 @@ ${LOGO_L2}\x1B[0m` + SYNC_END2);
|
|
|
10313
10985
|
var LEGACY_INFO_MARKER = "Drive AgentBox from the host:";
|
|
10314
10986
|
function installTargets() {
|
|
10315
10987
|
const home = homedir16();
|
|
10316
|
-
const claudeSkills =
|
|
10988
|
+
const claudeSkills = join19(home, ".claude", "skills");
|
|
10317
10989
|
return [
|
|
10318
|
-
{ src:
|
|
10990
|
+
{ src: join19("agentbox", "SKILL.md"), dest: join19(claudeSkills, "agentbox", "SKILL.md") },
|
|
10319
10991
|
{
|
|
10320
|
-
src:
|
|
10321
|
-
dest:
|
|
10992
|
+
src: join19("agentbox-info", "SKILL.md"),
|
|
10993
|
+
dest: join19(claudeSkills, "agentbox-info", "SKILL.md")
|
|
10322
10994
|
},
|
|
10323
10995
|
{
|
|
10324
|
-
src:
|
|
10325
|
-
dest:
|
|
10326
|
-
gateDir:
|
|
10996
|
+
src: join19("codex", "agentbox.md"),
|
|
10997
|
+
dest: join19(home, ".codex", "prompts", "agentbox.md"),
|
|
10998
|
+
gateDir: join19(home, ".codex")
|
|
10327
10999
|
},
|
|
10328
11000
|
{
|
|
10329
|
-
src:
|
|
10330
|
-
dest:
|
|
10331
|
-
gateDir:
|
|
11001
|
+
src: join19("opencode", "agentbox.md"),
|
|
11002
|
+
dest: join19(home, ".config", "opencode", "commands", "agentbox.md"),
|
|
11003
|
+
gateDir: join19(home, ".config", "opencode")
|
|
10332
11004
|
}
|
|
10333
11005
|
];
|
|
10334
11006
|
}
|
|
10335
11007
|
function resolveHostSkillsDir() {
|
|
10336
11008
|
const here = dirname3(fileURLToPath(import.meta.url));
|
|
10337
11009
|
const candidates = [
|
|
10338
|
-
|
|
10339
|
-
|
|
11010
|
+
resolve4(here, "..", "share", "host-skills"),
|
|
11011
|
+
resolve4(here, "..", "..", "share", "host-skills")
|
|
10340
11012
|
];
|
|
10341
11013
|
for (const c of candidates) {
|
|
10342
11014
|
if (existsSync8(c)) return c;
|
|
@@ -10375,7 +11047,7 @@ function installHostSkills(opts = {}) {
|
|
|
10375
11047
|
const blocked = [];
|
|
10376
11048
|
let skipped = 0;
|
|
10377
11049
|
for (const t of installTargets()) {
|
|
10378
|
-
const src =
|
|
11050
|
+
const src = join19(srcDir, t.src);
|
|
10379
11051
|
if (!existsSync8(src)) {
|
|
10380
11052
|
if (!quiet) log32.warn(`bundled file missing (skipped): ${src}`);
|
|
10381
11053
|
skipped++;
|
|
@@ -10397,7 +11069,7 @@ function installHostSkills(opts = {}) {
|
|
|
10397
11069
|
mkdirSync6(dirname3(t.dest), { recursive: true });
|
|
10398
11070
|
if (link) {
|
|
10399
11071
|
rmSync(t.dest, { force: true });
|
|
10400
|
-
symlinkSync2(
|
|
11072
|
+
symlinkSync2(resolve4(srcDir, t.src), t.dest);
|
|
10401
11073
|
} else {
|
|
10402
11074
|
if (isSymlink(t.dest)) rmSync(t.dest, { force: true });
|
|
10403
11075
|
writeFileSync5(t.dest, readFileSync2(src, "utf8"));
|
|
@@ -10430,7 +11102,7 @@ function ensureTty() {
|
|
|
10430
11102
|
async function runProviderLogin(name) {
|
|
10431
11103
|
if (name === "docker") return true;
|
|
10432
11104
|
if (name === "daytona") {
|
|
10433
|
-
const mod2 = await import("./dist-
|
|
11105
|
+
const mod2 = await import("./dist-SL2QSMBE.js");
|
|
10434
11106
|
const status2 = await mod2.getDaytonaStatus();
|
|
10435
11107
|
if (status2.configured) {
|
|
10436
11108
|
log32.info("daytona: already configured");
|
|
@@ -10443,7 +11115,7 @@ async function runProviderLogin(name) {
|
|
|
10443
11115
|
return true;
|
|
10444
11116
|
}
|
|
10445
11117
|
if (name === "hetzner") {
|
|
10446
|
-
const mod2 = await import("./dist-
|
|
11118
|
+
const mod2 = await import("./dist-VHI5QOSQ.js");
|
|
10447
11119
|
const status2 = mod2.readHetznerCredStatus();
|
|
10448
11120
|
if (status2.source !== "none") {
|
|
10449
11121
|
log32.info("hetzner: already configured");
|
|
@@ -10456,7 +11128,7 @@ async function runProviderLogin(name) {
|
|
|
10456
11128
|
return true;
|
|
10457
11129
|
}
|
|
10458
11130
|
if (name === "vercel") {
|
|
10459
|
-
const mod2 = await import("./dist-
|
|
11131
|
+
const mod2 = await import("./dist-XC47DSCR.js");
|
|
10460
11132
|
const status2 = mod2.readVercelCredStatus();
|
|
10461
11133
|
if (status2.auth !== "none") {
|
|
10462
11134
|
log32.info(`vercel: already configured (${status2.auth})`);
|
|
@@ -10468,7 +11140,7 @@ async function runProviderLogin(name) {
|
|
|
10468
11140
|
await mod2.ensureVercelCredentials();
|
|
10469
11141
|
return true;
|
|
10470
11142
|
}
|
|
10471
|
-
const mod = await import("./dist-
|
|
11143
|
+
const mod = await import("./dist-4IQFJJQI.js");
|
|
10472
11144
|
const status = mod.readE2bCredStatus();
|
|
10473
11145
|
if (status.auth !== "none") {
|
|
10474
11146
|
log32.info(`e2b: already configured (${status.auth})`);
|
|
@@ -10653,9 +11325,15 @@ var doctorCommand = new Command28("doctor").description(
|
|
|
10653
11325
|
);
|
|
10654
11326
|
process.exit(1);
|
|
10655
11327
|
}
|
|
11328
|
+
const [sys, prov, integrations] = await Promise.all([
|
|
11329
|
+
runSystemChecks(),
|
|
11330
|
+
runProviderChecks(name),
|
|
11331
|
+
integrationsChecks()
|
|
11332
|
+
]);
|
|
10656
11333
|
groups = [
|
|
10657
|
-
{ title: "system", results:
|
|
10658
|
-
|
|
11334
|
+
{ title: "system", results: sys },
|
|
11335
|
+
prov,
|
|
11336
|
+
{ title: "integrations", results: integrations }
|
|
10659
11337
|
];
|
|
10660
11338
|
} else {
|
|
10661
11339
|
groups = await runAllChecks();
|
|
@@ -10868,17 +11546,17 @@ async function applyLiveCloudStates(boxes) {
|
|
|
10868
11546
|
);
|
|
10869
11547
|
}
|
|
10870
11548
|
function withTimeout(p, ms) {
|
|
10871
|
-
return new Promise((
|
|
10872
|
-
const t = setTimeout(() =>
|
|
11549
|
+
return new Promise((resolve5) => {
|
|
11550
|
+
const t = setTimeout(() => resolve5(null), ms);
|
|
10873
11551
|
if (typeof t.unref === "function") t.unref();
|
|
10874
11552
|
p.then(
|
|
10875
11553
|
(v) => {
|
|
10876
11554
|
clearTimeout(t);
|
|
10877
|
-
|
|
11555
|
+
resolve5(v);
|
|
10878
11556
|
},
|
|
10879
11557
|
() => {
|
|
10880
11558
|
clearTimeout(t);
|
|
10881
|
-
|
|
11559
|
+
resolve5(null);
|
|
10882
11560
|
}
|
|
10883
11561
|
);
|
|
10884
11562
|
});
|
|
@@ -10932,7 +11610,7 @@ async function watchRender(produce, rawInterval, opts = {}) {
|
|
|
10932
11610
|
process.once("SIGINT", () => process.exit(0));
|
|
10933
11611
|
}
|
|
10934
11612
|
const hint = interactive ? "q or Ctrl-C to exit" : "Ctrl-C to exit";
|
|
10935
|
-
const
|
|
11613
|
+
const sleep6 = (d) => new Promise((r) => {
|
|
10936
11614
|
const t = setTimeout(() => {
|
|
10937
11615
|
wake = null;
|
|
10938
11616
|
r();
|
|
@@ -10963,7 +11641,7 @@ async function watchRender(produce, rawInterval, opts = {}) {
|
|
|
10963
11641
|
${trimmed}
|
|
10964
11642
|
`);
|
|
10965
11643
|
}
|
|
10966
|
-
await
|
|
11644
|
+
await sleep6(ms);
|
|
10967
11645
|
if (exiting) {
|
|
10968
11646
|
restore();
|
|
10969
11647
|
process.exit(0);
|
|
@@ -11338,7 +12016,7 @@ import { log as log35 } from "@clack/prompts";
|
|
|
11338
12016
|
import { execa as execa4 } from "execa";
|
|
11339
12017
|
import { existsSync as existsSync9, mkdirSync as mkdirSync7 } from "fs";
|
|
11340
12018
|
import { homedir as homedir17 } from "os";
|
|
11341
|
-
import { join as
|
|
12019
|
+
import { join as join20 } from "path";
|
|
11342
12020
|
import { Command as Command32 } from "commander";
|
|
11343
12021
|
|
|
11344
12022
|
// src/commands/path.ts
|
|
@@ -11400,7 +12078,7 @@ var openCommand = new Command32("open").description("Open a box's /workspace in
|
|
|
11400
12078
|
}
|
|
11401
12079
|
});
|
|
11402
12080
|
async function runCloudOpen(box, provider, opts) {
|
|
11403
|
-
const mountRoot =
|
|
12081
|
+
const mountRoot = join20(homedir17(), ".agentbox", "mounts", box.name);
|
|
11404
12082
|
if (opts.unmount) {
|
|
11405
12083
|
const ok = await tryUnmount(mountRoot);
|
|
11406
12084
|
if (ok) process.stdout.write(`unmounted ${mountRoot}
|
|
@@ -11674,7 +12352,7 @@ async function pruneCloud(provider, opts) {
|
|
|
11674
12352
|
}
|
|
11675
12353
|
|
|
11676
12354
|
// src/commands/queue.ts
|
|
11677
|
-
import { readFile as
|
|
12355
|
+
import { readFile as readFile6, stat as stat6 } from "fs/promises";
|
|
11678
12356
|
import { intro as intro8, log as log37, outro as outro7 } from "@clack/prompts";
|
|
11679
12357
|
import { Command as Command35 } from "commander";
|
|
11680
12358
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
|
|
@@ -11721,8 +12399,8 @@ var queueShowCommand = new Command35("show").description("Dump a job manifest an
|
|
|
11721
12399
|
process.stdout.write(JSON.stringify(job, null, 2) + "\n");
|
|
11722
12400
|
const tailN = Number.parseInt(opts.tail, 10) || 50;
|
|
11723
12401
|
try {
|
|
11724
|
-
await
|
|
11725
|
-
const text = await
|
|
12402
|
+
await stat6(job.logPath);
|
|
12403
|
+
const text = await readFile6(job.logPath, "utf8");
|
|
11726
12404
|
const lines = text.split(/\r?\n/);
|
|
11727
12405
|
const slice = lines.slice(Math.max(0, lines.length - tailN - 1));
|
|
11728
12406
|
process.stdout.write(`
|
|
@@ -11873,11 +12551,11 @@ async function pollUntil(deadline, probe) {
|
|
|
11873
12551
|
if (result !== void 0) return result;
|
|
11874
12552
|
const remaining = deadline - Date.now();
|
|
11875
12553
|
if (remaining <= 0) break;
|
|
11876
|
-
await
|
|
12554
|
+
await sleep5(Math.min(QUEUE_POLL_INTERVAL_MS, remaining));
|
|
11877
12555
|
}
|
|
11878
12556
|
throw new QueueWaitTimeout();
|
|
11879
12557
|
}
|
|
11880
|
-
function
|
|
12558
|
+
function sleep5(ms) {
|
|
11881
12559
|
return new Promise((r) => setTimeout(r, ms));
|
|
11882
12560
|
}
|
|
11883
12561
|
function parsePositiveInt3(raw, label) {
|
|
@@ -11927,7 +12605,8 @@ async function rehydrateFromState() {
|
|
|
11927
12605
|
cloudBackend: b.cloud?.backend,
|
|
11928
12606
|
relayPreviewUrl: b.cloud?.relayPreviewUrl,
|
|
11929
12607
|
relayPreviewToken: b.cloud?.relayPreviewToken,
|
|
11930
|
-
bridgeToken: b.cloud?.bridgeToken
|
|
12608
|
+
bridgeToken: b.cloud?.bridgeToken,
|
|
12609
|
+
autoApproveHostActions: b.autoApproveHostActions
|
|
11931
12610
|
}))
|
|
11932
12611
|
);
|
|
11933
12612
|
}
|
|
@@ -12149,6 +12828,36 @@ async function runDockerJob(job, log47, onBoxCreated) {
|
|
|
12149
12828
|
} else {
|
|
12150
12829
|
throw new Error(`unknown agent kind: ${String(job.agent)}`);
|
|
12151
12830
|
}
|
|
12831
|
+
await maybeOpenQueuedTerminal(job, result.record.name, log47);
|
|
12832
|
+
}
|
|
12833
|
+
function agentBinaryName(agent) {
|
|
12834
|
+
return agent === "claude-code" ? "claude" : agent;
|
|
12835
|
+
}
|
|
12836
|
+
async function maybeOpenQueuedTerminal(job, boxName, log47) {
|
|
12837
|
+
const ctx = job.openTerminal;
|
|
12838
|
+
if (!ctx) return;
|
|
12839
|
+
const cliEntry = process.env["AGENTBOX_CLI_ENTRY"];
|
|
12840
|
+
if (!cliEntry) {
|
|
12841
|
+
log47.write("queue.openIn: AGENTBOX_CLI_ENTRY unset; cannot open terminal");
|
|
12842
|
+
return;
|
|
12843
|
+
}
|
|
12844
|
+
const argv2 = [
|
|
12845
|
+
process.execPath,
|
|
12846
|
+
cliEntry,
|
|
12847
|
+
agentBinaryName(job.agent),
|
|
12848
|
+
"attach",
|
|
12849
|
+
boxName,
|
|
12850
|
+
"--attach-in",
|
|
12851
|
+
"same"
|
|
12852
|
+
];
|
|
12853
|
+
try {
|
|
12854
|
+
const r = await spawnQueuedOpenTerminal(ctx, argv2, boxName);
|
|
12855
|
+
log47.write(
|
|
12856
|
+
r.launched ? `queue.openIn: ${r.note}` : `queue.openIn: open failed: ${r.error ?? ""}`
|
|
12857
|
+
);
|
|
12858
|
+
} catch (err) {
|
|
12859
|
+
log47.write(`queue.openIn: open threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
12860
|
+
}
|
|
12152
12861
|
}
|
|
12153
12862
|
async function runCloudJob(job, log47, onBoxCreated) {
|
|
12154
12863
|
const opts = job.createOpts;
|
|
@@ -12206,6 +12915,7 @@ async function runCloudJob(job, log47, onBoxCreated) {
|
|
|
12206
12915
|
sessionName,
|
|
12207
12916
|
extraArgs
|
|
12208
12917
|
});
|
|
12918
|
+
await maybeOpenQueuedTerminal(job, result.record.name, log47);
|
|
12209
12919
|
}
|
|
12210
12920
|
function buildOverridesFromJob(job) {
|
|
12211
12921
|
const opts = job.createOpts;
|
|
@@ -12757,12 +13467,12 @@ async function renderText(i) {
|
|
|
12757
13467
|
`env files ${i.record.withEnv ? "yes" : "no"}`,
|
|
12758
13468
|
"endpoints",
|
|
12759
13469
|
...renderEndpoints(i),
|
|
12760
|
-
`mem limit ${lim?.memoryBytes ?
|
|
13470
|
+
`mem limit ${lim?.memoryBytes ? fmtBytes2(lim.memoryBytes) : "unlimited"}`,
|
|
12761
13471
|
`cpu limit ${fmtLimit(lim?.cpus, "")}`,
|
|
12762
13472
|
`pids limit ${fmtLimit(lim?.pidsLimit, "")}`,
|
|
12763
13473
|
`disk limit ${lim?.disk ? `${lim.disk} (best-effort; no-op on overlay2/macOS)` : "unlimited"}`,
|
|
12764
13474
|
`snapshot dir ${i.record.snapshotDir ?? "(none)"}`,
|
|
12765
|
-
`snapshot size ${
|
|
13475
|
+
`snapshot size ${fmtBytes2(i.snapshotSizeBytes)}`,
|
|
12766
13476
|
`checkpoint ${renderCheckpoint(i, ckptBytes)}`,
|
|
12767
13477
|
`host export ${i.hostPaths.mergedExport} (run \`agentbox open\` to refresh)`,
|
|
12768
13478
|
`created ${i.record.createdAt}`
|
|
@@ -12772,7 +13482,7 @@ async function renderText(i) {
|
|
|
12772
13482
|
function renderCheckpoint(i, sizeBytes) {
|
|
12773
13483
|
const src = i.record.checkpointSource;
|
|
12774
13484
|
if (!src || !i.record.checkpointImage) return "(none)";
|
|
12775
|
-
const sizePart = sizeBytes !== null ? ` ${
|
|
13485
|
+
const sizePart = sizeBytes !== null ? ` ${fmtBytes2(sizeBytes)}` : "";
|
|
12776
13486
|
return `${src.ref} (${src.type}, chain ${src.chain.length}) \u2192 ${i.record.checkpointImage}${sizePart}`;
|
|
12777
13487
|
}
|
|
12778
13488
|
function renderClaudeSession(i) {
|
|
@@ -12843,7 +13553,7 @@ async function renderCloudText(box) {
|
|
|
12843
13553
|
`bridge token ${box.cloud?.bridgeToken ? "(set)" : "(unset)"}`,
|
|
12844
13554
|
`playwright ${box.withPlaywright ? "yes" : "no"}`,
|
|
12845
13555
|
`env files ${box.withEnv ? "yes" : "no"}`,
|
|
12846
|
-
`mem limit ${lim?.memoryBytes ?
|
|
13556
|
+
`mem limit ${lim?.memoryBytes ? fmtBytes2(lim.memoryBytes) : "unlimited"}`,
|
|
12847
13557
|
`cpu limit ${fmtLimit(lim?.cpus, "")}`,
|
|
12848
13558
|
`pids limit ${fmtLimit(lim?.pidsLimit, "")}`,
|
|
12849
13559
|
`persisted ${persisted ? `${persisted.timestamp} (${String(persisted.services.length)} svc, ${String(persisted.tasks.length)} tasks, ${String(persisted.ports.length)} ports)` : "(none)"}`,
|
|
@@ -13004,20 +13714,20 @@ function renderResources(s) {
|
|
|
13004
13714
|
if (s.live) {
|
|
13005
13715
|
seg.push(`cpu ${fmtPercent(s.cpuPercent)}${lim(s.limits.cpus)}`);
|
|
13006
13716
|
seg.push(
|
|
13007
|
-
`mem ${
|
|
13717
|
+
`mem ${fmtBytes2(s.memUsedBytes)} / ${fmtBytes2(s.memLimitBytes)} (${fmtPercent(s.memPercent)})${lim(s.limits.memoryBytes ? fmtBytes2(s.limits.memoryBytes) : null)}`
|
|
13008
13718
|
);
|
|
13009
13719
|
seg.push(`pids ${s.pids === null ? "\u2014" : String(s.pids)}${lim(s.limits.pidsLimit)}`);
|
|
13010
13720
|
} else {
|
|
13011
13721
|
seg.push("not running");
|
|
13012
|
-
if (s.limits.memoryBytes) seg.push(`mem limit ${
|
|
13722
|
+
if (s.limits.memoryBytes) seg.push(`mem limit ${fmtBytes2(s.limits.memoryBytes)}`);
|
|
13013
13723
|
if (s.limits.cpus) seg.push(`cpu limit ${String(s.limits.cpus)}`);
|
|
13014
13724
|
if (s.limits.pidsLimit) seg.push(`pids limit ${String(s.limits.pidsLimit)}`);
|
|
13015
13725
|
}
|
|
13016
13726
|
seg.push(
|
|
13017
|
-
`disk ${
|
|
13727
|
+
`disk ${fmtBytes2(s.diskUsedBytes)}${s.limits.disk ? ` (limit ${s.limits.disk}, no-op on overlay2/macOS)` : ""}`
|
|
13018
13728
|
);
|
|
13019
|
-
if (s.snapshotDiskBytes !== null) seg.push(`snapshot ${
|
|
13020
|
-
if (s.checkpointVolumeBytes !== null) seg.push(`ckpt ${
|
|
13729
|
+
if (s.snapshotDiskBytes !== null) seg.push(`snapshot ${fmtBytes2(s.snapshotDiskBytes)}`);
|
|
13730
|
+
if (s.checkpointVolumeBytes !== null) seg.push(`ckpt ${fmtBytes2(s.checkpointVolumeBytes)}`);
|
|
13021
13731
|
let line = ` ${seg.join(" ")}`;
|
|
13022
13732
|
for (const w of s.warnings) line += `
|
|
13023
13733
|
note: ${w}`;
|
|
@@ -13114,8 +13824,8 @@ restart with: agentbox start ${box.name}
|
|
|
13114
13824
|
import { Command as Command43 } from "commander";
|
|
13115
13825
|
var COLS = ["BOX", "STATE", "CPU%", "MEM USAGE / LIMIT", "MEM%", "PIDS", "DISK", "NET I/O"];
|
|
13116
13826
|
function row(name, state, s) {
|
|
13117
|
-
const mem = `${
|
|
13118
|
-
const net = s.netRxBytes === null && s.netTxBytes === null ? "\u2014" : `${
|
|
13827
|
+
const mem = `${fmtBytes2(s.memUsedBytes)} / ${fmtBytes2(s.memLimitBytes)}`;
|
|
13828
|
+
const net = s.netRxBytes === null && s.netTxBytes === null ? "\u2014" : `${fmtBytes2(s.netRxBytes)} / ${fmtBytes2(s.netTxBytes)}`;
|
|
13119
13829
|
return [
|
|
13120
13830
|
name,
|
|
13121
13831
|
state,
|
|
@@ -13123,7 +13833,7 @@ function row(name, state, s) {
|
|
|
13123
13833
|
s.live ? mem : "\u2014",
|
|
13124
13834
|
fmtPercent(s.memPercent),
|
|
13125
13835
|
s.pids === null ? "\u2014" : String(s.pids),
|
|
13126
|
-
|
|
13836
|
+
fmtBytes2(s.diskUsedBytes),
|
|
13127
13837
|
s.live ? net : "\u2014"
|
|
13128
13838
|
];
|
|
13129
13839
|
}
|
|
@@ -13179,8 +13889,8 @@ async function renderProjectFooters() {
|
|
|
13179
13889
|
allCheckpointImagesBytes(),
|
|
13180
13890
|
agentboxHomeBytes()
|
|
13181
13891
|
]);
|
|
13182
|
-
if (home !== null) parts.push(`~/.agentbox: ${
|
|
13183
|
-
if (ckpt !== null) parts.push(`checkpoints: ${
|
|
13892
|
+
if (home !== null) parts.push(`~/.agentbox: ${fmtBytes2(home)}`);
|
|
13893
|
+
if (ckpt !== null) parts.push(`checkpoints: ${fmtBytes2(ckpt)}`);
|
|
13184
13894
|
return parts.length > 0 ? `
|
|
13185
13895
|
|
|
13186
13896
|
SYSTEM: ${parts.join(" - ")}` : "";
|