@madarco/agentbox 0.9.0 → 0.10.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 +89 -0
- package/README.md +161 -0
- package/dist/{_cloud-attach-ZXBCNWJX.js → _cloud-attach-O6NYTLES.js} +3 -3
- package/dist/{chunk-BXQMIEHC.js → chunk-2GPORKYF.js} +254 -162
- package/dist/chunk-2GPORKYF.js.map +1 -0
- package/dist/{chunk-NCJP5MTN.js → chunk-7UIAO7PC.js} +213 -51
- package/dist/chunk-7UIAO7PC.js.map +1 -0
- package/dist/{chunk-GU5LW4B5.js → chunk-R4O5WPHW.js} +374 -62
- package/dist/chunk-R4O5WPHW.js.map +1 -0
- package/dist/{dist-GDHP34ZK.js → dist-5FQGYRW5.js} +15 -3
- package/dist/dist-5FQGYRW5.js.map +1 -0
- package/dist/{dist-32EZBYG4.js → dist-BQNX7RQE.js} +12 -2
- package/dist/{dist-XML54CNB.js → dist-PZW3GWWU.js} +30 -5
- package/dist/dist-PZW3GWWU.js.map +1 -0
- package/dist/{dist-CX5CGVEB.js → dist-TMHSUVTP.js} +3 -3
- package/dist/index.js +1773 -526
- package/dist/index.js.map +1 -1
- package/package.json +9 -7
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +9 -8
- package/runtime/docker/packages/ctl/dist/bin.cjs +32 -3
- package/runtime/hetzner/agentbox-setup-skill.md +9 -8
- package/runtime/hetzner/ctl.cjs +32 -3
- package/runtime/relay/bin.cjs +32 -3
- package/runtime/vercel/agentbox-setup-skill.md +9 -8
- package/runtime/vercel/ctl.cjs +32 -3
- package/runtime/vercel/custom-system-CLAUDE.md +1 -4
- package/runtime/vercel/scripts/provision.sh +40 -0
- package/share/agentbox-setup/SKILL.md +9 -8
- package/dist/chunk-BXQMIEHC.js.map +0 -1
- package/dist/chunk-GU5LW4B5.js.map +0 -1
- package/dist/chunk-NCJP5MTN.js.map +0 -1
- package/dist/dist-GDHP34ZK.js.map +0 -1
- package/dist/dist-XML54CNB.js.map +0 -1
- /package/dist/{_cloud-attach-ZXBCNWJX.js.map → _cloud-attach-O6NYTLES.js.map} +0 -0
- /package/dist/{dist-32EZBYG4.js.map → dist-BQNX7RQE.js.map} +0 -0
- /package/dist/{dist-CX5CGVEB.js.map → dist-TMHSUVTP.js.map} +0 -0
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_RELAY_PORT,
|
|
4
4
|
readBoxStatus
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-7UIAO7PC.js";
|
|
6
6
|
|
|
7
7
|
// src/commands/_cloud-attach.ts
|
|
8
8
|
import { spawn as spawn3 } from "child_process";
|
|
9
|
+
import { appendFileSync } from "fs";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { join as join2 } from "path";
|
|
12
|
+
import { spinner } from "@clack/prompts";
|
|
9
13
|
|
|
10
14
|
// src/provider/registry.ts
|
|
11
15
|
var KNOWN = ["docker", "daytona", "hetzner", "vercel"];
|
|
@@ -15,21 +19,21 @@ function isKnownProvider(name) {
|
|
|
15
19
|
async function getProvider(name) {
|
|
16
20
|
switch (name) {
|
|
17
21
|
case "docker": {
|
|
18
|
-
const mod = await import("./dist-
|
|
22
|
+
const mod = await import("./dist-BQNX7RQE.js");
|
|
19
23
|
return mod.dockerProvider;
|
|
20
24
|
}
|
|
21
25
|
case "daytona": {
|
|
22
|
-
const mod = await import("./dist-
|
|
26
|
+
const mod = await import("./dist-TMHSUVTP.js");
|
|
23
27
|
await mod.ensureDaytonaCredentials();
|
|
24
28
|
return mod.daytonaProvider;
|
|
25
29
|
}
|
|
26
30
|
case "hetzner": {
|
|
27
|
-
const mod = await import("./dist-
|
|
31
|
+
const mod = await import("./dist-5FQGYRW5.js");
|
|
28
32
|
await mod.ensureHetznerCredentials();
|
|
29
33
|
return mod.hetznerProvider;
|
|
30
34
|
}
|
|
31
35
|
case "vercel": {
|
|
32
|
-
const mod = await import("./dist-
|
|
36
|
+
const mod = await import("./dist-PZW3GWWU.js");
|
|
33
37
|
await mod.ensureVercelCredentials();
|
|
34
38
|
return mod.vercelProvider;
|
|
35
39
|
}
|
|
@@ -688,9 +692,13 @@ function statusLine(box, w, stateLabel, groups = HINT_GROUPS, fallbackGroups) {
|
|
|
688
692
|
// src/wrapped-pty/footer.ts
|
|
689
693
|
var SPINNER_FRAMES = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
|
|
690
694
|
var URGENT = "\x1B[38;5;220m\x1B[1m";
|
|
695
|
+
var TITLE = "\x1B[1m\x1B[38;5;253m";
|
|
691
696
|
var TXT = "\x1B[38;5;250m";
|
|
692
697
|
var SUBTLE = "\x1B[38;5;245m";
|
|
693
698
|
var RESET = "\x1B[0m";
|
|
699
|
+
var UNDERLINE = "\x1B[4m";
|
|
700
|
+
var NO_UNDERLINE = "\x1B[24m";
|
|
701
|
+
var QUESTION_ACCENT = "\x1B[38;5;51m\x1B[1m";
|
|
694
702
|
var NOTICE_BG = "\x1B[48;5;220m";
|
|
695
703
|
var NOTICE_FG = "\x1B[38;5;16m\x1B[1m";
|
|
696
704
|
var FLASH_FG = "\x1B[38;5;150m\x1B[1m";
|
|
@@ -723,6 +731,17 @@ function padTo(visible, width) {
|
|
|
723
731
|
}
|
|
724
732
|
return visible + " ".repeat(width - visible.length);
|
|
725
733
|
}
|
|
734
|
+
function answerChip(defaultAnswer) {
|
|
735
|
+
const yesKey = "y Yes";
|
|
736
|
+
const noKey = "n No";
|
|
737
|
+
const sep = " \xB7 ";
|
|
738
|
+
const yesIsDefault = defaultAnswer === "y";
|
|
739
|
+
const yes = yesIsDefault ? `${UNDERLINE}${yesKey}${NO_UNDERLINE}` : yesKey;
|
|
740
|
+
const no = yesIsDefault ? noKey : `${UNDERLINE}${noKey}${NO_UNDERLINE}`;
|
|
741
|
+
const ansi = `${NOTICE_BG}${NOTICE_FG} ${yes}${sep}${no} ${RESET}`;
|
|
742
|
+
const width = ` ${yesKey}${sep}${noKey} `.length;
|
|
743
|
+
return { ansi, width };
|
|
744
|
+
}
|
|
726
745
|
function renderFooter(state, cols) {
|
|
727
746
|
if (cols <= 0) return "";
|
|
728
747
|
if (state.kind === "idle") {
|
|
@@ -753,18 +772,16 @@ function renderFooter(state, cols) {
|
|
|
753
772
|
return `${BAR_BG}${FLASH_FG}${prefix}${TXT}${message2}${RESET}`;
|
|
754
773
|
}
|
|
755
774
|
if (state.kind === "notice") {
|
|
756
|
-
const
|
|
757
|
-
const prefix = ` ${
|
|
775
|
+
const spinner2 = SPINNER_FRAMES[state.frame % SPINNER_FRAMES.length];
|
|
776
|
+
const prefix = ` ${spinner2} `;
|
|
758
777
|
const inner2 = Math.max(0, cols - prefix.length);
|
|
759
778
|
const message2 = padTo(state.message, inner2);
|
|
760
779
|
return `${NOTICE_BG}${NOTICE_FG}${prefix}${message2}${RESET}`;
|
|
761
780
|
}
|
|
762
|
-
const
|
|
763
|
-
const yn = def === "y" ? "[Y/n]" : "[y/N]";
|
|
781
|
+
const chip = answerChip(state.prompt.defaultAnswer);
|
|
764
782
|
const tag = " [!] ";
|
|
765
783
|
const sep = " ";
|
|
766
|
-
const
|
|
767
|
-
const inner = Math.max(0, cols - tag.length - hintW);
|
|
784
|
+
const inner = Math.max(0, cols - tag.length - chip.width);
|
|
768
785
|
const detailRaw = state.prompt.detail ?? "";
|
|
769
786
|
let message = state.prompt.message;
|
|
770
787
|
let detail = detailRaw;
|
|
@@ -779,7 +796,7 @@ function renderFooter(state, cols) {
|
|
|
779
796
|
}
|
|
780
797
|
const middlePlain = detail.length > 0 ? `${message}${sep}${detail}` : message;
|
|
781
798
|
const padded = padTo(middlePlain, inner);
|
|
782
|
-
return `${BAR_BG}${URGENT}${tag}${TXT}${padded}${
|
|
799
|
+
return `${BAR_BG}${URGENT}${tag}${TXT}${padded}${RESET}${chip.ansi}`;
|
|
783
800
|
}
|
|
784
801
|
function cursorMoveTo(row, col) {
|
|
785
802
|
return `\x1B[${String(row)};${String(col)}H`;
|
|
@@ -788,6 +805,73 @@ var CURSOR_SAVE = "\x1B7";
|
|
|
788
805
|
var CURSOR_RESTORE = "\x1B8";
|
|
789
806
|
var SYNC_BEGIN = "\x1B[?2026h";
|
|
790
807
|
var SYNC_END = "\x1B[?2026l";
|
|
808
|
+
var ALERT_BAND_ROWS = 3;
|
|
809
|
+
function blankBar(cols, bg) {
|
|
810
|
+
return `${bg}${" ".repeat(Math.max(0, cols))}${RESET}`;
|
|
811
|
+
}
|
|
812
|
+
function renderPromptBand(prompt, cols, rows) {
|
|
813
|
+
const tag = " [!] ";
|
|
814
|
+
const indent = " ".repeat(tag.length);
|
|
815
|
+
const contW = Math.max(0, cols - indent.length);
|
|
816
|
+
const chip = answerChip(prompt.defaultAnswer);
|
|
817
|
+
const title = (prompt.context?.command ?? "confirm").toUpperCase();
|
|
818
|
+
const titleW = Math.max(0, cols - tag.length - chip.width);
|
|
819
|
+
const titlePadded = padTo(title, titleW);
|
|
820
|
+
const line1 = `${BAR_BG}${URGENT}${tag}${TITLE}${titlePadded}${RESET}${chip.ansi}`;
|
|
821
|
+
const line2 = `${BAR_BG}${TXT}${indent}${padTo(prompt.message, contW)}${RESET}`;
|
|
822
|
+
const line3 = `${BAR_BG}${SUBTLE}${indent}${padTo(prompt.detail ?? "", contW)}${RESET}`;
|
|
823
|
+
return [line1, line2, line3].slice(0, rows);
|
|
824
|
+
}
|
|
825
|
+
function renderNoticeBand(message, frame, cols, rows) {
|
|
826
|
+
const spinner2 = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
|
|
827
|
+
const prefix = ` ${spinner2} `;
|
|
828
|
+
const indent = " ".repeat(prefix.length);
|
|
829
|
+
const firstW = Math.max(0, cols - prefix.length);
|
|
830
|
+
const contW = Math.max(0, cols - indent.length);
|
|
831
|
+
const out = [];
|
|
832
|
+
let i = 0;
|
|
833
|
+
for (let r = 0; r < rows; r++) {
|
|
834
|
+
const isLast = r === rows - 1;
|
|
835
|
+
const w = r === 0 ? firstW : contW;
|
|
836
|
+
let cell;
|
|
837
|
+
if (i >= message.length) {
|
|
838
|
+
cell = " ".repeat(w);
|
|
839
|
+
} else if (isLast) {
|
|
840
|
+
cell = padTo(message.slice(i), w);
|
|
841
|
+
i = message.length;
|
|
842
|
+
} else {
|
|
843
|
+
cell = message.slice(i, i + w).padEnd(w);
|
|
844
|
+
i += w;
|
|
845
|
+
}
|
|
846
|
+
const lead = r === 0 ? prefix : indent;
|
|
847
|
+
out.push(`${NOTICE_BG}${NOTICE_FG}${lead}${cell}${RESET}`);
|
|
848
|
+
}
|
|
849
|
+
return out;
|
|
850
|
+
}
|
|
851
|
+
function renderQuestionBand(payload, cols, rows) {
|
|
852
|
+
const q = payload.questions[0];
|
|
853
|
+
if (!q) return Array.from({ length: rows }, () => blankBar(cols, BAR_BG));
|
|
854
|
+
const tag = " [?] ";
|
|
855
|
+
const indent = " ".repeat(tag.length);
|
|
856
|
+
const innerW = Math.max(0, cols - tag.length);
|
|
857
|
+
const contW = Math.max(0, cols - indent.length);
|
|
858
|
+
const header = q.header && q.header.trim().length > 0 ? q.header : "Question";
|
|
859
|
+
const headerPadded = padTo(header, innerW);
|
|
860
|
+
const line1 = `${BAR_BG}${QUESTION_ACCENT}${tag}${TXT}${headerPadded}${RESET}`;
|
|
861
|
+
const questionText = padTo(q.question, contW);
|
|
862
|
+
const line2 = `${BAR_BG}${TXT}${indent}${questionText}${RESET}`;
|
|
863
|
+
const optLabels = q.options.map((o) => o.label).join(" \xB7 ");
|
|
864
|
+
const optsLine = optLabels.length > 0 ? `options: ${optLabels}` : "";
|
|
865
|
+
const optsPadded = padTo(optsLine, contW);
|
|
866
|
+
const line3 = `${BAR_BG}${SUBTLE}${indent}${optsPadded}${RESET}`;
|
|
867
|
+
return [line1, line2, line3].slice(0, rows);
|
|
868
|
+
}
|
|
869
|
+
function renderAlertBand(state, cols, rows = ALERT_BAND_ROWS) {
|
|
870
|
+
if (cols <= 0 || rows <= 0) return Array.from({ length: Math.max(0, rows) }, () => "");
|
|
871
|
+
if (state.kind === "prompt") return renderPromptBand(state.prompt, cols, rows);
|
|
872
|
+
if (state.kind === "notice") return renderNoticeBand(state.message, state.frame, cols, rows);
|
|
873
|
+
return renderQuestionBand(state.question, cols, rows);
|
|
874
|
+
}
|
|
791
875
|
|
|
792
876
|
// src/wrapped-pty/prompt-client.ts
|
|
793
877
|
import { request as httpRequest } from "http";
|
|
@@ -956,9 +1040,13 @@ function postAnswer(opts) {
|
|
|
956
1040
|
|
|
957
1041
|
// src/wrapped-pty/run.ts
|
|
958
1042
|
var FOOTER_ROWS = 1;
|
|
1043
|
+
var MIN_INNER_ROWS = 5;
|
|
959
1044
|
var STATUS_POLL_INTERVAL_MS = 3e3;
|
|
960
1045
|
var SPINNER_INTERVAL_MS = 120;
|
|
961
1046
|
var FLASH_DURATION_MS = 2e3;
|
|
1047
|
+
var RAPID_RECONNECT_MS = 8e3;
|
|
1048
|
+
var MAX_RAPID_RECONNECTS = 3;
|
|
1049
|
+
var CHECKPOINT_DROP_GRACE_MS = 4e3;
|
|
962
1050
|
var ACTION_FLASH = {
|
|
963
1051
|
screen: "Opening noVNC viewer\u2026",
|
|
964
1052
|
code: "Launching VS Code / Cursor\u2026",
|
|
@@ -1011,12 +1099,19 @@ async function runWrappedAttach(opts) {
|
|
|
1011
1099
|
const cols = process.stdout.columns ?? 80;
|
|
1012
1100
|
const rows = process.stdout.rows ?? 24;
|
|
1013
1101
|
const innerRows = Math.max(1, rows - FOOTER_ROWS);
|
|
1014
|
-
|
|
1102
|
+
let pty = backend.ptySpawn(command, opts.dockerArgv, {
|
|
1015
1103
|
name: "xterm-256color",
|
|
1016
1104
|
cols,
|
|
1017
1105
|
rows: innerRows,
|
|
1018
1106
|
env: opts.env ? { ...process.env, ...opts.env } : process.env
|
|
1019
1107
|
});
|
|
1108
|
+
let lastSpawnAt = Date.now();
|
|
1109
|
+
const resizePty = (c, r) => {
|
|
1110
|
+
try {
|
|
1111
|
+
pty.resize(c, r);
|
|
1112
|
+
} catch {
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1020
1115
|
pushTerminalTitle();
|
|
1021
1116
|
let lastEmittedTitle = opts.boxName;
|
|
1022
1117
|
setTerminalTitle(lastEmittedTitle);
|
|
@@ -1036,21 +1131,46 @@ async function runWrappedAttach(opts) {
|
|
|
1036
1131
|
let lastActivity;
|
|
1037
1132
|
let capturingPrompt = null;
|
|
1038
1133
|
let activeNotice = null;
|
|
1134
|
+
let reconnectBanner = null;
|
|
1039
1135
|
let noticeFrame = 0;
|
|
1136
|
+
let questionPayload = null;
|
|
1137
|
+
let bandState = null;
|
|
1138
|
+
let bandReservedRows = 0;
|
|
1040
1139
|
let spinnerTimer = null;
|
|
1041
1140
|
let flashMessage = null;
|
|
1042
1141
|
let flashTimer = null;
|
|
1043
|
-
|
|
1142
|
+
let reconnecting = false;
|
|
1143
|
+
let reconnectAbort = null;
|
|
1144
|
+
let userDetached = false;
|
|
1145
|
+
let checkpointNoticeAt = 0;
|
|
1146
|
+
let checkpointNoticeClearedAt = 0;
|
|
1147
|
+
const reservedRows = () => FOOTER_ROWS + bandReservedRows;
|
|
1148
|
+
const bandFits = () => {
|
|
1149
|
+
const rs = process.stdout.rows ?? rows;
|
|
1150
|
+
return rs - FOOTER_ROWS - ALERT_BAND_ROWS >= MIN_INNER_ROWS;
|
|
1151
|
+
};
|
|
1152
|
+
const redrawChrome = () => {
|
|
1044
1153
|
const cs = process.stdout.columns ?? cols;
|
|
1045
1154
|
const rs = process.stdout.rows ?? rows;
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1155
|
+
const footerLine = renderFooter(footerState, cs);
|
|
1156
|
+
let payload = SYNC_BEGIN + CURSOR_SAVE;
|
|
1157
|
+
if (bandReservedRows > 0 && bandState) {
|
|
1158
|
+
const bandLines = renderAlertBand(bandState, cs, bandReservedRows);
|
|
1159
|
+
for (let i = 0; i < bandLines.length; i++) {
|
|
1160
|
+
const row = rs - FOOTER_ROWS - (bandLines.length - i);
|
|
1161
|
+
payload += cursorMoveTo(row + 1, 1) + bandLines[i];
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
payload += cursorMoveTo(rs, 1) + footerLine + CURSOR_RESTORE + SYNC_END;
|
|
1048
1165
|
process.stdout.write(payload);
|
|
1049
1166
|
};
|
|
1050
1167
|
const recomputeFooter = () => {
|
|
1051
|
-
|
|
1168
|
+
const collapsed = bandState !== null && bandReservedRows === 0;
|
|
1169
|
+
if (collapsed && reconnectBanner) {
|
|
1170
|
+
footerState = { kind: "notice", message: reconnectBanner, frame: noticeFrame };
|
|
1171
|
+
} else if (collapsed && capturingPrompt) {
|
|
1052
1172
|
footerState = { kind: "prompt", prompt: capturingPrompt };
|
|
1053
|
-
} else if (activeNotice) {
|
|
1173
|
+
} else if (collapsed && activeNotice) {
|
|
1054
1174
|
footerState = { kind: "notice", message: activeNotice.message, frame: noticeFrame };
|
|
1055
1175
|
} else if (flashMessage) {
|
|
1056
1176
|
footerState = { kind: "flash", message: flashMessage };
|
|
@@ -1058,13 +1178,48 @@ async function runWrappedAttach(opts) {
|
|
|
1058
1178
|
footerState = buildIdle(lastSessionTitle, lastActivity);
|
|
1059
1179
|
}
|
|
1060
1180
|
};
|
|
1181
|
+
const recomputeBand = () => {
|
|
1182
|
+
if (reconnectBanner) {
|
|
1183
|
+
bandState = { kind: "notice", message: reconnectBanner, frame: noticeFrame };
|
|
1184
|
+
} else if (capturingPrompt) {
|
|
1185
|
+
bandState = { kind: "prompt", prompt: capturingPrompt };
|
|
1186
|
+
} else if (activeNotice) {
|
|
1187
|
+
bandState = { kind: "notice", message: activeNotice.message, frame: noticeFrame };
|
|
1188
|
+
} else if (questionPayload) {
|
|
1189
|
+
bandState = { kind: "question", question: questionPayload };
|
|
1190
|
+
} else {
|
|
1191
|
+
bandState = null;
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
const relayoutForBand = () => {
|
|
1195
|
+
const cs = process.stdout.columns ?? cols;
|
|
1196
|
+
const rs = process.stdout.rows ?? rows;
|
|
1197
|
+
const inner = Math.max(1, rs - reservedRows());
|
|
1198
|
+
resizePty(cs, inner);
|
|
1199
|
+
process.stdout.write(`\x1B[1;${String(inner)}r`);
|
|
1200
|
+
let clear = SYNC_BEGIN + CURSOR_SAVE;
|
|
1201
|
+
for (let r = inner + 1; r <= rs; r++) clear += cursorMoveTo(r, 1) + "\x1B[2K";
|
|
1202
|
+
clear += CURSOR_RESTORE + SYNC_END;
|
|
1203
|
+
process.stdout.write(clear);
|
|
1204
|
+
};
|
|
1205
|
+
const applyBandChange = () => {
|
|
1206
|
+
recomputeBand();
|
|
1207
|
+
const wantRows = bandState && bandFits() ? ALERT_BAND_ROWS : 0;
|
|
1208
|
+
if (wantRows !== bandReservedRows) {
|
|
1209
|
+
bandReservedRows = wantRows;
|
|
1210
|
+
relayoutForBand();
|
|
1211
|
+
}
|
|
1212
|
+
recomputeFooter();
|
|
1213
|
+
redrawChrome();
|
|
1214
|
+
};
|
|
1061
1215
|
const startSpinner = () => {
|
|
1062
1216
|
if (spinnerTimer) return;
|
|
1063
1217
|
spinnerTimer = setInterval(() => {
|
|
1064
1218
|
noticeFrame++;
|
|
1065
|
-
if (
|
|
1066
|
-
|
|
1067
|
-
|
|
1219
|
+
if (bandState?.kind === "notice") {
|
|
1220
|
+
bandState = { kind: "notice", message: bandState.message, frame: noticeFrame };
|
|
1221
|
+
if (bandReservedRows === 0) recomputeFooter();
|
|
1222
|
+
redrawChrome();
|
|
1068
1223
|
}
|
|
1069
1224
|
}, SPINNER_INTERVAL_MS);
|
|
1070
1225
|
if (typeof spinnerTimer.unref === "function") spinnerTimer.unref();
|
|
@@ -1075,14 +1230,20 @@ async function runWrappedAttach(opts) {
|
|
|
1075
1230
|
spinnerTimer = null;
|
|
1076
1231
|
}
|
|
1077
1232
|
};
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1233
|
+
const wireOutput = () => {
|
|
1234
|
+
pty.onData((d) => {
|
|
1235
|
+
process.stdout.write(d);
|
|
1236
|
+
redrawChrome();
|
|
1237
|
+
});
|
|
1238
|
+
};
|
|
1239
|
+
wireOutput();
|
|
1082
1240
|
const leaderChords = detachable ? { c: "code", s: "screen", u: "url", d: "detach" } : { c: "code", s: "screen", u: "url" };
|
|
1083
1241
|
const runAction = (name) => {
|
|
1084
1242
|
if (name === "detach") {
|
|
1085
|
-
|
|
1243
|
+
if (!reconnecting) {
|
|
1244
|
+
userDetached = true;
|
|
1245
|
+
pty.write("d");
|
|
1246
|
+
}
|
|
1086
1247
|
return;
|
|
1087
1248
|
}
|
|
1088
1249
|
const cliEntry = process.argv[1];
|
|
@@ -1104,11 +1265,11 @@ async function runWrappedAttach(opts) {
|
|
|
1104
1265
|
flashTimer = null;
|
|
1105
1266
|
flashMessage = null;
|
|
1106
1267
|
recomputeFooter();
|
|
1107
|
-
|
|
1268
|
+
redrawChrome();
|
|
1108
1269
|
}, FLASH_DURATION_MS);
|
|
1109
1270
|
if (typeof flashTimer.unref === "function") flashTimer.unref();
|
|
1110
1271
|
recomputeFooter();
|
|
1111
|
-
|
|
1272
|
+
redrawChrome();
|
|
1112
1273
|
};
|
|
1113
1274
|
const handlePasteImage = async () => {
|
|
1114
1275
|
if (!opts.onPasteImage) return;
|
|
@@ -1118,7 +1279,7 @@ async function runWrappedAttach(opts) {
|
|
|
1118
1279
|
}
|
|
1119
1280
|
flashMessage = "Pasting image\u2026";
|
|
1120
1281
|
recomputeFooter();
|
|
1121
|
-
|
|
1282
|
+
redrawChrome();
|
|
1122
1283
|
let result = "error";
|
|
1123
1284
|
try {
|
|
1124
1285
|
result = await opts.onPasteImage();
|
|
@@ -1130,27 +1291,30 @@ async function runWrappedAttach(opts) {
|
|
|
1130
1291
|
flashTimer = null;
|
|
1131
1292
|
flashMessage = null;
|
|
1132
1293
|
recomputeFooter();
|
|
1133
|
-
|
|
1294
|
+
redrawChrome();
|
|
1134
1295
|
}, FLASH_DURATION_MS);
|
|
1135
1296
|
if (typeof flashTimer.unref === "function") flashTimer.unref();
|
|
1136
1297
|
recomputeFooter();
|
|
1137
|
-
|
|
1298
|
+
redrawChrome();
|
|
1138
1299
|
};
|
|
1139
1300
|
const router = createInputRouter({
|
|
1140
1301
|
onForward: (b) => {
|
|
1302
|
+
if (reconnecting) {
|
|
1303
|
+
if (b.length === 1 && b[0] === 3) reconnectAbort?.abort();
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1141
1306
|
pty.write(b.toString("utf8"));
|
|
1142
1307
|
},
|
|
1143
1308
|
onAnswer: (body) => {
|
|
1144
1309
|
void postAnswer({ relayBaseUrl: opts.relayBaseUrl, body });
|
|
1145
1310
|
capturingPrompt = null;
|
|
1146
|
-
|
|
1147
|
-
redrawFooter();
|
|
1311
|
+
applyBandChange();
|
|
1148
1312
|
},
|
|
1149
1313
|
leaderChords,
|
|
1150
1314
|
onLeaderChange: (open) => {
|
|
1151
1315
|
leaderActive = open;
|
|
1152
1316
|
recomputeFooter();
|
|
1153
|
-
|
|
1317
|
+
redrawChrome();
|
|
1154
1318
|
},
|
|
1155
1319
|
onAction: (name) => {
|
|
1156
1320
|
runAction(name);
|
|
@@ -1166,10 +1330,12 @@ async function runWrappedAttach(opts) {
|
|
|
1166
1330
|
const onResize = () => {
|
|
1167
1331
|
const cs = process.stdout.columns ?? cols;
|
|
1168
1332
|
const rs = process.stdout.rows ?? rows;
|
|
1169
|
-
|
|
1170
|
-
|
|
1333
|
+
bandReservedRows = bandState && bandFits() ? ALERT_BAND_ROWS : 0;
|
|
1334
|
+
const inner = Math.max(1, rs - reservedRows());
|
|
1335
|
+
resizePty(cs, inner);
|
|
1171
1336
|
process.stdout.write(`\x1B[1;${String(inner)}r`);
|
|
1172
|
-
|
|
1337
|
+
recomputeFooter();
|
|
1338
|
+
redrawChrome();
|
|
1173
1339
|
};
|
|
1174
1340
|
process.stdout.on("resize", onResize);
|
|
1175
1341
|
const stream = subscribePrompts({
|
|
@@ -1177,8 +1343,7 @@ async function runWrappedAttach(opts) {
|
|
|
1177
1343
|
boxId: opts.boxId,
|
|
1178
1344
|
onPrompt: (ev) => {
|
|
1179
1345
|
capturingPrompt = ev;
|
|
1180
|
-
|
|
1181
|
-
redrawFooter();
|
|
1346
|
+
applyBandChange();
|
|
1182
1347
|
router.capture(ev).catch((e) => {
|
|
1183
1348
|
const msg = e instanceof Error ? e.message : String(e);
|
|
1184
1349
|
if (msg !== "resolved-elsewhere") {
|
|
@@ -1190,22 +1355,21 @@ async function runWrappedAttach(opts) {
|
|
|
1190
1355
|
if (capturingPrompt && capturingPrompt.id === id) {
|
|
1191
1356
|
capturingPrompt = null;
|
|
1192
1357
|
router.abort("resolved-elsewhere");
|
|
1193
|
-
|
|
1194
|
-
redrawFooter();
|
|
1358
|
+
applyBandChange();
|
|
1195
1359
|
}
|
|
1196
1360
|
},
|
|
1197
1361
|
onNotice: (ev) => {
|
|
1362
|
+
if (ev.kind === "checkpoint") checkpointNoticeAt = Date.now();
|
|
1198
1363
|
activeNotice = ev;
|
|
1199
1364
|
startSpinner();
|
|
1200
|
-
|
|
1201
|
-
redrawFooter();
|
|
1365
|
+
applyBandChange();
|
|
1202
1366
|
},
|
|
1203
1367
|
onNoticeCleared: (id) => {
|
|
1204
1368
|
if (activeNotice && activeNotice.id === id) {
|
|
1369
|
+
if (activeNotice.kind === "checkpoint") checkpointNoticeClearedAt = Date.now();
|
|
1205
1370
|
activeNotice = null;
|
|
1206
1371
|
stopSpinner();
|
|
1207
|
-
|
|
1208
|
-
redrawFooter();
|
|
1372
|
+
applyBandChange();
|
|
1209
1373
|
}
|
|
1210
1374
|
}
|
|
1211
1375
|
});
|
|
@@ -1224,12 +1388,18 @@ async function runWrappedAttach(opts) {
|
|
|
1224
1388
|
lastEmittedTitle = desiredTitle;
|
|
1225
1389
|
setTerminalTitle(desiredTitle);
|
|
1226
1390
|
}
|
|
1391
|
+
const nextQuestion = opts.mode === "claude" && status?.claude.state === "question" ? status.claude.question ?? null : null;
|
|
1392
|
+
const questionChanged = (nextQuestion?.capturedAt ?? null) !== (questionPayload?.capturedAt ?? null);
|
|
1393
|
+
if (questionChanged) {
|
|
1394
|
+
questionPayload = nextQuestion;
|
|
1395
|
+
applyBandChange();
|
|
1396
|
+
}
|
|
1227
1397
|
if (nextTitle === lastSessionTitle && nextActivity === lastActivity) return;
|
|
1228
1398
|
lastSessionTitle = nextTitle;
|
|
1229
1399
|
lastActivity = nextActivity;
|
|
1230
1400
|
if (footerState.kind === "idle") {
|
|
1231
1401
|
recomputeFooter();
|
|
1232
|
-
|
|
1402
|
+
redrawChrome();
|
|
1233
1403
|
}
|
|
1234
1404
|
} catch (e) {
|
|
1235
1405
|
logErr(`status poll failed: ${e.message}`);
|
|
@@ -1244,10 +1414,82 @@ async function runWrappedAttach(opts) {
|
|
|
1244
1414
|
if (opts.mode === "shell" && !detachable) {
|
|
1245
1415
|
process.stdout.write("\x1B[H\x1B[2J");
|
|
1246
1416
|
}
|
|
1247
|
-
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1250
|
-
|
|
1417
|
+
redrawChrome();
|
|
1418
|
+
const reconnectFlow = async (code) => {
|
|
1419
|
+
const controller = new AbortController();
|
|
1420
|
+
reconnecting = true;
|
|
1421
|
+
reconnectAbort = controller;
|
|
1422
|
+
reconnectBanner = "box rebooting \u2014 reconnecting\u2026";
|
|
1423
|
+
startSpinner();
|
|
1424
|
+
applyBandChange();
|
|
1425
|
+
let spec = null;
|
|
1426
|
+
try {
|
|
1427
|
+
spec = await opts.reconnect?.(controller.signal, code) ?? null;
|
|
1428
|
+
} catch (e) {
|
|
1429
|
+
logErr(`reconnect failed: ${e.message}`);
|
|
1430
|
+
} finally {
|
|
1431
|
+
reconnecting = false;
|
|
1432
|
+
reconnectAbort = null;
|
|
1433
|
+
reconnectBanner = null;
|
|
1434
|
+
if (!activeNotice) stopSpinner();
|
|
1435
|
+
applyBandChange();
|
|
1436
|
+
}
|
|
1437
|
+
if (spec) {
|
|
1438
|
+
flashMessage = "reconnected";
|
|
1439
|
+
if (flashTimer) clearTimeout(flashTimer);
|
|
1440
|
+
flashTimer = setTimeout(() => {
|
|
1441
|
+
flashTimer = null;
|
|
1442
|
+
flashMessage = null;
|
|
1443
|
+
recomputeFooter();
|
|
1444
|
+
redrawChrome();
|
|
1445
|
+
}, FLASH_DURATION_MS);
|
|
1446
|
+
if (typeof flashTimer.unref === "function") flashTimer.unref();
|
|
1447
|
+
recomputeFooter();
|
|
1448
|
+
redrawChrome();
|
|
1449
|
+
}
|
|
1450
|
+
return spec;
|
|
1451
|
+
};
|
|
1452
|
+
let exitCode = 0;
|
|
1453
|
+
let rapidFails = 0;
|
|
1454
|
+
for (; ; ) {
|
|
1455
|
+
const code = await new Promise((resolve) => {
|
|
1456
|
+
pty.onExit(({ exitCode: exitCode2 }) => resolve(exitCode2));
|
|
1457
|
+
});
|
|
1458
|
+
if (userDetached || !opts.reconnect) {
|
|
1459
|
+
exitCode = code;
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
const checkpointing = checkpointNoticeAt > checkpointNoticeClearedAt || Date.now() - checkpointNoticeClearedAt < CHECKPOINT_DROP_GRACE_MS;
|
|
1463
|
+
if (!checkpointing && code === 0) {
|
|
1464
|
+
exitCode = code;
|
|
1465
|
+
break;
|
|
1466
|
+
}
|
|
1467
|
+
rapidFails = Date.now() - lastSpawnAt < RAPID_RECONNECT_MS ? rapidFails + 1 : 0;
|
|
1468
|
+
if (rapidFails >= MAX_RAPID_RECONNECTS) {
|
|
1469
|
+
logErr("giving up reconnect after repeated rapid failures");
|
|
1470
|
+
exitCode = code;
|
|
1471
|
+
break;
|
|
1472
|
+
}
|
|
1473
|
+
const next = await reconnectFlow(code);
|
|
1474
|
+
if (!next) {
|
|
1475
|
+
exitCode = code;
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
const rsNow = process.stdout.rows ?? rows;
|
|
1479
|
+
const innerNow = Math.max(1, rsNow - reservedRows());
|
|
1480
|
+
pty = backend.ptySpawn(next.command, next.argv, {
|
|
1481
|
+
name: "xterm-256color",
|
|
1482
|
+
cols: process.stdout.columns ?? cols,
|
|
1483
|
+
rows: innerNow,
|
|
1484
|
+
env: next.env ? { ...process.env, ...next.env } : process.env
|
|
1485
|
+
});
|
|
1486
|
+
wireOutput();
|
|
1487
|
+
lastSpawnAt = Date.now();
|
|
1488
|
+
checkpointNoticeAt = 0;
|
|
1489
|
+
checkpointNoticeClearedAt = 0;
|
|
1490
|
+
process.stdout.write(`\x1B[1;${String(innerNow)}r`);
|
|
1491
|
+
redrawChrome();
|
|
1492
|
+
}
|
|
1251
1493
|
process.stdin.off("data", onStdinData);
|
|
1252
1494
|
process.stdout.off("resize", onResize);
|
|
1253
1495
|
clearInterval(statusTimer);
|
|
@@ -1259,9 +1501,12 @@ async function runWrappedAttach(opts) {
|
|
|
1259
1501
|
router.dispose();
|
|
1260
1502
|
const rsFinal = process.stdout.rows ?? rows;
|
|
1261
1503
|
const csFinal = process.stdout.columns ?? cols;
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1504
|
+
let teardownPaint = "\x1B[r";
|
|
1505
|
+
for (let r = rsFinal - bandReservedRows; r <= rsFinal; r++) {
|
|
1506
|
+
if (r >= 1) teardownPaint += cursorMoveTo(r, 1) + "\x1B[2K";
|
|
1507
|
+
}
|
|
1508
|
+
teardownPaint += cursorMoveTo(rsFinal, csFinal);
|
|
1509
|
+
process.stdout.write(teardownPaint);
|
|
1265
1510
|
popTerminalTitle();
|
|
1266
1511
|
if (exitCode === 0 && opts.detachNotice) {
|
|
1267
1512
|
process.stdout.write("\x1B[1A\x1B[2K\r" + opts.detachNotice + "\n");
|
|
@@ -1423,6 +1668,24 @@ async function pasteHostClipboardImage(provider, box) {
|
|
|
1423
1668
|
|
|
1424
1669
|
// src/commands/_cloud-attach.ts
|
|
1425
1670
|
var RELAY_HOST_URL = `http://127.0.0.1:${String(DEFAULT_RELAY_PORT)}`;
|
|
1671
|
+
var RECONNECT_TIMEOUT_MS = 5 * 6e4;
|
|
1672
|
+
function abortableSleep(ms, signal) {
|
|
1673
|
+
return new Promise((resolve) => {
|
|
1674
|
+
if (signal.aborted) {
|
|
1675
|
+
resolve();
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
const t = setTimeout(resolve, ms);
|
|
1679
|
+
signal.addEventListener(
|
|
1680
|
+
"abort",
|
|
1681
|
+
() => {
|
|
1682
|
+
clearTimeout(t);
|
|
1683
|
+
resolve();
|
|
1684
|
+
},
|
|
1685
|
+
{ once: true }
|
|
1686
|
+
);
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1426
1689
|
function buildCloudAttachInnerCommand(binary, extraArgs) {
|
|
1427
1690
|
if (!extraArgs || extraArgs.length === 0) {
|
|
1428
1691
|
return `bash -lc exec\\ ${binary}`;
|
|
@@ -1435,10 +1698,22 @@ async function cloudAgentAttach(args) {
|
|
|
1435
1698
|
if (!provider.buildAttach) {
|
|
1436
1699
|
throw new Error(`provider '${provider.name}' does not support interactive attach`);
|
|
1437
1700
|
}
|
|
1701
|
+
const buildAttach = provider.buildAttach.bind(provider);
|
|
1702
|
+
let box = args.box;
|
|
1703
|
+
const state = await provider.probeState(box);
|
|
1704
|
+
if (state === "missing") {
|
|
1705
|
+
throw new Error(`cloud sandbox for ${box.name} is missing; was it destroyed?`);
|
|
1706
|
+
}
|
|
1707
|
+
if (state !== "running") {
|
|
1708
|
+
const s = spinner();
|
|
1709
|
+
s.start(state === "paused" ? "resuming box" : "starting box");
|
|
1710
|
+
box = await provider.start(box);
|
|
1711
|
+
s.stop("box running");
|
|
1712
|
+
}
|
|
1438
1713
|
const command = buildCloudAttachInnerCommand(args.binary, args.extraArgs);
|
|
1439
|
-
const safeOpenIn =
|
|
1714
|
+
const safeOpenIn = box.provider === "daytona" ? "same" : args.openIn;
|
|
1440
1715
|
if (safeOpenIn && safeOpenIn !== "same" && args.extraArgs && args.extraArgs.length > 0) {
|
|
1441
|
-
const pre = await provider.buildAttach(
|
|
1716
|
+
const pre = await provider.buildAttach(box, "agent", {
|
|
1442
1717
|
sessionName: args.sessionName,
|
|
1443
1718
|
command,
|
|
1444
1719
|
detached: true
|
|
@@ -1449,25 +1724,60 @@ async function cloudAgentAttach(args) {
|
|
|
1449
1724
|
if (pre.cleanup) await pre.cleanup();
|
|
1450
1725
|
}
|
|
1451
1726
|
}
|
|
1452
|
-
|
|
1727
|
+
let spec = await provider.buildAttach(box, "agent", {
|
|
1453
1728
|
sessionName: args.sessionName,
|
|
1454
1729
|
command
|
|
1455
1730
|
});
|
|
1456
1731
|
const canPaste = args.mode === "claude" && await clipboardCaptureAvailable();
|
|
1732
|
+
const reconnect = async (signal) => {
|
|
1733
|
+
const deadline = Date.now() + RECONNECT_TIMEOUT_MS;
|
|
1734
|
+
let backoff = 500;
|
|
1735
|
+
for (; ; ) {
|
|
1736
|
+
if (signal.aborted || Date.now() > deadline) return null;
|
|
1737
|
+
try {
|
|
1738
|
+
box = await provider.start(box);
|
|
1739
|
+
break;
|
|
1740
|
+
} catch {
|
|
1741
|
+
await abortableSleep(backoff, signal);
|
|
1742
|
+
backoff = Math.min(backoff * 2, 5e3);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
if (signal.aborted) return null;
|
|
1746
|
+
const prev = spec;
|
|
1747
|
+
spec = await buildAttach(box, "agent", { sessionName: args.sessionName, command });
|
|
1748
|
+
if (prev.cleanup) {
|
|
1749
|
+
try {
|
|
1750
|
+
await prev.cleanup();
|
|
1751
|
+
} catch {
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
return { command: spec.argv[0], argv: spec.argv.slice(1), env: spec.env };
|
|
1755
|
+
};
|
|
1457
1756
|
try {
|
|
1458
1757
|
const code = await runWrappedAttach({
|
|
1459
|
-
container:
|
|
1758
|
+
container: box.name,
|
|
1460
1759
|
command: spec.argv[0],
|
|
1461
1760
|
dockerArgv: spec.argv.slice(1),
|
|
1462
1761
|
env: spec.env,
|
|
1463
1762
|
relayBaseUrl: RELAY_HOST_URL,
|
|
1464
|
-
boxId:
|
|
1465
|
-
boxName:
|
|
1466
|
-
projectIndex:
|
|
1763
|
+
boxId: box.id,
|
|
1764
|
+
boxName: box.name,
|
|
1765
|
+
projectIndex: box.projectIndex,
|
|
1467
1766
|
mode: args.mode,
|
|
1468
1767
|
detachable: true,
|
|
1469
1768
|
openIn: safeOpenIn,
|
|
1470
|
-
|
|
1769
|
+
reconnect,
|
|
1770
|
+
onError: (msg) => {
|
|
1771
|
+
try {
|
|
1772
|
+
appendFileSync(
|
|
1773
|
+
join2(homedir(), ".agentbox", "logs", "attach.log"),
|
|
1774
|
+
`${(/* @__PURE__ */ new Date()).toISOString()} [${box.name}] ${msg}
|
|
1775
|
+
`
|
|
1776
|
+
);
|
|
1777
|
+
} catch {
|
|
1778
|
+
}
|
|
1779
|
+
},
|
|
1780
|
+
onPasteImage: canPaste ? () => pasteHostClipboardImage(provider, box) : void 0
|
|
1471
1781
|
});
|
|
1472
1782
|
process.exit(code);
|
|
1473
1783
|
} finally {
|
|
@@ -1505,6 +1815,8 @@ export {
|
|
|
1505
1815
|
ADVANCED_HINT_GROUPS,
|
|
1506
1816
|
statusLine,
|
|
1507
1817
|
renderFooter,
|
|
1818
|
+
ALERT_BAND_ROWS,
|
|
1819
|
+
renderAlertBand,
|
|
1508
1820
|
subscribePrompts,
|
|
1509
1821
|
postAnswer,
|
|
1510
1822
|
runWrappedAttach,
|
|
@@ -1513,4 +1825,4 @@ export {
|
|
|
1513
1825
|
buildCloudAttachInnerCommand,
|
|
1514
1826
|
cloudAgentAttach
|
|
1515
1827
|
};
|
|
1516
|
-
//# sourceMappingURL=chunk-
|
|
1828
|
+
//# sourceMappingURL=chunk-R4O5WPHW.js.map
|