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.
Files changed (44) hide show
  1. package/dist/adapters/cli/coco.js +3 -3
  2. package/dist/adapters/cli/coco.js.map +1 -1
  3. package/dist/adapters/cli/codex.d.ts.map +1 -1
  4. package/dist/adapters/cli/codex.js +15 -0
  5. package/dist/adapters/cli/codex.js.map +1 -1
  6. package/dist/adapters/cli/types.d.ts +6 -5
  7. package/dist/adapters/cli/types.d.ts.map +1 -1
  8. package/dist/config.d.ts +1 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +4 -0
  11. package/dist/config.js.map +1 -1
  12. package/dist/core/command-handler.d.ts.map +1 -1
  13. package/dist/core/command-handler.js +2 -1
  14. package/dist/core/command-handler.js.map +1 -1
  15. package/dist/core/dashboard-rows.d.ts +3 -0
  16. package/dist/core/dashboard-rows.d.ts.map +1 -1
  17. package/dist/core/dashboard-rows.js +2 -0
  18. package/dist/core/dashboard-rows.js.map +1 -1
  19. package/dist/core/terminal-proxy.d.ts +32 -0
  20. package/dist/core/terminal-proxy.d.ts.map +1 -0
  21. package/dist/core/terminal-proxy.js +139 -0
  22. package/dist/core/terminal-proxy.js.map +1 -0
  23. package/dist/core/terminal-url.d.ts +27 -0
  24. package/dist/core/terminal-url.d.ts.map +1 -0
  25. package/dist/core/terminal-url.js +26 -0
  26. package/dist/core/terminal-url.js.map +1 -0
  27. package/dist/core/worker-pool.d.ts.map +1 -1
  28. package/dist/core/worker-pool.js +11 -20
  29. package/dist/core/worker-pool.js.map +1 -1
  30. package/dist/daemon.d.ts.map +1 -1
  31. package/dist/daemon.js +31 -1
  32. package/dist/daemon.js.map +1 -1
  33. package/dist/dashboard/web/sessions.js +1 -1
  34. package/dist/dashboard/web/sessions.js.map +1 -1
  35. package/dist/dashboard-web/app.js +1 -1
  36. package/dist/im/lark/card-handler.d.ts.map +1 -1
  37. package/dist/im/lark/card-handler.js +8 -8
  38. package/dist/im/lark/card-handler.js.map +1 -1
  39. package/dist/services/codex-bridge-queue.d.ts.map +1 -1
  40. package/dist/services/codex-bridge-queue.js +69 -39
  41. package/dist/services/codex-bridge-queue.js.map +1 -1
  42. package/dist/worker.js +62 -78
  43. package/dist/worker.js.map +1 -1
  44. 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":"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"}
1
+ {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/core/worker-pool.ts"],"names":[],"mappings":"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"}
@@ -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 terminalWriteUrl(ds.workerPort, ds.workerToken);
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 = terminalReadUrl(port);
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 = terminalReadUrl(port);
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 = terminalReadUrl(port);
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 = terminalReadUrl(msg.port);
1172
- const writeUrl = terminalWriteUrl(msg.port, msg.token);
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 = `http://${config.web.externalHost}:${ds.workerPort}`;
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 = `http://${config.web.externalHost}:${ds.workerPort}`;
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 = `http://${config.web.externalHost}:${ds.workerPort}`;
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 = `http://${config.web.externalHost}:${ds.workerPort}`;
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);