@jagilber-org/index-server 1.27.0 → 1.27.2

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/dashboard/client/admin.html +57 -27
  3. package/dist/dashboard/client/css/admin.css +54 -0
  4. package/dist/dashboard/client/js/admin.config.js +3 -6
  5. package/dist/dashboard/client/js/admin.embeddings.js +63 -4
  6. package/dist/dashboard/client/js/admin.events.js +256 -0
  7. package/dist/dashboard/client/js/admin.maintenance.js +75 -32
  8. package/dist/dashboard/client/js/admin.sessions.js +1 -1
  9. package/dist/dashboard/server/AdminPanel.js +83 -6
  10. package/dist/dashboard/server/AdminPanelConfig.d.ts +11 -0
  11. package/dist/dashboard/server/AdminPanelConfig.js +47 -17
  12. package/dist/dashboard/server/DashboardServer.js +13 -0
  13. package/dist/dashboard/server/routes/admin.routes.js +143 -17
  14. package/dist/dashboard/server/routes/embeddings.routes.js +91 -1
  15. package/dist/server/sdkServer.js +12 -4
  16. package/dist/services/embeddingService.d.ts +2 -0
  17. package/dist/services/embeddingService.js +16 -4
  18. package/dist/services/embeddingTrigger.d.ts +33 -0
  19. package/dist/services/embeddingTrigger.js +86 -0
  20. package/dist/services/eventBuffer.d.ts +45 -0
  21. package/dist/services/eventBuffer.js +110 -0
  22. package/dist/services/handlers/instructions.import.js +71 -13
  23. package/dist/services/handlers.dashboardConfig.js +81 -0
  24. package/dist/services/indexContext.d.ts +18 -0
  25. package/dist/services/indexContext.js +133 -24
  26. package/dist/services/logger.js +9 -0
  27. package/dist/services/manifestManager.js +11 -1
  28. package/dist/services/seedBootstrap.js +5 -1
  29. package/dist/services/storage/factory.d.ts +2 -0
  30. package/dist/services/storage/factory.js +12 -1
  31. package/dist/services/tracing.js +3 -1
  32. package/package.json +12 -2
  33. package/server.json +3 -3
@@ -3,6 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports._internal = void 0;
7
+ exports._resetIndexContextProcessLatches = _resetIndexContextProcessLatches;
8
+ exports._resetIndexContextStateForTests = _resetIndexContextStateForTests;
6
9
  exports.getInvariantRepairSummary = getInvariantRepairSummary;
7
10
  exports.clearUsageRateLimit = clearUsageRateLimit;
8
11
  exports.loadUsageSnapshot = loadUsageSnapshot;
@@ -95,6 +98,33 @@ function trackInvariantRepair(id, field, source) {
95
98
  if (invariantRepairLog.length > MAX_REPAIR_LOG)
96
99
  invariantRepairLog.shift();
97
100
  }
101
+ // ── Process-scoped latches for noise + work suppression ──────────
102
+ // Symptom that motivated these latches (observed live on dev port 8687,
103
+ // 2026-05-01): every dashboard request triggered ensureLoaded() →
104
+ // migrateJsonToSqlite() because jsonFiles.length > sqliteRowCount was
105
+ // permanently true (a few JSON files failed loader validation), and every
106
+ // /api/admin/stats request emitted hundreds of stack-traced WARN entries
107
+ // from restoreFirstSeenInvariant for entries whose firstSeenTs was
108
+ // genuinely unrecoverable. Both of those are infinite-cost loops once the
109
+ // process is up. We dedupe both per-process here.
110
+ const autoMigrationAttempted = new Set();
111
+ const firstSeenExhaustedReported = new Set();
112
+ /**
113
+ * Test-only hook — reset process-scoped latches between vitest specs.
114
+ * @internal Not part of the public API.
115
+ */
116
+ function _resetIndexContextProcessLatches() {
117
+ autoMigrationAttempted.clear();
118
+ firstSeenExhaustedReported.clear();
119
+ }
120
+ /**
121
+ * Test-only hook — reset the module-scoped index state cache.
122
+ * @internal Not part of the public API.
123
+ */
124
+ function _resetIndexContextStateForTests() {
125
+ state = null;
126
+ dirty = false;
127
+ }
98
128
  /** Returns a summary of invariant repairs for health check visibility. */
99
129
  function getInvariantRepairSummary() {
100
130
  return { totalRepairs: invariantRepairLog.length, recentRepairs: invariantRepairLog.slice(-20) };
@@ -110,7 +140,7 @@ function restoreFirstSeenInvariant(e) {
110
140
  e.firstSeenTs = auth;
111
141
  (0, features_1.incrementCounter)('usage:firstSeenAuthorityRepair');
112
142
  trackInvariantRepair(e.id, 'firstSeenTs', 'authority');
113
- (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs restored from authority for ${e.id}`);
143
+ (0, logger_js_1.logDebug)(`[invariant-repair] firstSeenTs restored from authority for ${e.id}`);
114
144
  return;
115
145
  }
116
146
  const ep = ephemeralFirstSeen[e.id];
@@ -118,7 +148,7 @@ function restoreFirstSeenInvariant(e) {
118
148
  e.firstSeenTs = ep;
119
149
  (0, features_1.incrementCounter)('usage:firstSeenInvariantRepair');
120
150
  trackInvariantRepair(e.id, 'firstSeenTs', 'ephemeral');
121
- (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs restored from ephemeral cache for ${e.id}`);
151
+ (0, logger_js_1.logDebug)(`[invariant-repair] firstSeenTs restored from ephemeral cache for ${e.id}`);
122
152
  return;
123
153
  }
124
154
  const snap = lastGoodUsageSnapshot[e.id];
@@ -126,15 +156,40 @@ function restoreFirstSeenInvariant(e) {
126
156
  e.firstSeenTs = snap.firstSeenTs;
127
157
  (0, features_1.incrementCounter)('usage:firstSeenInvariantRepair');
128
158
  trackInvariantRepair(e.id, 'firstSeenTs', 'snapshot');
129
- (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs restored from snapshot for ${e.id}`);
159
+ (0, logger_js_1.logDebug)(`[invariant-repair] firstSeenTs restored from snapshot for ${e.id}`);
160
+ return;
161
+ }
162
+ // Final fallback: createdAt. By definition firstSeenTs ≤ createdAt is impossible
163
+ // (the index can never have observed an entry before it was created). For
164
+ // freshly-imported / freshly-added entries with no usage history yet, this is
165
+ // the correct answer; for legacy on-disk entries written before write-path
166
+ // populated firstSeenTs, it heals them silently. Repaired silently (no WARN)
167
+ // because this is the documented authoritative semantic, not a defect.
168
+ if (e.createdAt) {
169
+ e.firstSeenTs = e.createdAt;
170
+ firstSeenAuthority[e.id] = e.createdAt;
171
+ (0, features_1.incrementCounter)('usage:firstSeenCreatedAtFallback');
172
+ trackInvariantRepair(e.id, 'firstSeenTs', 'createdAt');
173
+ return;
130
174
  }
131
- // If still missing after all repair sources, track an exhausted repair attempt (extremely rare diagnostic)
175
+ // If still missing after all repair sources, track an exhausted repair attempt (extremely rare diagnostic).
176
+ // Dedup the WARN per-id-per-process: a permanently unrecoverable id otherwise spams hundreds of
177
+ // stack-traced WARNs per dashboard poll (RCA 2026-05-01, dev port 8687). The counter and audit
178
+ // trail still increment on every call so health metrics remain accurate.
132
179
  if (!e.firstSeenTs) {
133
180
  (0, features_1.incrementCounter)('usage:firstSeenRepairExhausted');
134
181
  trackInvariantRepair(e.id, 'firstSeenTs', 'exhausted');
135
- (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs repair exhausted — no source found for ${e.id}`);
182
+ if (!firstSeenExhaustedReported.has(e.id)) {
183
+ firstSeenExhaustedReported.add(e.id);
184
+ (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs repair exhausted — no source found for ${e.id}`);
185
+ }
136
186
  }
137
187
  }
188
+ /**
189
+ * Internal handles for unit tests only. Not part of the public API.
190
+ * @internal
191
+ */
192
+ exports._internal = { restoreFirstSeenInvariant };
138
193
  // Usage invariant repair (mirrors firstSeen invariant strategy). Extremely rare reload races in CI produced
139
194
  // states where a freshly re-materialized InstructionEntry temporarily lacked its prior usageCount (observed
140
195
  // by usageTracking.spec snapshot reads) even though authority maps retained the correct monotonic value.
@@ -421,14 +476,30 @@ function touchIndexVersion() {
421
476
  }
422
477
  catch { /* ignore */ }
423
478
  }
424
- function readVersionMTime() { try {
425
- const vf = getVersionFile();
426
- if (fs_1.default.existsSync(vf)) {
427
- const st = fs_1.default.statSync(vf);
479
+ function readVersionMTime() {
480
+ try {
481
+ const vf = getVersionFile();
482
+ if (fs_1.default.existsSync(vf)) {
483
+ const st = fs_1.default.statSync(vf);
484
+ return st.mtimeMs || 0;
485
+ }
486
+ }
487
+ catch { /* ignore */ }
488
+ // Fallback: when no .index-version file exists, use the instructions
489
+ // directory's own mtime so the ensureLoaded() cache short-circuit can still
490
+ // recognize an unchanged source. RCA 2026-05-01 (live dev port 8787 vs an
491
+ // operator-explored repo with no version file): readVersionMTime() returned
492
+ // 0, the falsy short-circuit `if (currentVersionMTime && ...)` always failed,
493
+ // every dashboard poll triggered a full disk reload + simple-reload trace,
494
+ // saturating the event loop and breaking dashboard imports.
495
+ try {
496
+ const baseDir = getInstructionsDir();
497
+ const st = fs_1.default.statSync(baseDir);
428
498
  return st.mtimeMs || 0;
429
499
  }
500
+ catch { /* ignore */ }
501
+ return 0;
430
502
  }
431
- catch { /* ignore */ } return 0; }
432
503
  function readVersionToken() { try {
433
504
  const vf = getVersionFile();
434
505
  if (fs_1.default.existsSync(vf)) {
@@ -503,20 +574,30 @@ function ensureLoaded() {
503
574
  const backend = (0, runtimeConfig_1.getRuntimeConfig)().storage?.backend ?? 'json';
504
575
  const store = backend === 'sqlite' ? getStoreForDir(baseDir) : null;
505
576
  let result = store ? store.load() : new indexLoader_1.IndexLoader(baseDir).load();
506
- // Auto-migrate JSON → SQLite when JSON files on disk outnumber SQLite rows
577
+ // Auto-migrate JSON → SQLite when JSON files on disk outnumber SQLite rows.
578
+ // Per-process latch (RCA 2026-05-01): without this, mismatched counts caused by
579
+ // a few unparseable JSON files (jsonFiles.length permanently > sqlite rows) made
580
+ // ensureLoaded() re-invoke migrateJsonToSqlite() on every reload tick, causing
581
+ // INSERT-OR-REPLACE storms and unbounded WAL growth (~1.21 GB observed in dev
582
+ // before the fix). One attempt per (baseDir, dbPath) per process is enough; if
583
+ // an operator truly needs a re-migrate, they restart the server.
507
584
  if (store && (0, runtimeConfig_1.getRuntimeConfig)().storage?.sqliteMigrateOnStart) {
508
- const jsonFiles = fs_1.default.existsSync(baseDir) ? fs_1.default.readdirSync(baseDir).filter(f => f.endsWith('.json') && !f.startsWith('_')) : [];
509
- if (jsonFiles.length > result.entries.length) {
510
- try {
511
- const dbPath = (0, runtimeConfig_1.getRuntimeConfig)().storage?.sqlitePath ?? path_1.default.join(process.cwd(), 'data', 'index.db');
512
- const mr = (0, migrationEngine_1.migrateJsonToSqlite)(baseDir, dbPath);
513
- if (mr.migrated > 0) {
514
- (0, logger_js_1.logInfo)(`[storage] Auto-migrated ${mr.migrated} instruction(s) from JSON → SQLite`);
515
- result = store.load();
585
+ const dbPath = (0, runtimeConfig_1.getRuntimeConfig)().storage?.sqlitePath ?? path_1.default.join(process.cwd(), 'data', 'index.db');
586
+ const latchKey = `${baseDir}|${dbPath}`;
587
+ if (!autoMigrationAttempted.has(latchKey)) {
588
+ autoMigrationAttempted.add(latchKey);
589
+ const jsonFiles = fs_1.default.existsSync(baseDir) ? fs_1.default.readdirSync(baseDir).filter(f => f.endsWith('.json') && !f.startsWith('_')) : [];
590
+ if (jsonFiles.length > result.entries.length) {
591
+ try {
592
+ const mr = (0, migrationEngine_1.migrateJsonToSqlite)(baseDir, dbPath);
593
+ if (mr.migrated > 0) {
594
+ (0, logger_js_1.logInfo)(`[storage] Auto-migrated ${mr.migrated} instruction(s) from JSON → SQLite`);
595
+ result = store.load();
596
+ }
597
+ }
598
+ catch (err) {
599
+ (0, logger_js_1.logWarn)('[storage] Auto-migration failed:', err);
516
600
  }
517
- }
518
- catch (err) {
519
- (0, logger_js_1.logWarn)('[storage] Auto-migration failed:', err);
520
601
  }
521
602
  }
522
603
  }
@@ -525,7 +606,17 @@ function ensureLoaded() {
525
606
  // Deduplicate list using byId so two on-disk files with the same id field never produce duplicate
526
607
  // search results. byId already uses last-write-wins semantics; list must be consistent with it.
527
608
  const deduplicatedList = Array.from(byId.values());
528
- state = { loadedAt: new Date().toISOString(), hash: result.hash, byId, list: deduplicatedList, fileCount: deduplicatedList.length, versionMTime: currentVersionMTime, versionToken: currentVersionToken, loadErrors: result.errors, loadDebug: result.debug, loadSummary: result.summary };
609
+ // RCA 2026-05-01 (live dev port 8787): IndexLoader.load() writes _manifest.json
610
+ // and _skipped.json into baseDir as a side-effect, which bumps the directory's
611
+ // mtime. If we cached the pre-load mtime here, the very next ensureLoaded()
612
+ // call would observe a newer mtime, miss the cache, and reload again — an
613
+ // unbounded loop that emitted [trace:ensureLoaded:simple-reload] hundreds of
614
+ // times per second and saturated the event loop (dashboard imports failed).
615
+ // Re-read the mtime AFTER load so the cached value reflects the post-write
616
+ // state; subsequent calls without source changes will then short-circuit.
617
+ const postLoadVersionMTime = readVersionMTime();
618
+ const postLoadVersionToken = readVersionToken();
619
+ state = { loadedAt: new Date().toISOString(), hash: result.hash, byId, list: deduplicatedList, fileCount: deduplicatedList.length, versionMTime: postLoadVersionMTime || currentVersionMTime, versionToken: postLoadVersionToken || currentVersionToken, loadErrors: result.errors, loadDebug: result.debug, loadSummary: result.summary };
529
620
  dirty = false;
530
621
  // Overlay usage snapshot (simplified; no spin/repair loops here—existing invariant repairs still occur in getIndexState)
531
622
  try {
@@ -573,7 +664,11 @@ async function ensureLoadedAsync() {
573
664
  const byId = new Map();
574
665
  result.entries.forEach(e => byId.set(e.id, e));
575
666
  const deduplicatedList = Array.from(byId.values());
576
- state = { loadedAt: new Date().toISOString(), hash: result.hash, byId, list: deduplicatedList, fileCount: deduplicatedList.length, versionMTime: currentVersionMTime, versionToken: currentVersionToken, loadErrors: result.errors, loadDebug: result.debug, loadSummary: result.summary };
667
+ // See RCA comment in ensureLoaded() above: re-read mtime AFTER load to absorb
668
+ // _manifest.json / _skipped.json side-effect writes.
669
+ const postLoadVersionMTime = readVersionMTime();
670
+ const postLoadVersionToken = readVersionToken();
671
+ state = { loadedAt: new Date().toISOString(), hash: result.hash, byId, list: deduplicatedList, fileCount: deduplicatedList.length, versionMTime: postLoadVersionMTime || currentVersionMTime, versionToken: postLoadVersionToken || currentVersionToken, loadErrors: result.errors, loadDebug: result.debug, loadSummary: result.summary };
577
672
  dirty = false;
578
673
  try {
579
674
  const snap = loadUsageSnapshot();
@@ -826,6 +921,15 @@ function isDuplicateInstructionWriteError(error) {
826
921
  }
827
922
  function writeEntry(entry, opts) {
828
923
  const file = path_1.default.join(getInstructionsDir(), `${entry.id}.json`);
924
+ // Establish firstSeenTs at the write boundary if the caller omitted it.
925
+ // Semantically firstSeenTs ≤ createdAt always — for fresh entries this is
926
+ // the correct value, and persisting it on disk avoids spurious
927
+ // [invariant-repair] WARN spam on every subsequent getIndexState() poll
928
+ // (RCA 2026-05-01 dev port 8687, third loop in chain after PR #285/#286).
929
+ if (!entry.firstSeenTs) {
930
+ entry.firstSeenTs = entry.createdAt || new Date().toISOString();
931
+ firstSeenAuthority[entry.id] = entry.firstSeenTs;
932
+ }
829
933
  const classifier = new classificationService_1.ClassificationService();
830
934
  let record = classifier.normalize(entry);
831
935
  if (record.owner === 'unowned') {
@@ -880,6 +984,11 @@ function writeEntry(entry, opts) {
880
984
  }
881
985
  async function writeEntryAsync(entry, opts) {
882
986
  const file = path_1.default.join(getInstructionsDir(), `${entry.id}.json`);
987
+ // See writeEntry: establish firstSeenTs at the write boundary if missing.
988
+ if (!entry.firstSeenTs) {
989
+ entry.firstSeenTs = entry.createdAt || new Date().toISOString();
990
+ firstSeenAuthority[entry.id] = entry.firstSeenTs;
991
+ }
883
992
  const classifier = new classificationService_1.ClassificationService();
884
993
  let record = classifier.normalize(entry);
885
994
  if (record.owner === 'unowned') {
@@ -11,6 +11,7 @@ const fs_1 = __importDefault(require("fs"));
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const runtimeConfig_1 = require("../config/runtimeConfig");
13
13
  const mcpLogBridge_1 = require("./mcpLogBridge");
14
+ const eventBuffer_1 = require("./eventBuffer");
14
15
  /** Numeric priority for log level filtering (lower = more verbose). */
15
16
  const LEVEL_PRIORITY = {
16
17
  TRACE: 0,
@@ -231,6 +232,14 @@ function emit(rec) {
231
232
  }
232
233
  catch { /* ignore file write failures */ }
233
234
  }
235
+ // Surface WARN/ERROR into the in-process events ring buffer so the dashboard
236
+ // Monitoring panel can display them without log-file tailing (OB-3, OB-5).
237
+ if (rec.level === 'WARN' || rec.level === 'ERROR') {
238
+ try {
239
+ (0, eventBuffer_1.recordEvent)(rec.level, rec.msg, rec.detail, rec.pid);
240
+ }
241
+ catch { /* never let buffer failure break logging */ }
242
+ }
234
243
  }
235
244
  /**
236
245
  * Emit a structured NDJSON log record at the specified level.
@@ -20,7 +20,17 @@ const features_1 = require("./features");
20
20
  const logger_1 = require("./logger");
21
21
  const runtimeConfig_1 = require("../config/runtimeConfig");
22
22
  const MANIFEST_RELATIVE = path_1.default.join('snapshots', 'index-manifest.json');
23
- function getManifestPath() { return path_1.default.join(process.cwd(), MANIFEST_RELATIVE); }
23
+ function getManifestPath() {
24
+ // Tests run in parallel forks (vitest pool=forks, maxWorkers=4) and each fork
25
+ // shares the same process.cwd(); without an override, multiple test files that
26
+ // enable manifest write race on the same on-disk file. Honor an explicit path
27
+ // override so test setup can isolate per-spec output. Production leaves this
28
+ // unset and continues to use the canonical snapshot path.
29
+ const override = process.env.INDEX_SERVER_MANIFEST_PATH;
30
+ if (override && override.trim())
31
+ return path_1.default.isAbsolute(override) ? override : path_1.default.join(process.cwd(), override);
32
+ return path_1.default.join(process.cwd(), MANIFEST_RELATIVE);
33
+ }
24
34
  /**
25
35
  * Load the manifest from disk.
26
36
  * @returns The parsed {@link IndexManifest}, or `null` if the file is absent or unparseable
@@ -223,7 +223,11 @@ function autoSeedBootstrap() {
223
223
  // Directory empty OR missing seed triggers creation.
224
224
  try {
225
225
  const tmp = path_1.default.join(dir, `.${seed.file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`);
226
- fs_1.default.writeFileSync(tmp, JSON.stringify(seed.json, null, 2), { encoding: 'utf8' });
226
+ // Inject timestamps at write time so loaders never trigger
227
+ // [invariant-repair] firstSeenTs WARN noise on subsequent reads.
228
+ const nowIso = new Date().toISOString();
229
+ const stamped = { createdAt: nowIso, firstSeenTs: nowIso, ...seed.json };
230
+ fs_1.default.writeFileSync(tmp, JSON.stringify(stamped, null, 2), { encoding: 'utf8' });
227
231
  fs_1.default.renameSync(tmp, target);
228
232
  summary.created.push(seed.file);
229
233
  }
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import type { IInstructionStore, IEmbeddingStore } from './types.js';
8
8
  export type StorageBackend = 'json' | 'sqlite';
9
+ /** Test-only: reset the warn-once latch. */
10
+ export declare function _resetSqliteExperimentalWarning(): void;
9
11
  /**
10
12
  * Check that the current Node.js version meets the minimum requirement.
11
13
  * Throws a clear error if the version is too old.
@@ -6,6 +6,7 @@
6
6
  * Default: JsonFileStore (json). Experimental: SqliteStore (sqlite).
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports._resetSqliteExperimentalWarning = _resetSqliteExperimentalWarning;
9
10
  exports.checkNodeVersion = checkNodeVersion;
10
11
  exports.createStore = createStore;
11
12
  exports.createEmbeddingStore = createEmbeddingStore;
@@ -14,6 +15,13 @@ const jsonFileStore_js_1 = require("./jsonFileStore.js");
14
15
  const jsonEmbeddingStore_js_1 = require("./jsonEmbeddingStore.js");
15
16
  const sqliteStore_js_1 = require("./sqliteStore.js");
16
17
  const logger_js_1 = require("../logger.js");
18
+ /** Process-scoped flag so the EXPERIMENTAL SQLite warning is emitted once,
19
+ * not per-store-creation (which floods the events ring on every request). */
20
+ let sqliteExperimentalWarned = false;
21
+ /** Test-only: reset the warn-once latch. */
22
+ function _resetSqliteExperimentalWarning() {
23
+ sqliteExperimentalWarned = false;
24
+ }
17
25
  /**
18
26
  * Check that the current Node.js version meets the minimum requirement.
19
27
  * Throws a clear error if the version is too old.
@@ -46,7 +54,10 @@ function createStore(backend, dir, sqlitePath) {
46
54
  switch (resolvedBackend) {
47
55
  case 'sqlite': {
48
56
  checkNodeVersion('22.5.0', 'SQLite storage backend (node:sqlite)');
49
- (0, logger_js_1.logWarn)('[storage] ⚠️ EXPERIMENTAL: SQLite backend is enabled. This feature has limited testing and may have data-loss or compatibility issues. Not recommended for production use.');
57
+ if (!sqliteExperimentalWarned) {
58
+ sqliteExperimentalWarned = true;
59
+ (0, logger_js_1.logWarn)('[storage] ⚠️ EXPERIMENTAL: SQLite backend is enabled. This feature has limited testing and may have data-loss or compatibility issues. Not recommended for production use.');
60
+ }
50
61
  const dbPath = sqlitePath ?? config.storage?.sqlitePath ?? 'data/index.db';
51
62
  return new sqliteStore_js_1.SqliteStore(dbPath);
52
63
  }
@@ -219,8 +219,10 @@ function emitTrace(label, data, min = 1) {
219
219
  if (caller)
220
220
  rec.func = caller;
221
221
  pushBuffer(rec, tracing.buffer);
222
+ // Trace records are diagnostic, not errors. Route through DEBUG so they do
223
+ // not pollute the WARN/ERROR events ring buffer surfaced to the dashboard.
222
224
  try {
223
- (0, logger_js_1.logError)(label, JSON.stringify(rec));
225
+ (0, logger_js_1.logDebug)(label, JSON.stringify(rec));
224
226
  }
225
227
  catch { /* ignore */ }
226
228
  if (!(tracing.persist || tracing.file))
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@jagilber-org/index-server",
3
- "version": "1.27.0",
3
+ "version": "1.27.2",
4
4
  "mcpName": "io.github.jagilber-org/index-server",
5
- "description": "MCP instruction indexing server for AI assistant governance — search, CRUD, schema validation, usage tracking, and cross-repo knowledge promotion.",
5
+ "description": "MCP instruction indexing server with search, CRUD, validation, and cross-repo knowledge promotion.",
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org/",
8
8
  "access": "public"
@@ -62,6 +62,7 @@
62
62
  "typecheck": "tsc -p tsconfig.json --noEmit",
63
63
  "lint": "eslint .",
64
64
  "lint:instructions": "node scripts/lint-instructions.mjs",
65
+ "lint:logs": "node scripts/crawl-logs.mjs --dir logs --file test-results/test-output.log --allowlist .crawl-logs-allowlist --strict",
65
66
  "format": "prettier --write .",
66
67
  "prepare": "node scripts/setup-hooks.cjs",
67
68
  "audit": "npm audit --omit=dev --audit-level=high",
@@ -134,6 +135,15 @@
134
135
  "ws": "^8.19.0",
135
136
  "zod": "^3.23.8"
136
137
  },
138
+ "overrides": {
139
+ "mermaid": {
140
+ "uuid": "^14.0.0"
141
+ },
142
+ "@mermaid-js/layout-elk": {
143
+ "uuid": "^14.0.0"
144
+ },
145
+ "@types/express-serve-static-core": "5.0.7"
146
+ },
137
147
  "optionalScripts": {},
138
148
  "promptReviewCriteriaVersion": "1.0.0",
139
149
  "license": "MIT",
package/server.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.jagilber-org/index-server",
4
- "description": "MCP instruction indexing server for AI assistant governance — search, CRUD, schema validation, usage tracking, and cross-repo knowledge promotion.",
4
+ "description": "MCP instruction indexing server with search, CRUD, validation, and cross-repo knowledge promotion.",
5
5
  "repository": {
6
6
  "url": "https://github.com/jagilber-org/index-server",
7
7
  "source": "github"
8
8
  },
9
- "version": "1.27.0",
9
+ "version": "1.27.2",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@jagilber-org/index-server",
14
- "version": "1.27.0",
14
+ "version": "1.27.2",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }