@monotykamary/localterm-server 2.70.7 → 2.71.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/terminal-browser-frame-relay.d.ts +11 -0
- package/dist/terminal-browser-frame-relay.d.ts.map +1 -0
- package/dist/terminal-browser-frame-relay.js +58 -0
- package/dist/terminal-browser-frame-relay.js.map +1 -0
- package/dist/terminal-browser-frame-scanner.d.ts +21 -0
- package/dist/terminal-browser-frame-scanner.d.ts.map +1 -0
- package/dist/terminal-browser-frame-scanner.js +90 -0
- package/dist/terminal-browser-frame-scanner.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SessionOutputTransport } from "./session-output-transport.js";
|
|
2
|
+
import type { ManagedSession } from "./session-manager.js";
|
|
3
|
+
import type { TerminalBrowserFrame } from "./terminal-browser-frame-scanner.js";
|
|
4
|
+
export declare class TerminalBrowserFrameRelay {
|
|
5
|
+
private readonly transport;
|
|
6
|
+
private readonly state;
|
|
7
|
+
constructor(transport: SessionOutputTransport);
|
|
8
|
+
push(managed: ManagedSession, frame: TerminalBrowserFrame): void;
|
|
9
|
+
private pump;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=terminal-browser-frame-relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-browser-frame-relay.d.ts","sourceRoot":"","sources":["../src/terminal-browser-frame-relay.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAyBhF,qBAAa,yBAAyB;IAGxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAFtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6C;IAEnE,YAA6B,SAAS,EAAE,sBAAsB,EAAI;IAElE,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAW/D;YAEa,IAAI;CAcnB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { MAX_TERMINAL_BROWSER_FRAME_BYTES } from "./constants.js";
|
|
3
|
+
async function readFrameFile(frame) {
|
|
4
|
+
const expected = frame.width * frame.height * 4;
|
|
5
|
+
if (expected <= 0 || expected > MAX_TERMINAL_BROWSER_FRAME_BYTES)
|
|
6
|
+
return null;
|
|
7
|
+
try {
|
|
8
|
+
const stat = await fs.stat(frame.path);
|
|
9
|
+
if (stat.size !== expected)
|
|
10
|
+
return null;
|
|
11
|
+
const data = await fs.readFile(frame.path);
|
|
12
|
+
if (data.length !== expected)
|
|
13
|
+
return null;
|
|
14
|
+
return new Uint8Array(data);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// latest-wins relay: terminal-browser renders at display cadence while a single
|
|
21
|
+
// frame read + broadcast is async, so frames must not queue (that would pile up
|
|
22
|
+
// megabytes of RGBA). The newest frame on the wire replaces any previous one
|
|
23
|
+
// still pending, bounding memory and keeping the client as fresh as possible.
|
|
24
|
+
export class TerminalBrowserFrameRelay {
|
|
25
|
+
transport;
|
|
26
|
+
state = new WeakMap();
|
|
27
|
+
constructor(transport) {
|
|
28
|
+
this.transport = transport;
|
|
29
|
+
}
|
|
30
|
+
push(managed, frame) {
|
|
31
|
+
let state = this.state.get(managed);
|
|
32
|
+
if (!state) {
|
|
33
|
+
state = { pending: null, active: false };
|
|
34
|
+
this.state.set(managed, state);
|
|
35
|
+
}
|
|
36
|
+
state.pending = frame;
|
|
37
|
+
if (!state.active) {
|
|
38
|
+
state.active = true;
|
|
39
|
+
void this.pump(managed, state);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async pump(managed, state) {
|
|
43
|
+
try {
|
|
44
|
+
while (state.pending) {
|
|
45
|
+
const frame = state.pending;
|
|
46
|
+
state.pending = null;
|
|
47
|
+
const pixels = await readFrameFile(frame);
|
|
48
|
+
if (pixels) {
|
|
49
|
+
this.transport.broadcastTerminalBrowserFrame(managed, frame.width, frame.height, pixels);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
state.active = false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=terminal-browser-frame-relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-browser-frame-relay.js","sourceRoot":"","sources":["../src/terminal-browser-frame-relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,gCAAgC,EAAE,MAAM,gBAAgB,CAAC;AAUlE,KAAK,UAAU,aAAa,CAAC,KAA2B;IACtD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAChD,IAAI,QAAQ,IAAI,CAAC,IAAI,QAAQ,GAAG,gCAAgC;QAAE,OAAO,IAAI,CAAC;IAC9E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACxC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC1C,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,6EAA6E;AAC7E,8EAA8E;AAC9E,MAAM,OAAO,yBAAyB;IAGP,SAAS;IAFrB,KAAK,GAAG,IAAI,OAAO,EAA8B,CAAC;IAEnE,YAA6B,SAAiC;yBAAjC,SAAS;IAA2B,CAAC;IAElE,IAAI,CAAC,OAAuB,EAAE,KAA2B;QACvD,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;YACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;QACD,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAClB,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;YACpB,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,OAAuB,EAAE,KAAiB;QAC3D,IAAI,CAAC;YACH,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC5B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;gBAC1C,IAAI,MAAM,EAAE,CAAC;oBACX,IAAI,CAAC,SAAS,CAAC,6BAA6B,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAC3F,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QACvB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface TerminalBrowserFrame {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
imageId: number;
|
|
5
|
+
path: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const TERMINAL_BROWSER_FRAME_PATH_REGEX: RegExp;
|
|
8
|
+
export declare function isTerminalBrowserFramePath(name: string, tmpdir: string): boolean;
|
|
9
|
+
export declare function parseTerminalBrowserTransmit(body: string): {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
imageId: number;
|
|
13
|
+
name: string;
|
|
14
|
+
} | null;
|
|
15
|
+
export declare class TerminalBrowserFrameScanner {
|
|
16
|
+
private readonly isAllowedPath;
|
|
17
|
+
private partial;
|
|
18
|
+
constructor(isAllowedPath: (name: string) => boolean);
|
|
19
|
+
push(chunk: string): TerminalBrowserFrame[];
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=terminal-browser-frame-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-browser-frame-scanner.d.ts","sourceRoot":"","sources":["../src/terminal-browser-frame-scanner.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAQD,eAAO,MAAM,iCAAiC,QAAyC,CAAC;AAExF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAKhF;AAID,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAoBzE;AAMD,qBAAa,2BAA2B;IAG1B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAF1C,OAAO,CAAC,OAAO,CAAM;IAErB,YAA6B,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,EAAI;IAEzE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,EAAE,CA+B1C;CACF"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { MAX_TERMINAL_BROWSER_FRAME_STREAM_BYTES } from "./constants.js";
|
|
3
|
+
// kitty "file" medium: the app transmits a base64 filesystem path by name and
|
|
4
|
+
// the terminal reads the pixels itself. terminal-browser writes
|
|
5
|
+
// <tmp>/terminal-browser-<pid>-<terminal-id>-<slot>.rgba for this.
|
|
6
|
+
const ESC = "\x1b";
|
|
7
|
+
const ESCAPE_START = ESC + "_";
|
|
8
|
+
const ESCAPE_END = ESC + "\\";
|
|
9
|
+
export const TERMINAL_BROWSER_FRAME_PATH_REGEX = /^terminal-browser-\d+-\d+-\d+\.rgba$/;
|
|
10
|
+
export function isTerminalBrowserFramePath(name, tmpdir) {
|
|
11
|
+
const resolved = path.resolve(name);
|
|
12
|
+
const root = path.resolve(tmpdir) + path.sep;
|
|
13
|
+
if (!resolved.startsWith(root))
|
|
14
|
+
return false;
|
|
15
|
+
return TERMINAL_BROWSER_FRAME_PATH_REGEX.test(path.basename(resolved));
|
|
16
|
+
}
|
|
17
|
+
// Parses the body of a \x1b_G…\x1b\\ APC chunk into name-transmit metadata, or
|
|
18
|
+
// null when it isn't a kitty file-medium transmit. Exported for direct testing.
|
|
19
|
+
export function parseTerminalBrowserTransmit(body) {
|
|
20
|
+
const semi = body.indexOf(";");
|
|
21
|
+
if (semi === -1)
|
|
22
|
+
return null;
|
|
23
|
+
const control = body.slice(0, semi);
|
|
24
|
+
const payload = body.slice(semi + 1);
|
|
25
|
+
const fields = {};
|
|
26
|
+
for (const part of control.split(",")) {
|
|
27
|
+
const eq = part.indexOf("=");
|
|
28
|
+
if (eq === -1)
|
|
29
|
+
continue;
|
|
30
|
+
fields[part.slice(0, eq)] = part.slice(eq + 1);
|
|
31
|
+
}
|
|
32
|
+
if ((fields.a !== "T" && fields.a !== "t") || fields.t !== "f")
|
|
33
|
+
return null;
|
|
34
|
+
const width = Number(fields.s);
|
|
35
|
+
const height = Number(fields.v);
|
|
36
|
+
const imageId = Number(fields.i);
|
|
37
|
+
if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (!Number.isInteger(imageId) || imageId < 0)
|
|
41
|
+
return null;
|
|
42
|
+
return { width, height, imageId, name: Buffer.from(payload, "base64").toString("utf8") };
|
|
43
|
+
}
|
|
44
|
+
// Incremental scanner: PTY output arrives as many small data events, so a single
|
|
45
|
+
// \x1b_G…\x1b\\ APC can span several. Holds the incomplete tail until the
|
|
46
|
+
// terminator lands, then emits any complete frames. Non-frame APCs (inline
|
|
47
|
+
// transmits, sixel, other kitty actions) pass through untouched and are ignored.
|
|
48
|
+
export class TerminalBrowserFrameScanner {
|
|
49
|
+
isAllowedPath;
|
|
50
|
+
partial = "";
|
|
51
|
+
constructor(isAllowedPath) {
|
|
52
|
+
this.isAllowedPath = isAllowedPath;
|
|
53
|
+
}
|
|
54
|
+
push(chunk) {
|
|
55
|
+
const buffer = this.partial + chunk;
|
|
56
|
+
this.partial = "";
|
|
57
|
+
const frames = [];
|
|
58
|
+
let from = 0;
|
|
59
|
+
while (from < buffer.length) {
|
|
60
|
+
const start = buffer.indexOf(ESCAPE_START, from);
|
|
61
|
+
if (start === -1)
|
|
62
|
+
break;
|
|
63
|
+
const end = buffer.indexOf(ESCAPE_END, start + ESCAPE_START.length);
|
|
64
|
+
if (end === -1) {
|
|
65
|
+
const tail = buffer.slice(start);
|
|
66
|
+
if (tail.length <= MAX_TERMINAL_BROWSER_FRAME_STREAM_BYTES)
|
|
67
|
+
this.partial = tail;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
// An APC carries a single final byte after ESC _ ; the kitty graphics
|
|
71
|
+
// protocol uses 'G', so the body here reads "Ga=…". Strip it so the
|
|
72
|
+
// control data parses cleanly.
|
|
73
|
+
let body = buffer.slice(start + ESCAPE_START.length, end);
|
|
74
|
+
if (body.charCodeAt(0) === 0x47)
|
|
75
|
+
body = body.slice(1);
|
|
76
|
+
const parsed = parseTerminalBrowserTransmit(body);
|
|
77
|
+
if (parsed && this.isAllowedPath(parsed.name)) {
|
|
78
|
+
frames.push({
|
|
79
|
+
width: parsed.width,
|
|
80
|
+
height: parsed.height,
|
|
81
|
+
imageId: parsed.imageId,
|
|
82
|
+
path: parsed.name,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
from = end + ESCAPE_END.length;
|
|
86
|
+
}
|
|
87
|
+
return frames;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=terminal-browser-frame-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-browser-frame-scanner.js","sourceRoot":"","sources":["../src/terminal-browser-frame-scanner.ts"],"names":[],"mappings":"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"}
|