@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,296 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
6
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
import { closeDb, openDb } from '../db/client.js';
|
|
9
|
+
import { Repository } from '../db/repository.js';
|
|
10
|
+
import { ChokidarWatcher } from '../ingest/chokidar.js';
|
|
11
|
+
import { IngesterService } from '../ingest/ingester.js';
|
|
12
|
+
import { checkConsistency, listMarkdownFiles } from '../ingest/manifest.js';
|
|
13
|
+
import { kgExecuteToolConfig, makeKgExecuteHandler } from '../mcp/tools/execute.js';
|
|
14
|
+
import { kgSearchToolConfig, makeKgSearchHandler } from '../mcp/tools/search.js';
|
|
15
|
+
import { writeAuditRow } from '../observability/audit.js';
|
|
16
|
+
import { child, logger } from '../observability/logger.js';
|
|
17
|
+
import { installSighupHandler, metrics } from '../observability/metrics.js';
|
|
18
|
+
import { createEmbedder } from '../retrieval/embedder.js';
|
|
19
|
+
import { QuickJSExecutor } from '../sandbox/executor.js';
|
|
20
|
+
/**
|
|
21
|
+
* Build (but don't start) the stdio server. Exposed as a function so future
|
|
22
|
+
* tests / in-process integrations can construct a server without going
|
|
23
|
+
* through the CLI argv path.
|
|
24
|
+
*/
|
|
25
|
+
export async function buildServer(options) {
|
|
26
|
+
const projectWiki = resolveAbs(options.wikiPath);
|
|
27
|
+
if (!existsSync(projectWiki)) {
|
|
28
|
+
throw new Error(`wiki path does not exist: ${projectWiki}`);
|
|
29
|
+
}
|
|
30
|
+
const projectDbPath = resolveAbs(options.dbPath ?? defaultDbPathFor(projectWiki));
|
|
31
|
+
const profileWiki = resolveAbs(options.profilePath ?? defaultProfileWikiPath());
|
|
32
|
+
const profileDbPath = resolveAbs(options.profileDbPath ?? defaultDbPathFor(profileWiki));
|
|
33
|
+
const enablePersonal = existsSync(profileWiki);
|
|
34
|
+
// Step 1: open DBs
|
|
35
|
+
const projectBundle = openDb(projectDbPath);
|
|
36
|
+
const personalBundle = enablePersonal ? openDb(profileDbPath) : null;
|
|
37
|
+
// Step 2: warm embedder
|
|
38
|
+
const embedder = createEmbedder();
|
|
39
|
+
await embedder.warmup();
|
|
40
|
+
// Step 3: ingesters per scope
|
|
41
|
+
const projectIngester = new IngesterService(projectBundle, embedder, 'project', projectWiki);
|
|
42
|
+
const personalIngester = personalBundle
|
|
43
|
+
? new IngesterService(personalBundle, embedder, 'personal', profileWiki)
|
|
44
|
+
: null;
|
|
45
|
+
// Step 4: startup consistency check — re-ingest any stale files.
|
|
46
|
+
// Pass the DB writer so checkConsistency can cross-validate manifest claims
|
|
47
|
+
// against actual DB rows. This catches the "manifest says indexed but DB is
|
|
48
|
+
// empty" scenario (DB recreated, migration dropped tables, etc.).
|
|
49
|
+
await runStartupConsistency(projectIngester, projectWiki, 'project', projectBundle.writer);
|
|
50
|
+
if (personalIngester && personalBundle) {
|
|
51
|
+
await runStartupConsistency(personalIngester, profileWiki, 'personal', personalBundle.writer);
|
|
52
|
+
}
|
|
53
|
+
// Step 5: start watchers
|
|
54
|
+
const projectWatcher = new ChokidarWatcher({ rootDir: projectWiki, scope: 'project' });
|
|
55
|
+
const personalWatcher = personalIngester
|
|
56
|
+
? new ChokidarWatcher({ rootDir: profileWiki, scope: 'personal' })
|
|
57
|
+
: null;
|
|
58
|
+
await projectWatcher.start(makeOnEvent(projectIngester));
|
|
59
|
+
if (personalWatcher && personalIngester) {
|
|
60
|
+
await personalWatcher.start(makeOnEvent(personalIngester));
|
|
61
|
+
}
|
|
62
|
+
// Step 6: build the MCP server
|
|
63
|
+
const repository = new Repository(projectBundle, personalBundle);
|
|
64
|
+
const executor = new QuickJSExecutor();
|
|
65
|
+
await executor.warmup();
|
|
66
|
+
// Audit JSONL paths (CLAUDE.md §Security #7: scope-split)
|
|
67
|
+
const projectAuditJsonl = resolve(dirname(projectDbPath), 'audit.jsonl');
|
|
68
|
+
const personalAuditJsonl = resolve(homedir(), '.kg/audit.jsonl');
|
|
69
|
+
const auditCtx = {
|
|
70
|
+
projectBundle,
|
|
71
|
+
personalBundle: personalBundle ?? undefined,
|
|
72
|
+
projectAuditJsonl,
|
|
73
|
+
personalAuditJsonl,
|
|
74
|
+
};
|
|
75
|
+
const mcp = new McpServer({ name: 'kg-mcp', version: '0.7.0' }, { capabilities: { tools: {} } });
|
|
76
|
+
mcp.registerTool('kg_search', kgSearchToolConfig, instrumentHandler('kg_search', makeKgSearchHandler({
|
|
77
|
+
repository, embedder, bundle: projectBundle,
|
|
78
|
+
personalBundle: personalBundle ?? undefined,
|
|
79
|
+
}), auditCtx));
|
|
80
|
+
mcp.registerTool('kg_execute', kgExecuteToolConfig, instrumentHandler('kg_execute', makeKgExecuteHandler({
|
|
81
|
+
repository, executor, bundle: projectBundle, embedder,
|
|
82
|
+
wikiRoot: projectWiki,
|
|
83
|
+
personalBundle: personalBundle ?? undefined,
|
|
84
|
+
personalWikiRoot: enablePersonal ? profileWiki : undefined,
|
|
85
|
+
}), auditCtx));
|
|
86
|
+
// Step 7: periodic background health check — detect index drift at runtime.
|
|
87
|
+
// Compares disk file count vs DB indexed file count every HEALTH_CHECK_INTERVAL_MS.
|
|
88
|
+
// If there's a mismatch, re-runs the full consistency check (with DB cross-validation)
|
|
89
|
+
// and re-ingests stale files. This catches missed chokidar events, silent write
|
|
90
|
+
// failures, or DB state changes that happen while the server is running.
|
|
91
|
+
const HEALTH_CHECK_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
92
|
+
let healthCheckRunning = false;
|
|
93
|
+
const healthCheckTimer = setInterval(() => {
|
|
94
|
+
if (healthCheckRunning)
|
|
95
|
+
return; // don't overlap
|
|
96
|
+
void runHealthCheck();
|
|
97
|
+
}, HEALTH_CHECK_INTERVAL_MS);
|
|
98
|
+
// Don't let the timer keep the process alive on shutdown.
|
|
99
|
+
healthCheckTimer.unref();
|
|
100
|
+
async function runHealthCheck() {
|
|
101
|
+
healthCheckRunning = true;
|
|
102
|
+
try {
|
|
103
|
+
const diskFiles = listMarkdownFiles(projectWiki);
|
|
104
|
+
const dbCount = projectBundle.writer
|
|
105
|
+
.prepare('SELECT COUNT(DISTINCT source_uri) AS cnt FROM kg_nodes WHERE scope = ?')
|
|
106
|
+
.get('project')?.cnt ?? 0;
|
|
107
|
+
if (diskFiles.length !== dbCount) {
|
|
108
|
+
logger.info({ diskFiles: diskFiles.length, dbIndexed: dbCount, scope: 'project' }, 'health check: index drift detected — running consistency check');
|
|
109
|
+
await runStartupConsistency(projectIngester, projectWiki, 'project', projectBundle.writer);
|
|
110
|
+
}
|
|
111
|
+
if (personalIngester && personalBundle) {
|
|
112
|
+
const personalDiskFiles = listMarkdownFiles(profileWiki);
|
|
113
|
+
const personalDbCount = personalBundle.writer
|
|
114
|
+
.prepare('SELECT COUNT(DISTINCT source_uri) AS cnt FROM kg_nodes WHERE scope = ?')
|
|
115
|
+
.get('personal')?.cnt ?? 0;
|
|
116
|
+
if (personalDiskFiles.length !== personalDbCount) {
|
|
117
|
+
logger.info({ diskFiles: personalDiskFiles.length, dbIndexed: personalDbCount, scope: 'personal' }, 'health check: index drift detected — running consistency check');
|
|
118
|
+
await runStartupConsistency(personalIngester, profileWiki, 'personal', personalBundle.writer);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
logger.warn({ err }, 'health check failed');
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
healthCheckRunning = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
let shuttingDown = false;
|
|
130
|
+
const shutdown = async (signal) => {
|
|
131
|
+
if (shuttingDown)
|
|
132
|
+
return;
|
|
133
|
+
shuttingDown = true;
|
|
134
|
+
logger.info({ signal }, 'kg-mcp shutting down');
|
|
135
|
+
try {
|
|
136
|
+
clearInterval(healthCheckTimer);
|
|
137
|
+
await projectWatcher.stop();
|
|
138
|
+
if (personalWatcher)
|
|
139
|
+
await personalWatcher.stop();
|
|
140
|
+
await mcp.close();
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
logger.warn({ err }, 'shutdown error');
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
closeDb(projectBundle);
|
|
147
|
+
if (personalBundle)
|
|
148
|
+
closeDb(personalBundle);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
return { server: mcp, shutdown };
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The CLI entry point: build the server, connect stdio, install signal
|
|
155
|
+
* handlers, listen forever.
|
|
156
|
+
*/
|
|
157
|
+
export async function serveCommand(options) {
|
|
158
|
+
logger.info({ wikiPath: options.wikiPath }, 'kg-mcp starting');
|
|
159
|
+
installSighupHandler();
|
|
160
|
+
const { server, shutdown } = await buildServer(options);
|
|
161
|
+
const transport = new StdioServerTransport();
|
|
162
|
+
process.on('SIGTERM', () => {
|
|
163
|
+
void shutdown('SIGTERM').then(() => process.exit(0));
|
|
164
|
+
});
|
|
165
|
+
process.on('SIGINT', () => {
|
|
166
|
+
void shutdown('SIGINT').then(() => process.exit(0));
|
|
167
|
+
});
|
|
168
|
+
await server.connect(transport);
|
|
169
|
+
logger.info('kg-mcp listening on stdio');
|
|
170
|
+
}
|
|
171
|
+
// ----------------------------------------------------------------------------
|
|
172
|
+
// Internals
|
|
173
|
+
// ----------------------------------------------------------------------------
|
|
174
|
+
function makeOnEvent(ingester) {
|
|
175
|
+
return async (ev) => {
|
|
176
|
+
if (ev.kind === 'file:removed') {
|
|
177
|
+
ingester.removeFile(ev.path);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
await ingester.ingestFile(ev.path);
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async function runStartupConsistency(ingester, wikiDir, scope, writer) {
|
|
184
|
+
ingester.reloadManifest();
|
|
185
|
+
const manifest = ingester.getManifest();
|
|
186
|
+
const stale = checkConsistency(manifest, wikiDir, writer);
|
|
187
|
+
if (stale.length === 0) {
|
|
188
|
+
logger.info({ scope, wikiDir }, 'startup consistency check: nothing stale');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
logger.info({ scope, wikiDir, stale: stale.length }, 'startup consistency check: re-ingesting stale files');
|
|
192
|
+
for (const file of stale) {
|
|
193
|
+
try {
|
|
194
|
+
await ingester.ingestFile(file);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
logger.warn({ err, file, scope }, 'startup consistency: file ingest failed');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function resolveAbs(p) {
|
|
202
|
+
return isAbsolute(p) ? p : resolve(process.cwd(), p);
|
|
203
|
+
}
|
|
204
|
+
function defaultDbPathFor(wikiDir) {
|
|
205
|
+
return resolve(dirname(wikiDir), 'kg.db');
|
|
206
|
+
}
|
|
207
|
+
function defaultProfileWikiPath() {
|
|
208
|
+
const env = process.env.KG_PROFILE_PATH;
|
|
209
|
+
if (env)
|
|
210
|
+
return resolve(env, 'wiki');
|
|
211
|
+
return resolve(homedir(), '.pharos/profile/wiki');
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Wrap a tool handler with:
|
|
215
|
+
* 1. Per-call correlation ID (callId) threaded into a pino child logger
|
|
216
|
+
* 2. Structured entry/exit logging with latency
|
|
217
|
+
* 3. Audit row written to the scope-appropriate DB + JSONL mirror
|
|
218
|
+
* 4. Metrics counter update
|
|
219
|
+
*/
|
|
220
|
+
function instrumentHandler(toolName, handler, ctx) {
|
|
221
|
+
return async (args) => {
|
|
222
|
+
const callId = randomUUID();
|
|
223
|
+
const scope = args.scope ?? 'project';
|
|
224
|
+
const log = child({ callId, tool: toolName, scope });
|
|
225
|
+
log.info({ args: summarizeArgs(args) }, 'tool call start');
|
|
226
|
+
const start = performance.now();
|
|
227
|
+
let result;
|
|
228
|
+
let error;
|
|
229
|
+
try {
|
|
230
|
+
result = await handler(args);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
const latencyMs = Math.round(performance.now() - start);
|
|
234
|
+
error = err instanceof Error ? err.message : String(err);
|
|
235
|
+
log.error({ err, latencyMs }, 'tool call failed');
|
|
236
|
+
metrics.recordToolCall(toolName, latencyMs, true);
|
|
237
|
+
writeAudit(ctx, toolName, scope, callId, undefined, error);
|
|
238
|
+
throw err;
|
|
239
|
+
}
|
|
240
|
+
const latencyMs = Math.round(performance.now() - start);
|
|
241
|
+
// Extract tokens_used from the envelope embedded in the response text
|
|
242
|
+
let responseTokens;
|
|
243
|
+
try {
|
|
244
|
+
const envelope = JSON.parse(result.content[0].text);
|
|
245
|
+
responseTokens = envelope?.meta?.tokens_used;
|
|
246
|
+
if (envelope?.result && typeof envelope.result === 'object' && 'error' in envelope.result) {
|
|
247
|
+
error = String(envelope.result.error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// Non-JSON response — shouldn't happen, but don't crash the wrapper
|
|
252
|
+
}
|
|
253
|
+
log.info({ latencyMs, responseTokens }, 'tool call end');
|
|
254
|
+
metrics.recordToolCall(toolName, latencyMs, !!error);
|
|
255
|
+
writeAudit(ctx, toolName, scope, callId, responseTokens, error);
|
|
256
|
+
return result;
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Write audit row to the correct DB + JSONL per scope-split rule.
|
|
261
|
+
*/
|
|
262
|
+
function writeAudit(ctx, toolName, scope, callId, responseTokens, error) {
|
|
263
|
+
const entry = {
|
|
264
|
+
toolName,
|
|
265
|
+
scopeRequested: scope,
|
|
266
|
+
callerCtx: callId,
|
|
267
|
+
responseTokens,
|
|
268
|
+
error,
|
|
269
|
+
};
|
|
270
|
+
if (scope === 'project') {
|
|
271
|
+
writeAuditRow(ctx.projectBundle.writer, ctx.projectAuditJsonl, entry);
|
|
272
|
+
}
|
|
273
|
+
else if (ctx.personalBundle) {
|
|
274
|
+
writeAuditRow(ctx.personalBundle.writer, ctx.personalAuditJsonl, entry);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
// Fallback: write to project DB if personal is not configured
|
|
278
|
+
writeAuditRow(ctx.projectBundle.writer, ctx.projectAuditJsonl, entry);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Summarize args for logging without dumping huge code strings.
|
|
283
|
+
*/
|
|
284
|
+
function summarizeArgs(args) {
|
|
285
|
+
const summary = {};
|
|
286
|
+
for (const [k, v] of Object.entries(args)) {
|
|
287
|
+
if (k === 'code' && typeof v === 'string') {
|
|
288
|
+
summary[k] = v.length > 100 ? v.slice(0, 100) + '...' : v;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
summary[k] = v;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return summary;
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/cli/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `kg status` — health check for KG-MCP Phase 2.
|
|
3
|
+
*
|
|
4
|
+
* Opens the project + personal DBs (if they exist), reports row counts per
|
|
5
|
+
* table, schema version, last full rebuild timestamp, and SQLite version.
|
|
6
|
+
*
|
|
7
|
+
* No DB writes — strictly read-only. Safe to run while a `kg serve` instance
|
|
8
|
+
* is also running on the same DB (WAL mode + read connection makes this fine).
|
|
9
|
+
*/
|
|
10
|
+
export interface StatusOptions {
|
|
11
|
+
/** Path to project DB. If omitted, defaults to `.pharos/kg.db` from wikiPath if given. */
|
|
12
|
+
dbPath?: string;
|
|
13
|
+
/** Path to project wiki dir, used to derive default dbPath if dbPath omitted */
|
|
14
|
+
wikiPath?: string;
|
|
15
|
+
/** Path to personal DB (default: `~/.pharos/profile/kg.db`) */
|
|
16
|
+
profileDbPath?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ScopeStatus {
|
|
19
|
+
scope: 'project' | 'personal';
|
|
20
|
+
dbPath: string;
|
|
21
|
+
exists: boolean;
|
|
22
|
+
schemaVersion: string | null;
|
|
23
|
+
lastFullRebuild: number | null;
|
|
24
|
+
sqliteVersion: string | null;
|
|
25
|
+
rowCounts: Record<string, number>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build a status report for both scopes. The result is a structured object
|
|
29
|
+
* suitable for both human-readable rendering and JSON output (e.g.
|
|
30
|
+
* `kg status --json`).
|
|
31
|
+
*/
|
|
32
|
+
export declare function statusCommand(options?: StatusOptions): ScopeStatus[];
|
|
33
|
+
/**
|
|
34
|
+
* Pretty-print a list of `ScopeStatus` to a string. Used by the CLI entry to
|
|
35
|
+
* write to stdout. Tests assert against the structured `statusCommand` result
|
|
36
|
+
* directly, not the rendered string.
|
|
37
|
+
*/
|
|
38
|
+
export declare function renderStatus(statuses: ScopeStatus[]): string;
|
|
39
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/cli/status.ts"],"names":[],"mappings":"AAOA;;;;;;;;GAQG;AAEH,MAAM,WAAW,aAAa;IAC5B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,GAAG,UAAU,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,WAAW,EAAE,CAQxE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CAqB5D"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
4
|
+
import { closeDb, openDb } from '../db/client.js';
|
|
5
|
+
import { KG_TABLES, KG_VIRTUAL_TABLES } from '../db/schema.js';
|
|
6
|
+
/**
|
|
7
|
+
* Build a status report for both scopes. The result is a structured object
|
|
8
|
+
* suitable for both human-readable rendering and JSON output (e.g.
|
|
9
|
+
* `kg status --json`).
|
|
10
|
+
*/
|
|
11
|
+
export function statusCommand(options = {}) {
|
|
12
|
+
const projectDbPath = resolveProjectDb(options);
|
|
13
|
+
const profileDbPath = resolveProfileDb(options);
|
|
14
|
+
return [
|
|
15
|
+
inspectScope('project', projectDbPath),
|
|
16
|
+
inspectScope('personal', profileDbPath),
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Pretty-print a list of `ScopeStatus` to a string. Used by the CLI entry to
|
|
21
|
+
* write to stdout. Tests assert against the structured `statusCommand` result
|
|
22
|
+
* directly, not the rendered string.
|
|
23
|
+
*/
|
|
24
|
+
export function renderStatus(statuses) {
|
|
25
|
+
const lines = [];
|
|
26
|
+
for (const s of statuses) {
|
|
27
|
+
lines.push(`${s.scope.toUpperCase()} DB: ${s.dbPath}`);
|
|
28
|
+
if (!s.exists) {
|
|
29
|
+
lines.push(' (not initialized)');
|
|
30
|
+
lines.push('');
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
lines.push(` sqlite_version: ${s.sqliteVersion ?? '<unknown>'}`);
|
|
34
|
+
lines.push(` schema_version: ${s.schemaVersion ?? '<absent>'}`);
|
|
35
|
+
lines.push(` last_full_rebuild: ${s.lastFullRebuild ? new Date(s.lastFullRebuild).toISOString() : '<never>'}`);
|
|
36
|
+
for (const table of [...KG_TABLES, ...KG_VIRTUAL_TABLES]) {
|
|
37
|
+
const count = s.rowCounts[table] ?? 0;
|
|
38
|
+
lines.push(` ${table.padEnd(20)} ${count}`);
|
|
39
|
+
}
|
|
40
|
+
lines.push('');
|
|
41
|
+
}
|
|
42
|
+
return lines.join('\n');
|
|
43
|
+
}
|
|
44
|
+
// ----------------------------------------------------------------------------
|
|
45
|
+
// Internals
|
|
46
|
+
// ----------------------------------------------------------------------------
|
|
47
|
+
function inspectScope(scope, dbPath) {
|
|
48
|
+
const status = {
|
|
49
|
+
scope,
|
|
50
|
+
dbPath,
|
|
51
|
+
exists: existsSync(dbPath),
|
|
52
|
+
schemaVersion: null,
|
|
53
|
+
lastFullRebuild: null,
|
|
54
|
+
sqliteVersion: null,
|
|
55
|
+
rowCounts: {},
|
|
56
|
+
};
|
|
57
|
+
if (!status.exists)
|
|
58
|
+
return status;
|
|
59
|
+
// Open with `runMigrations: false` to avoid surprising the user with a
|
|
60
|
+
// schema upgrade just from running `status`. If the schema is out of
|
|
61
|
+
// date, the row counts will still be readable for the tables that exist.
|
|
62
|
+
const bundle = openDb(dbPath, { runMigrations: false });
|
|
63
|
+
try {
|
|
64
|
+
status.sqliteVersion = bundle.writer.prepare('SELECT sqlite_version() AS v').get().v;
|
|
65
|
+
const schemaRow = bundle.writer
|
|
66
|
+
.prepare("SELECT value FROM kg_meta WHERE key = 'schema_version'")
|
|
67
|
+
.get();
|
|
68
|
+
status.schemaVersion = schemaRow?.value ?? null;
|
|
69
|
+
const rebuildRow = bundle.writer
|
|
70
|
+
.prepare("SELECT value FROM kg_meta WHERE key = 'last_full_rebuild'")
|
|
71
|
+
.get();
|
|
72
|
+
status.lastFullRebuild = rebuildRow ? parseInt(rebuildRow.value, 10) : null;
|
|
73
|
+
for (const table of [...KG_TABLES, ...KG_VIRTUAL_TABLES]) {
|
|
74
|
+
try {
|
|
75
|
+
const row = bundle.writer.prepare(`SELECT count(*) AS c FROM ${table}`).get();
|
|
76
|
+
status.rowCounts[table] = row.c;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Table may not exist on an old schema; report 0 and move on.
|
|
80
|
+
status.rowCounts[table] = 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
closeDb(bundle);
|
|
86
|
+
}
|
|
87
|
+
return status;
|
|
88
|
+
}
|
|
89
|
+
function resolveProjectDb(options) {
|
|
90
|
+
if (options.dbPath)
|
|
91
|
+
return resolveAbs(options.dbPath);
|
|
92
|
+
if (options.wikiPath) {
|
|
93
|
+
return resolve(dirname(resolveAbs(options.wikiPath)), 'kg.db');
|
|
94
|
+
}
|
|
95
|
+
// Last resort: ./.pharos/kg.db relative to cwd
|
|
96
|
+
return resolve(process.cwd(), '.pharos/kg.db');
|
|
97
|
+
}
|
|
98
|
+
function resolveProfileDb(options) {
|
|
99
|
+
if (options.profileDbPath)
|
|
100
|
+
return resolveAbs(options.profileDbPath);
|
|
101
|
+
const env = process.env.KG_PROFILE_PATH;
|
|
102
|
+
const profileDir = env ? resolveAbs(env) : resolve(homedir(), '.pharos/profile');
|
|
103
|
+
return resolve(profileDir, 'kg.db');
|
|
104
|
+
}
|
|
105
|
+
function resolveAbs(p) {
|
|
106
|
+
return isAbsolute(p) ? p : resolve(process.cwd(), p);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/cli/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AA+B/D;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,UAAyB,EAAE;IACvD,MAAM,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAEhD,OAAO;QACL,YAAY,CAAC,SAAS,EAAE,aAAa,CAAC;QACtC,YAAY,CAAC,UAAU,EAAE,aAAa,CAAC;KACxC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,QAAuB;IAClD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,SAAS;QACX,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,aAAa,IAAI,WAAW,EAAE,CAAC,CAAC;QAClE,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,aAAa,IAAI,UAAU,EAAE,CAAC,CAAC;QACjE,KAAK,CAAC,IAAI,CACR,wBAAwB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CACpG,CAAC;QACF,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,EAAE,GAAG,iBAAiB,CAAC,EAAE,CAAC;YACzD,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,SAAS,YAAY,CAAC,KAA6B,EAAE,MAAc;IACjE,MAAM,MAAM,GAAgB;QAC1B,KAAK;QACL,MAAM;QACN,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC;QAC1B,aAAa,EAAE,IAAI;QACnB,eAAe,EAAE,IAAI;QACrB,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,EAAE;KACd,CAAC;IAEF,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IAElC,uEAAuE;IACvE,qEAAqE;IACrE,yEAAyE;IACzE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,IAAI,CAAC;QACH,MAAM,CAAC,aAAa,GAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC,GAAG,EAE/E,CAAC,CAAC,CAAC;QAEL,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM;aAC5B,OAAO,CAAC,wDAAwD,CAAC;aACjE,GAAG,EAAmC,CAAC;QAC1C,MAAM,CAAC,aAAa,GAAG,SAAS,EAAE,KAAK,IAAI,IAAI,CAAC;QAEhD,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM;aAC7B,OAAO,CAAC,2DAA2D,CAAC;aACpE,GAAG,EAAmC,CAAC;QAC1C,MAAM,CAAC,eAAe,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAE5E,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,EAAE,GAAG,iBAAiB,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC,GAAG,EAE1E,CAAC;gBACF,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAClC,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;gBAC9D,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAsB;IAC9C,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IACD,+CAA+C;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAsB;IAC9C,IAAI,OAAO,CAAC,aAAa;QAAE,OAAO,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACxC,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,iBAAiB,CAAC,CAAC;IACjF,OAAO,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type Database as BetterSqliteDatabase } from 'better-sqlite3';
|
|
2
|
+
import { type BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
|
3
|
+
import * as schema from './schema.js';
|
|
4
|
+
/**
|
|
5
|
+
* SQLite connection management for KG-MCP.
|
|
6
|
+
*
|
|
7
|
+
* **Single-writer + read-pool-of-2** invariant (CLAUDE.md §Architecture #3,
|
|
8
|
+
* §Database Rules #1):
|
|
9
|
+
* - One writer connection — all DML for a given DB file goes through this.
|
|
10
|
+
* Enforced at the app layer via the single-flight gate in the ingester
|
|
11
|
+
* (a `Map<source_uri, Promise>`), not by the DB driver.
|
|
12
|
+
* - Two reader connections in a tiny round-robin pool. Reads can run
|
|
13
|
+
* concurrently with the writer because we use WAL mode. SQLite's
|
|
14
|
+
* "many readers, one writer" model is fastest with this exact shape;
|
|
15
|
+
* adding a connection pool with multiple writers actually HURTS
|
|
16
|
+
* throughput (presearch.md §Loop 0 gotcha).
|
|
17
|
+
*
|
|
18
|
+
* **Mandatory pragmas** (CLAUDE.md §Database Rules #1) applied on EVERY
|
|
19
|
+
* connection (writer + readers) at open time. Foreign keys are enforced
|
|
20
|
+
* per-connection in SQLite, so missing them on a reader would silently
|
|
21
|
+
* skip CASCADE checks.
|
|
22
|
+
*
|
|
23
|
+
* **sqlite-vec extension** is loaded on every connection so that the vec0
|
|
24
|
+
* virtual table is queryable from any reader and writable by the writer.
|
|
25
|
+
* The migration that creates `kg_chunks_vec` requires the extension to be
|
|
26
|
+
* loaded BEFORE drizzle's `migrate()` runs — this file orders things so
|
|
27
|
+
* that load happens first.
|
|
28
|
+
*
|
|
29
|
+
* **SQLite version check**: 3.50.4 OR ≥3.51.3, never 3.51.0 (FTS5
|
|
30
|
+
* regression, presearch D18). Throws on a bad version at openDb() time.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* A bundle of one writer + two readers for a given SQLite file. The
|
|
34
|
+
* `drizzleWriter` is a drizzle wrapper around the writer connection,
|
|
35
|
+
* suitable for Drizzle queries; the raw `writer` and `readers` are
|
|
36
|
+
* also exposed for raw-SQL hot paths (FTS5, vec0, repository LIKE
|
|
37
|
+
* search) where Drizzle's overhead isn't worth it.
|
|
38
|
+
*/
|
|
39
|
+
export interface DbBundle {
|
|
40
|
+
/** Absolute path to the SQLite file */
|
|
41
|
+
path: string;
|
|
42
|
+
/** Single writer connection — all DML goes through this */
|
|
43
|
+
writer: BetterSqliteDatabase;
|
|
44
|
+
/** Read pool of 2; pick via {@link nextReader} for round-robin */
|
|
45
|
+
readers: BetterSqliteDatabase[];
|
|
46
|
+
/** Drizzle wrapper around `writer` for typed queries */
|
|
47
|
+
drizzleWriter: BetterSQLite3Database<typeof schema>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The minimum acceptable SQLite version. better-sqlite3@12.8.0 bundles
|
|
51
|
+
* 3.51.3 which is fine. Anything older than 3.50.4 is rejected because
|
|
52
|
+
* older versions miss FTS5 features we rely on; anything in the 3.51.0-3.51.2
|
|
53
|
+
* range is rejected due to the FTS5 regression.
|
|
54
|
+
*
|
|
55
|
+
* The check is: version === '3.50.4' OR version >= '3.51.3'.
|
|
56
|
+
*/
|
|
57
|
+
declare function isAllowedSqliteVersion(version: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* The 6 mandatory pragmas, applied on every fresh connection. Order matters
|
|
60
|
+
* for `journal_mode=WAL` (must run before any read/write). `cache_size=-20000`
|
|
61
|
+
* is in KB units (negative = KB instead of pages); presearch §Performance
|
|
62
|
+
* settled on 20MB per connection.
|
|
63
|
+
*/
|
|
64
|
+
declare function applyPragmas(db: BetterSqliteDatabase): void;
|
|
65
|
+
/**
|
|
66
|
+
* Load the sqlite-vec extension on a connection. Must be called BEFORE any
|
|
67
|
+
* query that touches `kg_chunks_vec`, including the migration that creates
|
|
68
|
+
* the virtual table.
|
|
69
|
+
*
|
|
70
|
+
* better-sqlite3 disables loadExtension() by default; we re-enable it on
|
|
71
|
+
* the specific connection only and load the bundled extension shipped by
|
|
72
|
+
* the sqlite-vec npm package (no system install required).
|
|
73
|
+
*/
|
|
74
|
+
declare function loadSqliteVec(db: BetterSqliteDatabase): void;
|
|
75
|
+
/**
|
|
76
|
+
* Open (or create) a SQLite file with the full Phase 2 setup applied:
|
|
77
|
+
* 1. Create the parent directory if missing
|
|
78
|
+
* 2. Open one writer + two readers via better-sqlite3
|
|
79
|
+
* 3. Apply mandatory pragmas on each connection
|
|
80
|
+
* 4. Load sqlite-vec on each connection
|
|
81
|
+
* 5. Verify SQLite version (3.50.4 or ≥3.51.3, never 3.51.0)
|
|
82
|
+
* 6. Run drizzle migrations on the writer
|
|
83
|
+
* 7. Stamp `kg_meta.schema_version='1'` if not yet set
|
|
84
|
+
*
|
|
85
|
+
* Returns a `DbBundle` ready for repository / ingester use.
|
|
86
|
+
*
|
|
87
|
+
* Pass `{ runMigrations: false }` to skip step 6 — useful for tests that
|
|
88
|
+
* want to assert pragma state on a virgin connection.
|
|
89
|
+
*/
|
|
90
|
+
export interface OpenDbOptions {
|
|
91
|
+
/** Skip the migration run (default: true). Tests use `false` to test pragmas in isolation. */
|
|
92
|
+
runMigrations?: boolean;
|
|
93
|
+
}
|
|
94
|
+
export declare function openDb(path: string, options?: OpenDbOptions): DbBundle;
|
|
95
|
+
/**
|
|
96
|
+
* Close every connection in a bundle. Idempotent — safe to call twice.
|
|
97
|
+
* Logs at debug level so the production stdio path stays quiet on shutdown.
|
|
98
|
+
*/
|
|
99
|
+
export declare function closeDb(bundle: DbBundle): void;
|
|
100
|
+
export declare function nextReader(bundle: DbBundle): BetterSqliteDatabase;
|
|
101
|
+
/** Exposed for schema.test.ts to verify the version-check rule directly. */
|
|
102
|
+
export declare const __test: {
|
|
103
|
+
isAllowedSqliteVersion: typeof isAllowedSqliteVersion;
|
|
104
|
+
applyPragmas: typeof applyPragmas;
|
|
105
|
+
loadSqliteVec: typeof loadSqliteVec;
|
|
106
|
+
MIGRATIONS_FOLDER: string;
|
|
107
|
+
};
|
|
108
|
+
export {};
|
|
109
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAIA,OAAiB,EAAE,KAAK,QAAQ,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAW,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAKjF,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,MAAM,EAAE,oBAAoB,CAAC;IAC7B,kEAAkE;IAClE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,wDAAwD;IACxD,aAAa,EAAE,qBAAqB,CAAC,OAAO,MAAM,CAAC,CAAC;CACrD;AASD;;;;;;;GAOG;AACH,iBAAS,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAkBxD;AAMD;;;;;GAKG;AACH,iBAAS,YAAY,CAAC,EAAE,EAAE,oBAAoB,GAAG,IAAI,CAOpD;AAED;;;;;;;;GAQG;AACH,iBAAS,aAAa,CAAC,EAAE,EAAE,oBAAoB,GAAG,IAAI,CAGrD;AAmBD;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,aAAa;IAC5B,8FAA8F;IAC9F,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,QAAQ,CAiD1E;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAc9C;AAYD,wBAAgB,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG,oBAAoB,CAGjE;AAMD,4EAA4E;AAC5E,eAAO,MAAM,MAAM;;;;;CAKlB,CAAC"}
|