@threadbase-sh/scanner 0.9.1 → 0.9.3
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.js +61 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +60 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +60 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -371,15 +371,18 @@ declare class ConversationScanner {
|
|
|
371
371
|
private readonly dbPath;
|
|
372
372
|
private readonly sidecarEnabled;
|
|
373
373
|
private engineInstance;
|
|
374
|
+
private inFlightScan;
|
|
374
375
|
private emitter;
|
|
375
376
|
private watcher;
|
|
376
377
|
private queue;
|
|
377
378
|
private periodicTimer;
|
|
379
|
+
private inFlightReconcile;
|
|
378
380
|
constructor(options?: ConversationScannerOptions);
|
|
379
381
|
private get persistent();
|
|
380
382
|
private engine;
|
|
381
|
-
close(): void
|
|
383
|
+
close(): Promise<void>;
|
|
382
384
|
scan(options?: ScanOptions): Promise<ScanResult>;
|
|
385
|
+
private runScan;
|
|
383
386
|
private scanPersistent;
|
|
384
387
|
private finalize;
|
|
385
388
|
private scanInMemory;
|
package/dist/index.d.ts
CHANGED
|
@@ -371,15 +371,18 @@ declare class ConversationScanner {
|
|
|
371
371
|
private readonly dbPath;
|
|
372
372
|
private readonly sidecarEnabled;
|
|
373
373
|
private engineInstance;
|
|
374
|
+
private inFlightScan;
|
|
374
375
|
private emitter;
|
|
375
376
|
private watcher;
|
|
376
377
|
private queue;
|
|
377
378
|
private periodicTimer;
|
|
379
|
+
private inFlightReconcile;
|
|
378
380
|
constructor(options?: ConversationScannerOptions);
|
|
379
381
|
private get persistent();
|
|
380
382
|
private engine;
|
|
381
|
-
close(): void
|
|
383
|
+
close(): Promise<void>;
|
|
382
384
|
scan(options?: ScanOptions): Promise<ScanResult>;
|
|
385
|
+
private runScan;
|
|
383
386
|
private scanPersistent;
|
|
384
387
|
private finalize;
|
|
385
388
|
private scanInMemory;
|
package/dist/index.js
CHANGED
|
@@ -1781,6 +1781,19 @@ var ConversationFilesRepo = class {
|
|
|
1781
1781
|
const rows = this.db.prepare("SELECT absolute_path FROM conversation_files WHERE status != 'deleted'").all();
|
|
1782
1782
|
return rows.map((r) => r.absolute_path);
|
|
1783
1783
|
}
|
|
1784
|
+
// Active file paths belonging to any of the given accounts. Backs the
|
|
1785
|
+
// deletion-reconcile so a scan that covered only some accounts can't mark
|
|
1786
|
+
// another account's files deleted (they live in the same shared index.db but
|
|
1787
|
+
// a different profile owns them). Returns [] for an empty account list.
|
|
1788
|
+
activePathsByAccounts(accounts) {
|
|
1789
|
+
if (accounts.length === 0) return [];
|
|
1790
|
+
const placeholders = accounts.map(() => "?").join(", ");
|
|
1791
|
+
const rows = this.db.prepare(
|
|
1792
|
+
`SELECT absolute_path FROM conversation_files
|
|
1793
|
+
WHERE status != 'deleted' AND account IN (${placeholders})`
|
|
1794
|
+
).all(...accounts);
|
|
1795
|
+
return rows.map((r) => r.absolute_path);
|
|
1796
|
+
}
|
|
1784
1797
|
// Active files whose immediate parent is exactly parentDir (no nested
|
|
1785
1798
|
// subdirectories). Backs the dir-mtime gate's reuse path: a project dir with
|
|
1786
1799
|
// an unchanged mtime and no nested files can skip the glob entirely.
|
|
@@ -2153,8 +2166,15 @@ var PersistentEngine = class {
|
|
|
2153
2166
|
scanned += batch.length;
|
|
2154
2167
|
options.onProgress?.(scanned, discovered.length);
|
|
2155
2168
|
}
|
|
2169
|
+
const coveredAccounts = /* @__PURE__ */ new Set();
|
|
2170
|
+
if (enabled.includes(CLAUDE_CODE_PROVIDER)) {
|
|
2171
|
+
for (const p of activeProfiles) coveredAccounts.add(p.id);
|
|
2172
|
+
}
|
|
2173
|
+
if (enabled.includes(CODEX_CLI_PROVIDER) && (options.codexRoots?.length ?? 0) > 0) {
|
|
2174
|
+
coveredAccounts.add("codex");
|
|
2175
|
+
}
|
|
2156
2176
|
const seen = new Set(discovered.map((d) => d.filePath));
|
|
2157
|
-
for (const path of this.files.
|
|
2177
|
+
for (const path of this.files.activePathsByAccounts([...coveredAccounts])) {
|
|
2158
2178
|
if (!seen.has(path)) this.markDeleted(path);
|
|
2159
2179
|
}
|
|
2160
2180
|
log.info({ scanned, indexed: this.conversations.count() }, "persistent: indexAll complete");
|
|
@@ -2464,10 +2484,21 @@ var ConversationScanner = class {
|
|
|
2464
2484
|
dbPath;
|
|
2465
2485
|
sidecarEnabled;
|
|
2466
2486
|
engineInstance = null;
|
|
2487
|
+
// The most recent scan()'s promise while it is still running, or null when
|
|
2488
|
+
// idle. close() awaits it before closing the SQLite handle so a fire-and-
|
|
2489
|
+
// forget scan can't have its DB shut mid-indexAll ("database connection is
|
|
2490
|
+
// not open"). Tracks the latest scan only — concurrent scans on one instance
|
|
2491
|
+
// aren't a supported pattern (a single writer DB), and the latest resolving
|
|
2492
|
+
// implies earlier ones already settled in practice.
|
|
2493
|
+
inFlightScan = null;
|
|
2467
2494
|
emitter = new EventEmitter();
|
|
2468
2495
|
watcher = null;
|
|
2469
2496
|
queue = null;
|
|
2470
2497
|
periodicTimer = null;
|
|
2498
|
+
// The in-flight periodicReconcile() job, if one is mid-run. unwatch() awaits
|
|
2499
|
+
// it before dropping the queue/engine so its post-await engine.files access
|
|
2500
|
+
// can't hit a closed DB (the watch-mode half of Bug #4).
|
|
2501
|
+
inFlightReconcile = null;
|
|
2471
2502
|
constructor(options) {
|
|
2472
2503
|
this.conversationLRU = new LRUCache(options?.conversationCacheSize ?? 5);
|
|
2473
2504
|
if (options?.persistent === false) {
|
|
@@ -2490,14 +2521,31 @@ var ConversationScanner = class {
|
|
|
2490
2521
|
return this.engineInstance;
|
|
2491
2522
|
}
|
|
2492
2523
|
// Release the SQLite connection. No-op in legacy mode. Safe to call
|
|
2493
|
-
// repeatedly.
|
|
2494
|
-
//
|
|
2495
|
-
close()
|
|
2496
|
-
|
|
2524
|
+
// repeatedly. Awaits watcher teardown AND any in-flight scan before closing
|
|
2525
|
+
// the DB handle, so a fire-and-forget scan() can never have its connection
|
|
2526
|
+
// shut mid-indexAll() ("database connection is not open"). This is why close()
|
|
2527
|
+
// is async — callers should await it during shutdown. (Scanner review Bug #4.)
|
|
2528
|
+
async close() {
|
|
2529
|
+
if (this.watcher || this.queue || this.periodicTimer) await this.unwatch();
|
|
2530
|
+
if (this.inFlightScan) {
|
|
2531
|
+
try {
|
|
2532
|
+
await this.inFlightScan;
|
|
2533
|
+
} catch {
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2497
2536
|
this.engineInstance?.close();
|
|
2498
2537
|
this.engineInstance = null;
|
|
2499
2538
|
}
|
|
2500
2539
|
async scan(options = {}) {
|
|
2540
|
+
const promise = this.runScan(options);
|
|
2541
|
+
this.inFlightScan = promise;
|
|
2542
|
+
try {
|
|
2543
|
+
return await promise;
|
|
2544
|
+
} finally {
|
|
2545
|
+
if (this.inFlightScan === promise) this.inFlightScan = null;
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
async runScan(options) {
|
|
2501
2549
|
const profiles = await this.resolveProfiles(options.profiles);
|
|
2502
2550
|
const activeProfiles = profiles.filter((p) => p.enabled && p.scanHistory !== false);
|
|
2503
2551
|
this.lastTier = resolveTier(options.tier ?? "standard", options.tiers);
|
|
@@ -2933,7 +2981,10 @@ var ConversationScanner = class {
|
|
|
2933
2981
|
const periodicMs = options.periodicMs ?? 6e4;
|
|
2934
2982
|
if (periodicMs > 0) {
|
|
2935
2983
|
this.periodicTimer = setInterval(() => {
|
|
2936
|
-
|
|
2984
|
+
const job = this.periodicReconcile(activeProfiles).finally(() => {
|
|
2985
|
+
if (this.inFlightReconcile === job) this.inFlightReconcile = null;
|
|
2986
|
+
});
|
|
2987
|
+
this.inFlightReconcile = job;
|
|
2937
2988
|
}, periodicMs);
|
|
2938
2989
|
this.periodicTimer.unref?.();
|
|
2939
2990
|
}
|
|
@@ -2977,6 +3028,9 @@ var ConversationScanner = class {
|
|
|
2977
3028
|
clearInterval(this.periodicTimer);
|
|
2978
3029
|
this.periodicTimer = null;
|
|
2979
3030
|
}
|
|
3031
|
+
if (this.inFlightReconcile) {
|
|
3032
|
+
await this.inFlightReconcile;
|
|
3033
|
+
}
|
|
2980
3034
|
if (this.watcher) {
|
|
2981
3035
|
await this.watcher.stop();
|
|
2982
3036
|
this.watcher = null;
|