@threadbase-sh/streamer 1.67.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 +83 -15
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +60 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +65 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6477,7 +6477,7 @@ import {
|
|
|
6477
6477
|
search
|
|
6478
6478
|
} from "@threadbase-sh/scanner";
|
|
6479
6479
|
import { createReadStream, existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
|
|
6480
|
-
import { join as join12 } from "path";
|
|
6480
|
+
import { basename as basename5, join as join12 } from "path";
|
|
6481
6481
|
import { createInterface } from "readline";
|
|
6482
6482
|
|
|
6483
6483
|
// src/conversation-cache.ts
|
|
@@ -8644,6 +8644,64 @@ var ConversationHandlers = class {
|
|
|
8644
8644
|
}
|
|
8645
8645
|
return null;
|
|
8646
8646
|
}
|
|
8647
|
+
/**
|
|
8648
|
+
* Resolve a conversation id to a JSONL path, in order of authority.
|
|
8649
|
+
*
|
|
8650
|
+
* `findJsonlPath` alone answers 64.0% of this machine's 961 conversations and
|
|
8651
|
+
* 0 of 343 Codex ones — it reconstructs `<projectsDir>/<dir>/<uuid>.jsonl`,
|
|
8652
|
+
* which is Claude Code's layout, and a Codex rollout is
|
|
8653
|
+
* `rollout-<ts>-<uuid>.jsonl` under a date path, so that walk cannot match one
|
|
8654
|
+
* by construction. The ladder below answers 99.7%, leaving only the three
|
|
8655
|
+
* whose file is genuinely gone.
|
|
8656
|
+
*/
|
|
8657
|
+
async locateJsonlPath(uuid, lookupId) {
|
|
8658
|
+
const live = this.deps.findLiveSessionFilePath(uuid) ?? this.deps.findLiveSessionFilePath(lookupId);
|
|
8659
|
+
if (live) return live;
|
|
8660
|
+
const cached3 = this.cache?.getMetaById(lookupId)?.filePath;
|
|
8661
|
+
if (cached3 && await this.isJsonlPathFor(cached3, lookupId)) return cached3;
|
|
8662
|
+
return this.findJsonlPath(lookupId);
|
|
8663
|
+
}
|
|
8664
|
+
/**
|
|
8665
|
+
* Does `filePath` actually hold the conversation `requestedId` names?
|
|
8666
|
+
*
|
|
8667
|
+
* The filename settles it for both providers: Claude writes `<uuid>.jsonl`
|
|
8668
|
+
* (or `agent-<agentId>.jsonl`), Codex writes `rollout-<ts>-<uuid>.jsonl`.
|
|
8669
|
+
* Only when the name says nothing do we open the file — and there the naive
|
|
8670
|
+
* rule is wrong, because **a Claude subagent transcript carries the PARENT's
|
|
8671
|
+
* `sessionId`**. Matching on `sessionId` alone therefore verifies exactly the
|
|
8672
|
+
* file this check exists to reject, so a sidechain is refused outright unless
|
|
8673
|
+
* it was asked for by its own `agent-<agentId>` name, which the filename
|
|
8674
|
+
* branch above already covers.
|
|
8675
|
+
*/
|
|
8676
|
+
async isJsonlPathFor(filePath, requestedId) {
|
|
8677
|
+
if (!existsSync8(filePath)) return false;
|
|
8678
|
+
const stem = basename5(filePath).replace(/\.jsonl$/, "");
|
|
8679
|
+
if (stem === requestedId || stem.includes(requestedId)) return true;
|
|
8680
|
+
const first = await this.readFirstJsonlEntry(filePath);
|
|
8681
|
+
if (!first || first.isSidechain === true) return false;
|
|
8682
|
+
return first.sessionId === requestedId;
|
|
8683
|
+
}
|
|
8684
|
+
/** First parseable JSONL line, for identity checks. Null on an empty or unreadable file. */
|
|
8685
|
+
async readFirstJsonlEntry(filePath) {
|
|
8686
|
+
return new Promise((resolve2) => {
|
|
8687
|
+
const rl = createInterface({ input: createReadStream(filePath), crlfDelay: Infinity });
|
|
8688
|
+
let done = false;
|
|
8689
|
+
rl.on("line", (line) => {
|
|
8690
|
+
if (done) return;
|
|
8691
|
+
try {
|
|
8692
|
+
const entry = JSON.parse(line);
|
|
8693
|
+
done = true;
|
|
8694
|
+
rl.close();
|
|
8695
|
+
resolve2(entry);
|
|
8696
|
+
} catch {
|
|
8697
|
+
}
|
|
8698
|
+
});
|
|
8699
|
+
rl.on("close", () => {
|
|
8700
|
+
if (!done) resolve2(null);
|
|
8701
|
+
});
|
|
8702
|
+
rl.on("error", () => resolve2(null));
|
|
8703
|
+
});
|
|
8704
|
+
}
|
|
8647
8705
|
async readCwdFromJsonl(filePath) {
|
|
8648
8706
|
return new Promise((resolve2) => {
|
|
8649
8707
|
const rl = createInterface({ input: createReadStream(filePath), crlfDelay: Infinity });
|
|
@@ -8669,7 +8727,7 @@ var ConversationHandlers = class {
|
|
|
8669
8727
|
async findConversationByUuid(uuid) {
|
|
8670
8728
|
const lookupId = this.deps.resolveConversationLookupId(uuid);
|
|
8671
8729
|
if (!this.scannerManager.ready && !this.scanProfiles) {
|
|
8672
|
-
const filePath2 =
|
|
8730
|
+
const filePath2 = await this.locateJsonlPath(uuid, lookupId);
|
|
8673
8731
|
if (filePath2) {
|
|
8674
8732
|
const account = this.cache?.getMetaById(lookupId)?.account ?? void 0;
|
|
8675
8733
|
const coldScanner = this.scannerManager.current ?? this.scannerManager.newScanner();
|
|
@@ -8721,7 +8779,7 @@ var ConversationHandlers = class {
|
|
|
8721
8779
|
return fromIndex;
|
|
8722
8780
|
}
|
|
8723
8781
|
if (this.scanProfiles) return null;
|
|
8724
|
-
const filePath =
|
|
8782
|
+
const filePath = await this.locateJsonlPath(uuid, lookupId);
|
|
8725
8783
|
if (!filePath) return null;
|
|
8726
8784
|
if (this.scannerManager.ready) {
|
|
8727
8785
|
const account = this.cache?.getMetaById(lookupId)?.account ?? void 0;
|
|
@@ -9135,7 +9193,7 @@ var ConversationHandlers = class {
|
|
|
9135
9193
|
|
|
9136
9194
|
// src/api/handlers/sessions.handlers.ts
|
|
9137
9195
|
import { existsSync as existsSync10 } from "fs";
|
|
9138
|
-
import { basename as
|
|
9196
|
+
import { basename as basename6, dirname as dirname9, join as join16 } from "path";
|
|
9139
9197
|
|
|
9140
9198
|
// node_modules/nanoid/index.js
|
|
9141
9199
|
import crypto2 from "crypto";
|
|
@@ -10659,7 +10717,7 @@ var SessionHandlers = class {
|
|
|
10659
10717
|
const jsonlCwd = jsonlPath ? await this.deps.readCwdFromJsonl(jsonlPath) : null;
|
|
10660
10718
|
if (jsonlCwd) {
|
|
10661
10719
|
projectPath = jsonlCwd;
|
|
10662
|
-
projectName = projectName ||
|
|
10720
|
+
projectName = projectName || basename6(jsonlCwd);
|
|
10663
10721
|
}
|
|
10664
10722
|
}
|
|
10665
10723
|
if (!projectPath) {
|
|
@@ -15256,7 +15314,7 @@ function discoveredToResponse(d, conversationId) {
|
|
|
15256
15314
|
// src/session-watchers.ts
|
|
15257
15315
|
import { existsSync as existsSync15, watch as fsWatch, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync13 } from "fs";
|
|
15258
15316
|
import { homedir as homedir13 } from "os";
|
|
15259
|
-
import { basename as
|
|
15317
|
+
import { basename as basename7, join as join24 } from "path";
|
|
15260
15318
|
var SessionWatchers = class {
|
|
15261
15319
|
constructor(deps) {
|
|
15262
15320
|
this.deps = deps;
|
|
@@ -15362,7 +15420,7 @@ var SessionWatchers = class {
|
|
|
15362
15420
|
try {
|
|
15363
15421
|
const now = Date.now();
|
|
15364
15422
|
const match = readdirSync7(projectsDir).filter((f) => f.endsWith(".jsonl")).map((f) => ({ f, mtime: statSync13(join24(projectsDir, f)).mtimeMs })).filter(({ mtime }) => now - mtime < 5e3).filter(
|
|
15365
|
-
({ f }) =>
|
|
15423
|
+
({ f }) => basename7(f, ".jsonl") === sessionId || this.readFirstLineSessionId(join24(projectsDir, f)) === sessionId
|
|
15366
15424
|
).sort((a, b) => b.mtime - a.mtime)[0];
|
|
15367
15425
|
if (match) resolvedFilePath = join24(projectsDir, match.f);
|
|
15368
15426
|
} catch {
|