@threadbase-sh/streamer 1.30.0 → 1.31.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/cli.cjs +85 -91
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +70 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -7
- package/dist/index.d.ts +18 -7
- package/dist/index.js +70 -88
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -137480,7 +137480,7 @@ function isLocalRequest(remoteAddr) {
|
|
|
137480
137480
|
const addr = remoteAddr ?? "";
|
|
137481
137481
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
137482
137482
|
}
|
|
137483
|
-
var PUBLIC_PATHS = /* @__PURE__ */ new Set(["/healthz"
|
|
137483
|
+
var PUBLIC_PATHS = /* @__PURE__ */ new Set(["/healthz"]);
|
|
137484
137484
|
var LOCAL_ONLY_PATHS = /* @__PURE__ */ new Set(["/api/logs", "/api/logs/meta"]);
|
|
137485
137485
|
var PUBLIC_POST_PATHS = /* @__PURE__ */ new Set(["/api/pair/exchange", "/api/__update"]);
|
|
137486
137486
|
var PUBLIC_POST_PREFIXES = ["/internal/sessions/"];
|
|
@@ -137935,7 +137935,7 @@ function verifySignature(rawBody, signature, secret) {
|
|
|
137935
137935
|
}
|
|
137936
137936
|
}
|
|
137937
137937
|
function isWithinSkew(timestampHeader, skewSeconds) {
|
|
137938
|
-
if (!timestampHeader) return
|
|
137938
|
+
if (!timestampHeader) return true;
|
|
137939
137939
|
const t = Number(timestampHeader);
|
|
137940
137940
|
if (!Number.isFinite(t)) return false;
|
|
137941
137941
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -138146,21 +138146,18 @@ var createSessionRoutes = (deps) => {
|
|
|
138146
138146
|
};
|
|
138147
138147
|
|
|
138148
138148
|
// src/api/routes/ws.routes.ts
|
|
138149
|
-
init_auth();
|
|
138150
138149
|
var createWsRoutes = (deps, upgradeWebSocket) => {
|
|
138151
138150
|
const app = new Hono2();
|
|
138152
138151
|
app.get(
|
|
138153
138152
|
"/ws",
|
|
138154
|
-
upgradeWebSocket((
|
|
138155
|
-
const key = c.req.query("key");
|
|
138156
|
-
const preAuthed = typeof key === "string" && validateApiKey(key, deps.apiKey);
|
|
138153
|
+
upgradeWebSocket(() => {
|
|
138157
138154
|
let openWs = null;
|
|
138158
138155
|
return {
|
|
138159
138156
|
onOpen(_evt, ws2) {
|
|
138160
138157
|
const raw2 = ws2.raw;
|
|
138161
138158
|
if (!raw2) return;
|
|
138162
138159
|
openWs = raw2;
|
|
138163
|
-
deps.handleWsOpen(raw2
|
|
138160
|
+
deps.handleWsOpen(raw2);
|
|
138164
138161
|
},
|
|
138165
138162
|
onMessage(evt, _ws) {
|
|
138166
138163
|
if (openWs) deps.handleWsMessage(openWs, evt.data);
|
|
@@ -140064,6 +140061,7 @@ function debounce(fn, waitMs) {
|
|
|
140064
140061
|
|
|
140065
140062
|
// src/codex-pty-runner.ts
|
|
140066
140063
|
var OUTPUT_BUFFER_MAX = 65536;
|
|
140064
|
+
var INPUT_HISTORY_MAX = 50;
|
|
140067
140065
|
var PTY_COLS = 120;
|
|
140068
140066
|
var PTY_ROWS = 40;
|
|
140069
140067
|
var SCREEN_SCROLLBACK = 1e3;
|
|
@@ -140144,6 +140142,7 @@ var CodexPtyRunner = class {
|
|
|
140144
140142
|
onPermissionChange;
|
|
140145
140143
|
onLiveQuestion;
|
|
140146
140144
|
onLiveQuestionGone;
|
|
140145
|
+
onUserMessage;
|
|
140147
140146
|
log;
|
|
140148
140147
|
// Tracks sessions whose PTY has spawned but Codex hasn't yet reached its
|
|
140149
140148
|
// "Ready" status bar — i.e. onReady hasn't fired.
|
|
@@ -140174,6 +140173,7 @@ var CodexPtyRunner = class {
|
|
|
140174
140173
|
this.onPermissionChange = options.onPermissionChange;
|
|
140175
140174
|
this.onLiveQuestion = options.onLiveQuestion;
|
|
140176
140175
|
this.onLiveQuestionGone = options.onLiveQuestionGone;
|
|
140176
|
+
this.onUserMessage = options.onUserMessage;
|
|
140177
140177
|
this.log = options.logger ?? getLogger("codex-pty");
|
|
140178
140178
|
}
|
|
140179
140179
|
// Resume an existing Codex session. sessionId is the Codex-persisted
|
|
@@ -140217,7 +140217,8 @@ var CodexPtyRunner = class {
|
|
|
140217
140217
|
lastOutput: "",
|
|
140218
140218
|
process: proc,
|
|
140219
140219
|
outputBuffer: Buffer.alloc(0),
|
|
140220
|
-
screen: createScreen()
|
|
140220
|
+
screen: createScreen(),
|
|
140221
|
+
inputHistory: []
|
|
140221
140222
|
};
|
|
140222
140223
|
this.sessions.set(sessionId, session);
|
|
140223
140224
|
this.pendingReady.add(sessionId);
|
|
@@ -140263,7 +140264,8 @@ var CodexPtyRunner = class {
|
|
|
140263
140264
|
lastOutput: "",
|
|
140264
140265
|
process: proc,
|
|
140265
140266
|
outputBuffer: Buffer.alloc(0),
|
|
140266
|
-
screen: createScreen()
|
|
140267
|
+
screen: createScreen(),
|
|
140268
|
+
inputHistory: []
|
|
140267
140269
|
};
|
|
140268
140270
|
this.sessions.set(sessionId, session);
|
|
140269
140271
|
this.pendingReady.add(sessionId);
|
|
@@ -140381,6 +140383,7 @@ var CodexPtyRunner = class {
|
|
|
140381
140383
|
// confirmed Codex accepts plain keystrokes), then submit \r after a short
|
|
140382
140384
|
// delay so Codex's TUI gets an event-loop tick to process the input first.
|
|
140383
140385
|
writeSubmit(sessionId, session, input, path2, promptCount) {
|
|
140386
|
+
this.recordUserMessage(session, input);
|
|
140384
140387
|
this.log.info(
|
|
140385
140388
|
`[codex.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${input.length} digest=${digestBytes(input)}`,
|
|
140386
140389
|
{
|
|
@@ -140510,6 +140513,19 @@ var CodexPtyRunner = class {
|
|
|
140510
140513
|
}
|
|
140511
140514
|
return lines.slice(-maxLines);
|
|
140512
140515
|
}
|
|
140516
|
+
getInputHistory(sessionId) {
|
|
140517
|
+
return this.sessions.get(sessionId)?.inputHistory ?? [];
|
|
140518
|
+
}
|
|
140519
|
+
// Record a submitted user message as ground truth and fire onUserMessage.
|
|
140520
|
+
// Called from writeSubmit (direct and flush paths) — never from sendKeys.
|
|
140521
|
+
recordUserMessage(session, text) {
|
|
140522
|
+
const ts2 = Date.now();
|
|
140523
|
+
session.inputHistory.push({ text, ts: ts2 });
|
|
140524
|
+
if (session.inputHistory.length > INPUT_HISTORY_MAX) {
|
|
140525
|
+
session.inputHistory.shift();
|
|
140526
|
+
}
|
|
140527
|
+
this.onUserMessage?.(session.id, text, ts2);
|
|
140528
|
+
}
|
|
140513
140529
|
getSession(sessionId) {
|
|
140514
140530
|
const session = this.sessions.get(sessionId);
|
|
140515
140531
|
return session ? toPublicSession(session) : null;
|
|
@@ -140888,6 +140904,7 @@ function detectShellPrompt(lines) {
|
|
|
140888
140904
|
|
|
140889
140905
|
// src/pty-manager.ts
|
|
140890
140906
|
var OUTPUT_BUFFER_MAX2 = 65536;
|
|
140907
|
+
var INPUT_HISTORY_MAX2 = 50;
|
|
140891
140908
|
var PTY_COLS2 = 120;
|
|
140892
140909
|
var PTY_ROWS2 = 40;
|
|
140893
140910
|
var SCREEN_SCROLLBACK2 = 1e3;
|
|
@@ -140946,6 +140963,7 @@ var PTYManager = class {
|
|
|
140946
140963
|
onPermissionChange;
|
|
140947
140964
|
onLiveQuestion;
|
|
140948
140965
|
onLiveQuestionGone;
|
|
140966
|
+
onUserMessage;
|
|
140949
140967
|
// Per-session permission-gate state. True between an OSC 777 (gate open) and
|
|
140950
140968
|
// the next prompt-ready without a fresh 777 (gate closed). Prevents
|
|
140951
140969
|
// re-broadcasting open/close on every chunk.
|
|
@@ -140991,6 +141009,7 @@ var PTYManager = class {
|
|
|
140991
141009
|
this.onPermissionChange = options.onPermissionChange;
|
|
140992
141010
|
this.onLiveQuestion = options.onLiveQuestion;
|
|
140993
141011
|
this.onLiveQuestionGone = options.onLiveQuestionGone;
|
|
141012
|
+
this.onUserMessage = options.onUserMessage;
|
|
140994
141013
|
this.log = options.logger ?? getLogger("pty");
|
|
140995
141014
|
}
|
|
140996
141015
|
// Resume an existing Claude conversation. sessionId is the JSONL UUID.
|
|
@@ -141053,7 +141072,8 @@ var PTYManager = class {
|
|
|
141053
141072
|
lastOutput: "",
|
|
141054
141073
|
process: proc,
|
|
141055
141074
|
outputBuffer: Buffer.alloc(0),
|
|
141056
|
-
screen: createScreen2()
|
|
141075
|
+
screen: createScreen2(),
|
|
141076
|
+
inputHistory: []
|
|
141057
141077
|
};
|
|
141058
141078
|
this.sessions.set(sessionId, session);
|
|
141059
141079
|
this.pendingReady.add(sessionId);
|
|
@@ -141104,7 +141124,8 @@ var PTYManager = class {
|
|
|
141104
141124
|
lastOutput: "",
|
|
141105
141125
|
process: proc,
|
|
141106
141126
|
outputBuffer: Buffer.alloc(0),
|
|
141107
|
-
screen: createScreen2()
|
|
141127
|
+
screen: createScreen2(),
|
|
141128
|
+
inputHistory: []
|
|
141108
141129
|
};
|
|
141109
141130
|
this.sessions.set(sessionId, session);
|
|
141110
141131
|
this.pendingReady.add(sessionId);
|
|
@@ -141184,6 +141205,7 @@ var PTYManager = class {
|
|
|
141184
141205
|
// step gives the TUI as many extra ticks as it needs, capped at
|
|
141185
141206
|
// SUBMIT_MAX_WAIT_MS so a silent/wedged PTY still gets its \r eventually.
|
|
141186
141207
|
writeSubmit(sessionId, session, input, path2, promptCount) {
|
|
141208
|
+
this.recordUserMessage(session, input);
|
|
141187
141209
|
const pasteBytes = buildPasteBytes(input);
|
|
141188
141210
|
this.log.info(
|
|
141189
141211
|
`[pty.input.write] ${sessionId.slice(0, 8)} promptCount=${promptCount} bytes=${pasteBytes.length} digest=${digestBytes2(pasteBytes)}`,
|
|
@@ -141314,6 +141336,20 @@ var PTYManager = class {
|
|
|
141314
141336
|
}
|
|
141315
141337
|
return lines.slice(-maxLines);
|
|
141316
141338
|
}
|
|
141339
|
+
getInputHistory(sessionId) {
|
|
141340
|
+
return this.sessions.get(sessionId)?.inputHistory ?? [];
|
|
141341
|
+
}
|
|
141342
|
+
// Record a submitted user message as ground truth and fire onUserMessage.
|
|
141343
|
+
// Called from writeSubmit (both direct and flush paths) — never from
|
|
141344
|
+
// sendKeys, so raw keystrokes aren't logged as messages.
|
|
141345
|
+
recordUserMessage(session, text) {
|
|
141346
|
+
const ts2 = Date.now();
|
|
141347
|
+
session.inputHistory.push({ text, ts: ts2 });
|
|
141348
|
+
if (session.inputHistory.length > INPUT_HISTORY_MAX2) {
|
|
141349
|
+
session.inputHistory.shift();
|
|
141350
|
+
}
|
|
141351
|
+
this.onUserMessage?.(session.id, text, ts2);
|
|
141352
|
+
}
|
|
141317
141353
|
getSession(sessionId) {
|
|
141318
141354
|
const session = this.sessions.get(sessionId);
|
|
141319
141355
|
return session ? toPublicSession2(session) : null;
|
|
@@ -141610,6 +141646,9 @@ var LiveSessionManager = class {
|
|
|
141610
141646
|
getOutputLines(sessionId, maxLines) {
|
|
141611
141647
|
return this.runnerFor(sessionId).getOutputLines(sessionId, maxLines);
|
|
141612
141648
|
}
|
|
141649
|
+
getInputHistory(sessionId) {
|
|
141650
|
+
return this.runnerFor(sessionId).getInputHistory(sessionId);
|
|
141651
|
+
}
|
|
141613
141652
|
getSession(sessionId) {
|
|
141614
141653
|
for (const runner of this.runners.values()) {
|
|
141615
141654
|
const session = runner.getSession(sessionId);
|
|
@@ -143833,13 +143872,6 @@ function deriveProjectChatTitle(input) {
|
|
|
143833
143872
|
return `Untitled \xB7 ${input.id.slice(0, 8)}`;
|
|
143834
143873
|
}
|
|
143835
143874
|
|
|
143836
|
-
// src/services/questions/permissionAnswerKeys.ts
|
|
143837
|
-
var ANSWER_KEYS_ALLOWLIST = /^(?:\r|[yn]\r|\x03|\d+\r)$/;
|
|
143838
|
-
function sanitizeAnswerKeys(keys) {
|
|
143839
|
-
if (keys === void 0) return void 0;
|
|
143840
|
-
return ANSWER_KEYS_ALLOWLIST.test(keys) ? keys : void 0;
|
|
143841
|
-
}
|
|
143842
|
-
|
|
143843
143875
|
// src/services/questions/detectAskUserQuestion.ts
|
|
143844
143876
|
function normalizeContent2(raw2) {
|
|
143845
143877
|
if (Array.isArray(raw2)) return raw2;
|
|
@@ -144682,8 +144714,6 @@ var WSHub = class {
|
|
|
144682
144714
|
var BROWSE_SYSTEM_PROMPT = (browseRoot) => `You are working within the project boundary: ${browseRoot}. Do not read, write, or execute commands that access files or directories outside this boundary.`;
|
|
144683
144715
|
var DEFAULT_SYSTEM_PROMPT = "When presenting options or choices to the user, limit the options to at most 3.";
|
|
144684
144716
|
var DEFAULT_PTY_GRACE_PERIOD_MS = 27e4;
|
|
144685
|
-
var DEFAULT_WS_AUTH_TIMEOUT_MS = 5e3;
|
|
144686
|
-
var WS_CLOSE_UNAUTHORIZED = 4401;
|
|
144687
144717
|
var REFRESH_TTL_MS = 2e3;
|
|
144688
144718
|
var START_READY_TIMEOUT_MS = 1e4;
|
|
144689
144719
|
function parseIncludeAgentsEnv(raw2) {
|
|
@@ -144774,14 +144804,6 @@ var StreamerServer = class {
|
|
|
144774
144804
|
clientIdToWs = /* @__PURE__ */ new Map();
|
|
144775
144805
|
// Reverse map for cleanup on close
|
|
144776
144806
|
wsToClientId = /* @__PURE__ */ new Map();
|
|
144777
|
-
// M1 — WS auth. Sockets that have authenticated (via ?key= at upgrade OR a
|
|
144778
|
-
// { type: "auth", token } first message). Only authed sockets are added to
|
|
144779
|
-
// the hub and receive broadcasts.
|
|
144780
|
-
// Plan: https://github.com/RonenMars/threadbase-streamer/blob/a251353bfa417bd48ce3f15086bc336a2c622629/docs/plans/2026-06-24-security-hardening.md#L40
|
|
144781
|
-
wsAuthed = /* @__PURE__ */ new Set();
|
|
144782
|
-
// Keyless sockets awaiting their first-message auth handshake → close timer.
|
|
144783
|
-
wsAuthPending = /* @__PURE__ */ new Map();
|
|
144784
|
-
wsAuthTimeoutMs;
|
|
144785
144807
|
cache = null;
|
|
144786
144808
|
projectsRepo = null;
|
|
144787
144809
|
conversationsRepo = null;
|
|
@@ -144821,7 +144843,6 @@ var StreamerServer = class {
|
|
|
144821
144843
|
this.scanProfiles = config2.scanProfiles;
|
|
144822
144844
|
this.codexRoots = config2.codexRoots ?? [(0, import_path23.join)((0, import_os11.homedir)(), ".codex", "sessions")];
|
|
144823
144845
|
this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
144824
|
-
this.wsAuthTimeoutMs = config2.wsAuthTimeoutMs ?? DEFAULT_WS_AUTH_TIMEOUT_MS;
|
|
144825
144846
|
this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
144826
144847
|
this.defaultPermissionMode = config2.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
|
|
144827
144848
|
this.cacheDir = config2.cacheDir ?? loadCacheDir() ?? (0, import_path23.join)((0, import_os11.homedir)(), ".threadbase", "cache");
|
|
@@ -144950,6 +144971,9 @@ var StreamerServer = class {
|
|
|
144950
144971
|
onOutput: (sessionId, data) => {
|
|
144951
144972
|
this.wsHub.broadcast({ type: "terminal_output", sessionId, data });
|
|
144952
144973
|
},
|
|
144974
|
+
onUserMessage: (sessionId, text, ts2) => {
|
|
144975
|
+
this.wsHub.broadcast({ type: "user_message", sessionId, text, ts: ts2 });
|
|
144976
|
+
},
|
|
144953
144977
|
onPermissionChange: (sessionId, gate) => {
|
|
144954
144978
|
this.handlePermissionChange(sessionId, gate);
|
|
144955
144979
|
},
|
|
@@ -145079,39 +145103,17 @@ var StreamerServer = class {
|
|
|
145079
145103
|
handlePairExchange: (req, res) => this.handlePairExchange(req, res),
|
|
145080
145104
|
handleBrowse: (url2, res) => this.handleBrowse(url2, res),
|
|
145081
145105
|
handleMkdir: (req, res) => this.handleMkdir(req, res),
|
|
145082
|
-
handleWsOpen: (ws2
|
|
145083
|
-
|
|
145084
|
-
|
|
145085
|
-
|
|
145106
|
+
handleWsOpen: (ws2) => {
|
|
145107
|
+
this.wsHub.addClient(ws2);
|
|
145108
|
+
const sessions = this.sessionStore.list(this.ptyAttachedIds());
|
|
145109
|
+
ws2.send(JSON.stringify({ type: "session_list", sessions }));
|
|
145110
|
+
if (this.cacheReady) {
|
|
145111
|
+
ws2.send(JSON.stringify({ type: "cache_ready" }));
|
|
145086
145112
|
}
|
|
145087
|
-
const timer = setTimeout(() => {
|
|
145088
|
-
this.wsAuthPending.delete(ws2);
|
|
145089
|
-
try {
|
|
145090
|
-
ws2.close(WS_CLOSE_UNAUTHORIZED, "auth timeout");
|
|
145091
|
-
} catch {
|
|
145092
|
-
}
|
|
145093
|
-
}, this.wsAuthTimeoutMs);
|
|
145094
|
-
this.wsAuthPending.set(ws2, timer);
|
|
145095
145113
|
},
|
|
145096
145114
|
handleWsMessage: async (ws2, raw2) => {
|
|
145097
145115
|
try {
|
|
145098
145116
|
const msg = JSON.parse(String(raw2));
|
|
145099
|
-
if (!this.wsAuthed.has(ws2)) {
|
|
145100
|
-
if (msg.type === "auth" && typeof msg.token === "string") {
|
|
145101
|
-
const t = this.wsAuthPending.get(ws2);
|
|
145102
|
-
if (t) clearTimeout(t);
|
|
145103
|
-
this.wsAuthPending.delete(ws2);
|
|
145104
|
-
if (validateApiKey(msg.token, this.apiKey)) {
|
|
145105
|
-
this.completeWsAuth(ws2);
|
|
145106
|
-
} else {
|
|
145107
|
-
try {
|
|
145108
|
-
ws2.close(WS_CLOSE_UNAUTHORIZED, "unauthorized");
|
|
145109
|
-
} catch {
|
|
145110
|
-
}
|
|
145111
|
-
}
|
|
145112
|
-
}
|
|
145113
|
-
return;
|
|
145114
|
-
}
|
|
145115
145117
|
if (msg.type === "register" && typeof msg.clientId === "string") {
|
|
145116
145118
|
const oldClientId = this.wsToClientId.get(ws2);
|
|
145117
145119
|
if (oldClientId) this.clientIdToWs.delete(oldClientId);
|
|
@@ -145122,7 +145124,15 @@ var StreamerServer = class {
|
|
|
145122
145124
|
this.addSessionSubscriber(msg.sessionId, ws2);
|
|
145123
145125
|
if (this.ptyManager.hasSession(msg.sessionId)) {
|
|
145124
145126
|
const lines = await this.ptyManager.getOutputLines(msg.sessionId, 200);
|
|
145125
|
-
|
|
145127
|
+
const userMessages = this.ptyManager.getInputHistory(msg.sessionId);
|
|
145128
|
+
ws2.send(
|
|
145129
|
+
JSON.stringify({
|
|
145130
|
+
type: "terminal_replay",
|
|
145131
|
+
sessionId: msg.sessionId,
|
|
145132
|
+
lines,
|
|
145133
|
+
userMessages
|
|
145134
|
+
})
|
|
145135
|
+
);
|
|
145126
145136
|
}
|
|
145127
145137
|
const pendingGate = this.pendingPermission.get(msg.sessionId);
|
|
145128
145138
|
if (pendingGate) {
|
|
@@ -145158,20 +145168,12 @@ var StreamerServer = class {
|
|
|
145158
145168
|
}
|
|
145159
145169
|
}
|
|
145160
145170
|
if (msg.type === "hold_session" && typeof msg.sessionId === "string") {
|
|
145161
|
-
|
|
145162
|
-
this.startGraceTimer(msg.sessionId, 0);
|
|
145163
|
-
}
|
|
145171
|
+
this.startGraceTimer(msg.sessionId, 0);
|
|
145164
145172
|
}
|
|
145165
145173
|
} catch {
|
|
145166
145174
|
}
|
|
145167
145175
|
},
|
|
145168
145176
|
handleWsClose: (ws2) => {
|
|
145169
|
-
const pendingTimer = this.wsAuthPending.get(ws2);
|
|
145170
|
-
if (pendingTimer) {
|
|
145171
|
-
clearTimeout(pendingTimer);
|
|
145172
|
-
this.wsAuthPending.delete(ws2);
|
|
145173
|
-
}
|
|
145174
|
-
this.wsAuthed.delete(ws2);
|
|
145175
145177
|
const clientId = this.wsToClientId.get(ws2);
|
|
145176
145178
|
if (clientId) {
|
|
145177
145179
|
this.clientIdToWs.delete(clientId);
|
|
@@ -145241,20 +145243,6 @@ var StreamerServer = class {
|
|
|
145241
145243
|
this.wsHub.broadcast(payload);
|
|
145242
145244
|
}
|
|
145243
145245
|
}
|
|
145244
|
-
// M1: finalize a WebSocket auth (via ?key= at upgrade or a first-message
|
|
145245
|
-
// handshake) — register it with the hub and send the initial snapshot. Only
|
|
145246
|
-
// authed sockets reach this, so no unauthenticated client ever receives a
|
|
145247
|
-
// broadcast.
|
|
145248
|
-
// Plan: https://github.com/RonenMars/threadbase-streamer/blob/a251353bfa417bd48ce3f15086bc336a2c622629/docs/plans/2026-06-24-security-hardening.md#L40
|
|
145249
|
-
completeWsAuth(ws2) {
|
|
145250
|
-
this.wsAuthed.add(ws2);
|
|
145251
|
-
this.wsHub.addClient(ws2);
|
|
145252
|
-
const sessions = this.sessionStore.list(this.ptyAttachedIds());
|
|
145253
|
-
ws2.send(JSON.stringify({ type: "session_list", sessions }));
|
|
145254
|
-
if (this.cacheReady) {
|
|
145255
|
-
ws2.send(JSON.stringify({ type: "cache_ready" }));
|
|
145256
|
-
}
|
|
145257
|
-
}
|
|
145258
145246
|
addSessionSubscriber(sessionId, ws2) {
|
|
145259
145247
|
let subs = this.sessionSubscribers.get(sessionId);
|
|
145260
145248
|
if (!subs) {
|
|
@@ -145541,9 +145529,6 @@ var StreamerServer = class {
|
|
|
145541
145529
|
this.ptyManager.dispose();
|
|
145542
145530
|
this.fileWatcher.dispose();
|
|
145543
145531
|
this.wsHub.dispose();
|
|
145544
|
-
for (const timer of this.wsAuthPending.values()) clearTimeout(timer);
|
|
145545
|
-
this.wsAuthPending.clear();
|
|
145546
|
-
this.wsAuthed.clear();
|
|
145547
145532
|
this.pairTokens.dispose();
|
|
145548
145533
|
if (this.dbPool) {
|
|
145549
145534
|
await this.dbPool.end();
|
|
@@ -146670,10 +146655,6 @@ var StreamerServer = class {
|
|
|
146670
146655
|
return;
|
|
146671
146656
|
}
|
|
146672
146657
|
this.pendingPermission.set(sessionId, gate);
|
|
146673
|
-
const safeOptions = gate.options.map((o) => {
|
|
146674
|
-
const answerKeys = sanitizeAnswerKeys(o.answerKeys);
|
|
146675
|
-
return answerKeys === void 0 ? { index: o.index, label: o.label } : { ...o, answerKeys };
|
|
146676
|
-
});
|
|
146677
146658
|
const subscriberCount = this.sessionSubscribers.get(sessionId)?.size ?? 0;
|
|
146678
146659
|
this.log.info(
|
|
146679
146660
|
`[ws.broadcast_permission] ${sessionId.slice(0, 8)} subscribers=${subscriberCount}`,
|
|
@@ -146684,7 +146665,7 @@ var StreamerServer = class {
|
|
|
146684
146665
|
sessionId,
|
|
146685
146666
|
...gate.prompt ? { prompt: gate.prompt } : {},
|
|
146686
146667
|
...gate.detail ? { detail: gate.detail } : {},
|
|
146687
|
-
options:
|
|
146668
|
+
options: gate.options,
|
|
146688
146669
|
...gate.cursor !== void 0 ? { cursor: gate.cursor } : {}
|
|
146689
146670
|
});
|
|
146690
146671
|
}
|
|
@@ -147782,8 +147763,11 @@ var import_node_path13 = require("path");
|
|
|
147782
147763
|
function stampVersionTxt(destDir, version2) {
|
|
147783
147764
|
const distDir = (0, import_node_path13.join)(destDir, "dist");
|
|
147784
147765
|
(0, import_node_fs10.mkdirSync)(distDir, { recursive: true });
|
|
147785
|
-
(0, import_node_fs10.writeFileSync)((0, import_node_path13.join)(distDir, "version.txt"),
|
|
147786
|
-
|
|
147766
|
+
(0, import_node_fs10.writeFileSync)((0, import_node_path13.join)(distDir, "version.txt"), versionStamp(version2));
|
|
147767
|
+
}
|
|
147768
|
+
function versionStamp(version2) {
|
|
147769
|
+
return `${version2}+update
|
|
147770
|
+
`;
|
|
147787
147771
|
}
|
|
147788
147772
|
|
|
147789
147773
|
// src/updater/swap.ts
|
|
@@ -147805,6 +147789,7 @@ function swapCurrent(version2) {
|
|
|
147805
147789
|
}
|
|
147806
147790
|
(0, import_node_fs11.renameSync)(tmp, CURRENT_SYMLINK);
|
|
147807
147791
|
(0, import_node_fs11.copyFileSync)((0, import_node_path14.join)(CURRENT_SYMLINK, "dist", "cli.cjs"), (0, import_node_path14.join)(THREADBASE_ROOT, "cli.js"));
|
|
147792
|
+
publishVersionTxt(target, version2);
|
|
147808
147793
|
return;
|
|
147809
147794
|
}
|
|
147810
147795
|
const tmpLink = `${CURRENT_SYMLINK}.new`;
|
|
@@ -147818,6 +147803,15 @@ function swapCurrent(version2) {
|
|
|
147818
147803
|
if ((0, import_node_fs11.existsSync)(tmpCliJs) || lstatSafeIsSymlink(tmpCliJs)) (0, import_node_fs11.unlinkSync)(tmpCliJs);
|
|
147819
147804
|
(0, import_node_fs11.symlinkSync)((0, import_node_path14.join)(target, "dist", "cli.cjs"), tmpCliJs);
|
|
147820
147805
|
(0, import_node_fs11.renameSync)(tmpCliJs, cliJs);
|
|
147806
|
+
publishVersionTxt(target, version2);
|
|
147807
|
+
}
|
|
147808
|
+
function publishVersionTxt(target, version2) {
|
|
147809
|
+
const dest = (0, import_node_path14.join)(THREADBASE_ROOT, "version.txt");
|
|
147810
|
+
try {
|
|
147811
|
+
(0, import_node_fs11.copyFileSync)((0, import_node_path14.join)(target, "dist", "version.txt"), dest);
|
|
147812
|
+
} catch {
|
|
147813
|
+
(0, import_node_fs11.writeFileSync)(dest, versionStamp(version2));
|
|
147814
|
+
}
|
|
147821
147815
|
}
|
|
147822
147816
|
function lstatSafeIsSymlink(path2) {
|
|
147823
147817
|
try {
|