opencode-rag-plugin 1.12.17 → 1.14.7
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/ReadMe.md +15 -13
- package/dist/api.d.ts +54 -2
- package/dist/api.js +41 -1
- package/dist/api.js.map +1 -1
- package/dist/chunker/base.js +2 -0
- package/dist/chunker/base.js.map +1 -1
- package/dist/chunker/bash.d.ts +5 -0
- package/dist/chunker/bash.js +5 -0
- package/dist/chunker/bash.js.map +1 -1
- package/dist/chunker/c.d.ts +6 -0
- package/dist/chunker/c.js +6 -0
- package/dist/chunker/c.js.map +1 -1
- package/dist/chunker/cpp.d.ts +6 -0
- package/dist/chunker/cpp.js +6 -0
- package/dist/chunker/cpp.js.map +1 -1
- package/dist/chunker/csharp.d.ts +6 -0
- package/dist/chunker/csharp.js +6 -0
- package/dist/chunker/csharp.js.map +1 -1
- package/dist/chunker/css.d.ts +6 -0
- package/dist/chunker/css.js +6 -0
- package/dist/chunker/css.js.map +1 -1
- package/dist/chunker/doc.d.ts +18 -0
- package/dist/chunker/doc.js +18 -0
- package/dist/chunker/doc.js.map +1 -1
- package/dist/chunker/dockerfile.d.ts +6 -0
- package/dist/chunker/dockerfile.js +6 -0
- package/dist/chunker/dockerfile.js.map +1 -1
- package/dist/chunker/docx.d.ts +18 -0
- package/dist/chunker/docx.js +18 -0
- package/dist/chunker/docx.js.map +1 -1
- package/dist/chunker/excel.d.ts +19 -0
- package/dist/chunker/excel.js +19 -0
- package/dist/chunker/excel.js.map +1 -1
- package/dist/chunker/go.d.ts +5 -0
- package/dist/chunker/go.js +5 -0
- package/dist/chunker/go.js.map +1 -1
- package/dist/chunker/grammar.d.ts +62 -0
- package/dist/chunker/grammar.js +133 -0
- package/dist/chunker/grammar.js.map +1 -1
- package/dist/chunker/html.d.ts +5 -0
- package/dist/chunker/html.js +5 -0
- package/dist/chunker/html.js.map +1 -1
- package/dist/chunker/image.d.ts +31 -0
- package/dist/chunker/image.js +50 -4
- package/dist/chunker/image.js.map +1 -1
- package/dist/chunker/ini.d.ts +5 -0
- package/dist/chunker/ini.js +5 -0
- package/dist/chunker/ini.js.map +1 -1
- package/dist/chunker/java.d.ts +5 -0
- package/dist/chunker/java.js +5 -0
- package/dist/chunker/java.js.map +1 -1
- package/dist/chunker/javascript.d.ts +6 -0
- package/dist/chunker/javascript.js +6 -0
- package/dist/chunker/javascript.js.map +1 -1
- package/dist/chunker/json.d.ts +5 -0
- package/dist/chunker/json.js +5 -0
- package/dist/chunker/json.js.map +1 -1
- package/dist/chunker/kotlin.d.ts +6 -0
- package/dist/chunker/kotlin.js +6 -0
- package/dist/chunker/kotlin.js.map +1 -1
- package/dist/chunker/loader.d.ts +11 -0
- package/dist/chunker/loader.js +20 -0
- package/dist/chunker/loader.js.map +1 -1
- package/dist/chunker/markdown.d.ts +5 -0
- package/dist/chunker/markdown.js +5 -0
- package/dist/chunker/markdown.js.map +1 -1
- package/dist/chunker/pdf.d.ts +19 -0
- package/dist/chunker/pdf.js +30 -0
- package/dist/chunker/pdf.js.map +1 -1
- package/dist/chunker/php.d.ts +5 -0
- package/dist/chunker/php.js +5 -0
- package/dist/chunker/php.js.map +1 -1
- package/dist/chunker/powershell.d.ts +5 -0
- package/dist/chunker/powershell.js +5 -0
- package/dist/chunker/powershell.js.map +1 -1
- package/dist/chunker/python.d.ts +5 -0
- package/dist/chunker/python.js +5 -0
- package/dist/chunker/python.js.map +1 -1
- package/dist/chunker/razor.d.ts +12 -0
- package/dist/chunker/razor.js +25 -0
- package/dist/chunker/razor.js.map +1 -1
- package/dist/chunker/ruby.d.ts +5 -0
- package/dist/chunker/ruby.js +5 -0
- package/dist/chunker/ruby.js.map +1 -1
- package/dist/chunker/rust.d.ts +6 -0
- package/dist/chunker/rust.js +6 -0
- package/dist/chunker/rust.js.map +1 -1
- package/dist/chunker/sln.d.ts +11 -0
- package/dist/chunker/sln.js +11 -0
- package/dist/chunker/sln.js.map +1 -1
- package/dist/chunker/sql.d.ts +5 -0
- package/dist/chunker/sql.js +5 -0
- package/dist/chunker/sql.js.map +1 -1
- package/dist/chunker/ssl.d.ts +12 -0
- package/dist/chunker/ssl.js +21 -2
- package/dist/chunker/ssl.js.map +1 -1
- package/dist/chunker/swift.d.ts +5 -0
- package/dist/chunker/swift.js +5 -0
- package/dist/chunker/swift.js.map +1 -1
- package/dist/chunker/tex.d.ts +12 -0
- package/dist/chunker/tex.js +12 -0
- package/dist/chunker/tex.js.map +1 -1
- package/dist/chunker/toml.d.ts +5 -0
- package/dist/chunker/toml.js +5 -0
- package/dist/chunker/toml.js.map +1 -1
- package/dist/chunker/typescript.d.ts +6 -0
- package/dist/chunker/typescript.js +6 -0
- package/dist/chunker/typescript.js.map +1 -1
- package/dist/chunker/xml.d.ts +5 -0
- package/dist/chunker/xml.js +5 -0
- package/dist/chunker/xml.js.map +1 -1
- package/dist/chunker/yaml.d.ts +5 -0
- package/dist/chunker/yaml.js +5 -0
- package/dist/chunker/yaml.js.map +1 -1
- package/dist/cli/commands/clear.d.ts +12 -0
- package/dist/cli/commands/clear.js +81 -0
- package/dist/cli/commands/clear.js.map +1 -0
- package/dist/cli/commands/describe-image.d.ts +14 -0
- package/dist/cli/commands/describe-image.js +68 -0
- package/dist/cli/commands/describe-image.js.map +1 -0
- package/dist/cli/commands/dump.d.ts +13 -0
- package/dist/cli/commands/dump.js +55 -0
- package/dist/cli/commands/dump.js.map +1 -0
- package/dist/cli/commands/eval.d.ts +32 -0
- package/dist/cli/commands/eval.js +158 -0
- package/dist/cli/commands/eval.js.map +1 -0
- package/dist/cli/commands/index-command.d.ts +17 -0
- package/dist/cli/commands/index-command.js +167 -0
- package/dist/cli/commands/index-command.js.map +1 -0
- package/dist/cli/commands/index.d.ts +16 -0
- package/dist/cli/commands/index.js +17 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init-helpers.d.ts +101 -0
- package/dist/cli/commands/init-helpers.js +398 -0
- package/dist/cli/commands/init-helpers.js.map +1 -0
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.js +251 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/list.d.ts +13 -0
- package/dist/cli/commands/list.js +46 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +14 -0
- package/dist/cli/commands/mcp.js +35 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/query.d.ts +14 -0
- package/dist/cli/commands/query.js +86 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/show.d.ts +13 -0
- package/dist/cli/commands/show.js +51 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/status.d.ts +14 -0
- package/dist/cli/commands/status.js +104 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/ui.d.ts +14 -0
- package/dist/cli/commands/ui.js +69 -0
- package/dist/cli/commands/ui.js.map +1 -0
- package/dist/cli/commands/update.d.ts +14 -0
- package/dist/cli/commands/update.js +76 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/format.d.ts +112 -0
- package/dist/cli/format.js +184 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/helpers.d.ts +51 -0
- package/dist/cli/helpers.js +86 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/index.d.ts +28 -0
- package/dist/cli/index.js +107 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/progress.d.ts +39 -0
- package/dist/cli/progress.js +137 -0
- package/dist/cli/progress.js.map +1 -0
- package/dist/cli/types.d.ts +40 -0
- package/dist/cli/types.js +3 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli.d.ts +11 -4
- package/dist/cli.js +9 -1253
- package/dist/cli.js.map +1 -1
- package/dist/content/doc.d.ts +1 -1
- package/dist/content/doc.js +1 -1
- package/dist/content/doc.js.map +1 -1
- package/dist/content/docx.d.ts +1 -1
- package/dist/content/docx.js +1 -1
- package/dist/content/docx.js.map +1 -1
- package/dist/content/excel.d.ts +1 -1
- package/dist/content/excel.js +1 -1
- package/dist/content/excel.js.map +1 -1
- package/dist/content/pdf.d.ts +1 -1
- package/dist/content/pdf.js +1 -1
- package/dist/content/pdf.js.map +1 -1
- package/dist/content/reader.d.ts +3 -1
- package/dist/content/reader.js +42 -10
- package/dist/content/reader.js.map +1 -1
- package/dist/core/bootstrap.d.ts +18 -0
- package/dist/core/bootstrap.js +9 -15
- package/dist/core/bootstrap.js.map +1 -1
- package/dist/core/config.d.ts +146 -0
- package/dist/core/config.js +66 -14
- package/dist/core/config.js.map +1 -1
- package/dist/core/doc-progress.d.ts +15 -0
- package/dist/core/doc-progress.js +61 -0
- package/dist/core/doc-progress.js.map +1 -0
- package/dist/core/fileLogger.d.ts +7 -0
- package/dist/core/fileLogger.js +2 -0
- package/dist/core/fileLogger.js.map +1 -1
- package/dist/core/interfaces.d.ts +52 -1
- package/dist/core/manifest.d.ts +25 -0
- package/dist/core/manifest.js +15 -1
- package/dist/core/manifest.js.map +1 -1
- package/dist/core/provider-defaults.d.ts +8 -0
- package/dist/core/provider-defaults.js +3 -0
- package/dist/core/provider-defaults.js.map +1 -1
- package/dist/core/rag-injection-flag.d.ts +3 -0
- package/dist/core/rag-injection-flag.js +2 -0
- package/dist/core/rag-injection-flag.js.map +1 -1
- package/dist/core/resolve-api-key.d.ts +1 -0
- package/dist/core/resolve-api-key.js +1 -0
- package/dist/core/resolve-api-key.js.map +1 -1
- package/dist/core/runtime-overrides.d.ts +4 -0
- package/dist/core/runtime-overrides.js +4 -1
- package/dist/core/runtime-overrides.js.map +1 -1
- package/dist/describer/anthropic.d.ts +22 -2
- package/dist/describer/anthropic.js +43 -111
- package/dist/describer/anthropic.js.map +1 -1
- package/dist/describer/describer.d.ts +21 -4
- package/dist/describer/describer.js +53 -112
- package/dist/describer/describer.js.map +1 -1
- package/dist/describer/gemini.d.ts +1 -2
- package/dist/describer/gemini.js +21 -114
- package/dist/describer/gemini.js.map +1 -1
- package/dist/describer/shared.d.ts +3 -0
- package/dist/describer/shared.js +23 -0
- package/dist/describer/shared.js.map +1 -0
- package/dist/embedder/factory.d.ts +25 -0
- package/dist/embedder/factory.js +25 -0
- package/dist/embedder/factory.js.map +1 -1
- package/dist/embedder/health.d.ts +10 -0
- package/dist/embedder/health.js +26 -14
- package/dist/embedder/health.js.map +1 -1
- package/dist/embedder/http.d.ts +47 -0
- package/dist/embedder/http.js +61 -3
- package/dist/embedder/http.js.map +1 -1
- package/dist/eval/index.d.ts +6 -0
- package/dist/eval/index.js.map +1 -1
- package/dist/eval/run-token-test.js +1 -5
- package/dist/eval/run-token-test.js.map +1 -1
- package/dist/eval/session-logger.d.ts +5 -0
- package/dist/eval/session-logger.js +4 -0
- package/dist/eval/session-logger.js.map +1 -1
- package/dist/eval/storage.d.ts +9 -0
- package/dist/eval/storage.js +9 -5
- package/dist/eval/storage.js.map +1 -1
- package/dist/eval/token-analysis.d.ts +8 -0
- package/dist/eval/token-analysis.js +6 -2
- package/dist/eval/token-analysis.js.map +1 -1
- package/dist/eval/token-counter.d.ts +1 -3
- package/dist/eval/token-counter.js +4 -5
- package/dist/eval/token-counter.js.map +1 -1
- package/dist/eval/types.d.ts +6 -0
- package/dist/eval/types.js +2 -0
- package/dist/eval/types.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/indexer/description-stage.d.ts +1 -1
- package/dist/indexer/description-stage.js +25 -5
- package/dist/indexer/description-stage.js.map +1 -1
- package/dist/indexer/embed-stage.js +0 -8
- package/dist/indexer/embed-stage.js.map +1 -1
- package/dist/indexer/pipeline.d.ts +27 -3
- package/dist/indexer/pipeline.js +325 -143
- package/dist/indexer/pipeline.js.map +1 -1
- package/dist/indexer/stats.d.ts +35 -0
- package/dist/indexer/stats.js +9 -0
- package/dist/indexer/stats.js.map +1 -1
- package/dist/indexer/watch.d.ts +25 -0
- package/dist/indexer/watch.js +21 -0
- package/dist/indexer/watch.js.map +1 -1
- package/dist/indexer/worker.d.ts +96 -2
- package/dist/indexer/worker.js +109 -24
- package/dist/indexer/worker.js.map +1 -1
- package/dist/indexer.d.ts +11 -1
- package/dist/indexer.js +9 -0
- package/dist/indexer.js.map +1 -1
- package/dist/mcp/cli.d.ts +3 -0
- package/dist/mcp/cli.js +1 -0
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/handlers.d.ts +41 -0
- package/dist/mcp/handlers.js +40 -1
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +18 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/opencode/create-read-tool.js +1 -0
- package/dist/opencode/create-read-tool.js.map +1 -1
- package/dist/opencode/tools.d.ts +8 -17
- package/dist/opencode/tools.js +74 -109
- package/dist/opencode/tools.js.map +1 -1
- package/dist/plugin-entry.d.ts +6 -0
- package/dist/plugin-entry.js +6 -0
- package/dist/plugin-entry.js.map +1 -1
- package/dist/plugin.d.ts +37 -0
- package/dist/plugin.js +278 -41
- package/dist/plugin.js.map +1 -1
- package/dist/retriever/keyword-index.js +9 -9
- package/dist/retriever/keyword-index.js.map +1 -1
- package/dist/retriever/retriever.js +14 -4
- package/dist/retriever/retriever.js.map +1 -1
- package/dist/tui.d.ts +5 -0
- package/dist/tui.js +101 -13
- package/dist/tui.js.map +1 -1
- package/dist/types/opencode-plugin.d.ts +4 -1
- package/dist/updater.d.ts +23 -1
- package/dist/updater.js +55 -16
- package/dist/updater.js.map +1 -1
- package/dist/vectorstore/factory.d.ts +13 -0
- package/dist/vectorstore/factory.js +13 -0
- package/dist/vectorstore/factory.js.map +1 -1
- package/dist/vectorstore/lancedb.d.ts +78 -1
- package/dist/vectorstore/lancedb.js +195 -21
- package/dist/vectorstore/lancedb.js.map +1 -1
- package/dist/vectorstore/memory.d.ts +5 -0
- package/dist/vectorstore/memory.js +11 -0
- package/dist/vectorstore/memory.js.map +1 -1
- package/dist/watcher.d.ts +24 -0
- package/dist/watcher.js +17 -35
- package/dist/watcher.js.map +1 -1
- package/dist/web/api.d.ts +37 -0
- package/dist/web/api.js +54 -0
- package/dist/web/api.js.map +1 -1
- package/dist/web/server.d.ts +15 -0
- package/dist/web/server.js +14 -0
- package/dist/web/server.js.map +1 -1
- package/dist/web/static.d.ts +8 -0
- package/dist/web/static.js +8 -0
- package/dist/web/static.js.map +1 -1
- package/package.json +11 -15
package/dist/indexer/pipeline.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import pLimit from "p-limit";
|
|
3
4
|
import { scanWorkspaceFiles } from "../content/reader.js";
|
|
4
5
|
import { loadManifest, saveManifest, normalizeFilePath } from "../core/manifest.js";
|
|
5
6
|
import { embedBatch } from "../embedder/factory.js";
|
|
7
|
+
import { createVectorStore } from "../vectorstore/factory.js";
|
|
8
|
+
import { swapStoreDirectories } from "../vectorstore/lancedb.js";
|
|
6
9
|
import { createIndexStats } from "./stats.js";
|
|
7
|
-
import { prepareFile } from "./worker.js";
|
|
10
|
+
import { prepareFile, buildTextsToEmbed, storeFileChunks } from "./worker.js";
|
|
11
|
+
import { buildFallbackDescription } from "./description-stage.js";
|
|
8
12
|
import { getCurrentCommit, getChangedFilesSince, getUntrackedFiles, getRepoRoot } from "./git-diff.js";
|
|
9
13
|
export { createWatchPassScheduler, createWatchIgnore } from "./watch.js";
|
|
10
14
|
function createLogger(logger) {
|
|
@@ -29,17 +33,77 @@ function tryUpdateLastGitCommit(cwd, manifest) {
|
|
|
29
33
|
return false;
|
|
30
34
|
}
|
|
31
35
|
}
|
|
36
|
+
const LOCK_FILE = "index.lock";
|
|
37
|
+
const LOCK_MAX_AGE_MS = 5 * 60 * 1000;
|
|
38
|
+
function isPidAlive(pid) {
|
|
39
|
+
try {
|
|
40
|
+
process.kill(pid, 0);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Execute a full index pass: scan workspace, prepare files, generate embeddings
|
|
49
|
+
* (and optionally AI descriptions), and store results into the vector store.
|
|
50
|
+
* Uses a lock file to prevent concurrent passes.
|
|
51
|
+
*
|
|
52
|
+
* @param options - Configuration for the index pass.
|
|
53
|
+
* @returns Aggregate statistics for the pass.
|
|
54
|
+
*/
|
|
32
55
|
export async function runIndexPass(options) {
|
|
33
56
|
const logger = createLogger(options.logger);
|
|
57
|
+
const lockPath = path.join(options.storePath, LOCK_FILE);
|
|
58
|
+
try {
|
|
59
|
+
const raw = await fs.readFile(lockPath, "utf-8");
|
|
60
|
+
const lock = JSON.parse(raw);
|
|
61
|
+
const age = lock.startedAt ? Date.now() - lock.startedAt : Infinity;
|
|
62
|
+
if (lock.pid && !isPidAlive(lock.pid)) {
|
|
63
|
+
logger.debug(`Stale lock from dead process ${lock.pid} — continuing`);
|
|
64
|
+
}
|
|
65
|
+
else if (lock.startedAt && age < LOCK_MAX_AGE_MS) {
|
|
66
|
+
logger.warn(`Another index pass is running (PID ${lock.pid ?? "unknown"}). Skipping.`);
|
|
67
|
+
return createIndexStats(0, "missing");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// No lock file — proceed
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
await fs.writeFile(lockPath, JSON.stringify({ pid: process.pid, startedAt: Date.now() }), "utf-8");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Best-effort lock
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return await runIndexPassInner(options, logger);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
try {
|
|
84
|
+
await fs.unlink(lockPath);
|
|
85
|
+
}
|
|
86
|
+
catch { }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function runIndexPassInner(options, logger) {
|
|
34
90
|
const loadResult = await loadManifest(options.storePath);
|
|
35
91
|
const manifest = loadResult.manifest;
|
|
36
92
|
let manifestStatus = loadResult.status;
|
|
37
93
|
let rebuildPerformed = false;
|
|
38
|
-
logger.
|
|
94
|
+
logger.info(`Manifest loaded: ${manifestStatus}, ${Object.keys(manifest.files).length} entries`);
|
|
39
95
|
if (options.force) {
|
|
40
96
|
manifestStatus = "missing";
|
|
41
97
|
logger.debug("Force mode: ignoring manifest");
|
|
42
98
|
}
|
|
99
|
+
// Clear files that had description failures so they are fully re-indexed
|
|
100
|
+
const descFailedPaths = Object.keys(manifest.files).filter((p) => manifest.files[p]?.descriptionFailed);
|
|
101
|
+
if (descFailedPaths.length > 0) {
|
|
102
|
+
logger.info(` ${descFailedPaths.length} file(s) marked as description-failed — re-indexing`);
|
|
103
|
+
for (const p of descFailedPaths) {
|
|
104
|
+
delete manifest.files[p];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
43
107
|
let filterPaths;
|
|
44
108
|
let gitDeletedPaths = [];
|
|
45
109
|
if (!options.force && manifestStatus === "ok" && manifest.lastGitCommit) {
|
|
@@ -59,8 +123,10 @@ export async function runIndexPass(options) {
|
|
|
59
123
|
}
|
|
60
124
|
}
|
|
61
125
|
}
|
|
126
|
+
const scanStart = Date.now();
|
|
62
127
|
const workspaceFiles = await scanWorkspaceFiles(options.cwd, options.config, logger, options.force ? undefined : manifest, filterPaths);
|
|
63
|
-
|
|
128
|
+
const scanSec = ((Date.now() - scanStart) / 1000).toFixed(1);
|
|
129
|
+
logger.info(`Workspace scan complete: ${workspaceFiles.length} files in ${scanSec}s`);
|
|
64
130
|
const existingCount = await options.store.count();
|
|
65
131
|
// Detect data loss: if the store has far fewer chunks than the manifest expects,
|
|
66
132
|
// treat it as a corrupt store (e.g. schema migration dropped the old table).
|
|
@@ -76,8 +142,10 @@ export async function runIndexPass(options) {
|
|
|
76
142
|
manifestStatus = "missing";
|
|
77
143
|
}
|
|
78
144
|
}
|
|
145
|
+
// Effective store used throughout the pass — may be a temp store for atomic rebuild.
|
|
146
|
+
let effectiveStore = options.store;
|
|
147
|
+
let tempStorePath;
|
|
79
148
|
if (options.force || (manifestStatus !== "ok" && existingCount > 0)) {
|
|
80
|
-
await options.store.clear();
|
|
81
149
|
options.keywordIndex?.clear();
|
|
82
150
|
for (const key of Object.keys(manifest.files)) {
|
|
83
151
|
delete manifest.files[key];
|
|
@@ -88,6 +156,23 @@ export async function runIndexPass(options) {
|
|
|
88
156
|
logger.warn("Manifest missing or corrupt; rebuilding full index.");
|
|
89
157
|
}
|
|
90
158
|
manifestStatus = options.force ? "missing" : manifestStatus;
|
|
159
|
+
// Build the new index into a temporary store first, then atomically
|
|
160
|
+
// swap on completion. Original data stays untouched if the process
|
|
161
|
+
// is aborted (Ctrl+C, crash) before the swap completes.
|
|
162
|
+
if (options.dimension) {
|
|
163
|
+
tempStorePath = options.storePath + "_tmp";
|
|
164
|
+
try {
|
|
165
|
+
await fs.rm(tempStorePath, { recursive: true, force: true });
|
|
166
|
+
}
|
|
167
|
+
catch { /* may not exist */ }
|
|
168
|
+
effectiveStore = createVectorStore(options.config, tempStorePath, options.dimension);
|
|
169
|
+
logger.debug(`Rebuilding index in temporary store at ${tempStorePath}`);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// Fallback — in-memory store or no dimension: clear in place.
|
|
173
|
+
logger.warn("No embedding dimension available; falling back to in-place clear.");
|
|
174
|
+
await options.store.clear();
|
|
175
|
+
}
|
|
91
176
|
}
|
|
92
177
|
const stats = createIndexStats(workspaceFiles.length, manifestStatus);
|
|
93
178
|
stats.rebuildPerformed = rebuildPerformed;
|
|
@@ -103,7 +188,8 @@ export async function runIndexPass(options) {
|
|
|
103
188
|
const currentPaths = new Set(workspaceFiles.map((file) => file.normalizedPath));
|
|
104
189
|
let stalePaths;
|
|
105
190
|
if (filterPaths) {
|
|
106
|
-
|
|
191
|
+
const repoRoot = getRepoRoot(options.cwd) ?? options.cwd;
|
|
192
|
+
stalePaths = gitDeletedPaths.map((p) => normalizeFilePath(path.resolve(repoRoot, p)));
|
|
107
193
|
}
|
|
108
194
|
else {
|
|
109
195
|
stalePaths = Object.keys(manifest.files).filter((p) => !currentPaths.has(p));
|
|
@@ -112,7 +198,7 @@ export async function runIndexPass(options) {
|
|
|
112
198
|
logger.debug(`Removing ${stalePaths.length} stale files from index...`);
|
|
113
199
|
const deleteLimit = pLimit(options.config.indexing.concurrency);
|
|
114
200
|
await Promise.all(stalePaths.map((p) => deleteLimit(async () => {
|
|
115
|
-
await
|
|
201
|
+
await effectiveStore.deleteByFilePath(p);
|
|
116
202
|
options.keywordIndex?.removeByFilePath(p);
|
|
117
203
|
delete manifest.files[p];
|
|
118
204
|
stats.deletedFiles++;
|
|
@@ -136,143 +222,246 @@ export async function runIndexPass(options) {
|
|
|
136
222
|
}
|
|
137
223
|
}
|
|
138
224
|
if (stats.deletedFiles > 0) {
|
|
139
|
-
logger.
|
|
225
|
+
logger.debug(`Removed ${stats.deletedFiles} deleted files from index.`);
|
|
140
226
|
}
|
|
141
|
-
logger.
|
|
142
|
-
// ── Phase 1: prepare all files (chunk +
|
|
227
|
+
logger.debug(`Processing ${workspaceFiles.length} files (${newCount} new, ${modifiedCount} modified, ${unchangedCount} unchanged)...`);
|
|
228
|
+
// ── Phase 1: prepare all files (chunk + keyword, defer descriptions if provider present) ──
|
|
143
229
|
const limit = pLimit(options.config.indexing.concurrency);
|
|
144
|
-
|
|
145
|
-
let started = 0;
|
|
146
|
-
const total = workspaceFiles.length;
|
|
230
|
+
const deferDescriptions = !!options.descriptionProvider;
|
|
147
231
|
const prepared = await Promise.all(workspaceFiles.map((file) => limit(async () => {
|
|
148
|
-
const fileLabel = file.normalizedPath;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (total > 20 && completed % 50 === 0) {
|
|
154
|
-
logger.info(` Preparing file ${completed}/${total}...`);
|
|
232
|
+
const fileLabel = path.relative(options.cwd, file.normalizedPath).replace(/\\/g, "/");
|
|
233
|
+
const isActive = !file.isEmpty && !file.isTooSmall &&
|
|
234
|
+
(!manifest.files[file.normalizedPath] || manifest.files[file.normalizedPath].hash !== file.hash);
|
|
235
|
+
if (isActive) {
|
|
236
|
+
options.progress?.startFile(fileLabel);
|
|
155
237
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const deletionLimit = pLimit(options.config.indexing.concurrency);
|
|
160
|
-
await Promise.all(prepared.map((prep) => deletionLimit(async () => {
|
|
161
|
-
if (prep.earlyResult?.isRemoved) {
|
|
162
|
-
await options.store.deleteByFilePath(prep.normalizedPath);
|
|
163
|
-
options.keywordIndex?.removeByFilePath(prep.normalizedPath);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
if (prep.isModified && prep.chunks && prep.chunks.length > 0) {
|
|
167
|
-
await options.store.deleteByFilePath(prep.normalizedPath);
|
|
168
|
-
options.keywordIndex?.removeByFilePath(prep.normalizedPath);
|
|
238
|
+
const prep = await prepareFile(file, options.cwd, manifest.files[file.normalizedPath], options.config, options.keywordIndex, options.descriptionProvider, logger, deferDescriptions);
|
|
239
|
+
if (prep.earlyResult && isActive) {
|
|
240
|
+
options.progress?.finishFile(fileLabel);
|
|
169
241
|
}
|
|
242
|
+
return prep;
|
|
170
243
|
})));
|
|
171
|
-
// ── Phase
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
244
|
+
// ── Phase 1.5: global description generation (single pool, no nested concurrency) ──
|
|
245
|
+
if (deferDescriptions) {
|
|
246
|
+
const deferredPreps = prepared.filter((p) => p.chunks && p.chunks.length > 0 && p.relPath !== undefined);
|
|
247
|
+
if (deferredPreps.length > 0) {
|
|
248
|
+
const allChunks = [];
|
|
249
|
+
const oversizedChunks = [];
|
|
250
|
+
const maxContentChars = options.config.description?.maxContentChars;
|
|
251
|
+
for (const prep of deferredPreps) {
|
|
252
|
+
for (const chunk of prep.chunks) {
|
|
253
|
+
if (chunk.metadata.contentType !== "image") {
|
|
254
|
+
if (maxContentChars && chunk.content.length > maxContentChars) {
|
|
255
|
+
oversizedChunks.push(chunk);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
allChunks.push(chunk);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
for (const chunk of oversizedChunks) {
|
|
264
|
+
chunk.description = buildFallbackDescription(chunk);
|
|
265
|
+
}
|
|
266
|
+
// Advance progress to Description stage before descriptions start
|
|
267
|
+
for (const prep of deferredPreps) {
|
|
268
|
+
options.progress?.finishStage(prep.fileLabel);
|
|
269
|
+
}
|
|
270
|
+
if (allChunks.length > 0) {
|
|
271
|
+
try {
|
|
272
|
+
const batchResult = await options.descriptionProvider.generateBatchDescriptions(allChunks, (msg) => logger.debug(msg));
|
|
273
|
+
for (const chunk of allChunks) {
|
|
274
|
+
const desc = batchResult.get(chunk.id);
|
|
275
|
+
if (desc && desc.trim().length > 0) {
|
|
276
|
+
chunk.description = desc;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
logger.warn(` Global description generation failed: ${err.message}`);
|
|
282
|
+
for (const prep of deferredPreps) {
|
|
283
|
+
if (prep.chunks.some((c) => c.metadata.contentType !== "image")) {
|
|
284
|
+
prep.descriptionFailed = true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Phase 1.6: build textToEmbed with descriptions injected
|
|
290
|
+
for (const prep of deferredPreps) {
|
|
291
|
+
prep.textToEmbed = buildTextsToEmbed(prep.chunks, prep.relPath, prep.metaHeader ?? "", prep.docPrefix ?? "", prep.isImageFile ?? false);
|
|
180
292
|
}
|
|
181
293
|
}
|
|
182
294
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
295
|
+
// ── Phase 2+3: per-file delete, embed + store (in parallel) ──
|
|
296
|
+
// Deletions for modified/re-removed files happen per-worker, right before
|
|
297
|
+
// embedding, so that an abort mid-embed doesn't orphan old entries.
|
|
298
|
+
const isOllama = options.embedder.name === "ollama";
|
|
299
|
+
const ollamaMaxBatch = options.config.indexing.ollamaMaxBatchSize ?? 4000;
|
|
300
|
+
const defaultBatchSize = options.config.indexing.embedBatchSize;
|
|
301
|
+
const defaultConcurrency = options.config.indexing.embedConcurrency ?? 1;
|
|
302
|
+
// File metadata look-up for manifest entries
|
|
303
|
+
const fileMeta = new Map(workspaceFiles.map((f) => [f.normalizedPath, { mtime: f.mtime, size: f.size }]));
|
|
304
|
+
// Serialised manifest-save queue — prevents concurrent write races and acts
|
|
305
|
+
// as a checkpoint for Ctrl+C resilience. Each worker appends to this chain
|
|
306
|
+
// after a successful store, so previously completed files are never lost.
|
|
307
|
+
let manifestSaveChain = Promise.resolve(undefined);
|
|
308
|
+
function enqueueManifestSave() {
|
|
309
|
+
manifestSaveChain = manifestSaveChain.then(() => saveManifest(options.storePath, manifest));
|
|
189
310
|
}
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
embedByPrep.set(prepIdx, []);
|
|
311
|
+
const aborted = () => options.abortSignal?.aborted ?? false;
|
|
312
|
+
const embedStoreLimit = pLimit(options.config.indexing.concurrency);
|
|
313
|
+
const workerResults = await Promise.all(prepared.map((prep) => embedStoreLimit(async () => {
|
|
314
|
+
// ── Check abort signal before any work ──
|
|
315
|
+
if (aborted()) {
|
|
316
|
+
return { normalizedPath: prep.normalizedPath, skipped: true };
|
|
197
317
|
}
|
|
198
|
-
|
|
199
|
-
embedByPrep.get(prepIdx).push(emb);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
// ── Phase 3: store all files with their embeddings (batched) ──
|
|
203
|
-
logger.debug(`Phase 3: storing chunks for ${prepared.length} files...`);
|
|
204
|
-
const allValidChunks = [];
|
|
205
|
-
const workerResults = prepared.map((prep, idx) => {
|
|
318
|
+
// ── Early results (empty / too-small / unchanged / chunking failure) ──
|
|
206
319
|
if (prep.earlyResult) {
|
|
320
|
+
// Remove stale manifest/store entries for files that no longer produce chunks
|
|
321
|
+
if (prep.earlyResult.isRemoved) {
|
|
322
|
+
await effectiveStore.deleteByFilePath(prep.normalizedPath);
|
|
323
|
+
options.keywordIndex?.removeByFilePath(prep.normalizedPath);
|
|
324
|
+
delete manifest.files[prep.normalizedPath];
|
|
325
|
+
enqueueManifestSave();
|
|
326
|
+
}
|
|
207
327
|
return prep.earlyResult;
|
|
208
328
|
}
|
|
209
|
-
if (!prep.chunks || !prep.textToEmbed) {
|
|
329
|
+
if (!prep.chunks || !prep.textToEmbed || prep.textToEmbed.length === 0) {
|
|
330
|
+
options.progress?.finishFile(prep.fileLabel);
|
|
331
|
+
return {
|
|
332
|
+
normalizedPath: prep.normalizedPath, hash: prep.hash, chunkCount: 0,
|
|
333
|
+
fileLabel: prep.fileLabel,
|
|
334
|
+
isNew: false, isModified: false, isUnchanged: false, isEmpty: false,
|
|
335
|
+
isTooSmall: false, isRemoved: true, hadChunks: false,
|
|
336
|
+
descriptionFailed: prep.descriptionFailed,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
// ── Embed ──
|
|
340
|
+
const batchSize = isOllama
|
|
341
|
+
? Math.min(prep.textToEmbed.length, ollamaMaxBatch)
|
|
342
|
+
: defaultBatchSize;
|
|
343
|
+
const concurrency = isOllama ? 1 : defaultConcurrency;
|
|
344
|
+
options.progress?.finishStage(prep.fileLabel);
|
|
345
|
+
let embeddings;
|
|
346
|
+
try {
|
|
347
|
+
embeddings = await embedBatch(options.embedder, prep.textToEmbed, batchSize, "document", concurrency);
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
logger.warn(` ${prep.fileLabel} (embedding failed: ${err.message})`);
|
|
351
|
+
options.progress?.failFile(prep.fileLabel);
|
|
210
352
|
return {
|
|
211
|
-
normalizedPath: prep.normalizedPath, hash: prep.hash, chunkCount: 0,
|
|
212
|
-
|
|
353
|
+
normalizedPath: prep.normalizedPath, hash: prep.hash, chunkCount: 0,
|
|
354
|
+
fileLabel: prep.fileLabel,
|
|
355
|
+
isNew: false, isModified: false, isUnchanged: false, isEmpty: false,
|
|
356
|
+
isTooSmall: false, isRemoved: true, hadChunks: false,
|
|
357
|
+
descriptionFailed: prep.descriptionFailed,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
// ── Store (new data first; orphan cleanup is handled inside the store) ──
|
|
361
|
+
const result = await storeFileChunks(prep, embeddings, effectiveStore, logger);
|
|
362
|
+
// ── Update manifest in-memory and enqueue an atomic save ──
|
|
363
|
+
if (result.chunkCount > 0 && !result.isRemoved) {
|
|
364
|
+
const meta = fileMeta.get(result.normalizedPath);
|
|
365
|
+
manifest.files[result.normalizedPath] = {
|
|
366
|
+
hash: result.hash,
|
|
367
|
+
chunkCount: result.chunkCount,
|
|
368
|
+
indexedAt: Date.now(),
|
|
369
|
+
mtime: meta?.mtime,
|
|
370
|
+
size: meta?.size,
|
|
371
|
+
descriptionFailed: result.descriptionFailed,
|
|
213
372
|
};
|
|
373
|
+
enqueueManifestSave();
|
|
214
374
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
375
|
+
else if (result.isRemoved) {
|
|
376
|
+
delete manifest.files[result.normalizedPath];
|
|
377
|
+
enqueueManifestSave();
|
|
378
|
+
}
|
|
379
|
+
options.progress?.finishFile(prep.fileLabel);
|
|
380
|
+
return result;
|
|
381
|
+
})));
|
|
382
|
+
// Strip skipped sentinels
|
|
383
|
+
const finalResults = [];
|
|
384
|
+
for (const r of workerResults) {
|
|
385
|
+
if (r.skipped)
|
|
386
|
+
break;
|
|
387
|
+
finalResults.push(r);
|
|
388
|
+
}
|
|
389
|
+
// Drain any in-flight manifest saves so all file entries are durable
|
|
390
|
+
await manifestSaveChain;
|
|
391
|
+
// Update mtime/size for unchanged files (speeds up the next scan)
|
|
392
|
+
for (const { normalizedPath, mtime, size } of workspaceFiles) {
|
|
393
|
+
const entry = manifest.files[normalizedPath];
|
|
394
|
+
if (entry && (mtime !== undefined || size !== undefined)) {
|
|
395
|
+
entry.mtime = mtime;
|
|
396
|
+
entry.size = size;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// ── Aggregate stats ──
|
|
400
|
+
aggregateStats(stats, finalResults);
|
|
401
|
+
// Update timestamps; advance lastGitCommit ONLY on a complete pass
|
|
402
|
+
manifest.lastIndexedAt = Date.now();
|
|
403
|
+
if (!aborted()) {
|
|
404
|
+
tryUpdateLastGitCommit(options.cwd, manifest);
|
|
405
|
+
}
|
|
406
|
+
// ── Atomically promote temp store if a full rebuild was performed ──
|
|
407
|
+
if (tempStorePath) {
|
|
408
|
+
if (!aborted()) {
|
|
409
|
+
try {
|
|
410
|
+
await effectiveStore.close();
|
|
411
|
+
await options.store.close();
|
|
412
|
+
// Swap the newly-built temp directory into the real path
|
|
413
|
+
await swapStoreDirectories(tempStorePath, options.storePath);
|
|
414
|
+
// Re-open the original store handle so callers can search the new data
|
|
415
|
+
if (typeof options.store.reopen === "function") {
|
|
416
|
+
await options.store.reopen(options.storePath);
|
|
417
|
+
}
|
|
418
|
+
logger.debug(`Promoted temporary store ${tempStorePath} → ${options.storePath}`);
|
|
220
419
|
}
|
|
221
|
-
|
|
222
|
-
|
|
420
|
+
catch (err) {
|
|
421
|
+
logger.warn(`Could not promote temporary store: ${err.message}. ` +
|
|
422
|
+
`Original data preserved at ${options.storePath}`);
|
|
423
|
+
try {
|
|
424
|
+
await fs.rm(tempStorePath, { recursive: true, force: true });
|
|
425
|
+
}
|
|
426
|
+
catch { }
|
|
223
427
|
}
|
|
224
428
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
isNew: !prep.isModified,
|
|
234
|
-
isModified: prep.isModified,
|
|
235
|
-
isUnchanged: false,
|
|
236
|
-
isEmpty: false,
|
|
237
|
-
isTooSmall: false,
|
|
238
|
-
isRemoved: false,
|
|
239
|
-
hadChunks: prep.chunks.length > 0,
|
|
240
|
-
};
|
|
241
|
-
});
|
|
242
|
-
if (allValidChunks.length > 0) {
|
|
243
|
-
const STORE_BATCH = 1000;
|
|
244
|
-
for (let i = 0; i < allValidChunks.length; i += STORE_BATCH) {
|
|
245
|
-
const batch = allValidChunks.slice(i, i + STORE_BATCH);
|
|
246
|
-
await options.store.addChunks(batch);
|
|
429
|
+
else {
|
|
430
|
+
// Aborted — discard temp, keep original data intact
|
|
431
|
+
effectiveStore.close().catch(() => { });
|
|
432
|
+
try {
|
|
433
|
+
await fs.rm(tempStorePath, { recursive: true, force: true });
|
|
434
|
+
}
|
|
435
|
+
catch { }
|
|
436
|
+
logger.debug("Index pass cancelled; discarded temporary store.");
|
|
247
437
|
}
|
|
248
|
-
logger.debug(` Stored ${allValidChunks.length} chunks in batched writes`);
|
|
249
438
|
}
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
manifest.lastIndexedAt = Date.now();
|
|
253
|
-
tryUpdateLastGitCommit(options.cwd, manifest);
|
|
439
|
+
// Save manifest and keyword index (always to the real store path — after
|
|
440
|
+
// a successful swap this points to the new data; after an abort it's the old).
|
|
254
441
|
await saveManifest(options.storePath, manifest);
|
|
255
442
|
await options.keywordIndex?.save(options.storePath);
|
|
256
|
-
|
|
443
|
+
// Count from the store — after a successful swap, the original handle has
|
|
444
|
+
// been reopened pointing to the new directory.
|
|
445
|
+
try {
|
|
446
|
+
stats.finalCount = await options.store.count();
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
stats.finalCount = (tempStorePath ? 0 : stats.totalChunks);
|
|
450
|
+
}
|
|
257
451
|
return stats;
|
|
258
452
|
}
|
|
259
|
-
function aggregateStats(stats, results
|
|
260
|
-
const fileMeta = new Map(workspaceFiles.map((f) => [f.normalizedPath, { mtime: f.mtime, size: f.size }]));
|
|
453
|
+
function aggregateStats(stats, results) {
|
|
261
454
|
for (const result of results) {
|
|
262
455
|
if (result.isEmpty) {
|
|
263
456
|
stats.skippedEmptyFiles++;
|
|
264
|
-
if (result.isRemoved)
|
|
265
|
-
delete manifest.files[result.normalizedPath];
|
|
457
|
+
if (result.isRemoved)
|
|
266
458
|
stats.removedFiles++;
|
|
267
|
-
}
|
|
268
459
|
continue;
|
|
269
460
|
}
|
|
270
461
|
if (result.isTooSmall) {
|
|
271
462
|
stats.skippedSmallFiles++;
|
|
272
|
-
if (result.isRemoved)
|
|
273
|
-
delete manifest.files[result.normalizedPath];
|
|
463
|
+
if (result.isRemoved)
|
|
274
464
|
stats.removedFiles++;
|
|
275
|
-
}
|
|
276
465
|
continue;
|
|
277
466
|
}
|
|
278
467
|
if (result.isUnchanged) {
|
|
@@ -280,7 +469,6 @@ function aggregateStats(stats, results, manifest, workspaceFiles) {
|
|
|
280
469
|
continue;
|
|
281
470
|
}
|
|
282
471
|
if (result.isRemoved) {
|
|
283
|
-
delete manifest.files[result.normalizedPath];
|
|
284
472
|
stats.removedFiles++;
|
|
285
473
|
continue;
|
|
286
474
|
}
|
|
@@ -291,65 +479,59 @@ function aggregateStats(stats, results, manifest, workspaceFiles) {
|
|
|
291
479
|
stats.newFiles++;
|
|
292
480
|
}
|
|
293
481
|
if (result.chunkCount > 0) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
chunkCount: result.chunkCount,
|
|
298
|
-
indexedAt: Date.now(),
|
|
299
|
-
mtime: meta?.mtime,
|
|
300
|
-
size: meta?.size,
|
|
301
|
-
};
|
|
482
|
+
if (result.descriptionFailed) {
|
|
483
|
+
stats.descriptionFailedFiles++;
|
|
484
|
+
}
|
|
302
485
|
stats.totalChunks += result.chunkCount;
|
|
303
486
|
stats.batchesFlushed++;
|
|
304
487
|
}
|
|
305
488
|
}
|
|
306
|
-
for (const { normalizedPath, mtime, size } of workspaceFiles) {
|
|
307
|
-
const entry = manifest.files[normalizedPath];
|
|
308
|
-
if (entry && (mtime !== undefined || size !== undefined)) {
|
|
309
|
-
entry.mtime = mtime;
|
|
310
|
-
entry.size = size;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
489
|
}
|
|
314
|
-
export async function getIndexStatusSummary(cwd, storePath, config, store) {
|
|
490
|
+
export async function getIndexStatusSummary(cwd, storePath, config, store, skipScan) {
|
|
315
491
|
const loadResult = await loadManifest(storePath);
|
|
316
492
|
const manifest = loadResult.manifest;
|
|
317
|
-
const workspaceFiles = await scanWorkspaceFiles(cwd, config, undefined, manifest);
|
|
318
493
|
const storeCount = await store.count();
|
|
319
494
|
if (loadResult.status !== "ok") {
|
|
320
495
|
return {
|
|
321
496
|
manifestStatus: loadResult.status,
|
|
322
497
|
manifestEntries: 0,
|
|
323
498
|
upToDateFiles: 0,
|
|
324
|
-
pendingFiles:
|
|
499
|
+
pendingFiles: 0,
|
|
325
500
|
rebuildRequired: storeCount > 0,
|
|
326
501
|
storeChunkCount: storeCount,
|
|
327
502
|
manifestExpectedChunks: 0,
|
|
328
503
|
};
|
|
329
504
|
}
|
|
330
|
-
const currentPaths = new Set(workspaceFiles.map((file) => file.normalizedPath));
|
|
331
505
|
let upToDateFiles = 0;
|
|
332
506
|
let pendingFiles = 0;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
507
|
+
let manifestExpectedChunks = 0;
|
|
508
|
+
if (!skipScan) {
|
|
509
|
+
const workspaceFiles = await scanWorkspaceFiles(cwd, config, undefined, manifest);
|
|
510
|
+
const currentPaths = new Set(workspaceFiles.map((file) => file.normalizedPath));
|
|
511
|
+
for (const file of workspaceFiles) {
|
|
512
|
+
const previous = manifest.files[file.normalizedPath];
|
|
513
|
+
if (file.isEmpty || file.isTooSmall) {
|
|
514
|
+
if (previous)
|
|
515
|
+
pendingFiles++;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (previous && previous.hash === file.hash) {
|
|
519
|
+
upToDateFiles++;
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
337
522
|
pendingFiles++;
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
if (previous && previous.hash === file.hash) {
|
|
341
|
-
upToDateFiles++;
|
|
523
|
+
}
|
|
342
524
|
}
|
|
343
|
-
|
|
344
|
-
|
|
525
|
+
for (const indexedPath of Object.keys(manifest.files)) {
|
|
526
|
+
if (!currentPaths.has(indexedPath)) {
|
|
527
|
+
pendingFiles++;
|
|
528
|
+
}
|
|
345
529
|
}
|
|
530
|
+
manifestExpectedChunks = Object.values(manifest.files).reduce((sum, entry) => sum + entry.chunkCount, 0);
|
|
346
531
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
pendingFiles++;
|
|
350
|
-
}
|
|
532
|
+
else {
|
|
533
|
+
manifestExpectedChunks = Object.values(manifest.files).reduce((sum, entry) => sum + entry.chunkCount, 0);
|
|
351
534
|
}
|
|
352
|
-
const manifestExpectedChunks = Object.values(manifest.files).reduce((sum, entry) => sum + entry.chunkCount, 0);
|
|
353
535
|
return {
|
|
354
536
|
manifestStatus: loadResult.status,
|
|
355
537
|
manifestEntries: Object.keys(manifest.files).length,
|