@thenewlabs/entangle-serve 2.2.1 → 2.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/agent.d.ts +7 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +9 -1
- package/dist/agent.js.map +1 -1
- package/dist/box-renderer.d.ts +31 -0
- package/dist/box-renderer.d.ts.map +1 -0
- package/dist/box-renderer.js +80 -0
- package/dist/box-renderer.js.map +1 -0
- package/dist/host-terminal.d.ts +23 -0
- package/dist/host-terminal.d.ts.map +1 -0
- package/dist/host-terminal.js +160 -0
- package/dist/host-terminal.js.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -1
- package/dist/multi-session.d.ts +3 -0
- package/dist/multi-session.d.ts.map +1 -1
- package/dist/multi-session.js +83 -0
- package/dist/multi-session.js.map +1 -1
- package/dist/session.d.ts +3 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +4 -1
- package/dist/session.js.map +1 -1
- package/dist/shared-session.d.ts +68 -0
- package/dist/shared-session.d.ts.map +1 -0
- package/dist/shared-session.js +135 -0
- package/dist/shared-session.js.map +1 -0
- package/dist/vt-grid.d.ts +41 -0
- package/dist/vt-grid.d.ts.map +1 -0
- package/dist/vt-grid.js +311 -0
- package/dist/vt-grid.js.map +1 -0
- package/package.json +4 -4
package/dist/agent.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { type CapabilityInfo } from './capability.js';
|
|
2
|
+
import type { SharedSession } from './shared-session.js';
|
|
2
3
|
interface AgentOptions {
|
|
3
4
|
serverUrl: string;
|
|
4
5
|
outputMode?: string;
|
|
5
6
|
password?: string;
|
|
6
7
|
pinnedCapability?: CapabilityInfo;
|
|
8
|
+
sharedSession?: SharedSession;
|
|
9
|
+
onCapabilityReady?: (info: {
|
|
10
|
+
link: string;
|
|
11
|
+
capId: string;
|
|
12
|
+
S: string;
|
|
13
|
+
}) => void;
|
|
7
14
|
}
|
|
8
15
|
export declare function startAgent(options: AgentOptions): Promise<void>;
|
|
9
16
|
export {};
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,UAAU,YAAY;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,cAAc,CAAC;IAGlC,aAAa,CAAC,EAAE,aAAa,CAAC;IAG9B,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAChF;AAgBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA8CrE"}
|
package/dist/agent.js
CHANGED
|
@@ -13,6 +13,8 @@ export async function startAgent(options) {
|
|
|
13
13
|
output,
|
|
14
14
|
...(options.outputMode && { outputMode: options.outputMode }),
|
|
15
15
|
...(options.password && { password: options.password }),
|
|
16
|
+
...(options.sharedSession && { sharedSession: options.sharedSession }),
|
|
17
|
+
...(options.onCapabilityReady && { onCapabilityReady: options.onCapabilityReady }),
|
|
16
18
|
};
|
|
17
19
|
output.info('Starting agent');
|
|
18
20
|
// Hash password if provided (Argon2id with a random salt).
|
|
@@ -85,7 +87,7 @@ async function connectToServer(state, serverUrl) {
|
|
|
85
87
|
state.output.info(`Invoker connected: socketId=${socketId}, capId=${capId}`);
|
|
86
88
|
// Register synchronously (no await) so the session exists before this
|
|
87
89
|
// invoker's AUTH1 arrives on the next relay message.
|
|
88
|
-
const session = handleInvokerConnection(state.ws, socketId, cap, state.passwordHash);
|
|
90
|
+
const session = handleInvokerConnection(state.ws, socketId, cap, state.passwordHash, state.sharedSession);
|
|
89
91
|
relaySessions.set(socketId, session);
|
|
90
92
|
}
|
|
91
93
|
else if (msg.type === 'RELAY_MSG') {
|
|
@@ -132,6 +134,12 @@ function displayCapability(state) {
|
|
|
132
134
|
if (!cap)
|
|
133
135
|
return;
|
|
134
136
|
const link = `${relayUrl}/cap/${cap.capId}#S=${cap.S}`;
|
|
137
|
+
// In shared-terminal mode the host UI owns the display (it shows the URL in
|
|
138
|
+
// the session frame), so hand it the link instead of printing the block.
|
|
139
|
+
if (state.onCapabilityReady) {
|
|
140
|
+
state.onCapabilityReady({ link, capId: cap.capId, S: cap.S });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
135
143
|
state.output.info('');
|
|
136
144
|
state.output.info('=====================================');
|
|
137
145
|
state.output.info(state.pinned ? 'Using pinned capability' : 'Ephemeral capability created (not stored)');
|
package/dist/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAuB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { VtGrid } from './vt-grid.js';
|
|
2
|
+
export declare class BoxRenderer {
|
|
3
|
+
private grid;
|
|
4
|
+
cols: number;
|
|
5
|
+
rows: number;
|
|
6
|
+
constructor(grid: VtGrid, cols: number, rows: number);
|
|
7
|
+
/** Interior width available to the shell (inside the side rails). */
|
|
8
|
+
get innerCols(): number;
|
|
9
|
+
/** Interior height available to the shell (below the title, above the bar). */
|
|
10
|
+
get innerRows(): number;
|
|
11
|
+
/** Update the total terminal size after a resize. */
|
|
12
|
+
resize(cols: number, rows: number): void;
|
|
13
|
+
/**
|
|
14
|
+
* Compose one complete frame: hides the cursor, repaints every border and
|
|
15
|
+
* interior row from the grid, then re-places and shows the real cursor at the
|
|
16
|
+
* grid's cursor position (offset into the box).
|
|
17
|
+
*/
|
|
18
|
+
frame(state: {
|
|
19
|
+
viewers: number;
|
|
20
|
+
url: string | undefined;
|
|
21
|
+
}): string;
|
|
22
|
+
private topBorder;
|
|
23
|
+
private bottomBorder;
|
|
24
|
+
/**
|
|
25
|
+
* Pad a border prefix (already including its leading corner) out to the full
|
|
26
|
+
* terminal width with `═`, ending in `endCorner`. Over-long prefixes are
|
|
27
|
+
* truncated so the line never exceeds the terminal width.
|
|
28
|
+
*/
|
|
29
|
+
private padBorder;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=box-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"box-renderer.d.ts","sourceRoot":"","sources":["../src/box-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAoB3C,qBAAa,WAAW;IAEpB,OAAO,CAAC,IAAI;IACL,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;gBAFX,IAAI,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM;IAGrB,qEAAqE;IACrE,IAAI,SAAS,IAAI,MAAM,CAAuC;IAE9D,+EAA+E;IAC/E,IAAI,SAAS,IAAI,MAAM,CAAuC;IAE9D,qDAAqD;IACrD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKxC;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,MAAM;IAwBlE,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,YAAY;IAKpB;;;;OAIG;IACH,OAAO,CAAC,SAAS;CAOlB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draws the shared shell inside a full-terminal "session frame": side rails, a
|
|
3
|
+
* title bar naming the session and its viewer count, and a bottom bar showing
|
|
4
|
+
* the URL to join. The shell itself is held in a {@link VtGrid} sized to the box
|
|
5
|
+
* interior; this renderer only composes ANSI for one frame — it performs no I/O
|
|
6
|
+
* so the host module owns all writes and lifecycle.
|
|
7
|
+
*
|
|
8
|
+
* The interior is `(cols - 2) x (rows - 2)` at terminal offset row 2, col 2, so
|
|
9
|
+
* grid cell (x, y) maps to terminal (col x + 2, row y + 2).
|
|
10
|
+
*/
|
|
11
|
+
const TOP_LEFT = '╔';
|
|
12
|
+
const TOP_RIGHT = '╗';
|
|
13
|
+
const BOTTOM_LEFT = '╚';
|
|
14
|
+
const BOTTOM_RIGHT = '╝';
|
|
15
|
+
const HORIZONTAL = '═';
|
|
16
|
+
const VERTICAL = '║';
|
|
17
|
+
export class BoxRenderer {
|
|
18
|
+
grid;
|
|
19
|
+
cols;
|
|
20
|
+
rows;
|
|
21
|
+
constructor(grid, cols, rows) {
|
|
22
|
+
this.grid = grid;
|
|
23
|
+
this.cols = cols;
|
|
24
|
+
this.rows = rows;
|
|
25
|
+
}
|
|
26
|
+
/** Interior width available to the shell (inside the side rails). */
|
|
27
|
+
get innerCols() { return Math.max(1, this.cols - 2); }
|
|
28
|
+
/** Interior height available to the shell (below the title, above the bar). */
|
|
29
|
+
get innerRows() { return Math.max(1, this.rows - 2); }
|
|
30
|
+
/** Update the total terminal size after a resize. */
|
|
31
|
+
resize(cols, rows) {
|
|
32
|
+
this.cols = cols;
|
|
33
|
+
this.rows = rows;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Compose one complete frame: hides the cursor, repaints every border and
|
|
37
|
+
* interior row from the grid, then re-places and shows the real cursor at the
|
|
38
|
+
* grid's cursor position (offset into the box).
|
|
39
|
+
*/
|
|
40
|
+
frame(state) {
|
|
41
|
+
const parts = ['\x1b[?25l\x1b[0m'];
|
|
42
|
+
// Title bar.
|
|
43
|
+
parts.push('\x1b[1;1H', this.topBorder(state.viewers));
|
|
44
|
+
// Interior rows: rail + shell row + rail.
|
|
45
|
+
const gridRows = this.grid.renderRows();
|
|
46
|
+
for (let i = 0; i < this.innerRows; i++) {
|
|
47
|
+
const row = gridRows[i] ?? ' '.repeat(this.innerCols);
|
|
48
|
+
parts.push(`\x1b[${i + 2};1H`, VERTICAL, row, VERTICAL);
|
|
49
|
+
}
|
|
50
|
+
// Bottom bar.
|
|
51
|
+
parts.push(`\x1b[${this.rows};1H`, this.bottomBorder(state.url));
|
|
52
|
+
// Real cursor, clamped into the interior and offset by the frame.
|
|
53
|
+
const cx = Math.min(Math.max(0, this.grid.cursorX), this.innerCols - 1) + 2;
|
|
54
|
+
const cy = Math.min(Math.max(0, this.grid.cursorY), this.innerRows - 1) + 2;
|
|
55
|
+
parts.push(`\x1b[${cy};${cx}H`, '\x1b[?25h');
|
|
56
|
+
return parts.join('');
|
|
57
|
+
}
|
|
58
|
+
topBorder(viewers) {
|
|
59
|
+
const label = `${TOP_LEFT}${HORIZONTAL} ⧉ entangle · shared · ` +
|
|
60
|
+
`${viewers} viewer${viewers === 1 ? '' : 's'} `;
|
|
61
|
+
return this.padBorder(label, TOP_RIGHT);
|
|
62
|
+
}
|
|
63
|
+
bottomBorder(url) {
|
|
64
|
+
const shown = url ?? 'connecting…';
|
|
65
|
+
return this.padBorder(`${BOTTOM_LEFT}${HORIZONTAL} ${shown} `, BOTTOM_RIGHT);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Pad a border prefix (already including its leading corner) out to the full
|
|
69
|
+
* terminal width with `═`, ending in `endCorner`. Over-long prefixes are
|
|
70
|
+
* truncated so the line never exceeds the terminal width.
|
|
71
|
+
*/
|
|
72
|
+
padBorder(label, endCorner) {
|
|
73
|
+
const width = this.cols;
|
|
74
|
+
if (label.length + 1 >= width) {
|
|
75
|
+
return label.slice(0, Math.max(0, width - 1)) + endCorner;
|
|
76
|
+
}
|
|
77
|
+
return label + HORIZONTAL.repeat(width - label.length - 1) + endCorner;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=box-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"box-renderer.js","sourceRoot":"","sources":["../src/box-renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { OutputHandler } from '@thenewlabs/entangle-utils';
|
|
2
|
+
import type { SharedSession } from './shared-session.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handle returned by {@link attachHostTerminal}. The session URL is only known
|
|
5
|
+
* once the relay assigns the capability, so index.ts feeds it in later via
|
|
6
|
+
* {@link HostTerminalHandle.setUrl}; until then the bottom bar shows a
|
|
7
|
+
* "connecting…" placeholder.
|
|
8
|
+
*/
|
|
9
|
+
export interface HostTerminalHandle {
|
|
10
|
+
setUrl(link: string): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Wire the host's own terminal to the shared session.
|
|
14
|
+
*
|
|
15
|
+
* On a real, big-enough terminal the host sees their shell rendered *inside* a
|
|
16
|
+
* bordered session frame (side rails, a title bar with the live viewer count,
|
|
17
|
+
* and a bottom bar with the join URL). The shell output is parsed into a
|
|
18
|
+
* {@link VtGrid} sized to the box interior and repainted on a throttle so a
|
|
19
|
+
* left rail is never overwritten. Elsewhere (too small, or not a TTY) it falls
|
|
20
|
+
* back to raw pass-through.
|
|
21
|
+
*/
|
|
22
|
+
export declare function attachHostTerminal(shared: SharedSession, output: OutputHandler): HostTerminalHandle;
|
|
23
|
+
//# sourceMappingURL=host-terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-terminal.d.ts","sourceRoot":"","sources":["../src/host-terminal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWzD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,GAAG,kBAAkB,CAYnG"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { VtGrid } from './vt-grid.js';
|
|
2
|
+
import { BoxRenderer } from './box-renderer.js';
|
|
3
|
+
/** Minimum terminal size that can hold the session frame with a usable interior. */
|
|
4
|
+
const MIN_COLS = 20;
|
|
5
|
+
const MIN_ROWS = 6;
|
|
6
|
+
/** Repaint cadence: coalesce shell output into ~30ms frames rather than per byte. */
|
|
7
|
+
const FRAME_MS = 30;
|
|
8
|
+
/**
|
|
9
|
+
* Wire the host's own terminal to the shared session.
|
|
10
|
+
*
|
|
11
|
+
* On a real, big-enough terminal the host sees their shell rendered *inside* a
|
|
12
|
+
* bordered session frame (side rails, a title bar with the live viewer count,
|
|
13
|
+
* and a bottom bar with the join URL). The shell output is parsed into a
|
|
14
|
+
* {@link VtGrid} sized to the box interior and repainted on a throttle so a
|
|
15
|
+
* left rail is never overwritten. Elsewhere (too small, or not a TTY) it falls
|
|
16
|
+
* back to raw pass-through.
|
|
17
|
+
*/
|
|
18
|
+
export function attachHostTerminal(shared, output) {
|
|
19
|
+
const stdout = process.stdout;
|
|
20
|
+
const stdin = process.stdin;
|
|
21
|
+
const cols = stdout.columns || 0;
|
|
22
|
+
const rows = stdout.rows || 0;
|
|
23
|
+
const boxable = !!stdout.isTTY && !!stdin.isTTY && cols >= MIN_COLS && rows >= MIN_ROWS;
|
|
24
|
+
return boxable
|
|
25
|
+
? attachBoxTerminal(shared, output, cols, rows)
|
|
26
|
+
: attachRawTerminal(shared, output);
|
|
27
|
+
}
|
|
28
|
+
/** Bordered session-frame UI (the normal interactive host experience). */
|
|
29
|
+
function attachBoxTerminal(shared, output, cols, rows) {
|
|
30
|
+
const stdout = process.stdout;
|
|
31
|
+
const stdin = process.stdin;
|
|
32
|
+
const write = (s) => { try {
|
|
33
|
+
stdout.write(s);
|
|
34
|
+
}
|
|
35
|
+
catch { } };
|
|
36
|
+
const grid = new VtGrid(cols - 2, rows - 2);
|
|
37
|
+
const renderer = new BoxRenderer(grid, cols, rows);
|
|
38
|
+
// The shell is authoritative-sized to the box interior, not the full terminal.
|
|
39
|
+
shared.resize(renderer.innerCols, renderer.innerRows);
|
|
40
|
+
// Paint whatever the shell emitted before we attached.
|
|
41
|
+
const initial = shared.getReplay();
|
|
42
|
+
if (initial.length > 0)
|
|
43
|
+
grid.write(Buffer.from(initial).toString('utf8'));
|
|
44
|
+
let url;
|
|
45
|
+
let viewers = shared.viewerCount();
|
|
46
|
+
let dirty = true;
|
|
47
|
+
let clearFirst = true;
|
|
48
|
+
const paint = () => {
|
|
49
|
+
if (!dirty)
|
|
50
|
+
return;
|
|
51
|
+
dirty = false;
|
|
52
|
+
if (clearFirst) {
|
|
53
|
+
write('\x1b[2J\x1b[H');
|
|
54
|
+
clearFirst = false;
|
|
55
|
+
}
|
|
56
|
+
write(renderer.frame({ viewers, url }));
|
|
57
|
+
};
|
|
58
|
+
// Coalesce shell output and title changes into throttled frames.
|
|
59
|
+
const timer = setInterval(paint, FRAME_MS);
|
|
60
|
+
timer.unref?.();
|
|
61
|
+
shared.onHostData((chunk) => { grid.write(chunk.toString('utf8')); dirty = true; });
|
|
62
|
+
shared.onViewersChange((n) => { viewers = n; dirty = true; });
|
|
63
|
+
const wasRaw = !!stdin.isRaw;
|
|
64
|
+
stdin.setRawMode(true);
|
|
65
|
+
stdin.resume();
|
|
66
|
+
const onInput = (d) => shared.write(new Uint8Array(d));
|
|
67
|
+
stdin.on('data', onInput);
|
|
68
|
+
const onResize = () => {
|
|
69
|
+
const nextCols = stdout.columns || 0;
|
|
70
|
+
const nextRows = stdout.rows || 0;
|
|
71
|
+
renderer.resize(nextCols, nextRows);
|
|
72
|
+
grid.resize(renderer.innerCols, renderer.innerRows);
|
|
73
|
+
shared.resize(renderer.innerCols, renderer.innerRows);
|
|
74
|
+
clearFirst = true;
|
|
75
|
+
dirty = true;
|
|
76
|
+
};
|
|
77
|
+
stdout.on('resize', onResize);
|
|
78
|
+
let restored = false;
|
|
79
|
+
const restore = () => {
|
|
80
|
+
if (restored)
|
|
81
|
+
return;
|
|
82
|
+
restored = true;
|
|
83
|
+
clearInterval(timer);
|
|
84
|
+
stdin.removeListener('data', onInput);
|
|
85
|
+
stdout.removeListener('resize', onResize);
|
|
86
|
+
write('\x1b[?25h'); // show cursor
|
|
87
|
+
if (stdin.isTTY) {
|
|
88
|
+
try {
|
|
89
|
+
stdin.setRawMode(wasRaw);
|
|
90
|
+
}
|
|
91
|
+
catch { }
|
|
92
|
+
}
|
|
93
|
+
stdin.pause();
|
|
94
|
+
};
|
|
95
|
+
shared.onExit((code) => {
|
|
96
|
+
restore();
|
|
97
|
+
write('\n');
|
|
98
|
+
output.info('Shared session ended.');
|
|
99
|
+
process.exit(code ?? 0);
|
|
100
|
+
});
|
|
101
|
+
process.on('exit', restore);
|
|
102
|
+
process.on('SIGINT', () => { restore(); process.exit(130); });
|
|
103
|
+
// Clear once and draw the first frame immediately.
|
|
104
|
+
paint();
|
|
105
|
+
return { setUrl(link) { url = link; dirty = true; } };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Raw pass-through: mirror the shell byte-for-byte and forward keystrokes, with
|
|
109
|
+
* no frame. Used when the terminal is too small for a box (or not a TTY).
|
|
110
|
+
*/
|
|
111
|
+
function attachRawTerminal(shared, output) {
|
|
112
|
+
const stdin = process.stdin;
|
|
113
|
+
const stdout = process.stdout;
|
|
114
|
+
const initial = shared.getReplay();
|
|
115
|
+
if (initial.length > 0) {
|
|
116
|
+
try {
|
|
117
|
+
stdout.write(Buffer.from(initial));
|
|
118
|
+
}
|
|
119
|
+
catch { }
|
|
120
|
+
}
|
|
121
|
+
shared.onHostData((chunk) => { try {
|
|
122
|
+
stdout.write(chunk);
|
|
123
|
+
}
|
|
124
|
+
catch { } });
|
|
125
|
+
const wasRaw = !!stdin.isRaw;
|
|
126
|
+
if (stdin.isTTY)
|
|
127
|
+
stdin.setRawMode(true);
|
|
128
|
+
stdin.resume();
|
|
129
|
+
const onInput = (d) => shared.write(new Uint8Array(d));
|
|
130
|
+
stdin.on('data', onInput);
|
|
131
|
+
const onResize = () => shared.resize(stdout.columns || 80, stdout.rows || 24);
|
|
132
|
+
stdout.on('resize', onResize);
|
|
133
|
+
let restored = false;
|
|
134
|
+
const restore = () => {
|
|
135
|
+
if (restored)
|
|
136
|
+
return;
|
|
137
|
+
restored = true;
|
|
138
|
+
stdin.removeListener('data', onInput);
|
|
139
|
+
stdout.removeListener('resize', onResize);
|
|
140
|
+
if (stdin.isTTY) {
|
|
141
|
+
try {
|
|
142
|
+
stdin.setRawMode(wasRaw);
|
|
143
|
+
}
|
|
144
|
+
catch { }
|
|
145
|
+
}
|
|
146
|
+
stdin.pause();
|
|
147
|
+
};
|
|
148
|
+
shared.onExit((code) => {
|
|
149
|
+
restore();
|
|
150
|
+
output.info(`Shared session ended (code=${code ?? 0}).`);
|
|
151
|
+
process.exit(code ?? 0);
|
|
152
|
+
});
|
|
153
|
+
process.on('exit', restore);
|
|
154
|
+
return {
|
|
155
|
+
setUrl(link) {
|
|
156
|
+
output.info(`⧉ entangle session shared — open to collaborate:\n ${link}\n`);
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=host-terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-terminal.js","sourceRoot":"","sources":["../src/host-terminal.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import { getConfig, getVersionInfo, OutputHandler, parseOutputMode } from '@then
|
|
|
4
4
|
import { startAgent } from './agent.js';
|
|
5
5
|
import { createCapability, resolveServeTarget } from './capability.js';
|
|
6
6
|
import { promptHidden } from './prompt.js';
|
|
7
|
+
import { SharedSession } from './shared-session.js';
|
|
8
|
+
import { attachHostTerminal } from './host-terminal.js';
|
|
7
9
|
const program = new Command();
|
|
8
10
|
program
|
|
9
11
|
.name('entangle-serve')
|
|
@@ -21,6 +23,8 @@ program
|
|
|
21
23
|
.option('--server <url>', 'Relay server URL (overrides the origin of the positional URL)')
|
|
22
24
|
.option('--password [password]', 'Require a password to connect; pass the flag alone to be prompted, or set AGENT_PASSWORD')
|
|
23
25
|
.option('--capability <url>', 'Serve a specific capability URL (https://relay/cap/<capId>#S=<secret>) instead of minting a fresh ephemeral one; its host is also used as the relay server')
|
|
26
|
+
.option('--shared', 'Serve one shared terminal that everyone with the URL attaches to (default when run in a terminal)')
|
|
27
|
+
.option('--headless', 'Run headless: each connection gets its own shell instead of a shared terminal')
|
|
24
28
|
.action(async (url, options) => {
|
|
25
29
|
try {
|
|
26
30
|
// Propagate output mode to all loggers in this process
|
|
@@ -49,11 +53,38 @@ program
|
|
|
49
53
|
else {
|
|
50
54
|
password = process.env.AGENT_PASSWORD;
|
|
51
55
|
}
|
|
56
|
+
// Shared-terminal mode: on by default when attached to a real terminal,
|
|
57
|
+
// forced by --shared, disabled by --headless/--no-shared. Only meaningful
|
|
58
|
+
// in text mode (stream-json is for programmatic invokers).
|
|
59
|
+
const isTty = !!process.stdout.isTTY && !!process.stdin.isTTY;
|
|
60
|
+
const shared = options.headless === true ? false
|
|
61
|
+
: options.shared === true ? true
|
|
62
|
+
: isTty && outputMode === 'text';
|
|
63
|
+
let sharedSession;
|
|
64
|
+
// The host UI (when attached) sizes the shared PTY to the box interior and
|
|
65
|
+
// takes the session URL for its bottom bar once the relay assigns it.
|
|
66
|
+
let hostHandle;
|
|
67
|
+
if (shared) {
|
|
68
|
+
const cols = process.stdout.columns || 80;
|
|
69
|
+
const rows = process.stdout.rows || 24;
|
|
70
|
+
sharedSession = new SharedSession(output, { cols, rows });
|
|
71
|
+
if (isTty)
|
|
72
|
+
hostHandle = attachHostTerminal(sharedSession, output);
|
|
73
|
+
}
|
|
52
74
|
await startAgent({
|
|
53
75
|
serverUrl,
|
|
54
76
|
outputMode: program.opts().outputMode,
|
|
55
77
|
...(password ? { password } : {}),
|
|
56
78
|
...(pinnedCapability && { pinnedCapability }),
|
|
79
|
+
...(sharedSession && {
|
|
80
|
+
sharedSession,
|
|
81
|
+
onCapabilityReady: ({ link }) => {
|
|
82
|
+
if (hostHandle)
|
|
83
|
+
hostHandle.setUrl(link);
|
|
84
|
+
else
|
|
85
|
+
output.info(`⧉ entangle session shared — open to collaborate:\n ${link}\n`);
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
57
88
|
});
|
|
58
89
|
}
|
|
59
90
|
catch (error) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACvG,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/multi-session.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { deriveKeys } from '@thenewlabs/entangle-crypto';
|
|
|
4
4
|
import { BidirectionalCounters, StreamCounters } from '@thenewlabs/entangle-utils';
|
|
5
5
|
import { StreamManager } from './stream-manager.js';
|
|
6
6
|
import type { CapabilityInfo } from './capability.js';
|
|
7
|
+
import type { SharedSession } from './shared-session.js';
|
|
7
8
|
export interface MultiSession {
|
|
8
9
|
socketId: string;
|
|
9
10
|
ws: WebSocket;
|
|
@@ -18,6 +19,8 @@ export interface MultiSession {
|
|
|
18
19
|
nonceC?: string;
|
|
19
20
|
streamManager?: StreamManager;
|
|
20
21
|
terminated?: boolean;
|
|
22
|
+
sharedSession?: SharedSession | undefined;
|
|
23
|
+
sharedViewers?: Set<string>;
|
|
21
24
|
}
|
|
22
25
|
export declare function handleMultiStreamFrame(session: MultiSession, frame: {
|
|
23
26
|
type: FrameType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-session.d.ts","sourceRoot":"","sources":["../src/multi-session.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EACL,SAAS,EAWV,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,UAAU,EAKX,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAGL,qBAAqB,EACrB,cAAc,EACf,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"multi-session.d.ts","sourceRoot":"","sources":["../src/multi-session.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EACL,SAAS,EAWV,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,UAAU,EAKX,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAGL,qBAAqB,EACrB,cAAc,EACf,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,SAAS,CAAC;IACd,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC;IAC9C,QAAQ,EAAE,qBAAqB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;IAEvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IAIrB,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC7B;AAkWD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,GAC9C,OAAO,CAAC,IAAI,CAAC,CAsIf;AAGD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAW/D"}
|
package/dist/multi-session.js
CHANGED
|
@@ -2,6 +2,7 @@ import { FrameType, encodeFrame, } from '@thenewlabs/entangle-protocol';
|
|
|
2
2
|
import { streamAeadEncrypt, streamAeadDecrypt, frameAad, AeadDir, } from '@thenewlabs/entangle-crypto';
|
|
3
3
|
import { OutputHandler, parseOutputMode, } from '@thenewlabs/entangle-utils';
|
|
4
4
|
import { encode, decode } from 'cborg';
|
|
5
|
+
import { randomBytes } from 'crypto';
|
|
5
6
|
import { StreamManager } from './stream-manager.js';
|
|
6
7
|
const output = new OutputHandler({ mode: parseOutputMode(process.env.OUTPUT_MODE || 'text') });
|
|
7
8
|
// Tear down a single invoker session on a protocol/counter error WITHOUT
|
|
@@ -48,8 +49,56 @@ async function sendEncrypted(session, frameType, message, sid) {
|
|
|
48
49
|
// Best-effort fallback: avoid crashing handler on send errors
|
|
49
50
|
}
|
|
50
51
|
}
|
|
52
|
+
// Attach a viewer to the shared terminal instead of spawning a new shell.
|
|
53
|
+
// Ordering matters: the 'opened' confirmation and the replay snapshot must be
|
|
54
|
+
// the first frames the client sees for this sid, so any live output produced
|
|
55
|
+
// during setup is buffered and flushed only after they are sent.
|
|
56
|
+
async function handleSharedAttach(session, _message) {
|
|
57
|
+
const shared = session.sharedSession;
|
|
58
|
+
const sid = randomBytes(8).toString('base64url');
|
|
59
|
+
if (!session.sharedViewers)
|
|
60
|
+
session.sharedViewers = new Set();
|
|
61
|
+
session.sharedViewers.add(sid);
|
|
62
|
+
let live = false;
|
|
63
|
+
const pending = [];
|
|
64
|
+
const { replay } = shared.attach({
|
|
65
|
+
sid,
|
|
66
|
+
onData: (chunk) => {
|
|
67
|
+
if (!live) {
|
|
68
|
+
pending.push(chunk);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
void sendStreamData(session, sid, chunk, 'stdout');
|
|
72
|
+
},
|
|
73
|
+
onExit: (code, signal) => {
|
|
74
|
+
session.sharedViewers?.delete(sid);
|
|
75
|
+
void sendStreamExit(session, sid, code, signal);
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
// Confirm the open with the agent-assigned sid (first frame for this sid).
|
|
79
|
+
const openedMsg = {
|
|
80
|
+
ctr: 0,
|
|
81
|
+
msg: { v: 1, kind: 'opened', sid, startedAt: Date.now(), mode: 'pty' },
|
|
82
|
+
};
|
|
83
|
+
await sendEncrypted(session, FrameType.STREAM_OPEN, openedMsg, sid);
|
|
84
|
+
// Sync the late joiner's screen with the recent scrollback.
|
|
85
|
+
if (replay.length > 0) {
|
|
86
|
+
await sendStreamData(session, sid, replay, 'stdout');
|
|
87
|
+
}
|
|
88
|
+
// Go live and flush anything captured during setup, preserving order.
|
|
89
|
+
live = true;
|
|
90
|
+
for (const chunk of pending) {
|
|
91
|
+
await sendStreamData(session, sid, chunk, 'stdout');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
51
94
|
// Handle stream open request
|
|
52
95
|
async function handleStreamOpen(session, message) {
|
|
96
|
+
// In shared-terminal mode a PTY open attaches to the one shared shell; a
|
|
97
|
+
// 'cmd' open still spawns normally so one-off `connect <url> ls` keeps working.
|
|
98
|
+
if (session.sharedSession && message.msg?.mode === 'pty') {
|
|
99
|
+
await handleSharedAttach(session, message);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
53
102
|
if (!session.streamManager) {
|
|
54
103
|
session.streamManager = new StreamManager({
|
|
55
104
|
policy: session.cap.policy,
|
|
@@ -118,6 +167,11 @@ async function handleStreamOpen(session, message) {
|
|
|
118
167
|
}
|
|
119
168
|
// Handle stream data (stdin)
|
|
120
169
|
async function handleStreamData(session, message) {
|
|
170
|
+
// Collaborative input: a viewer's keystrokes merge into the shared shell.
|
|
171
|
+
if (session.sharedViewers?.has(message.msg.sid)) {
|
|
172
|
+
session.sharedSession?.write(message.msg.chunk);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
121
175
|
if (!session.streamManager) {
|
|
122
176
|
await sendStreamError(session, message.msg.sid, 'No active streams');
|
|
123
177
|
return;
|
|
@@ -131,6 +185,10 @@ async function handleStreamData(session, message) {
|
|
|
131
185
|
}
|
|
132
186
|
// Handle stream resize (PTY only)
|
|
133
187
|
async function handleStreamResize(session, message) {
|
|
188
|
+
// The host terminal owns the shared shell's size; ignore viewer resizes so
|
|
189
|
+
// participants don't fight over the dimensions.
|
|
190
|
+
if (session.sharedViewers?.has(message.msg.sid))
|
|
191
|
+
return;
|
|
134
192
|
if (!session.streamManager) {
|
|
135
193
|
await sendStreamError(session, message.msg.sid, 'No active streams');
|
|
136
194
|
return;
|
|
@@ -144,6 +202,11 @@ async function handleStreamResize(session, message) {
|
|
|
144
202
|
}
|
|
145
203
|
// Handle stream signal
|
|
146
204
|
async function handleStreamSignal(session, message) {
|
|
205
|
+
// In the shared shell, control chars (e.g. Ctrl-C) arrive as raw data and are
|
|
206
|
+
// written through; explicit signal frames from a viewer are ignored so one
|
|
207
|
+
// participant can't kill the shell out from under the others.
|
|
208
|
+
if (session.sharedViewers?.has(message.msg.sid))
|
|
209
|
+
return;
|
|
147
210
|
if (!session.streamManager) {
|
|
148
211
|
await sendStreamError(session, message.msg.sid, 'No active streams');
|
|
149
212
|
return;
|
|
@@ -157,6 +220,19 @@ async function handleStreamSignal(session, message) {
|
|
|
157
220
|
}
|
|
158
221
|
// Handle stream close
|
|
159
222
|
async function handleStreamClose(session, message) {
|
|
223
|
+
// A viewer leaving the shared shell just detaches — the shell keeps running
|
|
224
|
+
// for everyone else.
|
|
225
|
+
if (session.sharedViewers?.has(message.msg.sid)) {
|
|
226
|
+
session.sharedViewers.delete(message.msg.sid);
|
|
227
|
+
session.sharedSession?.detach(message.msg.sid);
|
|
228
|
+
const closedMsg = {
|
|
229
|
+
ctr: 0,
|
|
230
|
+
msg: { v: 1, kind: 'closed', sid: message.msg.sid },
|
|
231
|
+
};
|
|
232
|
+
await sendEncrypted(session, FrameType.STREAM_CLOSE, closedMsg, message.msg.sid);
|
|
233
|
+
session.streamCounters.removeStream(message.msg.sid);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
160
236
|
if (!session.streamManager) {
|
|
161
237
|
return;
|
|
162
238
|
}
|
|
@@ -355,6 +431,13 @@ export async function handleMultiStreamFrame(session, frame) {
|
|
|
355
431
|
}
|
|
356
432
|
// Clean up session
|
|
357
433
|
export function cleanupMultiSession(session) {
|
|
434
|
+
// Detach this invoker's viewers from the shared shell without killing it.
|
|
435
|
+
if (session.sharedViewers && session.sharedSession) {
|
|
436
|
+
for (const sid of session.sharedViewers) {
|
|
437
|
+
session.sharedSession.detach(sid);
|
|
438
|
+
}
|
|
439
|
+
session.sharedViewers.clear();
|
|
440
|
+
}
|
|
358
441
|
if (session.streamManager) {
|
|
359
442
|
session.streamManager.closeAllStreams('Session cleanup');
|
|
360
443
|
}
|