@threadbase-sh/streamer 1.67.0 → 1.67.2
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 +86 -17
- package/dist/cli.cjs.map +1 -1
- package/dist/conversation-cache.d.ts.map +1 -1
- package/dist/index.cjs +63 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +68 -9
- 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
|
|
@@ -6769,6 +6769,7 @@ CREATE TABLE IF NOT EXISTS session_names (
|
|
|
6769
6769
|
);
|
|
6770
6770
|
`;
|
|
6771
6771
|
var cacheLog = getLogger("cache");
|
|
6772
|
+
var PARSE_STATE_LOOKBACK = 8;
|
|
6772
6773
|
var ConversationCache = class _ConversationCache {
|
|
6773
6774
|
db;
|
|
6774
6775
|
tailSize;
|
|
@@ -7285,7 +7286,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
7285
7286
|
if (to <= from) return { messages: [], total, fromIndex: from };
|
|
7286
7287
|
const rows = this.getMessageIndexWindow(
|
|
7287
7288
|
_ConversationCache.conversationIdForFile(filePath),
|
|
7288
|
-
from,
|
|
7289
|
+
Math.max(0, from - PARSE_STATE_LOOKBACK),
|
|
7289
7290
|
to
|
|
7290
7291
|
);
|
|
7291
7292
|
if (rows.length === 0) return { messages: [], total, fromIndex: from };
|
|
@@ -7297,7 +7298,7 @@ var ConversationCache = class _ConversationCache {
|
|
|
7297
7298
|
const buf = Buffer.alloc(row.byte_length);
|
|
7298
7299
|
readSync3(fd, buf, 0, row.byte_length, row.byte_offset);
|
|
7299
7300
|
const msg = parseJsonlLine(buf.toString("utf-8"), state);
|
|
7300
|
-
if (msg) messages.push(msg);
|
|
7301
|
+
if (msg && row.message_index >= from) messages.push(msg);
|
|
7301
7302
|
}
|
|
7302
7303
|
} finally {
|
|
7303
7304
|
closeSync3(fd);
|
|
@@ -8644,6 +8645,64 @@ var ConversationHandlers = class {
|
|
|
8644
8645
|
}
|
|
8645
8646
|
return null;
|
|
8646
8647
|
}
|
|
8648
|
+
/**
|
|
8649
|
+
* Resolve a conversation id to a JSONL path, in order of authority.
|
|
8650
|
+
*
|
|
8651
|
+
* `findJsonlPath` alone answers 64.0% of this machine's 961 conversations and
|
|
8652
|
+
* 0 of 343 Codex ones — it reconstructs `<projectsDir>/<dir>/<uuid>.jsonl`,
|
|
8653
|
+
* which is Claude Code's layout, and a Codex rollout is
|
|
8654
|
+
* `rollout-<ts>-<uuid>.jsonl` under a date path, so that walk cannot match one
|
|
8655
|
+
* by construction. The ladder below answers 99.7%, leaving only the three
|
|
8656
|
+
* whose file is genuinely gone.
|
|
8657
|
+
*/
|
|
8658
|
+
async locateJsonlPath(uuid, lookupId) {
|
|
8659
|
+
const live = this.deps.findLiveSessionFilePath(uuid) ?? this.deps.findLiveSessionFilePath(lookupId);
|
|
8660
|
+
if (live) return live;
|
|
8661
|
+
const cached3 = this.cache?.getMetaById(lookupId)?.filePath;
|
|
8662
|
+
if (cached3 && await this.isJsonlPathFor(cached3, lookupId)) return cached3;
|
|
8663
|
+
return this.findJsonlPath(lookupId);
|
|
8664
|
+
}
|
|
8665
|
+
/**
|
|
8666
|
+
* Does `filePath` actually hold the conversation `requestedId` names?
|
|
8667
|
+
*
|
|
8668
|
+
* The filename settles it for both providers: Claude writes `<uuid>.jsonl`
|
|
8669
|
+
* (or `agent-<agentId>.jsonl`), Codex writes `rollout-<ts>-<uuid>.jsonl`.
|
|
8670
|
+
* Only when the name says nothing do we open the file — and there the naive
|
|
8671
|
+
* rule is wrong, because **a Claude subagent transcript carries the PARENT's
|
|
8672
|
+
* `sessionId`**. Matching on `sessionId` alone therefore verifies exactly the
|
|
8673
|
+
* file this check exists to reject, so a sidechain is refused outright unless
|
|
8674
|
+
* it was asked for by its own `agent-<agentId>` name, which the filename
|
|
8675
|
+
* branch above already covers.
|
|
8676
|
+
*/
|
|
8677
|
+
async isJsonlPathFor(filePath, requestedId) {
|
|
8678
|
+
if (!existsSync8(filePath)) return false;
|
|
8679
|
+
const stem = basename5(filePath).replace(/\.jsonl$/, "");
|
|
8680
|
+
if (stem === requestedId || stem.includes(requestedId)) return true;
|
|
8681
|
+
const first = await this.readFirstJsonlEntry(filePath);
|
|
8682
|
+
if (!first || first.isSidechain === true) return false;
|
|
8683
|
+
return first.sessionId === requestedId;
|
|
8684
|
+
}
|
|
8685
|
+
/** First parseable JSONL line, for identity checks. Null on an empty or unreadable file. */
|
|
8686
|
+
async readFirstJsonlEntry(filePath) {
|
|
8687
|
+
return new Promise((resolve2) => {
|
|
8688
|
+
const rl = createInterface({ input: createReadStream(filePath), crlfDelay: Infinity });
|
|
8689
|
+
let done = false;
|
|
8690
|
+
rl.on("line", (line) => {
|
|
8691
|
+
if (done) return;
|
|
8692
|
+
try {
|
|
8693
|
+
const entry = JSON.parse(line);
|
|
8694
|
+
done = true;
|
|
8695
|
+
rl.close();
|
|
8696
|
+
resolve2(entry);
|
|
8697
|
+
} catch {
|
|
8698
|
+
}
|
|
8699
|
+
});
|
|
8700
|
+
rl.on("close", () => {
|
|
8701
|
+
if (!done) resolve2(null);
|
|
8702
|
+
});
|
|
8703
|
+
rl.on("error", () => resolve2(null));
|
|
8704
|
+
});
|
|
8705
|
+
}
|
|
8647
8706
|
async readCwdFromJsonl(filePath) {
|
|
8648
8707
|
return new Promise((resolve2) => {
|
|
8649
8708
|
const rl = createInterface({ input: createReadStream(filePath), crlfDelay: Infinity });
|
|
@@ -8669,7 +8728,7 @@ var ConversationHandlers = class {
|
|
|
8669
8728
|
async findConversationByUuid(uuid) {
|
|
8670
8729
|
const lookupId = this.deps.resolveConversationLookupId(uuid);
|
|
8671
8730
|
if (!this.scannerManager.ready && !this.scanProfiles) {
|
|
8672
|
-
const filePath2 =
|
|
8731
|
+
const filePath2 = await this.locateJsonlPath(uuid, lookupId);
|
|
8673
8732
|
if (filePath2) {
|
|
8674
8733
|
const account = this.cache?.getMetaById(lookupId)?.account ?? void 0;
|
|
8675
8734
|
const coldScanner = this.scannerManager.current ?? this.scannerManager.newScanner();
|
|
@@ -8721,7 +8780,7 @@ var ConversationHandlers = class {
|
|
|
8721
8780
|
return fromIndex;
|
|
8722
8781
|
}
|
|
8723
8782
|
if (this.scanProfiles) return null;
|
|
8724
|
-
const filePath =
|
|
8783
|
+
const filePath = await this.locateJsonlPath(uuid, lookupId);
|
|
8725
8784
|
if (!filePath) return null;
|
|
8726
8785
|
if (this.scannerManager.ready) {
|
|
8727
8786
|
const account = this.cache?.getMetaById(lookupId)?.account ?? void 0;
|
|
@@ -9135,7 +9194,7 @@ var ConversationHandlers = class {
|
|
|
9135
9194
|
|
|
9136
9195
|
// src/api/handlers/sessions.handlers.ts
|
|
9137
9196
|
import { existsSync as existsSync10 } from "fs";
|
|
9138
|
-
import { basename as
|
|
9197
|
+
import { basename as basename6, dirname as dirname9, join as join16 } from "path";
|
|
9139
9198
|
|
|
9140
9199
|
// node_modules/nanoid/index.js
|
|
9141
9200
|
import crypto2 from "crypto";
|
|
@@ -10659,7 +10718,7 @@ var SessionHandlers = class {
|
|
|
10659
10718
|
const jsonlCwd = jsonlPath ? await this.deps.readCwdFromJsonl(jsonlPath) : null;
|
|
10660
10719
|
if (jsonlCwd) {
|
|
10661
10720
|
projectPath = jsonlCwd;
|
|
10662
|
-
projectName = projectName ||
|
|
10721
|
+
projectName = projectName || basename6(jsonlCwd);
|
|
10663
10722
|
}
|
|
10664
10723
|
}
|
|
10665
10724
|
if (!projectPath) {
|
|
@@ -15256,7 +15315,7 @@ function discoveredToResponse(d, conversationId) {
|
|
|
15256
15315
|
// src/session-watchers.ts
|
|
15257
15316
|
import { existsSync as existsSync15, watch as fsWatch, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync13 } from "fs";
|
|
15258
15317
|
import { homedir as homedir13 } from "os";
|
|
15259
|
-
import { basename as
|
|
15318
|
+
import { basename as basename7, join as join24 } from "path";
|
|
15260
15319
|
var SessionWatchers = class {
|
|
15261
15320
|
constructor(deps) {
|
|
15262
15321
|
this.deps = deps;
|
|
@@ -15362,7 +15421,7 @@ var SessionWatchers = class {
|
|
|
15362
15421
|
try {
|
|
15363
15422
|
const now = Date.now();
|
|
15364
15423
|
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 }) =>
|
|
15424
|
+
({ f }) => basename7(f, ".jsonl") === sessionId || this.readFirstLineSessionId(join24(projectsDir, f)) === sessionId
|
|
15366
15425
|
).sort((a, b) => b.mtime - a.mtime)[0];
|
|
15367
15426
|
if (match) resolvedFilePath = join24(projectsDir, match.f);
|
|
15368
15427
|
} catch {
|