@threadbase-sh/streamer 1.66.0 → 1.67.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/api/handlers/conversations.handlers.d.ts +29 -0
- package/dist/api/handlers/conversations.handlers.d.ts.map +1 -1
- package/dist/cli.cjs +97 -15
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +74 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +79 -7
- package/dist/index.js.map +1 -1
- package/dist/server-wiring.d.ts +1 -0
- package/dist/server-wiring.d.ts.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -8684,6 +8684,64 @@ var ConversationHandlers = class {
|
|
|
8684
8684
|
}
|
|
8685
8685
|
return null;
|
|
8686
8686
|
}
|
|
8687
|
+
/**
|
|
8688
|
+
* Resolve a conversation id to a JSONL path, in order of authority.
|
|
8689
|
+
*
|
|
8690
|
+
* `findJsonlPath` alone answers 64.0% of this machine's 961 conversations and
|
|
8691
|
+
* 0 of 343 Codex ones — it reconstructs `<projectsDir>/<dir>/<uuid>.jsonl`,
|
|
8692
|
+
* which is Claude Code's layout, and a Codex rollout is
|
|
8693
|
+
* `rollout-<ts>-<uuid>.jsonl` under a date path, so that walk cannot match one
|
|
8694
|
+
* by construction. The ladder below answers 99.7%, leaving only the three
|
|
8695
|
+
* whose file is genuinely gone.
|
|
8696
|
+
*/
|
|
8697
|
+
async locateJsonlPath(uuid, lookupId) {
|
|
8698
|
+
const live = this.deps.findLiveSessionFilePath(uuid) ?? this.deps.findLiveSessionFilePath(lookupId);
|
|
8699
|
+
if (live) return live;
|
|
8700
|
+
const cached3 = this.cache?.getMetaById(lookupId)?.filePath;
|
|
8701
|
+
if (cached3 && await this.isJsonlPathFor(cached3, lookupId)) return cached3;
|
|
8702
|
+
return this.findJsonlPath(lookupId);
|
|
8703
|
+
}
|
|
8704
|
+
/**
|
|
8705
|
+
* Does `filePath` actually hold the conversation `requestedId` names?
|
|
8706
|
+
*
|
|
8707
|
+
* The filename settles it for both providers: Claude writes `<uuid>.jsonl`
|
|
8708
|
+
* (or `agent-<agentId>.jsonl`), Codex writes `rollout-<ts>-<uuid>.jsonl`.
|
|
8709
|
+
* Only when the name says nothing do we open the file — and there the naive
|
|
8710
|
+
* rule is wrong, because **a Claude subagent transcript carries the PARENT's
|
|
8711
|
+
* `sessionId`**. Matching on `sessionId` alone therefore verifies exactly the
|
|
8712
|
+
* file this check exists to reject, so a sidechain is refused outright unless
|
|
8713
|
+
* it was asked for by its own `agent-<agentId>` name, which the filename
|
|
8714
|
+
* branch above already covers.
|
|
8715
|
+
*/
|
|
8716
|
+
async isJsonlPathFor(filePath, requestedId) {
|
|
8717
|
+
if (!(0, import_fs13.existsSync)(filePath)) return false;
|
|
8718
|
+
const stem = (0, import_path13.basename)(filePath).replace(/\.jsonl$/, "");
|
|
8719
|
+
if (stem === requestedId || stem.includes(requestedId)) return true;
|
|
8720
|
+
const first = await this.readFirstJsonlEntry(filePath);
|
|
8721
|
+
if (!first || first.isSidechain === true) return false;
|
|
8722
|
+
return first.sessionId === requestedId;
|
|
8723
|
+
}
|
|
8724
|
+
/** First parseable JSONL line, for identity checks. Null on an empty or unreadable file. */
|
|
8725
|
+
async readFirstJsonlEntry(filePath) {
|
|
8726
|
+
return new Promise((resolve2) => {
|
|
8727
|
+
const rl = (0, import_readline.createInterface)({ input: (0, import_fs13.createReadStream)(filePath), crlfDelay: Infinity });
|
|
8728
|
+
let done = false;
|
|
8729
|
+
rl.on("line", (line) => {
|
|
8730
|
+
if (done) return;
|
|
8731
|
+
try {
|
|
8732
|
+
const entry = JSON.parse(line);
|
|
8733
|
+
done = true;
|
|
8734
|
+
rl.close();
|
|
8735
|
+
resolve2(entry);
|
|
8736
|
+
} catch {
|
|
8737
|
+
}
|
|
8738
|
+
});
|
|
8739
|
+
rl.on("close", () => {
|
|
8740
|
+
if (!done) resolve2(null);
|
|
8741
|
+
});
|
|
8742
|
+
rl.on("error", () => resolve2(null));
|
|
8743
|
+
});
|
|
8744
|
+
}
|
|
8687
8745
|
async readCwdFromJsonl(filePath) {
|
|
8688
8746
|
return new Promise((resolve2) => {
|
|
8689
8747
|
const rl = (0, import_readline.createInterface)({ input: (0, import_fs13.createReadStream)(filePath), crlfDelay: Infinity });
|
|
@@ -8709,7 +8767,7 @@ var ConversationHandlers = class {
|
|
|
8709
8767
|
async findConversationByUuid(uuid) {
|
|
8710
8768
|
const lookupId = this.deps.resolveConversationLookupId(uuid);
|
|
8711
8769
|
if (!this.scannerManager.ready && !this.scanProfiles) {
|
|
8712
|
-
const filePath2 =
|
|
8770
|
+
const filePath2 = await this.locateJsonlPath(uuid, lookupId);
|
|
8713
8771
|
if (filePath2) {
|
|
8714
8772
|
const account = this.cache?.getMetaById(lookupId)?.account ?? void 0;
|
|
8715
8773
|
const coldScanner = this.scannerManager.current ?? this.scannerManager.newScanner();
|
|
@@ -8761,7 +8819,7 @@ var ConversationHandlers = class {
|
|
|
8761
8819
|
return fromIndex;
|
|
8762
8820
|
}
|
|
8763
8821
|
if (this.scanProfiles) return null;
|
|
8764
|
-
const filePath =
|
|
8822
|
+
const filePath = await this.locateJsonlPath(uuid, lookupId);
|
|
8765
8823
|
if (!filePath) return null;
|
|
8766
8824
|
if (this.scannerManager.ready) {
|
|
8767
8825
|
const account = this.cache?.getMetaById(lookupId)?.account ?? void 0;
|
|
@@ -13008,6 +13066,13 @@ function createApiDeps(deps) {
|
|
|
13008
13066
|
);
|
|
13009
13067
|
}
|
|
13010
13068
|
}
|
|
13069
|
+
if (msg.type === "unsubscribe_session" && typeof msg.sessionId === "string") {
|
|
13070
|
+
if (!wsAllows(principal, "history:read")) {
|
|
13071
|
+
deny(msg.type, "history:read");
|
|
13072
|
+
return;
|
|
13073
|
+
}
|
|
13074
|
+
deps.removeSessionSubscriber(msg.sessionId, ws);
|
|
13075
|
+
}
|
|
13011
13076
|
if (msg.type === "hold_session" && typeof msg.sessionId === "string") {
|
|
13012
13077
|
if (!wsAllows(principal, "session:control")) {
|
|
13013
13078
|
deny(msg.type, "session:control");
|
|
@@ -16193,6 +16258,7 @@ var StreamerServer = class {
|
|
|
16193
16258
|
withReconciledLifecycle: (sessions) => this.withReconciledLifecycle(sessions),
|
|
16194
16259
|
currentWarmupState: () => this.currentWarmupState(),
|
|
16195
16260
|
addSessionSubscriber: (sessionId, ws) => this.addSessionSubscriber(sessionId, ws),
|
|
16261
|
+
removeSessionSubscriber: (sessionId, ws) => this.removeSessionSubscriber(sessionId, ws),
|
|
16196
16262
|
startGraceTimer: (sessionId, delayMs) => this.startGraceTimer(sessionId, delayMs),
|
|
16197
16263
|
armHoldWhenIdle: (sessionId) => this.armHoldWhenIdle(sessionId),
|
|
16198
16264
|
handleSessionsCount: (res) => this.handleSessionsCount(res),
|
|
@@ -16322,6 +16388,12 @@ var StreamerServer = class {
|
|
|
16322
16388
|
);
|
|
16323
16389
|
}
|
|
16324
16390
|
}
|
|
16391
|
+
removeSessionSubscriber(sessionId, ws) {
|
|
16392
|
+
const subs = this.sessionSubscribers.get(sessionId);
|
|
16393
|
+
if (!subs) return;
|
|
16394
|
+
subs.delete(ws);
|
|
16395
|
+
if (subs.size === 0) this.sessionSubscribers.delete(sessionId);
|
|
16396
|
+
}
|
|
16325
16397
|
/**
|
|
16326
16398
|
* Bring up Live Activity push, if credentials are present (Feature 12).
|
|
16327
16399
|
*
|