@cotal-ai/manager 0.8.3 → 0.9.1
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/attach-client.d.ts +14 -1
- package/dist/attach-client.d.ts.map +1 -1
- package/dist/attach-client.js +161 -14
- package/dist/attach-client.js.map +1 -1
- package/dist/attach-endpoint.d.ts.map +1 -1
- package/dist/attach-endpoint.js +43 -10
- package/dist/attach-endpoint.js.map +1 -1
- package/dist/commands.d.ts +4 -3
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +35 -15
- package/dist/commands.js.map +1 -1
- package/dist/manager.d.ts +66 -4
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +283 -23
- package/dist/manager.js.map +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +9 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/pty.d.ts.map +1 -1
- package/dist/runtime/pty.js +26 -10
- package/dist/runtime/pty.js.map +1 -1
- package/package.json +9 -4
package/dist/attach-client.d.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The detach key — Ctrl-] (0x1d) by default, as in telnet/ssh escape conventions. `COTAL_DETACH_KEY`
|
|
3
|
+
* rebinds it to another control key when Ctrl-] clashes with a keybinding inside the agent's TUI;
|
|
4
|
+
* accepts `ctrl-<char>` or `^<char>` (e.g. `ctrl-b`, `^_`). Read at point of use, per the repo's
|
|
5
|
+
* `COTAL_*` convention. An unparseable value — including a set-but-empty or whitespace-only one —
|
|
6
|
+
* throws (named), never silently falling back; the caller turns that into a loud exit. Only an
|
|
7
|
+
* unset var = the Ctrl-] default.
|
|
8
|
+
*/
|
|
9
|
+
export declare function detachKey(): {
|
|
10
|
+
byte: number;
|
|
11
|
+
label: string;
|
|
12
|
+
overridden: boolean;
|
|
13
|
+
};
|
|
1
14
|
/**
|
|
2
15
|
* Drive a manager's attach endpoint from the terminal: raw-mode stdin streams to
|
|
3
16
|
* the PTY, PTY output streams to stdout, and SIGWINCH-style resizes are forwarded.
|
|
4
|
-
* Ctrl-] detaches without killing the agent.
|
|
17
|
+
* The detach key (Ctrl-] by default, {@link detachKey}) detaches without killing the agent.
|
|
5
18
|
*/
|
|
6
19
|
export declare function attachClient(url: string): Promise<void>;
|
|
7
20
|
//# sourceMappingURL=attach-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach-client.d.ts","sourceRoot":"","sources":["../src/attach-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"attach-client.d.ts","sourceRoot":"","sources":["../src/attach-client.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,SAAS,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAYhF;AAwDD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmIvD"}
|
package/dist/attach-client.js
CHANGED
|
@@ -1,27 +1,91 @@
|
|
|
1
1
|
import WebSocket from "ws";
|
|
2
|
-
|
|
3
|
-
const DETACH = 0x1d;
|
|
2
|
+
import { c } from "./ui.js";
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* The detach key — Ctrl-] (0x1d) by default, as in telnet/ssh escape conventions. `COTAL_DETACH_KEY`
|
|
5
|
+
* rebinds it to another control key when Ctrl-] clashes with a keybinding inside the agent's TUI;
|
|
6
|
+
* accepts `ctrl-<char>` or `^<char>` (e.g. `ctrl-b`, `^_`). Read at point of use, per the repo's
|
|
7
|
+
* `COTAL_*` convention. An unparseable value — including a set-but-empty or whitespace-only one —
|
|
8
|
+
* throws (named), never silently falling back; the caller turns that into a loud exit. Only an
|
|
9
|
+
* unset var = the Ctrl-] default.
|
|
11
10
|
*/
|
|
12
|
-
|
|
11
|
+
export function detachKey() {
|
|
12
|
+
const spec = process.env.COTAL_DETACH_KEY;
|
|
13
|
+
if (spec === undefined)
|
|
14
|
+
return { byte: 0x1d, label: "Ctrl-]", overridden: false };
|
|
15
|
+
const m = /^(?:ctrl-|\^)([a-z@[\\\]^_])$/i.exec(spec.trim());
|
|
16
|
+
if (!m) {
|
|
17
|
+
throw new Error(`invalid COTAL_DETACH_KEY "${spec}" — expected ctrl-<char> or ^<char> for a control key ` +
|
|
18
|
+
`(a-z, or one of @ [ \\ ] ^ _), e.g. ctrl-b`);
|
|
19
|
+
}
|
|
20
|
+
const ch = m[1].toUpperCase();
|
|
21
|
+
return { byte: ch.charCodeAt(0) & 0x1f, label: `Ctrl-${ch}`, overridden: true };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Terminal modes a full-screen child (a fullscreen TUI: OpenCode, or Claude under `/tui fullscreen`)
|
|
25
|
+
* commonly turns on but that we must undo locally on detach/exit: the agent keeps running after
|
|
26
|
+
* Ctrl-], so it never restores OUR terminal. Without this, detaching from a mouse-tracking TUI leaves
|
|
27
|
+
* the terminal reporting every cursor move as input (a stream of `ESC[<…M` escape codes), or its
|
|
28
|
+
* focus in/out as `ESC[I`/`ESC[O`. Disables all mouse-report modes + focus reporting + bracketed
|
|
29
|
+
* paste, resets the keypad/cursor-key modes, shows the cursor, and resets attributes. Leaving the
|
|
30
|
+
* alternate screen is handled separately (ALT_LEAVE_SEQ), only when we actually entered it — see
|
|
31
|
+
* cleanup().
|
|
32
|
+
*/
|
|
33
|
+
const MOUSE_OFF = "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1005l\x1b[?1006l\x1b[?1015l"; // all mouse tracking off
|
|
34
|
+
const RESTORE = MOUSE_OFF +
|
|
13
35
|
"\x1b[?1004l" + // focus reporting off
|
|
14
36
|
"\x1b[?2004l" + // bracketed paste off
|
|
15
37
|
"\x1b[?1l" + // application cursor keys off (DECCKM): a full-screen TUI enables it; left on, arrows emit ESC O A not ESC [ A
|
|
16
38
|
"\x1b>" + // keypad → numeric (DECKPNM)
|
|
17
39
|
"\x1b[?25h" + // show cursor
|
|
18
40
|
"\x1b[0m"; // reset attributes
|
|
41
|
+
// Leave the alternate screen on detach, but ONLY if the child put us there (altScreen). The attach
|
|
42
|
+
// snapshot faithfully re-enters the child's alt-screen (`?1049h`) on our terminal; without a matching
|
|
43
|
+
// leave we strand it in the alt buffer, which has no scrollback and — with xterm alt-scroll — turns
|
|
44
|
+
// the wheel into ↑/↓ that walk shell history. Leaving the alt buffer IS the whole fix: alt-scroll
|
|
45
|
+
// (`?1007`) only acts inside the alt buffer, so `?1049l` alone makes it inert. We deliberately don't
|
|
46
|
+
// send `?1007l` — nothing on this path ever enabled it (unlike console.ts, which sets `?1007h` on
|
|
47
|
+
// entry), so disabling it would clobber the operator's own alternate-scroll preference for later apps.
|
|
48
|
+
const ALT_LEAVE_SEQ = "\x1b[?1049l"; // leave alt-screen — alt-scroll is inert once back in the main buffer
|
|
49
|
+
// Wheel-scroll for full-screen children. A fullscreen TUI (OpenCode, or Claude under `/tui
|
|
50
|
+
// fullscreen`) runs in the alternate screen — where the terminal's native scrollback is dead — and
|
|
51
|
+
// scrolls its content itself off mouse reports. But it enables that mouse capture on ITS pty, and the
|
|
52
|
+
// enable doesn't reliably survive the attach hop (backlog eviction / nested pty), so our terminal
|
|
53
|
+
// never reports the wheel and scrolling dies. So while the child is in the alternate screen we enable
|
|
54
|
+
// SGR mouse reporting on OUR terminal and translate each wheel tick into PageUp/PageDown — the
|
|
55
|
+
// keystrokes a fullscreen TUI treats as "scroll the view" (OpenCode: `messages_page_up`/`_down`;
|
|
56
|
+
// Claude's fullscreen binds PageUp/PageDown too). A child in the normal screen (inline Claude) keeps
|
|
57
|
+
// altScreen=false, so this stays off and its native terminal-scrollback wheel is untouched.
|
|
58
|
+
const MOUSE_ON = "\x1b[?1002h\x1b[?1006h"; // button+drag tracking, SGR encoding
|
|
59
|
+
const PAGE_UP = "\x1b[5~";
|
|
60
|
+
const PAGE_DOWN = "\x1b[6~";
|
|
61
|
+
// Enter/leave the alternate screen: xterm `?1049`, plus the older `?1047`/`?47`.
|
|
62
|
+
const ALT_ENTER = /\x1b\[\?(?:1049|1047|47)h/g;
|
|
63
|
+
const ALT_LEAVE = /\x1b\[\?(?:1049|1047|47)l/g;
|
|
64
|
+
// A complete SGR mouse report: `ESC [ < btn ; col ; row (M|m)`.
|
|
65
|
+
const SGR_MOUSE = /^\x1b\[<(\d+);\d+;\d+[Mm]/;
|
|
66
|
+
const lastIndexOfRe = (re, s) => {
|
|
67
|
+
re.lastIndex = 0;
|
|
68
|
+
let last = -1;
|
|
69
|
+
for (let m = re.exec(s); m; m = re.exec(s))
|
|
70
|
+
last = m.index;
|
|
71
|
+
return last;
|
|
72
|
+
};
|
|
19
73
|
/**
|
|
20
74
|
* Drive a manager's attach endpoint from the terminal: raw-mode stdin streams to
|
|
21
75
|
* the PTY, PTY output streams to stdout, and SIGWINCH-style resizes are forwarded.
|
|
22
|
-
* Ctrl-] detaches without killing the agent.
|
|
76
|
+
* The detach key (Ctrl-] by default, {@link detachKey}) detaches without killing the agent.
|
|
23
77
|
*/
|
|
24
78
|
export function attachClient(url) {
|
|
79
|
+
// Resolve the detach key before connecting so a bad COTAL_DETACH_KEY fails loudly up front
|
|
80
|
+
// (matching the manager's other fail-fast exits) instead of after an attach we'd only tear down.
|
|
81
|
+
let detach;
|
|
82
|
+
try {
|
|
83
|
+
detach = detachKey();
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
console.error(c.red(`✗ ${e.message}`));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
25
89
|
return new Promise((resolve, reject) => {
|
|
26
90
|
const ws = new WebSocket(url);
|
|
27
91
|
const stdin = process.stdin;
|
|
@@ -31,25 +95,105 @@ export function attachClient(url) {
|
|
|
31
95
|
// PTY write or the on-detach restore write. Register a no-op listener once here (not in cleanup,
|
|
32
96
|
// so it outlives the async error tick) to turn it into a handled no-op.
|
|
33
97
|
process.stdout.on("error", () => { });
|
|
98
|
+
// Wheel-scroll state (see MOUSE_ON above): whether the child is in the alternate screen, and a
|
|
99
|
+
// buffer for an SGR mouse report split across stdin reads.
|
|
100
|
+
let altScreen = false;
|
|
101
|
+
let mouseBuf = "";
|
|
102
|
+
// Carry the tail of the previous output frame so an alt-screen escape split across ws frames
|
|
103
|
+
// (e.g. `ESC[?10` then `49h`) is still detected; 16 bytes covers these private-mode sequences.
|
|
104
|
+
// Acting only on state *change* below makes re-scanning the carried bytes idempotent.
|
|
105
|
+
let scanTail = "";
|
|
106
|
+
// Track the child's alt-screen transitions in its output so we can arm/disarm wheel translation.
|
|
107
|
+
const trackAltScreen = (data) => {
|
|
108
|
+
const s = scanTail + data.toString("latin1");
|
|
109
|
+
scanTail = s.slice(-16);
|
|
110
|
+
const enter = lastIndexOfRe(ALT_ENTER, s);
|
|
111
|
+
const leave = lastIndexOfRe(ALT_LEAVE, s);
|
|
112
|
+
if (enter === -1 && leave === -1)
|
|
113
|
+
return;
|
|
114
|
+
const nowAlt = enter > leave;
|
|
115
|
+
if (nowAlt === altScreen)
|
|
116
|
+
return;
|
|
117
|
+
altScreen = nowAlt;
|
|
118
|
+
if (altScreen) {
|
|
119
|
+
if (process.stdout.isTTY)
|
|
120
|
+
process.stdout.write(MOUSE_ON);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Leaving alt-screen mid-session: undo the mouse modes WE enabled so the terminal stops
|
|
124
|
+
// reporting the wheel/clicks and native scrollback works again — don't wait for detach.
|
|
125
|
+
mouseBuf = "";
|
|
126
|
+
if (process.stdout.isTTY)
|
|
127
|
+
process.stdout.write(MOUSE_OFF);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
34
130
|
const sendResize = () => ws.send(`r:${process.stdout.columns ?? 80},${process.stdout.rows ?? 24}`);
|
|
35
131
|
const onInput = (d) => {
|
|
36
|
-
if (d.length === 1 && d[0] ===
|
|
132
|
+
if (d.length === 1 && d[0] === detach.byte) {
|
|
37
133
|
ws.close();
|
|
38
134
|
return;
|
|
39
135
|
}
|
|
40
|
-
|
|
136
|
+
// Inline child: forward keystrokes raw (its wheel scrolls the local terminal, not the app).
|
|
137
|
+
if (!altScreen) {
|
|
138
|
+
ws.send(d);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
// Full-screen child: rewrite wheel reports to PageUp/PageDown, pass everything else through.
|
|
142
|
+
mouseBuf += d.toString("latin1");
|
|
143
|
+
let out = "";
|
|
144
|
+
for (;;) {
|
|
145
|
+
const i = mouseBuf.indexOf("\x1b[<");
|
|
146
|
+
if (i === -1) {
|
|
147
|
+
// A report split right before `<` (`ESC[` now, `<…M` next) would slip through untranslated:
|
|
148
|
+
// hold a trailing `ESC[` for the next read. Never hold a bare trailing `ESC` — that's the
|
|
149
|
+
// Escape key and must forward at once (e.g. OpenCode's interrupt); the rarer `ESC`|`[<…`
|
|
150
|
+
// split stays raw by design.
|
|
151
|
+
const keep = mouseBuf.endsWith("\x1b[") ? 2 : 0;
|
|
152
|
+
out += mouseBuf.slice(0, mouseBuf.length - keep);
|
|
153
|
+
mouseBuf = mouseBuf.slice(mouseBuf.length - keep);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
out += mouseBuf.slice(0, i);
|
|
157
|
+
const rest = mouseBuf.slice(i);
|
|
158
|
+
const m = SGR_MOUSE.exec(rest);
|
|
159
|
+
if (!m) {
|
|
160
|
+
// Hold only a still-valid partial report (`ESC[<` + digits/semicolons) for the next read;
|
|
161
|
+
// anything else can't become an SGR mouse report, so pass it straight through.
|
|
162
|
+
if (/^\x1b\[<[\d;]*$/.test(rest))
|
|
163
|
+
mouseBuf = rest;
|
|
164
|
+
else {
|
|
165
|
+
out += rest;
|
|
166
|
+
mouseBuf = "";
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
const btn = Number(m[1]);
|
|
171
|
+
if (btn & 0x40)
|
|
172
|
+
out += btn & 1 ? PAGE_DOWN : PAGE_UP; // wheel: 64=up, 65=down
|
|
173
|
+
else
|
|
174
|
+
out += m[0]; // other mouse (click/drag): forward raw so the TUI still gets it
|
|
175
|
+
mouseBuf = rest.slice(m[0].length);
|
|
176
|
+
}
|
|
177
|
+
if (out)
|
|
178
|
+
ws.send(Buffer.from(out, "latin1"));
|
|
41
179
|
};
|
|
42
180
|
const cleanup = () => {
|
|
43
181
|
stdin.off("data", onInput);
|
|
44
182
|
process.stdout.off("resize", sendResize);
|
|
45
183
|
// Undo terminal modes the (still-running) agent's TUI enabled — it won't restore us on detach.
|
|
184
|
+
// If the child put us in its alternate screen (altScreen), leave it too so the terminal isn't
|
|
185
|
+
// stranded in the alt buffer; an inline child (altScreen=false) keeps its scrollback.
|
|
46
186
|
if (process.stdout.isTTY)
|
|
47
|
-
process.stdout.write(RESTORE);
|
|
187
|
+
process.stdout.write(RESTORE + (altScreen ? ALT_LEAVE_SEQ : ""));
|
|
48
188
|
if (stdin.isTTY)
|
|
49
189
|
stdin.setRawMode(wasRaw);
|
|
50
190
|
stdin.pause();
|
|
51
191
|
};
|
|
52
192
|
ws.on("open", () => {
|
|
193
|
+
// Make an override visible (the CLI's "attached to X — Ctrl-] to detach" hint still prints the
|
|
194
|
+
// default label). Only on override, so the default case stays free of duplicate noise.
|
|
195
|
+
if (detach.overridden)
|
|
196
|
+
console.error(c.dim(`detach key: ${detach.label} (via COTAL_DETACH_KEY)`));
|
|
53
197
|
if (stdin.isTTY)
|
|
54
198
|
stdin.setRawMode(true);
|
|
55
199
|
stdin.resume();
|
|
@@ -57,7 +201,10 @@ export function attachClient(url) {
|
|
|
57
201
|
process.stdout.on("resize", sendResize);
|
|
58
202
|
stdin.on("data", onInput);
|
|
59
203
|
});
|
|
60
|
-
ws.on("message", (data) =>
|
|
204
|
+
ws.on("message", (data) => {
|
|
205
|
+
trackAltScreen(data);
|
|
206
|
+
process.stdout.write(data);
|
|
207
|
+
});
|
|
61
208
|
ws.on("close", () => {
|
|
62
209
|
cleanup();
|
|
63
210
|
resolve();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach-client.js","sourceRoot":"","sources":["../src/attach-client.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"attach-client.js","sourceRoot":"","sources":["../src/attach-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach-endpoint.d.ts","sourceRoot":"","sources":["../src/attach-endpoint.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAmBtD,sEAAsE;AACtE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;;IAQvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAHR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,EACjD,IAAI,EAAE,MAAM,OAAO;IACpC,2FAA2F;IAC1E,QAAQ,EAAE,MAAM,SAAS,EAAE,EAC5C,IAAI,SAAI;
|
|
1
|
+
{"version":3,"file":"attach-endpoint.d.ts","sourceRoot":"","sources":["../src/attach-endpoint.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAmBtD,sEAAsE;AACtE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;;IAQvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAHR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,EACjD,IAAI,EAAE,MAAM,OAAO;IACpC,2FAA2F;IAC1E,QAAQ,EAAE,MAAM,SAAS,EAAE,EAC5C,IAAI,SAAI;IAwBJ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAUtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B,2DAA2D;IAC3D,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAI3C,oDAAoD;IACpD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIzB,4BAA4B;IAC5B,UAAU,IAAI,MAAM;CA2IrB"}
|
package/dist/attach-endpoint.js
CHANGED
|
@@ -63,7 +63,9 @@ export class AttachEndpoint {
|
|
|
63
63
|
socket.end("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
|
-
this.#wss.handleUpgrade(req, socket, head, (ws) =>
|
|
66
|
+
this.#wss.handleUpgrade(req, socket, head, (ws) => {
|
|
67
|
+
this.#onConnection(ws, name).catch(() => ws.close());
|
|
68
|
+
});
|
|
67
69
|
});
|
|
68
70
|
}
|
|
69
71
|
async start() {
|
|
@@ -172,7 +174,7 @@ export class AttachEndpoint {
|
|
|
172
174
|
return;
|
|
173
175
|
res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
174
176
|
}
|
|
175
|
-
#onConnection(ws, name) {
|
|
177
|
+
async #onConnection(ws, name) {
|
|
176
178
|
const handle = this.lookup(name);
|
|
177
179
|
if (!handle || handle.status() !== "running") {
|
|
178
180
|
ws.close();
|
|
@@ -188,14 +190,26 @@ export class AttachEndpoint {
|
|
|
188
190
|
ws.close(1011, `attach unsupported for the ${handle.kind} runtime — watch its surface directly`);
|
|
189
191
|
return;
|
|
190
192
|
}
|
|
191
|
-
const
|
|
192
|
-
if (backlog.length)
|
|
193
|
-
ws.send(backlog);
|
|
194
|
-
const offData = session.onData((chunk) => {
|
|
193
|
+
const send = (b) => {
|
|
195
194
|
if (ws.readyState === ws.OPEN)
|
|
196
|
-
ws.send(
|
|
195
|
+
ws.send(b);
|
|
196
|
+
};
|
|
197
|
+
// Subscribe to live output BEFORE requesting the snapshot, and buffer what arrives until the
|
|
198
|
+
// snapshot is sent. The snapshot (below) is a point-in-time screen image; anything the child
|
|
199
|
+
// emits after it must land after it, in order. Subscribing first (no await in between) means no
|
|
200
|
+
// chunk can slip through unobserved, and the buffer stops a mid-snapshot burst from racing ahead
|
|
201
|
+
// of — or being lost before — the image.
|
|
202
|
+
let live = false;
|
|
203
|
+
const buffered = [];
|
|
204
|
+
const offData = session.onData((chunk) => {
|
|
205
|
+
if (live)
|
|
206
|
+
send(chunk);
|
|
207
|
+
else
|
|
208
|
+
buffered.push(chunk);
|
|
197
209
|
});
|
|
198
210
|
const offExit = session.onExit(() => ws.close());
|
|
211
|
+
// Wire input + cleanup now too, so the client's initial `r:cols,rows` (and any early keystrokes)
|
|
212
|
+
// during the snapshot await aren't dropped, and a disconnect mid-await still unsubscribes.
|
|
199
213
|
ws.on("message", (data, isBinary) => {
|
|
200
214
|
if (isBinary) {
|
|
201
215
|
session.write(data.toString("utf8"));
|
|
@@ -203,15 +217,34 @@ export class AttachEndpoint {
|
|
|
203
217
|
}
|
|
204
218
|
const text = data.toString();
|
|
205
219
|
const m = /^r:(\d+),(\d+)$/.exec(text);
|
|
206
|
-
if (m)
|
|
207
|
-
session.resize(Number(m[1]), Number(m[2]));
|
|
208
|
-
else
|
|
220
|
+
if (!m) {
|
|
209
221
|
session.write(text);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
session.resize(Number(m[1]), Number(m[2]));
|
|
210
225
|
});
|
|
211
226
|
ws.on("close", () => {
|
|
212
227
|
offData();
|
|
213
228
|
offExit();
|
|
214
229
|
});
|
|
230
|
+
// Reconstructed screen image: the backend replays a byte-exact snapshot — including the
|
|
231
|
+
// alternate-screen buffer of a full-screen TUI (OpenCode/Claude) — so a late or concurrent attach
|
|
232
|
+
// paints correctly without depending on the child to repaint. (Raw scrollback replay couldn't
|
|
233
|
+
// rebuild an alt-screen, leaving same-size re/co-attaches a partial screen.) Then flush anything
|
|
234
|
+
// that arrived while we built it, and switch to sending live output straight through.
|
|
235
|
+
let snapshot;
|
|
236
|
+
try {
|
|
237
|
+
snapshot = await session.backlog();
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
ws.close(1011, "attach snapshot failed");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (snapshot.length)
|
|
244
|
+
send(snapshot);
|
|
245
|
+
for (const chunk of buffered)
|
|
246
|
+
send(chunk);
|
|
247
|
+
live = true; // snapshot delivered — stream subsequent output straight through
|
|
215
248
|
}
|
|
216
249
|
}
|
|
217
250
|
//# sourceMappingURL=attach-endpoint.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach-endpoint.js","sourceRoot":"","sources":["../src/attach-endpoint.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"attach-endpoint.js","sourceRoot":"","sources":["../src/attach-endpoint.ts"],"names":[],"mappings":"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"}
|
package/dist/commands.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Profile } from "@cotal-ai/core";
|
|
1
2
|
type Values = Record<string, string | undefined>;
|
|
2
3
|
/** Resolve which running mesh a control command (`ps`/`start`/`stop`/`attach`) targets, with the
|
|
3
4
|
* same precedence + preflight as the rest of the CLI ({@link resolveMeshTarget} / `connectOrExit`):
|
|
@@ -9,15 +10,15 @@ type Values = Record<string, string | undefined>;
|
|
|
9
10
|
* same escape hatch `connectOrExit` has; plain reachability, no prune).
|
|
10
11
|
* 3. otherwise the registry/`current` resolver, with the same stale-prune + friendly preflight —
|
|
11
12
|
* so `cotal ps --space <name>` reaches that mesh's RECORDED broker instead of silently assuming
|
|
12
|
-
* `DEFAULT_SERVER` (:4222); `--server` overrides. The
|
|
13
|
-
* the RESOLVED mesh's own recorded root (so `--space other` loads other's auth, guarded by
|
|
13
|
+
* `DEFAULT_SERVER` (:4222); `--server` overrides. The tier-scoped caller cred (`profile`) is minted
|
|
14
|
+
* from the RESOLVED mesh's own recorded root (so `--space other` loads other's auth, guarded by
|
|
14
15
|
* `targetFromEntry`'s `auth.space === m.space` check), or none for an open mesh.
|
|
15
16
|
* Shares `@cotal-ai/workspace`'s `preflightTarget`/`pruneStaleMeshes` with the CLI, so a dead/mismatched entry gets
|
|
16
17
|
* the same one-sentence message + stale-prune the rest of the CLI gives — not a raw NATS trace. The
|
|
17
18
|
* pre-resolution sweep runs only for bare / `--server`-only resolution; an explicit `--space` is
|
|
18
19
|
* resolved + preflighted directly (so a `--server` override can recover a dead-recorded mesh).
|
|
19
20
|
* `ask()` can therefore trust the target is reachable + auth-valid. */
|
|
20
|
-
export declare function resolveManagerTarget(v: Values): Promise<{
|
|
21
|
+
export declare function resolveManagerTarget(v: Values, profile: Profile): Promise<{
|
|
21
22
|
space: string;
|
|
22
23
|
server: string;
|
|
23
24
|
creds?: string;
|
package/dist/commands.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAGA,OAAO,EAcL,KAAK,OAAO,EACb,MAAM,gBAAgB,CAAC;AAqBxB,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AA+BjD;;;;;;;;;;;;;;;;;wEAiBwE;AACxE,wBAAsB,oBAAoB,CACxC,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkC5D"}
|
package/dist/commands.js
CHANGED
|
@@ -48,15 +48,15 @@ async function preflightOrExit(target, probeCreds) {
|
|
|
48
48
|
* same escape hatch `connectOrExit` has; plain reachability, no prune).
|
|
49
49
|
* 3. otherwise the registry/`current` resolver, with the same stale-prune + friendly preflight —
|
|
50
50
|
* so `cotal ps --space <name>` reaches that mesh's RECORDED broker instead of silently assuming
|
|
51
|
-
* `DEFAULT_SERVER` (:4222); `--server` overrides. The
|
|
52
|
-
* the RESOLVED mesh's own recorded root (so `--space other` loads other's auth, guarded by
|
|
51
|
+
* `DEFAULT_SERVER` (:4222); `--server` overrides. The tier-scoped caller cred (`profile`) is minted
|
|
52
|
+
* from the RESOLVED mesh's own recorded root (so `--space other` loads other's auth, guarded by
|
|
53
53
|
* `targetFromEntry`'s `auth.space === m.space` check), or none for an open mesh.
|
|
54
54
|
* Shares `@cotal-ai/workspace`'s `preflightTarget`/`pruneStaleMeshes` with the CLI, so a dead/mismatched entry gets
|
|
55
55
|
* the same one-sentence message + stale-prune the rest of the CLI gives — not a raw NATS trace. The
|
|
56
56
|
* pre-resolution sweep runs only for bare / `--server`-only resolution; an explicit `--space` is
|
|
57
57
|
* resolved + preflighted directly (so a `--server` override can recover a dead-recorded mesh).
|
|
58
58
|
* `ask()` can therefore trust the target is reachable + auth-valid. */
|
|
59
|
-
export async function resolveManagerTarget(v) {
|
|
59
|
+
export async function resolveManagerTarget(v, profile) {
|
|
60
60
|
if (v.creds) {
|
|
61
61
|
const server = v.server ?? DEFAULT_SERVER;
|
|
62
62
|
const creds = readFileSync(v.creds, "utf8");
|
|
@@ -89,7 +89,7 @@ export async function resolveManagerTarget(v) {
|
|
|
89
89
|
}
|
|
90
90
|
throw e;
|
|
91
91
|
}
|
|
92
|
-
const creds = target.auth ? await mintCreds(target.auth, newIdentity(),
|
|
92
|
+
const creds = target.auth ? await mintCreds(target.auth, newIdentity(), profile) : undefined;
|
|
93
93
|
await preflightOrExit(target, creds);
|
|
94
94
|
return { space: target.space, server: target.server, creds };
|
|
95
95
|
}
|
|
@@ -105,6 +105,7 @@ function parse(argv) {
|
|
|
105
105
|
agent: { type: "string" },
|
|
106
106
|
config: { type: "string" },
|
|
107
107
|
model: { type: "string" }, // start: model override, wins over the agent file's `model:`
|
|
108
|
+
resume: { type: "string" }, // start: fork an existing session id into the mesh (host-local; claude only)
|
|
108
109
|
roster: { type: "string" },
|
|
109
110
|
creds: { type: "string" },
|
|
110
111
|
runtime: { type: "string" }, // supervise: force pty | tmux | cmux (default pty)
|
|
@@ -130,10 +131,11 @@ function parse(argv) {
|
|
|
130
131
|
/** Connect a short-lived client with the resolved creds, send one control request to the manager,
|
|
131
132
|
* disconnect. The target is already reachability- + auth-preflighted by {@link resolveManagerTarget}
|
|
132
133
|
* (which prints the friendly message + prunes on failure), so this connects straight through. `tier`
|
|
133
|
-
* picks the control subject: privileged for
|
|
134
|
+
* picks the control subject: privileged for start/ps; admin for the operator's cross-agent ops
|
|
134
135
|
* (stop/attach/purge), which the manager refuses on the privileged subject for a non-owner. `creds`
|
|
135
|
-
* is the
|
|
136
|
-
*
|
|
136
|
+
* is the tier-scoped caller cred resolved by {@link resolveManagerTarget} (`control-caller-privileged`
|
|
137
|
+
* for start/ps, `control-caller-admin` for stop/attach — each holds ONLY its own tier's pub grant, so
|
|
138
|
+
* `tier` here matches the cred the caller minted), or undefined on an open mesh. */
|
|
137
139
|
async function ask(space, server, op, args, creds, tier = CONTROL_PRIVILEGED) {
|
|
138
140
|
const ep = new CotalEndpoint({
|
|
139
141
|
space,
|
|
@@ -169,13 +171,19 @@ async function start(argv) {
|
|
|
169
171
|
console.error(c.red("--name is required"));
|
|
170
172
|
process.exit(1);
|
|
171
173
|
}
|
|
172
|
-
|
|
174
|
+
// `--resume ""` asked to resume but named no session — fail loud, don't silently start fresh.
|
|
175
|
+
if (v.resume !== undefined && !v.resume.trim()) {
|
|
176
|
+
console.error(c.red("--resume needs a session id (got an empty value)"));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const t = await resolveManagerTarget(v, "control-caller-privileged");
|
|
173
180
|
const reply = await ask(t.space, t.server, "start", {
|
|
174
181
|
name: v.name,
|
|
175
182
|
role: v.role,
|
|
176
183
|
agent: v.agent,
|
|
177
184
|
config: v.config,
|
|
178
185
|
model: v.model,
|
|
186
|
+
resume: v.resume, // fork an existing session into the mesh (host-local id; caveated for detached start — see docs)
|
|
179
187
|
// Opt-in: only sent when `--transcript` is given; absent => the daemon's default (mirror off).
|
|
180
188
|
transcript: v.transcript ? true : undefined,
|
|
181
189
|
cwd: v.cwd,
|
|
@@ -192,8 +200,10 @@ async function stop(argv) {
|
|
|
192
200
|
process.exit(1);
|
|
193
201
|
}
|
|
194
202
|
// Operator stop is a cross-agent (admin) op — the CLI operator isn't the agent's spawner, so the
|
|
195
|
-
// privileged subject would reject it; admin
|
|
196
|
-
|
|
203
|
+
// privileged subject would reject it; the admin tier reaches any agent. The scoped `control-caller-admin`
|
|
204
|
+
// cred holds ONLY `ctl.<admin>.<id>` (that IS the cross-agent authority — the manager doesn't re-check
|
|
205
|
+
// the caller), so it reaches this op and nothing else.
|
|
206
|
+
const t = await resolveManagerTarget(v, "control-caller-admin");
|
|
197
207
|
const reply = await ask(t.space, t.server, "stop", {
|
|
198
208
|
name: v.name,
|
|
199
209
|
}, t.creds, CONTROL_ADMIN);
|
|
@@ -202,7 +212,7 @@ async function stop(argv) {
|
|
|
202
212
|
}
|
|
203
213
|
async function ps(argv) {
|
|
204
214
|
const v = parse(argv);
|
|
205
|
-
const t = await resolveManagerTarget(v);
|
|
215
|
+
const t = await resolveManagerTarget(v, "control-caller-privileged");
|
|
206
216
|
const reply = await ask(t.space, t.server, "ps", undefined, t.creds);
|
|
207
217
|
failIfNotOk(reply);
|
|
208
218
|
const rows = reply.data ?? [];
|
|
@@ -230,8 +240,8 @@ async function attach(argv) {
|
|
|
230
240
|
process.exit(1);
|
|
231
241
|
}
|
|
232
242
|
// Operator attach is a cross-agent (admin) op — same reasoning as stop (the operator isn't the
|
|
233
|
-
// spawner; admin reaches any agent).
|
|
234
|
-
const t = await resolveManagerTarget(v);
|
|
243
|
+
// spawner; admin reaches any agent). Scoped `control-caller-admin`: ctl.<admin> only.
|
|
244
|
+
const t = await resolveManagerTarget(v, "control-caller-admin");
|
|
235
245
|
const reply = await ask(t.space, t.server, "attach", {
|
|
236
246
|
name: v.name,
|
|
237
247
|
}, t.creds, CONTROL_ADMIN);
|
|
@@ -270,7 +280,17 @@ async function runManager(argv, defaultRuntime) {
|
|
|
270
280
|
process.exit(1);
|
|
271
281
|
}
|
|
272
282
|
const consolePort = v["console-port"] ? Number(v["console-port"]) : undefined;
|
|
273
|
-
|
|
283
|
+
// Construction resolves the runtime (createRuntime) — which fails loud on an unusable env, e.g. the
|
|
284
|
+
// pty runtime under Bun. Render that as one actionable line, not a raw stack (this also lands in
|
|
285
|
+
// `.cotal/manager.log` for a detached `cotal up` daemon).
|
|
286
|
+
let mgr;
|
|
287
|
+
try {
|
|
288
|
+
mgr = new Manager({ space, servers: server, runtime, consolePort });
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
console.error(c.red(`✗ ${e.message}`));
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
274
294
|
await mgr.start();
|
|
275
295
|
console.log(c.green("✓ manager up") +
|
|
276
296
|
c.dim(` (space ${space} · ${mgr.runtimeKind})`) +
|
|
@@ -361,7 +381,7 @@ const managerCommands = [
|
|
|
361
381
|
kind: "command",
|
|
362
382
|
name: "start",
|
|
363
383
|
group: "Control plane",
|
|
364
|
-
summary: "ask the manager to spawn a persona — --name <persona> [--role <r>] [--agent <a>] [--config <file>] [--model <m>] [--cwd <dir>] (loads .cotal/agents/<persona>.md; the peer joins under its name:)",
|
|
384
|
+
summary: "ask the manager to spawn a persona — --name <persona> [--role <r>] [--agent <a>] [--config <file>] [--model <m>] [--cwd <dir>] [--resume <id> (pair with --cwd)] (loads .cotal/agents/<persona>.md; the peer joins under its name:)",
|
|
365
385
|
run: start,
|
|
366
386
|
},
|
|
367
387
|
{
|