sensemaking 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/status.js +20 -14
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/output/column-hint.js +8 -7
- package/dist/cjs/output/column-hint.js.map +1 -1
- package/dist/cjs/store/index.d.cts +1 -0
- package/dist/cjs/store/index.d.ts +1 -0
- package/dist/cjs/store/index.js +15 -2
- package/dist/cjs/store/index.js.map +1 -1
- package/dist/cjs/watch.js +1 -0
- package/dist/cjs/watch.js.map +1 -1
- package/dist/esm/cli/status.js +6 -3
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/output/column-hint.js +8 -7
- package/dist/esm/output/column-hint.js.map +1 -1
- package/dist/esm/store/index.d.ts +1 -0
- package/dist/esm/store/index.js +14 -2
- package/dist/esm/store/index.js.map +1 -1
- package/dist/esm/watch.js +2 -1
- package/dist/esm/watch.js.map +1 -1
- package/package.json +1 -1
- package/skills/sense-setup/SKILL.md +1 -1
package/dist/cjs/cli/status.js
CHANGED
|
@@ -327,7 +327,7 @@ function embedReachable(e) {
|
|
|
327
327
|
}
|
|
328
328
|
var status = function status(ctx) {
|
|
329
329
|
return _async_to_generator(function() {
|
|
330
|
-
var _ref, _process_env_e_key, _cfg_migratedFrom, _cfg_unknownKeys, _cfg_queries, values, format, cfg, _ref1, store, dbPath, warnings, features, e, hasModel, reachable, _tmp, vectors, _tmp1, presets, staticLanguages, declaredLanguages, detectedLanguages, heartbeat, busyTimeoutMs, _tmp2, keyEnv, result, _tmp3, migrated, shown, total, breakdown, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, line, pid;
|
|
330
|
+
var _ref, _process_env_e_key, _cfg_migratedFrom, _cfg_unknownKeys, _cfg_queries, values, format, cfg, _ref1, store, dbPath, warnings, features, e, hasModel, reachable, _tmp, vectors, _tmp1, presets, staticLanguages, declaredLanguages, detectedLanguages, heartbeat, cacheSchema, busyTimeoutMs, _tmp2, keyEnv, result, _tmp3, migrated, shown, total, breakdown, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, line, pid;
|
|
331
331
|
return _ts_generator(this, function(_state) {
|
|
332
332
|
switch(_state.label){
|
|
333
333
|
case 0:
|
|
@@ -402,29 +402,35 @@ var status = function status(ctx) {
|
|
|
402
402
|
];
|
|
403
403
|
case 10:
|
|
404
404
|
heartbeat = _state.sent();
|
|
405
|
+
return [
|
|
406
|
+
4,
|
|
407
|
+
(0, _indexts1.getMeta)(store, 'schema_version')
|
|
408
|
+
];
|
|
409
|
+
case 11:
|
|
410
|
+
cacheSchema = _state.sent();
|
|
405
411
|
if (!(store.name === 'sqlite')) return [
|
|
406
412
|
3,
|
|
407
|
-
|
|
413
|
+
14
|
|
408
414
|
];
|
|
409
415
|
return [
|
|
410
416
|
4,
|
|
411
417
|
store.prepare('PRAGMA busy_timeout')
|
|
412
418
|
];
|
|
413
|
-
case
|
|
419
|
+
case 12:
|
|
414
420
|
return [
|
|
415
421
|
4,
|
|
416
422
|
_state.sent().get()
|
|
417
423
|
];
|
|
418
|
-
case
|
|
424
|
+
case 13:
|
|
419
425
|
_tmp2 = _state.sent().timeout;
|
|
420
426
|
return [
|
|
421
427
|
3,
|
|
422
|
-
|
|
428
|
+
15
|
|
423
429
|
];
|
|
424
|
-
case 13:
|
|
425
|
-
_tmp2 = null;
|
|
426
|
-
_state.label = 14;
|
|
427
430
|
case 14:
|
|
431
|
+
_tmp2 = null;
|
|
432
|
+
_state.label = 15;
|
|
433
|
+
case 15:
|
|
428
434
|
busyTimeoutMs = _tmp2;
|
|
429
435
|
// The env var holds the token; only its name and whether it is set are ever reported.
|
|
430
436
|
keyEnv = (e === null || e === void 0 ? void 0 : e.key) ? {
|
|
@@ -439,19 +445,19 @@ var status = function status(ctx) {
|
|
|
439
445
|
tree: cfg.baseDir,
|
|
440
446
|
cache: (0, _nodepath.dirname)(dbPath),
|
|
441
447
|
db: dbPath,
|
|
442
|
-
cacheSchema:
|
|
448
|
+
cacheSchema: cacheSchema
|
|
443
449
|
};
|
|
444
450
|
return [
|
|
445
451
|
4,
|
|
446
452
|
(0, _indexts1.docCount)(store)
|
|
447
453
|
];
|
|
448
|
-
case
|
|
454
|
+
case 16:
|
|
449
455
|
_tmp3.docs = _state.sent();
|
|
450
456
|
return [
|
|
451
457
|
4,
|
|
452
458
|
parseErrorCount(store)
|
|
453
459
|
];
|
|
454
|
-
case
|
|
460
|
+
case 17:
|
|
455
461
|
_tmp3.unparseableFrontmatter = _state.sent(), _tmp3.features = features.on, _tmp3.featuresOff = features.off, _tmp3.embed = e ? _object_spread({
|
|
456
462
|
provider: e.provider,
|
|
457
463
|
model: e.model,
|
|
@@ -467,7 +473,7 @@ var status = function status(ctx) {
|
|
|
467
473
|
4,
|
|
468
474
|
(0, _indexts1.getMeta)(store, 'watch_pid')
|
|
469
475
|
];
|
|
470
|
-
case
|
|
476
|
+
case 18:
|
|
471
477
|
result = (_tmp3.watcherPid = _state.sent(), _tmp3.watcherHeartbeatSecondsAgo = heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null, _tmp3.busyTimeoutMs = busyTimeoutMs, _tmp3);
|
|
472
478
|
if (format === 'json') {
|
|
473
479
|
console.log((0, _outputts.stringifyJson)(result, 2));
|
|
@@ -477,7 +483,7 @@ var status = function status(ctx) {
|
|
|
477
483
|
// A load-time stderr warning has scrolled away by the time anyone looks here.
|
|
478
484
|
if (cfg.unknownKeys) console.log(" unknown keys, ignored: ".concat(cfg.unknownKeys.join(', ')));
|
|
479
485
|
console.log("tree: ".concat(result.tree));
|
|
480
|
-
console.log("cache: ".concat(result.cache, " (schema ").concat(
|
|
486
|
+
console.log("cache: ".concat(result.cache, " (schema ").concat(result.cacheSchema, "; delete this directory to reset)"));
|
|
481
487
|
console.log("docs: ".concat(result.docs).concat(result.unparseableFrontmatter > 0 ? " (".concat(result.unparseableFrontmatter, " with unparseable frontmatter: WHERE _parse_error IS NOT NULL)") : ''));
|
|
482
488
|
console.log('');
|
|
483
489
|
console.log((0, _outputts.featuresLine)(features));
|
|
@@ -541,7 +547,7 @@ var status = function status(ctx) {
|
|
|
541
547
|
4,
|
|
542
548
|
store.close()
|
|
543
549
|
];
|
|
544
|
-
case
|
|
550
|
+
case 19:
|
|
545
551
|
_state.sent();
|
|
546
552
|
return [
|
|
547
553
|
2
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/status.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { languageDistribution } from '../embed/distribution.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { featuresLine, presetsLines, stringifyJson } from '../output/output.ts';\nimport { docCount, getMeta, openStore, SCHEMA_VERSION } from '../store/index.ts';\nimport type { Store } from '../store/types.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it\n// derived, state it stored. Config content is not echoed -- the config line says which file to\n// read, and duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a\n// semantic-on preset with nothing embedded yet -- reported here because a fast reconcile on\n// such a tree otherwise reads as \"the feature isn't on\".\nasync function vectorState(store: Store): Promise<{ embedded: number; pending: number } | null> {\n try {\n const stmt = await store.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings');\n return (await stmt.get()) as { embedded: number; pending: number };\n } catch {\n return null;\n }\n}\n\nasync function parseErrorCount(store: Store): Promise<number> {\n const stmt = await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`);\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { store, dbPath, warnings } = await openStore(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? await vectorState(store) : null;\n const presets = await presetCoverage(store, cfg);\n const staticLanguages = e && e.provider === 'static' ? readLanguages(e.model) : undefined;\n const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;\n const detectedLanguages = (await languageDistribution(store)) ?? null;\n const heartbeat = await getMeta(store, 'watch_heartbeat');\n // Derived at open() (F): 3x the largest reconcile this cache has ever held its write\n // transaction for, floored at 30s -- read back from the connection rather than\n // recomputed here, so this always reports what open() actually set. The pragma is\n // sqlite-engine-specific; other stores have no such setting and report null.\n const busyTimeoutMs = store.name === 'sqlite' ? ((await (await store.prepare('PRAGMA busy_timeout')).get()) as { timeout: number }).timeout : null;\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema: SCHEMA_VERSION,\n docs: await docCount(store),\n unparseableFrontmatter: await parseErrorCount(store),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: await getMeta(store, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n busyTimeoutMs,\n };\n\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree: ${result.tree}`);\n console.log(`cache: ${result.cache} (schema ${SCHEMA_VERSION}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages) console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n if (result.busyTimeoutMs !== null) console.log(`sqlite: busy_timeout ${result.busyTimeoutMs}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)`);\n }\n await store.close();\n};\nexport default status;\n"],"names":["redactUrl","vectorState","store","stmt","prepare","get","parseErrorCount","n","url","parsed","URL","username","password","toString","replace","embedReachable","e","base","provider","probeReachable","status","ctx","process","cfg","values","format","dbPath","warnings","features","hasModel","reachable","vectors","presets","staticLanguages","declaredLanguages","detectedLanguages","heartbeat","busyTimeoutMs","keyEnv","result","migrated","shown","total","breakdown","line","pid","parse","argv","USAGE","name","FORMAT","CONFIG","formatOf","resolveConfig","config","openStore","printWarnings","featureStates","embedConfig","modelPresent","presetCoverage","readLanguages","model","undefined","length","languageDistribution","getMeta","timeout","key","set","env","configPath","configVersion","SUPPORTED_CONFIG_VERSION","migratedFrom","unknownConfigKeys","unknownKeys","tree","baseDir","cache","dirname","db","cacheSchema","SCHEMA_VERSION","docCount","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","modelDir","downloaded","languages","queries","Object","keys","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","stringifyJson","join","featuresLine","isDownloadable","MODEL_FILENAMES","embedded","pending","reduce","a","b","entries","sort","map","code","presetsLines","close"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/status.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { languageDistribution } from '../embed/distribution.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { featuresLine, presetsLines, stringifyJson } from '../output/output.ts';\nimport { docCount, getMeta, openStore } from '../store/index.ts';\nimport type { Store } from '../store/types.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it\n// derived, state it stored. Config content is not echoed -- the config line says which file to\n// read, and duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a\n// semantic-on preset with nothing embedded yet -- reported here because a fast reconcile on\n// such a tree otherwise reads as \"the feature isn't on\".\nasync function vectorState(store: Store): Promise<{ embedded: number; pending: number } | null> {\n try {\n const stmt = await store.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings');\n return (await stmt.get()) as { embedded: number; pending: number };\n } catch {\n return null;\n }\n}\n\nasync function parseErrorCount(store: Store): Promise<number> {\n const stmt = await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`);\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { store, dbPath, warnings } = await openStore(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? await vectorState(store) : null;\n const presets = await presetCoverage(store, cfg);\n const staticLanguages = e && e.provider === 'static' ? readLanguages(e.model) : undefined;\n const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;\n const detectedLanguages = (await languageDistribution(store)) ?? null;\n const heartbeat = await getMeta(store, 'watch_heartbeat');\n // Written by the store's open() on every open, so it is this cache's true version on either\n // store (sqlite 18, duckdb 2); a version mismatch already triggered a rebuild before this read.\n const cacheSchema = await getMeta(store, 'schema_version');\n // Derived at open() (F): 3x the largest reconcile this cache has ever held its write\n // transaction for, floored at 30s -- read back from the connection rather than\n // recomputed here, so this always reports what open() actually set. The pragma is\n // sqlite-engine-specific; other stores have no such setting and report null.\n const busyTimeoutMs = store.name === 'sqlite' ? ((await (await store.prepare('PRAGMA busy_timeout')).get()) as { timeout: number }).timeout : null;\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema,\n docs: await docCount(store),\n unparseableFrontmatter: await parseErrorCount(store),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: await getMeta(store, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n busyTimeoutMs,\n };\n\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree: ${result.tree}`);\n console.log(`cache: ${result.cache} (schema ${result.cacheSchema}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages) console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n if (result.busyTimeoutMs !== null) console.log(`sqlite: busy_timeout ${result.busyTimeoutMs}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)`);\n }\n await store.close();\n};\nexport default status;\n"],"names":["redactUrl","vectorState","store","stmt","prepare","get","parseErrorCount","n","url","parsed","URL","username","password","toString","replace","embedReachable","e","base","provider","probeReachable","status","ctx","process","cfg","values","format","dbPath","warnings","features","hasModel","reachable","vectors","presets","staticLanguages","declaredLanguages","detectedLanguages","heartbeat","cacheSchema","busyTimeoutMs","keyEnv","result","migrated","shown","total","breakdown","line","pid","parse","argv","USAGE","name","FORMAT","CONFIG","formatOf","resolveConfig","config","openStore","printWarnings","featureStates","embedConfig","modelPresent","presetCoverage","readLanguages","model","undefined","length","languageDistribution","getMeta","timeout","key","set","env","configPath","configVersion","SUPPORTED_CONFIG_VERSION","migratedFrom","unknownConfigKeys","unknownKeys","tree","baseDir","cache","dirname","db","docCount","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","modelDir","downloaded","languages","queries","Object","keys","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","stringifyJson","join","featuresLine","isDownloadable","MODEL_FILENAMES","embedded","pending","reduce","a","b","entries","sort","map","code","presetsLines","close"],"mappings":";;;;;;;;;;;QAwJA;eAAA;;QAnHgBA;eAAAA;;;wBArCQ;wBACO;uBACsC;8BAChC;sBACN;uBACwD;wBAC7B;wBACb;wBAEvB;wBACyC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG/D,0FAA0F;AAC1F,+FAA+F;AAC/F,4FAA4F;AAC5F,kGAAkG;AAElG,2FAA2F;AAC3F,4FAA4F;AAC5F,yDAAyD;AACzD,SAAeC,YAAYC,KAAY;;YAE7BC;;;;;;;;;;oBAAO;;wBAAMD,MAAME,OAAO,CAAC;;;oBAA3BD,OAAO;oBACL;;wBAAMA,KAAKE,GAAG;;;oBAAtB;;wBAAQ;;;;oBAER;;wBAAO;;;;;;;;IAEX;;AAEA,SAAeC,gBAAgBJ,KAAY;;YACnCC;;;;oBAAO;;wBAAMD,MAAME,OAAO,CAAC;;;oBAA3BD,OAAO;oBACJ;;wBAAMA,KAAKE,GAAG;;;oBAAvB;;wBAAS,cAAoCE,CAAC;;;;IAChD;;AAIO,SAASP,UAAUQ,GAAW;IACnC,IAAI;QACF,IAAMC,SAAS,IAAIC,IAAIF;QACvB,IAAI,CAACC,OAAOE,QAAQ,IAAI,CAACF,OAAOG,QAAQ,EAAE,OAAOJ;QACjDC,OAAOE,QAAQ,GAAG;QAClBF,OAAOG,QAAQ,GAAG;QAClB,OAAO,AAAC,GAAuC,OAArCH,OAAOI,QAAQ,GAAGC,OAAO,CAAC,OAAO,KAAI;IACjD,EAAE,eAAM;QACN,OAAON;IACT;AACF;AAEA,gGAAgG;AAChG,+FAA+F;AAC/F,SAAeO,eAAeC,CAAqC;;YAEnDA,QAARC;;YADN,IAAID,EAAEE,QAAQ,KAAK,YAAaF,EAAEE,QAAQ,KAAK,YAAY,CAACF,EAAER,GAAG,EAAG;;gBAAO;;YACrES,OAAO,EAACD,SAAAA,EAAER,GAAG,cAALQ,oBAAAA,SAAS,0BAA0BF,OAAO,CAAC,QAAQ;YACjE;;gBAAOK,IAAAA,sBAAc,EAACH,EAAEE,QAAQ,KAAK,WAAW,AAAC,GAAO,OAALD,MAAK,aAAWA;;;IACrE;;AAEA,IAAMG,SAAkB,gBAAOC;;YAeF,MAWkBC,oBAI7BC,mBACKA,kBAWEA,cAzCfC,QACFC,QACAF,KAC8B,OAA5BrB,OAAOwB,QAAQC,UAGjBC,UACAZ,GACAa,UACAC,iBACAC,gBACAC,SACAC,iBACAC,mBACAC,mBACAC,WAGAC,aAKAC,sBAEAC,QACAC,eAwBEC,UAmBIC,OAOAC,OACAC,WAQL,2BAAA,mBAAA,gBAAA,WAAA,OAAMC,MAGLC;;;;oBAxFAtB,SAAWuB,IAAAA,eAAK,EAAC1B,IAAI2B,IAAI,EAAE,AAAC,UAAqBC,OAAZ5B,IAAI6B,IAAI,EAAC,KAAgB,OAAbD,eAAK,CAAC7B,MAAM,GAAI,mBAAK+B,gBAAM,EAAKC,gBAAM,GAAvF5B;oBACFC,SAAS4B,IAAAA,kBAAQ,EAAC7B;oBAClBD,MAAMF,IAAIiC,aAAa,CAAC9B,OAAO+B,MAAM;oBACP;;wBAAMC,IAAAA,mBAAS,EAACjC;;;oBAAhB,QAAA,eAA5BrB,QAA4B,MAA5BA,OAAOwB,SAAqB,MAArBA,QAAQC,WAAa,MAAbA;oBACvB8B,IAAAA,uBAAa,EAAC9B;oBAERC,WAAW8B,IAAAA,sBAAa,EAACnC;oBACzBP,IAAI2C,IAAAA,oBAAW,EAACpC;oBAChBM,WAAWb,IAAI4C,IAAAA,qBAAY,EAACrC,OAAO;yBACvBP,GAAAA;;;;oBAAI;;wBAAMD,eAAeC;;;2BAArB;;;;;;2BAA0B;;;oBAA1Cc;yBACUd,GAAAA;;;;oBAAI;;wBAAMf,YAAYC;;;4BAAlB;;;;;;4BAA2B;;;oBAAzC6B;oBACU;;wBAAM8B,IAAAA,wBAAc,EAAC3D,OAAOqB;;;oBAAtCS,UAAU;oBACVC,kBAAkBjB,KAAKA,EAAEE,QAAQ,KAAK,WAAW4C,IAAAA,sBAAa,EAAC9C,EAAE+C,KAAK,IAAIC;oBAC1E9B,oBAAoBD,mBAAmBA,gBAAgBgC,MAAM,GAAG,IAAIhC,kBAAkB;oBACjE;;wBAAMiC,IAAAA,oCAAoB,EAAChE;;;oBAAhDiC,qBAAqB,OAAA,2BAAA,kBAAA,OAAsC;oBAC/C;;wBAAMgC,IAAAA,iBAAO,EAACjE,OAAO;;;oBAAjCkC,YAAY;oBAGE;;wBAAM+B,IAAAA,iBAAO,EAACjE,OAAO;;;oBAAnCmC,cAAc;yBAKEnC,CAAAA,MAAMgD,IAAI,KAAK,QAAO,GAAtBhD;;;;oBAAmC;;wBAAMA,MAAME,OAAO,CAAC;;;oBAA3B;;wBAAO,cAA4CC,GAAG;;;4BAAxD,AAAE,cAAkF+D,OAAO;;;;;;4BAAG;;;oBAAxI9B;oBACN,sFAAsF;oBAChFC,SAASvB,CAAAA,cAAAA,wBAAAA,EAAGqD,GAAG,IAAG;wBAAEnB,MAAMlC,EAAEqD,GAAG;wBAAEC,KAAK,EAAChD,qBAAAA,QAAQiD,GAAG,CAACvD,EAAEqD,GAAG,CAAC,cAAlB/C,gCAAAA,qBAAsB,QAAQ;oBAAG,IAAI;;wBAEhFiC,QAAQhC,IAAIiD,UAAU;wBACtBC,eAAeC,iCAAwB;wBACvCC,YAAY,GAAEpD,oBAAAA,IAAIoD,YAAY,cAAhBpD,+BAAAA,oBAAoB;wBAClCqD,iBAAiB,GAAErD,mBAAAA,IAAIsD,WAAW,cAAftD,8BAAAA,mBAAmB;wBACtCuD,MAAMvD,IAAIwD,OAAO;wBACjBC,OAAOC,IAAAA,iBAAO,EAACvD;wBACfwD,IAAIxD;wBACJW,aAAAA;;oBACM;;wBAAM8C,IAAAA,kBAAQ,EAACjF;;;0BAArBkF,OAAM;oBACkB;;wBAAM9E,gBAAgBJ;;;0BAA9CmF,yBAAwB,qBACxBzD,WAAUA,SAAS0D,EAAE,QACrBC,cAAa3D,SAAS4D,GAAG,QACzBC,QAAOzE,IAAI;wBAAEE,UAAUF,EAAEE,QAAQ;wBAAE6C,OAAO/C,EAAE+C,KAAK;wBAAE2B,KAAK1E,EAAEE,QAAQ,KAAK,WAAWyE,IAAAA,iBAAQ,EAAC3E,EAAE+C,KAAK,IAAI;wBAAMvD,KAAKQ,EAAER,GAAG,GAAGR,UAAUgB,EAAER,GAAG,IAAI;wBAAMsB,WAAAA;wBAAWS,QAAAA;wBAAQqD,YAAY/D;wBAAUgE,WAAW3D;wBAAmBC,mBAAAA;uBAAuBJ,oBAAAA,qBAAAA,UAAW,CAAC,KAAO,YACnQC,UAAAA,eACA8D,UAASC,OAAOC,IAAI,EAACzE,eAAAA,IAAIuE,OAAO,cAAXvE,0BAAAA,eAAe,CAAC,GAAG0C,MAAM;oBAClC;;wBAAME,IAAAA,iBAAO,EAACjE,OAAO;;;oBAhB7BsC,gBAgBJyD,aAAY,qBACZC,6BAA4B9D,YAAY+D,KAAKC,KAAK,CAAC,AAACC,CAAAA,KAAKC,GAAG,KAAKD,KAAKtD,KAAK,CAACX,UAAS,IAAK,QAAQ,YAClGE,gBAAAA;oBAGF,IAAIb,WAAW,QAAQ;wBACrB8E,QAAQC,GAAG,CAACC,IAAAA,uBAAa,EAACjE,QAAQ;oBACpC,OAAO;wBACCC,WAAWlB,IAAIoD,YAAY,KAAKX,YAAY,AAAC,oBAAoC,OAAjBzC,IAAIoD,YAAY,EAAC,kBAAgB;wBACvG4B,QAAQC,GAAG,CAAC,AAAC,aAA+B9B,OAAnBlC,OAAOe,MAAM,EAAC,OAAgCd,OAA3BiC,iCAAwB,EAAY,OAATjC,UAAS;wBAChF,8EAA8E;wBAC9E,IAAIlB,IAAIsD,WAAW,EAAE0B,QAAQC,GAAG,CAAC,AAAC,oCAA8D,OAA3BjF,IAAIsD,WAAW,CAAC6B,IAAI,CAAC;wBAC1FH,QAAQC,GAAG,CAAC,AAAC,aAAwB,OAAZhE,OAAOsC,IAAI;wBACpCyB,QAAQC,GAAG,CAAC,AAAC,aAAoChE,OAAxBA,OAAOwC,KAAK,EAAC,aAA8B,OAAnBxC,OAAOH,WAAW,EAAC;wBACpEkE,QAAQC,GAAG,CAAC,AAAC,aAA0BhE,OAAdA,OAAO4C,IAAI,EAAgJ,OAA7I5C,OAAO6C,sBAAsB,GAAG,IAAI,AAAC,MAAmC,OAA9B7C,OAAO6C,sBAAsB,EAAC,oEAAkE;wBACjLkB,QAAQC,GAAG,CAAC;wBACZD,QAAQC,GAAG,CAACG,IAAAA,sBAAY,EAAC/E;wBACzB,4FAA4F;wBAC5F,0BAA0B;wBAC1B,IAAI,CAACZ,GAAGuF,QAAQC,GAAG,CAAC;6BACf;4BACHD,QAAQC,GAAG,CAAC,AAAC,aAA0BxF,OAAdA,EAAEE,QAAQ,EAAC,KAAW,OAARF,EAAE+C,KAAK;4BAC9C,IAAI/C,EAAEE,QAAQ,KAAK,UAAUqF,QAAQC,GAAG,CAAC,AAAC,sBAA2C3E,OAAtB8D,IAAAA,iBAAQ,EAAC3E,EAAE+C,KAAK,GAAE,MAAoM,OAAhMlC,WAAW,YAAY,AAAC,YAAsK,OAA3J+E,IAAAA,uBAAc,EAAC5F,EAAE+C,KAAK,IAAI,AAAC,kDAA2D,OAAT1C,IAAI6B,IAAI,EAAC,4BAA2B,AAAC,MAAqB,OAAhB2D,wBAAe,EAAC,wBAAsB;4BACrR,IAAI3E,mBAAmBqE,QAAQC,GAAG,CAAC,AAAC,wBAAoD,OAA7BtE,kBAAkBwE,IAAI,CAAC,OAAM;4BACxF,qFAAqF;4BACrF,kDAAkD;4BAClD,IAAI1F,EAAER,GAAG,IAAIsB,cAAc,MAAM;gCACzBY,QAAQ1B,EAAER,GAAG,GAAGR,UAAUgB,EAAER,GAAG,IAAI;gCACzC+F,QAAQC,GAAG,CAAC,AAAC,sBAA6B1E,OAARY,OAAgF,OAAxEZ,cAAc,OAAO,KAAKA,YAAY,iBAAiB;4BACnG;4BACA,IAAIS,QAAQgE,QAAQC,GAAG,CAAC,AAAC,0BAAyCjE,OAAhBA,OAAOW,IAAI,EAAC,MAAmC,OAA/BX,OAAO+B,GAAG,GAAG,QAAQ,WAAU;4BACjG,IAAI,CAACzC,UAAU0E,QAAQC,GAAG,CAAC;iCACtB,IAAIzE,SAASwE,QAAQC,GAAG,CAAC,AAAC,sBAAmDzE,OAA9BA,QAAQ+E,QAAQ,EAAC,eAA6B,OAAhB/E,QAAQgF,OAAO,EAAC;4BAClG,IAAI5E,mBAAmB;gCACfQ,QAAQoD,OAAOvE,MAAM,CAACW,mBAAmB6E,MAAM,CAAC,SAACC,GAAGC;2CAAMD,IAAIC;mCAAG;gCACjEtE,YAAYmD,OAAOoB,OAAO,CAAChF,mBAC9BiF,IAAI,CAAC,SAACH,GAAGC;2CAAMA,CAAC,CAAC,EAAE,GAAGD,CAAC,CAAC,EAAE;mCAC1BI,GAAG,CAAC;6EAAEC,kBAAM/G;2CAAO,AAAC,GAAU4F,OAARmB,MAAK,KAAiC,OAA9BnB,KAAKC,KAAK,CAAC,AAAC7F,IAAIoC,QAAS,MAAK;mCAC5D+D,IAAI,CAAC;gCACRH,QAAQC,GAAG,CAAC,AAAC,uBAAoC7D,OAAdC,WAAU,MAAU,OAAND,OAAM;4BACzD;wBACF;wBACA4D,QAAQC,GAAG,CAAC;wBACP,kCAAA,2BAAA;;4BAAL,IAAK,YAAce,IAAAA,sBAAY,EAACvF,+BAA3B,6BAAA,QAAA,yBAAA;gCAAMa,OAAN;gCAAqC0D,QAAQC,GAAG,CAAC3D;;;4BAAjD;4BAAA;;;qCAAA,6BAAA;oCAAA;;;oCAAA;0CAAA;;;;wBACL0D,QAAQC,GAAG,CAAC;wBACZD,QAAQC,GAAG,CAAC,AAAC,aAAqCnF,OAAzBmB,OAAOsD,OAAO,EAAC,YAAmB,OAATzE,IAAI6B,IAAI,EAAC;wBACrDJ,MAAMN,OAAOyD,UAAU,GAAG,AAAC,OAAwB,OAAlBzD,OAAOyD,UAAU,EAAC,QAAM;wBAC/DM,QAAQC,GAAG,CAAChE,OAAO0D,0BAA0B,KAAK,OAAO,mBAAmB,AAAC,aAAiC1D,OAArBM,KAAI,mBAAmD,OAAlCN,OAAO0D,0BAA0B,EAAC;wBAChJ,IAAI1D,OAAOF,aAAa,KAAK,MAAMiE,QAAQC,GAAG,CAAC,AAAC,0BAA8C,OAArBhE,OAAOF,aAAa,EAAC;oBAChG;oBACA;;wBAAMpC,MAAMsH,KAAK;;;oBAAjB;;;;;;IACF;;IACA,WAAepG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/types.ts"],"sourcesContent":["import type { SignalWeights } from './signals.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 5;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; commands degrade when one is off.\n// links/sections/tags/rank are opt-out toggles in the top-level `features` block; embed is not a\n// member of that block -- it is on exactly when an `embed` block names a model.\nexport const FEATURE_NAMES = ['links', 'sections', 'tags', 'rank', 'embed'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `embed` names the model and gives the tree vectors at all; a preset's `signals` says which\n// engines that scope's searches use.\nexport interface EmbedConfig {\n model?: string;\n provider?: 'static' | 'openai' | 'cohere';\n url?: string; // openai provider: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // openai/cohere provider: name of the env var holding the bearer token, if any\n languages?: string[]; // owner-declared model languages; enables the language-fit check where no card exists\n chunkTokens?: number; // chunk size ceiling in estimated tokens for small-context models; default 500\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\n// A named, self-contained file-selection scope. include/exclude are globby patterns resolved\n// relative to the config file. No inheritance between presets -- every field a preset wants\n// it declares itself. Presets may overlap freely; they are views, not partitions.\nexport interface Preset {\n include: string[];\n exclude?: string[];\n k?: number; // result count for `search` scoped to this preset; default 10\n // Exhaustive when present: every signal this preset's searches compose, each mapped to\n // its RRF weight (a finite number > 0; presence is enablement). Absent means every signal\n // whose prerequisite holds, each at weight 1 -- words always, links when the feature is on,\n // vectors when the tree names a model. A layer searched for exact wording (ingested sources,\n // archives, generated output) declares {\"words\":1,\"links\":1} and costs no embedding at all.\n signals?: SignalWeights;\n where?: string; // standing SQL condition against frontmatter alias `f`\n}\n\n// A saved `search` invocation: `sense <name>` runs like\n// `sense search <search> [--preset] [--include] [--exclude] [--where] [--k]`.\nexport interface SavedSearch {\n search: string;\n preset?: string;\n include?: string[];\n exclude?: string[];\n where?: string;\n k?: number;\n}\n\n// An entry names the verb it runs, one to one with the commands: { sql } runs like\n// `sense sql`, { search } like `sense search`.\nexport type SavedQuery = { sql: string } | SavedSearch;\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n // File selection, index-time, and per-preset search defaults. A file is indexed iff any\n // preset's include/exclude covers it (union). `default` is used when a command names no preset.\n presets: Record<string, Preset>;\n // Global feature defaults; absent block or key means enabled. embed is not a member here --\n // see the `embed` block.\n features?: { links?: boolean; sections?: boolean; tags?: boolean; rank?: boolean };\n // Names the embedding model. Present means every indexed file gets vectors; absent means none do.\n embed?: EmbedConfig;\n // Settings for the `content` FTS5 table. `tokenize` decides which languages the tree can be\n // word-searched in at all; changing it rebuilds the index.\n content?: { tokenize?: string };\n // Backing store engine for this tree
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/types.ts"],"sourcesContent":["import type { SignalWeights } from './signals.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 5;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; commands degrade when one is off.\n// links/sections/tags/rank are opt-out toggles in the top-level `features` block; embed is not a\n// member of that block -- it is on exactly when an `embed` block names a model.\nexport const FEATURE_NAMES = ['links', 'sections', 'tags', 'rank', 'embed'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `embed` names the model and gives the tree vectors at all; a preset's `signals` says which\n// engines that scope's searches use.\nexport interface EmbedConfig {\n model?: string;\n provider?: 'static' | 'openai' | 'cohere';\n url?: string; // openai provider: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // openai/cohere provider: name of the env var holding the bearer token, if any\n languages?: string[]; // owner-declared model languages; enables the language-fit check where no card exists\n chunkTokens?: number; // chunk size ceiling in estimated tokens for small-context models; default 500\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\n// A named, self-contained file-selection scope. include/exclude are globby patterns resolved\n// relative to the config file. No inheritance between presets -- every field a preset wants\n// it declares itself. Presets may overlap freely; they are views, not partitions.\nexport interface Preset {\n include: string[];\n exclude?: string[];\n k?: number; // result count for `search` scoped to this preset; default 10\n // Exhaustive when present: every signal this preset's searches compose, each mapped to\n // its RRF weight (a finite number > 0; presence is enablement). Absent means every signal\n // whose prerequisite holds, each at weight 1 -- words always, links when the feature is on,\n // vectors when the tree names a model. A layer searched for exact wording (ingested sources,\n // archives, generated output) declares {\"words\":1,\"links\":1} and costs no embedding at all.\n signals?: SignalWeights;\n where?: string; // standing SQL condition against frontmatter alias `f`\n}\n\n// A saved `search` invocation: `sense <name>` runs like\n// `sense search <search> [--preset] [--include] [--exclude] [--where] [--k]`.\nexport interface SavedSearch {\n search: string;\n preset?: string;\n include?: string[];\n exclude?: string[];\n where?: string;\n k?: number;\n}\n\n// An entry names the verb it runs, one to one with the commands: { sql } runs like\n// `sense sql`, { search } like `sense search`.\nexport type SavedQuery = { sql: string } | SavedSearch;\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n // File selection, index-time, and per-preset search defaults. A file is indexed iff any\n // preset's include/exclude covers it (union). `default` is used when a command names no preset.\n presets: Record<string, Preset>;\n // Global feature defaults; absent block or key means enabled. embed is not a member here --\n // see the `embed` block.\n features?: { links?: boolean; sections?: boolean; tags?: boolean; rank?: boolean };\n // Names the embedding model. Present means every indexed file gets vectors; absent means none do.\n embed?: EmbedConfig;\n // Settings for the `content` FTS5 table. `tokenize` decides which languages the tree can be\n // word-searched in at all; changing it rebuilds the index.\n content?: { tokenize?: string };\n // Backing store engine for this tree: \"sqlite\" (default, zero-dependency) or \"duckdb\"\n // (an optional dependency, installed on first use). Both implement the same commands.\n store?: 'sqlite' | 'duckdb';\n queries: Record<string, SavedQuery>;\n}\n\nexport interface ResolvedConfig extends Config {\n baseDir: string;\n configPath: string | null;\n // Keys the file declares that this build does not read; cli reports them.\n unknownKeys?: string[];\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\nexport interface SearchOverrides {\n preset?: string;\n k?: number;\n where?: string;\n include?: string[];\n exclude?: string[];\n noExclude?: boolean; // drop the preset's exclude for this command; --exclude still applies\n}\n\nexport interface EffectiveSearch {\n presetName: string;\n k: number;\n where?: string;\n include: string[];\n exclude?: string[];\n signals: SignalWeights; // this preset's effective signal weights\n}\n"],"names":["CONFIG_FILENAME","DEFAULT_EMBED_MODEL","FEATURE_NAMES","STATE_DIR","SUPPORTED_CONFIG_VERSION"],"mappings":";;;;;;;;;;;QAEaA;eAAAA;;QAuBAC;eAAAA;;QAdAC;eAAAA;;QARAC;eAAAA;;QAGAC;eAAAA;;;AAJN,IAAMJ,kBAAkB;AACxB,IAAMG,YAAY;AAGlB,IAAMC,2BAA2B;AAKjC,IAAMF,gBAAgB;IAAC;IAAS;IAAY;IAAQ;IAAQ;CAAQ;AAcpE,IAAMD,sBAAsB"}
|
|
@@ -35,17 +35,18 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
35
35
|
if (n === "Map" || n === "Set") return Array.from(n);
|
|
36
36
|
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
37
37
|
}
|
|
38
|
-
// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
38
|
+
// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL
|
|
39
|
+
// it parses as an expression: `plugin - id`, or table `a` column `b`. SQLite names a fragment
|
|
40
|
+
// (`no such column: plugin`), DuckDB names the fragment or the table (`Referenced column/table
|
|
41
|
+
// "..." not found`); this maps either back to the real column so the error names the fix.
|
|
42
42
|
function startsWithBoundary(name, prefix) {
|
|
43
43
|
return name.length > prefix.length && name.startsWith(prefix) && /\W/.test(name[prefix.length]);
|
|
44
44
|
}
|
|
45
45
|
function columnHint(columns, err) {
|
|
46
|
-
var
|
|
46
|
+
var _ref, _match_;
|
|
47
|
+
var match = /no such column: (\S+)|Referenced column "([^"]+)" not found in FROM clause|Referenced table "([^"]+)" not found/.exec(err.message);
|
|
47
48
|
if (!match) return err;
|
|
48
|
-
var token = match[1];
|
|
49
|
+
var token = (_ref = (_match_ = match[1]) !== null && _match_ !== void 0 ? _match_ : match[2]) !== null && _ref !== void 0 ? _ref : match[3];
|
|
49
50
|
// `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, which also
|
|
50
51
|
// covers a column literally named `a.b` -- the full-token check below matches that case
|
|
51
52
|
// directly, so stripping here never needs to special-case it.
|
|
@@ -98,6 +99,6 @@ function columnHint(columns, err) {
|
|
|
98
99
|
var quoted = _to_consumable_array(candidates).map(function(name) {
|
|
99
100
|
return '"'.concat(name.split('"').join('""'), '"');
|
|
100
101
|
}).join(', ');
|
|
101
|
-
return new Error("".concat(err.message, " -- ").concat(quoted, " needs double quotes in SQL: unquoted,
|
|
102
|
+
return new Error("".concat(err.message, " -- ").concat(quoted, " needs double quotes in SQL: unquoted, the engine parses the punctuation as an operator or a qualifier, not part of a column name."));
|
|
102
103
|
}
|
|
103
104
|
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL\n// it parses as an expression: `plugin - id`, or table `a` column `b`. SQLite names a fragment\n// (`no such column: plugin`), DuckDB names the fragment or the table (`Referenced column/table\n// \"...\" not found`); this maps either back to the real column so the error names the fix.\n\nfunction startsWithBoundary(name: string, prefix: string): boolean {\n return name.length > prefix.length && name.startsWith(prefix) && /\\W/.test(name[prefix.length]);\n}\n\n// columns: the tree's frontmatter columns (store.docs.columns()), read by the caller so this\n// stays a pure function rather than a store dependency.\nexport function columnHint(columns: string[], err: Error): Error {\n const match = /no such column: (\\S+)|Referenced column \"([^\"]+)\" not found in FROM clause|Referenced table \"([^\"]+)\" not found/.exec(err.message);\n if (!match) return err;\n const token = match[1] ?? match[2] ?? match[3];\n // `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, which also\n // covers a column literally named `a.b` -- the full-token check below matches that case\n // directly, so stripping here never needs to special-case it.\n const aliasStripped = token.replace(/^[A-Za-z_]\\w*\\./, '');\n const forms = new Set([token, aliasStripped]);\n const candidates = new Set<string>();\n for (const name of columns) {\n for (const form of forms) {\n if (name === form || startsWithBoundary(name, form)) candidates.add(name);\n }\n }\n if (candidates.size === 0) return err;\n const quoted = [...candidates].map((name) => `\"${name.split('\"').join('\"\"')}\"`).join(', ');\n return new Error(`${err.message} -- ${quoted} needs double quotes in SQL: unquoted, the engine parses the punctuation as an operator or a qualifier, not part of a column name.`);\n}\n"],"names":["columnHint","startsWithBoundary","name","prefix","length","startsWith","test","columns","err","match","exec","message","token","aliasStripped","replace","forms","Set","candidates","form","add","size","quoted","map","split","join","Error"],"mappings":";;;;+BAWgBA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAXhB,gGAAgG;AAChG,8FAA8F;AAC9F,+FAA+F;AAC/F,0FAA0F;AAE1F,SAASC,mBAAmBC,IAAY,EAAEC,MAAc;IACtD,OAAOD,KAAKE,MAAM,GAAGD,OAAOC,MAAM,IAAIF,KAAKG,UAAU,CAACF,WAAW,KAAKG,IAAI,CAACJ,IAAI,CAACC,OAAOC,MAAM,CAAC;AAChG;AAIO,SAASJ,WAAWO,OAAiB,EAAEC,GAAU;QAGxCC,MAAAA;IAFd,IAAMA,QAAQ,kHAAkHC,IAAI,CAACF,IAAIG,OAAO;IAChJ,IAAI,CAACF,OAAO,OAAOD;IACnB,IAAMI,SAAQH,QAAAA,UAAAA,KAAK,CAAC,EAAE,cAARA,qBAAAA,UAAYA,KAAK,CAAC,EAAE,cAApBA,kBAAAA,OAAwBA,KAAK,CAAC,EAAE;IAC9C,uFAAuF;IACvF,wFAAwF;IACxF,8DAA8D;IAC9D,IAAMI,gBAAgBD,MAAME,OAAO,CAAC,mBAAmB;IACvD,IAAMC,QAAQ,IAAIC,IAAI;QAACJ;QAAOC;KAAc;IAC5C,IAAMI,aAAa,IAAID;QAClB,kCAAA,2BAAA;;QAAL,QAAK,YAAcT,4BAAd,SAAA,6BAAA,QAAA,yBAAA,iCAAuB;YAAvB,IAAML,OAAN;gBACE,mCAAA,4BAAA;;gBAAL,QAAK,aAAca,0BAAd,UAAA,8BAAA,SAAA,0BAAA,kCAAqB;oBAArB,IAAMG,OAAN;oBACH,IAAIhB,SAASgB,QAAQjB,mBAAmBC,MAAMgB,OAAOD,WAAWE,GAAG,CAACjB;gBACtE;;gBAFK;gBAAA;;;yBAAA,8BAAA;wBAAA;;;wBAAA;8BAAA;;;;QAGP;;QAJK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAKL,IAAIe,WAAWG,IAAI,KAAK,GAAG,OAAOZ;IAClC,IAAMa,SAAS,AAAC,qBAAGJ,YAAYK,GAAG,CAAC,SAACpB;eAAS,AAAC,IAA8B,OAA3BA,KAAKqB,KAAK,CAAC,KAAKC,IAAI,CAAC,OAAM;OAAIA,IAAI,CAAC;IACrF,OAAO,IAAIC,MAAM,AAAC,GAAoBJ,OAAlBb,IAAIG,OAAO,EAAC,QAAa,OAAPU,QAAO;AAC/C"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ResolvedConfig } from '../config/index.js';
|
|
2
2
|
import type { OpenResult } from './sqlite/open.js';
|
|
3
3
|
export declare function openStore(cfg: ResolvedConfig): Promise<OpenResult>;
|
|
4
|
+
export declare function requireWatchConcurrency(cfg: ResolvedConfig): void;
|
|
4
5
|
export { getMeta, setMeta } from './meta.js';
|
|
5
6
|
export type { OpenResult } from './sqlite/open.js';
|
|
6
7
|
export { clearCache, DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.js';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ResolvedConfig } from '../config/index.js';
|
|
2
2
|
import type { OpenResult } from './sqlite/open.js';
|
|
3
3
|
export declare function openStore(cfg: ResolvedConfig): Promise<OpenResult>;
|
|
4
|
+
export declare function requireWatchConcurrency(cfg: ResolvedConfig): void;
|
|
4
5
|
export { getMeta, setMeta } from './meta.js';
|
|
5
6
|
export type { OpenResult } from './sqlite/open.js';
|
|
6
7
|
export { clearCache, DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.js';
|
package/dist/cjs/store/index.js
CHANGED
|
@@ -29,6 +29,9 @@ _export(exports, {
|
|
|
29
29
|
get openStore () {
|
|
30
30
|
return openStore;
|
|
31
31
|
},
|
|
32
|
+
get requireWatchConcurrency () {
|
|
33
|
+
return requireWatchConcurrency;
|
|
34
|
+
},
|
|
32
35
|
get setMeta () {
|
|
33
36
|
return _metats.setMeta;
|
|
34
37
|
}
|
|
@@ -177,13 +180,17 @@ var REGISTRY = {
|
|
|
177
180
|
open: _opents.openDuckdb
|
|
178
181
|
}
|
|
179
182
|
};
|
|
183
|
+
function entryFor(name) {
|
|
184
|
+
var entry = REGISTRY[name];
|
|
185
|
+
if (!entry) throw new _errorsts.SenseError('STORE_UNKNOWN', 'unknown backing store "'.concat(name, '"; available: ').concat(Object.keys(REGISTRY).join(', ')));
|
|
186
|
+
return entry;
|
|
187
|
+
}
|
|
180
188
|
function openStore(cfg) {
|
|
181
189
|
return _async_to_generator(function() {
|
|
182
190
|
var name, entry;
|
|
183
191
|
return _ts_generator(this, function(_state) {
|
|
184
192
|
name = (0, _indexts.storeName)(cfg);
|
|
185
|
-
entry =
|
|
186
|
-
if (!entry) throw new _errorsts.SenseError('STORE_UNKNOWN', 'unknown backing store "'.concat(name, '"; available: ').concat(Object.keys(REGISTRY).join(', ')));
|
|
193
|
+
entry = entryFor(name);
|
|
187
194
|
// The one unambiguous, config-level capability need: an embed block some preset actually
|
|
188
195
|
// uses for vectors. Word/lexical search has no equivalent unambiguous signal at this level
|
|
189
196
|
// (every preset defaults to wanting "words"), so a store lacking 'lexical' fails loudly at
|
|
@@ -198,4 +205,10 @@ function openStore(cfg) {
|
|
|
198
205
|
});
|
|
199
206
|
})();
|
|
200
207
|
}
|
|
208
|
+
function requireWatchConcurrency(cfg) {
|
|
209
|
+
var name = (0, _indexts.storeName)(cfg);
|
|
210
|
+
if (!entryFor(name).capabilities.has('watch-concurrency')) {
|
|
211
|
+
throw new _errorsts.SenseError('STORE_CAPABILITY_MISSING', 'store "'.concat(name, '" does not implement "watch-concurrency" in this build; sense watch holds the store open for its whole run, and this store locks the cache file, so every other command on this tree would fail to open; set "store" to "sqlite" in sense.config.json'));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
201
214
|
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/index.ts"],"sourcesContent":["// Rows -> a backing store: registry + openStore(cfg). Parsing lives in scan.ts; everything\n// beyond frontmatter + content lives in src/features/.\n\nimport type { ResolvedConfig } from '../config/index.ts';\nimport { anyPresetEmbeds, storeName } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { openDuckdb } from './duckdb/open.ts';\nimport { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.ts';\nimport type { OpenResult } from './sqlite/open.ts';\nimport { openSqlite } from './sqlite/open.ts';\nimport { CAPABILITIES as SQLITE_CAPABILITIES } from './sqlite/store.ts';\nimport type { Capability } from './types.ts';\n\ntype StoreName = 'sqlite' | 'duckdb';\n\ninterface StoreEntry {\n capabilities: ReadonlySet<Capability>;\n open: (cfg: ResolvedConfig) => Promise<OpenResult>;\n}\n\n// Capabilities are checked against the registry entry before opening (no wasted connect/reconcile\n// work for a config that cannot be satisfied), then the store itself opens.\nconst REGISTRY: Record<StoreName, StoreEntry> = {\n sqlite: { capabilities: SQLITE_CAPABILITIES, open: openSqlite },\n duckdb: { capabilities: DUCKDB_CAPABILITIES, open: openDuckdb },\n};\n\
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/index.ts"],"sourcesContent":["// Rows -> a backing store: registry + openStore(cfg). Parsing lives in scan.ts; everything\n// beyond frontmatter + content lives in src/features/.\n\nimport type { ResolvedConfig } from '../config/index.ts';\nimport { anyPresetEmbeds, storeName } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { openDuckdb } from './duckdb/open.ts';\nimport { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.ts';\nimport type { OpenResult } from './sqlite/open.ts';\nimport { openSqlite } from './sqlite/open.ts';\nimport { CAPABILITIES as SQLITE_CAPABILITIES } from './sqlite/store.ts';\nimport type { Capability } from './types.ts';\n\ntype StoreName = 'sqlite' | 'duckdb';\n\ninterface StoreEntry {\n capabilities: ReadonlySet<Capability>;\n open: (cfg: ResolvedConfig) => Promise<OpenResult>;\n}\n\n// Capabilities are checked against the registry entry before opening (no wasted connect/reconcile\n// work for a config that cannot be satisfied), then the store itself opens.\nconst REGISTRY: Record<StoreName, StoreEntry> = {\n sqlite: { capabilities: SQLITE_CAPABILITIES, open: openSqlite },\n duckdb: { capabilities: DUCKDB_CAPABILITIES, open: openDuckdb },\n};\n\nfunction entryFor(name: StoreName): StoreEntry {\n const entry = REGISTRY[name];\n if (!entry) throw new SenseError('STORE_UNKNOWN', `unknown backing store \"${name}\"; available: ${Object.keys(REGISTRY).join(', ')}`);\n return entry;\n}\n\nexport async function openStore(cfg: ResolvedConfig): Promise<OpenResult> {\n const name = storeName(cfg);\n const entry = entryFor(name);\n\n // The one unambiguous, config-level capability need: an embed block some preset actually\n // uses for vectors. Word/lexical search has no equivalent unambiguous signal at this level\n // (every preset defaults to wanting \"words\"), so a store lacking 'lexical' fails loudly at\n // first use instead (see duckdb/store.ts's lexical.query()).\n if (anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"vectors\" in this build, but this config's \"embed\" block is in use by at least one preset; remove or narrow it, or set \"store\" to a store that supports vectors (sqlite)`);\n }\n\n return entry.open(cfg);\n}\n\n// watch keeps its connection open for the run, so a file-locking store would fail every\n// other command on the tree; check before open, as openStore does for \"vectors\".\nexport function requireWatchConcurrency(cfg: ResolvedConfig): void {\n const name = storeName(cfg);\n if (!entryFor(name).capabilities.has('watch-concurrency')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"watch-concurrency\" in this build; sense watch holds the store open for its whole run, and this store locks the cache file, so every other command on this tree would fail to open; set \"store\" to \"sqlite\" in sense.config.json`);\n }\n}\n\nexport { getMeta, setMeta } from './meta.ts';\nexport type { OpenResult } from './sqlite/open.ts';\nexport { clearCache, DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.ts';\nexport type { Capability, Connection, DocumentStore, LexicalHit, LexicalIndex, LexicalQueryOptions, RawStatement, RunResult, SqlSession, Statement, Store, VectorCandidate, VectorSimilar, VectorStore, VectorWriteRow } from './types.ts';\n"],"names":["DB_FILENAME","SCHEMA_VERSION","clearCache","docCount","getMeta","openStore","requireWatchConcurrency","setMeta","REGISTRY","sqlite","capabilities","SQLITE_CAPABILITIES","open","openSqlite","duckdb","DUCKDB_CAPABILITIES","openDuckdb","entryFor","name","entry","SenseError","Object","keys","join","cfg","storeName","anyPresetEmbeds","has"],"mappings":"AAAA,2FAA2F;AAC3F,uDAAuD;;;;;;;;;;;;QA0DlCA;eAAAA,oBAAW;;QAAYC;eAAAA,uBAAc;;QAAjDC;eAAAA,mBAAU;;QAAeC;eAAAA,iBAAQ;;QAFjCC;eAAAA,eAAO;;QAxBMC;eAAAA;;QAiBNC;eAAAA;;QAOEC;eAAAA,eAAO;;;uBArDkB;wBAChB;sBACA;uBACyB;uBAEzB;wBACyB;sBA+CnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AArCjC,kGAAkG;AAClG,4EAA4E;AAC5E,IAAMC,WAA0C;IAC9CC,QAAQ;QAAEC,cAAcC,sBAAmB;QAAEC,MAAMC,mBAAU;IAAC;IAC9DC,QAAQ;QAAEJ,cAAcK,qBAAmB;QAAEH,MAAMI,kBAAU;IAAC;AAChE;AAEA,SAASC,SAASC,IAAe;IAC/B,IAAMC,QAAQX,QAAQ,CAACU,KAAK;IAC5B,IAAI,CAACC,OAAO,MAAM,IAAIC,oBAAU,CAAC,iBAAiB,AAAC,0BAA8CC,OAArBH,MAAK,kBAAiD,OAAjCG,OAAOC,IAAI,CAACd,UAAUe,IAAI,CAAC;IAC5H,OAAOJ;AACT;AAEO,SAAed,UAAUmB,GAAmB;;YAC3CN,MACAC;;YADAD,OAAOO,IAAAA,kBAAS,EAACD;YACjBL,QAAQF,SAASC;YAEvB,yFAAyF;YACzF,2FAA2F;YAC3F,2FAA2F;YAC3F,6DAA6D;YAC7D,IAAIQ,IAAAA,wBAAe,EAACF,QAAQ,CAACL,MAAMT,YAAY,CAACiB,GAAG,CAAC,YAAY;gBAC9D,MAAM,IAAIP,oBAAU,CAAC,4BAA4B,AAAC,UAAc,OAALF,MAAK;YAClE;YAEA;;gBAAOC,MAAMP,IAAI,CAACY;;;IACpB;;AAIO,SAASlB,wBAAwBkB,GAAmB;IACzD,IAAMN,OAAOO,IAAAA,kBAAS,EAACD;IACvB,IAAI,CAACP,SAASC,MAAMR,YAAY,CAACiB,GAAG,CAAC,sBAAsB;QACzD,MAAM,IAAIP,oBAAU,CAAC,4BAA4B,AAAC,UAAc,OAALF,MAAK;IAClE;AACF"}
|
package/dist/cjs/watch.js
CHANGED
|
@@ -150,6 +150,7 @@ function runWatch(_0) {
|
|
|
150
150
|
switch(_state.label){
|
|
151
151
|
case 0:
|
|
152
152
|
opts = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
|
|
153
|
+
(0, _indexts1.requireWatchConcurrency)(cfg);
|
|
153
154
|
onEvent = (_opts_onEvent = opts.onEvent) !== null && _opts_onEvent !== void 0 ? _opts_onEvent : function() {};
|
|
154
155
|
debounceMs = (_opts_debounceMs = opts.debounceMs) !== null && _opts_debounceMs !== void 0 ? _opts_debounceMs : DEBOUNCE_MS;
|
|
155
156
|
heartbeatIntervalMs = (_opts_heartbeatIntervalMs = opts.heartbeatIntervalMs) !== null && _opts_heartbeatIntervalMs !== void 0 ? _opts_heartbeatIntervalMs : HEARTBEAT_INTERVAL_MS;
|
package/dist/cjs/watch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/watch.ts"],"sourcesContent":["import { watch as fsWatch } from 'node:fs';\nimport type { ResolvedConfig } from './config/index.ts';\nimport { STATE_DIR } from './config/index.ts';\nimport { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { docCount, getMeta, openStore, setMeta } from './store/index.ts';\n\n// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.\nconst DEBOUNCE_MS = 200;\nconst HEARTBEAT_INTERVAL_MS = 5000;\nconst STALE_HEARTBEAT_MS = 15000;\n\nexport type WatchEvent = { type: 'started'; baseDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n // Aborting runs the same shutdown path as SIGINT/SIGTERM.\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\n// Runs in the foreground until SIGINT/SIGTERM/signal abort. Throws WATCH_ACTIVE if another watcher's heartbeat is still fresh and --force wasn't given.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const heartbeatIntervalMs = opts.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;\n const { store, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg);\n const baseDir = cfg.baseDir;\n\n const existingHeartbeat = await getMeta(store, 'watch_heartbeat');\n if (existingHeartbeat && !opts.force) {\n const age = Date.now() - Date.parse(existingHeartbeat);\n if (age >= 0 && age < STALE_HEARTBEAT_MS) {\n await store.close();\n throw new SenseError('WATCH_ACTIVE', `another watcher appears active (heartbeat ${Math.round(age / 1000)}s ago); use --force to override`);\n }\n }\n\n onEvent({ type: 'started', baseDir, dbPath });\n if (initialWarnings.length > 0 || initialParsed > 0) {\n onEvent({ type: 'reconciled', parsed: initialParsed, total: await docCount(store), warnings: initialWarnings });\n }\n\n let stopping = false;\n const touchHeartbeat = async () => {\n await setMeta(store, 'watch_heartbeat', new Date().toISOString());\n await setMeta(store, 'watch_pid', String(process.pid));\n };\n await touchHeartbeat();\n\n let debounceTimer: NodeJS.Timeout | null = null;\n const scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(async () => {\n debounceTimer = null;\n try {\n const { parsed, warnings } = await store.reconcile();\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n }\n }, debounceMs);\n };\n\n // Ignore our own state dir, or the heartbeat write would retrigger itself forever.\n const watcher = fsWatch(baseDir, { recursive: true }, (_event, filename) => {\n if (typeof filename === 'string' && filename.startsWith(STATE_DIR)) return;\n scheduleReconcile();\n });\n const heartbeatTimer = setInterval(\n guardedTick(touchHeartbeat, () => stopping),\n heartbeatIntervalMs\n );\n\n return new Promise<void>((resolveShutdown) => {\n // SIGINT/SIGTERM and an aborted signal all run this same path exactly once; each is\n // unregistered here too so a second runWatch call in the same process starts clean.\n const shutdown = async () => {\n if (stopping) return;\n stopping = true;\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n opts.signal?.removeEventListener('abort', shutdown);\n clearInterval(heartbeatTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n await setMeta(store, 'watch_heartbeat', null);\n await setMeta(store, 'watch_pid', null);\n await store.close();\n resolveShutdown();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n if (opts.signal?.aborted) shutdown();\n else opts.signal?.addEventListener('abort', shutdown, { once: true });\n });\n}\n"],"names":["runWatch","DEBOUNCE_MS","HEARTBEAT_INTERVAL_MS","STALE_HEARTBEAT_MS","cfg","opts","onEvent","debounceMs","heartbeatIntervalMs","store","dbPath","initialWarnings","initialParsed","baseDir","existingHeartbeat","age","stopping","touchHeartbeat","debounceTimer","scheduleReconcile","watcher","heartbeatTimer","openStore","warnings","parsed","getMeta","force","Date","now","parse","close","SenseError","Math","round","type","length","docCount","total","setMeta","toISOString","String","process","pid","clearTimeout","setTimeout","err","reconcile","message","fsWatch","recursive","_event","filename","startsWith","STATE_DIR","setInterval","guardedTick","Promise","resolveShutdown","shutdown","off","signal","removeEventListener","clearInterval","once","aborted","addEventListener"],"mappings":";;;;+BAwBsBA;;;eAAAA;;;sBAxBW;uBAEP;wBACC;6BACC;
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/watch.ts"],"sourcesContent":["import { watch as fsWatch } from 'node:fs';\nimport type { ResolvedConfig } from './config/index.ts';\nimport { STATE_DIR } from './config/index.ts';\nimport { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { docCount, getMeta, openStore, requireWatchConcurrency, setMeta } from './store/index.ts';\n\n// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.\nconst DEBOUNCE_MS = 200;\nconst HEARTBEAT_INTERVAL_MS = 5000;\nconst STALE_HEARTBEAT_MS = 15000;\n\nexport type WatchEvent = { type: 'started'; baseDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n // Aborting runs the same shutdown path as SIGINT/SIGTERM.\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\n// Runs in the foreground until SIGINT/SIGTERM/signal abort. Throws WATCH_ACTIVE if another watcher's heartbeat is still fresh and --force wasn't given.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n requireWatchConcurrency(cfg);\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const heartbeatIntervalMs = opts.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;\n const { store, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg);\n const baseDir = cfg.baseDir;\n\n const existingHeartbeat = await getMeta(store, 'watch_heartbeat');\n if (existingHeartbeat && !opts.force) {\n const age = Date.now() - Date.parse(existingHeartbeat);\n if (age >= 0 && age < STALE_HEARTBEAT_MS) {\n await store.close();\n throw new SenseError('WATCH_ACTIVE', `another watcher appears active (heartbeat ${Math.round(age / 1000)}s ago); use --force to override`);\n }\n }\n\n onEvent({ type: 'started', baseDir, dbPath });\n if (initialWarnings.length > 0 || initialParsed > 0) {\n onEvent({ type: 'reconciled', parsed: initialParsed, total: await docCount(store), warnings: initialWarnings });\n }\n\n let stopping = false;\n const touchHeartbeat = async () => {\n await setMeta(store, 'watch_heartbeat', new Date().toISOString());\n await setMeta(store, 'watch_pid', String(process.pid));\n };\n await touchHeartbeat();\n\n let debounceTimer: NodeJS.Timeout | null = null;\n const scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(async () => {\n debounceTimer = null;\n try {\n const { parsed, warnings } = await store.reconcile();\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n }\n }, debounceMs);\n };\n\n // Ignore our own state dir, or the heartbeat write would retrigger itself forever.\n const watcher = fsWatch(baseDir, { recursive: true }, (_event, filename) => {\n if (typeof filename === 'string' && filename.startsWith(STATE_DIR)) return;\n scheduleReconcile();\n });\n const heartbeatTimer = setInterval(\n guardedTick(touchHeartbeat, () => stopping),\n heartbeatIntervalMs\n );\n\n return new Promise<void>((resolveShutdown) => {\n // SIGINT/SIGTERM and an aborted signal all run this same path exactly once; each is\n // unregistered here too so a second runWatch call in the same process starts clean.\n const shutdown = async () => {\n if (stopping) return;\n stopping = true;\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n opts.signal?.removeEventListener('abort', shutdown);\n clearInterval(heartbeatTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n await setMeta(store, 'watch_heartbeat', null);\n await setMeta(store, 'watch_pid', null);\n await store.close();\n resolveShutdown();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n if (opts.signal?.aborted) shutdown();\n else opts.signal?.addEventListener('abort', shutdown, { once: true });\n });\n}\n"],"names":["runWatch","DEBOUNCE_MS","HEARTBEAT_INTERVAL_MS","STALE_HEARTBEAT_MS","cfg","opts","onEvent","debounceMs","heartbeatIntervalMs","store","dbPath","initialWarnings","initialParsed","baseDir","existingHeartbeat","age","stopping","touchHeartbeat","debounceTimer","scheduleReconcile","watcher","heartbeatTimer","requireWatchConcurrency","openStore","warnings","parsed","getMeta","force","Date","now","parse","close","SenseError","Math","round","type","length","docCount","total","setMeta","toISOString","String","process","pid","clearTimeout","setTimeout","err","reconcile","message","fsWatch","recursive","_event","filename","startsWith","STATE_DIR","setInterval","guardedTick","Promise","resolveShutdown","shutdown","off","signal","removeEventListener","clearInterval","once","aborted","addEventListener"],"mappings":"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"}
|
package/dist/esm/cli/status.js
CHANGED
|
@@ -5,7 +5,7 @@ import { languageDistribution } from '../embed/distribution.js';
|
|
|
5
5
|
import { probeReachable } from '../embed/http.js';
|
|
6
6
|
import { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.js';
|
|
7
7
|
import { featuresLine, presetsLines, stringifyJson } from '../output/output.js';
|
|
8
|
-
import { docCount, getMeta, openStore
|
|
8
|
+
import { docCount, getMeta, openStore } from '../store/index.js';
|
|
9
9
|
import { USAGE } from './index.js';
|
|
10
10
|
import { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.js';
|
|
11
11
|
// status names what you cannot read off the config file: locations sense chose, values it
|
|
@@ -68,6 +68,9 @@ const status = async (ctx)=>{
|
|
|
68
68
|
const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;
|
|
69
69
|
const detectedLanguages = (_ref = await languageDistribution(store)) !== null && _ref !== void 0 ? _ref : null;
|
|
70
70
|
const heartbeat = await getMeta(store, 'watch_heartbeat');
|
|
71
|
+
// Written by the store's open() on every open, so it is this cache's true version on either
|
|
72
|
+
// store (sqlite 18, duckdb 2); a version mismatch already triggered a rebuild before this read.
|
|
73
|
+
const cacheSchema = await getMeta(store, 'schema_version');
|
|
71
74
|
// Derived at open() (F): 3x the largest reconcile this cache has ever held its write
|
|
72
75
|
// transaction for, floored at 30s -- read back from the connection rather than
|
|
73
76
|
// recomputed here, so this always reports what open() actually set. The pragma is
|
|
@@ -86,7 +89,7 @@ const status = async (ctx)=>{
|
|
|
86
89
|
tree: cfg.baseDir,
|
|
87
90
|
cache: dirname(dbPath),
|
|
88
91
|
db: dbPath,
|
|
89
|
-
cacheSchema
|
|
92
|
+
cacheSchema,
|
|
90
93
|
docs: await docCount(store),
|
|
91
94
|
unparseableFrontmatter: await parseErrorCount(store),
|
|
92
95
|
features: features.on,
|
|
@@ -117,7 +120,7 @@ const status = async (ctx)=>{
|
|
|
117
120
|
// A load-time stderr warning has scrolled away by the time anyone looks here.
|
|
118
121
|
if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);
|
|
119
122
|
console.log(`tree: ${result.tree}`);
|
|
120
|
-
console.log(`cache: ${result.cache} (schema ${
|
|
123
|
+
console.log(`cache: ${result.cache} (schema ${result.cacheSchema}; delete this directory to reset)`);
|
|
121
124
|
console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);
|
|
122
125
|
console.log('');
|
|
123
126
|
console.log(featuresLine(features));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/status.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { languageDistribution } from '../embed/distribution.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { featuresLine, presetsLines, stringifyJson } from '../output/output.ts';\nimport { docCount, getMeta, openStore, SCHEMA_VERSION } from '../store/index.ts';\nimport type { Store } from '../store/types.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it\n// derived, state it stored. Config content is not echoed -- the config line says which file to\n// read, and duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a\n// semantic-on preset with nothing embedded yet -- reported here because a fast reconcile on\n// such a tree otherwise reads as \"the feature isn't on\".\nasync function vectorState(store: Store): Promise<{ embedded: number; pending: number } | null> {\n try {\n const stmt = await store.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings');\n return (await stmt.get()) as { embedded: number; pending: number };\n } catch {\n return null;\n }\n}\n\nasync function parseErrorCount(store: Store): Promise<number> {\n const stmt = await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`);\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { store, dbPath, warnings } = await openStore(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? await vectorState(store) : null;\n const presets = await presetCoverage(store, cfg);\n const staticLanguages = e && e.provider === 'static' ? readLanguages(e.model) : undefined;\n const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;\n const detectedLanguages = (await languageDistribution(store)) ?? null;\n const heartbeat = await getMeta(store, 'watch_heartbeat');\n // Derived at open() (F): 3x the largest reconcile this cache has ever held its write\n // transaction for, floored at 30s -- read back from the connection rather than\n // recomputed here, so this always reports what open() actually set. The pragma is\n // sqlite-engine-specific; other stores have no such setting and report null.\n const busyTimeoutMs = store.name === 'sqlite' ? ((await (await store.prepare('PRAGMA busy_timeout')).get()) as { timeout: number }).timeout : null;\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema: SCHEMA_VERSION,\n docs: await docCount(store),\n unparseableFrontmatter: await parseErrorCount(store),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: await getMeta(store, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n busyTimeoutMs,\n };\n\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree: ${result.tree}`);\n console.log(`cache: ${result.cache} (schema ${SCHEMA_VERSION}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages) console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n if (result.busyTimeoutMs !== null) console.log(`sqlite: busy_timeout ${result.busyTimeoutMs}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)`);\n }\n await store.close();\n};\nexport default status;\n"],"names":["dirname","presetCoverage","embedConfig","featureStates","SUPPORTED_CONFIG_VERSION","languageDistribution","probeReachable","isDownloadable","MODEL_FILENAMES","modelDir","modelPresent","readLanguages","featuresLine","presetsLines","stringifyJson","docCount","getMeta","openStore","SCHEMA_VERSION","USAGE","CONFIG","FORMAT","formatOf","parse","printWarnings","vectorState","store","stmt","prepare","get","parseErrorCount","n","redactUrl","url","parsed","URL","username","password","toString","replace","embedReachable","e","provider","base","status","ctx","process","cfg","values","argv","name","format","resolveConfig","config","dbPath","warnings","features","hasModel","reachable","vectors","presets","staticLanguages","model","undefined","declaredLanguages","length","detectedLanguages","heartbeat","busyTimeoutMs","timeout","keyEnv","key","set","env","result","configPath","configVersion","migratedFrom","unknownConfigKeys","unknownKeys","tree","baseDir","cache","db","cacheSchema","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","downloaded","languages","queries","Object","keys","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","migrated","join","shown","embedded","pending","total","reduce","a","b","breakdown","entries","sort","map","code","line","pid","close"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/status.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { languageDistribution } from '../embed/distribution.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { featuresLine, presetsLines, stringifyJson } from '../output/output.ts';\nimport { docCount, getMeta, openStore } from '../store/index.ts';\nimport type { Store } from '../store/types.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it\n// derived, state it stored. Config content is not echoed -- the config line says which file to\n// read, and duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a\n// semantic-on preset with nothing embedded yet -- reported here because a fast reconcile on\n// such a tree otherwise reads as \"the feature isn't on\".\nasync function vectorState(store: Store): Promise<{ embedded: number; pending: number } | null> {\n try {\n const stmt = await store.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings');\n return (await stmt.get()) as { embedded: number; pending: number };\n } catch {\n return null;\n }\n}\n\nasync function parseErrorCount(store: Store): Promise<number> {\n const stmt = await store.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`);\n return ((await stmt.get()) as { n: number }).n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { store, dbPath, warnings } = await openStore(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? await vectorState(store) : null;\n const presets = await presetCoverage(store, cfg);\n const staticLanguages = e && e.provider === 'static' ? readLanguages(e.model) : undefined;\n const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;\n const detectedLanguages = (await languageDistribution(store)) ?? null;\n const heartbeat = await getMeta(store, 'watch_heartbeat');\n // Written by the store's open() on every open, so it is this cache's true version on either\n // store (sqlite 18, duckdb 2); a version mismatch already triggered a rebuild before this read.\n const cacheSchema = await getMeta(store, 'schema_version');\n // Derived at open() (F): 3x the largest reconcile this cache has ever held its write\n // transaction for, floored at 30s -- read back from the connection rather than\n // recomputed here, so this always reports what open() actually set. The pragma is\n // sqlite-engine-specific; other stores have no such setting and report null.\n const busyTimeoutMs = store.name === 'sqlite' ? ((await (await store.prepare('PRAGMA busy_timeout')).get()) as { timeout: number }).timeout : null;\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema,\n docs: await docCount(store),\n unparseableFrontmatter: await parseErrorCount(store),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: await getMeta(store, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n busyTimeoutMs,\n };\n\n if (format === 'json') {\n console.log(stringifyJson(result, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree: ${result.tree}`);\n console.log(`cache: ${result.cache} (schema ${result.cacheSchema}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages) console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n if (result.busyTimeoutMs !== null) console.log(`sqlite: busy_timeout ${result.busyTimeoutMs}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)`);\n }\n await store.close();\n};\nexport default status;\n"],"names":["dirname","presetCoverage","embedConfig","featureStates","SUPPORTED_CONFIG_VERSION","languageDistribution","probeReachable","isDownloadable","MODEL_FILENAMES","modelDir","modelPresent","readLanguages","featuresLine","presetsLines","stringifyJson","docCount","getMeta","openStore","USAGE","CONFIG","FORMAT","formatOf","parse","printWarnings","vectorState","store","stmt","prepare","get","parseErrorCount","n","redactUrl","url","parsed","URL","username","password","toString","replace","embedReachable","e","provider","base","status","ctx","process","cfg","values","argv","name","format","resolveConfig","config","dbPath","warnings","features","hasModel","reachable","vectors","presets","staticLanguages","model","undefined","declaredLanguages","length","detectedLanguages","heartbeat","cacheSchema","busyTimeoutMs","timeout","keyEnv","key","set","env","result","configPath","configVersion","migratedFrom","unknownConfigKeys","unknownKeys","tree","baseDir","cache","db","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","downloaded","languages","queries","Object","keys","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","migrated","join","shown","embedded","pending","total","reduce","a","b","breakdown","entries","sort","map","code","line","pid","close"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/types.ts"],"sourcesContent":["import type { SignalWeights } from './signals.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 5;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; commands degrade when one is off.\n// links/sections/tags/rank are opt-out toggles in the top-level `features` block; embed is not a\n// member of that block -- it is on exactly when an `embed` block names a model.\nexport const FEATURE_NAMES = ['links', 'sections', 'tags', 'rank', 'embed'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `embed` names the model and gives the tree vectors at all; a preset's `signals` says which\n// engines that scope's searches use.\nexport interface EmbedConfig {\n model?: string;\n provider?: 'static' | 'openai' | 'cohere';\n url?: string; // openai provider: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // openai/cohere provider: name of the env var holding the bearer token, if any\n languages?: string[]; // owner-declared model languages; enables the language-fit check where no card exists\n chunkTokens?: number; // chunk size ceiling in estimated tokens for small-context models; default 500\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\n// A named, self-contained file-selection scope. include/exclude are globby patterns resolved\n// relative to the config file. No inheritance between presets -- every field a preset wants\n// it declares itself. Presets may overlap freely; they are views, not partitions.\nexport interface Preset {\n include: string[];\n exclude?: string[];\n k?: number; // result count for `search` scoped to this preset; default 10\n // Exhaustive when present: every signal this preset's searches compose, each mapped to\n // its RRF weight (a finite number > 0; presence is enablement). Absent means every signal\n // whose prerequisite holds, each at weight 1 -- words always, links when the feature is on,\n // vectors when the tree names a model. A layer searched for exact wording (ingested sources,\n // archives, generated output) declares {\"words\":1,\"links\":1} and costs no embedding at all.\n signals?: SignalWeights;\n where?: string; // standing SQL condition against frontmatter alias `f`\n}\n\n// A saved `search` invocation: `sense <name>` runs like\n// `sense search <search> [--preset] [--include] [--exclude] [--where] [--k]`.\nexport interface SavedSearch {\n search: string;\n preset?: string;\n include?: string[];\n exclude?: string[];\n where?: string;\n k?: number;\n}\n\n// An entry names the verb it runs, one to one with the commands: { sql } runs like\n// `sense sql`, { search } like `sense search`.\nexport type SavedQuery = { sql: string } | SavedSearch;\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n // File selection, index-time, and per-preset search defaults. A file is indexed iff any\n // preset's include/exclude covers it (union). `default` is used when a command names no preset.\n presets: Record<string, Preset>;\n // Global feature defaults; absent block or key means enabled. embed is not a member here --\n // see the `embed` block.\n features?: { links?: boolean; sections?: boolean; tags?: boolean; rank?: boolean };\n // Names the embedding model. Present means every indexed file gets vectors; absent means none do.\n embed?: EmbedConfig;\n // Settings for the `content` FTS5 table. `tokenize` decides which languages the tree can be\n // word-searched in at all; changing it rebuilds the index.\n content?: { tokenize?: string };\n // Backing store engine for this tree
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/types.ts"],"sourcesContent":["import type { SignalWeights } from './signals.ts';\n\nexport const CONFIG_FILENAME = 'sense.config.json';\nexport const STATE_DIR = '.sense';\n\n// Highest sense.config.json `version` this build understands. Older versions auto-migrate on load.\nexport const SUPPORTED_CONFIG_VERSION = 5;\n\n// Each feature owns its tables, parse-time extraction, and reconcile step; commands degrade when one is off.\n// links/sections/tags/rank are opt-out toggles in the top-level `features` block; embed is not a\n// member of that block -- it is on exactly when an `embed` block names a model.\nexport const FEATURE_NAMES = ['links', 'sections', 'tags', 'rank', 'embed'] as const;\nexport type FeatureName = (typeof FEATURE_NAMES)[number];\n\n// `embed` names the model and gives the tree vectors at all; a preset's `signals` says which\n// engines that scope's searches use.\nexport interface EmbedConfig {\n model?: string;\n provider?: 'static' | 'openai' | 'cohere';\n url?: string; // openai provider: OpenAI-compatible base URL, e.g. http://localhost:11434/v1\n key?: string; // openai/cohere provider: name of the env var holding the bearer token, if any\n languages?: string[]; // owner-declared model languages; enables the language-fit check where no card exists\n chunkTokens?: number; // chunk size ceiling in estimated tokens for small-context models; default 500\n}\n\nexport const DEFAULT_EMBED_MODEL = 'minishlab/potion-retrieval-32M';\n\n// A named, self-contained file-selection scope. include/exclude are globby patterns resolved\n// relative to the config file. No inheritance between presets -- every field a preset wants\n// it declares itself. Presets may overlap freely; they are views, not partitions.\nexport interface Preset {\n include: string[];\n exclude?: string[];\n k?: number; // result count for `search` scoped to this preset; default 10\n // Exhaustive when present: every signal this preset's searches compose, each mapped to\n // its RRF weight (a finite number > 0; presence is enablement). Absent means every signal\n // whose prerequisite holds, each at weight 1 -- words always, links when the feature is on,\n // vectors when the tree names a model. A layer searched for exact wording (ingested sources,\n // archives, generated output) declares {\"words\":1,\"links\":1} and costs no embedding at all.\n signals?: SignalWeights;\n where?: string; // standing SQL condition against frontmatter alias `f`\n}\n\n// A saved `search` invocation: `sense <name>` runs like\n// `sense search <search> [--preset] [--include] [--exclude] [--where] [--k]`.\nexport interface SavedSearch {\n search: string;\n preset?: string;\n include?: string[];\n exclude?: string[];\n where?: string;\n k?: number;\n}\n\n// An entry names the verb it runs, one to one with the commands: { sql } runs like\n// `sense sql`, { search } like `sense search`.\nexport type SavedQuery = { sql: string } | SavedSearch;\n\nexport interface Config {\n // Editor-only pointer to schema.json; never read by sense.\n $schema?: string;\n version?: number;\n // File selection, index-time, and per-preset search defaults. A file is indexed iff any\n // preset's include/exclude covers it (union). `default` is used when a command names no preset.\n presets: Record<string, Preset>;\n // Global feature defaults; absent block or key means enabled. embed is not a member here --\n // see the `embed` block.\n features?: { links?: boolean; sections?: boolean; tags?: boolean; rank?: boolean };\n // Names the embedding model. Present means every indexed file gets vectors; absent means none do.\n embed?: EmbedConfig;\n // Settings for the `content` FTS5 table. `tokenize` decides which languages the tree can be\n // word-searched in at all; changing it rebuilds the index.\n content?: { tokenize?: string };\n // Backing store engine for this tree: \"sqlite\" (default, zero-dependency) or \"duckdb\"\n // (an optional dependency, installed on first use). Both implement the same commands.\n store?: 'sqlite' | 'duckdb';\n queries: Record<string, SavedQuery>;\n}\n\nexport interface ResolvedConfig extends Config {\n baseDir: string;\n configPath: string | null;\n // Keys the file declares that this build does not read; cli reports them.\n unknownKeys?: string[];\n // Set when loadConfig auto-migrated the file on disk; cli reports it.\n migratedFrom?: number;\n}\n\nexport interface SearchOverrides {\n preset?: string;\n k?: number;\n where?: string;\n include?: string[];\n exclude?: string[];\n noExclude?: boolean; // drop the preset's exclude for this command; --exclude still applies\n}\n\nexport interface EffectiveSearch {\n presetName: string;\n k: number;\n where?: string;\n include: string[];\n exclude?: string[];\n signals: SignalWeights; // this preset's effective signal weights\n}\n"],"names":["CONFIG_FILENAME","STATE_DIR","SUPPORTED_CONFIG_VERSION","FEATURE_NAMES","DEFAULT_EMBED_MODEL"],"mappings":"AAEA,OAAO,MAAMA,kBAAkB,oBAAoB;AACnD,OAAO,MAAMC,YAAY,SAAS;AAElC,mGAAmG;AACnG,OAAO,MAAMC,2BAA2B,EAAE;AAE1C,6GAA6G;AAC7G,iGAAiG;AACjG,gFAAgF;AAChF,OAAO,MAAMC,gBAAgB;IAAC;IAAS;IAAY;IAAQ;IAAQ;CAAQ,CAAU;AAcrF,OAAO,MAAMC,sBAAsB,iCAAiC"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL
|
|
2
|
+
// it parses as an expression: `plugin - id`, or table `a` column `b`. SQLite names a fragment
|
|
3
|
+
// (`no such column: plugin`), DuckDB names the fragment or the table (`Referenced column/table
|
|
4
|
+
// "..." not found`); this maps either back to the real column so the error names the fix.
|
|
5
5
|
function startsWithBoundary(name, prefix) {
|
|
6
6
|
return name.length > prefix.length && name.startsWith(prefix) && /\W/.test(name[prefix.length]);
|
|
7
7
|
}
|
|
8
8
|
// columns: the tree's frontmatter columns (store.docs.columns()), read by the caller so this
|
|
9
9
|
// stays a pure function rather than a store dependency.
|
|
10
10
|
export function columnHint(columns, err) {
|
|
11
|
-
|
|
11
|
+
var _ref, _match_;
|
|
12
|
+
const match = /no such column: (\S+)|Referenced column "([^"]+)" not found in FROM clause|Referenced table "([^"]+)" not found/.exec(err.message);
|
|
12
13
|
if (!match) return err;
|
|
13
|
-
const token = match[1];
|
|
14
|
+
const token = (_ref = (_match_ = match[1]) !== null && _match_ !== void 0 ? _match_ : match[2]) !== null && _ref !== void 0 ? _ref : match[3];
|
|
14
15
|
// `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, which also
|
|
15
16
|
// covers a column literally named `a.b` -- the full-token check below matches that case
|
|
16
17
|
// directly, so stripping here never needs to special-case it.
|
|
@@ -29,5 +30,5 @@ export function columnHint(columns, err) {
|
|
|
29
30
|
const quoted = [
|
|
30
31
|
...candidates
|
|
31
32
|
].map((name)=>`"${name.split('"').join('""')}"`).join(', ');
|
|
32
|
-
return new Error(`${err.message} -- ${quoted} needs double quotes in SQL: unquoted,
|
|
33
|
+
return new Error(`${err.message} -- ${quoted} needs double quotes in SQL: unquoted, the engine parses the punctuation as an operator or a qualifier, not part of a column name.`);
|
|
33
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/output/column-hint.ts"],"sourcesContent":["// A frontmatter key with punctuation (`plugin-id`, `a.b`) is a real column, but unquoted in SQL\n// it parses as an expression: `plugin - id`, or table `a` column `b`. SQLite names a fragment\n// (`no such column: plugin`), DuckDB names the fragment or the table (`Referenced column/table\n// \"...\" not found`); this maps either back to the real column so the error names the fix.\n\nfunction startsWithBoundary(name: string, prefix: string): boolean {\n return name.length > prefix.length && name.startsWith(prefix) && /\\W/.test(name[prefix.length]);\n}\n\n// columns: the tree's frontmatter columns (store.docs.columns()), read by the caller so this\n// stays a pure function rather than a store dependency.\nexport function columnHint(columns: string[], err: Error): Error {\n const match = /no such column: (\\S+)|Referenced column \"([^\"]+)\" not found in FROM clause|Referenced table \"([^\"]+)\" not found/.exec(err.message);\n if (!match) return err;\n const token = match[1] ?? match[2] ?? match[3];\n // `f.plugin` -> `plugin`; left as-is when there's no leading alias segment, which also\n // covers a column literally named `a.b` -- the full-token check below matches that case\n // directly, so stripping here never needs to special-case it.\n const aliasStripped = token.replace(/^[A-Za-z_]\\w*\\./, '');\n const forms = new Set([token, aliasStripped]);\n const candidates = new Set<string>();\n for (const name of columns) {\n for (const form of forms) {\n if (name === form || startsWithBoundary(name, form)) candidates.add(name);\n }\n }\n if (candidates.size === 0) return err;\n const quoted = [...candidates].map((name) => `\"${name.split('\"').join('\"\"')}\"`).join(', ');\n return new Error(`${err.message} -- ${quoted} needs double quotes in SQL: unquoted, the engine parses the punctuation as an operator or a qualifier, not part of a column name.`);\n}\n"],"names":["startsWithBoundary","name","prefix","length","startsWith","test","columnHint","columns","err","match","exec","message","token","aliasStripped","replace","forms","Set","candidates","form","add","size","quoted","map","split","join","Error"],"mappings":"AAAA,gGAAgG;AAChG,8FAA8F;AAC9F,+FAA+F;AAC/F,0FAA0F;AAE1F,SAASA,mBAAmBC,IAAY,EAAEC,MAAc;IACtD,OAAOD,KAAKE,MAAM,GAAGD,OAAOC,MAAM,IAAIF,KAAKG,UAAU,CAACF,WAAW,KAAKG,IAAI,CAACJ,IAAI,CAACC,OAAOC,MAAM,CAAC;AAChG;AAEA,6FAA6F;AAC7F,wDAAwD;AACxD,OAAO,SAASG,WAAWC,OAAiB,EAAEC,GAAU;QAGxCC,MAAAA;IAFd,MAAMA,QAAQ,kHAAkHC,IAAI,CAACF,IAAIG,OAAO;IAChJ,IAAI,CAACF,OAAO,OAAOD;IACnB,MAAMI,SAAQH,QAAAA,UAAAA,KAAK,CAAC,EAAE,cAARA,qBAAAA,UAAYA,KAAK,CAAC,EAAE,cAApBA,kBAAAA,OAAwBA,KAAK,CAAC,EAAE;IAC9C,uFAAuF;IACvF,wFAAwF;IACxF,8DAA8D;IAC9D,MAAMI,gBAAgBD,MAAME,OAAO,CAAC,mBAAmB;IACvD,MAAMC,QAAQ,IAAIC,IAAI;QAACJ;QAAOC;KAAc;IAC5C,MAAMI,aAAa,IAAID;IACvB,KAAK,MAAMf,QAAQM,QAAS;QAC1B,KAAK,MAAMW,QAAQH,MAAO;YACxB,IAAId,SAASiB,QAAQlB,mBAAmBC,MAAMiB,OAAOD,WAAWE,GAAG,CAAClB;QACtE;IACF;IACA,IAAIgB,WAAWG,IAAI,KAAK,GAAG,OAAOZ;IAClC,MAAMa,SAAS;WAAIJ;KAAW,CAACK,GAAG,CAAC,CAACrB,OAAS,CAAC,CAAC,EAAEA,KAAKsB,KAAK,CAAC,KAAKC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAEA,IAAI,CAAC;IACrF,OAAO,IAAIC,MAAM,GAAGjB,IAAIG,OAAO,CAAC,IAAI,EAAEU,OAAO,kIAAkI,CAAC;AAClL"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ResolvedConfig } from '../config/index.js';
|
|
2
2
|
import type { OpenResult } from './sqlite/open.js';
|
|
3
3
|
export declare function openStore(cfg: ResolvedConfig): Promise<OpenResult>;
|
|
4
|
+
export declare function requireWatchConcurrency(cfg: ResolvedConfig): void;
|
|
4
5
|
export { getMeta, setMeta } from './meta.js';
|
|
5
6
|
export type { OpenResult } from './sqlite/open.js';
|
|
6
7
|
export { clearCache, DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.js';
|
package/dist/esm/store/index.js
CHANGED
|
@@ -18,10 +18,14 @@ const REGISTRY = {
|
|
|
18
18
|
open: openDuckdb
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
|
-
|
|
22
|
-
const name = storeName(cfg);
|
|
21
|
+
function entryFor(name) {
|
|
23
22
|
const entry = REGISTRY[name];
|
|
24
23
|
if (!entry) throw new SenseError('STORE_UNKNOWN', `unknown backing store "${name}"; available: ${Object.keys(REGISTRY).join(', ')}`);
|
|
24
|
+
return entry;
|
|
25
|
+
}
|
|
26
|
+
export async function openStore(cfg) {
|
|
27
|
+
const name = storeName(cfg);
|
|
28
|
+
const entry = entryFor(name);
|
|
25
29
|
// The one unambiguous, config-level capability need: an embed block some preset actually
|
|
26
30
|
// uses for vectors. Word/lexical search has no equivalent unambiguous signal at this level
|
|
27
31
|
// (every preset defaults to wanting "words"), so a store lacking 'lexical' fails loudly at
|
|
@@ -31,5 +35,13 @@ export async function openStore(cfg) {
|
|
|
31
35
|
}
|
|
32
36
|
return entry.open(cfg);
|
|
33
37
|
}
|
|
38
|
+
// watch keeps its connection open for the run, so a file-locking store would fail every
|
|
39
|
+
// other command on the tree; check before open, as openStore does for "vectors".
|
|
40
|
+
export function requireWatchConcurrency(cfg) {
|
|
41
|
+
const name = storeName(cfg);
|
|
42
|
+
if (!entryFor(name).capabilities.has('watch-concurrency')) {
|
|
43
|
+
throw new SenseError('STORE_CAPABILITY_MISSING', `store "${name}" does not implement "watch-concurrency" in this build; sense watch holds the store open for its whole run, and this store locks the cache file, so every other command on this tree would fail to open; set "store" to "sqlite" in sense.config.json`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
34
46
|
export { getMeta, setMeta } from './meta.js';
|
|
35
47
|
export { clearCache, DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/index.ts"],"sourcesContent":["// Rows -> a backing store: registry + openStore(cfg). Parsing lives in scan.ts; everything\n// beyond frontmatter + content lives in src/features/.\n\nimport type { ResolvedConfig } from '../config/index.ts';\nimport { anyPresetEmbeds, storeName } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { openDuckdb } from './duckdb/open.ts';\nimport { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.ts';\nimport type { OpenResult } from './sqlite/open.ts';\nimport { openSqlite } from './sqlite/open.ts';\nimport { CAPABILITIES as SQLITE_CAPABILITIES } from './sqlite/store.ts';\nimport type { Capability } from './types.ts';\n\ntype StoreName = 'sqlite' | 'duckdb';\n\ninterface StoreEntry {\n capabilities: ReadonlySet<Capability>;\n open: (cfg: ResolvedConfig) => Promise<OpenResult>;\n}\n\n// Capabilities are checked against the registry entry before opening (no wasted connect/reconcile\n// work for a config that cannot be satisfied), then the store itself opens.\nconst REGISTRY: Record<StoreName, StoreEntry> = {\n sqlite: { capabilities: SQLITE_CAPABILITIES, open: openSqlite },\n duckdb: { capabilities: DUCKDB_CAPABILITIES, open: openDuckdb },\n};\n\
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/index.ts"],"sourcesContent":["// Rows -> a backing store: registry + openStore(cfg). Parsing lives in scan.ts; everything\n// beyond frontmatter + content lives in src/features/.\n\nimport type { ResolvedConfig } from '../config/index.ts';\nimport { anyPresetEmbeds, storeName } from '../config/index.ts';\nimport { SenseError } from '../errors.ts';\nimport { openDuckdb } from './duckdb/open.ts';\nimport { CAPABILITIES as DUCKDB_CAPABILITIES } from './duckdb/store.ts';\nimport type { OpenResult } from './sqlite/open.ts';\nimport { openSqlite } from './sqlite/open.ts';\nimport { CAPABILITIES as SQLITE_CAPABILITIES } from './sqlite/store.ts';\nimport type { Capability } from './types.ts';\n\ntype StoreName = 'sqlite' | 'duckdb';\n\ninterface StoreEntry {\n capabilities: ReadonlySet<Capability>;\n open: (cfg: ResolvedConfig) => Promise<OpenResult>;\n}\n\n// Capabilities are checked against the registry entry before opening (no wasted connect/reconcile\n// work for a config that cannot be satisfied), then the store itself opens.\nconst REGISTRY: Record<StoreName, StoreEntry> = {\n sqlite: { capabilities: SQLITE_CAPABILITIES, open: openSqlite },\n duckdb: { capabilities: DUCKDB_CAPABILITIES, open: openDuckdb },\n};\n\nfunction entryFor(name: StoreName): StoreEntry {\n const entry = REGISTRY[name];\n if (!entry) throw new SenseError('STORE_UNKNOWN', `unknown backing store \"${name}\"; available: ${Object.keys(REGISTRY).join(', ')}`);\n return entry;\n}\n\nexport async function openStore(cfg: ResolvedConfig): Promise<OpenResult> {\n const name = storeName(cfg);\n const entry = entryFor(name);\n\n // The one unambiguous, config-level capability need: an embed block some preset actually\n // uses for vectors. Word/lexical search has no equivalent unambiguous signal at this level\n // (every preset defaults to wanting \"words\"), so a store lacking 'lexical' fails loudly at\n // first use instead (see duckdb/store.ts's lexical.query()).\n if (anyPresetEmbeds(cfg) && !entry.capabilities.has('vectors')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"vectors\" in this build, but this config's \"embed\" block is in use by at least one preset; remove or narrow it, or set \"store\" to a store that supports vectors (sqlite)`);\n }\n\n return entry.open(cfg);\n}\n\n// watch keeps its connection open for the run, so a file-locking store would fail every\n// other command on the tree; check before open, as openStore does for \"vectors\".\nexport function requireWatchConcurrency(cfg: ResolvedConfig): void {\n const name = storeName(cfg);\n if (!entryFor(name).capabilities.has('watch-concurrency')) {\n throw new SenseError('STORE_CAPABILITY_MISSING', `store \"${name}\" does not implement \"watch-concurrency\" in this build; sense watch holds the store open for its whole run, and this store locks the cache file, so every other command on this tree would fail to open; set \"store\" to \"sqlite\" in sense.config.json`);\n }\n}\n\nexport { getMeta, setMeta } from './meta.ts';\nexport type { OpenResult } from './sqlite/open.ts';\nexport { clearCache, DB_FILENAME, docCount, SCHEMA_VERSION } from './sqlite/open.ts';\nexport type { Capability, Connection, DocumentStore, LexicalHit, LexicalIndex, LexicalQueryOptions, RawStatement, RunResult, SqlSession, Statement, Store, VectorCandidate, VectorSimilar, VectorStore, VectorWriteRow } from './types.ts';\n"],"names":["anyPresetEmbeds","storeName","SenseError","openDuckdb","CAPABILITIES","DUCKDB_CAPABILITIES","openSqlite","SQLITE_CAPABILITIES","REGISTRY","sqlite","capabilities","open","duckdb","entryFor","name","entry","Object","keys","join","openStore","cfg","has","requireWatchConcurrency","getMeta","setMeta","clearCache","DB_FILENAME","docCount","SCHEMA_VERSION"],"mappings":"AAAA,2FAA2F;AAC3F,uDAAuD;AAGvD,SAASA,eAAe,EAAEC,SAAS,QAAQ,qBAAqB;AAChE,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,UAAU,QAAQ,mBAAmB;AAC9C,SAASC,gBAAgBC,mBAAmB,QAAQ,oBAAoB;AAExE,SAASC,UAAU,QAAQ,mBAAmB;AAC9C,SAASF,gBAAgBG,mBAAmB,QAAQ,oBAAoB;AAUxE,kGAAkG;AAClG,4EAA4E;AAC5E,MAAMC,WAA0C;IAC9CC,QAAQ;QAAEC,cAAcH;QAAqBI,MAAML;IAAW;IAC9DM,QAAQ;QAAEF,cAAcL;QAAqBM,MAAMR;IAAW;AAChE;AAEA,SAASU,SAASC,IAAe;IAC/B,MAAMC,QAAQP,QAAQ,CAACM,KAAK;IAC5B,IAAI,CAACC,OAAO,MAAM,IAAIb,WAAW,iBAAiB,CAAC,uBAAuB,EAAEY,KAAK,cAAc,EAAEE,OAAOC,IAAI,CAACT,UAAUU,IAAI,CAAC,OAAO;IACnI,OAAOH;AACT;AAEA,OAAO,eAAeI,UAAUC,GAAmB;IACjD,MAAMN,OAAOb,UAAUmB;IACvB,MAAML,QAAQF,SAASC;IAEvB,yFAAyF;IACzF,2FAA2F;IAC3F,2FAA2F;IAC3F,6DAA6D;IAC7D,IAAId,gBAAgBoB,QAAQ,CAACL,MAAML,YAAY,CAACW,GAAG,CAAC,YAAY;QAC9D,MAAM,IAAInB,WAAW,4BAA4B,CAAC,OAAO,EAAEY,KAAK,6LAA6L,CAAC;IAChQ;IAEA,OAAOC,MAAMJ,IAAI,CAACS;AACpB;AAEA,wFAAwF;AACxF,iFAAiF;AACjF,OAAO,SAASE,wBAAwBF,GAAmB;IACzD,MAAMN,OAAOb,UAAUmB;IACvB,IAAI,CAACP,SAASC,MAAMJ,YAAY,CAACW,GAAG,CAAC,sBAAsB;QACzD,MAAM,IAAInB,WAAW,4BAA4B,CAAC,OAAO,EAAEY,KAAK,qPAAqP,CAAC;IACxT;AACF;AAEA,SAASS,OAAO,EAAEC,OAAO,QAAQ,YAAY;AAE7C,SAASC,UAAU,EAAEC,WAAW,EAAEC,QAAQ,EAAEC,cAAc,QAAQ,mBAAmB"}
|
package/dist/esm/watch.js
CHANGED
|
@@ -2,7 +2,7 @@ import { watch as fsWatch } from 'node:fs';
|
|
|
2
2
|
import { STATE_DIR } from './config/index.js';
|
|
3
3
|
import { SenseError } from './errors.js';
|
|
4
4
|
import { guardedTick } from './lib/guarded-tick.js';
|
|
5
|
-
import { docCount, getMeta, openStore, setMeta } from './store/index.js';
|
|
5
|
+
import { docCount, getMeta, openStore, requireWatchConcurrency, setMeta } from './store/index.js';
|
|
6
6
|
// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.
|
|
7
7
|
const DEBOUNCE_MS = 200;
|
|
8
8
|
const HEARTBEAT_INTERVAL_MS = 5000;
|
|
@@ -10,6 +10,7 @@ const STALE_HEARTBEAT_MS = 15000;
|
|
|
10
10
|
// Runs in the foreground until SIGINT/SIGTERM/signal abort. Throws WATCH_ACTIVE if another watcher's heartbeat is still fresh and --force wasn't given.
|
|
11
11
|
export async function runWatch(cfg, opts = {}) {
|
|
12
12
|
var _opts_onEvent, _opts_debounceMs, _opts_heartbeatIntervalMs;
|
|
13
|
+
requireWatchConcurrency(cfg);
|
|
13
14
|
const onEvent = (_opts_onEvent = opts.onEvent) !== null && _opts_onEvent !== void 0 ? _opts_onEvent : ()=>{};
|
|
14
15
|
const debounceMs = (_opts_debounceMs = opts.debounceMs) !== null && _opts_debounceMs !== void 0 ? _opts_debounceMs : DEBOUNCE_MS;
|
|
15
16
|
const heartbeatIntervalMs = (_opts_heartbeatIntervalMs = opts.heartbeatIntervalMs) !== null && _opts_heartbeatIntervalMs !== void 0 ? _opts_heartbeatIntervalMs : HEARTBEAT_INTERVAL_MS;
|
package/dist/esm/watch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/watch.ts"],"sourcesContent":["import { watch as fsWatch } from 'node:fs';\nimport type { ResolvedConfig } from './config/index.ts';\nimport { STATE_DIR } from './config/index.ts';\nimport { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { docCount, getMeta, openStore, setMeta } from './store/index.ts';\n\n// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.\nconst DEBOUNCE_MS = 200;\nconst HEARTBEAT_INTERVAL_MS = 5000;\nconst STALE_HEARTBEAT_MS = 15000;\n\nexport type WatchEvent = { type: 'started'; baseDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n // Aborting runs the same shutdown path as SIGINT/SIGTERM.\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\n// Runs in the foreground until SIGINT/SIGTERM/signal abort. Throws WATCH_ACTIVE if another watcher's heartbeat is still fresh and --force wasn't given.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const heartbeatIntervalMs = opts.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;\n const { store, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg);\n const baseDir = cfg.baseDir;\n\n const existingHeartbeat = await getMeta(store, 'watch_heartbeat');\n if (existingHeartbeat && !opts.force) {\n const age = Date.now() - Date.parse(existingHeartbeat);\n if (age >= 0 && age < STALE_HEARTBEAT_MS) {\n await store.close();\n throw new SenseError('WATCH_ACTIVE', `another watcher appears active (heartbeat ${Math.round(age / 1000)}s ago); use --force to override`);\n }\n }\n\n onEvent({ type: 'started', baseDir, dbPath });\n if (initialWarnings.length > 0 || initialParsed > 0) {\n onEvent({ type: 'reconciled', parsed: initialParsed, total: await docCount(store), warnings: initialWarnings });\n }\n\n let stopping = false;\n const touchHeartbeat = async () => {\n await setMeta(store, 'watch_heartbeat', new Date().toISOString());\n await setMeta(store, 'watch_pid', String(process.pid));\n };\n await touchHeartbeat();\n\n let debounceTimer: NodeJS.Timeout | null = null;\n const scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(async () => {\n debounceTimer = null;\n try {\n const { parsed, warnings } = await store.reconcile();\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n }\n }, debounceMs);\n };\n\n // Ignore our own state dir, or the heartbeat write would retrigger itself forever.\n const watcher = fsWatch(baseDir, { recursive: true }, (_event, filename) => {\n if (typeof filename === 'string' && filename.startsWith(STATE_DIR)) return;\n scheduleReconcile();\n });\n const heartbeatTimer = setInterval(\n guardedTick(touchHeartbeat, () => stopping),\n heartbeatIntervalMs\n );\n\n return new Promise<void>((resolveShutdown) => {\n // SIGINT/SIGTERM and an aborted signal all run this same path exactly once; each is\n // unregistered here too so a second runWatch call in the same process starts clean.\n const shutdown = async () => {\n if (stopping) return;\n stopping = true;\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n opts.signal?.removeEventListener('abort', shutdown);\n clearInterval(heartbeatTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n await setMeta(store, 'watch_heartbeat', null);\n await setMeta(store, 'watch_pid', null);\n await store.close();\n resolveShutdown();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n if (opts.signal?.aborted) shutdown();\n else opts.signal?.addEventListener('abort', shutdown, { once: true });\n });\n}\n"],"names":["watch","fsWatch","STATE_DIR","SenseError","guardedTick","docCount","getMeta","openStore","setMeta","DEBOUNCE_MS","HEARTBEAT_INTERVAL_MS","STALE_HEARTBEAT_MS","runWatch","cfg","opts","onEvent","debounceMs","heartbeatIntervalMs","store","dbPath","warnings","initialWarnings","parsed","initialParsed","baseDir","existingHeartbeat","force","age","Date","now","parse","close","Math","round","type","length","total","stopping","touchHeartbeat","toISOString","String","process","pid","debounceTimer","scheduleReconcile","clearTimeout","setTimeout","reconcile","err","message","watcher","recursive","_event","filename","startsWith","heartbeatTimer","setInterval","Promise","resolveShutdown","shutdown","off","signal","removeEventListener","clearInterval","once","aborted","addEventListener"],"mappings":"AAAA,SAASA,SAASC,OAAO,QAAQ,UAAU;AAE3C,SAASC,SAAS,QAAQ,oBAAoB;AAC9C,SAASC,UAAU,QAAQ,cAAc;AACzC,SAASC,WAAW,QAAQ,wBAAwB;AACpD,SAASC,QAAQ,EAAEC,OAAO,EAAEC,SAAS,EAAEC,OAAO,QAAQ,mBAAmB;
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/watch.ts"],"sourcesContent":["import { watch as fsWatch } from 'node:fs';\nimport type { ResolvedConfig } from './config/index.ts';\nimport { STATE_DIR } from './config/index.ts';\nimport { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { docCount, getMeta, openStore, requireWatchConcurrency, setMeta } from './store/index.ts';\n\n// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.\nconst DEBOUNCE_MS = 200;\nconst HEARTBEAT_INTERVAL_MS = 5000;\nconst STALE_HEARTBEAT_MS = 15000;\n\nexport type WatchEvent = { type: 'started'; baseDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n // Aborting runs the same shutdown path as SIGINT/SIGTERM.\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\n// Runs in the foreground until SIGINT/SIGTERM/signal abort. Throws WATCH_ACTIVE if another watcher's heartbeat is still fresh and --force wasn't given.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n requireWatchConcurrency(cfg);\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const heartbeatIntervalMs = opts.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;\n const { store, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg);\n const baseDir = cfg.baseDir;\n\n const existingHeartbeat = await getMeta(store, 'watch_heartbeat');\n if (existingHeartbeat && !opts.force) {\n const age = Date.now() - Date.parse(existingHeartbeat);\n if (age >= 0 && age < STALE_HEARTBEAT_MS) {\n await store.close();\n throw new SenseError('WATCH_ACTIVE', `another watcher appears active (heartbeat ${Math.round(age / 1000)}s ago); use --force to override`);\n }\n }\n\n onEvent({ type: 'started', baseDir, dbPath });\n if (initialWarnings.length > 0 || initialParsed > 0) {\n onEvent({ type: 'reconciled', parsed: initialParsed, total: await docCount(store), warnings: initialWarnings });\n }\n\n let stopping = false;\n const touchHeartbeat = async () => {\n await setMeta(store, 'watch_heartbeat', new Date().toISOString());\n await setMeta(store, 'watch_pid', String(process.pid));\n };\n await touchHeartbeat();\n\n let debounceTimer: NodeJS.Timeout | null = null;\n const scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(async () => {\n debounceTimer = null;\n try {\n const { parsed, warnings } = await store.reconcile();\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n }\n }, debounceMs);\n };\n\n // Ignore our own state dir, or the heartbeat write would retrigger itself forever.\n const watcher = fsWatch(baseDir, { recursive: true }, (_event, filename) => {\n if (typeof filename === 'string' && filename.startsWith(STATE_DIR)) return;\n scheduleReconcile();\n });\n const heartbeatTimer = setInterval(\n guardedTick(touchHeartbeat, () => stopping),\n heartbeatIntervalMs\n );\n\n return new Promise<void>((resolveShutdown) => {\n // SIGINT/SIGTERM and an aborted signal all run this same path exactly once; each is\n // unregistered here too so a second runWatch call in the same process starts clean.\n const shutdown = async () => {\n if (stopping) return;\n stopping = true;\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n opts.signal?.removeEventListener('abort', shutdown);\n clearInterval(heartbeatTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n await setMeta(store, 'watch_heartbeat', null);\n await setMeta(store, 'watch_pid', null);\n await store.close();\n resolveShutdown();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n if (opts.signal?.aborted) shutdown();\n else opts.signal?.addEventListener('abort', shutdown, { once: true });\n });\n}\n"],"names":["watch","fsWatch","STATE_DIR","SenseError","guardedTick","docCount","getMeta","openStore","requireWatchConcurrency","setMeta","DEBOUNCE_MS","HEARTBEAT_INTERVAL_MS","STALE_HEARTBEAT_MS","runWatch","cfg","opts","onEvent","debounceMs","heartbeatIntervalMs","store","dbPath","warnings","initialWarnings","parsed","initialParsed","baseDir","existingHeartbeat","force","age","Date","now","parse","close","Math","round","type","length","total","stopping","touchHeartbeat","toISOString","String","process","pid","debounceTimer","scheduleReconcile","clearTimeout","setTimeout","reconcile","err","message","watcher","recursive","_event","filename","startsWith","heartbeatTimer","setInterval","Promise","resolveShutdown","shutdown","off","signal","removeEventListener","clearInterval","once","aborted","addEventListener"],"mappings":"AAAA,SAASA,SAASC,OAAO,QAAQ,UAAU;AAE3C,SAASC,SAAS,QAAQ,oBAAoB;AAC9C,SAASC,UAAU,QAAQ,cAAc;AACzC,SAASC,WAAW,QAAQ,wBAAwB;AACpD,SAASC,QAAQ,EAAEC,OAAO,EAAEC,SAAS,EAAEC,uBAAuB,EAAEC,OAAO,QAAQ,mBAAmB;AAElG,uJAAuJ;AACvJ,MAAMC,cAAc;AACpB,MAAMC,wBAAwB;AAC9B,MAAMC,qBAAqB;AAa3B,wJAAwJ;AACxJ,OAAO,eAAeC,SAASC,GAAmB,EAAEC,OAAqB,CAAC,CAAC;QAEzDA,eACGA,kBACSA;IAH5BP,wBAAwBM;IACxB,MAAME,WAAUD,gBAAAA,KAAKC,OAAO,cAAZD,2BAAAA,gBAAiB,KAAO;IACxC,MAAME,cAAaF,mBAAAA,KAAKE,UAAU,cAAfF,8BAAAA,mBAAmBL;IACtC,MAAMQ,uBAAsBH,4BAAAA,KAAKG,mBAAmB,cAAxBH,uCAAAA,4BAA4BJ;IACxD,MAAM,EAAEQ,KAAK,EAAEC,MAAM,EAAEC,UAAUC,eAAe,EAAEC,QAAQC,aAAa,EAAE,GAAG,MAAMjB,UAAUO;IAC5F,MAAMW,UAAUX,IAAIW,OAAO;IAE3B,MAAMC,oBAAoB,MAAMpB,QAAQa,OAAO;IAC/C,IAAIO,qBAAqB,CAACX,KAAKY,KAAK,EAAE;QACpC,MAAMC,MAAMC,KAAKC,GAAG,KAAKD,KAAKE,KAAK,CAACL;QACpC,IAAIE,OAAO,KAAKA,MAAMhB,oBAAoB;YACxC,MAAMO,MAAMa,KAAK;YACjB,MAAM,IAAI7B,WAAW,gBAAgB,CAAC,0CAA0C,EAAE8B,KAAKC,KAAK,CAACN,MAAM,MAAM,+BAA+B,CAAC;QAC3I;IACF;IAEAZ,QAAQ;QAAEmB,MAAM;QAAWV;QAASL;IAAO;IAC3C,IAAIE,gBAAgBc,MAAM,GAAG,KAAKZ,gBAAgB,GAAG;QACnDR,QAAQ;YAAEmB,MAAM;YAAcZ,QAAQC;YAAea,OAAO,MAAMhC,SAASc;YAAQE,UAAUC;QAAgB;IAC/G;IAEA,IAAIgB,WAAW;IACf,MAAMC,iBAAiB;QACrB,MAAM9B,QAAQU,OAAO,mBAAmB,IAAIU,OAAOW,WAAW;QAC9D,MAAM/B,QAAQU,OAAO,aAAasB,OAAOC,QAAQC,GAAG;IACtD;IACA,MAAMJ;IAEN,IAAIK,gBAAuC;IAC3C,MAAMC,oBAAoB;QACxB,IAAID,eAAeE,aAAaF;QAChCA,gBAAgBG,WAAW;YACzBH,gBAAgB;YAChB,IAAI;gBACF,MAAM,EAAErB,MAAM,EAAEF,QAAQ,EAAE,GAAG,MAAMF,MAAM6B,SAAS;gBAClDhC,QAAQ;oBAAEmB,MAAM;oBAAcZ;oBAAQc,OAAO,MAAMhC,SAASc;oBAAQE;gBAAS;YAC/E,EAAE,OAAO4B,KAAK;gBACZjC,QAAQ;oBAAEmB,MAAM;oBAAmBe,SAAS,AAACD,IAAcC,OAAO;gBAAC;YACrE;QACF,GAAGjC;IACL;IAEA,mFAAmF;IACnF,MAAMkC,UAAUlD,QAAQwB,SAAS;QAAE2B,WAAW;IAAK,GAAG,CAACC,QAAQC;QAC7D,IAAI,OAAOA,aAAa,YAAYA,SAASC,UAAU,CAACrD,YAAY;QACpE2C;IACF;IACA,MAAMW,iBAAiBC,YACrBrD,YAAYmC,gBAAgB,IAAMD,WAClCpB;IAGF,OAAO,IAAIwC,QAAc,CAACC;YAmBpB5C,cACCA;QAnBL,oFAAoF;QACpF,oFAAoF;QACpF,MAAM6C,WAAW;gBAKf7C;YAJA,IAAIuB,UAAU;YACdA,WAAW;YACXI,QAAQmB,GAAG,CAAC,UAAUD;YACtBlB,QAAQmB,GAAG,CAAC,WAAWD;aACvB7C,eAAAA,KAAK+C,MAAM,cAAX/C,mCAAAA,aAAagD,mBAAmB,CAAC,SAASH;YAC1CI,cAAcR;YACd,IAAIZ,eAAeE,aAAaF;YAChCO,QAAQnB,KAAK;YACb,MAAMvB,QAAQU,OAAO,mBAAmB;YACxC,MAAMV,QAAQU,OAAO,aAAa;YAClC,MAAMA,MAAMa,KAAK;YACjB2B;QACF;QACAjB,QAAQuB,IAAI,CAAC,UAAUL;QACvBlB,QAAQuB,IAAI,CAAC,WAAWL;QACxB,KAAI7C,eAAAA,KAAK+C,MAAM,cAAX/C,mCAAAA,aAAamD,OAAO,EAAEN;cACrB7C,gBAAAA,KAAK+C,MAAM,cAAX/C,oCAAAA,cAAaoD,gBAAgB,CAAC,SAASP,UAAU;YAAEK,MAAM;QAAK;IACrE;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sensemaking",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Query and search your markdown notes with context-aware progressive disclosure: SQL over frontmatter, links, and text, plus semantic search and link-graph ranking. No server, no build step",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -10,7 +10,7 @@ Querying an existing tree is the `sense` skill. This one covers making a tree: i
|
|
|
10
10
|
## Setup
|
|
11
11
|
|
|
12
12
|
- `npm install -g sensemaking`, then `sense init` at the tree root writes `sense.config.json`: two presets (`default`, and `large` showing what a big tree tunes) and an `embed` block naming the model. The model fetches once per machine at the first vector search (progress on stderr); `sense download` prefetches it instead where that timing matters (CI, air-gapped setup). Config discovery walks up from cwd; `--config <path>` overrides.
|
|
13
|
-
- **Backing store.** The config's `store` key: `sqlite` (default, zero-dependency, Node's built-in SQLite) or `duckdb` (experimental; the first command that opens a duckdb tree installs `@duckdb/node-api` on its own, a one-time native download of about 110 MB). The same commands, tables, and `has`/`basename`/`segment` functions run on both. The difference is FTS5: under `duckdb`, `search` text and raw `MATCH` reject FTS5's prefix, boolean, `NEAR`, initial-token, and column-filter operators with a named error, and sqlite's FTS5 SQL (`MATCH`, `snippet()`, `bm25()`) does not run, so saved queries written in that syntax are sqlite dialect. A tree whose saved queries or search vocabulary depend on FTS5 operators is a tree that stays on `sqlite`. Each store keeps its own cache file (`.sense/cache.db`, `.sense/cache.duckdb`); switching stores is a rebuild, not a migration.
|
|
13
|
+
- **Backing store.** The config's `store` key: `sqlite` (default, zero-dependency, Node's built-in SQLite) or `duckdb` (experimental; the first command that opens a duckdb tree installs `@duckdb/node-api` on its own, a one-time native download of about 110 MB). The same commands, tables, and `has`/`basename`/`segment` functions run on both. The difference is FTS5: under `duckdb`, `search` text and raw `MATCH` reject FTS5's prefix, boolean, `NEAR`, initial-token, and column-filter operators with a named error, and sqlite's FTS5 SQL (`MATCH`, `snippet()`, `bm25()`) does not run, so saved queries written in that syntax are sqlite dialect. A tree whose saved queries or search vocabulary depend on FTS5 operators is a tree that stays on `sqlite`. `sense watch` is the other store-bound command: under `duckdb` it is a named error, because the store locks its cache file for the watcher's connection, which would block every other command on the tree for the watch's lifetime. Each store keeps its own cache file (`.sense/cache.db`, `.sense/cache.duckdb`); switching stores is a rebuild, not a migration.
|
|
14
14
|
- Globs resolve relative to the config file, never the cwd.
|
|
15
15
|
- `sense status` and `sense map` show each preset's coverage (files matched, embedded count), so what a config actually indexes is always visible in output. A config edit that changes coverage rebuilds the cache and names the preset that caused it on stderr.
|
|
16
16
|
|