@madarco/agentbox 0.2.0 → 0.3.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/dist/{chunk-3OPLNXQ5.js → chunk-3NCUES35.js} +2 -2
- package/dist/{chunk-3NU4PS5W.js → chunk-7NQFIBQG.js} +10 -4
- package/dist/chunk-7NQFIBQG.js.map +1 -0
- package/dist/{chunk-3NIZKZPJ.js → chunk-MOC54XL6.js} +1 -1
- package/dist/{chunk-3NIZKZPJ.js.map → chunk-MOC54XL6.js.map} +1 -1
- package/dist/{create-SE6H4B5U-ESNDTXAI.js → create-SE6H4B5U-IWAZHJHV.js} +3 -3
- package/dist/index.js +311 -60
- package/dist/index.js.map +1 -1
- package/dist/{lifecycle-A4QHFADW-4DREGGAE.js → lifecycle-YTMZYKOE-R4M3OR27.js} +3 -3
- package/package.json +3 -3
- package/runtime/docker/packages/ctl/dist/bin.cjs +24 -3
- package/dist/chunk-3NU4PS5W.js.map +0 -1
- /package/dist/{chunk-3OPLNXQ5.js.map → chunk-3NCUES35.js.map} +0 -0
- /package/dist/{create-SE6H4B5U-ESNDTXAI.js.map → create-SE6H4B5U-IWAZHJHV.js.map} +0 -0
- /package/dist/{lifecycle-A4QHFADW-4DREGGAE.js.map → lifecycle-YTMZYKOE-R4M3OR27.js.map} +0 -0
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
createBox,
|
|
4
4
|
defaultBoxName,
|
|
5
5
|
sanitizeBasename
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-3NCUES35.js";
|
|
7
|
+
import "./chunk-MOC54XL6.js";
|
|
8
8
|
import "./chunk-IDR4HVIC.js";
|
|
9
9
|
import "./chunk-SOMIKEN2.js";
|
|
10
10
|
export {
|
|
@@ -12,4 +12,4 @@ export {
|
|
|
12
12
|
defaultBoxName,
|
|
13
13
|
sanitizeBasename
|
|
14
14
|
};
|
|
15
|
-
//# sourceMappingURL=create-SE6H4B5U-
|
|
15
|
+
//# sourceMappingURL=create-SE6H4B5U-IWAZHJHV.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createBox
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3NCUES35.js";
|
|
5
5
|
import {
|
|
6
6
|
AmbiguousBoxError,
|
|
7
7
|
BoxNotFoundError,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
startBox,
|
|
16
16
|
stopBox,
|
|
17
17
|
unpauseBox
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-7NQFIBQG.js";
|
|
19
19
|
import {
|
|
20
20
|
ClaudeSessionError,
|
|
21
21
|
SHARED_CLAUDE_VOLUME,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
resolveClaudeVolume,
|
|
38
38
|
startClaudeSession,
|
|
39
39
|
stopRelay
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-MOC54XL6.js";
|
|
41
41
|
import {
|
|
42
42
|
STATE_DIR,
|
|
43
43
|
readState,
|
|
@@ -1600,6 +1600,7 @@ var InputParser = class {
|
|
|
1600
1600
|
if (b === LEADER) {
|
|
1601
1601
|
this.flush();
|
|
1602
1602
|
this.state = "leader";
|
|
1603
|
+
this.onEvent({ type: "leader", active: true });
|
|
1603
1604
|
this.arm(this.leaderMs, "leader");
|
|
1604
1605
|
} else if (b === ESC) {
|
|
1605
1606
|
this.flush();
|
|
@@ -1622,14 +1623,18 @@ var InputParser = class {
|
|
|
1622
1623
|
if (c === "v") this.onEvent({ type: "action", name: "vnc" });
|
|
1623
1624
|
else if (c === "w") this.onEvent({ type: "action", name: "web" });
|
|
1624
1625
|
else if (c === "c") this.onEvent({ type: "action", name: "code" });
|
|
1625
|
-
else if (c === "
|
|
1626
|
-
else if (c === "
|
|
1626
|
+
else if (c === "s") this.onEvent({ type: "action", name: "stop" });
|
|
1627
|
+
else if (c === "p") this.onEvent({ type: "action", name: "pause" });
|
|
1628
|
+
else if (c === "d") this.onEvent({ type: "action", name: "destroy" });
|
|
1629
|
+
else if (c === "q") this.onEvent({ type: "quit" });
|
|
1630
|
+
else if (c === "k") this.onEvent({ type: "switch", dir: "prev" });
|
|
1627
1631
|
else if (c === "j" || c === "n" || c === "N") this.onEvent({ type: "switch", dir: "next" });
|
|
1628
1632
|
else {
|
|
1629
1633
|
this.fwd.push(b);
|
|
1630
1634
|
this.flush();
|
|
1631
1635
|
}
|
|
1632
1636
|
}
|
|
1637
|
+
this.onEvent({ type: "leader", active: false });
|
|
1633
1638
|
this.state = "normal";
|
|
1634
1639
|
i++;
|
|
1635
1640
|
continue;
|
|
@@ -1737,6 +1742,7 @@ var InputParser = class {
|
|
|
1737
1742
|
if (kind === "leader" && this.state === "leader") {
|
|
1738
1743
|
this.fwd.push(LEADER);
|
|
1739
1744
|
this.flush();
|
|
1745
|
+
this.onEvent({ type: "leader", active: false });
|
|
1740
1746
|
this.state = "normal";
|
|
1741
1747
|
} else if (kind === "esc" && (this.state === "esc" || this.state === "mouseX10")) {
|
|
1742
1748
|
this.forwardVerbatim(this.esc);
|
|
@@ -1861,6 +1867,18 @@ var PtySession = class {
|
|
|
1861
1867
|
};
|
|
1862
1868
|
|
|
1863
1869
|
// src/dashboard/sidebar.ts
|
|
1870
|
+
function ellipsize(s, max) {
|
|
1871
|
+
if (max <= 0) return "";
|
|
1872
|
+
if (s.length <= max) return s;
|
|
1873
|
+
if (max === 1) return "\u2026";
|
|
1874
|
+
return s.slice(0, max - 1) + "\u2026";
|
|
1875
|
+
}
|
|
1876
|
+
function ellipsizeHead(s, max) {
|
|
1877
|
+
if (max <= 0) return "";
|
|
1878
|
+
if (s.length <= max) return s;
|
|
1879
|
+
if (max === 1) return "\u2026";
|
|
1880
|
+
return "\u2026" + s.slice(s.length - (max - 1));
|
|
1881
|
+
}
|
|
1864
1882
|
function activityCell(b) {
|
|
1865
1883
|
if (b.state !== "running") return `[${b.state}]`;
|
|
1866
1884
|
switch (b.claudeActivity) {
|
|
@@ -1876,8 +1894,12 @@ function activityCell(b) {
|
|
|
1876
1894
|
}
|
|
1877
1895
|
var NEW_BOX_ID = "__agentbox_new__";
|
|
1878
1896
|
var NEW_BOX_LABEL = "+ New box";
|
|
1879
|
-
var SIDEBAR_HEADER = "
|
|
1880
|
-
|
|
1897
|
+
var SIDEBAR_HEADER = "AgentBox";
|
|
1898
|
+
function topBorder(label, w) {
|
|
1899
|
+
const lead = `\u256D\u2500\u2500\u2500 ${label} `;
|
|
1900
|
+
if (lead.length >= w) return lead.slice(0, w);
|
|
1901
|
+
return lead + "\u2500".repeat(w - lead.length);
|
|
1902
|
+
}
|
|
1881
1903
|
function fit(s, w) {
|
|
1882
1904
|
if (s.length === w) return s;
|
|
1883
1905
|
if (s.length > w) return s.slice(0, w);
|
|
@@ -1889,17 +1911,55 @@ function center(s, w) {
|
|
|
1889
1911
|
const leftPad = Math.floor(pad / 2);
|
|
1890
1912
|
return " ".repeat(leftPad) + s + " ".repeat(pad - leftPad);
|
|
1891
1913
|
}
|
|
1914
|
+
function projectLabel(project) {
|
|
1915
|
+
if (!project) return "(no project)";
|
|
1916
|
+
const parts = project.split("/").filter(Boolean);
|
|
1917
|
+
return parts[parts.length - 1] ?? project;
|
|
1918
|
+
}
|
|
1919
|
+
function stripTitleGlyph(s) {
|
|
1920
|
+
const t = s.replace(/^[\s\p{S}*·]+/u, "");
|
|
1921
|
+
return t.length > 0 ? t : s.trim();
|
|
1922
|
+
}
|
|
1923
|
+
function boxRow(b, marker, w) {
|
|
1924
|
+
const numStr = b.index != null ? `${b.index} ` : "";
|
|
1925
|
+
const status = activityCell(b);
|
|
1926
|
+
const left = `${marker}${numStr}`;
|
|
1927
|
+
const room = w - left.length - status.length - 1;
|
|
1928
|
+
if (room <= 0) return fit(`${left}${status}`, w);
|
|
1929
|
+
const middle = b.state === "running" && b.sessionTitle ? ellipsize(stripTitleGlyph(b.sessionTitle), room) : ellipsizeHead(b.name, room);
|
|
1930
|
+
return fit(`${left}${middle}`, w - status.length) + status;
|
|
1931
|
+
}
|
|
1892
1932
|
function sidebarLines(boxes, selectedId, w, h) {
|
|
1893
|
-
const lines = [
|
|
1894
|
-
const
|
|
1933
|
+
const lines = [topBorder(SIDEBAR_HEADER, w), fit("", w)];
|
|
1934
|
+
const rowOwner = [null, null];
|
|
1935
|
+
const headerRows = [true, false];
|
|
1936
|
+
const push = (line, owner, header) => {
|
|
1937
|
+
lines.push(fit(line, w));
|
|
1938
|
+
rowOwner.push(owner);
|
|
1939
|
+
headerRows.push(header);
|
|
1940
|
+
};
|
|
1941
|
+
let prevProject;
|
|
1942
|
+
let seenGroup = false;
|
|
1895
1943
|
for (const b of boxes) {
|
|
1896
|
-
const marker = b.id === selectedId ? "\u25B8
|
|
1897
|
-
|
|
1898
|
-
|
|
1944
|
+
const marker = b.id === selectedId ? "\u25B8" : " ";
|
|
1945
|
+
if (b.id === NEW_BOX_ID) {
|
|
1946
|
+
push(`${marker}${NEW_BOX_LABEL}`, b.id, false);
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1949
|
+
if (!seenGroup || b.project !== prevProject) {
|
|
1950
|
+
push(center(` \u2500\u2500 ${projectLabel(b.project)} \u2500\u2500 `, w), null, true);
|
|
1951
|
+
prevProject = b.project;
|
|
1952
|
+
seenGroup = true;
|
|
1953
|
+
}
|
|
1954
|
+
push(boxRow(b, marker, w), b.id, false);
|
|
1899
1955
|
}
|
|
1900
|
-
if (boxes.length === 0)
|
|
1901
|
-
while (lines.length < h)
|
|
1902
|
-
return
|
|
1956
|
+
if (boxes.length === 0) push(" (no boxes)", null, false);
|
|
1957
|
+
while (lines.length < h) push("", null, false);
|
|
1958
|
+
return {
|
|
1959
|
+
lines: lines.slice(0, h),
|
|
1960
|
+
rowOwner: rowOwner.slice(0, h),
|
|
1961
|
+
headerRows: headerRows.slice(0, h)
|
|
1962
|
+
};
|
|
1903
1963
|
}
|
|
1904
1964
|
function menuLines(boxName, w, h) {
|
|
1905
1965
|
const body = [
|
|
@@ -1963,29 +2023,58 @@ var BRAND_NOBOLD = "\x1B[22m";
|
|
|
1963
2023
|
var HINT_KEY = "\x1B[38;5;255m";
|
|
1964
2024
|
var HINT_TXT = "\x1B[38;5;245m";
|
|
1965
2025
|
var BAR_RESET = "\x1B[0m";
|
|
2026
|
+
var SWITCH_HINT = ["Control+Option+\u2191/\u2193", "switch"];
|
|
1966
2027
|
var HINT_GROUPS = [
|
|
1967
|
-
|
|
2028
|
+
SWITCH_HINT,
|
|
1968
2029
|
["Control+a c", "code"],
|
|
1969
2030
|
["Control+a v", "vnc"],
|
|
1970
2031
|
["Control+a w", "web"],
|
|
1971
2032
|
["Control+a q", "quit"]
|
|
1972
2033
|
];
|
|
1973
|
-
|
|
2034
|
+
var COLLAPSED_HINT_GROUPS = [
|
|
2035
|
+
SWITCH_HINT,
|
|
2036
|
+
["Control+a", "more"]
|
|
2037
|
+
];
|
|
2038
|
+
var ADVANCED_HINT_GROUPS = [
|
|
2039
|
+
["c", "code"],
|
|
2040
|
+
["v", "vnc"],
|
|
2041
|
+
["w", "web"],
|
|
2042
|
+
["s", "stop"],
|
|
2043
|
+
["p", "pause"],
|
|
2044
|
+
["d", "destroy"],
|
|
2045
|
+
["q", "quit"]
|
|
2046
|
+
];
|
|
2047
|
+
function statusLine(box, w, stateLabel, groups = HINT_GROUPS) {
|
|
1974
2048
|
const state = stateLabel ?? (box ? box.state === "running" ? box.claudeActivity ?? "unknown" : box.state : "");
|
|
1975
2049
|
const brandPrefix = box ? " agentbox \u25B8 " : " agentbox ";
|
|
1976
|
-
const
|
|
1977
|
-
const
|
|
1978
|
-
const
|
|
2050
|
+
const base = box ? `${box.name} (${state})` : "";
|
|
2051
|
+
const coreMain = box ? `${base} ` : "";
|
|
2052
|
+
const corePlain = brandPrefix + coreMain;
|
|
1979
2053
|
const SEP = " \u2502 ";
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
`${HINT_TXT}${SEP}`
|
|
1983
|
-
)
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2054
|
+
const renderHints = (g) => ({
|
|
2055
|
+
plain: g.map(([k, l]) => `${k}: ${l}`).join(SEP) + " ",
|
|
2056
|
+
styled: g.map(([k, l]) => `${HINT_KEY}${k}${HINT_TXT}: ${l}`).join(`${HINT_TXT}${SEP}`) + " "
|
|
2057
|
+
});
|
|
2058
|
+
let hints = null;
|
|
2059
|
+
for (const g of [groups, COLLAPSED_HINT_GROUPS]) {
|
|
2060
|
+
const h = renderHints(g);
|
|
2061
|
+
if (corePlain.length + h.plain.length + 1 <= w) {
|
|
2062
|
+
hints = h;
|
|
2063
|
+
break;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
if (!hints) {
|
|
2067
|
+
return BAR_BASE + BAR_BRAND + fit(corePlain, w) + BAR_RESET;
|
|
2068
|
+
}
|
|
2069
|
+
const room = w - corePlain.length - hints.plain.length - 1;
|
|
2070
|
+
let titleSeg = "";
|
|
2071
|
+
if (box?.sessionTitle && room >= 7) {
|
|
2072
|
+
titleSeg = ` \u2014 ${ellipsize(box.sessionTitle, Math.min(40, room - 3))}`;
|
|
2073
|
+
}
|
|
2074
|
+
const leftPlain = brandPrefix + base + titleSeg + (box ? " " : "");
|
|
2075
|
+
const leftStyled = BAR_BRAND + brandPrefix + BRAND_BOLD + base + titleSeg + (box ? " " : "") + BRAND_NOBOLD;
|
|
2076
|
+
const gap = w - leftPlain.length - hints.plain.length;
|
|
2077
|
+
return BAR_BASE + leftStyled + BAR_BASE + " ".repeat(gap) + hints.styled + BAR_RESET;
|
|
1989
2078
|
}
|
|
1990
2079
|
|
|
1991
2080
|
// src/dashboard/compositor.ts
|
|
@@ -1996,6 +2085,7 @@ var SGR_RESET = "\x1B[0m";
|
|
|
1996
2085
|
var POLL_MS = 1e3;
|
|
1997
2086
|
var FRAME_MS = 16;
|
|
1998
2087
|
var RESIZE_DEBOUNCE_MS = 120;
|
|
2088
|
+
var LEADER_LINGER_MS = 1500;
|
|
1999
2089
|
var SYNC_BEGIN = "\x1B[?2026h";
|
|
2000
2090
|
var SYNC_END = "\x1B[?2026l";
|
|
2001
2091
|
function cursorTo2(x, y) {
|
|
@@ -2008,10 +2098,40 @@ var Compositor = class {
|
|
|
2008
2098
|
this.layout = computeLayout(this.out.columns ?? 100, this.out.rows ?? 30);
|
|
2009
2099
|
this.parser = new InputParser({
|
|
2010
2100
|
onEvent: (e) => {
|
|
2101
|
+
if (e.type === "leader") {
|
|
2102
|
+
if (this.leaderLingerTimer) {
|
|
2103
|
+
clearTimeout(this.leaderLingerTimer);
|
|
2104
|
+
this.leaderLingerTimer = null;
|
|
2105
|
+
}
|
|
2106
|
+
if (e.active) {
|
|
2107
|
+
this.leaderActive = true;
|
|
2108
|
+
} else {
|
|
2109
|
+
this.leaderLingerTimer = setTimeout(() => {
|
|
2110
|
+
this.leaderLingerTimer = null;
|
|
2111
|
+
this.leaderActive = false;
|
|
2112
|
+
this.drawChrome();
|
|
2113
|
+
}, LEADER_LINGER_MS);
|
|
2114
|
+
}
|
|
2115
|
+
this.drawChrome();
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
if (this.pendingConfirm) {
|
|
2119
|
+
if (e.type === "forward") {
|
|
2120
|
+
this.handleConfirmKey(e.bytes);
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
this.pendingConfirm = null;
|
|
2124
|
+
this.drawChrome();
|
|
2125
|
+
}
|
|
2011
2126
|
if (e.type === "quit") this.onSig();
|
|
2012
2127
|
else if (e.type === "switch") this.switchBox(e.dir);
|
|
2013
|
-
else if (e.type === "action")
|
|
2014
|
-
|
|
2128
|
+
else if (e.type === "action") {
|
|
2129
|
+
if (e.name === "pause" || e.name === "stop" || e.name === "destroy") {
|
|
2130
|
+
void this.doLifecycle(e.name);
|
|
2131
|
+
} else {
|
|
2132
|
+
void this.doAction(e.name);
|
|
2133
|
+
}
|
|
2134
|
+
} else if (this.createMenu) this.handleCreateMenuKey(e.bytes);
|
|
2015
2135
|
else if (this.lifecycleMenu) this.handleLifecycleMenuKey(e.bytes);
|
|
2016
2136
|
else if (this.menu) this.handleMenuKey(e.bytes);
|
|
2017
2137
|
else this.session?.write(e.bytes);
|
|
@@ -2038,6 +2158,14 @@ var Compositor = class {
|
|
|
2038
2158
|
menu = null;
|
|
2039
2159
|
lifecycleMenu = null;
|
|
2040
2160
|
createMenu = null;
|
|
2161
|
+
/** True while the Ctrl-a leader is pending — swaps the footer to the
|
|
2162
|
+
* expanded chord menu (chrome only; never touches the right pane). */
|
|
2163
|
+
leaderActive = false;
|
|
2164
|
+
/** Holds the expanded footer for LEADER_LINGER_MS after the leader resolves
|
|
2165
|
+
* (so the chord menu doesn't flash by). */
|
|
2166
|
+
leaderLingerTimer = null;
|
|
2167
|
+
/** Set while a destroy confirm is pending in the status bar. */
|
|
2168
|
+
pendingConfirm = null;
|
|
2041
2169
|
activeMode = "claude";
|
|
2042
2170
|
flashMsg = null;
|
|
2043
2171
|
flashTimer = null;
|
|
@@ -2097,7 +2225,9 @@ var Compositor = class {
|
|
|
2097
2225
|
return this.boxes.find((b) => b.id === this.selectedId);
|
|
2098
2226
|
}
|
|
2099
2227
|
async poll() {
|
|
2100
|
-
const before = JSON.stringify(
|
|
2228
|
+
const before = JSON.stringify(
|
|
2229
|
+
this.boxes.map((b) => [b.id, b.state, b.claudeActivity, b.sessionTitle])
|
|
2230
|
+
);
|
|
2101
2231
|
await this.refreshBoxes();
|
|
2102
2232
|
if (this.busy) {
|
|
2103
2233
|
} else if (!this.boxes.some((b) => b.id === this.selectedId) && this.boxes[0]) {
|
|
@@ -2109,7 +2239,9 @@ var Compositor = class {
|
|
|
2109
2239
|
const reresolve = this.session && !running || this.placeholder && running || this.menu && !running || this.lifecycleMenu != null && box?.state !== this.lifecycleMenu.state;
|
|
2110
2240
|
if (reresolve) await this.spawnActive();
|
|
2111
2241
|
}
|
|
2112
|
-
if (JSON.stringify(
|
|
2242
|
+
if (JSON.stringify(
|
|
2243
|
+
this.boxes.map((b) => [b.id, b.state, b.claudeActivity, b.sessionTitle])
|
|
2244
|
+
) !== before) {
|
|
2113
2245
|
this.drawChrome();
|
|
2114
2246
|
}
|
|
2115
2247
|
}
|
|
@@ -2124,6 +2256,7 @@ var Compositor = class {
|
|
|
2124
2256
|
this.menu = null;
|
|
2125
2257
|
this.lifecycleMenu = null;
|
|
2126
2258
|
this.createMenu = null;
|
|
2259
|
+
this.pendingConfirm = null;
|
|
2127
2260
|
this.clearRightPane();
|
|
2128
2261
|
const id = this.selectedId;
|
|
2129
2262
|
const target = await this.deps.resolveTarget(id);
|
|
@@ -2137,6 +2270,7 @@ var Compositor = class {
|
|
|
2137
2270
|
this.menu = null;
|
|
2138
2271
|
this.lifecycleMenu = null;
|
|
2139
2272
|
this.createMenu = null;
|
|
2273
|
+
this.pendingConfirm = null;
|
|
2140
2274
|
if (target.kind === "attach") {
|
|
2141
2275
|
this.activeMode = target.mode ?? "claude";
|
|
2142
2276
|
this.session = new PtySession(
|
|
@@ -2214,7 +2348,7 @@ var Compositor = class {
|
|
|
2214
2348
|
for (const b of bytes) {
|
|
2215
2349
|
if (m.confirmDestroy) {
|
|
2216
2350
|
if (b === 121 || b === 13 || b === 10) {
|
|
2217
|
-
void this.
|
|
2351
|
+
void this.runDestroy(this.selectedId, this.selectedBox()?.name ?? this.selectedId);
|
|
2218
2352
|
} else {
|
|
2219
2353
|
m.confirmDestroy = false;
|
|
2220
2354
|
this.drawChrome();
|
|
@@ -2224,7 +2358,7 @@ var Compositor = class {
|
|
|
2224
2358
|
}
|
|
2225
2359
|
const resumeKey = m.state === "paused" ? 117 : 115;
|
|
2226
2360
|
if (b === resumeKey) {
|
|
2227
|
-
void this.
|
|
2361
|
+
void this.resumeSelected();
|
|
2228
2362
|
return;
|
|
2229
2363
|
}
|
|
2230
2364
|
if (b === 100) {
|
|
@@ -2235,37 +2369,27 @@ var Compositor = class {
|
|
|
2235
2369
|
}
|
|
2236
2370
|
}
|
|
2237
2371
|
}
|
|
2238
|
-
async
|
|
2372
|
+
async resumeSelected() {
|
|
2239
2373
|
if (this.busy) return;
|
|
2240
2374
|
const id = this.selectedId;
|
|
2241
2375
|
const name = this.selectedBox()?.name ?? id;
|
|
2242
|
-
const
|
|
2376
|
+
const verb = this.lifecycleMenu?.state === "stopped" ? "start" : "unpause";
|
|
2243
2377
|
this.busy = true;
|
|
2244
2378
|
this.menu = null;
|
|
2245
2379
|
this.lifecycleMenu = null;
|
|
2246
2380
|
this.createMenu = null;
|
|
2247
|
-
this.placeholder = ["",
|
|
2381
|
+
this.placeholder = ["", " Resuming\u2026"];
|
|
2248
2382
|
this.prevRows = null;
|
|
2249
2383
|
this.drawChrome();
|
|
2250
2384
|
this.scheduleRender();
|
|
2251
2385
|
try {
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
await this.spawnActive();
|
|
2257
|
-
} else {
|
|
2258
|
-
await this.deps.destroyBox(id);
|
|
2259
|
-
if (this.tornDown) return;
|
|
2260
|
-
await this.refreshBoxes();
|
|
2261
|
-
if (this.boxes[0]) this.selectedId = this.boxes[0].id;
|
|
2262
|
-
await this.spawnActive();
|
|
2263
|
-
this.flash(`destroyed ${name}`);
|
|
2264
|
-
}
|
|
2386
|
+
await this.deps.resumeBox(id);
|
|
2387
|
+
if (this.selectedId !== id || this.tornDown) return;
|
|
2388
|
+
await this.refreshBoxes();
|
|
2389
|
+
await this.spawnActive();
|
|
2265
2390
|
} catch (err) {
|
|
2266
2391
|
if (this.selectedId !== id || this.tornDown) return;
|
|
2267
2392
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2268
|
-
const verb = which === "resume" ? resumeVerb : "destroy";
|
|
2269
2393
|
this.placeholder = [
|
|
2270
2394
|
"",
|
|
2271
2395
|
` Failed to ${verb} ${name}:`,
|
|
@@ -2279,6 +2403,102 @@ var Compositor = class {
|
|
|
2279
2403
|
this.busy = false;
|
|
2280
2404
|
}
|
|
2281
2405
|
}
|
|
2406
|
+
/** Destroy `id` and recover the selection. Shared by the lifecycle-menu
|
|
2407
|
+
* confirm and the running-box `Ctrl-a d` status-bar confirm. */
|
|
2408
|
+
async runDestroy(id, name) {
|
|
2409
|
+
if (this.busy) return;
|
|
2410
|
+
this.busy = true;
|
|
2411
|
+
this.menu = null;
|
|
2412
|
+
this.lifecycleMenu = null;
|
|
2413
|
+
this.createMenu = null;
|
|
2414
|
+
this.placeholder = ["", " Destroying\u2026"];
|
|
2415
|
+
this.prevRows = null;
|
|
2416
|
+
this.drawChrome();
|
|
2417
|
+
this.scheduleRender();
|
|
2418
|
+
try {
|
|
2419
|
+
await this.deps.destroyBox(id);
|
|
2420
|
+
if (this.tornDown) return;
|
|
2421
|
+
await this.refreshBoxes();
|
|
2422
|
+
if (this.boxes[0]) this.selectedId = this.boxes[0].id;
|
|
2423
|
+
await this.spawnActive();
|
|
2424
|
+
this.flash(`destroyed ${name}`);
|
|
2425
|
+
} catch (err) {
|
|
2426
|
+
if (this.tornDown) return;
|
|
2427
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2428
|
+
this.placeholder = [
|
|
2429
|
+
"",
|
|
2430
|
+
` Failed to destroy ${name}:`,
|
|
2431
|
+
` ${msg}`,
|
|
2432
|
+
"",
|
|
2433
|
+
` Try from a shell: agentbox destroy ${name}`
|
|
2434
|
+
];
|
|
2435
|
+
this.prevRows = null;
|
|
2436
|
+
this.scheduleRender();
|
|
2437
|
+
} finally {
|
|
2438
|
+
this.busy = false;
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
/** Ctrl-a p/s/d on the selected box. pause/stop transition state (the pane
|
|
2442
|
+
* re-resolves to the lifecycle menu); destroy asks to confirm first. */
|
|
2443
|
+
async doLifecycle(name) {
|
|
2444
|
+
if (this.selectedId === NEW_BOX_ID) {
|
|
2445
|
+
this.flash("select a box first");
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
const id = this.selectedId;
|
|
2449
|
+
const boxName = this.selectedBox()?.name ?? id;
|
|
2450
|
+
if (name === "destroy") {
|
|
2451
|
+
this.pendingConfirm = { boxId: id, name: boxName };
|
|
2452
|
+
this.drawChrome();
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2455
|
+
if (this.selectedBox()?.state !== "running") {
|
|
2456
|
+
this.flash(`${boxName} is not running`);
|
|
2457
|
+
return;
|
|
2458
|
+
}
|
|
2459
|
+
if (this.busy) return;
|
|
2460
|
+
this.busy = true;
|
|
2461
|
+
this.menu = null;
|
|
2462
|
+
this.lifecycleMenu = null;
|
|
2463
|
+
this.createMenu = null;
|
|
2464
|
+
this.placeholder = ["", name === "pause" ? " Pausing\u2026" : " Stopping\u2026"];
|
|
2465
|
+
this.prevRows = null;
|
|
2466
|
+
this.drawChrome();
|
|
2467
|
+
this.scheduleRender();
|
|
2468
|
+
try {
|
|
2469
|
+
if (name === "pause") await this.deps.pauseBox(id);
|
|
2470
|
+
else await this.deps.stopBox(id);
|
|
2471
|
+
if (this.selectedId !== id || this.tornDown) return;
|
|
2472
|
+
await this.refreshBoxes();
|
|
2473
|
+
await this.spawnActive();
|
|
2474
|
+
this.flash(`${name === "pause" ? "paused" : "stopped"} ${boxName}`);
|
|
2475
|
+
} catch (err) {
|
|
2476
|
+
if (this.selectedId !== id || this.tornDown) return;
|
|
2477
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2478
|
+
this.placeholder = [
|
|
2479
|
+
"",
|
|
2480
|
+
` Failed to ${name} ${boxName}:`,
|
|
2481
|
+
` ${msg}`,
|
|
2482
|
+
"",
|
|
2483
|
+
` Try from a shell: agentbox ${name} ${boxName}`
|
|
2484
|
+
];
|
|
2485
|
+
this.prevRows = null;
|
|
2486
|
+
this.scheduleRender();
|
|
2487
|
+
} finally {
|
|
2488
|
+
this.busy = false;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
handleConfirmKey(bytes) {
|
|
2492
|
+
const c = this.pendingConfirm;
|
|
2493
|
+
if (!c) return;
|
|
2494
|
+
const b = bytes[0];
|
|
2495
|
+
this.pendingConfirm = null;
|
|
2496
|
+
if (b === 121 || b === 13 || b === 10) {
|
|
2497
|
+
void this.runDestroy(c.boxId, c.name);
|
|
2498
|
+
} else {
|
|
2499
|
+
this.drawChrome();
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2282
2502
|
handleCreateMenuKey(bytes) {
|
|
2283
2503
|
for (const b of bytes) {
|
|
2284
2504
|
if (b === 99 || b === 13 || b === 10) {
|
|
@@ -2360,6 +2580,7 @@ var Compositor = class {
|
|
|
2360
2580
|
}
|
|
2361
2581
|
switchBox(dir) {
|
|
2362
2582
|
if (this.boxes.length === 0) return;
|
|
2583
|
+
this.pendingConfirm = null;
|
|
2363
2584
|
const i = Math.max(
|
|
2364
2585
|
0,
|
|
2365
2586
|
this.boxes.findIndex((b) => b.id === this.selectedId)
|
|
@@ -2431,23 +2652,36 @@ var Compositor = class {
|
|
|
2431
2652
|
drawChrome() {
|
|
2432
2653
|
if (this.tornDown || this.layout.tooSmall) return;
|
|
2433
2654
|
const { sidebar, sepX, statusY } = this.layout;
|
|
2434
|
-
const lines = sidebarLines(
|
|
2435
|
-
|
|
2436
|
-
|
|
2655
|
+
const { lines, rowOwner, headerRows } = sidebarLines(
|
|
2656
|
+
this.boxes,
|
|
2657
|
+
this.selectedId,
|
|
2658
|
+
sidebar.w,
|
|
2659
|
+
sidebar.h
|
|
2660
|
+
);
|
|
2437
2661
|
let s = SYNC_BEGIN + "\x1B[0m";
|
|
2438
2662
|
for (let i = 0; i < lines.length; i++) {
|
|
2439
|
-
const style = i
|
|
2663
|
+
const style = headerRows[i] ? SB_HEADER : rowOwner[i] === this.selectedId ? SB_SELECTED : SB_BODY;
|
|
2440
2664
|
s += cursorTo2(0, i) + style + lines[i] + SGR_RESET;
|
|
2441
2665
|
}
|
|
2442
|
-
for (let y = 0; y < sidebar.h; y++)
|
|
2666
|
+
for (let y = 0; y < sidebar.h; y++)
|
|
2667
|
+
s += cursorTo2(sepX, y) + SB_HEADER + (y === 0 ? "\u256E" : "\u2502") + SGR_RESET;
|
|
2443
2668
|
let status;
|
|
2444
|
-
if (this.
|
|
2669
|
+
if (this.pendingConfirm) {
|
|
2670
|
+
const w = this.layout.cols;
|
|
2671
|
+
const txt = ` Destroy ${this.pendingConfirm.name}? y = confirm \xB7 any other key = cancel `.slice(0, w).padEnd(w);
|
|
2672
|
+
status = `\x1B[7m${txt}\x1B[0m`;
|
|
2673
|
+
} else if (this.flashMsg) {
|
|
2445
2674
|
const w = this.layout.cols;
|
|
2446
2675
|
const txt = ` ${this.flashMsg} `.slice(0, w).padEnd(w);
|
|
2447
2676
|
status = `\x1B[7m${txt}\x1B[0m`;
|
|
2448
2677
|
} else {
|
|
2449
2678
|
const stateLabel = this.selectedId === NEW_BOX_ID ? "create" : this.menu ? "menu" : this.session && this.activeMode === "shell" ? "shell" : void 0;
|
|
2450
|
-
status = statusLine(
|
|
2679
|
+
status = statusLine(
|
|
2680
|
+
this.selectedBox(),
|
|
2681
|
+
this.layout.cols,
|
|
2682
|
+
stateLabel,
|
|
2683
|
+
this.leaderActive ? ADVANCED_HINT_GROUPS : void 0
|
|
2684
|
+
);
|
|
2451
2685
|
}
|
|
2452
2686
|
s += cursorTo2(0, statusY) + status;
|
|
2453
2687
|
this.out.write(s + SYNC_END);
|
|
@@ -2474,6 +2708,7 @@ var Compositor = class {
|
|
|
2474
2708
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
2475
2709
|
if (this.resizeTimer) clearTimeout(this.resizeTimer);
|
|
2476
2710
|
if (this.flashTimer) clearTimeout(this.flashTimer);
|
|
2711
|
+
if (this.leaderLingerTimer) clearTimeout(this.leaderLingerTimer);
|
|
2477
2712
|
this.parser.dispose();
|
|
2478
2713
|
this.disposeSession();
|
|
2479
2714
|
this.inp.off("data", this.onData);
|
|
@@ -2501,7 +2736,15 @@ function scoped(all, projectRoot, boxes) {
|
|
|
2501
2736
|
return sortBoxes(all ? boxes : boxes.filter((b) => b.projectRoot === projectRoot));
|
|
2502
2737
|
}
|
|
2503
2738
|
function toSidebar(b) {
|
|
2504
|
-
return {
|
|
2739
|
+
return {
|
|
2740
|
+
id: b.id,
|
|
2741
|
+
name: b.name,
|
|
2742
|
+
state: b.state,
|
|
2743
|
+
claudeActivity: b.claudeActivity,
|
|
2744
|
+
sessionTitle: b.claudeSessionTitle,
|
|
2745
|
+
index: b.projectIndex,
|
|
2746
|
+
project: b.projectRoot
|
|
2747
|
+
};
|
|
2505
2748
|
}
|
|
2506
2749
|
var dashboardCommand = new Command7("dashboard").description("Box list + the selected box live Agent session").argument("[box]", "initial box (default: first running box; -p restricts to the cwd project)").option("-p, --project", "only this project's boxes (default: all boxes globally)").action(async (idOrName, opts) => {
|
|
2507
2750
|
try {
|
|
@@ -2675,6 +2918,12 @@ var dashboardCommand = new Command7("dashboard").description("Box list + the sel
|
|
|
2675
2918
|
if (box.state === "paused") await unpauseBox(box.id);
|
|
2676
2919
|
else await startBox(box.id);
|
|
2677
2920
|
};
|
|
2921
|
+
const pauseBoxAction = async (boxId) => {
|
|
2922
|
+
await pauseBox(boxId);
|
|
2923
|
+
};
|
|
2924
|
+
const stopBoxAction = async (boxId) => {
|
|
2925
|
+
await stopBox(boxId);
|
|
2926
|
+
};
|
|
2678
2927
|
const destroyBoxAction = async (boxId) => {
|
|
2679
2928
|
await destroyBox(boxId);
|
|
2680
2929
|
};
|
|
@@ -2688,6 +2937,8 @@ var dashboardCommand = new Command7("dashboard").description("Box list + the sel
|
|
|
2688
2937
|
openShell,
|
|
2689
2938
|
createNewBox,
|
|
2690
2939
|
resumeBox,
|
|
2940
|
+
pauseBox: pauseBoxAction,
|
|
2941
|
+
stopBox: stopBoxAction,
|
|
2691
2942
|
destroyBox: destroyBoxAction,
|
|
2692
2943
|
openVnc,
|
|
2693
2944
|
openCode,
|