botmux 2.47.2 → 2.48.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/adapters/cli/coco.js +3 -3
- package/dist/adapters/cli/coco.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +15 -0
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +6 -5
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +2 -1
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +3 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +2 -0
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/terminal-proxy.d.ts +32 -0
- package/dist/core/terminal-proxy.d.ts.map +1 -0
- package/dist/core/terminal-proxy.js +139 -0
- package/dist/core/terminal-proxy.js.map +1 -0
- package/dist/core/terminal-url.d.ts +27 -0
- package/dist/core/terminal-url.d.ts.map +1 -0
- package/dist/core/terminal-url.js +26 -0
- package/dist/core/terminal-url.js.map +1 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +11 -20
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +31 -1
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/web/sessions.js +1 -1
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +8 -8
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/services/codex-bridge-queue.d.ts.map +1 -1
- package/dist/services/codex-bridge-queue.js +69 -39
- package/dist/services/codex-bridge-queue.js.map +1 -1
- package/dist/worker.js +62 -78
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single fixed reverse-proxy port per daemon. Each session's xterm.js web
|
|
3
|
+
* terminal runs on its own dynamically-assigned worker port, which makes SSH
|
|
4
|
+
* port-forwarding painful on dev machines (one `ssh -L` per topic). This proxy
|
|
5
|
+
* fronts all of a daemon's session terminals under one stable port, routing by
|
|
6
|
+
* sub-path: `http://host:{proxyPort}/s/{sessionId}/...` → the worker's port.
|
|
7
|
+
* Forward one port, reach every session.
|
|
8
|
+
*/
|
|
9
|
+
export interface TerminalProxyOptions {
|
|
10
|
+
port: number;
|
|
11
|
+
host?: string;
|
|
12
|
+
/** Resolve a sessionId to its live worker HTTP port (undefined if not running). */
|
|
13
|
+
resolvePort: (sessionId: string) => number | undefined;
|
|
14
|
+
/** Max upward port probes when `port` is taken (EADDRINUSE). Default 20; 0 disables. */
|
|
15
|
+
maxProbe?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface TerminalProxyHandle {
|
|
18
|
+
port: number;
|
|
19
|
+
close: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Split a request URL of the form `/s/{sessionId}{rest}` into its sessionId and
|
|
23
|
+
* the remainder that should be forwarded to the worker. The remainder always
|
|
24
|
+
* starts with `/` so the worker sees a normal request (`/`, `/?token=x`, …).
|
|
25
|
+
* Returns null when the URL is not a session route.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseTarget(rawUrl: string): {
|
|
28
|
+
sessionId: string;
|
|
29
|
+
rest: string;
|
|
30
|
+
} | null;
|
|
31
|
+
export declare function startTerminalProxy(opts: TerminalProxyOptions): Promise<TerminalProxyHandle>;
|
|
32
|
+
//# sourceMappingURL=terminal-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-proxy.d.ts","sourceRoot":"","sources":["../../src/core/terminal-proxy.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AAEH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvD,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAUtF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAoH3F"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { createServer, request as httpRequest, } from 'node:http';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Split a request URL of the form `/s/{sessionId}{rest}` into its sessionId and
|
|
5
|
+
* the remainder that should be forwarded to the worker. The remainder always
|
|
6
|
+
* starts with `/` so the worker sees a normal request (`/`, `/?token=x`, …).
|
|
7
|
+
* Returns null when the URL is not a session route.
|
|
8
|
+
*/
|
|
9
|
+
export function parseTarget(rawUrl) {
|
|
10
|
+
if (!rawUrl.startsWith('/s/'))
|
|
11
|
+
return null;
|
|
12
|
+
const after = rawUrl.slice(3);
|
|
13
|
+
const m = /^([^/?#]+)(.*)$/.exec(after);
|
|
14
|
+
if (!m || !m[1])
|
|
15
|
+
return null;
|
|
16
|
+
const sessionId = m[1];
|
|
17
|
+
let rest = m[2] ?? '';
|
|
18
|
+
// '' → '/', '?x' → '/?x', '#x' → '/#x'; an explicit '/...' is kept as-is.
|
|
19
|
+
if (rest === '' || rest[0] === '?' || rest[0] === '#')
|
|
20
|
+
rest = '/' + rest;
|
|
21
|
+
return { sessionId, rest };
|
|
22
|
+
}
|
|
23
|
+
export function startTerminalProxy(opts) {
|
|
24
|
+
const host = opts.host ?? '0.0.0.0';
|
|
25
|
+
const server = createServer((req, res) => {
|
|
26
|
+
const parsed = parseTarget(req.url ?? '');
|
|
27
|
+
if (!parsed) {
|
|
28
|
+
res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
|
|
29
|
+
res.end('not found');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const port = opts.resolvePort(parsed.sessionId);
|
|
33
|
+
if (!port) {
|
|
34
|
+
res.writeHead(502, { 'content-type': 'text/plain; charset=utf-8' });
|
|
35
|
+
res.end('session not running');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const upstream = httpRequest({ host: '127.0.0.1', port, method: req.method, path: parsed.rest, headers: req.headers }, (up) => {
|
|
39
|
+
res.writeHead(up.statusCode ?? 502, up.headers);
|
|
40
|
+
up.pipe(res);
|
|
41
|
+
});
|
|
42
|
+
upstream.on('error', () => {
|
|
43
|
+
if (!res.headersSent)
|
|
44
|
+
res.writeHead(502, { 'content-type': 'text/plain; charset=utf-8' });
|
|
45
|
+
res.end('proxy error');
|
|
46
|
+
});
|
|
47
|
+
req.pipe(upstream);
|
|
48
|
+
});
|
|
49
|
+
server.on('upgrade', (req, clientSocket, head) => {
|
|
50
|
+
const parsed = parseTarget(req.url ?? '');
|
|
51
|
+
if (!parsed)
|
|
52
|
+
return clientSocket.destroy();
|
|
53
|
+
const port = opts.resolvePort(parsed.sessionId);
|
|
54
|
+
if (!port)
|
|
55
|
+
return clientSocket.destroy();
|
|
56
|
+
const upstream = httpRequest({
|
|
57
|
+
host: '127.0.0.1',
|
|
58
|
+
port,
|
|
59
|
+
method: req.method,
|
|
60
|
+
path: parsed.rest,
|
|
61
|
+
headers: req.headers,
|
|
62
|
+
});
|
|
63
|
+
upstream.on('upgrade', (upRes, upstreamSocket, upstreamHead) => {
|
|
64
|
+
// rawHeaders is a flat [k, v, k, v, ...] list — preserves duplicates/casing.
|
|
65
|
+
const lines = [`HTTP/1.1 ${upRes.statusCode} ${upRes.statusMessage}`];
|
|
66
|
+
const rh = upRes.rawHeaders;
|
|
67
|
+
for (let i = 0; i + 1 < rh.length; i += 2)
|
|
68
|
+
lines.push(`${rh[i]}: ${rh[i + 1]}`);
|
|
69
|
+
lines.push('', '');
|
|
70
|
+
clientSocket.write(lines.join('\r\n'));
|
|
71
|
+
if (upstreamHead?.length)
|
|
72
|
+
clientSocket.write(upstreamHead);
|
|
73
|
+
if (head?.length)
|
|
74
|
+
upstreamSocket.write(head);
|
|
75
|
+
upstreamSocket.pipe(clientSocket);
|
|
76
|
+
clientSocket.pipe(upstreamSocket);
|
|
77
|
+
const cleanup = () => { upstreamSocket.destroy(); clientSocket.destroy(); };
|
|
78
|
+
upstreamSocket.on('error', cleanup);
|
|
79
|
+
clientSocket.on('error', cleanup);
|
|
80
|
+
upstreamSocket.on('close', () => clientSocket.destroy());
|
|
81
|
+
clientSocket.on('close', () => upstreamSocket.destroy());
|
|
82
|
+
});
|
|
83
|
+
// Upstream answered without upgrading (e.g. worker rejected the handshake).
|
|
84
|
+
// Relay the response and close the client socket so it doesn't hang. The
|
|
85
|
+
// body arrives already de-chunked, so drop framing headers and let the
|
|
86
|
+
// socket close delimit the response (HTTP/1.1 connection-close framing).
|
|
87
|
+
upstream.on('response', (upRes) => {
|
|
88
|
+
const lines = [`HTTP/1.1 ${upRes.statusCode} ${upRes.statusMessage}`, 'connection: close'];
|
|
89
|
+
const rh = upRes.rawHeaders;
|
|
90
|
+
for (let i = 0; i + 1 < rh.length; i += 2) {
|
|
91
|
+
const name = rh[i].toLowerCase();
|
|
92
|
+
if (name === 'transfer-encoding' || name === 'content-length' || name === 'connection')
|
|
93
|
+
continue;
|
|
94
|
+
lines.push(`${rh[i]}: ${rh[i + 1]}`);
|
|
95
|
+
}
|
|
96
|
+
lines.push('', '');
|
|
97
|
+
clientSocket.write(lines.join('\r\n'));
|
|
98
|
+
upRes.on('data', (chunk) => clientSocket.write(chunk));
|
|
99
|
+
upRes.on('end', () => { clientSocket.end(); upRes.socket?.destroy(); });
|
|
100
|
+
upRes.on('error', () => clientSocket.destroy());
|
|
101
|
+
});
|
|
102
|
+
upstream.on('error', () => clientSocket.destroy());
|
|
103
|
+
upstream.end();
|
|
104
|
+
});
|
|
105
|
+
// When the preferred port is taken, probe upward to the next free port so the
|
|
106
|
+
// proxy always comes up on a single stable-ish port (the daemon advertises the
|
|
107
|
+
// actually-bound port via getTerminalProxyPort, so links auto-follow). After
|
|
108
|
+
// maxProbe exhausted attempts it rejects → daemon falls back to direct ports.
|
|
109
|
+
const maxProbe = opts.maxProbe ?? 20;
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
let port = opts.port;
|
|
112
|
+
let attempts = 0;
|
|
113
|
+
const onError = (err) => {
|
|
114
|
+
if (err.code === 'EADDRINUSE' && attempts < maxProbe) {
|
|
115
|
+
attempts++;
|
|
116
|
+
logger.warn(`[terminal-proxy] port ${port} in use, trying ${port + 1}`);
|
|
117
|
+
port++;
|
|
118
|
+
setImmediate(tryListen);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
reject(err);
|
|
122
|
+
};
|
|
123
|
+
const tryListen = () => {
|
|
124
|
+
server.once('error', onError);
|
|
125
|
+
server.listen(port, host, () => {
|
|
126
|
+
server.removeListener('error', onError);
|
|
127
|
+
const bound = server.address().port;
|
|
128
|
+
// Runtime error handler for post-bind failures.
|
|
129
|
+
server.on('error', (err) => logger.error(`[terminal-proxy] server error: ${err.message}`));
|
|
130
|
+
resolve({
|
|
131
|
+
port: bound,
|
|
132
|
+
close: () => new Promise((r) => server.close(() => r())),
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
tryListen();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=terminal-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-proxy.js","sourceRoot":"","sources":["../../src/core/terminal-proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the public URL for a session's web terminal. When the per-daemon
|
|
3
|
+
* reverse proxy (terminal-proxy.ts) is up, URLs go through it under
|
|
4
|
+
* `/s/{sessionId}` so users only forward one port. If the proxy failed to bind,
|
|
5
|
+
* we fall back to the worker's own port so links never go dead — the proxy is
|
|
6
|
+
* an enhancement, not a hard dependency. externalHost is read live (not
|
|
7
|
+
* snapshotted) so cards stay correct across network changes.
|
|
8
|
+
*/
|
|
9
|
+
interface TerminalUrlSession {
|
|
10
|
+
session: {
|
|
11
|
+
sessionId: string;
|
|
12
|
+
webPort?: number | null;
|
|
13
|
+
};
|
|
14
|
+
workerPort: number | null;
|
|
15
|
+
workerToken: string | null;
|
|
16
|
+
}
|
|
17
|
+
/** Marks the proxy live on `port`. Called only after a successful bind. */
|
|
18
|
+
export declare function setTerminalProxyPort(port: number): void;
|
|
19
|
+
/** Bound proxy port, or 0 when the proxy is not available. */
|
|
20
|
+
export declare function getTerminalProxyPort(): number;
|
|
21
|
+
/** Test/edge helper: revert to the no-proxy (direct-port) state. */
|
|
22
|
+
export declare function resetTerminalProxy(): void;
|
|
23
|
+
export declare function buildTerminalUrl(ds: TerminalUrlSession, opts?: {
|
|
24
|
+
write?: boolean;
|
|
25
|
+
}): string;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=terminal-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-url.d.ts","sourceRoot":"","sources":["../../src/core/terminal-url.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,UAAU,kBAAkB;IAC1B,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACxD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAKD,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGvD;AAED,8DAA8D;AAC9D,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,kBAAkB,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CAM/F"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { config } from '../config.js';
|
|
2
|
+
let proxyPort = 0;
|
|
3
|
+
let proxyReady = false;
|
|
4
|
+
/** Marks the proxy live on `port`. Called only after a successful bind. */
|
|
5
|
+
export function setTerminalProxyPort(port) {
|
|
6
|
+
proxyPort = port;
|
|
7
|
+
proxyReady = true;
|
|
8
|
+
}
|
|
9
|
+
/** Bound proxy port, or 0 when the proxy is not available. */
|
|
10
|
+
export function getTerminalProxyPort() {
|
|
11
|
+
return proxyReady ? proxyPort : 0;
|
|
12
|
+
}
|
|
13
|
+
/** Test/edge helper: revert to the no-proxy (direct-port) state. */
|
|
14
|
+
export function resetTerminalProxy() {
|
|
15
|
+
proxyPort = 0;
|
|
16
|
+
proxyReady = false;
|
|
17
|
+
}
|
|
18
|
+
export function buildTerminalUrl(ds, opts = {}) {
|
|
19
|
+
const base = proxyReady
|
|
20
|
+
? `http://${config.web.externalHost}:${proxyPort}/s/${ds.session.sessionId}`
|
|
21
|
+
: `http://${config.web.externalHost}:${ds.workerPort ?? ds.session.webPort}`;
|
|
22
|
+
if (opts.write && ds.workerToken)
|
|
23
|
+
return `${base}?token=${ds.workerToken}`;
|
|
24
|
+
return base;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=terminal-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-url.js","sourceRoot":"","sources":["../../src/core/terminal-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAiBtC,IAAI,SAAS,GAAG,CAAC,CAAC;AAClB,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB,2EAA2E;AAC3E,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,SAAS,GAAG,IAAI,CAAC;IACjB,UAAU,GAAG,IAAI,CAAC;AACpB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,oBAAoB;IAClC,OAAO,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,kBAAkB;IAChC,SAAS,GAAG,CAAC,CAAC;IACd,UAAU,GAAG,KAAK,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAsB,EAAE,OAA4B,EAAE;IACrF,MAAM,IAAI,GAAG,UAAU;QACrB,CAAC,CAAC,UAAU,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,SAAS,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE;QAC5E,CAAC,CAAC,UAAU,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAC/E,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,WAAW;QAAE,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3E,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/core/worker-pool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAyBvE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAkB,cAAc,EAAE,OAAO,EAAe,MAAM,aAAa,CAAC;AACxF,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/core/worker-pool.ts"],"names":[],"mappings":"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"}
|
package/dist/core/worker-pool.js
CHANGED
|
@@ -28,6 +28,7 @@ import { dashboardEventBus } from './dashboard-events.js';
|
|
|
28
28
|
import { composeRowFromActive } from './dashboard-rows.js';
|
|
29
29
|
import { knownBotOpenIdsFromCrossRef } from '../utils/bot-routing.js';
|
|
30
30
|
import { sessionKey, sessionAnchorId } from './types.js';
|
|
31
|
+
import { buildTerminalUrl } from './terminal-url.js';
|
|
31
32
|
import { usageLimitStateKey } from '../utils/cli-usage-limit.js';
|
|
32
33
|
const __filename = fileURLToPath(import.meta.url);
|
|
33
34
|
const __dirname = dirname(__filename);
|
|
@@ -112,16 +113,6 @@ export function isRelayableRealSession(ds) {
|
|
|
112
113
|
return true;
|
|
113
114
|
return false;
|
|
114
115
|
}
|
|
115
|
-
// ─── Terminal URL helpers ──────────────────────────────────────────────────
|
|
116
|
-
// config.web.externalHost is a live getter (re-resolves the LAN IP each read
|
|
117
|
-
// when WEB_EXTERNAL_HOST is unset), so building the URL fresh at every card
|
|
118
|
-
// render/patch is enough to keep links pointing at the current network.
|
|
119
|
-
function terminalReadUrl(port) {
|
|
120
|
-
return `http://${config.web.externalHost}:${port}`;
|
|
121
|
-
}
|
|
122
|
-
function terminalWriteUrl(port, token) {
|
|
123
|
-
return `${terminalReadUrl(port)}?token=${encodeURIComponent(token)}`;
|
|
124
|
-
}
|
|
125
116
|
// Per-bot opt-out: when true, botmux never posts/patches the live streaming
|
|
126
117
|
// session card. Read fresh from the in-memory registry so a dashboard toggle
|
|
127
118
|
// takes effect without a daemon restart. The `/card` command can override it
|
|
@@ -150,7 +141,7 @@ export function writableTerminalLinkFor(ds) {
|
|
|
150
141
|
}
|
|
151
142
|
if (!ds.workerPort || !ds.workerToken)
|
|
152
143
|
return undefined;
|
|
153
|
-
return
|
|
144
|
+
return buildTerminalUrl(ds, { write: true });
|
|
154
145
|
}
|
|
155
146
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
156
147
|
function tag(ds) {
|
|
@@ -208,7 +199,7 @@ function scheduleUsageLimitCardPatch(ds) {
|
|
|
208
199
|
return;
|
|
209
200
|
const bot = getBot(ds.larkAppId);
|
|
210
201
|
const effectiveCliId = sessionCliId(ds, bot.config);
|
|
211
|
-
const readUrl =
|
|
202
|
+
const readUrl = buildTerminalUrl(ds);
|
|
212
203
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
213
204
|
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'limited', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), ds.usageLimit, writableTerminalLinkFor(ds));
|
|
214
205
|
scheduleCardPatch(ds, cardJson);
|
|
@@ -361,7 +352,7 @@ export async function postFreshStreamingCard(ds, sessionReply) {
|
|
|
361
352
|
return false;
|
|
362
353
|
const botCfg = getBot(ds.larkAppId).config;
|
|
363
354
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
364
|
-
const readUrl =
|
|
355
|
+
const readUrl = buildTerminalUrl(ds);
|
|
365
356
|
const title = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
366
357
|
const status = ds.lastScreenStatus ?? 'idle';
|
|
367
358
|
// Park the current card (no-op when there's none) so the fresh one replaces
|
|
@@ -425,7 +416,7 @@ export async function postPrivateSnapshotCard(ds, audience) {
|
|
|
425
416
|
return { sent: 0, total: audience.length, notReady: true };
|
|
426
417
|
const botCfg = getBot(ds.larkAppId).config;
|
|
427
418
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
428
|
-
const readUrl =
|
|
419
|
+
const readUrl = buildTerminalUrl(ds);
|
|
429
420
|
const title = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
430
421
|
const status = ds.lastScreenStatus ?? 'idle';
|
|
431
422
|
const cardJson = buildPrivateSnapshotCard(readUrl, title, status, effectiveCliId, ds.currentImageKey, ds.lastScreenContent ?? '', ds.session.sessionId, sessionAnchorId(ds), localeForBot(ds.larkAppId), cardUsageLimit(ds));
|
|
@@ -1168,8 +1159,8 @@ function setupWorkerHandlers(ds, worker) {
|
|
|
1168
1159
|
// Persist port so it can be reused after daemon restart
|
|
1169
1160
|
ds.session.webPort = msg.port;
|
|
1170
1161
|
sessionStore.updateSession(ds.session);
|
|
1171
|
-
const readOnlyUrl =
|
|
1172
|
-
const writeUrl =
|
|
1162
|
+
const readOnlyUrl = buildTerminalUrl(ds);
|
|
1163
|
+
const writeUrl = buildTerminalUrl(ds, { write: true });
|
|
1173
1164
|
logger.info(`[${t}] Worker ready, terminal at ${readOnlyUrl}`);
|
|
1174
1165
|
if (ds.usageLimit) {
|
|
1175
1166
|
ds.lastScreenStatus = 'limited';
|
|
@@ -1325,7 +1316,7 @@ function setupWorkerHandlers(ds, worker) {
|
|
|
1325
1316
|
// the status patch; just don't touch any Lark card.
|
|
1326
1317
|
if (streamingCardDisabled(ds))
|
|
1327
1318
|
break;
|
|
1328
|
-
const readUrl =
|
|
1319
|
+
const readUrl = buildTerminalUrl(ds);
|
|
1329
1320
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
1330
1321
|
const mode = ds.displayMode ?? 'hidden';
|
|
1331
1322
|
if (ds.streamCardPending || !ds.streamCardId) {
|
|
@@ -1392,7 +1383,7 @@ function setupWorkerHandlers(ds, worker) {
|
|
|
1392
1383
|
break;
|
|
1393
1384
|
if (!ds.streamCardId || ds.streamCardId === CARD_POSTING_SENTINEL || !ds.workerPort)
|
|
1394
1385
|
break;
|
|
1395
|
-
const readUrl =
|
|
1386
|
+
const readUrl = buildTerminalUrl(ds);
|
|
1396
1387
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
1397
1388
|
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', ds.lastScreenStatus, effectiveCliId, 'screenshot', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, cardUsageLimit(ds), writableTerminalLinkFor(ds));
|
|
1398
1389
|
scheduleCardPatch(ds, cardJson);
|
|
@@ -1449,7 +1440,7 @@ function setupWorkerHandlers(ds, worker) {
|
|
|
1449
1440
|
logger.info(`[${t}] Adopted session ended`);
|
|
1450
1441
|
// Freeze the streaming card
|
|
1451
1442
|
if (ds.streamCardId && ds.workerPort) {
|
|
1452
|
-
const readUrl =
|
|
1443
|
+
const readUrl = buildTerminalUrl(ds);
|
|
1453
1444
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
1454
1445
|
const frozenCard = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'idle', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, undefined, writableTerminalLinkFor(ds));
|
|
1455
1446
|
scheduleCardPatch(ds, frozenCard);
|
|
@@ -1481,7 +1472,7 @@ function setupWorkerHandlers(ds, worker) {
|
|
|
1481
1472
|
logger.warn(`[${t}] ${getCliDisplayName(effectiveCliId)} crashed ${rc.count} times in 1 min, not auto-restarting`);
|
|
1482
1473
|
// Freeze the last streaming card so it doesn't stay at "working" forever
|
|
1483
1474
|
if (ds.streamCardId && ds.workerPort) {
|
|
1484
|
-
const readUrl =
|
|
1475
|
+
const readUrl = buildTerminalUrl(ds);
|
|
1485
1476
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
1486
1477
|
const frozenCard = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'idle', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, undefined, writableTerminalLinkFor(ds));
|
|
1487
1478
|
scheduleCardPatch(ds, frozenCard);
|