@pella-labs/pinakes 0.1.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/README.md +208 -0
- package/dist/cli/audit.d.ts +30 -0
- package/dist/cli/audit.d.ts.map +1 -0
- package/dist/cli/audit.js +49 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/export.d.ts +32 -0
- package/dist/cli/export.d.ts.map +1 -0
- package/dist/cli/export.js +73 -0
- package/dist/cli/export.js.map +1 -0
- package/dist/cli/import.d.ts +24 -0
- package/dist/cli/import.d.ts.map +1 -0
- package/dist/cli/import.js +96 -0
- package/dist/cli/import.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +172 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/purge.d.ts +23 -0
- package/dist/cli/purge.d.ts.map +1 -0
- package/dist/cli/purge.js +57 -0
- package/dist/cli/purge.js.map +1 -0
- package/dist/cli/rebuild.d.ts +54 -0
- package/dist/cli/rebuild.d.ts.map +1 -0
- package/dist/cli/rebuild.js +113 -0
- package/dist/cli/rebuild.js.map +1 -0
- package/dist/cli/serve.d.ts +49 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +296 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/status.d.ts +39 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +108 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/db/client.d.ts +109 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +175 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/repository.d.ts +82 -0
- package/dist/db/repository.d.ts.map +1 -0
- package/dist/db/repository.js +173 -0
- package/dist/db/repository.js.map +1 -0
- package/dist/db/schema.d.ts +990 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +259 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/types.d.ts +28 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +11 -0
- package/dist/db/types.js.map +1 -0
- package/dist/gaps/detector.d.ts +67 -0
- package/dist/gaps/detector.d.ts.map +1 -0
- package/dist/gaps/detector.js +160 -0
- package/dist/gaps/detector.js.map +1 -0
- package/dist/gate/budget.d.ts +90 -0
- package/dist/gate/budget.d.ts.map +1 -0
- package/dist/gate/budget.js +145 -0
- package/dist/gate/budget.js.map +1 -0
- package/dist/ingest/chokidar.d.ts +33 -0
- package/dist/ingest/chokidar.d.ts.map +1 -0
- package/dist/ingest/chokidar.js +152 -0
- package/dist/ingest/chokidar.js.map +1 -0
- package/dist/ingest/ingester.d.ts +117 -0
- package/dist/ingest/ingester.d.ts.map +1 -0
- package/dist/ingest/ingester.js +312 -0
- package/dist/ingest/ingester.js.map +1 -0
- package/dist/ingest/manifest.d.ts +87 -0
- package/dist/ingest/manifest.d.ts.map +1 -0
- package/dist/ingest/manifest.js +223 -0
- package/dist/ingest/manifest.js.map +1 -0
- package/dist/ingest/memory-store.d.ts +55 -0
- package/dist/ingest/memory-store.d.ts.map +1 -0
- package/dist/ingest/memory-store.js +94 -0
- package/dist/ingest/memory-store.js.map +1 -0
- package/dist/ingest/parse/chunk.d.ts +15 -0
- package/dist/ingest/parse/chunk.d.ts.map +1 -0
- package/dist/ingest/parse/chunk.js +88 -0
- package/dist/ingest/parse/chunk.js.map +1 -0
- package/dist/ingest/parse/markdown.d.ts +64 -0
- package/dist/ingest/parse/markdown.d.ts.map +1 -0
- package/dist/ingest/parse/markdown.js +152 -0
- package/dist/ingest/parse/markdown.js.map +1 -0
- package/dist/ingest/queue.d.ts +21 -0
- package/dist/ingest/queue.d.ts.map +1 -0
- package/dist/ingest/queue.js +24 -0
- package/dist/ingest/queue.js.map +1 -0
- package/dist/ingest/source.d.ts +42 -0
- package/dist/ingest/source.d.ts.map +1 -0
- package/dist/ingest/source.js +19 -0
- package/dist/ingest/source.js.map +1 -0
- package/dist/mcp/envelope.d.ts +73 -0
- package/dist/mcp/envelope.d.ts.map +1 -0
- package/dist/mcp/envelope.js +46 -0
- package/dist/mcp/envelope.js.map +1 -0
- package/dist/mcp/tools/execute.d.ts +55 -0
- package/dist/mcp/tools/execute.d.ts.map +1 -0
- package/dist/mcp/tools/execute.js +232 -0
- package/dist/mcp/tools/execute.js.map +1 -0
- package/dist/mcp/tools/search.d.ts +53 -0
- package/dist/mcp/tools/search.d.ts.map +1 -0
- package/dist/mcp/tools/search.js +114 -0
- package/dist/mcp/tools/search.js.map +1 -0
- package/dist/observability/audit.d.ts +25 -0
- package/dist/observability/audit.d.ts.map +1 -0
- package/dist/observability/audit.js +38 -0
- package/dist/observability/audit.js.map +1 -0
- package/dist/observability/logger.d.ts +4 -0
- package/dist/observability/logger.d.ts.map +1 -0
- package/dist/observability/logger.js +56 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/observability/metrics.d.ts +38 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +64 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/retrieval/embedder.d.ts +130 -0
- package/dist/retrieval/embedder.d.ts.map +1 -0
- package/dist/retrieval/embedder.js +278 -0
- package/dist/retrieval/embedder.js.map +1 -0
- package/dist/retrieval/fts.d.ts +42 -0
- package/dist/retrieval/fts.d.ts.map +1 -0
- package/dist/retrieval/fts.js +46 -0
- package/dist/retrieval/fts.js.map +1 -0
- package/dist/retrieval/hybrid.d.ts +43 -0
- package/dist/retrieval/hybrid.d.ts.map +1 -0
- package/dist/retrieval/hybrid.js +120 -0
- package/dist/retrieval/hybrid.js.map +1 -0
- package/dist/retrieval/vec.d.ts +39 -0
- package/dist/retrieval/vec.d.ts.map +1 -0
- package/dist/retrieval/vec.js +50 -0
- package/dist/retrieval/vec.js.map +1 -0
- package/dist/sandbox/bindings/budget.d.ts +10 -0
- package/dist/sandbox/bindings/budget.d.ts.map +1 -0
- package/dist/sandbox/bindings/budget.js +44 -0
- package/dist/sandbox/bindings/budget.js.map +1 -0
- package/dist/sandbox/bindings/install.d.ts +23 -0
- package/dist/sandbox/bindings/install.d.ts.map +1 -0
- package/dist/sandbox/bindings/install.js +15 -0
- package/dist/sandbox/bindings/install.js.map +1 -0
- package/dist/sandbox/bindings/kg.d.ts +29 -0
- package/dist/sandbox/bindings/kg.d.ts.map +1 -0
- package/dist/sandbox/bindings/kg.js +323 -0
- package/dist/sandbox/bindings/kg.js.map +1 -0
- package/dist/sandbox/bindings/logger.d.ts +11 -0
- package/dist/sandbox/bindings/logger.d.ts.map +1 -0
- package/dist/sandbox/bindings/logger.js +33 -0
- package/dist/sandbox/bindings/logger.js.map +1 -0
- package/dist/sandbox/bindings/write.d.ts +34 -0
- package/dist/sandbox/bindings/write.d.ts.map +1 -0
- package/dist/sandbox/bindings/write.js +195 -0
- package/dist/sandbox/bindings/write.js.map +1 -0
- package/dist/sandbox/executor.d.ts +68 -0
- package/dist/sandbox/executor.d.ts.map +1 -0
- package/dist/sandbox/executor.js +280 -0
- package/dist/sandbox/executor.js.map +1 -0
- package/dist/sandbox/helpers.d.ts +26 -0
- package/dist/sandbox/helpers.d.ts.map +1 -0
- package/dist/sandbox/helpers.js +131 -0
- package/dist/sandbox/helpers.js.map +1 -0
- package/dist/sandbox/pool.d.ts +63 -0
- package/dist/sandbox/pool.d.ts.map +1 -0
- package/dist/sandbox/pool.js +98 -0
- package/dist/sandbox/pool.js.map +1 -0
- package/dist/sandbox/vendored-codemode.d.ts +99 -0
- package/dist/sandbox/vendored-codemode.d.ts.map +1 -0
- package/dist/sandbox/vendored-codemode.js +471 -0
- package/dist/sandbox/vendored-codemode.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +74 -0
- package/dist/server.js.map +1 -0
- package/dist/spike.d.ts +15 -0
- package/dist/spike.d.ts.map +1 -0
- package/dist/spike.js +90 -0
- package/dist/spike.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { logger } from '../observability/logger.js';
|
|
6
|
+
import { countTokens } from '../gate/budget.js';
|
|
7
|
+
import { emptyManifest, fileSha, manifestPathFor, readManifest, removeManifestEntry, updateManifestEntry, writeManifest, } from './manifest.js';
|
|
8
|
+
import { parseMarkdown, detectConfidence } from './parse/markdown.js';
|
|
9
|
+
import { detectGaps } from '../gaps/detector.js';
|
|
10
|
+
import { chunkSection } from './parse/chunk.js';
|
|
11
|
+
// ----------------------------------------------------------------------------
|
|
12
|
+
// Single-flight gate (module-level)
|
|
13
|
+
// ----------------------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* Module-level single-flight map. Keyed by `${scope}:${absPath}` so the same
|
|
16
|
+
* file can be ingested concurrently for different scopes (e.g. if the wiki
|
|
17
|
+
* dir lives at the same path for both scopes — unusual but possible).
|
|
18
|
+
*
|
|
19
|
+
* The promise stored here is the FULL `ingestFile` operation; concurrent
|
|
20
|
+
* callers await the same promise and observe the same result. After the
|
|
21
|
+
* promise settles, the entry is removed so subsequent calls run fresh.
|
|
22
|
+
*
|
|
23
|
+
* Test reset hook: `__resetSingleFlightForTests()` clears the map between
|
|
24
|
+
* tests so a leftover entry from a prior test doesn't affect the next one.
|
|
25
|
+
*/
|
|
26
|
+
const inFlight = new Map();
|
|
27
|
+
/** Test-only: clear the single-flight map. Do not call from production code. */
|
|
28
|
+
export function __resetSingleFlightForTests() {
|
|
29
|
+
inFlight.clear();
|
|
30
|
+
}
|
|
31
|
+
// ----------------------------------------------------------------------------
|
|
32
|
+
// IngesterService
|
|
33
|
+
// ----------------------------------------------------------------------------
|
|
34
|
+
export class IngesterService {
|
|
35
|
+
bundle;
|
|
36
|
+
embedder;
|
|
37
|
+
scope;
|
|
38
|
+
manifest;
|
|
39
|
+
manifestPath;
|
|
40
|
+
constructor(bundle, embedder, scope, wikiRoot, options = {}) {
|
|
41
|
+
this.bundle = bundle;
|
|
42
|
+
this.embedder = embedder;
|
|
43
|
+
this.scope = scope;
|
|
44
|
+
this.manifestPath = options.manifestPath ?? manifestPathFor(wikiRoot);
|
|
45
|
+
this.manifest = readManifest(this.manifestPath);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Ingest a single markdown file. Single-flight: concurrent calls for the
|
|
49
|
+
* same file return the same in-progress promise.
|
|
50
|
+
*
|
|
51
|
+
* Returns the per-chunk metrics for this run (added/skipped/embedder calls)
|
|
52
|
+
* so callers (rebuild CLI, chokidar handler) can log a summary.
|
|
53
|
+
*/
|
|
54
|
+
async ingestFile(absPath) {
|
|
55
|
+
const key = `${this.scope}:${resolve(absPath)}`;
|
|
56
|
+
const existing = inFlight.get(key);
|
|
57
|
+
if (existing)
|
|
58
|
+
return existing;
|
|
59
|
+
const promise = this.ingestFileImpl(resolve(absPath))
|
|
60
|
+
.catch((err) => {
|
|
61
|
+
// Make sure single-flight cleanup runs even on error.
|
|
62
|
+
logger.error({ err, file: absPath, scope: this.scope }, 'ingest failed');
|
|
63
|
+
this.appendLog('ingest:error', absPath, {
|
|
64
|
+
error: err instanceof Error ? err.message : String(err),
|
|
65
|
+
});
|
|
66
|
+
throw err;
|
|
67
|
+
})
|
|
68
|
+
.finally(() => {
|
|
69
|
+
inFlight.delete(key);
|
|
70
|
+
});
|
|
71
|
+
inFlight.set(key, promise);
|
|
72
|
+
return promise;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Remove a file from the index (for `file:removed` chokidar events).
|
|
76
|
+
* Deletes the file's nodes (cascades to chunks/edges/vec rows via FK)
|
|
77
|
+
* and updates the manifest. Single-statement, no transaction needed.
|
|
78
|
+
*/
|
|
79
|
+
removeFile(absPath) {
|
|
80
|
+
const abs = resolve(absPath);
|
|
81
|
+
const sourceUri = pathToFileURL(abs).href;
|
|
82
|
+
this.bundle.writer
|
|
83
|
+
.prepare('DELETE FROM kg_nodes WHERE scope = ? AND source_uri = ?')
|
|
84
|
+
.run(this.scope, sourceUri);
|
|
85
|
+
this.manifest = removeManifestEntry(this.manifest, abs);
|
|
86
|
+
writeManifest(this.manifestPath, this.manifest);
|
|
87
|
+
this.appendLog('ingest:removed', abs, {});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The current in-memory manifest. Exposed for tests + the startup
|
|
91
|
+
* consistency check in `cli/serve.ts`.
|
|
92
|
+
*/
|
|
93
|
+
getManifest() {
|
|
94
|
+
return this.manifest;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Reload the manifest from disk. Used at startup before walking the
|
|
98
|
+
* consistency check, and by tests that mutate the manifest externally.
|
|
99
|
+
*/
|
|
100
|
+
reloadManifest() {
|
|
101
|
+
this.manifest = readManifest(this.manifestPath);
|
|
102
|
+
}
|
|
103
|
+
// --------------------------------------------------------------------------
|
|
104
|
+
// Internal: the actual ingest pipeline
|
|
105
|
+
// --------------------------------------------------------------------------
|
|
106
|
+
async ingestFileImpl(absPath) {
|
|
107
|
+
// Step 1: read file + compute file-level sha
|
|
108
|
+
const text = readFileSync(absPath, 'utf8');
|
|
109
|
+
const sourceSha = sha1(text);
|
|
110
|
+
const sourceUri = pathToFileURL(absPath).href;
|
|
111
|
+
// Step 2: manifest fast path — same source_sha → no work
|
|
112
|
+
const existingEntry = this.manifest.files[absPath];
|
|
113
|
+
if (existingEntry && existingEntry.source_sha === sourceSha) {
|
|
114
|
+
return {
|
|
115
|
+
chunks_added: 0,
|
|
116
|
+
chunks_skipped: 0,
|
|
117
|
+
embedder_calls: 0,
|
|
118
|
+
nodes_written: 0,
|
|
119
|
+
noop: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Step 3: parse + chunk + detect confidence
|
|
123
|
+
const sections = parseMarkdown(text);
|
|
124
|
+
const confidence = detectConfidence(text);
|
|
125
|
+
const planned = sections.map((section) => {
|
|
126
|
+
const nodeId = nodeIdFor(this.scope, sourceUri, section.section_path);
|
|
127
|
+
const chunks = chunkSection(section.content);
|
|
128
|
+
return {
|
|
129
|
+
nodeId,
|
|
130
|
+
section,
|
|
131
|
+
chunks: chunks.map((chunkText, idx) => ({
|
|
132
|
+
id: chunkIdFor(nodeId, idx),
|
|
133
|
+
text: chunkText.text,
|
|
134
|
+
chunkSha: sha1(chunkText.text),
|
|
135
|
+
tokenCount: chunkText.token_count,
|
|
136
|
+
chunkIndex: idx,
|
|
137
|
+
})),
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
// Step 4: load existing chunk_shas → embedding map for skip-unchanged
|
|
141
|
+
const existingEmbeddings = this.loadExistingEmbeddings(sourceUri);
|
|
142
|
+
// Step 5: embed only new chunks
|
|
143
|
+
let embedderCalls = 0;
|
|
144
|
+
let chunksSkipped = 0;
|
|
145
|
+
const embeddings = new Map();
|
|
146
|
+
for (const node of planned) {
|
|
147
|
+
for (const chunk of node.chunks) {
|
|
148
|
+
const cached = existingEmbeddings.get(chunk.chunkSha);
|
|
149
|
+
if (cached) {
|
|
150
|
+
embeddings.set(chunk.id, cached);
|
|
151
|
+
chunksSkipped++;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
try {
|
|
155
|
+
const vec = await this.embedder.embed(chunk.text);
|
|
156
|
+
embeddings.set(chunk.id, vec);
|
|
157
|
+
embedderCalls++;
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
// Per CLAUDE.md §AI Rules #4: embedder failure is non-fatal.
|
|
161
|
+
// Insert the chunk without a vec row; FTS5 still works.
|
|
162
|
+
logger.warn({ err, chunkId: chunk.id, file: absPath }, 'embedder failed for chunk; inserting without vec row');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Step 6: transaction — delete-then-insert nodes/chunks/vec for this file
|
|
168
|
+
const now = Date.now();
|
|
169
|
+
const totalChunks = planned.reduce((acc, n) => acc + n.chunks.length, 0);
|
|
170
|
+
this.runInTransaction((w) => {
|
|
171
|
+
// Delete old vec rows BEFORE deleting kg_chunks rows. We need to know
|
|
172
|
+
// the rowids first because the FK cascade will drop the kg_chunks rows
|
|
173
|
+
// (and sqlite-vec doesn't have FK awareness).
|
|
174
|
+
const oldRowids = w
|
|
175
|
+
.prepare(`SELECT c.rowid AS rowid
|
|
176
|
+
FROM kg_chunks c JOIN kg_nodes n ON c.node_id = n.id
|
|
177
|
+
WHERE n.scope = ? AND n.source_uri = ?`)
|
|
178
|
+
.all(this.scope, sourceUri);
|
|
179
|
+
if (oldRowids.length > 0) {
|
|
180
|
+
const placeholders = oldRowids.map(() => '?').join(',');
|
|
181
|
+
w.prepare(`DELETE FROM kg_chunks_vec WHERE rowid IN (${placeholders})`).run(...oldRowids.map((r) => r.rowid));
|
|
182
|
+
}
|
|
183
|
+
// Delete old nodes for this file (cascades to chunks → FTS5 trigger fires).
|
|
184
|
+
w.prepare('DELETE FROM kg_nodes WHERE scope = ? AND source_uri = ?').run(this.scope, sourceUri);
|
|
185
|
+
// Insert new nodes
|
|
186
|
+
const insertNode = w.prepare(`INSERT INTO kg_nodes (id, scope, source_uri, section_path, kind, title, content, source_sha, token_count, created_at, updated_at, last_accessed_at, confidence)
|
|
187
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
188
|
+
const insertChunk = w.prepare(`INSERT INTO kg_chunks (id, node_id, chunk_index, text, chunk_sha, token_count, created_at)
|
|
189
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
190
|
+
const insertVec = w.prepare('INSERT INTO kg_chunks_vec(rowid, embedding) VALUES (?, ?)');
|
|
191
|
+
for (const node of planned) {
|
|
192
|
+
const sectionTokenCount = countTokens(node.section.content);
|
|
193
|
+
insertNode.run(node.nodeId, this.scope, sourceUri, node.section.section_path, node.section.kind, node.section.title || null, node.section.content, sourceSha, sectionTokenCount, now, now, now, confidence);
|
|
194
|
+
for (const chunk of node.chunks) {
|
|
195
|
+
// Capture lastInsertRowid directly from the chunk insert. This avoids
|
|
196
|
+
// a follow-up SELECT.
|
|
197
|
+
const info = insertChunk.run(chunk.id, node.nodeId, chunk.chunkIndex, chunk.text, chunk.chunkSha, chunk.tokenCount, now);
|
|
198
|
+
// sqlite-vec's vec0 virtual table is strict: it rejects JS Number
|
|
199
|
+
// bindings for the rowid column with "Only integers are allowed for
|
|
200
|
+
// primary key values" — even though the value IS an integer at the
|
|
201
|
+
// SQL level. We MUST bind as BigInt. (Verified via repro 2026-04-09.)
|
|
202
|
+
// lastInsertRowid is `number | bigint`; coerce to BigInt either way.
|
|
203
|
+
const rowidBig = typeof info.lastInsertRowid === 'bigint'
|
|
204
|
+
? info.lastInsertRowid
|
|
205
|
+
: BigInt(info.lastInsertRowid);
|
|
206
|
+
const vec = embeddings.get(chunk.id);
|
|
207
|
+
if (vec) {
|
|
208
|
+
insertVec.run(rowidBig, Buffer.from(vec.buffer));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
// Step 7: append kg_log row
|
|
214
|
+
this.appendLog('ingest:done', absPath, {
|
|
215
|
+
nodes: planned.length,
|
|
216
|
+
chunks_added: totalChunks - chunksSkipped,
|
|
217
|
+
chunks_skipped: chunksSkipped,
|
|
218
|
+
embedder_calls: embedderCalls,
|
|
219
|
+
});
|
|
220
|
+
// Step 7.5: gap detection (Phase 6) — runs after commit, scans for concept
|
|
221
|
+
// mentions and resolves gaps when matching nodes appear
|
|
222
|
+
try {
|
|
223
|
+
const nodeTitles = planned
|
|
224
|
+
.map((n) => n.section.title)
|
|
225
|
+
.filter((t) => !!t);
|
|
226
|
+
detectGaps(this.bundle.writer, this.scope, text, nodeTitles);
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
// Non-fatal: gap detection failure should never block ingest
|
|
230
|
+
logger.warn({ err, file: absPath }, 'gap detection failed');
|
|
231
|
+
}
|
|
232
|
+
// Step 8: update manifest (AFTER commit)
|
|
233
|
+
const allChunkShas = planned.flatMap((n) => n.chunks.map((c) => c.chunkSha));
|
|
234
|
+
const newEntry = { source_sha: sourceSha, chunk_shas: allChunkShas };
|
|
235
|
+
this.manifest = updateManifestEntry(this.manifest, absPath, newEntry);
|
|
236
|
+
writeManifest(this.manifestPath, this.manifest);
|
|
237
|
+
return {
|
|
238
|
+
chunks_added: totalChunks - chunksSkipped,
|
|
239
|
+
chunks_skipped: chunksSkipped,
|
|
240
|
+
embedder_calls: embedderCalls,
|
|
241
|
+
nodes_written: planned.length,
|
|
242
|
+
noop: false,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Load `chunk_sha → embedding` for every existing chunk of a file. Used by
|
|
247
|
+
* the per-chunk skip optimization: if a new chunk's sha matches one in
|
|
248
|
+
* this map, we reuse the embedding instead of calling the embedder.
|
|
249
|
+
*
|
|
250
|
+
* Returns an empty map if the file isn't in the DB yet (first ingest).
|
|
251
|
+
*/
|
|
252
|
+
loadExistingEmbeddings(sourceUri) {
|
|
253
|
+
const out = new Map();
|
|
254
|
+
const rows = this.bundle.writer
|
|
255
|
+
.prepare(`SELECT c.chunk_sha AS chunk_sha, vec.embedding AS embedding
|
|
256
|
+
FROM kg_chunks c
|
|
257
|
+
JOIN kg_nodes n ON c.node_id = n.id
|
|
258
|
+
LEFT JOIN kg_chunks_vec vec ON vec.rowid = c.rowid
|
|
259
|
+
WHERE n.scope = ? AND n.source_uri = ?`)
|
|
260
|
+
.all(this.scope, sourceUri);
|
|
261
|
+
for (const row of rows) {
|
|
262
|
+
if (!row.embedding)
|
|
263
|
+
continue;
|
|
264
|
+
// sqlite-vec stores Float32 as a raw bytes blob. Reinterpret the buffer
|
|
265
|
+
// (no copy needed since we won't mutate it).
|
|
266
|
+
const buf = row.embedding;
|
|
267
|
+
const view = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
268
|
+
// Make a stable copy in case better-sqlite3 reuses the buffer.
|
|
269
|
+
out.set(row.chunk_sha, new Float32Array(view));
|
|
270
|
+
}
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
runInTransaction(fn) {
|
|
274
|
+
const txn = this.bundle.writer.transaction(fn);
|
|
275
|
+
txn(this.bundle.writer);
|
|
276
|
+
}
|
|
277
|
+
appendLog(kind, absPath, payload) {
|
|
278
|
+
try {
|
|
279
|
+
this.bundle.writer
|
|
280
|
+
.prepare(`INSERT INTO kg_log (ts, scope, kind, source_uri, payload)
|
|
281
|
+
VALUES (?, ?, ?, ?, ?)`)
|
|
282
|
+
.run(Date.now(), this.scope, kind, pathToFileURL(absPath).href, JSON.stringify(payload));
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
logger.warn({ err, kind, absPath }, 'failed to append kg_log row');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/** sha1(scope + ':' + source_uri + ':' + section_path) — locked id derivation */
|
|
290
|
+
function nodeIdFor(scope, sourceUri, sectionPath) {
|
|
291
|
+
return sha1(`${scope}:${sourceUri}:${sectionPath}`);
|
|
292
|
+
}
|
|
293
|
+
/** sha1(node_id + ':' + chunk_index) — locked id derivation */
|
|
294
|
+
function chunkIdFor(nodeId, chunkIndex) {
|
|
295
|
+
return sha1(`${nodeId}:${chunkIndex}`);
|
|
296
|
+
}
|
|
297
|
+
function sha1(input) {
|
|
298
|
+
return createHash('sha1').update(input).digest('hex');
|
|
299
|
+
}
|
|
300
|
+
// ----------------------------------------------------------------------------
|
|
301
|
+
// Cold-start helper for the rebuild CLI
|
|
302
|
+
// ----------------------------------------------------------------------------
|
|
303
|
+
/**
|
|
304
|
+
* Reset the in-memory manifest to empty. Used by `kg rebuild` when the
|
|
305
|
+
* caller passes `--clean` to force a full re-ingest from scratch.
|
|
306
|
+
*/
|
|
307
|
+
export function freshManifest() {
|
|
308
|
+
return emptyManifest();
|
|
309
|
+
}
|
|
310
|
+
export { manifestPathFor };
|
|
311
|
+
export { fileSha };
|
|
312
|
+
//# sourceMappingURL=ingester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingester.js","sourceRoot":"","sources":["../../src/ingest/ingester.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
|
|
2
|
+
export interface ManifestEntry {
|
|
3
|
+
/** sha1 of the entire source file */
|
|
4
|
+
source_sha: string;
|
|
5
|
+
/** Ordered list of chunk shas (one per chunk produced by the chunker) */
|
|
6
|
+
chunk_shas: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface Manifest {
|
|
9
|
+
version: number;
|
|
10
|
+
/** Maps absolute file paths → entry */
|
|
11
|
+
files: Record<string, ManifestEntry>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Build an empty manifest. Useful for cold starts and tests.
|
|
15
|
+
*/
|
|
16
|
+
export declare function emptyManifest(): Manifest;
|
|
17
|
+
/**
|
|
18
|
+
* Read a manifest from disk. Returns an empty manifest if the file doesn't
|
|
19
|
+
* exist or is unreadable. Logs a warning on parse errors but doesn't throw —
|
|
20
|
+
* a corrupted manifest is treated as "no manifest", and the consistency
|
|
21
|
+
* check will conservatively re-ingest everything (which is correct).
|
|
22
|
+
*/
|
|
23
|
+
export declare function readManifest(path: string): Manifest;
|
|
24
|
+
/**
|
|
25
|
+
* Write a manifest atomically. Creates the parent directory if missing.
|
|
26
|
+
* The write is tmpfile + rename, so a SIGKILL mid-write either leaves the
|
|
27
|
+
* old manifest intact or replaces it with the new one — never half-written.
|
|
28
|
+
*/
|
|
29
|
+
export declare function writeManifest(path: string, manifest: Manifest): void;
|
|
30
|
+
/**
|
|
31
|
+
* Update one entry in a manifest in-memory. Mutates the input and returns it
|
|
32
|
+
* (the caller is expected to follow up with `writeManifest`). The entry's
|
|
33
|
+
* key is the absolute path to the file as it was ingested.
|
|
34
|
+
*/
|
|
35
|
+
export declare function updateManifestEntry(manifest: Manifest, filePath: string, entry: ManifestEntry): Manifest;
|
|
36
|
+
/**
|
|
37
|
+
* Drop a file from the manifest (e.g. after a `file:removed` event).
|
|
38
|
+
*/
|
|
39
|
+
export declare function removeManifestEntry(manifest: Manifest, filePath: string): Manifest;
|
|
40
|
+
/**
|
|
41
|
+
* Compute the sha1 of a file's contents on disk. Used by `checkConsistency`
|
|
42
|
+
* and the ingester. Synchronous because we're already in the single-flight
|
|
43
|
+
* path and parallelizing this against itself buys nothing.
|
|
44
|
+
*/
|
|
45
|
+
export declare function fileSha(filePath: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Walk a wiki directory recursively and return absolute paths of all *.md
|
|
48
|
+
* files in deterministic (sorted) order. Used by both the consistency check
|
|
49
|
+
* and the rebuild CLI.
|
|
50
|
+
*/
|
|
51
|
+
export declare function listMarkdownFiles(rootDir: string): string[];
|
|
52
|
+
/**
|
|
53
|
+
* Compare an in-memory manifest against the current on-disk state of a wiki
|
|
54
|
+
* directory. Returns a list of file paths that need re-ingestion:
|
|
55
|
+
* - Files present on disk that are NOT in the manifest (new or unindexed)
|
|
56
|
+
* - Files present in BOTH but with mismatched `source_sha` (edited externally,
|
|
57
|
+
* or last ingest crashed mid-flight before the manifest could be updated)
|
|
58
|
+
* - Files where the manifest says "indexed" but the DB has zero rows (DB/manifest
|
|
59
|
+
* divergence — e.g. DB was recreated, migration dropped tables, or crash
|
|
60
|
+
* between COMMIT and manifest write left the manifest ahead of the DB)
|
|
61
|
+
*
|
|
62
|
+
* When DB/manifest divergence is detected for a file, its manifest entry is
|
|
63
|
+
* cleared so the ingester's manifest fast-path won't noop the re-ingest.
|
|
64
|
+
*
|
|
65
|
+
* Files that exist in the manifest but no longer on disk are NOT returned —
|
|
66
|
+
* they're deletions, handled separately by the chokidar `file:removed`
|
|
67
|
+
* pathway in Pass 3. The startup consistency check is for additions and
|
|
68
|
+
* mutations, not removals.
|
|
69
|
+
*
|
|
70
|
+
* **Performance**: O(N) over markdown files, one stat + sha per file, plus
|
|
71
|
+
* one lightweight COUNT query per file that passes the manifest check when
|
|
72
|
+
* a DB writer is provided. For a typical wiki of <100 files this is <100ms.
|
|
73
|
+
*/
|
|
74
|
+
export declare function checkConsistency(manifest: Manifest, rootDir: string, writer?: BetterSqliteDatabase): string[];
|
|
75
|
+
/**
|
|
76
|
+
* Compute the canonical manifest path for a given wiki directory and scope.
|
|
77
|
+
*
|
|
78
|
+
* - `'project'` → `<wikiPath>/../kg-manifest.json`
|
|
79
|
+
* (lives next to the wiki dir, inside the same `.pharos/` folder)
|
|
80
|
+
* - `'personal'` → `<wikiPath>/../kg-manifest.json` as well
|
|
81
|
+
* (lives next to `~/.pharos/profile/wiki/`, in `~/.pharos/profile/`)
|
|
82
|
+
*
|
|
83
|
+
* Both scopes use the same relative shape — the difference is the wikiPath
|
|
84
|
+
* the caller passes in.
|
|
85
|
+
*/
|
|
86
|
+
export declare function manifestPathFor(wikiPath: string): string;
|
|
87
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/ingest/manifest.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAwCvE,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,QAAQ,CAExC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAqBnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAOpE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,GACnB,QAAQ,CAIV;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAIlF;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA4B3D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,oBAAoB,GAC5B,MAAM,EAAE,CAsCV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync, existsSync, readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { logger } from '../observability/logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* Consistency manifest for KG-MCP Phase 2.
|
|
8
|
+
*
|
|
9
|
+
* Lives at:
|
|
10
|
+
* - `<wikiPath>/../kg-manifest.json` for project scope
|
|
11
|
+
* (e.g. `.pharos/kg-manifest.json` next to `.pharos/wiki/`)
|
|
12
|
+
* - `<profilePath>/kg-manifest.json` for personal scope
|
|
13
|
+
* (e.g. `~/.pharos/profile/kg-manifest.json`)
|
|
14
|
+
*
|
|
15
|
+
* **What it stores**: per ingested file, the file-level `source_sha` and
|
|
16
|
+
* the list of per-chunk `chunk_shas` that ingest produced. This is the
|
|
17
|
+
* mid-ingest crash recovery surface — if the process dies during a vec
|
|
18
|
+
* insert, the manifest may disagree with the DB on startup, and the
|
|
19
|
+
* consistency check (`checkConsistency`) detects which files need re-ingest.
|
|
20
|
+
*
|
|
21
|
+
* **Why we need this**: pre-v1 sqlite-vec has untested crash semantics
|
|
22
|
+
* (presearch.md F9). The DB may end up in a state where vec rows exist for
|
|
23
|
+
* a chunk_sha that doesn't match the on-disk file. The manifest is the
|
|
24
|
+
* tiebreaker — it records what we INTENDED to be on disk. On startup we
|
|
25
|
+
* compare it against the actual on-disk markdown file_sha; mismatches mean
|
|
26
|
+
* the file was edited externally (or the previous run crashed before
|
|
27
|
+
* completing). Either way, re-ingest is the right move.
|
|
28
|
+
*
|
|
29
|
+
* **Atomic writes**: writeManifest() writes to a tmpfile and renames over
|
|
30
|
+
* the target. SQLite uses the same pattern; rename is atomic on Linux/macOS
|
|
31
|
+
* for files on the same filesystem. This avoids the failure mode where a
|
|
32
|
+
* SIGKILL mid-write leaves a half-written manifest that fails to parse on
|
|
33
|
+
* the next startup.
|
|
34
|
+
*
|
|
35
|
+
* **Format**: JSON, hand-readable, future-proofed with a `version` field
|
|
36
|
+
* so we can detect old manifests if the structure ever changes (e.g. adding
|
|
37
|
+
* per-chunk embedding metadata).
|
|
38
|
+
*/
|
|
39
|
+
const MANIFEST_VERSION = 1;
|
|
40
|
+
/**
|
|
41
|
+
* Build an empty manifest. Useful for cold starts and tests.
|
|
42
|
+
*/
|
|
43
|
+
export function emptyManifest() {
|
|
44
|
+
return { version: MANIFEST_VERSION, files: {} };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Read a manifest from disk. Returns an empty manifest if the file doesn't
|
|
48
|
+
* exist or is unreadable. Logs a warning on parse errors but doesn't throw —
|
|
49
|
+
* a corrupted manifest is treated as "no manifest", and the consistency
|
|
50
|
+
* check will conservatively re-ingest everything (which is correct).
|
|
51
|
+
*/
|
|
52
|
+
export function readManifest(path) {
|
|
53
|
+
if (!existsSync(path))
|
|
54
|
+
return emptyManifest();
|
|
55
|
+
try {
|
|
56
|
+
const raw = readFileSync(path, 'utf8');
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
59
|
+
return emptyManifest();
|
|
60
|
+
if (parsed.version !== MANIFEST_VERSION) {
|
|
61
|
+
logger.warn({ path, foundVersion: parsed.version, expectedVersion: MANIFEST_VERSION }, 'manifest version mismatch — discarding');
|
|
62
|
+
return emptyManifest();
|
|
63
|
+
}
|
|
64
|
+
if (typeof parsed.files !== 'object' || parsed.files === null) {
|
|
65
|
+
return emptyManifest();
|
|
66
|
+
}
|
|
67
|
+
return parsed;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
logger.warn({ err, path }, 'failed to parse manifest — starting fresh');
|
|
71
|
+
return emptyManifest();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Write a manifest atomically. Creates the parent directory if missing.
|
|
76
|
+
* The write is tmpfile + rename, so a SIGKILL mid-write either leaves the
|
|
77
|
+
* old manifest intact or replaces it with the new one — never half-written.
|
|
78
|
+
*/
|
|
79
|
+
export function writeManifest(path, manifest) {
|
|
80
|
+
const abs = resolve(path);
|
|
81
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
82
|
+
const tmp = `${abs}.tmp.${process.pid}.${Date.now()}`;
|
|
83
|
+
const json = JSON.stringify(manifest, null, 2);
|
|
84
|
+
writeFileSync(tmp, json, 'utf8');
|
|
85
|
+
renameSync(tmp, abs);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Update one entry in a manifest in-memory. Mutates the input and returns it
|
|
89
|
+
* (the caller is expected to follow up with `writeManifest`). The entry's
|
|
90
|
+
* key is the absolute path to the file as it was ingested.
|
|
91
|
+
*/
|
|
92
|
+
export function updateManifestEntry(manifest, filePath, entry) {
|
|
93
|
+
const abs = resolve(filePath);
|
|
94
|
+
manifest.files[abs] = entry;
|
|
95
|
+
return manifest;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Drop a file from the manifest (e.g. after a `file:removed` event).
|
|
99
|
+
*/
|
|
100
|
+
export function removeManifestEntry(manifest, filePath) {
|
|
101
|
+
const abs = resolve(filePath);
|
|
102
|
+
delete manifest.files[abs];
|
|
103
|
+
return manifest;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Compute the sha1 of a file's contents on disk. Used by `checkConsistency`
|
|
107
|
+
* and the ingester. Synchronous because we're already in the single-flight
|
|
108
|
+
* path and parallelizing this against itself buys nothing.
|
|
109
|
+
*/
|
|
110
|
+
export function fileSha(filePath) {
|
|
111
|
+
const buf = readFileSync(filePath);
|
|
112
|
+
return createHash('sha1').update(buf).digest('hex');
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Walk a wiki directory recursively and return absolute paths of all *.md
|
|
116
|
+
* files in deterministic (sorted) order. Used by both the consistency check
|
|
117
|
+
* and the rebuild CLI.
|
|
118
|
+
*/
|
|
119
|
+
export function listMarkdownFiles(rootDir) {
|
|
120
|
+
const out = [];
|
|
121
|
+
const stack = [resolve(rootDir)];
|
|
122
|
+
while (stack.length > 0) {
|
|
123
|
+
const dir = stack.pop();
|
|
124
|
+
let entries;
|
|
125
|
+
try {
|
|
126
|
+
entries = readdirSync(dir);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
for (const name of entries) {
|
|
132
|
+
const full = join(dir, name);
|
|
133
|
+
let st;
|
|
134
|
+
try {
|
|
135
|
+
st = statSync(full);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (st.isDirectory()) {
|
|
141
|
+
stack.push(full);
|
|
142
|
+
}
|
|
143
|
+
else if (st.isFile() && name.toLowerCase().endsWith('.md')) {
|
|
144
|
+
out.push(full);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
out.sort();
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Compare an in-memory manifest against the current on-disk state of a wiki
|
|
153
|
+
* directory. Returns a list of file paths that need re-ingestion:
|
|
154
|
+
* - Files present on disk that are NOT in the manifest (new or unindexed)
|
|
155
|
+
* - Files present in BOTH but with mismatched `source_sha` (edited externally,
|
|
156
|
+
* or last ingest crashed mid-flight before the manifest could be updated)
|
|
157
|
+
* - Files where the manifest says "indexed" but the DB has zero rows (DB/manifest
|
|
158
|
+
* divergence — e.g. DB was recreated, migration dropped tables, or crash
|
|
159
|
+
* between COMMIT and manifest write left the manifest ahead of the DB)
|
|
160
|
+
*
|
|
161
|
+
* When DB/manifest divergence is detected for a file, its manifest entry is
|
|
162
|
+
* cleared so the ingester's manifest fast-path won't noop the re-ingest.
|
|
163
|
+
*
|
|
164
|
+
* Files that exist in the manifest but no longer on disk are NOT returned —
|
|
165
|
+
* they're deletions, handled separately by the chokidar `file:removed`
|
|
166
|
+
* pathway in Pass 3. The startup consistency check is for additions and
|
|
167
|
+
* mutations, not removals.
|
|
168
|
+
*
|
|
169
|
+
* **Performance**: O(N) over markdown files, one stat + sha per file, plus
|
|
170
|
+
* one lightweight COUNT query per file that passes the manifest check when
|
|
171
|
+
* a DB writer is provided. For a typical wiki of <100 files this is <100ms.
|
|
172
|
+
*/
|
|
173
|
+
export function checkConsistency(manifest, rootDir, writer) {
|
|
174
|
+
const stale = [];
|
|
175
|
+
const files = listMarkdownFiles(rootDir);
|
|
176
|
+
// Pre-build a set of indexed source_uris for O(1) lookup when DB is available.
|
|
177
|
+
// A single query is cheaper than N per-file COUNT queries.
|
|
178
|
+
let indexedUris = null;
|
|
179
|
+
if (writer) {
|
|
180
|
+
try {
|
|
181
|
+
const rows = writer
|
|
182
|
+
.prepare('SELECT DISTINCT source_uri FROM kg_nodes')
|
|
183
|
+
.all();
|
|
184
|
+
indexedUris = new Set(rows.map((r) => r.source_uri));
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
logger.warn({ err }, 'checkConsistency: failed to query kg_nodes — falling back to manifest-only');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
for (const f of files) {
|
|
191
|
+
const entry = manifest.files[f];
|
|
192
|
+
const onDiskSha = fileSha(f);
|
|
193
|
+
if (!entry || entry.source_sha !== onDiskSha) {
|
|
194
|
+
stale.push(f);
|
|
195
|
+
}
|
|
196
|
+
else if (indexedUris) {
|
|
197
|
+
// Manifest says this file is current — verify the DB agrees.
|
|
198
|
+
const sourceUri = pathToFileURL(resolve(f)).href;
|
|
199
|
+
if (!indexedUris.has(sourceUri)) {
|
|
200
|
+
logger.info({ file: f }, 'checkConsistency: manifest says indexed but DB has no rows — marking stale');
|
|
201
|
+
// Clear the manifest entry so the ingester's fast-path doesn't noop.
|
|
202
|
+
delete manifest.files[f];
|
|
203
|
+
stale.push(f);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return stale;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Compute the canonical manifest path for a given wiki directory and scope.
|
|
211
|
+
*
|
|
212
|
+
* - `'project'` → `<wikiPath>/../kg-manifest.json`
|
|
213
|
+
* (lives next to the wiki dir, inside the same `.pharos/` folder)
|
|
214
|
+
* - `'personal'` → `<wikiPath>/../kg-manifest.json` as well
|
|
215
|
+
* (lives next to `~/.pharos/profile/wiki/`, in `~/.pharos/profile/`)
|
|
216
|
+
*
|
|
217
|
+
* Both scopes use the same relative shape — the difference is the wikiPath
|
|
218
|
+
* the caller passes in.
|
|
219
|
+
*/
|
|
220
|
+
export function manifestPathFor(wikiPath) {
|
|
221
|
+
return resolve(dirname(resolve(wikiPath)), 'kg-manifest.json');
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/ingest/manifest.ts"],"names":[],"mappings":"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"}
|