@jagilber-org/index-server 1.22.1 → 1.26.4
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 +91 -2
- package/CODE_OF_CONDUCT.md +2 -0
- package/CONTRIBUTING.md +32 -2
- package/README.md +82 -19
- package/SECURITY.md +17 -5
- package/dist/config/dashboardConfig.d.ts +3 -0
- package/dist/config/dashboardConfig.js +3 -0
- package/dist/config/defaultValues.d.ts +1 -1
- package/dist/config/defaultValues.js +1 -1
- package/dist/config/featureConfig.d.ts +2 -0
- package/dist/config/featureConfig.js +6 -1
- package/dist/config/runtimeConfig.d.ts +1 -1
- package/dist/config/runtimeConfig.js +8 -9
- package/dist/dashboard/client/admin.html +170 -53
- package/dist/dashboard/client/css/admin.css +132 -0
- package/dist/dashboard/client/js/admin.auth.js +25 -11
- package/dist/dashboard/client/js/admin.config.js +1 -1
- package/dist/dashboard/client/js/admin.feedback.js +328 -0
- package/dist/dashboard/client/js/admin.graph.js +120 -18
- package/dist/dashboard/client/js/admin.instructions.js +27 -13
- package/dist/dashboard/client/js/admin.logs.js +1 -5
- package/dist/dashboard/client/js/admin.maintenance.js +53 -8
- package/dist/dashboard/client/js/admin.messaging.js +1 -4
- package/dist/dashboard/client/js/admin.overview.js +5 -1
- package/dist/dashboard/client/js/admin.sessions.js +1 -1
- package/dist/dashboard/client/js/admin.utils.js +43 -1
- package/dist/dashboard/client/js/mermaid.min.js +813 -537
- package/dist/dashboard/export/DataExporter.js +2 -1
- package/dist/dashboard/server/AdminPanel.d.ts +3 -0
- package/dist/dashboard/server/AdminPanel.js +132 -35
- package/dist/dashboard/server/ApiRoutes.js +40 -9
- package/dist/dashboard/server/DashboardServer.js +1 -1
- package/dist/dashboard/server/FileMetricsStorage.d.ts +19 -0
- package/dist/dashboard/server/FileMetricsStorage.js +52 -5
- package/dist/dashboard/server/HttpTransport.js +6 -0
- package/dist/dashboard/server/InstanceManager.js +7 -2
- package/dist/dashboard/server/KnowledgeStore.js +7 -2
- package/dist/dashboard/server/MetricsCollector.d.ts +16 -0
- package/dist/dashboard/server/MetricsCollector.js +113 -17
- package/dist/dashboard/server/legacyDashboardHtml.js +7 -2
- package/dist/dashboard/server/middleware/ensureLoadedMiddleware.d.ts +1 -1
- package/dist/dashboard/server/middleware/ensureLoadedMiddleware.js +8 -3
- package/dist/dashboard/server/routes/admin.feedback.routes.d.ts +15 -0
- package/dist/dashboard/server/routes/admin.feedback.routes.js +188 -0
- package/dist/dashboard/server/routes/admin.routes.js +35 -27
- package/dist/dashboard/server/routes/alerts.routes.js +4 -3
- package/dist/dashboard/server/routes/api.feedback.routes.js +2 -1
- package/dist/dashboard/server/routes/api.usage.routes.js +8 -7
- package/dist/dashboard/server/routes/embeddings.routes.d.ts +2 -1
- package/dist/dashboard/server/routes/embeddings.routes.js +18 -9
- package/dist/dashboard/server/routes/graph.routes.js +10 -13
- package/dist/dashboard/server/routes/index.d.ts +1 -0
- package/dist/dashboard/server/routes/index.js +74 -39
- package/dist/dashboard/server/routes/instances.routes.js +2 -1
- package/dist/dashboard/server/routes/instructions.routes.js +46 -27
- package/dist/dashboard/server/routes/knowledge.routes.js +4 -3
- package/dist/dashboard/server/routes/logs.routes.js +5 -4
- package/dist/dashboard/server/routes/messaging.routes.js +15 -14
- package/dist/dashboard/server/routes/metrics.routes.js +14 -13
- package/dist/dashboard/server/routes/scripts.routes.js +6 -3
- package/dist/dashboard/server/routes/status.routes.js +5 -4
- package/dist/dashboard/server/routes/synthetic.routes.js +3 -2
- package/dist/dashboard/server/routes/usage.routes.js +2 -1
- package/dist/dashboard/server/utils/escapeHtml.d.ts +1 -0
- package/dist/dashboard/server/utils/escapeHtml.js +11 -0
- package/dist/dashboard/server/utils/pathContainment.d.ts +1 -0
- package/dist/dashboard/server/utils/pathContainment.js +15 -0
- package/dist/dashboard/server/wsInit.js +2 -2
- package/dist/lib/mcpStdioLogging.d.ts +165 -0
- package/dist/lib/mcpStdioLogging.js +287 -0
- package/dist/schemas/index.d.ts +37 -2
- package/dist/schemas/index.js +27 -3
- package/dist/server/backgroundServicesStartup.d.ts +7 -1
- package/dist/server/backgroundServicesStartup.js +25 -8
- package/dist/server/certInit.d.ts +97 -0
- package/dist/server/certInit.js +359 -0
- package/dist/server/certInit.types.d.ts +92 -0
- package/dist/server/certInit.types.js +34 -0
- package/dist/server/handshake/fallbackFrames.d.ts +31 -0
- package/dist/server/handshake/fallbackFrames.js +38 -0
- package/dist/server/handshake/initializeDetector.d.ts +31 -0
- package/dist/server/handshake/initializeDetector.js +88 -0
- package/dist/server/handshake/protocol.d.ts +15 -0
- package/dist/server/handshake/protocol.js +37 -0
- package/dist/server/handshake/readyEmitter.d.ts +6 -0
- package/dist/server/handshake/readyEmitter.js +88 -0
- package/dist/server/handshake/safetyFallbacks.d.ts +1 -0
- package/dist/server/handshake/safetyFallbacks.js +134 -0
- package/dist/server/handshake/stdinSniffer.d.ts +1 -0
- package/dist/server/handshake/stdinSniffer.js +260 -0
- package/dist/server/handshake/tracing.d.ts +16 -0
- package/dist/server/handshake/tracing.js +95 -0
- package/dist/server/handshakeManager.d.ts +23 -23
- package/dist/server/handshakeManager.js +36 -466
- package/dist/server/index-server.d.ts +23 -0
- package/dist/server/index-server.js +194 -9
- package/dist/server/mcpReadOnlySurfaces.d.ts +44 -0
- package/dist/server/mcpReadOnlySurfaces.js +297 -0
- package/dist/server/sdkServer.js +69 -7
- package/dist/server/transport.d.ts +5 -6
- package/dist/server/transport.js +46 -64
- package/dist/server/transportFactory.d.ts +3 -9
- package/dist/server/transportFactory.js +18 -380
- package/dist/services/atomicFs.d.ts +3 -0
- package/dist/services/atomicFs.js +171 -13
- package/dist/services/auditLog.d.ts +17 -2
- package/dist/services/auditLog.js +75 -14
- package/dist/services/bootstrapGating.js +1 -1
- package/dist/services/categoryRules.d.ts +10 -0
- package/dist/services/categoryRules.js +17 -0
- package/dist/services/classificationService.js +7 -5
- package/dist/services/embeddingService.d.ts +27 -11
- package/dist/services/embeddingService.js +51 -14
- package/dist/services/feedbackStorage.d.ts +39 -0
- package/dist/services/feedbackStorage.js +88 -0
- package/dist/services/handlers/instructions.add.js +429 -317
- package/dist/services/handlers/instructions.groom.js +128 -31
- package/dist/services/handlers/instructions.import.js +56 -23
- package/dist/services/handlers/instructions.patch.js +43 -32
- package/dist/services/handlers/instructions.query.js +20 -29
- package/dist/services/handlers/instructions.shared.d.ts +54 -0
- package/dist/services/handlers/instructions.shared.js +126 -1
- package/dist/services/handlers.activation.js +83 -81
- package/dist/services/handlers.dashboardConfig.d.ts +2 -2
- package/dist/services/handlers.dashboardConfig.js +1 -2
- package/dist/services/handlers.diagnostics.js +75 -54
- package/dist/services/handlers.feedback.d.ts +4 -11
- package/dist/services/handlers.feedback.js +11 -333
- package/dist/services/handlers.gates.js +69 -37
- package/dist/services/handlers.graph.js +2 -2
- package/dist/services/handlers.help.js +2 -2
- package/dist/services/handlers.instructionSchema.js +4 -2
- package/dist/services/handlers.integrity.js +42 -22
- package/dist/services/handlers.messaging.js +1 -1
- package/dist/services/handlers.metrics.js +51 -6
- package/dist/services/handlers.prompt.js +10 -2
- package/dist/services/handlers.search.js +94 -44
- package/dist/services/handlers.trace.js +1 -1
- package/dist/services/handlers.usage.js +38 -7
- package/dist/services/indexContext.d.ts +21 -1
- package/dist/services/indexContext.js +263 -78
- package/dist/services/indexLoader.d.ts +1 -0
- package/dist/services/indexLoader.js +28 -8
- package/dist/services/instructionRecordValidation.d.ts +39 -0
- package/dist/services/instructionRecordValidation.js +388 -0
- package/dist/services/instructions.dispatcher.js +4 -4
- package/dist/services/loaderSchemaValidator.d.ts +15 -0
- package/dist/services/loaderSchemaValidator.js +69 -0
- package/dist/services/logger.js +11 -2
- package/dist/services/mcpLogBridge.d.ts +49 -0
- package/dist/services/mcpLogBridge.js +83 -0
- package/dist/services/ownershipService.js +18 -8
- package/dist/services/performanceBaseline.js +23 -22
- package/dist/services/promptReviewService.d.ts +3 -1
- package/dist/services/promptReviewService.js +41 -13
- package/dist/services/regexSafety.d.ts +6 -0
- package/dist/services/regexSafety.js +46 -0
- package/dist/services/seedBootstrap.js +1 -1
- package/dist/services/storage/factory.d.ts +14 -1
- package/dist/services/storage/factory.js +61 -1
- package/dist/services/storage/jsonEmbeddingStore.d.ts +15 -0
- package/dist/services/storage/jsonEmbeddingStore.js +83 -0
- package/dist/services/storage/jsonFileStore.d.ts +3 -1
- package/dist/services/storage/jsonFileStore.js +8 -6
- package/dist/services/storage/migrationEngine.d.ts +13 -0
- package/dist/services/storage/migrationEngine.js +31 -0
- package/dist/services/storage/sqliteEmbeddingStore.d.ts +30 -0
- package/dist/services/storage/sqliteEmbeddingStore.js +222 -0
- package/dist/services/storage/sqliteStore.d.ts +3 -1
- package/dist/services/storage/sqliteStore.js +2 -2
- package/dist/services/storage/types.d.ts +48 -1
- package/dist/services/toolRegistry.js +77 -67
- package/dist/services/toolRegistry.zod.js +89 -86
- package/dist/services/tracing.js +5 -4
- package/dist/utils/envUtils.d.ts +4 -0
- package/dist/utils/envUtils.js +7 -0
- package/dist/utils/memoryMonitor.js +11 -10
- package/package.json +12 -4
- package/schemas/instruction.schema.json +38 -1
- package/scripts/copy-dashboard-assets.mjs +1 -1
- package/scripts/dist/README.md +1 -1
- package/scripts/generate-certs.mjs +201 -0
- package/scripts/setup-wizard.mjs +781 -0
- package/server.json +20 -0
- package/dist/externalClientLib.d.ts +0 -1
- package/dist/externalClientLib.js +0 -2
- package/dist/portableClientWrapper.d.ts +0 -1
- package/dist/portableClientWrapper.js +0 -2
- package/dist/services/indexingService.d.ts +0 -1
- package/dist/services/indexingService.js +0 -2
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP Log Bridge — thin adapter over the generalized McpStdioLogger.
|
|
4
|
+
*
|
|
5
|
+
* Preserves the existing module-level API (`registerMcpServer`, `activateMcpLogBridge`,
|
|
6
|
+
* `isMcpLogBridgeActive`, `sendMcpLog`, `_restoreStderr`) so that all existing
|
|
7
|
+
* call sites in index-server.ts, sdkServer.ts, handshakeManager.ts, and logger.ts
|
|
8
|
+
* continue to work without changes.
|
|
9
|
+
*
|
|
10
|
+
* The actual stderr interception, buffering, replay, and MCP protocol routing is
|
|
11
|
+
* delegated to `McpStdioLogger` from `../lib/mcpStdioLogging`, which is a
|
|
12
|
+
* self-contained, reusable module for any MCP stdio server.
|
|
13
|
+
*
|
|
14
|
+
* See `src/lib/mcpStdioLogging.ts` for the generalized implementation and
|
|
15
|
+
* `docs/mcp_stdio_logging.md` for integration guidance.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.registerMcpServer = registerMcpServer;
|
|
19
|
+
exports.activateMcpLogBridge = activateMcpLogBridge;
|
|
20
|
+
exports.isMcpLogBridgeActive = isMcpLogBridgeActive;
|
|
21
|
+
exports.sendMcpLog = sendMcpLog;
|
|
22
|
+
exports.writeRealStderr = writeRealStderr;
|
|
23
|
+
exports._restoreStderr = _restoreStderr;
|
|
24
|
+
const mcpStdioLogging_1 = require("../lib/mcpStdioLogging");
|
|
25
|
+
const LEVEL_MAP = {
|
|
26
|
+
TRACE: 'debug',
|
|
27
|
+
DEBUG: 'debug',
|
|
28
|
+
INFO: 'info',
|
|
29
|
+
WARN: 'warning',
|
|
30
|
+
ERROR: 'error',
|
|
31
|
+
};
|
|
32
|
+
// Singleton instance — intercepts stderr immediately on module load.
|
|
33
|
+
const _logger = new mcpStdioLogging_1.McpStdioLogger({
|
|
34
|
+
serverName: 'index-server',
|
|
35
|
+
interceptImmediately: process.env.INDEX_SERVER_DISABLE_STDERR_BRIDGE !== '1',
|
|
36
|
+
maxBufferSize: 500,
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Register the SDK server instance. Called once after server creation.
|
|
40
|
+
* Does NOT activate the bridge — call `activateMcpLogBridge()` after ready.
|
|
41
|
+
*/
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
function registerMcpServer(server) {
|
|
44
|
+
_logger.registerServer(server);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Activate the bridge so subsequent log calls are routed via MCP protocol.
|
|
48
|
+
* Replays any buffered pre-handshake stderr lines through the protocol.
|
|
49
|
+
* Called from `emitReadyGlobal()` after the handshake completes.
|
|
50
|
+
*/
|
|
51
|
+
function activateMcpLogBridge() {
|
|
52
|
+
_logger.activate();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns true if the bridge is active and logs will be sent via MCP protocol.
|
|
56
|
+
*/
|
|
57
|
+
function isMcpLogBridgeActive() {
|
|
58
|
+
return _logger.isActive;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Send a log message through the MCP `notifications/message` protocol.
|
|
62
|
+
* No-op if the bridge is not yet active.
|
|
63
|
+
*
|
|
64
|
+
* @param level - The index-server log level (TRACE, DEBUG, INFO, WARN, ERROR)
|
|
65
|
+
* @param data - The log payload (typically the NDJSON string)
|
|
66
|
+
*/
|
|
67
|
+
function sendMcpLog(level, data) {
|
|
68
|
+
_logger.log(LEVEL_MAP[level] ?? 'info', data);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Write directly to the original process.stderr, bypassing the interceptor.
|
|
72
|
+
* Use from logger.ts to ensure VS Code Output panel always has content.
|
|
73
|
+
*/
|
|
74
|
+
function writeRealStderr(data) {
|
|
75
|
+
_logger.writeOriginalStderr(data);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Restore original stderr and deactivate the bridge.
|
|
79
|
+
* Intended for testing cleanup only.
|
|
80
|
+
*/
|
|
81
|
+
function _restoreStderr() {
|
|
82
|
+
_logger.restore();
|
|
83
|
+
}
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.resolveOwner = resolveOwner;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const logger_js_1 = require("./logger.js");
|
|
10
|
+
const regexSafety_js_1 = require("./regexSafety.js");
|
|
9
11
|
let cached = null;
|
|
10
12
|
function loadRules() {
|
|
11
13
|
const file = path_1.default.join(process.cwd(), 'owners.json');
|
|
@@ -13,8 +15,20 @@ function loadRules() {
|
|
|
13
15
|
const stat = fs_1.default.statSync(file);
|
|
14
16
|
if (cached && cached.mtimeMs === stat.mtimeMs)
|
|
15
17
|
return cached.rules;
|
|
16
|
-
const raw = JSON.parse(fs_1.default.readFileSync(file, 'utf8'));
|
|
17
|
-
const rules = Array.isArray(raw.ownership)
|
|
18
|
+
const raw = JSON.parse(fs_1.default.readFileSync(file, 'utf8')); // lgtm[js/file-system-race] — file path is cwd-resolved owners.json; mtime-checked above for cache invalidation
|
|
19
|
+
const rules = Array.isArray(raw.ownership)
|
|
20
|
+
? raw.ownership.flatMap((rule) => {
|
|
21
|
+
if (!rule || typeof rule.pattern !== 'string' || typeof rule.owner !== 'string') {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const { regex, error } = (0, regexSafety_js_1.compileSafeRegex)(rule.pattern);
|
|
25
|
+
if (!regex) {
|
|
26
|
+
(0, logger_js_1.logWarn)(`[ownershipService] Skipping ownership rule for "${rule.owner}": ${error}`);
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
return [{ owner: rule.owner, regex }];
|
|
30
|
+
})
|
|
31
|
+
: [];
|
|
18
32
|
cached = { mtimeMs: stat.mtimeMs, rules };
|
|
19
33
|
return rules;
|
|
20
34
|
}
|
|
@@ -25,12 +39,8 @@ function loadRules() {
|
|
|
25
39
|
function resolveOwner(id) {
|
|
26
40
|
const rules = loadRules();
|
|
27
41
|
for (const r of rules) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (re.test(id))
|
|
31
|
-
return r.owner;
|
|
32
|
-
}
|
|
33
|
-
catch { /* ignore */ }
|
|
42
|
+
if (r.regex.test(id))
|
|
43
|
+
return r.owner;
|
|
34
44
|
}
|
|
35
45
|
return undefined;
|
|
36
46
|
}
|
|
@@ -10,6 +10,7 @@ const child_process_1 = require("child_process");
|
|
|
10
10
|
const perf_hooks_1 = require("perf_hooks");
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const logger_js_1 = require("./logger.js");
|
|
13
14
|
function startServer(enableUsage) {
|
|
14
15
|
return new Promise((resolve) => {
|
|
15
16
|
const env = {
|
|
@@ -143,37 +144,37 @@ function calculateOverhead(withoutUsage, withUsage) {
|
|
|
143
144
|
return ((usageP95 - baselineP95) / baselineP95) * 100;
|
|
144
145
|
}
|
|
145
146
|
async function runPerformanceBaseline() {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
(0, logger_js_1.logInfo)('🚀 Starting Phase 1 Performance Baseline Measurement...');
|
|
148
|
+
(0, logger_js_1.logInfo)('Target: <5% overhead for usage tracking');
|
|
149
|
+
(0, logger_js_1.logInfo)('---');
|
|
149
150
|
// List operations
|
|
150
|
-
|
|
151
|
+
(0, logger_js_1.logInfo)('📊 Measuring list operations...');
|
|
151
152
|
const listWithoutUsage = await measureListOperations(false);
|
|
152
153
|
const listWithUsage = await measureListOperations(true);
|
|
153
154
|
const listOverhead = calculateOverhead(listWithoutUsage, listWithUsage);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
(0, logger_js_1.logInfo)('✅ List operations measured');
|
|
156
|
+
(0, logger_js_1.logInfo)(` Without usage: ${calculateStats(listWithoutUsage).p95.toFixed(2)}ms P95`);
|
|
157
|
+
(0, logger_js_1.logInfo)(` With usage: ${calculateStats(listWithUsage).p95.toFixed(2)}ms P95`);
|
|
158
|
+
(0, logger_js_1.logInfo)(` Overhead: ${listOverhead.toFixed(2)}%`);
|
|
159
|
+
(0, logger_js_1.logInfo)('---');
|
|
159
160
|
// Mutation operations
|
|
160
|
-
|
|
161
|
+
(0, logger_js_1.logInfo)('📊 Measuring mutation operations...');
|
|
161
162
|
const mutationWithoutUsage = await measureMutationOperations(false);
|
|
162
163
|
const mutationWithUsage = await measureMutationOperations(true);
|
|
163
164
|
const mutationOverhead = calculateOverhead(mutationWithoutUsage, mutationWithUsage);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
(0, logger_js_1.logInfo)('✅ Mutation operations measured');
|
|
166
|
+
(0, logger_js_1.logInfo)(` Without usage: ${calculateStats(mutationWithoutUsage).p95.toFixed(2)}ms P95`);
|
|
167
|
+
(0, logger_js_1.logInfo)(` With usage: ${calculateStats(mutationWithUsage).p95.toFixed(2)}ms P95`);
|
|
168
|
+
(0, logger_js_1.logInfo)(` Overhead: ${mutationOverhead.toFixed(2)}%`);
|
|
169
|
+
(0, logger_js_1.logInfo)('---');
|
|
169
170
|
// Summary
|
|
170
171
|
const maxOverhead = Math.max(listOverhead, mutationOverhead);
|
|
171
172
|
const meetsTarget = maxOverhead <= 5.0;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
173
|
+
(0, logger_js_1.logInfo)('📈 Performance Baseline Summary:');
|
|
174
|
+
(0, logger_js_1.logInfo)(` List operations overhead: ${listOverhead.toFixed(2)}%`);
|
|
175
|
+
(0, logger_js_1.logInfo)(` Mutation operations overhead: ${mutationOverhead.toFixed(2)}%`);
|
|
176
|
+
(0, logger_js_1.logInfo)(` Maximum overhead: ${maxOverhead.toFixed(2)}%`);
|
|
177
|
+
(0, logger_js_1.logInfo)(` Target met (<5%): ${meetsTarget ? '✅ YES' : '❌ NO'}`);
|
|
177
178
|
const results = {
|
|
178
179
|
listOperations: {
|
|
179
180
|
withoutUsage: listWithoutUsage,
|
|
@@ -204,7 +205,7 @@ async function runPerformanceBaseline() {
|
|
|
204
205
|
target: '<5% overhead',
|
|
205
206
|
results
|
|
206
207
|
}, null, 2));
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
(0, logger_js_1.logInfo)('---');
|
|
209
|
+
(0, logger_js_1.logInfo)(`💾 Detailed results saved to: ${baselineFile}`);
|
|
209
210
|
return results;
|
|
210
211
|
}
|
|
@@ -20,7 +20,7 @@ export interface PromptIssue {
|
|
|
20
20
|
match?: string;
|
|
21
21
|
}
|
|
22
22
|
export declare class PromptReviewService {
|
|
23
|
-
private
|
|
23
|
+
private compiledCategories;
|
|
24
24
|
/**
|
|
25
25
|
* @param criteriaPath - Optional explicit path to a `PROMPT-CRITERIA.json` file.
|
|
26
26
|
* When omitted the service searches a set of standard candidate locations.
|
|
@@ -32,6 +32,8 @@ export declare class PromptReviewService {
|
|
|
32
32
|
* @returns Array of {@link PromptIssue} objects describing detected rule violations; empty when clean
|
|
33
33
|
*/
|
|
34
34
|
review(prompt: string): PromptIssue[];
|
|
35
|
+
private compileCategories;
|
|
36
|
+
private compileRule;
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
37
39
|
* Aggregate a list of prompt issues into per-severity counts and identify the highest severity present.
|
|
@@ -7,8 +7,10 @@ exports.PromptReviewService = void 0;
|
|
|
7
7
|
exports.summarizeIssues = summarizeIssues;
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const logger_js_1 = require("./logger.js");
|
|
11
|
+
const regexSafety_js_1 = require("./regexSafety.js");
|
|
10
12
|
class PromptReviewService {
|
|
11
|
-
|
|
13
|
+
compiledCategories;
|
|
12
14
|
/**
|
|
13
15
|
* @param criteriaPath - Optional explicit path to a `PROMPT-CRITERIA.json` file.
|
|
14
16
|
* When omitted the service searches a set of standard candidate locations.
|
|
@@ -41,14 +43,14 @@ class PromptReviewService {
|
|
|
41
43
|
if (!loaded) {
|
|
42
44
|
// Graceful fallback: empty criteria so server can still start.
|
|
43
45
|
const msg = `[promptReviewService] WARN: Could not locate PROMPT-CRITERIA.json in any candidate paths. Using empty criteria.`;
|
|
44
|
-
// Write to stderr explicitly (
|
|
45
|
-
|
|
46
|
+
// Write to stderr explicitly (logWarn writes to stderr)
|
|
47
|
+
(0, logger_js_1.logWarn)(msg);
|
|
46
48
|
loaded = { version: '0.0.0', categories: [] };
|
|
47
49
|
}
|
|
48
50
|
else {
|
|
49
|
-
|
|
51
|
+
(0, logger_js_1.logInfo)(`[promptReviewService] Loaded criteria from ${usedPath}`); // stderr so it won't pollute stdout
|
|
50
52
|
}
|
|
51
|
-
this.
|
|
53
|
+
this.compiledCategories = this.compileCategories(loaded);
|
|
52
54
|
}
|
|
53
55
|
/**
|
|
54
56
|
* Run all loaded criteria rules against a prompt string and return any detected issues.
|
|
@@ -57,19 +59,17 @@ class PromptReviewService {
|
|
|
57
59
|
*/
|
|
58
60
|
review(prompt) {
|
|
59
61
|
const issues = [];
|
|
60
|
-
for (const cat of this.
|
|
62
|
+
for (const cat of this.compiledCategories) {
|
|
61
63
|
for (const rule of cat.rules) {
|
|
62
|
-
if (rule.
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const m = prompt.match(regex);
|
|
64
|
+
if (rule.patternRegex) {
|
|
65
|
+
rule.patternRegex.lastIndex = 0;
|
|
66
|
+
const m = prompt.match(rule.patternRegex);
|
|
66
67
|
if (m) {
|
|
67
68
|
issues.push({ ruleId: rule.id, severity: rule.severity, description: rule.description, match: m[0] });
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
|
-
if (rule.
|
|
71
|
-
|
|
72
|
-
if (!mc.test(prompt)) {
|
|
71
|
+
if (rule.mustContainRegex) {
|
|
72
|
+
if (!rule.mustContainRegex.test(prompt)) {
|
|
73
73
|
issues.push({ ruleId: rule.id, severity: rule.severity, description: 'Missing required token(s): ' + rule.description });
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -77,6 +77,34 @@ class PromptReviewService {
|
|
|
77
77
|
}
|
|
78
78
|
return issues;
|
|
79
79
|
}
|
|
80
|
+
compileCategories(criteria) {
|
|
81
|
+
return criteria.categories.map((category) => ({
|
|
82
|
+
id: category.id,
|
|
83
|
+
rules: category.rules.map((rule) => this.compileRule(category.id, rule)),
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
compileRule(categoryId, rule) {
|
|
87
|
+
const compiled = { ...rule };
|
|
88
|
+
if (rule.pattern) {
|
|
89
|
+
const { regex, error } = (0, regexSafety_js_1.compileSafeRegex)(rule.pattern, 'gi');
|
|
90
|
+
if (regex) {
|
|
91
|
+
compiled.patternRegex = regex;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
(0, logger_js_1.logWarn)(`[promptReviewService] Skipping unsafe pattern regex for rule "${rule.id}" in category "${categoryId}": ${error}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (rule.mustContain) {
|
|
98
|
+
const { regex, error } = (0, regexSafety_js_1.compileSafeRegex)(rule.mustContain, 'i');
|
|
99
|
+
if (regex) {
|
|
100
|
+
compiled.mustContainRegex = regex;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
(0, logger_js_1.logWarn)(`[promptReviewService] Skipping unsafe mustContain regex for rule "${rule.id}" in category "${categoryId}": ${error}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return compiled;
|
|
107
|
+
}
|
|
80
108
|
}
|
|
81
109
|
exports.PromptReviewService = PromptReviewService;
|
|
82
110
|
/**
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MAX_REGEX_PATTERN_LENGTH = void 0;
|
|
7
|
+
exports.getRegexSafetyError = getRegexSafetyError;
|
|
8
|
+
exports.compileSafeRegex = compileSafeRegex;
|
|
9
|
+
const safe_regex2_1 = __importDefault(require("safe-regex2"));
|
|
10
|
+
exports.MAX_REGEX_PATTERN_LENGTH = 200;
|
|
11
|
+
function getRegexSafetyError(pattern) {
|
|
12
|
+
if (pattern.length > exports.MAX_REGEX_PATTERN_LENGTH) {
|
|
13
|
+
return 'Regex patterns must not exceed 200 characters to prevent ReDoS';
|
|
14
|
+
}
|
|
15
|
+
if (/\([^)]*[+*}]\)[+*{]/.test(pattern)) {
|
|
16
|
+
return 'Regex pattern rejected: nested quantifiers can cause catastrophic backtracking';
|
|
17
|
+
}
|
|
18
|
+
if (/\)[+*}][^(]*\)[+*{]/.test(pattern)) {
|
|
19
|
+
return 'Regex pattern rejected: nested quantifiers can cause catastrophic backtracking';
|
|
20
|
+
}
|
|
21
|
+
if (/\([^)]*\|[^)]*\)[+*]{1,}/.test(pattern)) {
|
|
22
|
+
return 'Regex pattern rejected: alternation with quantifiers can cause catastrophic backtracking';
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
new RegExp(pattern);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return `Invalid regex pattern "${pattern}": check syntax and try again`;
|
|
29
|
+
}
|
|
30
|
+
if (!(0, safe_regex2_1.default)(pattern)) {
|
|
31
|
+
return 'Regex pattern rejected: potentially catastrophic backtracking detected';
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
function compileSafeRegex(pattern, flags) {
|
|
36
|
+
const error = getRegexSafetyError(pattern);
|
|
37
|
+
if (error) {
|
|
38
|
+
return { error };
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return { regex: new RegExp(pattern, flags) };
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return { error: `Invalid regex pattern "${pattern}": check syntax and try again` };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -94,7 +94,7 @@ Or add directly:
|
|
|
94
94
|
|
|
95
95
|
- \`index_groom\` — clean duplicates and stale entries
|
|
96
96
|
- \`index_governanceUpdate\` — deprecate outdated content (don't silently delete)
|
|
97
|
-
- \`
|
|
97
|
+
- \`feedback_submit\` — report issues or request features
|
|
98
98
|
- \`usage_track\` — signal when guidance was helpful or outdated
|
|
99
99
|
|
|
100
100
|
---
|
|
@@ -4,8 +4,13 @@
|
|
|
4
4
|
* Creates the appropriate IInstructionStore implementation based on config.
|
|
5
5
|
* Default: JsonFileStore (json). Experimental: SqliteStore (sqlite).
|
|
6
6
|
*/
|
|
7
|
-
import type { IInstructionStore } from './types.js';
|
|
7
|
+
import type { IInstructionStore, IEmbeddingStore } from './types.js';
|
|
8
8
|
export type StorageBackend = 'json' | 'sqlite';
|
|
9
|
+
/**
|
|
10
|
+
* Check that the current Node.js version meets the minimum requirement.
|
|
11
|
+
* Throws a clear error if the version is too old.
|
|
12
|
+
*/
|
|
13
|
+
export declare function checkNodeVersion(minVersion: string, feature: string): void;
|
|
9
14
|
/**
|
|
10
15
|
* Create a storage backend instance.
|
|
11
16
|
*
|
|
@@ -15,3 +20,11 @@ export type StorageBackend = 'json' | 'sqlite';
|
|
|
15
20
|
* @returns An IInstructionStore implementation.
|
|
16
21
|
*/
|
|
17
22
|
export declare function createStore(backend?: StorageBackend, dir?: string, sqlitePath?: string): IInstructionStore;
|
|
23
|
+
/**
|
|
24
|
+
* Create an embedding store instance.
|
|
25
|
+
*
|
|
26
|
+
* @param backend - Override backend type (default: from config).
|
|
27
|
+
* @param embeddingPath - Override embedding file/db path (default: from config).
|
|
28
|
+
* @returns An IEmbeddingStore implementation.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createEmbeddingStore(backend?: StorageBackend, embeddingPath?: string): IEmbeddingStore;
|
|
@@ -6,10 +6,31 @@
|
|
|
6
6
|
* Default: JsonFileStore (json). Experimental: SqliteStore (sqlite).
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.checkNodeVersion = checkNodeVersion;
|
|
9
10
|
exports.createStore = createStore;
|
|
11
|
+
exports.createEmbeddingStore = createEmbeddingStore;
|
|
10
12
|
const runtimeConfig_js_1 = require("../../config/runtimeConfig.js");
|
|
11
13
|
const jsonFileStore_js_1 = require("./jsonFileStore.js");
|
|
14
|
+
const jsonEmbeddingStore_js_1 = require("./jsonEmbeddingStore.js");
|
|
12
15
|
const sqliteStore_js_1 = require("./sqliteStore.js");
|
|
16
|
+
const logger_js_1 = require("../logger.js");
|
|
17
|
+
/**
|
|
18
|
+
* Check that the current Node.js version meets the minimum requirement.
|
|
19
|
+
* Throws a clear error if the version is too old.
|
|
20
|
+
*/
|
|
21
|
+
function checkNodeVersion(minVersion, feature) {
|
|
22
|
+
const current = process.versions.node;
|
|
23
|
+
const cur = current.split('.').map(Number);
|
|
24
|
+
const min = minVersion.split('.').map(Number);
|
|
25
|
+
for (let i = 0; i < 3; i++) {
|
|
26
|
+
if ((cur[i] ?? 0) > (min[i] ?? 0))
|
|
27
|
+
return;
|
|
28
|
+
if ((cur[i] ?? 0) < (min[i] ?? 0)) {
|
|
29
|
+
throw new Error(`Node.js ${minVersion}+ required for ${feature} (current: ${current}). ` +
|
|
30
|
+
`Please upgrade Node.js or switch to the JSON storage backend.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
13
34
|
/**
|
|
14
35
|
* Create a storage backend instance.
|
|
15
36
|
*
|
|
@@ -24,7 +45,8 @@ function createStore(backend, dir, sqlitePath) {
|
|
|
24
45
|
const resolvedDir = dir ?? config.index?.baseDir;
|
|
25
46
|
switch (resolvedBackend) {
|
|
26
47
|
case 'sqlite': {
|
|
27
|
-
|
|
48
|
+
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.');
|
|
28
50
|
const dbPath = sqlitePath ?? config.storage?.sqlitePath ?? 'data/index.db';
|
|
29
51
|
return new sqliteStore_js_1.SqliteStore(dbPath);
|
|
30
52
|
}
|
|
@@ -33,3 +55,41 @@ function createStore(backend, dir, sqlitePath) {
|
|
|
33
55
|
return new jsonFileStore_js_1.JsonFileStore(resolvedDir);
|
|
34
56
|
}
|
|
35
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Create an embedding store instance.
|
|
60
|
+
*
|
|
61
|
+
* @param backend - Override backend type (default: from config).
|
|
62
|
+
* @param embeddingPath - Override embedding file/db path (default: from config).
|
|
63
|
+
* @returns An IEmbeddingStore implementation.
|
|
64
|
+
*/
|
|
65
|
+
function createEmbeddingStore(backend, embeddingPath) {
|
|
66
|
+
const config = (0, runtimeConfig_js_1.getRuntimeConfig)();
|
|
67
|
+
const resolvedBackend = backend ?? config.storage?.backend ?? 'json';
|
|
68
|
+
switch (resolvedBackend) {
|
|
69
|
+
case 'sqlite': {
|
|
70
|
+
// Check if sqlite-vec is explicitly disabled via config
|
|
71
|
+
if (config.storage?.sqliteVecEnabled === false) {
|
|
72
|
+
const jsonPath = embeddingPath ?? config.semantic?.embeddingPath ?? 'data/embeddings.json';
|
|
73
|
+
return new jsonEmbeddingStore_js_1.JsonEmbeddingStore(jsonPath);
|
|
74
|
+
}
|
|
75
|
+
checkNodeVersion('22.13.0', 'sqlite-vec extension (DatabaseSync.loadExtension)');
|
|
76
|
+
// Lazy-load SqliteEmbeddingStore to avoid import errors when sqlite-vec is unavailable
|
|
77
|
+
try {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
79
|
+
const { SqliteEmbeddingStore } = require('./sqliteEmbeddingStore.js');
|
|
80
|
+
const dbPath = embeddingPath ?? (config.storage?.sqliteVecPath || 'data/embeddings.db');
|
|
81
|
+
return new SqliteEmbeddingStore(dbPath);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
(0, logger_js_1.logWarn)(`[storage] sqlite-vec embedding store failed to initialize: ${err instanceof Error ? err.message : 'unknown'}. Falling back to JSON.`);
|
|
85
|
+
const jsonPath = embeddingPath ?? config.semantic?.embeddingPath ?? 'data/embeddings.json';
|
|
86
|
+
return new jsonEmbeddingStore_js_1.JsonEmbeddingStore(jsonPath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
case 'json':
|
|
90
|
+
default: {
|
|
91
|
+
const jsonPath = embeddingPath ?? config.semantic?.embeddingPath ?? 'data/embeddings.json';
|
|
92
|
+
return new jsonEmbeddingStore_js_1.JsonEmbeddingStore(jsonPath);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JsonEmbeddingStore — IEmbeddingStore backed by a flat JSON file.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the existing embeddings.json disk cache format.
|
|
5
|
+
* Search is brute-force cosine similarity (no indexing).
|
|
6
|
+
*/
|
|
7
|
+
import type { IEmbeddingStore, EmbeddingCacheData, EmbeddingSearchResult } from './types.js';
|
|
8
|
+
export declare class JsonEmbeddingStore implements IEmbeddingStore {
|
|
9
|
+
private filePath;
|
|
10
|
+
constructor(filePath: string);
|
|
11
|
+
load(): EmbeddingCacheData | null;
|
|
12
|
+
save(data: EmbeddingCacheData): void;
|
|
13
|
+
search(queryVector: Float32Array, limit: number): EmbeddingSearchResult[];
|
|
14
|
+
close(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* JsonEmbeddingStore — IEmbeddingStore backed by a flat JSON file.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the existing embeddings.json disk cache format.
|
|
6
|
+
* Search is brute-force cosine similarity (no indexing).
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.JsonEmbeddingStore = void 0;
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
/**
|
|
16
|
+
* Cosine similarity between two numeric arrays.
|
|
17
|
+
*/
|
|
18
|
+
function cosineSimilarity(a, b) {
|
|
19
|
+
let dot = 0;
|
|
20
|
+
let normA = 0;
|
|
21
|
+
let normB = 0;
|
|
22
|
+
for (let i = 0; i < a.length; i++) {
|
|
23
|
+
dot += a[i] * b[i];
|
|
24
|
+
normA += a[i] * a[i];
|
|
25
|
+
normB += b[i] * b[i];
|
|
26
|
+
}
|
|
27
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
28
|
+
if (denom === 0)
|
|
29
|
+
return 0;
|
|
30
|
+
return dot / denom;
|
|
31
|
+
}
|
|
32
|
+
class JsonEmbeddingStore {
|
|
33
|
+
filePath;
|
|
34
|
+
constructor(filePath) {
|
|
35
|
+
this.filePath = filePath;
|
|
36
|
+
}
|
|
37
|
+
load() {
|
|
38
|
+
try {
|
|
39
|
+
if (!fs_1.default.existsSync(this.filePath))
|
|
40
|
+
return null;
|
|
41
|
+
const raw = fs_1.default.readFileSync(this.filePath, 'utf-8');
|
|
42
|
+
const data = JSON.parse(raw);
|
|
43
|
+
// Backwards-compat: older caches used 'catalogHash'
|
|
44
|
+
if (data && typeof data.catalogHash === 'string' && typeof data.indexHash !== 'string') {
|
|
45
|
+
data.indexHash = data.catalogHash;
|
|
46
|
+
delete data.catalogHash;
|
|
47
|
+
}
|
|
48
|
+
if (!data || typeof data.indexHash !== 'string' || typeof data.embeddings !== 'object')
|
|
49
|
+
return null;
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
save(data) {
|
|
57
|
+
const dir = path_1.default.dirname(this.filePath);
|
|
58
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
59
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
fs_1.default.writeFileSync(this.filePath, JSON.stringify(data), 'utf-8');
|
|
62
|
+
}
|
|
63
|
+
search(queryVector, limit) {
|
|
64
|
+
if (limit <= 0)
|
|
65
|
+
return [];
|
|
66
|
+
const cached = this.load();
|
|
67
|
+
if (!cached)
|
|
68
|
+
return [];
|
|
69
|
+
const query = Array.from(queryVector);
|
|
70
|
+
const results = [];
|
|
71
|
+
for (const [id, vec] of Object.entries(cached.embeddings)) {
|
|
72
|
+
const similarity = cosineSimilarity(query, vec);
|
|
73
|
+
// Distance = 1 - similarity (lower is closer)
|
|
74
|
+
results.push({ id, distance: 1 - similarity });
|
|
75
|
+
}
|
|
76
|
+
results.sort((a, b) => a.distance - b.distance);
|
|
77
|
+
return results.slice(0, limit);
|
|
78
|
+
}
|
|
79
|
+
close() {
|
|
80
|
+
// No resources to release for file-based store
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.JsonEmbeddingStore = JsonEmbeddingStore;
|
|
@@ -19,7 +19,9 @@ export declare class JsonFileStore implements IInstructionStore {
|
|
|
19
19
|
load(): LoadResult;
|
|
20
20
|
close(): void;
|
|
21
21
|
get(id: string): InstructionEntry | null;
|
|
22
|
-
write(entry: InstructionEntry
|
|
22
|
+
write(entry: InstructionEntry, opts?: {
|
|
23
|
+
createOnly?: boolean;
|
|
24
|
+
}): void;
|
|
23
25
|
remove(id: string): void;
|
|
24
26
|
list(opts?: ListOptions): InstructionEntry[];
|
|
25
27
|
query(opts: QueryOptions): InstructionEntry[];
|
|
@@ -14,8 +14,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
14
14
|
exports.JsonFileStore = void 0;
|
|
15
15
|
const fs_1 = __importDefault(require("fs"));
|
|
16
16
|
const path_1 = __importDefault(require("path"));
|
|
17
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
18
17
|
const hashUtils_js_1 = require("./hashUtils.js");
|
|
18
|
+
const atomicFs_js_1 = require("../atomicFs.js");
|
|
19
19
|
class JsonFileStore {
|
|
20
20
|
dir;
|
|
21
21
|
cache = new Map();
|
|
@@ -87,12 +87,14 @@ class JsonFileStore {
|
|
|
87
87
|
this.ensureLoaded();
|
|
88
88
|
return this.cache.get(id) ?? null;
|
|
89
89
|
}
|
|
90
|
-
write(entry) {
|
|
91
|
-
// Write to disk
|
|
90
|
+
write(entry, opts) {
|
|
92
91
|
const filePath = path_1.default.join(this.dir, `${entry.id}.json`);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
if (opts?.createOnly) {
|
|
93
|
+
(0, atomicFs_js_1.atomicCreateJson)(filePath, entry);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
(0, atomicFs_js_1.atomicWriteJson)(filePath, entry);
|
|
97
|
+
}
|
|
96
98
|
// Update in-memory cache
|
|
97
99
|
this.cache.set(entry.id, entry);
|
|
98
100
|
this.loaded = true;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Uses Node.js built-in node:sqlite. Zero third-party dependencies.
|
|
5
5
|
*/
|
|
6
|
+
import type { IEmbeddingStore } from './types.js';
|
|
6
7
|
export interface MigrationOptions {
|
|
7
8
|
onProgress?: (current: number, total: number) => void;
|
|
8
9
|
}
|
|
@@ -33,3 +34,15 @@ export declare function migrateJsonToSqlite(jsonDir: string, dbPath: string, opt
|
|
|
33
34
|
* Each entry becomes a separate .json file named by ID.
|
|
34
35
|
*/
|
|
35
36
|
export declare function migrateSqliteToJson(dbPath: string, jsonDir: string, opts?: MigrationOptions): ExportResult;
|
|
37
|
+
export interface EmbeddingMigrationResult {
|
|
38
|
+
migrated: number;
|
|
39
|
+
skipped: number;
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Migrate embeddings from a JSON file to an IEmbeddingStore (e.g. SqliteEmbeddingStore).
|
|
44
|
+
*
|
|
45
|
+
* Reads the JSON embedding cache and saves it into the target store.
|
|
46
|
+
* Idempotent: calling again with the same data overwrites safely.
|
|
47
|
+
*/
|
|
48
|
+
export declare function migrateJsonEmbeddingsToStore(jsonEmbeddingPath: string, targetStore: IEmbeddingStore): EmbeddingMigrationResult;
|