@jagilber-org/index-server 1.26.11 → 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.
- package/CHANGELOG.md +53 -1
- package/dist/config/dashboardConfig.d.ts +11 -4
- package/dist/config/dashboardConfig.js +1 -4
- package/dist/dashboard/client/admin.html +57 -27
- package/dist/dashboard/client/css/admin.css +54 -0
- package/dist/dashboard/client/js/admin.config.js +3 -6
- package/dist/dashboard/client/js/admin.embeddings.js +63 -4
- package/dist/dashboard/client/js/admin.events.js +256 -0
- package/dist/dashboard/client/js/admin.maintenance.js +75 -32
- package/dist/dashboard/client/js/admin.sessions.js +1 -1
- package/dist/dashboard/server/AdminPanel.js +83 -6
- package/dist/dashboard/server/AdminPanelConfig.d.ts +12 -2
- package/dist/dashboard/server/AdminPanelConfig.js +48 -19
- package/dist/dashboard/server/ApiRoutes.d.ts +5 -4
- package/dist/dashboard/server/ApiRoutes.js +40 -35
- package/dist/dashboard/server/DashboardServer.js +13 -0
- package/dist/dashboard/server/routes/admin.routes.js +143 -17
- package/dist/dashboard/server/routes/embeddings.routes.js +91 -1
- package/dist/dashboard/server/routes/index.js +11 -9
- package/dist/server/sdkServer.js +12 -4
- package/dist/services/embeddingService.d.ts +2 -0
- package/dist/services/embeddingService.js +16 -4
- package/dist/services/embeddingTrigger.d.ts +33 -0
- package/dist/services/embeddingTrigger.js +86 -0
- package/dist/services/eventBuffer.d.ts +45 -0
- package/dist/services/eventBuffer.js +110 -0
- package/dist/services/handlers/instructions.import.js +71 -13
- package/dist/services/handlers.dashboardConfig.js +82 -2
- package/dist/services/indexContext.d.ts +18 -0
- package/dist/services/indexContext.js +138 -30
- package/dist/services/logger.js +9 -0
- package/dist/services/manifestManager.js +11 -1
- package/dist/services/seedBootstrap.js +5 -1
- package/dist/services/storage/factory.d.ts +2 -0
- package/dist/services/storage/factory.js +12 -1
- package/dist/services/tracing.js +3 -1
- package/package.json +12 -2
- package/schemas/index-server.code-schema.json +7424 -1588
- package/schemas/manifest.json +3 -3
- package/server.json +3 -3
|
@@ -46,8 +46,7 @@ exports.FLAG_REGISTRY = [
|
|
|
46
46
|
{ name: 'INDEX_SERVER_VISIBILITY_DIAG', category: 'tracing', description: 'Force core trace level for visibility diagnostics.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.1.1' },
|
|
47
47
|
{ name: 'INDEX_SERVER_FILE_TRACE', category: 'tracing', description: 'Promote index file events to trace level (files).', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.1.1' },
|
|
48
48
|
// Usage & metrics
|
|
49
|
-
{ name: '
|
|
50
|
-
{ name: 'INDEX_SERVER_DISABLE_USAGE_RATE_LIMIT', category: 'usage', description: '(Deprecated) Legacy per-subsystem override to disable usage rate limit.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.1.1' },
|
|
49
|
+
{ name: 'INDEX_SERVER_RATE_LIMIT', category: 'usage', description: 'Dashboard HTTP API and usage-tracking rate limit, in requests per minute. 0 (default) disables rate limiting. Bulk import/export/backup/restore routes are unconditionally exempt.', stability: 'stable', default: '0', type: 'number', since: '1.27.0' },
|
|
51
50
|
{ name: 'INDEX_SERVER_DISABLE_USAGE_CLAMP', category: 'usage', description: 'Disable initial usage count clamp logic.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.1.1' },
|
|
52
51
|
{ name: 'INDEX_SERVER_USAGE_FLUSH_MS', category: 'usage', description: 'Override usage flush debounce interval.', stability: 'diagnostic', default: '75', type: 'number', since: '1.1.1' },
|
|
53
52
|
// Validation / schema
|
|
@@ -77,6 +76,87 @@ exports.FLAG_REGISTRY = [
|
|
|
77
76
|
{ name: 'INDEX_SERVER_LEADER_PORT', category: 'multi-instance', description: 'HTTP port for leader MCP transport (thin clients connect here).', stability: 'experimental', default: '9090', type: 'number', since: '1.8.5' },
|
|
78
77
|
{ name: 'INDEX_SERVER_HEARTBEAT_MS', category: 'multi-instance', description: 'Leader heartbeat interval (ms).', stability: 'experimental', default: '5000', type: 'number', since: '1.8.5' },
|
|
79
78
|
{ name: 'INDEX_SERVER_STALE_THRESHOLD_MS', category: 'multi-instance', description: 'Stale leader threshold (ms) before follower promotes.', stability: 'experimental', default: '15000', type: 'number', since: '1.8.5' },
|
|
79
|
+
// Semantic / embeddings
|
|
80
|
+
{ name: 'INDEX_SERVER_SEMANTIC_ENABLED', category: 'semantic', description: 'Enable semantic search & embedding compute.', stability: 'experimental', default: 'off', type: 'boolean', since: '1.20.0' },
|
|
81
|
+
{ name: 'INDEX_SERVER_SEMANTIC_MODEL', category: 'semantic', description: 'HuggingFace embedding model id (e.g. Xenova/all-MiniLM-L6-v2).', stability: 'experimental', default: '(default)', type: 'string', since: '1.20.0' },
|
|
82
|
+
{ name: 'INDEX_SERVER_SEMANTIC_DEVICE', category: 'semantic', description: 'Inference device: cpu | cuda | dml.', stability: 'experimental', default: 'cpu', type: 'string', since: '1.20.0' },
|
|
83
|
+
{ name: 'INDEX_SERVER_SEMANTIC_CACHE_DIR', category: 'semantic', description: 'Directory for downloaded model artifacts.', stability: 'experimental', default: './data/models', type: 'string', since: '1.20.0' },
|
|
84
|
+
{ name: 'INDEX_SERVER_SEMANTIC_LOCAL_ONLY', category: 'semantic', description: 'Disallow remote model downloads (offline mode).', stability: 'experimental', default: 'on', type: 'boolean', since: '1.20.0' },
|
|
85
|
+
{ name: 'INDEX_SERVER_EMBEDDING_PATH', category: 'semantic', description: 'Path to embeddings JSON cache file.', stability: 'experimental', default: './data/embeddings.json', type: 'string', since: '1.20.0' },
|
|
86
|
+
{ name: 'INDEX_SERVER_AUTO_EMBED_ON_IMPORT', category: 'semantic', description: 'Auto-compute embeddings after zip import / restore (when semantic enabled).', stability: 'experimental', default: 'on', type: 'boolean', since: '1.27.3' },
|
|
87
|
+
// Storage backend
|
|
88
|
+
{ name: 'INDEX_SERVER_STORAGE_BACKEND', category: 'storage', description: 'Instruction store backend: json (default) | sqlite (experimental).', stability: 'experimental', default: 'json', type: 'string', since: '1.25.0' },
|
|
89
|
+
{ name: 'INDEX_SERVER_SQLITE_PATH', category: 'storage', description: 'SQLite database file path.', stability: 'experimental', default: './data/index.db', type: 'string', since: '1.25.0' },
|
|
90
|
+
{ name: 'INDEX_SERVER_SQLITE_WAL', category: 'storage', description: 'Enable SQLite WAL journaling.', stability: 'experimental', default: 'on', type: 'boolean', since: '1.25.0' },
|
|
91
|
+
{ name: 'INDEX_SERVER_SQLITE_MIGRATE_ON_START', category: 'storage', description: 'Run schema migrations at startup.', stability: 'experimental', default: 'on', type: 'boolean', since: '1.25.0' },
|
|
92
|
+
{ name: 'INDEX_SERVER_SQLITE_VEC_ENABLED', category: 'storage', description: 'Enable sqlite-vec extension for embeddings.', stability: 'experimental', default: 'off', type: 'boolean', since: '1.25.0' },
|
|
93
|
+
{ name: 'INDEX_SERVER_SQLITE_VEC_PATH', category: 'storage', description: 'Path to sqlite-vec loadable extension.', stability: 'experimental', default: '(unset)', type: 'string', since: '1.25.0' },
|
|
94
|
+
// Feedback & messaging
|
|
95
|
+
{ name: 'INDEX_SERVER_FEEDBACK_DIR', category: 'feedback', description: 'Feedback storage directory.', stability: 'stable', default: './feedback', type: 'string', since: '1.10.0' },
|
|
96
|
+
{ name: 'INDEX_SERVER_FEEDBACK_MAX_ENTRIES', category: 'feedback', description: 'Maximum retained feedback entries.', stability: 'stable', default: '10000', type: 'number', since: '1.10.0' },
|
|
97
|
+
{ name: 'INDEX_SERVER_MESSAGING_DIR', category: 'messaging', description: 'Inter-agent messaging storage dir.', stability: 'experimental', default: './data/messaging', type: 'string', since: '1.18.0' },
|
|
98
|
+
{ name: 'INDEX_SERVER_MESSAGING_MAX', category: 'messaging', description: 'Max retained messages.', stability: 'experimental', default: '5000', type: 'number', since: '1.18.0' },
|
|
99
|
+
{ name: 'INDEX_SERVER_MESSAGING_SWEEP_MS', category: 'messaging', description: 'Messaging sweep interval (ms).', stability: 'experimental', default: '60000', type: 'number', since: '1.18.0' },
|
|
100
|
+
// Dashboard / TLS
|
|
101
|
+
{ name: 'INDEX_SERVER_DASHBOARD_TLS', category: 'dashboard', description: 'Enable HTTPS for the admin dashboard.', stability: 'stable', default: 'off', type: 'boolean', since: '1.20.0' },
|
|
102
|
+
{ name: 'INDEX_SERVER_DASHBOARD_TLS_CERT', category: 'dashboard', description: 'TLS certificate path.', stability: 'stable', default: '(unset)', type: 'string', since: '1.20.0' },
|
|
103
|
+
{ name: 'INDEX_SERVER_DASHBOARD_TLS_KEY', category: 'dashboard', description: 'TLS private key path.', stability: 'stable', default: '(unset)', type: 'string', since: '1.20.0' },
|
|
104
|
+
{ name: 'INDEX_SERVER_DASHBOARD_TLS_CA', category: 'dashboard', description: 'Optional TLS CA bundle path.', stability: 'stable', default: '(unset)', type: 'string', since: '1.20.0' },
|
|
105
|
+
{ name: 'INDEX_SERVER_DASHBOARD_GRAPH', category: 'dashboard', description: 'Enable dashboard graph rendering.', stability: 'stable', default: 'off', type: 'boolean', since: '1.18.0' },
|
|
106
|
+
{ name: 'INDEX_SERVER_HTTP_METRICS', category: 'dashboard', description: 'Expose Prometheus-style HTTP metrics.', stability: 'stable', default: 'on', type: 'boolean', since: '1.20.0' },
|
|
107
|
+
{ name: 'INDEX_SERVER_REQUEST_TIMEOUT', category: 'dashboard', description: 'HTTP request timeout (ms).', stability: 'stable', default: '30000', type: 'number', since: '1.20.0' },
|
|
108
|
+
{ name: 'INDEX_SERVER_MAX_CONNECTIONS', category: 'dashboard', description: 'Max concurrent HTTP connections.', stability: 'stable', default: '100', type: 'number', since: '1.20.0' },
|
|
109
|
+
{ name: 'INDEX_SERVER_ADMIN_API_KEY', category: 'auth', description: 'Bearer token for admin endpoints.', stability: 'stable', default: '(unset)', type: 'string', since: '1.20.0' },
|
|
110
|
+
{ name: 'INDEX_SERVER_ADMIN_MAX_SESSION_HISTORY', category: 'dashboard', description: 'Max retained admin session history.', stability: 'stable', default: '500', type: 'number', since: '1.20.0' },
|
|
111
|
+
{ name: 'INDEX_SERVER_BACKUPS_DIR', category: 'dashboard', description: 'Directory for backup zips.', stability: 'stable', default: './backups', type: 'string', since: '1.20.0' },
|
|
112
|
+
{ name: 'INDEX_SERVER_STATE_DIR', category: 'dashboard', description: 'Persistent dashboard state directory.', stability: 'stable', default: './data/state', type: 'string', since: '1.20.0' },
|
|
113
|
+
// Backup / mutation safety
|
|
114
|
+
{ name: 'INDEX_SERVER_AUTO_BACKUP', category: 'index', description: 'Auto-create backup before risky mutations.', stability: 'stable', default: 'on', type: 'boolean', since: '1.20.0' },
|
|
115
|
+
{ name: 'INDEX_SERVER_AUTO_BACKUP_INTERVAL_MS', category: 'index', description: 'Auto-backup interval ms.', stability: 'stable', default: '3600000', type: 'number', since: '1.20.0' },
|
|
116
|
+
{ name: 'INDEX_SERVER_AUTO_BACKUP_MAX_COUNT', category: 'index', description: 'Max retained auto-backups.', stability: 'stable', default: '10', type: 'number', since: '1.20.0' },
|
|
117
|
+
{ name: 'INDEX_SERVER_BACKUP_BEFORE_BULK_DELETE', category: 'index', description: 'Snapshot before bulk delete.', stability: 'stable', default: 'on', type: 'boolean', since: '1.20.0' },
|
|
118
|
+
{ name: 'INDEX_SERVER_MAX_BULK_DELETE', category: 'index', description: 'Max ids per bulk delete call.', stability: 'stable', default: '1000', type: 'number', since: '1.20.0' },
|
|
119
|
+
{ name: 'INDEX_SERVER_BODY_WARN_LENGTH', category: 'index', description: 'Warn-then-reject threshold for instruction body length.', stability: 'stable', default: '50000', type: 'number', since: '1.20.0' },
|
|
120
|
+
{ name: 'INDEX_SERVER_AUTO_SPLIT_OVERSIZED', category: 'index', description: 'Auto-split oversized instruction bodies on add.', stability: 'experimental', default: 'off', type: 'boolean', since: '1.20.0' },
|
|
121
|
+
{ name: 'INDEX_SERVER_AUTO_USAGE_TRACK', category: 'usage', description: 'Auto-track usage for tool invocations.', stability: 'stable', default: 'on', type: 'boolean', since: '1.20.0' },
|
|
122
|
+
// Bootstrap & seed
|
|
123
|
+
{ name: 'INDEX_SERVER_AUTO_SEED', category: 'bootstrap', description: 'Auto-seed canonical bootstrap instructions on first start.', stability: 'stable', default: 'on', type: 'boolean', since: '1.10.0' },
|
|
124
|
+
{ name: 'INDEX_SERVER_SEED_VERBOSE', category: 'bootstrap', description: 'Verbose seed-bootstrap logging.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.10.0' },
|
|
125
|
+
{ name: 'INDEX_SERVER_BOOTSTRAP_AUTOCONFIRM', category: 'bootstrap', description: 'Auto-confirm bootstrap (skip token prompt).', stability: 'stable', default: 'off', type: 'boolean', since: '1.10.0' },
|
|
126
|
+
{ name: 'INDEX_SERVER_BOOTSTRAP_TOKEN_TTL_SEC', category: 'bootstrap', description: 'Bootstrap token TTL (seconds).', stability: 'stable', default: '600', type: 'number', since: '1.10.0' },
|
|
127
|
+
// Logging surface
|
|
128
|
+
{ name: 'INDEX_SERVER_LOG_FILE', category: 'core', description: 'NDJSON log file path (or 1 to use default).', stability: 'stable', default: '(unset)', type: 'string', since: '1.0.0' },
|
|
129
|
+
{ name: 'INDEX_SERVER_LOG_LEVEL', category: 'core', description: 'Minimum log level: trace|debug|info|warn|error.', stability: 'stable', default: 'info', type: 'string', since: '1.20.0' },
|
|
130
|
+
{ name: 'INDEX_SERVER_LOG_JSON', category: 'core', description: '(Reserved) Force JSON log mode.', stability: 'reserved', default: 'off', type: 'boolean', since: '1.0.0' },
|
|
131
|
+
{ name: 'INDEX_SERVER_LOG_SYNC', category: 'diagnostics', description: 'Fsync after each log write (test determinism).', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.0.0' },
|
|
132
|
+
{ name: 'INDEX_SERVER_LOG_PROTOCOL', category: 'diagnostics', description: 'Log MCP protocol frames.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.0.0' },
|
|
133
|
+
{ name: 'INDEX_SERVER_EVENT_BUFFER_SIZE', category: 'diagnostics', description: 'Capacity of in-memory WARN/ERROR ring buffer surfaced in dashboard Events panel.', stability: 'stable', default: '500', type: 'number', since: '1.27.3' },
|
|
134
|
+
{ name: 'INDEX_SERVER_EVENT_SILENT', category: 'diagnostics', description: 'Suppress redundant index-event logs.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.20.0' },
|
|
135
|
+
// Profile / dev mode
|
|
136
|
+
{ name: 'INDEX_SERVER_PROFILE', category: 'core', description: 'Runtime profile selector (default | dev | prod).', stability: 'stable', default: 'default', type: 'string', since: '1.20.0' },
|
|
137
|
+
// Operationally-meaningful additions surfaced by the catalog drift test.
|
|
138
|
+
{ name: 'INDEX_SERVER_BODY_MAX_LENGTH', category: 'index', description: 'Hard reject threshold for instruction body length (bytes).', stability: 'stable', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
139
|
+
{ name: 'INDEX_SERVER_MAX_FILES', category: 'index', description: 'Soft cap on number of indexed instruction files.', stability: 'stable', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
140
|
+
{ name: 'INDEX_SERVER_LOAD_WARN_MS', category: 'diagnostics', description: 'Emit WARN if initial index load exceeds this many ms.', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
141
|
+
{ name: 'INDEX_SERVER_AGENT_ID', category: 'core', description: 'Logical agent identity for audit/attestation trailers.', stability: 'stable', default: '(unset)', type: 'string', since: '1.20.0' },
|
|
142
|
+
{ name: 'INDEX_SERVER_FLAGS_FILE', category: 'core', description: 'Path to feature-flag persistence file.', stability: 'stable', default: './flags.json', type: 'string', since: '1.10.0' },
|
|
143
|
+
{ name: 'INDEX_SERVER_HEALTH_MEMORY_THRESHOLD', category: 'diagnostics', description: 'Memory threshold (bytes) for /health degraded status.', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
144
|
+
{ name: 'INDEX_SERVER_HEALTH_ERROR_THRESHOLD', category: 'diagnostics', description: 'Error-rate threshold for /health degraded status.', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
145
|
+
{ name: 'INDEX_SERVER_HEALTH_MIN_UPTIME', category: 'diagnostics', description: 'Minimum uptime (s) before /health reports healthy.', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
146
|
+
{ name: 'INDEX_SERVER_RESOURCE_CAPACITY', category: 'diagnostics', description: 'In-memory resource sample buffer capacity.', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
147
|
+
{ name: 'INDEX_SERVER_RESOURCE_SAMPLE_INTERVAL_MS', category: 'diagnostics', description: 'Resource sampler interval (ms).', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
148
|
+
{ name: 'INDEX_SERVER_TOOLCALL_CHUNK_SIZE', category: 'diagnostics', description: 'Tool-call ring buffer chunk size.', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
149
|
+
{ name: 'INDEX_SERVER_TOOLCALL_FLUSH_MS', category: 'diagnostics', description: 'Tool-call ring buffer flush interval (ms).', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
150
|
+
{ name: 'INDEX_SERVER_TOOLCALL_COMPACT_MS', category: 'diagnostics', description: 'Tool-call ring buffer compaction interval (ms).', stability: 'diagnostic', default: '(unset)', type: 'number', since: '1.20.0' },
|
|
151
|
+
{ name: 'INDEX_SERVER_TOOLCALL_APPEND_LOG', category: 'diagnostics', description: 'Append-only tool-call log path.', stability: 'diagnostic', default: '(unset)', type: 'string', since: '1.20.0' },
|
|
152
|
+
{ name: 'INDEX_SERVER_AUDIT_LOG', category: 'diagnostics', description: 'Audit log file path (mutation operations).', stability: 'diagnostic', default: '(unset)', type: 'string', since: '1.10.0' },
|
|
153
|
+
{ name: 'INDEX_SERVER_NORMALIZATION_LOG', category: 'diagnostics', description: 'Normalization log file path.', stability: 'diagnostic', default: '(unset)', type: 'string', since: '1.10.0' },
|
|
154
|
+
{ name: 'INDEX_SERVER_TRACE', category: 'diagnostics', description: 'Enable verbose trace logging.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.0.0' },
|
|
155
|
+
{ name: 'INDEX_SERVER_TIMING_JSON', category: 'diagnostics', description: 'Emit timing data as JSON-NDJSON.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.20.0' },
|
|
156
|
+
{ name: 'INDEX_SERVER_MINIMAL_DEBUG', category: 'diagnostics', description: 'Minimal debug surface (reduces verbosity).', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.20.0' },
|
|
157
|
+
{ name: 'INDEX_SERVER_STRESS_MODE', category: 'diagnostics', description: 'Enable stress-test instrumentation.', stability: 'diagnostic', default: 'off', type: 'boolean', since: '1.20.0' },
|
|
158
|
+
{ name: 'INDEX_SERVER_GRAPH_INCLUDE_PRIMARY_EDGES', category: 'index', description: 'Include primary edges in graph export.', stability: 'experimental', default: 'on', type: 'boolean', since: '1.18.0' },
|
|
159
|
+
{ name: 'INDEX_SERVER_GRAPH_LARGE_CATEGORY_CAP', category: 'index', description: 'Cap large-category fanout in graph export.', stability: 'experimental', default: '(unset)', type: 'number', since: '1.18.0' },
|
|
80
160
|
];
|
|
81
161
|
function parseValue(meta) {
|
|
82
162
|
const raw = process.env[meta.name];
|
|
@@ -49,11 +49,29 @@ declare const invariantRepairLog: {
|
|
|
49
49
|
field: string;
|
|
50
50
|
source: string;
|
|
51
51
|
}[];
|
|
52
|
+
/**
|
|
53
|
+
* Test-only hook — reset process-scoped latches between vitest specs.
|
|
54
|
+
* @internal Not part of the public API.
|
|
55
|
+
*/
|
|
56
|
+
export declare function _resetIndexContextProcessLatches(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Test-only hook — reset the module-scoped index state cache.
|
|
59
|
+
* @internal Not part of the public API.
|
|
60
|
+
*/
|
|
61
|
+
export declare function _resetIndexContextStateForTests(): void;
|
|
52
62
|
/** Returns a summary of invariant repairs for health check visibility. */
|
|
53
63
|
export declare function getInvariantRepairSummary(): {
|
|
54
64
|
totalRepairs: number;
|
|
55
65
|
recentRepairs: typeof invariantRepairLog;
|
|
56
66
|
};
|
|
67
|
+
declare function restoreFirstSeenInvariant(e: InstructionEntry): void;
|
|
68
|
+
/**
|
|
69
|
+
* Internal handles for unit tests only. Not part of the public API.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export declare const _internal: {
|
|
73
|
+
restoreFirstSeenInvariant: typeof restoreFirstSeenInvariant;
|
|
74
|
+
};
|
|
57
75
|
export declare function clearUsageRateLimit(id?: string): void;
|
|
58
76
|
export declare function loadUsageSnapshot(): Record<string, UsagePersistRecord>;
|
|
59
77
|
export declare function getInstructionsDir(): string;
|
|
@@ -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;
|
|
@@ -36,7 +39,6 @@ const features_1 = require("./features");
|
|
|
36
39
|
const atomicFs_1 = require("./atomicFs");
|
|
37
40
|
const classificationService_1 = require("./classificationService");
|
|
38
41
|
const ownershipService_1 = require("./ownershipService");
|
|
39
|
-
const envUtils_1 = require("../utils/envUtils");
|
|
40
42
|
const runtimeConfig_1 = require("../config/runtimeConfig");
|
|
41
43
|
const factory_1 = require("./storage/factory");
|
|
42
44
|
const migrationEngine_1 = require("./storage/migrationEngine");
|
|
@@ -96,6 +98,33 @@ function trackInvariantRepair(id, field, source) {
|
|
|
96
98
|
if (invariantRepairLog.length > MAX_REPAIR_LOG)
|
|
97
99
|
invariantRepairLog.shift();
|
|
98
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
|
+
}
|
|
99
128
|
/** Returns a summary of invariant repairs for health check visibility. */
|
|
100
129
|
function getInvariantRepairSummary() {
|
|
101
130
|
return { totalRepairs: invariantRepairLog.length, recentRepairs: invariantRepairLog.slice(-20) };
|
|
@@ -111,7 +140,7 @@ function restoreFirstSeenInvariant(e) {
|
|
|
111
140
|
e.firstSeenTs = auth;
|
|
112
141
|
(0, features_1.incrementCounter)('usage:firstSeenAuthorityRepair');
|
|
113
142
|
trackInvariantRepair(e.id, 'firstSeenTs', 'authority');
|
|
114
|
-
(0, logger_js_1.
|
|
143
|
+
(0, logger_js_1.logDebug)(`[invariant-repair] firstSeenTs restored from authority for ${e.id}`);
|
|
115
144
|
return;
|
|
116
145
|
}
|
|
117
146
|
const ep = ephemeralFirstSeen[e.id];
|
|
@@ -119,7 +148,7 @@ function restoreFirstSeenInvariant(e) {
|
|
|
119
148
|
e.firstSeenTs = ep;
|
|
120
149
|
(0, features_1.incrementCounter)('usage:firstSeenInvariantRepair');
|
|
121
150
|
trackInvariantRepair(e.id, 'firstSeenTs', 'ephemeral');
|
|
122
|
-
(0, logger_js_1.
|
|
151
|
+
(0, logger_js_1.logDebug)(`[invariant-repair] firstSeenTs restored from ephemeral cache for ${e.id}`);
|
|
123
152
|
return;
|
|
124
153
|
}
|
|
125
154
|
const snap = lastGoodUsageSnapshot[e.id];
|
|
@@ -127,15 +156,40 @@ function restoreFirstSeenInvariant(e) {
|
|
|
127
156
|
e.firstSeenTs = snap.firstSeenTs;
|
|
128
157
|
(0, features_1.incrementCounter)('usage:firstSeenInvariantRepair');
|
|
129
158
|
trackInvariantRepair(e.id, 'firstSeenTs', 'snapshot');
|
|
130
|
-
(0, logger_js_1.
|
|
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;
|
|
131
174
|
}
|
|
132
|
-
// 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.
|
|
133
179
|
if (!e.firstSeenTs) {
|
|
134
180
|
(0, features_1.incrementCounter)('usage:firstSeenRepairExhausted');
|
|
135
181
|
trackInvariantRepair(e.id, 'firstSeenTs', 'exhausted');
|
|
136
|
-
(
|
|
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
|
+
}
|
|
137
186
|
}
|
|
138
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Internal handles for unit tests only. Not part of the public API.
|
|
190
|
+
* @internal
|
|
191
|
+
*/
|
|
192
|
+
exports._internal = { restoreFirstSeenInvariant };
|
|
139
193
|
// Usage invariant repair (mirrors firstSeen invariant strategy). Extremely rare reload races in CI produced
|
|
140
194
|
// states where a freshly re-materialized InstructionEntry temporarily lacked its prior usageCount (observed
|
|
141
195
|
// by usageTracking.spec snapshot reads) even though authority maps retained the correct monotonic value.
|
|
@@ -202,11 +256,11 @@ function restoreLastUsedInvariant(e) {
|
|
|
202
256
|
const USAGE_RATE_LIMIT_PER_SECOND = 10; // max increments per id per second
|
|
203
257
|
const usageRateLimiter = new Map();
|
|
204
258
|
function checkUsageRateLimit(id) {
|
|
205
|
-
// Rate limiting is
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (
|
|
259
|
+
// Rate limiting is opt-in. INDEX_SERVER_RATE_LIMIT=0 (default) or unset
|
|
260
|
+
// disables both the dashboard HTTP limiter and this usage limiter.
|
|
261
|
+
// Any positive integer enables both.
|
|
262
|
+
const rl = Number(process.env.INDEX_SERVER_RATE_LIMIT);
|
|
263
|
+
if (!Number.isFinite(rl) || rl <= 0)
|
|
210
264
|
return true;
|
|
211
265
|
const now = Date.now();
|
|
212
266
|
const windowStart = Math.floor(now / 1000) * 1000; // 1-second windows
|
|
@@ -422,14 +476,30 @@ function touchIndexVersion() {
|
|
|
422
476
|
}
|
|
423
477
|
catch { /* ignore */ }
|
|
424
478
|
}
|
|
425
|
-
function readVersionMTime() {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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);
|
|
429
498
|
return st.mtimeMs || 0;
|
|
430
499
|
}
|
|
500
|
+
catch { /* ignore */ }
|
|
501
|
+
return 0;
|
|
431
502
|
}
|
|
432
|
-
catch { /* ignore */ } return 0; }
|
|
433
503
|
function readVersionToken() { try {
|
|
434
504
|
const vf = getVersionFile();
|
|
435
505
|
if (fs_1.default.existsSync(vf)) {
|
|
@@ -504,20 +574,30 @@ function ensureLoaded() {
|
|
|
504
574
|
const backend = (0, runtimeConfig_1.getRuntimeConfig)().storage?.backend ?? 'json';
|
|
505
575
|
const store = backend === 'sqlite' ? getStoreForDir(baseDir) : null;
|
|
506
576
|
let result = store ? store.load() : new indexLoader_1.IndexLoader(baseDir).load();
|
|
507
|
-
// 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.
|
|
508
584
|
if (store && (0, runtimeConfig_1.getRuntimeConfig)().storage?.sqliteMigrateOnStart) {
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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);
|
|
517
600
|
}
|
|
518
|
-
}
|
|
519
|
-
catch (err) {
|
|
520
|
-
(0, logger_js_1.logWarn)('[storage] Auto-migration failed:', err);
|
|
521
601
|
}
|
|
522
602
|
}
|
|
523
603
|
}
|
|
@@ -526,7 +606,17 @@ function ensureLoaded() {
|
|
|
526
606
|
// Deduplicate list using byId so two on-disk files with the same id field never produce duplicate
|
|
527
607
|
// search results. byId already uses last-write-wins semantics; list must be consistent with it.
|
|
528
608
|
const deduplicatedList = Array.from(byId.values());
|
|
529
|
-
|
|
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 };
|
|
530
620
|
dirty = false;
|
|
531
621
|
// Overlay usage snapshot (simplified; no spin/repair loops here—existing invariant repairs still occur in getIndexState)
|
|
532
622
|
try {
|
|
@@ -574,7 +664,11 @@ async function ensureLoadedAsync() {
|
|
|
574
664
|
const byId = new Map();
|
|
575
665
|
result.entries.forEach(e => byId.set(e.id, e));
|
|
576
666
|
const deduplicatedList = Array.from(byId.values());
|
|
577
|
-
|
|
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 };
|
|
578
672
|
dirty = false;
|
|
579
673
|
try {
|
|
580
674
|
const snap = loadUsageSnapshot();
|
|
@@ -827,6 +921,15 @@ function isDuplicateInstructionWriteError(error) {
|
|
|
827
921
|
}
|
|
828
922
|
function writeEntry(entry, opts) {
|
|
829
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
|
+
}
|
|
830
933
|
const classifier = new classificationService_1.ClassificationService();
|
|
831
934
|
let record = classifier.normalize(entry);
|
|
832
935
|
if (record.owner === 'unowned') {
|
|
@@ -881,6 +984,11 @@ function writeEntry(entry, opts) {
|
|
|
881
984
|
}
|
|
882
985
|
async function writeEntryAsync(entry, opts) {
|
|
883
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
|
+
}
|
|
884
992
|
const classifier = new classificationService_1.ClassificationService();
|
|
885
993
|
let record = classifier.normalize(entry);
|
|
886
994
|
if (record.owner === 'unowned') {
|
package/dist/services/logger.js
CHANGED
|
@@ -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() {
|
|
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
|
-
|
|
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
|
-
(
|
|
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
|
}
|
package/dist/services/tracing.js
CHANGED
|
@@ -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.
|
|
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.
|
|
3
|
+
"version": "1.27.2",
|
|
4
4
|
"mcpName": "io.github.jagilber-org/index-server",
|
|
5
|
-
"description": "MCP instruction indexing server
|
|
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",
|