opencode-rag-plugin 1.19.5 → 1.20.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/chunker/image.d.ts +1 -1
- package/dist/chunker/image.js +35 -22
- package/dist/cli/commands/backend-detect.d.ts +61 -0
- package/dist/cli/commands/backend-detect.js +119 -0
- package/dist/cli/commands/describe-image.js +2 -1
- package/dist/cli/commands/index-command.js +11 -0
- package/dist/cli/commands/init-helpers.d.ts +4 -1
- package/dist/cli/commands/init-helpers.js +10 -5
- package/dist/cli/commands/init.js +30 -3
- package/dist/cli/commands/setup.js +7 -1
- package/dist/cli/types.d.ts +2 -0
- package/dist/core/config.d.ts +27 -1
- package/dist/core/config.js +10 -2
- package/dist/core/interfaces.d.ts +32 -4
- package/dist/core/manifest.js +1 -1
- package/dist/describer/describer.d.ts +20 -0
- package/dist/describer/describer.js +117 -14
- package/dist/describer/shared.d.ts +28 -0
- package/dist/describer/shared.js +60 -0
- package/dist/embedder/factory.js +1 -1
- package/dist/embedder/ollama.d.ts +3 -1
- package/dist/embedder/ollama.js +12 -2
- package/dist/indexer/pipeline.js +187 -99
- package/dist/mcp/handlers.d.ts +2 -0
- package/dist/mcp/handlers.js +1 -1
- package/dist/mcp/server.js +2 -1
- package/dist/opencode/system-guidance.js +4 -4
- package/dist/opencode/tools.js +4 -1
- package/dist/vectorstore/lancedb.d.ts +79 -7
- package/dist/vectorstore/lancedb.js +200 -72
- package/dist/vectorstore/memory.d.ts +8 -3
- package/dist/vectorstore/memory.js +22 -3
- package/dist/watcher.d.ts +8 -0
- package/dist/watcher.js +222 -96
- package/dist/web/api.js +24 -15
- package/dist/web/pca.d.ts +5 -2
- package/dist/web/pca.js +75 -20
- package/dist/web/ui/assets/ScatterPlot3D-BFWO5sAH.js +4116 -0
- package/dist/web/ui/assets/index-BLzCza1W.css +1 -0
- package/dist/web/ui/assets/index-BdPHzjQh.js +4 -0
- package/dist/web/ui/index.html +2 -2
- package/package.json +4 -1
- package/dist/web/ui/assets/index-BDPYdtA1.js +0 -3
- package/dist/web/ui/assets/index-CKdp79Tw.css +0 -1
|
@@ -99,6 +99,14 @@ export class LanceDbStore {
|
|
|
99
99
|
table = null;
|
|
100
100
|
tableInit = null;
|
|
101
101
|
writeLock = Promise.resolve(void 0);
|
|
102
|
+
/**
|
|
103
|
+
* Memoized once-per-process index-metric repair. Stores built by versions
|
|
104
|
+
* before the cosine search switch carry an IVF index trained with the
|
|
105
|
+
* default L2 metric, which makes every cosine query log
|
|
106
|
+
* "Requested metric Cosine is incompatible with index metric L2" and fall
|
|
107
|
+
* back to brute-force. This repairs that stale index on first search.
|
|
108
|
+
*/
|
|
109
|
+
indexRepairPromise = null;
|
|
102
110
|
/**
|
|
103
111
|
* Execute an async function under an exclusive write lock.
|
|
104
112
|
*
|
|
@@ -294,32 +302,65 @@ export class LanceDbStore {
|
|
|
294
302
|
}
|
|
295
303
|
/**
|
|
296
304
|
* Store chunks in the LanceDB table. New rows are inserted first, then
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
* insert and cleanup. Automatically attempts repair on
|
|
305
|
+
* old rows for the same file that are not part of this write are removed
|
|
306
|
+
* in a single delete per file. This ensures no data is lost if the process
|
|
307
|
+
* aborts between insert and cleanup. Automatically attempts repair on
|
|
308
|
+
* corruption errors.
|
|
309
|
+
*
|
|
310
|
+
* When `options.dedup` is `false` the cleanup step is skipped entirely —
|
|
311
|
+
* a pure append. Use this when writing into a store that provably has no
|
|
312
|
+
* prior rows for these files (e.g. a freshly-created rebuild store).
|
|
313
|
+
*
|
|
300
314
|
* @param chunks - The chunks to add.
|
|
315
|
+
* @param options - Optional write options (`dedup`, default `true`).
|
|
301
316
|
*/
|
|
302
|
-
async addChunks(chunks) {
|
|
317
|
+
async addChunks(chunks, options) {
|
|
303
318
|
if (chunks.length === 0)
|
|
304
319
|
return;
|
|
320
|
+
const dedup = options?.dedup ?? true;
|
|
305
321
|
await this.withWriteLock(async () => {
|
|
306
322
|
try {
|
|
307
|
-
await this.addChunksInternal(chunks);
|
|
323
|
+
await this.addChunksInternal(chunks, dedup);
|
|
308
324
|
}
|
|
309
325
|
catch (err) {
|
|
310
326
|
if (isCorruptionError(err) && await this.tryRepair()) {
|
|
311
|
-
await this.addChunksInternal(chunks);
|
|
327
|
+
await this.addChunksInternal(chunks, dedup);
|
|
312
328
|
return;
|
|
313
329
|
}
|
|
314
330
|
throw err;
|
|
315
331
|
}
|
|
316
332
|
});
|
|
317
333
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
334
|
+
/**
|
|
335
|
+
* Store chunks for many files in a single transaction: one `table.add`
|
|
336
|
+
* across all items, then one `table.delete` per item that needs dedup.
|
|
337
|
+
* This collapses what used to be a per-file add + per-startLine deletes
|
|
338
|
+
* (K+2 LanceDB versions per file) into ~1 + M versions per batch.
|
|
339
|
+
*
|
|
340
|
+
* @param items - Per-file chunk payloads with their dedup flags.
|
|
341
|
+
*/
|
|
342
|
+
async addChunksBulk(items) {
|
|
343
|
+
const active = items.filter((item) => item.chunks.length > 0);
|
|
344
|
+
if (active.length === 0)
|
|
345
|
+
return;
|
|
346
|
+
await this.withWriteLock(async () => {
|
|
347
|
+
try {
|
|
348
|
+
await this.addChunksBulkInternal(active);
|
|
349
|
+
}
|
|
350
|
+
catch (err) {
|
|
351
|
+
if (isCorruptionError(err) && await this.tryRepair()) {
|
|
352
|
+
await this.addChunksBulkInternal(active);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
throw err;
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
/** Map a chunk to its internal row shape, or null if it has no embedding. */
|
|
360
|
+
chunkToRow(c) {
|
|
361
|
+
if (!c.embedding || c.embedding.length === 0)
|
|
362
|
+
return null;
|
|
363
|
+
return {
|
|
323
364
|
id: c.id,
|
|
324
365
|
content: c.content,
|
|
325
366
|
description: c.description ?? "",
|
|
@@ -331,56 +372,75 @@ export class LanceDbStore {
|
|
|
331
372
|
kind: c.metadata.kind ?? "",
|
|
332
373
|
quirkType: c.metadata.quirkType ?? "",
|
|
333
374
|
tags: c.metadata.tags ? JSON.stringify(c.metadata.tags) : "",
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
/** Group new rows by file path for dedup deletes. */
|
|
378
|
+
rowsByFilePath(rows) {
|
|
379
|
+
const byFile = new Map();
|
|
339
380
|
for (const row of rows) {
|
|
340
|
-
const
|
|
341
|
-
const ids = newIdsByLine.get(key);
|
|
381
|
+
const ids = byFile.get(row.filePath);
|
|
342
382
|
if (ids) {
|
|
343
|
-
ids.
|
|
383
|
+
ids.push(row.id);
|
|
344
384
|
}
|
|
345
385
|
else {
|
|
346
|
-
|
|
386
|
+
byFile.set(row.filePath, [row.id]);
|
|
347
387
|
}
|
|
348
388
|
}
|
|
389
|
+
return byFile;
|
|
390
|
+
}
|
|
391
|
+
async addChunksInternal(chunks, dedup = true) {
|
|
392
|
+
const table = await this.getTable();
|
|
393
|
+
const rows = chunks
|
|
394
|
+
.map((c) => this.chunkToRow(c))
|
|
395
|
+
.filter((r) => r !== null);
|
|
396
|
+
if (rows.length === 0)
|
|
397
|
+
return;
|
|
349
398
|
// INSERT FIRST: data is safely stored before any delete
|
|
350
399
|
await table.add(rows);
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
400
|
+
if (!dedup)
|
|
401
|
+
return;
|
|
402
|
+
// THEN DEDUP: one delete per file removes prior-revision rows at the
|
|
403
|
+
// same (filePath, startLine) positions AND stale startLines, while the
|
|
404
|
+
// NOT IN clause preserves the newly inserted rows (so multiple new IDs
|
|
405
|
+
// sharing a startLine never delete each other). Insert-first ordering
|
|
406
|
+
// keeps an abort between insert and delete from losing data.
|
|
407
|
+
const byFile = this.rowsByFilePath(rows);
|
|
408
|
+
for (const [filePath, ids] of byFile) {
|
|
359
409
|
const escapedPath = filePath.replace(/'/g, "''");
|
|
360
|
-
const idList =
|
|
361
|
-
|
|
362
|
-
.join(", ");
|
|
363
|
-
await table.delete(`filePath = '${escapedPath}' AND startLine = ${startLine} AND id NOT IN (${idList})`);
|
|
410
|
+
const idList = ids.map((id) => `'${id.replace(/'/g, "''")}'`).join(", ");
|
|
411
|
+
await table.delete(`filePath = '${escapedPath}' AND id NOT IN (${idList})`);
|
|
364
412
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const
|
|
372
|
-
|
|
413
|
+
}
|
|
414
|
+
async addChunksBulkInternal(items) {
|
|
415
|
+
const table = await this.getTable();
|
|
416
|
+
const allRows = [];
|
|
417
|
+
const dedupByFile = new Map();
|
|
418
|
+
for (const item of items) {
|
|
419
|
+
const rows = item.chunks
|
|
420
|
+
.map((c) => this.chunkToRow(c))
|
|
421
|
+
.filter((r) => r !== null);
|
|
422
|
+
if (rows.length === 0)
|
|
373
423
|
continue;
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
424
|
+
allRows.push(...rows);
|
|
425
|
+
if (item.dedup) {
|
|
426
|
+
for (const [filePath, ids] of this.rowsByFilePath(rows)) {
|
|
427
|
+
const existing = dedupByFile.get(filePath);
|
|
428
|
+
if (existing) {
|
|
429
|
+
existing.push(...ids);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
dedupByFile.set(filePath, [...ids]);
|
|
433
|
+
}
|
|
380
434
|
}
|
|
381
435
|
}
|
|
436
|
+
}
|
|
437
|
+
if (allRows.length === 0)
|
|
438
|
+
return;
|
|
439
|
+
// INSERT FIRST (single add for the whole batch), then per-file dedup
|
|
440
|
+
await table.add(allRows);
|
|
441
|
+
for (const [filePath, ids] of dedupByFile) {
|
|
382
442
|
const escapedPath = filePath.replace(/'/g, "''");
|
|
383
|
-
const idList =
|
|
443
|
+
const idList = ids.map((id) => `'${id.replace(/'/g, "''")}'`).join(", ");
|
|
384
444
|
await table.delete(`filePath = '${escapedPath}' AND id NOT IN (${idList})`);
|
|
385
445
|
}
|
|
386
446
|
}
|
|
@@ -684,6 +744,10 @@ export class LanceDbStore {
|
|
|
684
744
|
const count = await table.countRows();
|
|
685
745
|
if (count === 0)
|
|
686
746
|
return [];
|
|
747
|
+
// One-time per-process repair: drop any stale L2 index so the cosine query
|
|
748
|
+
// below doesn't log "Requested metric Cosine is incompatible" and degrade
|
|
749
|
+
// to brute-force. Await it so the repair completes before this search.
|
|
750
|
+
await this.withWriteLock(() => this.ensureCosineIndex());
|
|
687
751
|
const whereClause = buildWhereClause(filter);
|
|
688
752
|
let results;
|
|
689
753
|
try {
|
|
@@ -741,39 +805,103 @@ export class LanceDbStore {
|
|
|
741
805
|
}
|
|
742
806
|
}
|
|
743
807
|
/**
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
808
|
+
* Ensure the ANN index on the `embedding` column uses the cosine metric,
|
|
809
|
+
* matching the `distanceType` requested by searchInternal.
|
|
810
|
+
*
|
|
811
|
+
* Older stores built the IVF index with the ivfFlat default (L2), so every
|
|
812
|
+
* cosine query hit "Requested metric Cosine is incompatible with index
|
|
813
|
+
* metric L2" and silently fell back to brute-force O(N) scans. This lazily
|
|
814
|
+
* replaces such an index with a cosine one — once per process.
|
|
815
|
+
*
|
|
816
|
+
* Callers must hold the write lock (searchInternal wraps the call in
|
|
817
|
+
* withWriteLock; optimize runs under it already).
|
|
747
818
|
*/
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
819
|
+
ensureCosineIndex() {
|
|
820
|
+
if (!this.indexRepairPromise) {
|
|
821
|
+
this.indexRepairPromise = this.repairIndexMetricOnce();
|
|
822
|
+
}
|
|
823
|
+
return this.indexRepairPromise;
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Perform a single index-metric repair pass. Skips stores that have no
|
|
827
|
+
* index and fewer than 1000 rows (brute-force is optimal there). Uses a
|
|
828
|
+
* single `createIndex` with `replace: true` — a dropIndex + createIndex
|
|
829
|
+
* sequence races in LanceDB ("Retryable commit conflict") and leaves the
|
|
830
|
+
* stale index in place. On failure the memo is cleared so the next
|
|
831
|
+
* search/optimize retries.
|
|
832
|
+
*/
|
|
833
|
+
async repairIndexMetricOnce() {
|
|
834
|
+
try {
|
|
835
|
+
const table = await this.getTable();
|
|
836
|
+
const count = await table.countRows().catch(() => 0);
|
|
837
|
+
const indices = await table.listIndices();
|
|
838
|
+
const vecIndex = indices.find((i) => i.columns.includes("embedding"));
|
|
839
|
+
const idxName = vecIndex?.name ?? "embedding_idx";
|
|
840
|
+
const stats = await table.indexStats(idxName);
|
|
841
|
+
if (stats && stats.distanceType === "cosine")
|
|
842
|
+
return; // already healthy
|
|
843
|
+
if (!stats && count < 1000)
|
|
844
|
+
return; // tiny store, no index — leave brute-force
|
|
845
|
+
const numPartitions = Math.max(16, Math.min(256, Math.floor(count / 256)));
|
|
846
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
761
847
|
try {
|
|
762
|
-
const count = await table.countRows().catch(() => 0);
|
|
763
|
-
const numPartitions = count > 0 ? Math.max(16, Math.min(256, Math.floor(count / 256))) : 16;
|
|
764
|
-
// The index metric MUST match the search metric ("cosine", see
|
|
765
|
-
// searchInternal). The ivfFlat default is "l2", which makes LanceDB
|
|
766
|
-
// ignore the index and fall back to brute-force on every query.
|
|
767
848
|
await table.createIndex("embedding", {
|
|
768
849
|
config: lancedb.Index.ivfFlat({ numPartitions, distanceType: "cosine" }),
|
|
850
|
+
replace: true,
|
|
851
|
+
name: idxName,
|
|
852
|
+
// Wait for the commit so the repair is deterministic and cannot
|
|
853
|
+
// race a background index-creation from another process.
|
|
854
|
+
waitTimeoutSeconds: 120,
|
|
769
855
|
});
|
|
856
|
+
return;
|
|
770
857
|
}
|
|
771
|
-
catch {
|
|
772
|
-
|
|
858
|
+
catch (err) {
|
|
859
|
+
const retriable = err instanceof Error &&
|
|
860
|
+
(err.message.includes("Retryable commit conflict") || err.message.includes("Please retry"));
|
|
861
|
+
if (!retriable || attempt === 3)
|
|
862
|
+
throw err;
|
|
863
|
+
await new Promise((resolve) => setTimeout(resolve, 150 * attempt));
|
|
773
864
|
}
|
|
774
865
|
}
|
|
775
|
-
|
|
776
|
-
|
|
866
|
+
}
|
|
867
|
+
catch (err) {
|
|
868
|
+
this.indexRepairPromise = null; // allow retry on a later search/optimize
|
|
869
|
+
console.warn(`[lancedb] index metric repair failed (queries continue in brute-force): ` +
|
|
870
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Compact fragments and prune old version manifests to prevent the
|
|
875
|
+
* version-manifest accumulation that causes countRows() to hang and the
|
|
876
|
+
* store phase to slow down as the index grows. Should be called at the
|
|
877
|
+
* end of a successful index pass, and periodically during long passes.
|
|
878
|
+
*
|
|
879
|
+
* @param options - `aggressive: true` prunes every version but the current
|
|
880
|
+
* one with `deleteUnverified`. Only safe for a private store that no other
|
|
881
|
+
* process reads (e.g. a temporary rebuild store). For the shared store,
|
|
882
|
+
* versions newer than 1 hour are retained so in-flight queries (Web UI,
|
|
883
|
+
* background auto-index) can finish before their data files are reclaimed.
|
|
884
|
+
*/
|
|
885
|
+
async optimize(options) {
|
|
886
|
+
await this.withWriteLock(async () => {
|
|
887
|
+
try {
|
|
888
|
+
const table = await this.getTable();
|
|
889
|
+
if (options?.aggressive) {
|
|
890
|
+
await table.optimize({ cleanupOlderThan: new Date(), deleteUnverified: true });
|
|
891
|
+
}
|
|
892
|
+
else {
|
|
893
|
+
const threshold = new Date(Date.now() - 60 * 60 * 1000);
|
|
894
|
+
await table.optimize({ cleanupOlderThan: threshold, deleteUnverified: false });
|
|
895
|
+
}
|
|
896
|
+
// Build the ANN vector index — without it every vectorSearch() is a
|
|
897
|
+
// brute-force O(N) flat scan (slow at 50k+ chunks). Skips early when
|
|
898
|
+
// an index with the correct cosine metric already exists.
|
|
899
|
+
await this.ensureCosineIndex();
|
|
900
|
+
}
|
|
901
|
+
catch (err) {
|
|
902
|
+
// Optimize is best-effort — must not break indexing, but surface the
|
|
903
|
+
// failure instead of swallowing it silently.
|
|
904
|
+
console.warn(`[lancedb] optimize failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
777
905
|
}
|
|
778
906
|
});
|
|
779
907
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Ephemeral in-memory vector store using cosine similarity search.
|
|
3
3
|
*/
|
|
4
|
-
import type { VectorStore, Chunk, ChunkSummary, FileSummary, SearchResult, MetadataFilter } from "../core/interfaces.js";
|
|
4
|
+
import type { VectorStore, Chunk, ChunkSummary, FileSummary, SearchResult, MetadataFilter, BulkChunkWrite } from "../core/interfaces.js";
|
|
5
5
|
/** Ephemeral in-memory vector store using cosine similarity search. */
|
|
6
6
|
export declare class InMemoryVectorStore implements VectorStore {
|
|
7
7
|
private chunks;
|
|
8
|
-
addChunks(chunks: Chunk[]
|
|
8
|
+
addChunks(chunks: Chunk[], options?: {
|
|
9
|
+
dedup?: boolean;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
addChunksBulk(items: BulkChunkWrite[]): Promise<void>;
|
|
9
12
|
search(embedding: number[], topK: number): Promise<SearchResult[]>;
|
|
10
13
|
searchWithFilter(embedding: number[], topK: number, filter?: MetadataFilter): Promise<SearchResult[]>;
|
|
11
14
|
count(): Promise<number>;
|
|
@@ -22,5 +25,7 @@ export declare class InMemoryVectorStore implements VectorStore {
|
|
|
22
25
|
/** Release any held resources. No-op for the in-memory store. */
|
|
23
26
|
close(): Promise<void>;
|
|
24
27
|
/** No-op for the in-memory store. */
|
|
25
|
-
optimize(
|
|
28
|
+
optimize(_options?: {
|
|
29
|
+
aggressive?: boolean;
|
|
30
|
+
}): Promise<void>;
|
|
26
31
|
}
|
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
/** Ephemeral in-memory vector store using cosine similarity search. */
|
|
2
2
|
export class InMemoryVectorStore {
|
|
3
3
|
chunks = [];
|
|
4
|
-
async addChunks(chunks) {
|
|
5
|
-
|
|
4
|
+
async addChunks(chunks, options) {
|
|
5
|
+
const valid = chunks.filter((c) => c.embedding && c.embedding.length > 0);
|
|
6
|
+
if (valid.length === 0)
|
|
7
|
+
return;
|
|
8
|
+
if (options?.dedup === false) {
|
|
9
|
+
this.chunks.push(...valid);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
// Mirror LanceDB dedup semantics: keep existing rows for touched files
|
|
13
|
+
// that are part of this write; drop prior-revision rows that aren't.
|
|
14
|
+
const newIds = new Set(valid.map((c) => c.id));
|
|
15
|
+
const newPaths = new Set(valid.map((c) => c.metadata.filePath));
|
|
16
|
+
this.chunks = [
|
|
17
|
+
...this.chunks.filter((c) => !newPaths.has(c.metadata.filePath) || newIds.has(c.id)),
|
|
18
|
+
...valid,
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
async addChunksBulk(items) {
|
|
22
|
+
for (const item of items) {
|
|
23
|
+
await this.addChunks(item.chunks, { dedup: item.dedup });
|
|
24
|
+
}
|
|
6
25
|
}
|
|
7
26
|
async search(embedding, topK) {
|
|
8
27
|
return this.searchWithFilter(embedding, topK);
|
|
@@ -78,7 +97,7 @@ export class InMemoryVectorStore {
|
|
|
78
97
|
async close() {
|
|
79
98
|
}
|
|
80
99
|
/** No-op for the in-memory store. */
|
|
81
|
-
async optimize() {
|
|
100
|
+
async optimize(_options) {
|
|
82
101
|
}
|
|
83
102
|
}
|
|
84
103
|
/**
|
package/dist/watcher.d.ts
CHANGED
|
@@ -40,6 +40,14 @@ export type WatcherStatus = {
|
|
|
40
40
|
/** Timestamp (ms since epoch) of the last completed run, or undefined. */
|
|
41
41
|
lastRunAt: number | undefined;
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Atomically claim the watcher lock for a workspace. Returns true only if
|
|
45
|
+
* this process is now the active watcher. A stale lock (dead PID) or an
|
|
46
|
+
* unreadable/corrupt lock file is cleared and re-claimed.
|
|
47
|
+
*/
|
|
48
|
+
export declare function tryAcquireWatcherLock(storePath: string): boolean;
|
|
49
|
+
/** Release the watcher lock — only if this process owns it. */
|
|
50
|
+
export declare function releaseWatcherLock(storePath: string): void;
|
|
43
51
|
/**
|
|
44
52
|
* Create a background file watcher that automatically re-indexes the
|
|
45
53
|
* workspace when files change. Uses chokidar for file system events and
|