@threadbase-sh/streamer 1.17.0 → 1.18.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 +4621 -1173
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +80 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +80 -18
- package/dist/index.js.map +1 -1
- package/dist/migrations/006_add_provider_column.sql +1 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2319,6 +2319,14 @@ function runSqliteMigrations(db, migrationsDir) {
|
|
|
2319
2319
|
return { applied, skipped };
|
|
2320
2320
|
}
|
|
2321
2321
|
|
|
2322
|
+
// src/providers.ts
|
|
2323
|
+
var CLAUDE_CODE_PROVIDER = "claude-code";
|
|
2324
|
+
var CODEX_CLI_PROVIDER = "codex-cli";
|
|
2325
|
+
function isProviderResumable(provider, availabilityResumable) {
|
|
2326
|
+
if (provider === CODEX_CLI_PROVIDER) return false;
|
|
2327
|
+
return availabilityResumable;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2322
2330
|
// src/services/conversations/isAgentConversation.ts
|
|
2323
2331
|
var import_fs6 = require("fs");
|
|
2324
2332
|
var DEFAULT_AGENT_ENTRYPOINTS = /* @__PURE__ */ new Set(["sdk-cli", "claude-vscode"]);
|
|
@@ -2486,11 +2494,11 @@ var ConversationCache = class _ConversationCache {
|
|
|
2486
2494
|
INSERT INTO conversation_meta
|
|
2487
2495
|
(id, file_path, project_path, project_name, title, model, account, branch,
|
|
2488
2496
|
message_count, last_activity, first_message, last_message, preview, updated_at,
|
|
2489
|
-
mtime_ms, file_size)
|
|
2497
|
+
mtime_ms, file_size, provider)
|
|
2490
2498
|
VALUES
|
|
2491
2499
|
(@id, @file_path, @project_path, @project_name, @title, @model, @account, @branch,
|
|
2492
2500
|
@message_count, @last_activity, @first_message, @last_message, @preview, @updated_at,
|
|
2493
|
-
@mtime_ms, @file_size)
|
|
2501
|
+
@mtime_ms, @file_size, @provider)
|
|
2494
2502
|
ON CONFLICT(id) DO UPDATE SET
|
|
2495
2503
|
file_path = excluded.file_path,
|
|
2496
2504
|
project_path = excluded.project_path,
|
|
@@ -2506,7 +2514,8 @@ var ConversationCache = class _ConversationCache {
|
|
|
2506
2514
|
preview = excluded.preview,
|
|
2507
2515
|
updated_at = excluded.updated_at,
|
|
2508
2516
|
mtime_ms = excluded.mtime_ms,
|
|
2509
|
-
file_size = excluded.file_size
|
|
2517
|
+
file_size = excluded.file_size,
|
|
2518
|
+
provider = excluded.provider
|
|
2510
2519
|
WHERE conversation_meta.updated_at < excluded.updated_at
|
|
2511
2520
|
`),
|
|
2512
2521
|
getTail: db.prepare("SELECT * FROM conversation_tail WHERE conversation_id = ?"),
|
|
@@ -2530,6 +2539,10 @@ var ConversationCache = class _ConversationCache {
|
|
|
2530
2539
|
countByProject: db.prepare(
|
|
2531
2540
|
"SELECT COUNT(*) as n FROM conversation_meta WHERE project_path = ?"
|
|
2532
2541
|
),
|
|
2542
|
+
listByProvider: db.prepare(
|
|
2543
|
+
"SELECT * FROM conversation_meta WHERE provider = ? ORDER BY last_activity DESC LIMIT ? OFFSET ?"
|
|
2544
|
+
),
|
|
2545
|
+
countByProvider: db.prepare("SELECT COUNT(*) as n FROM conversation_meta WHERE provider = ?"),
|
|
2533
2546
|
deleteById: db.prepare("DELETE FROM conversation_meta WHERE id = ?"),
|
|
2534
2547
|
deleteTailById: db.prepare("DELETE FROM conversation_tail WHERE conversation_id = ?"),
|
|
2535
2548
|
deleteAll: db.prepare("DELETE FROM conversation_meta"),
|
|
@@ -2787,7 +2800,8 @@ var ConversationCache = class _ConversationCache {
|
|
|
2787
2800
|
preview: m.preview ?? null,
|
|
2788
2801
|
updated_at: 0,
|
|
2789
2802
|
mtime_ms: mtimeMs,
|
|
2790
|
-
file_size: fileSize
|
|
2803
|
+
file_size: fileSize,
|
|
2804
|
+
provider: m.provider ?? CLAUDE_CODE_PROVIDER
|
|
2791
2805
|
});
|
|
2792
2806
|
if (this.fileIndexLoaded) this.fileIndex.set(m.filePath, id);
|
|
2793
2807
|
upsertedIds.push(id);
|
|
@@ -2866,12 +2880,15 @@ var ConversationCache = class _ConversationCache {
|
|
|
2866
2880
|
return true;
|
|
2867
2881
|
}
|
|
2868
2882
|
listConversations(opts) {
|
|
2869
|
-
const { project, limit, offset } = opts;
|
|
2883
|
+
const { project, provider, limit, offset } = opts;
|
|
2870
2884
|
let total;
|
|
2871
2885
|
let rows;
|
|
2872
2886
|
if (project) {
|
|
2873
2887
|
total = this.stmts.countByProject.get(project).n;
|
|
2874
2888
|
rows = limit === 0 ? [] : this.stmts.listByProject.all(project, limit, offset);
|
|
2889
|
+
} else if (provider) {
|
|
2890
|
+
total = this.stmts.countByProvider.get(provider).n;
|
|
2891
|
+
rows = limit === 0 ? [] : this.stmts.listByProvider.all(provider, limit, offset);
|
|
2875
2892
|
} else {
|
|
2876
2893
|
total = this.stmts.count.get().n;
|
|
2877
2894
|
rows = limit === 0 ? [] : this.stmts.list.all(limit, offset);
|
|
@@ -2893,7 +2910,8 @@ var ConversationCache = class _ConversationCache {
|
|
|
2893
2910
|
firstMessage: r.first_message,
|
|
2894
2911
|
lastMessage: r.last_message,
|
|
2895
2912
|
preview: r.preview,
|
|
2896
|
-
source: r.source
|
|
2913
|
+
source: r.source,
|
|
2914
|
+
provider: r.provider ?? CLAUDE_CODE_PROVIDER
|
|
2897
2915
|
}))
|
|
2898
2916
|
};
|
|
2899
2917
|
}
|
|
@@ -2926,7 +2944,8 @@ var ConversationCache = class _ConversationCache {
|
|
|
2926
2944
|
firstMessage: row.first_message,
|
|
2927
2945
|
lastMessage: row.last_message,
|
|
2928
2946
|
preview: row.preview,
|
|
2929
|
-
source: row.source
|
|
2947
|
+
source: row.source,
|
|
2948
|
+
provider: row.provider ?? CLAUDE_CODE_PROVIDER
|
|
2930
2949
|
};
|
|
2931
2950
|
}
|
|
2932
2951
|
setConversationProjectId(conversationId, projectId) {
|
|
@@ -3437,6 +3456,7 @@ function normalizeConversationToProjectChat(conversation) {
|
|
|
3437
3456
|
createdAt: null,
|
|
3438
3457
|
status: "resumable",
|
|
3439
3458
|
source: "hdd-cache",
|
|
3459
|
+
provider: conversation.provider ?? CLAUDE_CODE_PROVIDER,
|
|
3440
3460
|
indexedAt: null,
|
|
3441
3461
|
fileMtime: null,
|
|
3442
3462
|
filePath: conversation.filePath ?? null,
|
|
@@ -4361,6 +4381,7 @@ var StreamerServer = class {
|
|
|
4361
4381
|
// in the constructor body (NOT a field initializer) so directoryDebounceMs
|
|
4362
4382
|
// is already set when debounce() captures the wait.
|
|
4363
4383
|
markScannerStaleDebounced;
|
|
4384
|
+
codexRoots;
|
|
4364
4385
|
includeAgents;
|
|
4365
4386
|
agentEntrypoints;
|
|
4366
4387
|
honoApp;
|
|
@@ -4375,6 +4396,7 @@ var StreamerServer = class {
|
|
|
4375
4396
|
this.verbose = config.verbose ?? false;
|
|
4376
4397
|
this.disableDb = config.disableDb ?? false;
|
|
4377
4398
|
this.scanProfiles = config.scanProfiles;
|
|
4399
|
+
this.codexRoots = config.codexRoots ?? [(0, import_path11.join)((0, import_os6.homedir)(), ".codex", "sessions")];
|
|
4378
4400
|
this.ptyGracePeriodMs = config.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
|
|
4379
4401
|
this.cacheDir = config.cacheDir ?? loadCacheDir() ?? (0, import_path11.join)((0, import_os6.homedir)(), ".threadbase", "cache");
|
|
4380
4402
|
this.tailSize = config.tailSize ?? loadTailSize() ?? 10;
|
|
@@ -4792,9 +4814,13 @@ var StreamerServer = class {
|
|
|
4792
4814
|
const warmupScanner = new import_scanner2.ConversationScanner();
|
|
4793
4815
|
const warmupStatCache = this.buildStatCache(null);
|
|
4794
4816
|
const shouldEmitProgress = createScanProgressThrottle();
|
|
4795
|
-
|
|
4817
|
+
const scanOpts = {
|
|
4796
4818
|
...this.scanProfiles ? { profiles: this.scanProfiles } : {},
|
|
4797
|
-
...
|
|
4819
|
+
...this.codexScanOpts(),
|
|
4820
|
+
...warmupStatCache ? { statCache: warmupStatCache } : {}
|
|
4821
|
+
};
|
|
4822
|
+
warmupScanner.scan({
|
|
4823
|
+
...scanOpts,
|
|
4798
4824
|
onProgress: (scanned, total) => {
|
|
4799
4825
|
if (shouldEmitProgress(scanned, total)) {
|
|
4800
4826
|
this.wsHub.broadcast({ type: "scan_progress", scanned, total });
|
|
@@ -5019,6 +5045,7 @@ var StreamerServer = class {
|
|
|
5019
5045
|
const offset = intParam(url, "offset", 0);
|
|
5020
5046
|
const sort = url.searchParams.get("sort") ?? "recent";
|
|
5021
5047
|
const project = url.searchParams.get("project") ?? void 0;
|
|
5048
|
+
const providerFilter = url.searchParams.get("provider") ?? void 0;
|
|
5022
5049
|
const bustCache = url.searchParams.get("refresh") === "1";
|
|
5023
5050
|
if (bustCache) {
|
|
5024
5051
|
this.cache?.invalidate();
|
|
@@ -5026,7 +5053,12 @@ var StreamerServer = class {
|
|
|
5026
5053
|
this.scannerReady = null;
|
|
5027
5054
|
}
|
|
5028
5055
|
if (this.cache && !bustCache) {
|
|
5029
|
-
const { conversations, total: total2 } = this.cache.listConversations({
|
|
5056
|
+
const { conversations, total: total2 } = this.cache.listConversations({
|
|
5057
|
+
project,
|
|
5058
|
+
provider: providerFilter,
|
|
5059
|
+
limit,
|
|
5060
|
+
offset
|
|
5061
|
+
});
|
|
5030
5062
|
const adapted2 = conversations.map((c) => ({
|
|
5031
5063
|
id: c.id,
|
|
5032
5064
|
title: deriveProjectChatTitle({
|
|
@@ -5045,7 +5077,8 @@ var StreamerServer = class {
|
|
|
5045
5077
|
lastActivity: c.lastActivity,
|
|
5046
5078
|
firstMessage: c.firstMessage ? JSON.parse(c.firstMessage) : void 0,
|
|
5047
5079
|
lastMessage: c.lastMessage ? JSON.parse(c.lastMessage) : void 0,
|
|
5048
|
-
model: c.model ?? void 0
|
|
5080
|
+
model: c.model ?? void 0,
|
|
5081
|
+
provider: c.provider ?? CLAUDE_CODE_PROVIDER
|
|
5049
5082
|
}));
|
|
5050
5083
|
json(res, 200, { conversations: adapted2, hasMore: offset + limit < total2, offset, total: total2 });
|
|
5051
5084
|
return;
|
|
@@ -5054,6 +5087,8 @@ var StreamerServer = class {
|
|
|
5054
5087
|
let metas = [...scanner.getMetadataCache().values()];
|
|
5055
5088
|
metas = (0, import_scanner2.applyIncludeFilter)(metas, "conversations");
|
|
5056
5089
|
if (project) metas = (0, import_scanner2.applyProjectFilter)(metas, project);
|
|
5090
|
+
if (providerFilter)
|
|
5091
|
+
metas = metas.filter((m) => (m.provider ?? CLAUDE_CODE_PROVIDER) === providerFilter);
|
|
5057
5092
|
metas = (0, import_scanner2.applySort)(metas, sort);
|
|
5058
5093
|
const total = metas.length;
|
|
5059
5094
|
const page = (0, import_scanner2.applyPagination)(metas, limit, offset);
|
|
@@ -5077,7 +5112,8 @@ var StreamerServer = class {
|
|
|
5077
5112
|
lastActivity: c.timestamp,
|
|
5078
5113
|
firstMessage: c.firstMessage ?? void 0,
|
|
5079
5114
|
lastMessage: c.lastMessage ?? void 0,
|
|
5080
|
-
model: c.model ?? void 0
|
|
5115
|
+
model: c.model ?? void 0,
|
|
5116
|
+
provider: c.provider ?? CLAUDE_CODE_PROVIDER
|
|
5081
5117
|
};
|
|
5082
5118
|
});
|
|
5083
5119
|
json(res, 200, { conversations: adapted, hasMore: offset + limit < total, offset, total });
|
|
@@ -5090,9 +5126,15 @@ var StreamerServer = class {
|
|
|
5090
5126
|
}
|
|
5091
5127
|
async handleConversationsCount(url, res) {
|
|
5092
5128
|
const project = url.searchParams.get("project") ?? void 0;
|
|
5129
|
+
const providerFilter = url.searchParams.get("provider") ?? void 0;
|
|
5093
5130
|
const bustCache = url.searchParams.get("refresh") === "1";
|
|
5094
5131
|
if (this.cache) {
|
|
5095
|
-
const { total } = this.cache.listConversations({
|
|
5132
|
+
const { total } = this.cache.listConversations({
|
|
5133
|
+
project,
|
|
5134
|
+
provider: providerFilter,
|
|
5135
|
+
limit: 0,
|
|
5136
|
+
offset: 0
|
|
5137
|
+
});
|
|
5096
5138
|
json(res, 200, { total });
|
|
5097
5139
|
if (bustCache) this.refreshCountInBackground();
|
|
5098
5140
|
return;
|
|
@@ -5101,6 +5143,8 @@ var StreamerServer = class {
|
|
|
5101
5143
|
let metas = [...scanner.getMetadataCache().values()];
|
|
5102
5144
|
metas = (0, import_scanner2.applyIncludeFilter)(metas, "conversations");
|
|
5103
5145
|
if (project) metas = (0, import_scanner2.applyProjectFilter)(metas, project);
|
|
5146
|
+
if (providerFilter)
|
|
5147
|
+
metas = metas.filter((m) => (m.provider ?? CLAUDE_CODE_PROVIDER) === providerFilter);
|
|
5104
5148
|
json(res, 200, { total: metas.length });
|
|
5105
5149
|
}
|
|
5106
5150
|
// Fire-and-forget full rescan that reconciles the SQLite cache from disk so a
|
|
@@ -5189,6 +5233,14 @@ var StreamerServer = class {
|
|
|
5189
5233
|
}
|
|
5190
5234
|
return statCache.size > 0 ? statCache : void 0;
|
|
5191
5235
|
}
|
|
5236
|
+
// Returns the provider + codexRoots fragment to spread into every scan()/search() call.
|
|
5237
|
+
// codexRoots=[] disables codex scanning (safe no-op per scanner contract).
|
|
5238
|
+
codexScanOpts() {
|
|
5239
|
+
return {
|
|
5240
|
+
providers: [CLAUDE_CODE_PROVIDER, CODEX_CLI_PROVIDER],
|
|
5241
|
+
codexRoots: this.codexRoots
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5192
5244
|
// skipStaleRescan: when an indexed scanner already exists, return it directly
|
|
5193
5245
|
// even if scannerStale is set, leaving the flag untouched so the next
|
|
5194
5246
|
// list-level call still rescans. The single-conversation detail path passes
|
|
@@ -5214,6 +5266,7 @@ var StreamerServer = class {
|
|
|
5214
5266
|
this.scanner = new import_scanner2.ConversationScanner();
|
|
5215
5267
|
this.scannerReady = this.scanner.scan({
|
|
5216
5268
|
...this.scanProfiles ? { profiles: this.scanProfiles } : {},
|
|
5269
|
+
...this.codexScanOpts(),
|
|
5217
5270
|
...statCache ? { statCache } : {}
|
|
5218
5271
|
});
|
|
5219
5272
|
await this.scannerReady;
|
|
@@ -5317,6 +5370,7 @@ var StreamerServer = class {
|
|
|
5317
5370
|
const tail = this.cache.getConversationTail(id);
|
|
5318
5371
|
if (tail && tail.messages.length > 0) {
|
|
5319
5372
|
const cachedMeta = this.cache.getMetaById(id);
|
|
5373
|
+
const cachedProvider = cachedMeta?.provider ?? CLAUDE_CODE_PROVIDER;
|
|
5320
5374
|
const availability2 = classifyResumability(cachedMeta?.projectPath);
|
|
5321
5375
|
const messagesPayload2 = tail.messages.map((m, idx) => ({
|
|
5322
5376
|
message_index: idx,
|
|
@@ -5335,7 +5389,8 @@ var StreamerServer = class {
|
|
|
5335
5389
|
file_path: cachedMeta?.filePath ?? void 0,
|
|
5336
5390
|
last_updated_at: cachedMeta?.lastActivity ?? void 0,
|
|
5337
5391
|
message_count: cachedMeta?.messageCount ?? void 0,
|
|
5338
|
-
|
|
5392
|
+
provider: cachedProvider,
|
|
5393
|
+
resumable: isProviderResumable(cachedProvider, availability2.resumable),
|
|
5339
5394
|
...availability2.unavailable_reason && {
|
|
5340
5395
|
unavailable_reason: availability2.unavailable_reason
|
|
5341
5396
|
}
|
|
@@ -5433,6 +5488,8 @@ var StreamerServer = class {
|
|
|
5433
5488
|
};
|
|
5434
5489
|
});
|
|
5435
5490
|
const conv = conversation;
|
|
5491
|
+
const cachedConvMeta = this.cache?.getMetaById(id);
|
|
5492
|
+
const convProvider = conv.provider ?? cachedConvMeta?.provider ?? CLAUDE_CODE_PROVIDER;
|
|
5436
5493
|
const availability = classifyResumability(conv.projectPath);
|
|
5437
5494
|
const body = {
|
|
5438
5495
|
meta: {
|
|
@@ -5444,7 +5501,8 @@ var StreamerServer = class {
|
|
|
5444
5501
|
last_updated_at: conv.timestamp,
|
|
5445
5502
|
message_count: conv.messageCount,
|
|
5446
5503
|
last_prompt: conv.lastPrompt ?? void 0,
|
|
5447
|
-
|
|
5504
|
+
provider: convProvider,
|
|
5505
|
+
resumable: isProviderResumable(convProvider, availability.resumable),
|
|
5448
5506
|
...availability.unavailable_reason && {
|
|
5449
5507
|
unavailable_reason: availability.unavailable_reason
|
|
5450
5508
|
}
|
|
@@ -5479,7 +5537,8 @@ var StreamerServer = class {
|
|
|
5479
5537
|
{
|
|
5480
5538
|
limit,
|
|
5481
5539
|
include: "conversations",
|
|
5482
|
-
...this.scanProfiles ? { profiles: this.scanProfiles } : {}
|
|
5540
|
+
...this.scanProfiles ? { profiles: this.scanProfiles } : {},
|
|
5541
|
+
...this.codexScanOpts()
|
|
5483
5542
|
},
|
|
5484
5543
|
scanner
|
|
5485
5544
|
);
|
|
@@ -5495,7 +5554,8 @@ var StreamerServer = class {
|
|
|
5495
5554
|
messageCount: r.meta.messageCount,
|
|
5496
5555
|
lastActivity: r.meta.timestamp,
|
|
5497
5556
|
firstMessage: r.meta.firstMessage ?? void 0,
|
|
5498
|
-
lastMessage: r.meta.lastMessage ?? void 0
|
|
5557
|
+
lastMessage: r.meta.lastMessage ?? void 0,
|
|
5558
|
+
provider: r.meta.provider ?? CLAUDE_CODE_PROVIDER
|
|
5499
5559
|
}));
|
|
5500
5560
|
json(res, 200, {
|
|
5501
5561
|
conversations: adapted,
|
|
@@ -6175,6 +6235,7 @@ function classifyResumability(cwd) {
|
|
|
6175
6235
|
}
|
|
6176
6236
|
function conversationToResumableSession(c) {
|
|
6177
6237
|
const availability = classifyResumability(c.projectPath);
|
|
6238
|
+
const provider = c.provider ?? CLAUDE_CODE_PROVIDER;
|
|
6178
6239
|
return {
|
|
6179
6240
|
type: "conversation",
|
|
6180
6241
|
id: c.id,
|
|
@@ -6199,7 +6260,8 @@ function conversationToResumableSession(c) {
|
|
|
6199
6260
|
...c.firstMessage != null && { firstMessageText: c.firstMessage },
|
|
6200
6261
|
...c.lastMessage != null && { lastMessageText: c.lastMessage },
|
|
6201
6262
|
filePath: c.filePath,
|
|
6202
|
-
|
|
6263
|
+
provider,
|
|
6264
|
+
resumable: isProviderResumable(provider, availability.resumable),
|
|
6203
6265
|
...availability.unavailable_reason && {
|
|
6204
6266
|
unavailable_reason: availability.unavailable_reason
|
|
6205
6267
|
}
|