Spaces:
Paused
Paused
Deploy from GitHub Actions 2025-12-15_17-15-49
Browse files- apps/backend/package.json +6 -2
- apps/backend/src/mcp/cognitive/UnifiedMemorySystem.ts +25 -16
- apps/backend/src/mcp/memory/CognitiveMemory.ts +41 -30
- apps/backend/src/mcp/memory/FailureMemory.ts +56 -53
- apps/backend/src/mcp/memory/PatternMemory.ts +57 -49
- apps/backend/src/mcp/memory/StorageAdapter.ts +107 -0
- apps/backend/src/platform/db/PrismaDatabaseAdapter.ts +12 -0
- apps/backend/src/services/NeuralChat/NeuralCortex.ts +44 -44
- apps/backend/src/services/SelfHealingAdapter.ts +21 -2
- apps/backend/src/services/embeddings/LocalGPUEmbeddings.ts +52 -48
- apps/backend/src/tests/autonomous.integration.test.ts +3 -2
- package-lock.json +663 -0
apps/backend/package.json
CHANGED
|
@@ -10,6 +10,7 @@
|
|
| 10 |
"build:tsc": "tsc",
|
| 11 |
"start": "node dist/index.js",
|
| 12 |
"test": "vitest run",
|
|
|
|
| 13 |
"neural-bridge": "tsx src/mcp/servers/NeuralBridgeServer.ts",
|
| 14 |
"neural-bridge:build": "tsc && node dist/mcp/servers/NeuralBridgeServer.js",
|
| 15 |
"ingest-drive": "tsx src/scripts/ingest-drive.ts"
|
|
@@ -69,7 +70,6 @@
|
|
| 69 |
"zod": "^3.25.76"
|
| 70 |
},
|
| 71 |
"devDependencies": {
|
| 72 |
-
"esbuild": "^0.24.2",
|
| 73 |
"@types/cors": "^2.8.17",
|
| 74 |
"@types/express": "^4.17.21",
|
| 75 |
"@types/imap": "^0.8.40",
|
|
@@ -84,7 +84,11 @@
|
|
| 84 |
"@types/uuid": "^9.0.7",
|
| 85 |
"@types/ws": "^8.5.10",
|
| 86 |
"@types/xml2js": "^0.4.14",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
"tsx": "^4.20.6",
|
| 88 |
"typescript": "~5.8.2"
|
| 89 |
}
|
| 90 |
-
}
|
|
|
|
| 10 |
"build:tsc": "tsc",
|
| 11 |
"start": "node dist/index.js",
|
| 12 |
"test": "vitest run",
|
| 13 |
+
"lint": "eslint .",
|
| 14 |
"neural-bridge": "tsx src/mcp/servers/NeuralBridgeServer.ts",
|
| 15 |
"neural-bridge:build": "tsc && node dist/mcp/servers/NeuralBridgeServer.js",
|
| 16 |
"ingest-drive": "tsx src/scripts/ingest-drive.ts"
|
|
|
|
| 70 |
"zod": "^3.25.76"
|
| 71 |
},
|
| 72 |
"devDependencies": {
|
|
|
|
| 73 |
"@types/cors": "^2.8.17",
|
| 74 |
"@types/express": "^4.17.21",
|
| 75 |
"@types/imap": "^0.8.40",
|
|
|
|
| 84 |
"@types/uuid": "^9.0.7",
|
| 85 |
"@types/ws": "^8.5.10",
|
| 86 |
"@types/xml2js": "^0.4.14",
|
| 87 |
+
"@typescript-eslint/eslint-plugin": "^7.16.0",
|
| 88 |
+
"@typescript-eslint/parser": "^7.16.0",
|
| 89 |
+
"esbuild": "^0.24.2",
|
| 90 |
+
"eslint": "^8.57.0",
|
| 91 |
"tsx": "^4.20.6",
|
| 92 |
"typescript": "~5.8.2"
|
| 93 |
}
|
| 94 |
+
}
|
apps/backend/src/mcp/cognitive/UnifiedMemorySystem.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
| 3 |
// Integrates existing repositories (CMA, SRAG, PAL, Evolution, ProjectMemory)
|
| 4 |
|
| 5 |
import { getCognitiveMemory, initCognitiveMemory, CognitiveMemory } from '../memory/CognitiveMemory.js';
|
| 6 |
-
import { getDatabase } from '../../database/index.js';
|
|
|
|
|
|
|
| 7 |
import { MemoryRepository } from '../../services/memory/memoryRepository.js';
|
| 8 |
import { SragRepository } from '../../services/srag/sragRepository.js';
|
| 9 |
import { PalRepository } from '../../services/pal/palRepository.js';
|
|
@@ -67,8 +69,15 @@ export class UnifiedMemorySystem {
|
|
| 67 |
|
| 68 |
// New init method to be called after DB is ready
|
| 69 |
public init() {
|
| 70 |
-
const
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
this.cognitive = getCognitiveMemory();
|
| 73 |
this.proceduralMemory = new ProductionRuleEngine(this.cognitive);
|
| 74 |
}
|
|
@@ -79,8 +88,8 @@ export class UnifiedMemorySystem {
|
|
| 79 |
if (!this.workingMemory.has(key)) {
|
| 80 |
const events = projectMemory.getLifecycleEvents(20);
|
| 81 |
const features = projectMemory.getFeatures();
|
| 82 |
-
this.workingMemory.set(key, {
|
| 83 |
-
recentEvents: events,
|
| 84 |
recentFeatures: features,
|
| 85 |
widgetStates: {},
|
| 86 |
userMood: { sentiment: 'neutral', arousal: 0.5, lastUpdated: Date.now() }
|
|
@@ -93,10 +102,10 @@ export class UnifiedMemorySystem {
|
|
| 93 |
async updateWidgetState(ctx: McpContext, widgetId: string, state: any): Promise<void> {
|
| 94 |
const wm = await this.getWorkingMemory(ctx);
|
| 95 |
wm.widgetStates[widgetId] = { ...state, lastUpdated: Date.now() };
|
| 96 |
-
|
| 97 |
// Trigger holographic analysis when state changes
|
| 98 |
const patterns = await this.findHolographicPatterns(ctx);
|
| 99 |
-
|
| 100 |
// Opdater adaptivt layout baseret på mønstre
|
| 101 |
this.updateAdaptiveLayout(wm, patterns);
|
| 102 |
}
|
|
@@ -107,7 +116,7 @@ export class UnifiedMemorySystem {
|
|
| 107 |
const state = this.workingMemory.get(key);
|
| 108 |
if (state) {
|
| 109 |
state.recentEvents = [...(state.recentEvents || []), result];
|
| 110 |
-
|
| 111 |
// Simuleret humør-analyse baseret på interaktion
|
| 112 |
// Hvis resultatet er en fejl -> stress op
|
| 113 |
if (result?.error) {
|
|
@@ -117,7 +126,7 @@ export class UnifiedMemorySystem {
|
|
| 117 |
// Reset langsomt mod neutral
|
| 118 |
state.userMood.arousal = Math.max(0.2, state.userMood.arousal - 0.05);
|
| 119 |
}
|
| 120 |
-
|
| 121 |
this.workingMemory.set(key, state);
|
| 122 |
}
|
| 123 |
}
|
|
@@ -125,14 +134,14 @@ export class UnifiedMemorySystem {
|
|
| 125 |
/** Enrich an incoming MCPMessage with memory context */
|
| 126 |
async enrichMCPRequest(message: any, ctx: McpContext): Promise<any> {
|
| 127 |
const wm = await this.getWorkingMemory(ctx);
|
| 128 |
-
return {
|
| 129 |
-
...message,
|
| 130 |
-
memoryContext: {
|
| 131 |
-
recentEvents: wm.recentEvents,
|
| 132 |
recentFeatures: wm.recentFeatures,
|
| 133 |
activeWidgets: wm.widgetStates,
|
| 134 |
systemSuggestion: wm.suggestedLayout
|
| 135 |
-
}
|
| 136 |
};
|
| 137 |
}
|
| 138 |
|
|
@@ -154,7 +163,7 @@ export class UnifiedMemorySystem {
|
|
| 154 |
/** Opdater layout forslag baseret på mønstre og humør */
|
| 155 |
private updateAdaptiveLayout(wm: WorkingMemoryState, patterns: any[]) {
|
| 156 |
// 1. Tjek for kritiske mønstre (Sikkerhed)
|
| 157 |
-
const securityPattern = patterns.find(p =>
|
| 158 |
['threat', 'attack', 'breach', 'password', 'alert'].includes(p.keyword) && p.frequency > 2
|
| 159 |
);
|
| 160 |
|
|
@@ -265,7 +274,7 @@ export class UnifiedMemorySystem {
|
|
| 265 |
private async componentHealth(component: string): Promise<ComponentHealth> {
|
| 266 |
try {
|
| 267 |
if (!this.cognitive || !this.cognitive.getSourceHealth) {
|
| 268 |
-
|
| 269 |
name: component,
|
| 270 |
healthScore: 0.8, // Default optimistic
|
| 271 |
latency: 0,
|
|
|
|
| 3 |
// Integrates existing repositories (CMA, SRAG, PAL, Evolution, ProjectMemory)
|
| 4 |
|
| 5 |
import { getCognitiveMemory, initCognitiveMemory, CognitiveMemory } from '../memory/CognitiveMemory.js';
|
| 6 |
+
import { getDatabase, getSqlJsDatabase } from '../../database/index.js';
|
| 7 |
+
import { getDatabaseAdapter } from '../../platform/db/PrismaDatabaseAdapter.js';
|
| 8 |
+
import { PostgresStorageAdapter } from '../memory/StorageAdapter.js';
|
| 9 |
import { MemoryRepository } from '../../services/memory/memoryRepository.js';
|
| 10 |
import { SragRepository } from '../../services/srag/sragRepository.js';
|
| 11 |
import { PalRepository } from '../../services/pal/palRepository.js';
|
|
|
|
| 69 |
|
| 70 |
// New init method to be called after DB is ready
|
| 71 |
public init() {
|
| 72 |
+
const dbAdapter = getDatabaseAdapter();
|
| 73 |
+
if (dbAdapter.isAvailable()) {
|
| 74 |
+
initCognitiveMemory(new PostgresStorageAdapter(dbAdapter));
|
| 75 |
+
} else {
|
| 76 |
+
const db = getSqlJsDatabase();
|
| 77 |
+
// Note: getSqlJsDatabase returns the raw sql.js instance needed for .exec()
|
| 78 |
+
// If it returns null, CognitiveMemory handles it (memory-only mode)
|
| 79 |
+
initCognitiveMemory(db);
|
| 80 |
+
}
|
| 81 |
this.cognitive = getCognitiveMemory();
|
| 82 |
this.proceduralMemory = new ProductionRuleEngine(this.cognitive);
|
| 83 |
}
|
|
|
|
| 88 |
if (!this.workingMemory.has(key)) {
|
| 89 |
const events = projectMemory.getLifecycleEvents(20);
|
| 90 |
const features = projectMemory.getFeatures();
|
| 91 |
+
this.workingMemory.set(key, {
|
| 92 |
+
recentEvents: events,
|
| 93 |
recentFeatures: features,
|
| 94 |
widgetStates: {},
|
| 95 |
userMood: { sentiment: 'neutral', arousal: 0.5, lastUpdated: Date.now() }
|
|
|
|
| 102 |
async updateWidgetState(ctx: McpContext, widgetId: string, state: any): Promise<void> {
|
| 103 |
const wm = await this.getWorkingMemory(ctx);
|
| 104 |
wm.widgetStates[widgetId] = { ...state, lastUpdated: Date.now() };
|
| 105 |
+
|
| 106 |
// Trigger holographic analysis when state changes
|
| 107 |
const patterns = await this.findHolographicPatterns(ctx);
|
| 108 |
+
|
| 109 |
// Opdater adaptivt layout baseret på mønstre
|
| 110 |
this.updateAdaptiveLayout(wm, patterns);
|
| 111 |
}
|
|
|
|
| 116 |
const state = this.workingMemory.get(key);
|
| 117 |
if (state) {
|
| 118 |
state.recentEvents = [...(state.recentEvents || []), result];
|
| 119 |
+
|
| 120 |
// Simuleret humør-analyse baseret på interaktion
|
| 121 |
// Hvis resultatet er en fejl -> stress op
|
| 122 |
if (result?.error) {
|
|
|
|
| 126 |
// Reset langsomt mod neutral
|
| 127 |
state.userMood.arousal = Math.max(0.2, state.userMood.arousal - 0.05);
|
| 128 |
}
|
| 129 |
+
|
| 130 |
this.workingMemory.set(key, state);
|
| 131 |
}
|
| 132 |
}
|
|
|
|
| 134 |
/** Enrich an incoming MCPMessage with memory context */
|
| 135 |
async enrichMCPRequest(message: any, ctx: McpContext): Promise<any> {
|
| 136 |
const wm = await this.getWorkingMemory(ctx);
|
| 137 |
+
return {
|
| 138 |
+
...message,
|
| 139 |
+
memoryContext: {
|
| 140 |
+
recentEvents: wm.recentEvents,
|
| 141 |
recentFeatures: wm.recentFeatures,
|
| 142 |
activeWidgets: wm.widgetStates,
|
| 143 |
systemSuggestion: wm.suggestedLayout
|
| 144 |
+
}
|
| 145 |
};
|
| 146 |
}
|
| 147 |
|
|
|
|
| 163 |
/** Opdater layout forslag baseret på mønstre og humør */
|
| 164 |
private updateAdaptiveLayout(wm: WorkingMemoryState, patterns: any[]) {
|
| 165 |
// 1. Tjek for kritiske mønstre (Sikkerhed)
|
| 166 |
+
const securityPattern = patterns.find(p =>
|
| 167 |
['threat', 'attack', 'breach', 'password', 'alert'].includes(p.keyword) && p.frequency > 2
|
| 168 |
);
|
| 169 |
|
|
|
|
| 274 |
private async componentHealth(component: string): Promise<ComponentHealth> {
|
| 275 |
try {
|
| 276 |
if (!this.cognitive || !this.cognitive.getSourceHealth) {
|
| 277 |
+
return {
|
| 278 |
name: component,
|
| 279 |
healthScore: 0.8, // Default optimistic
|
| 280 |
latency: 0,
|
apps/backend/src/mcp/memory/CognitiveMemory.ts
CHANGED
|
@@ -15,10 +15,11 @@
|
|
| 15 |
* ╚══════════════════════════════════════════════════════════════════════════════╝
|
| 16 |
*/
|
| 17 |
|
|
|
|
|
|
|
| 18 |
import type { Database } from 'sql.js';
|
| 19 |
import { PatternMemory, UsagePattern, PatternStatistics } from './PatternMemory.js';
|
| 20 |
import { FailureMemory, Failure, RecoveryPath, FailureStatistics, SourceHealthSummary } from './FailureMemory.js';
|
| 21 |
-
import { queryAll, execute, queryOne } from './SqlJsCompat.js';
|
| 22 |
import { logger } from '../../utils/logger.js';
|
| 23 |
import { eventBus, type EventType } from '../EventBus.js';
|
| 24 |
|
|
@@ -63,7 +64,7 @@ export interface CognitiveStats {
|
|
| 63 |
failures: FailureStatistics;
|
| 64 |
healthRecords: number;
|
| 65 |
initialized: boolean;
|
| 66 |
-
persistenceMode: 'sqlite' | 'memory';
|
| 67 |
}
|
| 68 |
|
| 69 |
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -74,19 +75,28 @@ export class CognitiveMemory {
|
|
| 74 |
public readonly patternMemory: PatternMemory;
|
| 75 |
public readonly failureMemory: FailureMemory;
|
| 76 |
|
| 77 |
-
private
|
| 78 |
private healthHistory: Map<string, HealthMetrics[]> = new Map();
|
| 79 |
private readonly MAX_HEALTH_RECORDS = 1000;
|
| 80 |
private initialized: boolean = false;
|
| 81 |
-
private persistenceMode: 'sqlite' | 'memory' = 'memory';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
|
| 83 |
-
|
| 84 |
-
this.
|
| 85 |
-
this.failureMemory = new FailureMemory(database);
|
| 86 |
|
| 87 |
-
if (
|
| 88 |
-
this.db = database;
|
| 89 |
-
this.persistenceMode = 'sqlite';
|
| 90 |
this.initializeHealthTable();
|
| 91 |
}
|
| 92 |
|
|
@@ -98,11 +108,14 @@ export class CognitiveMemory {
|
|
| 98 |
/**
|
| 99 |
* Initialize health metrics table for persistence
|
| 100 |
*/
|
| 101 |
-
private initializeHealthTable(): void {
|
| 102 |
-
if (!this.
|
| 103 |
|
| 104 |
try {
|
| 105 |
-
this.
|
|
|
|
|
|
|
|
|
|
| 106 |
CREATE TABLE IF NOT EXISTS mcp_source_health (
|
| 107 |
id TEXT PRIMARY KEY,
|
| 108 |
source_name TEXT NOT NULL,
|
|
@@ -113,11 +126,11 @@ export class CognitiveMemory {
|
|
| 113 |
success_rate REAL NOT NULL,
|
| 114 |
request_count INTEGER NOT NULL,
|
| 115 |
error_count INTEGER NOT NULL,
|
| 116 |
-
timestamp
|
| 117 |
)
|
| 118 |
`);
|
| 119 |
|
| 120 |
-
this.
|
| 121 |
ON mcp_source_health(source_name, timestamp DESC)`);
|
| 122 |
|
| 123 |
logger.debug('📊 Health metrics table initialized');
|
|
@@ -303,10 +316,10 @@ export class CognitiveMemory {
|
|
| 303 |
}
|
| 304 |
|
| 305 |
// Persist to database
|
| 306 |
-
if (this.
|
| 307 |
try {
|
| 308 |
const id = `${sourceName}-${Date.now()}`;
|
| 309 |
-
execute(
|
| 310 |
INSERT INTO mcp_source_health
|
| 311 |
(id, source_name, health_score, latency_p50, latency_p95, latency_p99,
|
| 312 |
success_rate, request_count, error_count, timestamp)
|
|
@@ -340,9 +353,9 @@ export class CognitiveMemory {
|
|
| 340 |
limit: number = 100
|
| 341 |
): Promise<HealthMetrics[]> {
|
| 342 |
// Try database first
|
| 343 |
-
if (this.
|
| 344 |
try {
|
| 345 |
-
const rows = queryAll<{
|
| 346 |
source_name: string;
|
| 347 |
health_score: number;
|
| 348 |
latency_p50: number;
|
|
@@ -352,7 +365,7 @@ export class CognitiveMemory {
|
|
| 352 |
request_count: number;
|
| 353 |
error_count: number;
|
| 354 |
timestamp: string;
|
| 355 |
-
}>(
|
| 356 |
SELECT * FROM mcp_source_health
|
| 357 |
WHERE source_name = ?
|
| 358 |
ORDER BY timestamp DESC
|
|
@@ -513,7 +526,7 @@ export class CognitiveMemory {
|
|
| 513 |
* Clean old data (maintenance)
|
| 514 |
*/
|
| 515 |
async cleanup(retentionDays: number = 30): Promise<void> {
|
| 516 |
-
if (!this.
|
| 517 |
logger.debug('🧹 Cognitive memory cleanup (in-memory - automatic via limits)');
|
| 518 |
return;
|
| 519 |
}
|
|
@@ -522,12 +535,10 @@ export class CognitiveMemory {
|
|
| 522 |
const cutoffDate = new Date();
|
| 523 |
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
|
| 524 |
|
| 525 |
-
|
| 526 |
DELETE FROM mcp_source_health
|
| 527 |
WHERE timestamp < ?
|
| 528 |
-
|
| 529 |
-
stmt.run([cutoffDate.toISOString()]);
|
| 530 |
-
stmt.free();
|
| 531 |
|
| 532 |
logger.info(`🧹 Cleaned health records older than ${retentionDays} days`);
|
| 533 |
} catch (error) {
|
|
@@ -542,12 +553,12 @@ export class CognitiveMemory {
|
|
| 542 |
|
| 543 |
let instance: CognitiveMemory | null = null;
|
| 544 |
|
| 545 |
-
export function initCognitiveMemory(
|
| 546 |
if (!instance) {
|
| 547 |
-
instance = new CognitiveMemory(
|
| 548 |
-
} else if (
|
| 549 |
-
// Upgrade to
|
| 550 |
-
instance = new CognitiveMemory(
|
| 551 |
}
|
| 552 |
return instance;
|
| 553 |
}
|
|
|
|
| 15 |
* ╚══════════════════════════════════════════════════════════════════════════════╝
|
| 16 |
*/
|
| 17 |
|
| 18 |
+
import type { StorageAdapter } from './StorageAdapter.js';
|
| 19 |
+
import { SqlJsStorageAdapter } from './StorageAdapter.js';
|
| 20 |
import type { Database } from 'sql.js';
|
| 21 |
import { PatternMemory, UsagePattern, PatternStatistics } from './PatternMemory.js';
|
| 22 |
import { FailureMemory, Failure, RecoveryPath, FailureStatistics, SourceHealthSummary } from './FailureMemory.js';
|
|
|
|
| 23 |
import { logger } from '../../utils/logger.js';
|
| 24 |
import { eventBus, type EventType } from '../EventBus.js';
|
| 25 |
|
|
|
|
| 64 |
failures: FailureStatistics;
|
| 65 |
healthRecords: number;
|
| 66 |
initialized: boolean;
|
| 67 |
+
persistenceMode: 'sqlite' | 'postgres' | 'memory';
|
| 68 |
}
|
| 69 |
|
| 70 |
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
| 75 |
public readonly patternMemory: PatternMemory;
|
| 76 |
public readonly failureMemory: FailureMemory;
|
| 77 |
|
| 78 |
+
private storage: StorageAdapter | null = null;
|
| 79 |
private healthHistory: Map<string, HealthMetrics[]> = new Map();
|
| 80 |
private readonly MAX_HEALTH_RECORDS = 1000;
|
| 81 |
private initialized: boolean = false;
|
| 82 |
+
private persistenceMode: 'sqlite' | 'postgres' | 'memory' = 'memory';
|
| 83 |
+
|
| 84 |
+
constructor(storageOrDb?: StorageAdapter | Database) {
|
| 85 |
+
if (storageOrDb) {
|
| 86 |
+
if ('exec' in storageOrDb || 'run' in storageOrDb) {
|
| 87 |
+
// It's a Database
|
| 88 |
+
this.storage = new SqlJsStorageAdapter(storageOrDb as Database);
|
| 89 |
+
} else {
|
| 90 |
+
// It's a StorageAdapter
|
| 91 |
+
this.storage = storageOrDb as StorageAdapter;
|
| 92 |
+
}
|
| 93 |
+
this.persistenceMode = this.storage.mode;
|
| 94 |
+
}
|
| 95 |
|
| 96 |
+
this.patternMemory = new PatternMemory(this.storage || undefined);
|
| 97 |
+
this.failureMemory = new FailureMemory(this.storage || undefined);
|
|
|
|
| 98 |
|
| 99 |
+
if (this.storage) {
|
|
|
|
|
|
|
| 100 |
this.initializeHealthTable();
|
| 101 |
}
|
| 102 |
|
|
|
|
| 108 |
/**
|
| 109 |
* Initialize health metrics table for persistence
|
| 110 |
*/
|
| 111 |
+
private async initializeHealthTable(): Promise<void> {
|
| 112 |
+
if (!this.storage) return;
|
| 113 |
|
| 114 |
try {
|
| 115 |
+
const isPostgres = this.storage.mode === 'postgres';
|
| 116 |
+
const timestampType = isPostgres ? 'TIMESTAMP WITH TIME ZONE' : 'DATETIME';
|
| 117 |
+
|
| 118 |
+
await this.storage.execute(`
|
| 119 |
CREATE TABLE IF NOT EXISTS mcp_source_health (
|
| 120 |
id TEXT PRIMARY KEY,
|
| 121 |
source_name TEXT NOT NULL,
|
|
|
|
| 126 |
success_rate REAL NOT NULL,
|
| 127 |
request_count INTEGER NOT NULL,
|
| 128 |
error_count INTEGER NOT NULL,
|
| 129 |
+
timestamp ${timestampType} NOT NULL DEFAULT CURRENT_TIMESTAMP
|
| 130 |
)
|
| 131 |
`);
|
| 132 |
|
| 133 |
+
await this.storage.execute(`CREATE INDEX IF NOT EXISTS idx_source_health_source
|
| 134 |
ON mcp_source_health(source_name, timestamp DESC)`);
|
| 135 |
|
| 136 |
logger.debug('📊 Health metrics table initialized');
|
|
|
|
| 316 |
}
|
| 317 |
|
| 318 |
// Persist to database
|
| 319 |
+
if (this.storage) {
|
| 320 |
try {
|
| 321 |
const id = `${sourceName}-${Date.now()}`;
|
| 322 |
+
await this.storage.execute(`
|
| 323 |
INSERT INTO mcp_source_health
|
| 324 |
(id, source_name, health_score, latency_p50, latency_p95, latency_p99,
|
| 325 |
success_rate, request_count, error_count, timestamp)
|
|
|
|
| 353 |
limit: number = 100
|
| 354 |
): Promise<HealthMetrics[]> {
|
| 355 |
// Try database first
|
| 356 |
+
if (this.storage) {
|
| 357 |
try {
|
| 358 |
+
const rows = await this.storage.queryAll<{
|
| 359 |
source_name: string;
|
| 360 |
health_score: number;
|
| 361 |
latency_p50: number;
|
|
|
|
| 365 |
request_count: number;
|
| 366 |
error_count: number;
|
| 367 |
timestamp: string;
|
| 368 |
+
}>(`
|
| 369 |
SELECT * FROM mcp_source_health
|
| 370 |
WHERE source_name = ?
|
| 371 |
ORDER BY timestamp DESC
|
|
|
|
| 526 |
* Clean old data (maintenance)
|
| 527 |
*/
|
| 528 |
async cleanup(retentionDays: number = 30): Promise<void> {
|
| 529 |
+
if (!this.storage) {
|
| 530 |
logger.debug('🧹 Cognitive memory cleanup (in-memory - automatic via limits)');
|
| 531 |
return;
|
| 532 |
}
|
|
|
|
| 535 |
const cutoffDate = new Date();
|
| 536 |
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
|
| 537 |
|
| 538 |
+
await this.storage.execute(`
|
| 539 |
DELETE FROM mcp_source_health
|
| 540 |
WHERE timestamp < ?
|
| 541 |
+
`, [cutoffDate.toISOString()]);
|
|
|
|
|
|
|
| 542 |
|
| 543 |
logger.info(`🧹 Cleaned health records older than ${retentionDays} days`);
|
| 544 |
} catch (error) {
|
|
|
|
| 553 |
|
| 554 |
let instance: CognitiveMemory | null = null;
|
| 555 |
|
| 556 |
+
export function initCognitiveMemory(storageOrDb?: StorageAdapter | Database): CognitiveMemory {
|
| 557 |
if (!instance) {
|
| 558 |
+
instance = new CognitiveMemory(storageOrDb);
|
| 559 |
+
} else if (storageOrDb && !instance['storage']) {
|
| 560 |
+
// Upgrade to persistent storage if provided later
|
| 561 |
+
instance = new CognitiveMemory(storageOrDb);
|
| 562 |
}
|
| 563 |
return instance;
|
| 564 |
}
|
apps/backend/src/mcp/memory/FailureMemory.ts
CHANGED
|
@@ -14,10 +14,9 @@
|
|
| 14 |
*/
|
| 15 |
|
| 16 |
import { v4 as uuidv4 } from 'uuid';
|
| 17 |
-
import type {
|
| 18 |
import { logger } from '../../utils/logger.js';
|
| 19 |
import { eventBus, type EventType } from '../EventBus.js';
|
| 20 |
-
import { queryAll, queryOne, queryScalar, execute, batchInsert } from './SqlJsCompat.js';
|
| 21 |
|
| 22 |
// ═══════════════════════════════════════════════════════════════════════════════
|
| 23 |
// INTERFACES
|
|
@@ -73,7 +72,7 @@ export interface SourceHealthSummary {
|
|
| 73 |
// ═══════════════════════════════════════════════════════════════════════════════
|
| 74 |
|
| 75 |
export class FailureMemory {
|
| 76 |
-
private
|
| 77 |
private initialized: boolean = false;
|
| 78 |
private writeQueue: Failure[] = [];
|
| 79 |
private flushInterval: ReturnType<typeof setInterval> | null = null;
|
|
@@ -84,30 +83,33 @@ export class FailureMemory {
|
|
| 84 |
private readonly FLUSH_INTERVAL_MS = 3000; // Faster flush for failures
|
| 85 |
private readonly MAX_DB_FAILURES = 50000;
|
| 86 |
|
| 87 |
-
constructor(
|
| 88 |
-
if (
|
| 89 |
-
this.
|
| 90 |
this.initialize();
|
| 91 |
}
|
| 92 |
}
|
| 93 |
|
| 94 |
/**
|
| 95 |
-
* Initialize with
|
| 96 |
*/
|
| 97 |
-
public
|
| 98 |
-
this.
|
| 99 |
this.initialize();
|
| 100 |
}
|
| 101 |
|
| 102 |
/**
|
| 103 |
* Initialize the service
|
| 104 |
*/
|
| 105 |
-
private initialize(): void {
|
| 106 |
-
if (this.initialized || !this.
|
| 107 |
|
| 108 |
try {
|
|
|
|
|
|
|
|
|
|
| 109 |
// Ensure table exists - use exec for DDL statements (more compatible)
|
| 110 |
-
this.
|
| 111 |
CREATE TABLE IF NOT EXISTS mcp_failure_memory (
|
| 112 |
id TEXT PRIMARY KEY,
|
| 113 |
source_name TEXT NOT NULL,
|
|
@@ -118,26 +120,26 @@ export class FailureMemory {
|
|
| 118 |
recovery_action TEXT,
|
| 119 |
recovery_success BOOLEAN,
|
| 120 |
recovery_time_ms INTEGER,
|
| 121 |
-
occurred_at
|
| 122 |
)
|
| 123 |
`);
|
| 124 |
|
| 125 |
// Create indexes
|
| 126 |
-
this.
|
| 127 |
ON mcp_failure_memory(source_name, occurred_at DESC)`);
|
| 128 |
-
this.
|
| 129 |
ON mcp_failure_memory(error_type, occurred_at DESC)`);
|
| 130 |
-
this.
|
| 131 |
ON mcp_failure_memory(recovery_action, recovery_success)`);
|
| 132 |
|
| 133 |
// Load recent failures into cache
|
| 134 |
-
this.loadCacheFromDb();
|
| 135 |
|
| 136 |
// Start background flush
|
| 137 |
this.flushInterval = setInterval(() => this.flushWriteQueue(), this.FLUSH_INTERVAL_MS);
|
| 138 |
|
| 139 |
this.initialized = true;
|
| 140 |
-
logger.info(
|
| 141 |
} catch (error) {
|
| 142 |
logger.error('❌ FailureMemory initialization failed:', error);
|
| 143 |
this.initialized = true;
|
|
@@ -147,11 +149,11 @@ export class FailureMemory {
|
|
| 147 |
/**
|
| 148 |
* Load recent failures from database into cache
|
| 149 |
*/
|
| 150 |
-
private loadCacheFromDb(): void {
|
| 151 |
-
if (!this.
|
| 152 |
|
| 153 |
try {
|
| 154 |
-
const rows = queryAll(
|
| 155 |
SELECT * FROM mcp_failure_memory
|
| 156 |
ORDER BY occurred_at DESC
|
| 157 |
LIMIT ?
|
|
@@ -263,9 +265,10 @@ export class FailureMemory {
|
|
| 263 |
}
|
| 264 |
|
| 265 |
// Update database
|
| 266 |
-
|
|
|
|
| 267 |
try {
|
| 268 |
-
execute(
|
| 269 |
UPDATE mcp_failure_memory
|
| 270 |
SET recovery_action = ?, recovery_success = ?, recovery_time_ms = ?
|
| 271 |
WHERE id = ?
|
|
@@ -286,8 +289,8 @@ export class FailureMemory {
|
|
| 286 |
/**
|
| 287 |
* Flush write queue to database
|
| 288 |
*/
|
| 289 |
-
private flushWriteQueue(): void {
|
| 290 |
-
if (!this.
|
| 291 |
|
| 292 |
const failures = [...this.writeQueue];
|
| 293 |
this.writeQueue = [];
|
|
@@ -306,16 +309,16 @@ export class FailureMemory {
|
|
| 306 |
JSON.stringify(f.errorContext),
|
| 307 |
JSON.stringify(f.queryContext),
|
| 308 |
f.recoveryAction || null,
|
| 309 |
-
f.recoverySuccess != null ?
|
| 310 |
f.recoveryTimeMs || null,
|
| 311 |
f.occurredAt.toISOString()
|
| 312 |
]);
|
| 313 |
|
| 314 |
-
batchInsert(
|
| 315 |
|
| 316 |
// Cleanup old failures periodically
|
| 317 |
if (Math.random() < 0.02) { // 2% chance per flush
|
| 318 |
-
this.cleanupOldFailures();
|
| 319 |
}
|
| 320 |
|
| 321 |
} catch (error) {
|
|
@@ -327,14 +330,14 @@ export class FailureMemory {
|
|
| 327 |
/**
|
| 328 |
* Cleanup old failures
|
| 329 |
*/
|
| 330 |
-
private cleanupOldFailures(): void {
|
| 331 |
-
if (!this.
|
| 332 |
|
| 333 |
try {
|
| 334 |
const ninetyDaysAgo = new Date();
|
| 335 |
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
|
| 336 |
|
| 337 |
-
execute(
|
| 338 |
DELETE FROM mcp_failure_memory
|
| 339 |
WHERE occurred_at < ?
|
| 340 |
AND id NOT IN (
|
|
@@ -357,9 +360,9 @@ export class FailureMemory {
|
|
| 357 |
sourceName: string,
|
| 358 |
limit: number = 50
|
| 359 |
): Promise<Failure[]> {
|
| 360 |
-
if (this.
|
| 361 |
try {
|
| 362 |
-
const rows = queryAll(
|
| 363 |
SELECT * FROM mcp_failure_memory
|
| 364 |
WHERE source_name = ?
|
| 365 |
ORDER BY occurred_at DESC
|
|
@@ -387,15 +390,15 @@ export class FailureMemory {
|
|
| 387 |
sourceName: string,
|
| 388 |
errorType: string
|
| 389 |
): Promise<RecoveryPath[]> {
|
| 390 |
-
if (this.
|
| 391 |
try {
|
| 392 |
-
const rows = queryAll<{
|
| 393 |
recovery_action: string;
|
| 394 |
total: number;
|
| 395 |
successes: number;
|
| 396 |
avg_time: number;
|
| 397 |
last_success: string | null;
|
| 398 |
-
}>(
|
| 399 |
SELECT
|
| 400 |
recovery_action,
|
| 401 |
COUNT(*) as total,
|
|
@@ -487,9 +490,9 @@ export class FailureMemory {
|
|
| 487 |
const cutoff = new Date();
|
| 488 |
cutoff.setMinutes(cutoff.getMinutes() - withinMinutes);
|
| 489 |
|
| 490 |
-
if (this.
|
| 491 |
try {
|
| 492 |
-
const count = queryScalar<number>(
|
| 493 |
SELECT COUNT(*)
|
| 494 |
FROM mcp_failure_memory
|
| 495 |
WHERE source_name = ? AND error_type = ? AND occurred_at > ?
|
|
@@ -519,9 +522,9 @@ export class FailureMemory {
|
|
| 519 |
|
| 520 |
let failures: Failure[] = [];
|
| 521 |
|
| 522 |
-
if (this.
|
| 523 |
try {
|
| 524 |
-
const rows = queryAll(
|
| 525 |
SELECT * FROM mcp_failure_memory
|
| 526 |
WHERE source_name = ? AND occurred_at > ?
|
| 527 |
ORDER BY occurred_at DESC
|
|
@@ -599,36 +602,36 @@ export class FailureMemory {
|
|
| 599 |
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
| 600 |
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
| 601 |
|
| 602 |
-
if (this.
|
| 603 |
try {
|
| 604 |
-
const total = queryScalar<number>(
|
| 605 |
-
const uniqueErrors = queryScalar<number>(
|
| 606 |
-
const uniqueSources = queryScalar<number>(
|
| 607 |
|
| 608 |
-
const recoveryRate = queryScalar<number>(
|
| 609 |
SELECT AVG(CAST(recovery_success AS FLOAT))
|
| 610 |
FROM mcp_failure_memory
|
| 611 |
WHERE recovery_action IS NOT NULL
|
| 612 |
`) || 0;
|
| 613 |
|
| 614 |
-
const avgRecoveryTime = queryScalar<number>(
|
| 615 |
SELECT AVG(recovery_time_ms)
|
| 616 |
FROM mcp_failure_memory
|
| 617 |
WHERE recovery_success = 1
|
| 618 |
`) || 0;
|
| 619 |
|
| 620 |
-
const last24h = queryScalar<number>(
|
| 621 |
'SELECT COUNT(*) FROM mcp_failure_memory WHERE occurred_at > ?',
|
| 622 |
[oneDayAgo.toISOString()]
|
| 623 |
) || 0;
|
| 624 |
|
| 625 |
-
const last7d = queryScalar<number>(
|
| 626 |
'SELECT COUNT(*) FROM mcp_failure_memory WHERE occurred_at > ?',
|
| 627 |
[sevenDaysAgo.toISOString()]
|
| 628 |
) || 0;
|
| 629 |
|
| 630 |
// Top error types
|
| 631 |
-
const topErrorRows = queryAll<{ error_type: string; count: number; recovery_rate: number }>(
|
| 632 |
SELECT
|
| 633 |
error_type,
|
| 634 |
COUNT(*) as count,
|
|
@@ -645,7 +648,7 @@ export class FailureMemory {
|
|
| 645 |
}));
|
| 646 |
|
| 647 |
// Top failing sources
|
| 648 |
-
const topSourceRows = queryAll<{ source_name: string; count: number }>(
|
| 649 |
SELECT source_name, COUNT(*) as count
|
| 650 |
FROM mcp_failure_memory
|
| 651 |
GROUP BY source_name
|
|
@@ -658,12 +661,12 @@ export class FailureMemory {
|
|
| 658 |
}));
|
| 659 |
|
| 660 |
// Recent recoveries
|
| 661 |
-
const recentRecoveryRows = queryAll<{
|
| 662 |
recovery_action: string;
|
| 663 |
recovery_success: number;
|
| 664 |
recovery_time_ms: number;
|
| 665 |
occurred_at: string;
|
| 666 |
-
}>(
|
| 667 |
SELECT recovery_action, recovery_success, recovery_time_ms, occurred_at
|
| 668 |
FROM mcp_failure_memory
|
| 669 |
WHERE recovery_action IS NOT NULL
|
|
@@ -713,7 +716,7 @@ export class FailureMemory {
|
|
| 713 |
* Force flush all pending writes
|
| 714 |
*/
|
| 715 |
async flush(): Promise<void> {
|
| 716 |
-
this.flushWriteQueue();
|
| 717 |
}
|
| 718 |
|
| 719 |
/**
|
|
|
|
| 14 |
*/
|
| 15 |
|
| 16 |
import { v4 as uuidv4 } from 'uuid';
|
| 17 |
+
import type { StorageAdapter } from './StorageAdapter.js';
|
| 18 |
import { logger } from '../../utils/logger.js';
|
| 19 |
import { eventBus, type EventType } from '../EventBus.js';
|
|
|
|
| 20 |
|
| 21 |
// ═══════════════════════════════════════════════════════════════════════════════
|
| 22 |
// INTERFACES
|
|
|
|
| 72 |
// ═══════════════════════════════════════════════════════════════════════════════
|
| 73 |
|
| 74 |
export class FailureMemory {
|
| 75 |
+
private storage: StorageAdapter | null = null;
|
| 76 |
private initialized: boolean = false;
|
| 77 |
private writeQueue: Failure[] = [];
|
| 78 |
private flushInterval: ReturnType<typeof setInterval> | null = null;
|
|
|
|
| 83 |
private readonly FLUSH_INTERVAL_MS = 3000; // Faster flush for failures
|
| 84 |
private readonly MAX_DB_FAILURES = 50000;
|
| 85 |
|
| 86 |
+
constructor(storage?: StorageAdapter) {
|
| 87 |
+
if (storage) {
|
| 88 |
+
this.storage = storage;
|
| 89 |
this.initialize();
|
| 90 |
}
|
| 91 |
}
|
| 92 |
|
| 93 |
/**
|
| 94 |
+
* Initialize with storage adapter
|
| 95 |
*/
|
| 96 |
+
public setStorage(storage: StorageAdapter): void {
|
| 97 |
+
this.storage = storage;
|
| 98 |
this.initialize();
|
| 99 |
}
|
| 100 |
|
| 101 |
/**
|
| 102 |
* Initialize the service
|
| 103 |
*/
|
| 104 |
+
private async initialize(): Promise<void> {
|
| 105 |
+
if (this.initialized || !this.storage) return;
|
| 106 |
|
| 107 |
try {
|
| 108 |
+
const isPostgres = this.storage.mode === 'postgres';
|
| 109 |
+
const timestampType = isPostgres ? 'TIMESTAMP WITH TIME ZONE' : 'DATETIME';
|
| 110 |
+
|
| 111 |
// Ensure table exists - use exec for DDL statements (more compatible)
|
| 112 |
+
await this.storage.execute(`
|
| 113 |
CREATE TABLE IF NOT EXISTS mcp_failure_memory (
|
| 114 |
id TEXT PRIMARY KEY,
|
| 115 |
source_name TEXT NOT NULL,
|
|
|
|
| 120 |
recovery_action TEXT,
|
| 121 |
recovery_success BOOLEAN,
|
| 122 |
recovery_time_ms INTEGER,
|
| 123 |
+
occurred_at ${timestampType} NOT NULL DEFAULT CURRENT_TIMESTAMP
|
| 124 |
)
|
| 125 |
`);
|
| 126 |
|
| 127 |
// Create indexes
|
| 128 |
+
await this.storage.execute(`CREATE INDEX IF NOT EXISTS idx_failure_memory_source
|
| 129 |
ON mcp_failure_memory(source_name, occurred_at DESC)`);
|
| 130 |
+
await this.storage.execute(`CREATE INDEX IF NOT EXISTS idx_failure_memory_error
|
| 131 |
ON mcp_failure_memory(error_type, occurred_at DESC)`);
|
| 132 |
+
await this.storage.execute(`CREATE INDEX IF NOT EXISTS idx_failure_memory_recovery
|
| 133 |
ON mcp_failure_memory(recovery_action, recovery_success)`);
|
| 134 |
|
| 135 |
// Load recent failures into cache
|
| 136 |
+
await this.loadCacheFromDb();
|
| 137 |
|
| 138 |
// Start background flush
|
| 139 |
this.flushInterval = setInterval(() => this.flushWriteQueue(), this.FLUSH_INTERVAL_MS);
|
| 140 |
|
| 141 |
this.initialized = true;
|
| 142 |
+
logger.info(`🛡️ FailureMemory initialized with ${this.storage.mode} persistence`);
|
| 143 |
} catch (error) {
|
| 144 |
logger.error('❌ FailureMemory initialization failed:', error);
|
| 145 |
this.initialized = true;
|
|
|
|
| 149 |
/**
|
| 150 |
* Load recent failures from database into cache
|
| 151 |
*/
|
| 152 |
+
private async loadCacheFromDb(): Promise<void> {
|
| 153 |
+
if (!this.storage) return;
|
| 154 |
|
| 155 |
try {
|
| 156 |
+
const rows = await this.storage.queryAll(`
|
| 157 |
SELECT * FROM mcp_failure_memory
|
| 158 |
ORDER BY occurred_at DESC
|
| 159 |
LIMIT ?
|
|
|
|
| 265 |
}
|
| 266 |
|
| 267 |
// Update database
|
| 268 |
+
// Update database
|
| 269 |
+
if (this.storage) {
|
| 270 |
try {
|
| 271 |
+
await this.storage.execute(`
|
| 272 |
UPDATE mcp_failure_memory
|
| 273 |
SET recovery_action = ?, recovery_success = ?, recovery_time_ms = ?
|
| 274 |
WHERE id = ?
|
|
|
|
| 289 |
/**
|
| 290 |
* Flush write queue to database
|
| 291 |
*/
|
| 292 |
+
private async flushWriteQueue(): Promise<void> {
|
| 293 |
+
if (!this.storage || this.writeQueue.length === 0) return;
|
| 294 |
|
| 295 |
const failures = [...this.writeQueue];
|
| 296 |
this.writeQueue = [];
|
|
|
|
| 309 |
JSON.stringify(f.errorContext),
|
| 310 |
JSON.stringify(f.queryContext),
|
| 311 |
f.recoveryAction || null,
|
| 312 |
+
f.recoverySuccess != null ? f.recoverySuccess : null, // Use boolean directly
|
| 313 |
f.recoveryTimeMs || null,
|
| 314 |
f.occurredAt.toISOString()
|
| 315 |
]);
|
| 316 |
|
| 317 |
+
await this.storage.batchInsert('mcp_failure_memory', columns, rows);
|
| 318 |
|
| 319 |
// Cleanup old failures periodically
|
| 320 |
if (Math.random() < 0.02) { // 2% chance per flush
|
| 321 |
+
this.cleanupOldFailures(); // Background
|
| 322 |
}
|
| 323 |
|
| 324 |
} catch (error) {
|
|
|
|
| 330 |
/**
|
| 331 |
* Cleanup old failures
|
| 332 |
*/
|
| 333 |
+
private async cleanupOldFailures(): Promise<void> {
|
| 334 |
+
if (!this.storage) return;
|
| 335 |
|
| 336 |
try {
|
| 337 |
const ninetyDaysAgo = new Date();
|
| 338 |
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
|
| 339 |
|
| 340 |
+
await this.storage.execute(`
|
| 341 |
DELETE FROM mcp_failure_memory
|
| 342 |
WHERE occurred_at < ?
|
| 343 |
AND id NOT IN (
|
|
|
|
| 360 |
sourceName: string,
|
| 361 |
limit: number = 50
|
| 362 |
): Promise<Failure[]> {
|
| 363 |
+
if (this.storage) {
|
| 364 |
try {
|
| 365 |
+
const rows = await this.storage.queryAll(`
|
| 366 |
SELECT * FROM mcp_failure_memory
|
| 367 |
WHERE source_name = ?
|
| 368 |
ORDER BY occurred_at DESC
|
|
|
|
| 390 |
sourceName: string,
|
| 391 |
errorType: string
|
| 392 |
): Promise<RecoveryPath[]> {
|
| 393 |
+
if (this.storage) {
|
| 394 |
try {
|
| 395 |
+
const rows = await this.storage.queryAll<{
|
| 396 |
recovery_action: string;
|
| 397 |
total: number;
|
| 398 |
successes: number;
|
| 399 |
avg_time: number;
|
| 400 |
last_success: string | null;
|
| 401 |
+
}>(`
|
| 402 |
SELECT
|
| 403 |
recovery_action,
|
| 404 |
COUNT(*) as total,
|
|
|
|
| 490 |
const cutoff = new Date();
|
| 491 |
cutoff.setMinutes(cutoff.getMinutes() - withinMinutes);
|
| 492 |
|
| 493 |
+
if (this.storage) {
|
| 494 |
try {
|
| 495 |
+
const count = await this.storage.queryScalar<number>(`
|
| 496 |
SELECT COUNT(*)
|
| 497 |
FROM mcp_failure_memory
|
| 498 |
WHERE source_name = ? AND error_type = ? AND occurred_at > ?
|
|
|
|
| 522 |
|
| 523 |
let failures: Failure[] = [];
|
| 524 |
|
| 525 |
+
if (this.storage) {
|
| 526 |
try {
|
| 527 |
+
const rows = await this.storage.queryAll(`
|
| 528 |
SELECT * FROM mcp_failure_memory
|
| 529 |
WHERE source_name = ? AND occurred_at > ?
|
| 530 |
ORDER BY occurred_at DESC
|
|
|
|
| 602 |
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
| 603 |
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
| 604 |
|
| 605 |
+
if (this.storage) {
|
| 606 |
try {
|
| 607 |
+
const total = await this.storage.queryScalar<number>('SELECT COUNT(*) FROM mcp_failure_memory') || 0;
|
| 608 |
+
const uniqueErrors = await this.storage.queryScalar<number>('SELECT COUNT(DISTINCT error_type) FROM mcp_failure_memory') || 0;
|
| 609 |
+
const uniqueSources = await this.storage.queryScalar<number>('SELECT COUNT(DISTINCT source_name) FROM mcp_failure_memory') || 0;
|
| 610 |
|
| 611 |
+
const recoveryRate = await this.storage.queryScalar<number>(`
|
| 612 |
SELECT AVG(CAST(recovery_success AS FLOAT))
|
| 613 |
FROM mcp_failure_memory
|
| 614 |
WHERE recovery_action IS NOT NULL
|
| 615 |
`) || 0;
|
| 616 |
|
| 617 |
+
const avgRecoveryTime = await this.storage.queryScalar<number>(`
|
| 618 |
SELECT AVG(recovery_time_ms)
|
| 619 |
FROM mcp_failure_memory
|
| 620 |
WHERE recovery_success = 1
|
| 621 |
`) || 0;
|
| 622 |
|
| 623 |
+
const last24h = await this.storage.queryScalar<number>(
|
| 624 |
'SELECT COUNT(*) FROM mcp_failure_memory WHERE occurred_at > ?',
|
| 625 |
[oneDayAgo.toISOString()]
|
| 626 |
) || 0;
|
| 627 |
|
| 628 |
+
const last7d = await this.storage.queryScalar<number>(
|
| 629 |
'SELECT COUNT(*) FROM mcp_failure_memory WHERE occurred_at > ?',
|
| 630 |
[sevenDaysAgo.toISOString()]
|
| 631 |
) || 0;
|
| 632 |
|
| 633 |
// Top error types
|
| 634 |
+
const topErrorRows = await this.storage.queryAll<{ error_type: string; count: number; recovery_rate: number }>(`
|
| 635 |
SELECT
|
| 636 |
error_type,
|
| 637 |
COUNT(*) as count,
|
|
|
|
| 648 |
}));
|
| 649 |
|
| 650 |
// Top failing sources
|
| 651 |
+
const topSourceRows = await this.storage.queryAll<{ source_name: string; count: number }>(`
|
| 652 |
SELECT source_name, COUNT(*) as count
|
| 653 |
FROM mcp_failure_memory
|
| 654 |
GROUP BY source_name
|
|
|
|
| 661 |
}));
|
| 662 |
|
| 663 |
// Recent recoveries
|
| 664 |
+
const recentRecoveryRows = await this.storage.queryAll<{
|
| 665 |
recovery_action: string;
|
| 666 |
recovery_success: number;
|
| 667 |
recovery_time_ms: number;
|
| 668 |
occurred_at: string;
|
| 669 |
+
}>(`
|
| 670 |
SELECT recovery_action, recovery_success, recovery_time_ms, occurred_at
|
| 671 |
FROM mcp_failure_memory
|
| 672 |
WHERE recovery_action IS NOT NULL
|
|
|
|
| 716 |
* Force flush all pending writes
|
| 717 |
*/
|
| 718 |
async flush(): Promise<void> {
|
| 719 |
+
await this.flushWriteQueue();
|
| 720 |
}
|
| 721 |
|
| 722 |
/**
|
apps/backend/src/mcp/memory/PatternMemory.ts
CHANGED
|
@@ -15,10 +15,9 @@
|
|
| 15 |
|
| 16 |
import { v4 as uuidv4 } from 'uuid';
|
| 17 |
import crypto from 'crypto';
|
| 18 |
-
import type {
|
| 19 |
import { logger } from '../../utils/logger.js';
|
| 20 |
import { eventBus, type EventType } from '../EventBus.js';
|
| 21 |
-
import { queryAll, queryOne, queryScalar, execute, batchInsert } from './SqlJsCompat.js';
|
| 22 |
|
| 23 |
// ═══════════════════════════════════════════════════════════════════════════════
|
| 24 |
// INTERFACES
|
|
@@ -76,7 +75,7 @@ export interface PatternStatistics {
|
|
| 76 |
// ═══════════════════════════════════════════════════════════════════════════════
|
| 77 |
|
| 78 |
export class PatternMemory {
|
| 79 |
-
private
|
| 80 |
private initialized: boolean = false;
|
| 81 |
private writeQueue: QueryPattern[] = [];
|
| 82 |
private flushInterval: ReturnType<typeof setInterval> | null = null;
|
|
@@ -87,30 +86,34 @@ export class PatternMemory {
|
|
| 87 |
private readonly FLUSH_INTERVAL_MS = 5000; // Batch writes every 5 seconds
|
| 88 |
private readonly MAX_DB_PATTERNS = 100000;
|
| 89 |
|
| 90 |
-
constructor(
|
| 91 |
-
if (
|
| 92 |
-
this.
|
| 93 |
this.initialize();
|
| 94 |
}
|
| 95 |
}
|
| 96 |
|
| 97 |
/**
|
| 98 |
-
* Initialize with
|
| 99 |
*/
|
| 100 |
-
public
|
| 101 |
-
this.
|
| 102 |
this.initialize();
|
| 103 |
}
|
| 104 |
|
| 105 |
/**
|
| 106 |
* Initialize the service
|
| 107 |
*/
|
| 108 |
-
private initialize(): void {
|
| 109 |
-
if (this.initialized || !this.
|
| 110 |
|
| 111 |
try {
|
| 112 |
-
//
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
CREATE TABLE IF NOT EXISTS mcp_query_patterns (
|
| 115 |
id TEXT PRIMARY KEY,
|
| 116 |
widget_id TEXT NOT NULL,
|
|
@@ -121,26 +124,26 @@ export class PatternMemory {
|
|
| 121 |
result_size INTEGER,
|
| 122 |
success BOOLEAN NOT NULL,
|
| 123 |
user_context TEXT,
|
| 124 |
-
timestamp
|
| 125 |
)
|
| 126 |
`);
|
| 127 |
|
| 128 |
// Create indexes for fast queries
|
| 129 |
-
this.
|
| 130 |
ON mcp_query_patterns(widget_id, timestamp DESC)`);
|
| 131 |
-
this.
|
| 132 |
ON mcp_query_patterns(query_signature)`);
|
| 133 |
-
this.
|
| 134 |
ON mcp_query_patterns(source_used, timestamp DESC)`);
|
| 135 |
|
| 136 |
// Load recent patterns into cache
|
| 137 |
-
this.loadCacheFromDb();
|
| 138 |
|
| 139 |
// Start background flush
|
| 140 |
this.flushInterval = setInterval(() => this.flushWriteQueue(), this.FLUSH_INTERVAL_MS);
|
| 141 |
|
| 142 |
this.initialized = true;
|
| 143 |
-
logger.info(
|
| 144 |
} catch (error) {
|
| 145 |
logger.error('❌ PatternMemory initialization failed:', error);
|
| 146 |
// Fall back to in-memory only
|
|
@@ -151,11 +154,12 @@ export class PatternMemory {
|
|
| 151 |
/**
|
| 152 |
* Load recent patterns from database into cache
|
| 153 |
*/
|
| 154 |
-
|
| 155 |
-
|
|
|
|
| 156 |
|
| 157 |
try {
|
| 158 |
-
const rows = queryAll(
|
| 159 |
SELECT * FROM mcp_query_patterns
|
| 160 |
ORDER BY timestamp DESC
|
| 161 |
LIMIT ?
|
|
@@ -245,8 +249,8 @@ export class PatternMemory {
|
|
| 245 |
/**
|
| 246 |
* Flush write queue to database (batched writes)
|
| 247 |
*/
|
| 248 |
-
private flushWriteQueue(): void {
|
| 249 |
-
if (!this.
|
| 250 |
|
| 251 |
const patterns = [...this.writeQueue];
|
| 252 |
this.writeQueue = [];
|
|
@@ -264,17 +268,17 @@ export class PatternMemory {
|
|
| 264 |
p.querySignature,
|
| 265 |
p.sourceUsed,
|
| 266 |
p.latencyMs,
|
| 267 |
-
p.resultSize
|
| 268 |
-
p.success
|
| 269 |
p.userContext ? JSON.stringify(p.userContext) : null,
|
| 270 |
p.timestamp.toISOString()
|
| 271 |
]);
|
| 272 |
|
| 273 |
-
batchInsert(
|
| 274 |
|
| 275 |
// Cleanup old patterns periodically
|
| 276 |
if (Math.random() < 0.01) { // 1% chance per flush
|
| 277 |
-
this.cleanupOldPatterns();
|
| 278 |
}
|
| 279 |
|
| 280 |
} catch (error) {
|
|
@@ -287,14 +291,17 @@ export class PatternMemory {
|
|
| 287 |
/**
|
| 288 |
* Cleanup old patterns to prevent database bloat
|
| 289 |
*/
|
| 290 |
-
private cleanupOldPatterns(): void {
|
| 291 |
-
if (!this.
|
| 292 |
|
| 293 |
try {
|
| 294 |
const thirtyDaysAgo = new Date();
|
| 295 |
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
| 296 |
|
| 297 |
-
|
|
|
|
|
|
|
|
|
|
| 298 |
DELETE FROM mcp_query_patterns
|
| 299 |
WHERE timestamp < ?
|
| 300 |
AND id NOT IN (
|
|
@@ -332,10 +339,10 @@ export class PatternMemory {
|
|
| 332 |
}
|
| 333 |
|
| 334 |
// Query database for more
|
| 335 |
-
if (this.
|
| 336 |
try {
|
| 337 |
const remaining = limit - results.length;
|
| 338 |
-
const rows = queryAll(
|
| 339 |
SELECT * FROM mcp_query_patterns
|
| 340 |
WHERE query_signature = ?
|
| 341 |
AND id NOT IN (${results.map(() => '?').join(',') || '\'\''})
|
|
@@ -367,9 +374,9 @@ export class PatternMemory {
|
|
| 367 |
let patterns: QueryPattern[] = [];
|
| 368 |
|
| 369 |
// Try database first
|
| 370 |
-
if (this.
|
| 371 |
try {
|
| 372 |
-
const rows = queryAll(
|
| 373 |
SELECT * FROM mcp_query_patterns
|
| 374 |
WHERE widget_id = ? AND timestamp > ?
|
| 375 |
ORDER BY timestamp DESC
|
|
@@ -435,12 +442,12 @@ export class PatternMemory {
|
|
| 435 |
* Get average latency for a source
|
| 436 |
*/
|
| 437 |
async getAverageLatency(sourceName: string): Promise<number> {
|
| 438 |
-
if (this.
|
| 439 |
try {
|
| 440 |
const oneDayAgo = new Date();
|
| 441 |
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
|
| 442 |
|
| 443 |
-
const result = queryOne<{ avg_latency: number }>(
|
| 444 |
SELECT AVG(latency_ms) as avg_latency
|
| 445 |
FROM mcp_query_patterns
|
| 446 |
WHERE source_used = ? AND success = 1 AND timestamp > ?
|
|
@@ -471,12 +478,12 @@ export class PatternMemory {
|
|
| 471 |
* Get success rate for a source and query type
|
| 472 |
*/
|
| 473 |
async getSuccessRate(sourceName: string, queryType: string): Promise<number> {
|
| 474 |
-
if (this.
|
| 475 |
try {
|
| 476 |
const sevenDaysAgo = new Date();
|
| 477 |
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
| 478 |
|
| 479 |
-
const result = queryOne<{ total: number; successes: number }>(
|
| 480 |
SELECT
|
| 481 |
COUNT(*) as total,
|
| 482 |
SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successes
|
|
@@ -515,37 +522,37 @@ export class PatternMemory {
|
|
| 515 |
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
| 516 |
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
| 517 |
|
| 518 |
-
if (this.
|
| 519 |
try {
|
| 520 |
// Total patterns
|
| 521 |
-
const total = queryScalar<number>(
|
| 522 |
|
| 523 |
// Unique widgets
|
| 524 |
-
const uniqueWidgets = queryScalar<number>(
|
| 525 |
|
| 526 |
// Unique sources
|
| 527 |
-
const uniqueSources = queryScalar<number>(
|
| 528 |
|
| 529 |
// Average latency
|
| 530 |
-
const avgLatency = queryScalar<number>(
|
| 531 |
|
| 532 |
// Success rate
|
| 533 |
-
const successRate = queryScalar<number>(
|
| 534 |
|
| 535 |
// Queries last 24h
|
| 536 |
-
const last24h = queryScalar<number>(
|
| 537 |
'SELECT COUNT(*) FROM mcp_query_patterns WHERE timestamp > ?',
|
| 538 |
[oneDayAgo.toISOString()]
|
| 539 |
) || 0;
|
| 540 |
|
| 541 |
// Queries last 7d
|
| 542 |
-
const last7d = queryScalar<number>(
|
| 543 |
'SELECT COUNT(*) FROM mcp_query_patterns WHERE timestamp > ?',
|
| 544 |
[sevenDaysAgo.toISOString()]
|
| 545 |
) || 0;
|
| 546 |
|
| 547 |
// Top sources
|
| 548 |
-
const topSourcesRows = queryAll<{ source_used: string; count: number; avg_latency: number }>(
|
| 549 |
SELECT source_used, COUNT(*) as count, AVG(latency_ms) as avg_latency
|
| 550 |
FROM mcp_query_patterns
|
| 551 |
GROUP BY source_used
|
|
@@ -559,7 +566,7 @@ export class PatternMemory {
|
|
| 559 |
}));
|
| 560 |
|
| 561 |
// Top widgets
|
| 562 |
-
const topWidgetsRows = queryAll<{ widget_id: string; count: number }>(
|
| 563 |
SELECT widget_id, COUNT(*) as count
|
| 564 |
FROM mcp_query_patterns
|
| 565 |
GROUP BY widget_id
|
|
@@ -575,6 +582,7 @@ export class PatternMemory {
|
|
| 575 |
totalPatterns: total,
|
| 576 |
uniqueWidgets,
|
| 577 |
uniqueSources,
|
|
|
|
| 578 |
avgLatencyMs: avgLatency,
|
| 579 |
successRate,
|
| 580 |
queriesLast24h: last24h,
|
|
|
|
| 15 |
|
| 16 |
import { v4 as uuidv4 } from 'uuid';
|
| 17 |
import crypto from 'crypto';
|
| 18 |
+
import type { StorageAdapter } from './StorageAdapter.js';
|
| 19 |
import { logger } from '../../utils/logger.js';
|
| 20 |
import { eventBus, type EventType } from '../EventBus.js';
|
|
|
|
| 21 |
|
| 22 |
// ═══════════════════════════════════════════════════════════════════════════════
|
| 23 |
// INTERFACES
|
|
|
|
| 75 |
// ═══════════════════════════════════════════════════════════════════════════════
|
| 76 |
|
| 77 |
export class PatternMemory {
|
| 78 |
+
private storage: StorageAdapter | null = null;
|
| 79 |
private initialized: boolean = false;
|
| 80 |
private writeQueue: QueryPattern[] = [];
|
| 81 |
private flushInterval: ReturnType<typeof setInterval> | null = null;
|
|
|
|
| 86 |
private readonly FLUSH_INTERVAL_MS = 5000; // Batch writes every 5 seconds
|
| 87 |
private readonly MAX_DB_PATTERNS = 100000;
|
| 88 |
|
| 89 |
+
constructor(storage?: StorageAdapter) {
|
| 90 |
+
if (storage) {
|
| 91 |
+
this.storage = storage;
|
| 92 |
this.initialize();
|
| 93 |
}
|
| 94 |
}
|
| 95 |
|
| 96 |
/**
|
| 97 |
+
* Initialize with storage adapter
|
| 98 |
*/
|
| 99 |
+
public setStorage(storage: StorageAdapter): void {
|
| 100 |
+
this.storage = storage;
|
| 101 |
this.initialize();
|
| 102 |
}
|
| 103 |
|
| 104 |
/**
|
| 105 |
* Initialize the service
|
| 106 |
*/
|
| 107 |
+
private async initialize(): Promise<void> {
|
| 108 |
+
if (this.initialized || !this.storage) return;
|
| 109 |
|
| 110 |
try {
|
| 111 |
+
// Postgres uses different syntax for some things, but basic CREATE TABLE is mostly compatible
|
| 112 |
+
// except for DATETIME DEFAULT CURRENT_TIMESTAMP vs TIMESTAMP WITH TIME ZONE
|
| 113 |
+
const isPostgres = this.storage.mode === 'postgres';
|
| 114 |
+
const timestampType = isPostgres ? 'TIMESTAMP WITH TIME ZONE' : 'DATETIME';
|
| 115 |
+
|
| 116 |
+
await this.storage.execute(`
|
| 117 |
CREATE TABLE IF NOT EXISTS mcp_query_patterns (
|
| 118 |
id TEXT PRIMARY KEY,
|
| 119 |
widget_id TEXT NOT NULL,
|
|
|
|
| 124 |
result_size INTEGER,
|
| 125 |
success BOOLEAN NOT NULL,
|
| 126 |
user_context TEXT,
|
| 127 |
+
timestamp ${timestampType} NOT NULL DEFAULT CURRENT_TIMESTAMP
|
| 128 |
)
|
| 129 |
`);
|
| 130 |
|
| 131 |
// Create indexes for fast queries
|
| 132 |
+
await this.storage.execute(`CREATE INDEX IF NOT EXISTS idx_query_patterns_widget
|
| 133 |
ON mcp_query_patterns(widget_id, timestamp DESC)`);
|
| 134 |
+
await this.storage.execute(`CREATE INDEX IF NOT EXISTS idx_query_patterns_signature
|
| 135 |
ON mcp_query_patterns(query_signature)`);
|
| 136 |
+
await this.storage.execute(`CREATE INDEX IF NOT EXISTS idx_query_patterns_source
|
| 137 |
ON mcp_query_patterns(source_used, timestamp DESC)`);
|
| 138 |
|
| 139 |
// Load recent patterns into cache
|
| 140 |
+
await this.loadCacheFromDb();
|
| 141 |
|
| 142 |
// Start background flush
|
| 143 |
this.flushInterval = setInterval(() => this.flushWriteQueue(), this.FLUSH_INTERVAL_MS);
|
| 144 |
|
| 145 |
this.initialized = true;
|
| 146 |
+
logger.info(`🧠 PatternMemory initialized with ${this.storage.mode} storage`);
|
| 147 |
} catch (error) {
|
| 148 |
logger.error('❌ PatternMemory initialization failed:', error);
|
| 149 |
// Fall back to in-memory only
|
|
|
|
| 154 |
/**
|
| 155 |
* Load recent patterns from database into cache
|
| 156 |
*/
|
| 157 |
+
|
| 158 |
+
private async loadCacheFromDb(): Promise<void> {
|
| 159 |
+
if (!this.storage) return;
|
| 160 |
|
| 161 |
try {
|
| 162 |
+
const rows = await this.storage.queryAll(`
|
| 163 |
SELECT * FROM mcp_query_patterns
|
| 164 |
ORDER BY timestamp DESC
|
| 165 |
LIMIT ?
|
|
|
|
| 249 |
/**
|
| 250 |
* Flush write queue to database (batched writes)
|
| 251 |
*/
|
| 252 |
+
private async flushWriteQueue(): Promise<void> {
|
| 253 |
+
if (!this.storage || this.writeQueue.length === 0) return;
|
| 254 |
|
| 255 |
const patterns = [...this.writeQueue];
|
| 256 |
this.writeQueue = [];
|
|
|
|
| 268 |
p.querySignature,
|
| 269 |
p.sourceUsed,
|
| 270 |
p.latencyMs,
|
| 271 |
+
p.resultSize ?? null,
|
| 272 |
+
p.success, // Use boolean directly for Postgres compatibility
|
| 273 |
p.userContext ? JSON.stringify(p.userContext) : null,
|
| 274 |
p.timestamp.toISOString()
|
| 275 |
]);
|
| 276 |
|
| 277 |
+
await this.storage.batchInsert('mcp_query_patterns', columns, rows);
|
| 278 |
|
| 279 |
// Cleanup old patterns periodically
|
| 280 |
if (Math.random() < 0.01) { // 1% chance per flush
|
| 281 |
+
this.cleanupOldPatterns(); // No await to keep it background
|
| 282 |
}
|
| 283 |
|
| 284 |
} catch (error) {
|
|
|
|
| 291 |
/**
|
| 292 |
* Cleanup old patterns to prevent database bloat
|
| 293 |
*/
|
| 294 |
+
private async cleanupOldPatterns(): Promise<void> {
|
| 295 |
+
if (!this.storage) return;
|
| 296 |
|
| 297 |
try {
|
| 298 |
const thirtyDaysAgo = new Date();
|
| 299 |
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
| 300 |
|
| 301 |
+
// Need to fix this query for Postgres compatibility if nested subquery limit issues arise
|
| 302 |
+
// But standard SQL roughly supports DELETE...
|
| 303 |
+
// Postgres supports DELETE FROM ... WHERE id NOT IN (...)
|
| 304 |
+
await this.storage.execute(`
|
| 305 |
DELETE FROM mcp_query_patterns
|
| 306 |
WHERE timestamp < ?
|
| 307 |
AND id NOT IN (
|
|
|
|
| 339 |
}
|
| 340 |
|
| 341 |
// Query database for more
|
| 342 |
+
if (this.storage) {
|
| 343 |
try {
|
| 344 |
const remaining = limit - results.length;
|
| 345 |
+
const rows = await this.storage.queryAll(`
|
| 346 |
SELECT * FROM mcp_query_patterns
|
| 347 |
WHERE query_signature = ?
|
| 348 |
AND id NOT IN (${results.map(() => '?').join(',') || '\'\''})
|
|
|
|
| 374 |
let patterns: QueryPattern[] = [];
|
| 375 |
|
| 376 |
// Try database first
|
| 377 |
+
if (this.storage) {
|
| 378 |
try {
|
| 379 |
+
const rows = await this.storage.queryAll(`
|
| 380 |
SELECT * FROM mcp_query_patterns
|
| 381 |
WHERE widget_id = ? AND timestamp > ?
|
| 382 |
ORDER BY timestamp DESC
|
|
|
|
| 442 |
* Get average latency for a source
|
| 443 |
*/
|
| 444 |
async getAverageLatency(sourceName: string): Promise<number> {
|
| 445 |
+
if (this.storage) {
|
| 446 |
try {
|
| 447 |
const oneDayAgo = new Date();
|
| 448 |
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
|
| 449 |
|
| 450 |
+
const result = await this.storage.queryOne<{ avg_latency: number }>(`
|
| 451 |
SELECT AVG(latency_ms) as avg_latency
|
| 452 |
FROM mcp_query_patterns
|
| 453 |
WHERE source_used = ? AND success = 1 AND timestamp > ?
|
|
|
|
| 478 |
* Get success rate for a source and query type
|
| 479 |
*/
|
| 480 |
async getSuccessRate(sourceName: string, queryType: string): Promise<number> {
|
| 481 |
+
if (this.storage) {
|
| 482 |
try {
|
| 483 |
const sevenDaysAgo = new Date();
|
| 484 |
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
| 485 |
|
| 486 |
+
const result = await this.storage.queryOne<{ total: number; successes: number }>(`
|
| 487 |
SELECT
|
| 488 |
COUNT(*) as total,
|
| 489 |
SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successes
|
|
|
|
| 522 |
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
| 523 |
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
| 524 |
|
| 525 |
+
if (this.storage) {
|
| 526 |
try {
|
| 527 |
// Total patterns
|
| 528 |
+
const total = await this.storage.queryScalar<number>('SELECT COUNT(*) FROM mcp_query_patterns') || 0;
|
| 529 |
|
| 530 |
// Unique widgets
|
| 531 |
+
const uniqueWidgets = await this.storage.queryScalar<number>('SELECT COUNT(DISTINCT widget_id) FROM mcp_query_patterns') || 0;
|
| 532 |
|
| 533 |
// Unique sources
|
| 534 |
+
const uniqueSources = await this.storage.queryScalar<number>('SELECT COUNT(DISTINCT source_used) FROM mcp_query_patterns') || 0;
|
| 535 |
|
| 536 |
// Average latency
|
| 537 |
+
const avgLatency = await this.storage.queryScalar<number>('SELECT AVG(latency_ms) FROM mcp_query_patterns WHERE success = 1') || 0;
|
| 538 |
|
| 539 |
// Success rate
|
| 540 |
+
const successRate = await this.storage.queryScalar<number>('SELECT AVG(CAST(success AS FLOAT)) FROM mcp_query_patterns') || 0;
|
| 541 |
|
| 542 |
// Queries last 24h
|
| 543 |
+
const last24h = await this.storage.queryScalar<number>(
|
| 544 |
'SELECT COUNT(*) FROM mcp_query_patterns WHERE timestamp > ?',
|
| 545 |
[oneDayAgo.toISOString()]
|
| 546 |
) || 0;
|
| 547 |
|
| 548 |
// Queries last 7d
|
| 549 |
+
const last7d = await this.storage.queryScalar<number>(
|
| 550 |
'SELECT COUNT(*) FROM mcp_query_patterns WHERE timestamp > ?',
|
| 551 |
[sevenDaysAgo.toISOString()]
|
| 552 |
) || 0;
|
| 553 |
|
| 554 |
// Top sources
|
| 555 |
+
const topSourcesRows = await this.storage.queryAll<{ source_used: string; count: number; avg_latency: number }>(`
|
| 556 |
SELECT source_used, COUNT(*) as count, AVG(latency_ms) as avg_latency
|
| 557 |
FROM mcp_query_patterns
|
| 558 |
GROUP BY source_used
|
|
|
|
| 566 |
}));
|
| 567 |
|
| 568 |
// Top widgets
|
| 569 |
+
const topWidgetsRows = await this.storage.queryAll<{ widget_id: string; count: number }>(`
|
| 570 |
SELECT widget_id, COUNT(*) as count
|
| 571 |
FROM mcp_query_patterns
|
| 572 |
GROUP BY widget_id
|
|
|
|
| 582 |
totalPatterns: total,
|
| 583 |
uniqueWidgets,
|
| 584 |
uniqueSources,
|
| 585 |
+
|
| 586 |
avgLatencyMs: avgLatency,
|
| 587 |
successRate,
|
| 588 |
queriesLast24h: last24h,
|
apps/backend/src/mcp/memory/StorageAdapter.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import type { Database } from 'sql.js';
|
| 3 |
+
import { logger } from '../../utils/logger.js';
|
| 4 |
+
import { queryAll, queryOne, execute, batchInsert, queryScalar } from './SqlJsCompat.js';
|
| 5 |
+
import { DatabaseAdapter } from '../../platform/db/PrismaDatabaseAdapter.js';
|
| 6 |
+
|
| 7 |
+
export interface StorageAdapter {
|
| 8 |
+
queryAll<T = any>(sql: string, params?: any[]): Promise<T[]>;
|
| 9 |
+
queryOne<T = any>(sql: string, params?: any[]): Promise<T | null>;
|
| 10 |
+
queryScalar<T = any>(sql: string, params?: any[]): Promise<T | null>;
|
| 11 |
+
execute(sql: string, params?: any[]): Promise<number>;
|
| 12 |
+
batchInsert(tableName: string, columns: string[], rows: any[][]): Promise<number>;
|
| 13 |
+
isAvailable(): boolean;
|
| 14 |
+
mode: 'sqlite' | 'postgres';
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
export class SqlJsStorageAdapter implements StorageAdapter {
|
| 18 |
+
constructor(private db: Database | null) { }
|
| 19 |
+
|
| 20 |
+
mode: 'sqlite' | 'postgres' = 'sqlite';
|
| 21 |
+
|
| 22 |
+
isAvailable(): boolean {
|
| 23 |
+
return !!this.db;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
async queryAll<T = any>(sql: string, params: any[] = []): Promise<T[]> {
|
| 27 |
+
if (!this.db) return [];
|
| 28 |
+
// SqlJsCompat functions are synchronous for sql.js, but we wrap in Promise for common interface
|
| 29 |
+
return Promise.resolve(queryAll<T>(this.db, sql, params));
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
async queryOne<T = any>(sql: string, params: any[] = []): Promise<T | null> {
|
| 33 |
+
if (!this.db) return null;
|
| 34 |
+
return Promise.resolve(queryOne<T>(this.db, sql, params));
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
async queryScalar<T = any>(sql: string, params: any[] = []): Promise<T | null> {
|
| 38 |
+
if (!this.db) return null;
|
| 39 |
+
return Promise.resolve(queryScalar<T>(this.db, sql, params));
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
async execute(sql: string, params: any[] = []): Promise<number> {
|
| 43 |
+
if (!this.db) return 0;
|
| 44 |
+
return Promise.resolve(execute(this.db, sql, params));
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
async batchInsert(tableName: string, columns: string[], rows: any[][]): Promise<number> {
|
| 48 |
+
if (!this.db) return 0;
|
| 49 |
+
return Promise.resolve(batchInsert(this.db, tableName, columns, rows));
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
export class PostgresStorageAdapter implements StorageAdapter {
|
| 54 |
+
constructor(private prisma: DatabaseAdapter) { }
|
| 55 |
+
|
| 56 |
+
mode: 'sqlite' | 'postgres' = 'postgres';
|
| 57 |
+
|
| 58 |
+
isAvailable(): boolean {
|
| 59 |
+
return this.prisma.isAvailable();
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
async queryAll<T = any>(sql: string, params: any[] = []): Promise<T[]> {
|
| 63 |
+
// Postgres uses $1, $2, etc. instead of ?
|
| 64 |
+
const { sql: pgSql, params: pgParams } = this.convertSql(sql, params);
|
| 65 |
+
return await this.prisma.query(pgSql, pgParams);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
async queryOne<T = any>(sql: string, params: any[] = []): Promise<T | null> {
|
| 69 |
+
const rows = await this.queryAll<T>(sql, params);
|
| 70 |
+
return rows.length > 0 ? rows[0] : null;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
async queryScalar<T = any>(sql: string, params: any[] = []): Promise<T | null> {
|
| 74 |
+
const rows = await this.queryAll(sql, params);
|
| 75 |
+
if (rows.length === 0) return null;
|
| 76 |
+
const firstValue = Object.values(rows[0])[0];
|
| 77 |
+
return firstValue as T;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
async execute(sql: string, params: any[] = []): Promise<number> {
|
| 81 |
+
const { sql: pgSql, params: pgParams } = this.convertSql(sql, params);
|
| 82 |
+
return await this.prisma.execute(pgSql, pgParams);
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
async batchInsert(tableName: string, columns: string[], rows: any[][]): Promise<number> {
|
| 86 |
+
if (rows.length === 0) return 0;
|
| 87 |
+
|
| 88 |
+
// Construct standard VALUES clause but with $1, $2...
|
| 89 |
+
// This is tricky with variable params.
|
| 90 |
+
// A simple loop implementation for now
|
| 91 |
+
let count = 0;
|
| 92 |
+
for (const row of rows) {
|
| 93 |
+
const placeholders = row.map((_, i) => `$${i + 1}`).join(', ');
|
| 94 |
+
const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
|
| 95 |
+
await this.prisma.query(sql, row);
|
| 96 |
+
count++;
|
| 97 |
+
}
|
| 98 |
+
return count;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
private convertSql(sql: string, params: any[]): { sql: string, params: any[] } {
|
| 102 |
+
// Convert ? to $1, $2, etc.
|
| 103 |
+
let paramIndex = 1;
|
| 104 |
+
const pgSql = sql.replace(/\?/g, () => `$${paramIndex++}`);
|
| 105 |
+
return { sql: pgSql, params };
|
| 106 |
+
}
|
| 107 |
+
}
|
apps/backend/src/platform/db/PrismaDatabaseAdapter.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface DatabaseAdapter {
|
|
| 24 |
initialize(): Promise<void>;
|
| 25 |
disconnect(): Promise<void>;
|
| 26 |
query(sql: string, params?: any[]): Promise<any>;
|
|
|
|
| 27 |
transaction<T>(fn: (tx: any) => Promise<T>): Promise<T>;
|
| 28 |
isAvailable(): boolean;
|
| 29 |
}
|
|
@@ -56,6 +57,10 @@ class StubDatabaseAdapter implements DatabaseAdapter {
|
|
| 56 |
throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`);
|
| 57 |
}
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
isAvailable(): boolean {
|
| 60 |
return false;
|
| 61 |
}
|
|
@@ -131,6 +136,13 @@ class PrismaDatabaseAdapterImpl implements DatabaseAdapter {
|
|
| 131 |
return this.prisma.$queryRawUnsafe(sql, ...(params || []));
|
| 132 |
}
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
async transaction<T>(fn: (tx: any) => Promise<T>): Promise<T> {
|
| 135 |
if (!this.prisma || !this.isInitialized) {
|
| 136 |
throw new Error('PostgreSQL not connected. Use SQLite for local operations.');
|
|
|
|
| 24 |
initialize(): Promise<void>;
|
| 25 |
disconnect(): Promise<void>;
|
| 26 |
query(sql: string, params?: any[]): Promise<any>;
|
| 27 |
+
execute(sql: string, params?: any[]): Promise<number>;
|
| 28 |
transaction<T>(fn: (tx: any) => Promise<T>): Promise<T>;
|
| 29 |
isAvailable(): boolean;
|
| 30 |
}
|
|
|
|
| 57 |
throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`);
|
| 58 |
}
|
| 59 |
|
| 60 |
+
async execute(_sql: string, _params?: any[]): Promise<number> {
|
| 61 |
+
throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`);
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
isAvailable(): boolean {
|
| 65 |
return false;
|
| 66 |
}
|
|
|
|
| 136 |
return this.prisma.$queryRawUnsafe(sql, ...(params || []));
|
| 137 |
}
|
| 138 |
|
| 139 |
+
async execute(sql: string, params?: any[]): Promise<number> {
|
| 140 |
+
if (!this.prisma || !this.isInitialized) {
|
| 141 |
+
throw new Error('PostgreSQL not connected. Use SQLite for local operations.');
|
| 142 |
+
}
|
| 143 |
+
return this.prisma.$executeRawUnsafe(sql, ...(params || []));
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
async transaction<T>(fn: (tx: any) => Promise<T>): Promise<T> {
|
| 147 |
if (!this.prisma || !this.isInitialized) {
|
| 148 |
throw new Error('PostgreSQL not connected. Use SQLite for local operations.');
|
apps/backend/src/services/NeuralChat/NeuralCortex.ts
CHANGED
|
@@ -67,7 +67,7 @@ export interface DiscoveredPattern {
|
|
| 67 |
|
| 68 |
class NeuralCortex {
|
| 69 |
private static instance: NeuralCortex;
|
| 70 |
-
|
| 71 |
public static getInstance(): NeuralCortex {
|
| 72 |
if (!NeuralCortex.instance) {
|
| 73 |
NeuralCortex.instance = new NeuralCortex();
|
|
@@ -86,7 +86,7 @@ class NeuralCortex {
|
|
| 86 |
}> {
|
| 87 |
const entities = this.extractEntities(message.body);
|
| 88 |
const concepts = this.extractConcepts(message.body);
|
| 89 |
-
|
| 90 |
// 1. Create message node in GRAPH (Neo4j)
|
| 91 |
await neo4jAdapter.runQuery(`
|
| 92 |
CREATE (m:Message {
|
|
@@ -158,23 +158,23 @@ class NeuralCortex {
|
|
| 158 |
*/
|
| 159 |
private extractEntities(text: string): string[] {
|
| 160 |
const entities: string[] = [];
|
| 161 |
-
|
| 162 |
// @mentions
|
| 163 |
const mentions = text.match(/@(\w+)/g);
|
| 164 |
if (mentions) entities.push(...mentions.map(m => m.slice(1)));
|
| 165 |
-
|
| 166 |
// File paths
|
| 167 |
const files = text.match(/[\w-]+\.(ts|js|tsx|jsx|json|md|py|yaml|yml|sql)/gi);
|
| 168 |
if (files) entities.push(...files);
|
| 169 |
-
|
| 170 |
// Component/Class names (PascalCase)
|
| 171 |
const components = text.match(/\b[A-Z][a-zA-Z]+(?:Widget|Service|Controller|Adapter|Component|Provider|Handler)\b/g);
|
| 172 |
if (components) entities.push(...components);
|
| 173 |
-
|
| 174 |
// URLs
|
| 175 |
const urls = text.match(/https?:\/\/[^\s]+/g);
|
| 176 |
if (urls) entities.push(...urls);
|
| 177 |
-
|
| 178 |
return [...new Set(entities)];
|
| 179 |
}
|
| 180 |
|
|
@@ -184,19 +184,19 @@ class NeuralCortex {
|
|
| 184 |
private extractConcepts(text: string): string[] {
|
| 185 |
const concepts: string[] = [];
|
| 186 |
const textLower = text.toLowerCase();
|
| 187 |
-
|
| 188 |
// Technologies
|
| 189 |
-
const techs = ['neo4j', 'react', 'typescript', 'docker', 'kubernetes', 'api', 'websocket', 'mcp', 'graphql', 'rest', 'postgresql', 'redis', 'vector', 'rag'];
|
| 190 |
techs.forEach(t => { if (textLower.includes(t)) concepts.push(t); });
|
| 191 |
-
|
| 192 |
// Actions
|
| 193 |
const actions = ['deploy', 'review', 'test', 'refactor', 'implement', 'fix', 'create', 'delete', 'update', 'analyze', 'research', 'architect'];
|
| 194 |
actions.forEach(a => { if (textLower.includes(a)) concepts.push(a); });
|
| 195 |
-
|
| 196 |
// Domains
|
| 197 |
const domains = ['security', 'performance', 'architecture', 'authentication', 'authorization', 'database', 'frontend', 'backend', 'infrastructure', 'ai', 'agents'];
|
| 198 |
domains.forEach(d => { if (textLower.includes(d)) concepts.push(d); });
|
| 199 |
-
|
| 200 |
return [...new Set(concepts)];
|
| 201 |
}
|
| 202 |
|
|
@@ -211,7 +211,7 @@ class NeuralCortex {
|
|
| 211 |
MERGE (m)-[:MENTIONS]->(e)
|
| 212 |
RETURN e.name as linked
|
| 213 |
`, { messageId, entity });
|
| 214 |
-
|
| 215 |
return result[0]?.linked || null;
|
| 216 |
} catch {
|
| 217 |
return null;
|
|
@@ -241,27 +241,27 @@ class NeuralCortex {
|
|
| 241 |
*/
|
| 242 |
async query(input: CortexQuery): Promise<CortexResult[]> {
|
| 243 |
const results: CortexResult[] = [];
|
| 244 |
-
|
| 245 |
switch (input.type) {
|
| 246 |
case 'search':
|
| 247 |
return await this.hybridSearch(input.query, input.context);
|
| 248 |
-
|
| 249 |
case 'pattern':
|
| 250 |
return await this.findPatterns(input.query, input.context);
|
| 251 |
-
|
| 252 |
case 'insight':
|
| 253 |
return await this.generateInsights(input.query, input.context);
|
| 254 |
-
|
| 255 |
case 'history':
|
| 256 |
return await this.getDecisionHistory(input.query, input.context);
|
| 257 |
-
|
| 258 |
case 'chat':
|
| 259 |
default:
|
| 260 |
// 1. Search Chat History (Vector + Keyword)
|
| 261 |
const chatResults = await this.searchMessages(input.query);
|
| 262 |
// 2. Search Knowledge Graph (Keyword/Hybrid)
|
| 263 |
const graphResults = await this.hybridSearch(input.query, input.context);
|
| 264 |
-
|
| 265 |
return [...chatResults, ...graphResults].sort((a, b) => b.relevance - a.relevance);
|
| 266 |
}
|
| 267 |
}
|
|
@@ -313,7 +313,7 @@ class NeuralCortex {
|
|
| 313 |
|
| 314 |
for (const r of graphResults) {
|
| 315 |
if (!existingIds.has(r.m.properties.id)) {
|
| 316 |
-
|
| 317 |
type: 'message' as const,
|
| 318 |
data: {
|
| 319 |
id: r.m.properties.id,
|
|
@@ -380,13 +380,13 @@ class NeuralCortex {
|
|
| 380 |
LIMIT 20
|
| 381 |
`, { query, nodeTypes });
|
| 382 |
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
type: 'node' as const,
|
| 391 |
data: {
|
| 392 |
name: name,
|
|
@@ -397,8 +397,8 @@ class NeuralCortex {
|
|
| 397 |
source: 'knowledge_graph',
|
| 398 |
connections: r.connections.filter((c: any) => c.target)
|
| 399 |
});
|
| 400 |
-
|
| 401 |
-
|
| 402 |
|
| 403 |
return results;
|
| 404 |
} catch {
|
|
@@ -567,7 +567,7 @@ class NeuralCortex {
|
|
| 567 |
return [];
|
| 568 |
}
|
| 569 |
}
|
| 570 |
-
|
| 571 |
/**
|
| 572 |
* Calculate relevance score (0-1) for a result
|
| 573 |
*/
|
|
@@ -575,12 +575,12 @@ class NeuralCortex {
|
|
| 575 |
if (!text) return 0;
|
| 576 |
const queryTerms = query.toLowerCase().split(' ');
|
| 577 |
const textLower = text.toLowerCase();
|
| 578 |
-
|
| 579 |
let matches = 0;
|
| 580 |
for (const term of queryTerms) {
|
| 581 |
if (textLower.includes(term)) matches++;
|
| 582 |
}
|
| 583 |
-
|
| 584 |
return matches / queryTerms.length;
|
| 585 |
}
|
| 586 |
|
|
@@ -589,7 +589,7 @@ class NeuralCortex {
|
|
| 589 |
*/
|
| 590 |
private async getDecisionHistory(query: string, context?: CortexQuery['context']): Promise<CortexResult[]> {
|
| 591 |
try {
|
| 592 |
-
|
| 593 |
MATCH (m:Message)
|
| 594 |
WHERE any(word IN ['decision', 'approved', 'rejected', 'selected', 'chose']
|
| 595 |
WHERE toLower(m.body) CONTAINS word)
|
|
@@ -599,17 +599,17 @@ class NeuralCortex {
|
|
| 599 |
LIMIT 20
|
| 600 |
`, { query });
|
| 601 |
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
} catch {
|
| 614 |
return [];
|
| 615 |
}
|
|
|
|
| 67 |
|
| 68 |
class NeuralCortex {
|
| 69 |
private static instance: NeuralCortex;
|
| 70 |
+
|
| 71 |
public static getInstance(): NeuralCortex {
|
| 72 |
if (!NeuralCortex.instance) {
|
| 73 |
NeuralCortex.instance = new NeuralCortex();
|
|
|
|
| 86 |
}> {
|
| 87 |
const entities = this.extractEntities(message.body);
|
| 88 |
const concepts = this.extractConcepts(message.body);
|
| 89 |
+
|
| 90 |
// 1. Create message node in GRAPH (Neo4j)
|
| 91 |
await neo4jAdapter.runQuery(`
|
| 92 |
CREATE (m:Message {
|
|
|
|
| 158 |
*/
|
| 159 |
private extractEntities(text: string): string[] {
|
| 160 |
const entities: string[] = [];
|
| 161 |
+
|
| 162 |
// @mentions
|
| 163 |
const mentions = text.match(/@(\w+)/g);
|
| 164 |
if (mentions) entities.push(...mentions.map(m => m.slice(1)));
|
| 165 |
+
|
| 166 |
// File paths
|
| 167 |
const files = text.match(/[\w-]+\.(ts|js|tsx|jsx|json|md|py|yaml|yml|sql)/gi);
|
| 168 |
if (files) entities.push(...files);
|
| 169 |
+
|
| 170 |
// Component/Class names (PascalCase)
|
| 171 |
const components = text.match(/\b[A-Z][a-zA-Z]+(?:Widget|Service|Controller|Adapter|Component|Provider|Handler)\b/g);
|
| 172 |
if (components) entities.push(...components);
|
| 173 |
+
|
| 174 |
// URLs
|
| 175 |
const urls = text.match(/https?:\/\/[^\s]+/g);
|
| 176 |
if (urls) entities.push(...urls);
|
| 177 |
+
|
| 178 |
return [...new Set(entities)];
|
| 179 |
}
|
| 180 |
|
|
|
|
| 184 |
private extractConcepts(text: string): string[] {
|
| 185 |
const concepts: string[] = [];
|
| 186 |
const textLower = text.toLowerCase();
|
| 187 |
+
|
| 188 |
// Technologies
|
| 189 |
+
const techs = ['neo4j', 'react', 'typescript', 'docker', 'kubernetes', 'api', 'websocket', 'mcp', 'graphql', 'rest', 'postgresql', 'redis', 'vector', 'pgvector', 'rag'];
|
| 190 |
techs.forEach(t => { if (textLower.includes(t)) concepts.push(t); });
|
| 191 |
+
|
| 192 |
// Actions
|
| 193 |
const actions = ['deploy', 'review', 'test', 'refactor', 'implement', 'fix', 'create', 'delete', 'update', 'analyze', 'research', 'architect'];
|
| 194 |
actions.forEach(a => { if (textLower.includes(a)) concepts.push(a); });
|
| 195 |
+
|
| 196 |
// Domains
|
| 197 |
const domains = ['security', 'performance', 'architecture', 'authentication', 'authorization', 'database', 'frontend', 'backend', 'infrastructure', 'ai', 'agents'];
|
| 198 |
domains.forEach(d => { if (textLower.includes(d)) concepts.push(d); });
|
| 199 |
+
|
| 200 |
return [...new Set(concepts)];
|
| 201 |
}
|
| 202 |
|
|
|
|
| 211 |
MERGE (m)-[:MENTIONS]->(e)
|
| 212 |
RETURN e.name as linked
|
| 213 |
`, { messageId, entity });
|
| 214 |
+
|
| 215 |
return result[0]?.linked || null;
|
| 216 |
} catch {
|
| 217 |
return null;
|
|
|
|
| 241 |
*/
|
| 242 |
async query(input: CortexQuery): Promise<CortexResult[]> {
|
| 243 |
const results: CortexResult[] = [];
|
| 244 |
+
|
| 245 |
switch (input.type) {
|
| 246 |
case 'search':
|
| 247 |
return await this.hybridSearch(input.query, input.context);
|
| 248 |
+
|
| 249 |
case 'pattern':
|
| 250 |
return await this.findPatterns(input.query, input.context);
|
| 251 |
+
|
| 252 |
case 'insight':
|
| 253 |
return await this.generateInsights(input.query, input.context);
|
| 254 |
+
|
| 255 |
case 'history':
|
| 256 |
return await this.getDecisionHistory(input.query, input.context);
|
| 257 |
+
|
| 258 |
case 'chat':
|
| 259 |
default:
|
| 260 |
// 1. Search Chat History (Vector + Keyword)
|
| 261 |
const chatResults = await this.searchMessages(input.query);
|
| 262 |
// 2. Search Knowledge Graph (Keyword/Hybrid)
|
| 263 |
const graphResults = await this.hybridSearch(input.query, input.context);
|
| 264 |
+
|
| 265 |
return [...chatResults, ...graphResults].sort((a, b) => b.relevance - a.relevance);
|
| 266 |
}
|
| 267 |
}
|
|
|
|
| 313 |
|
| 314 |
for (const r of graphResults) {
|
| 315 |
if (!existingIds.has(r.m.properties.id)) {
|
| 316 |
+
results.push({
|
| 317 |
type: 'message' as const,
|
| 318 |
data: {
|
| 319 |
id: r.m.properties.id,
|
|
|
|
| 380 |
LIMIT 20
|
| 381 |
`, { query, nodeTypes });
|
| 382 |
|
| 383 |
+
// Merge results (simple dedup by name)
|
| 384 |
+
const existingNames = new Set(results.map(r => r.data.name));
|
| 385 |
+
|
| 386 |
+
for (const r of graphResults) {
|
| 387 |
+
const name = r.n.properties.name || r.n.properties.id;
|
| 388 |
+
if (!existingNames.has(name)) {
|
| 389 |
+
results.push({
|
| 390 |
type: 'node' as const,
|
| 391 |
data: {
|
| 392 |
name: name,
|
|
|
|
| 397 |
source: 'knowledge_graph',
|
| 398 |
connections: r.connections.filter((c: any) => c.target)
|
| 399 |
});
|
| 400 |
+
}
|
| 401 |
+
}
|
| 402 |
|
| 403 |
return results;
|
| 404 |
} catch {
|
|
|
|
| 567 |
return [];
|
| 568 |
}
|
| 569 |
}
|
| 570 |
+
|
| 571 |
/**
|
| 572 |
* Calculate relevance score (0-1) for a result
|
| 573 |
*/
|
|
|
|
| 575 |
if (!text) return 0;
|
| 576 |
const queryTerms = query.toLowerCase().split(' ');
|
| 577 |
const textLower = text.toLowerCase();
|
| 578 |
+
|
| 579 |
let matches = 0;
|
| 580 |
for (const term of queryTerms) {
|
| 581 |
if (textLower.includes(term)) matches++;
|
| 582 |
}
|
| 583 |
+
|
| 584 |
return matches / queryTerms.length;
|
| 585 |
}
|
| 586 |
|
|
|
|
| 589 |
*/
|
| 590 |
private async getDecisionHistory(query: string, context?: CortexQuery['context']): Promise<CortexResult[]> {
|
| 591 |
try {
|
| 592 |
+
const history = await neo4jAdapter.runQuery(`
|
| 593 |
MATCH (m:Message)
|
| 594 |
WHERE any(word IN ['decision', 'approved', 'rejected', 'selected', 'chose']
|
| 595 |
WHERE toLower(m.body) CONTAINS word)
|
|
|
|
| 599 |
LIMIT 20
|
| 600 |
`, { query });
|
| 601 |
|
| 602 |
+
return history.map((h: any) => ({
|
| 603 |
+
type: 'message',
|
| 604 |
+
data: {
|
| 605 |
+
id: h.m.properties.id,
|
| 606 |
+
body: h.m.properties.body,
|
| 607 |
+
agent: h.agent,
|
| 608 |
+
timestamp: h.m.properties.timestamp
|
| 609 |
+
},
|
| 610 |
+
relevance: 1,
|
| 611 |
+
source: 'history'
|
| 612 |
+
}));
|
| 613 |
} catch {
|
| 614 |
return [];
|
| 615 |
}
|
apps/backend/src/services/SelfHealingAdapter.ts
CHANGED
|
@@ -12,7 +12,7 @@ interface HealingResult {
|
|
| 12 |
latency: number;
|
| 13 |
}
|
| 14 |
|
| 15 |
-
class SelfHealingAdapter {
|
| 16 |
private static instance: SelfHealingAdapter;
|
| 17 |
private retryLimits: Map<ErrorSignature, number> = new Map();
|
| 18 |
private circuitBreakers: Map<string, boolean> = new Map(); // True = OPEN (Broken)
|
|
@@ -29,6 +29,25 @@ class SelfHealingAdapter {
|
|
| 29 |
return SelfHealingAdapter.instance;
|
| 30 |
}
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
/**
|
| 33 |
* 🛡️ THE OMEGA SHIELD: Main entry point for all system errors.
|
| 34 |
* Wraps any operation in a Lazarus Loop.
|
|
@@ -158,7 +177,7 @@ class SelfHealingAdapter {
|
|
| 158 |
* Used by KnowledgeCompiler.
|
| 159 |
*/
|
| 160 |
public getPredictiveAlerts(): any[] {
|
| 161 |
-
// In a real
|
| 162 |
// For now, we return basic alerts if circuit breakers are open.
|
| 163 |
const alerts: any[] = [];
|
| 164 |
this.circuitBreakers.forEach((isOpen, name) => {
|
|
|
|
| 12 |
latency: number;
|
| 13 |
}
|
| 14 |
|
| 15 |
+
export class SelfHealingAdapter {
|
| 16 |
private static instance: SelfHealingAdapter;
|
| 17 |
private retryLimits: Map<ErrorSignature, number> = new Map();
|
| 18 |
private circuitBreakers: Map<string, boolean> = new Map(); // True = OPEN (Broken)
|
|
|
|
| 29 |
return SelfHealingAdapter.instance;
|
| 30 |
}
|
| 31 |
|
| 32 |
+
// Wrapper compatibility for CortexController
|
| 33 |
+
public async handleError(error: any, source: string): Promise<void> {
|
| 34 |
+
await this.heal(error instanceof Error ? error : new Error(String(error)), source, {});
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
public async runStartupValidation(_fullCheck: boolean = false): Promise<{ success: boolean; services: any[] }> {
|
| 38 |
+
// Basic validation implementation
|
| 39 |
+
return {
|
| 40 |
+
success: true,
|
| 41 |
+
services: this.getSystemStatus().services
|
| 42 |
+
};
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
public async learnFromError(error: any, context: any, _solution?: any): Promise<void> {
|
| 46 |
+
// Integration with Knowledge Graph to learn from failures
|
| 47 |
+
console.log(`🧠 [LAZARUS] Analyzing failure pattern: ${error instanceof Error ? error.message : String(error)} in ${JSON.stringify(context)}`);
|
| 48 |
+
// Logic to store into FailureMemory would go here
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
/**
|
| 52 |
* 🛡️ THE OMEGA SHIELD: Main entry point for all system errors.
|
| 53 |
* Wraps any operation in a Lazarus Loop.
|
|
|
|
| 177 |
* Used by KnowledgeCompiler.
|
| 178 |
*/
|
| 179 |
public getPredictiveAlerts(): any[] {
|
| 180 |
+
// In a real implementations, this would analyze error frequencies from metricsService.
|
| 181 |
// For now, we return basic alerts if circuit breakers are open.
|
| 182 |
const alerts: any[] = [];
|
| 183 |
this.circuitBreakers.forEach((isOpen, name) => {
|
apps/backend/src/services/embeddings/LocalGPUEmbeddings.ts
CHANGED
|
@@ -3,10 +3,14 @@ import path from 'path';
|
|
| 3 |
import { EmbeddingProvider } from './EmbeddingService.js';
|
| 4 |
import { logger } from '../../utils/logger.js';
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
| 12 |
name = 'local-gpu';
|
|
@@ -16,14 +20,14 @@ export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
|
| 16 |
private isReady: boolean = false;
|
| 17 |
private pendingRequests: Map<number, { resolve: (val: any) => void; reject: (err: any) => void }> = new Map();
|
| 18 |
private requestCounter: number = 0;
|
| 19 |
-
private cleanup: () => void = () => {}; // Dynamic cleanup callback
|
| 20 |
|
| 21 |
async initialize(): Promise<void> {
|
| 22 |
if (this.isReady) return;
|
| 23 |
|
| 24 |
return new Promise((resolve, reject) => {
|
| 25 |
try {
|
| 26 |
-
const scriptPath = path.join(
|
| 27 |
logger.info(`🔌 Starting GPU Bridge: python3 ${scriptPath}`);
|
| 28 |
|
| 29 |
this.process = spawn('python3', [scriptPath]);
|
|
@@ -33,10 +37,10 @@ export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
|
| 33 |
const lines = data.toString().split('\n');
|
| 34 |
for (const line of lines) {
|
| 35 |
if (!line.trim()) continue;
|
| 36 |
-
|
| 37 |
try {
|
| 38 |
const response = JSON.parse(line);
|
| 39 |
-
|
| 40 |
// Initial Ready Signal
|
| 41 |
if (response.status === 'ready') {
|
| 42 |
logger.info(`🚀 GPU Bridge Ready on device: ${response.device}`);
|
|
@@ -49,10 +53,10 @@ export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
|
| 49 |
// For a more robust solution with concurrency, we'd need Request IDs
|
| 50 |
// But for this implementation, we assume one request at a time via the Service lock
|
| 51 |
// or we rely on the strictly ordered stdio.
|
| 52 |
-
|
| 53 |
// NOTE: This simplified implementation assumes sequential processing (awaiting each call)
|
| 54 |
// which matches the current usage in EmbeddingService.
|
| 55 |
-
|
| 56 |
} catch (e) {
|
| 57 |
logger.error(`GPU Bridge parse error: ${e} [Line: ${line}]`);
|
| 58 |
}
|
|
@@ -76,11 +80,11 @@ export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
|
| 76 |
|
| 77 |
// Timeout backup
|
| 78 |
setTimeout(() => {
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
}, 10000);
|
| 85 |
|
| 86 |
} catch (error) {
|
|
@@ -95,43 +99,43 @@ export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
|
| 95 |
// We just need to ensure we don't interleave writes before reads are done.
|
| 96 |
private async execute<T>(payload: any): Promise<T> {
|
| 97 |
if (!this.process || !this.isReady) {
|
| 98 |
-
|
| 99 |
}
|
| 100 |
|
| 101 |
return new Promise((resolve, reject) => {
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
}
|
| 119 |
-
return; // Handled
|
| 120 |
-
} catch (e) {
|
| 121 |
-
// partial line? wait for next chunk
|
| 122 |
-
}
|
| 123 |
}
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
this.
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
});
|
| 136 |
}
|
| 137 |
|
|
|
|
| 3 |
import { EmbeddingProvider } from './EmbeddingService.js';
|
| 4 |
import { logger } from '../../utils/logger.js';
|
| 5 |
|
| 6 |
+
import { fileURLToPath } from 'url';
|
| 7 |
+
|
| 8 |
+
// ESM/CJS compatible directory logic
|
| 9 |
+
const currentDir = typeof import.meta !== 'undefined' && import.meta.url
|
| 10 |
+
? path.dirname(fileURLToPath(import.meta.url))
|
| 11 |
+
: process.cwd(); // Fallback for CJS if specific __dirname is tricky, usually path.dirname(__filename) but __filename is also issue.
|
| 12 |
+
// Better: assume ESM for this modern app or just use process.cwd() relative path if needed but finding built script is safer.
|
| 13 |
+
|
| 14 |
|
| 15 |
export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
| 16 |
name = 'local-gpu';
|
|
|
|
| 20 |
private isReady: boolean = false;
|
| 21 |
private pendingRequests: Map<number, { resolve: (val: any) => void; reject: (err: any) => void }> = new Map();
|
| 22 |
private requestCounter: number = 0;
|
| 23 |
+
private cleanup: () => void = () => { }; // Dynamic cleanup callback
|
| 24 |
|
| 25 |
async initialize(): Promise<void> {
|
| 26 |
if (this.isReady) return;
|
| 27 |
|
| 28 |
return new Promise((resolve, reject) => {
|
| 29 |
try {
|
| 30 |
+
const scriptPath = path.join(currentDir, 'gpu_bridge.py');
|
| 31 |
logger.info(`🔌 Starting GPU Bridge: python3 ${scriptPath}`);
|
| 32 |
|
| 33 |
this.process = spawn('python3', [scriptPath]);
|
|
|
|
| 37 |
const lines = data.toString().split('\n');
|
| 38 |
for (const line of lines) {
|
| 39 |
if (!line.trim()) continue;
|
| 40 |
+
|
| 41 |
try {
|
| 42 |
const response = JSON.parse(line);
|
| 43 |
+
|
| 44 |
// Initial Ready Signal
|
| 45 |
if (response.status === 'ready') {
|
| 46 |
logger.info(`🚀 GPU Bridge Ready on device: ${response.device}`);
|
|
|
|
| 53 |
// For a more robust solution with concurrency, we'd need Request IDs
|
| 54 |
// But for this implementation, we assume one request at a time via the Service lock
|
| 55 |
// or we rely on the strictly ordered stdio.
|
| 56 |
+
|
| 57 |
// NOTE: This simplified implementation assumes sequential processing (awaiting each call)
|
| 58 |
// which matches the current usage in EmbeddingService.
|
| 59 |
+
|
| 60 |
} catch (e) {
|
| 61 |
logger.error(`GPU Bridge parse error: ${e} [Line: ${line}]`);
|
| 62 |
}
|
|
|
|
| 80 |
|
| 81 |
// Timeout backup
|
| 82 |
setTimeout(() => {
|
| 83 |
+
if (!this.isReady) {
|
| 84 |
+
// If it takes too long, we might just be downloading the model (can take time)
|
| 85 |
+
// So we don't reject immediately, but warn.
|
| 86 |
+
logger.warn('⏳ GPU Bridge taking longer than expected (model download?)...');
|
| 87 |
+
}
|
| 88 |
}, 10000);
|
| 89 |
|
| 90 |
} catch (error) {
|
|
|
|
| 99 |
// We just need to ensure we don't interleave writes before reads are done.
|
| 100 |
private async execute<T>(payload: any): Promise<T> {
|
| 101 |
if (!this.process || !this.isReady) {
|
| 102 |
+
await this.initialize();
|
| 103 |
}
|
| 104 |
|
| 105 |
return new Promise((resolve, reject) => {
|
| 106 |
+
// Simple one-off listener for the next line of output
|
| 107 |
+
// This assumes strictly sequential execution provided by the await in the caller
|
| 108 |
+
const listener = (data: Buffer) => {
|
| 109 |
+
const lines = data.toString().split('\n').filter(l => l.trim());
|
| 110 |
+
for (const line of lines) {
|
| 111 |
+
try {
|
| 112 |
+
const response = JSON.parse(line);
|
| 113 |
+
// Ignore status messages if they appear late
|
| 114 |
+
if (response.status) continue;
|
| 115 |
+
|
| 116 |
+
if (response.error) {
|
| 117 |
+
this.cleanup();
|
| 118 |
+
reject(new Error(response.error));
|
| 119 |
+
} else {
|
| 120 |
+
this.cleanup();
|
| 121 |
+
resolve(response);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
}
|
| 123 |
+
return; // Handled
|
| 124 |
+
} catch (e) {
|
| 125 |
+
// partial line? wait for next chunk
|
| 126 |
+
}
|
| 127 |
+
}
|
| 128 |
+
};
|
| 129 |
+
|
| 130 |
+
const cleanup = () => {
|
| 131 |
+
this.process?.stdout?.off('data', listener);
|
| 132 |
+
this.cleanup = () => { }; // prevent double call
|
| 133 |
+
};
|
| 134 |
+
// Store cleanup on this scope so the listener callback can call it
|
| 135 |
+
(this as any).cleanup = cleanup;
|
| 136 |
+
|
| 137 |
+
this.process!.stdout!.on('data', listener);
|
| 138 |
+
this.process!.stdin!.write(JSON.stringify(payload) + '\n');
|
| 139 |
});
|
| 140 |
}
|
| 141 |
|
apps/backend/src/tests/autonomous.integration.test.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
| 15 |
|
| 16 |
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
|
| 17 |
import { initializeDatabase, getDatabase, getSqlJsDatabase } from '../database/index.js';
|
|
|
|
| 18 |
|
| 19 |
// Mock Embeddings to avoid ONNX/Float32Array issues in tests
|
| 20 |
vi.mock('../platform/embeddings/TransformersEmbeddings', () => ({
|
|
@@ -85,7 +86,7 @@ describe('PatternMemory - Enterprise Persistence', () => {
|
|
| 85 |
|
| 86 |
beforeEach(async () => {
|
| 87 |
const { PatternMemory } = await import('../mcp/memory/PatternMemory.js');
|
| 88 |
-
patternMemory = new PatternMemory(db);
|
| 89 |
});
|
| 90 |
|
| 91 |
it('should record query patterns', async () => {
|
|
@@ -212,7 +213,7 @@ describe('FailureMemory - Self-Healing Intelligence', () => {
|
|
| 212 |
|
| 213 |
beforeEach(async () => {
|
| 214 |
const { FailureMemory } = await import('../mcp/memory/FailureMemory.js');
|
| 215 |
-
failureMemory = new FailureMemory(db);
|
| 216 |
});
|
| 217 |
|
| 218 |
it('should record failures with context', async () => {
|
|
|
|
| 15 |
|
| 16 |
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
|
| 17 |
import { initializeDatabase, getDatabase, getSqlJsDatabase } from '../database/index.js';
|
| 18 |
+
import { SqlJsStorageAdapter } from '../mcp/memory/StorageAdapter.js';
|
| 19 |
|
| 20 |
// Mock Embeddings to avoid ONNX/Float32Array issues in tests
|
| 21 |
vi.mock('../platform/embeddings/TransformersEmbeddings', () => ({
|
|
|
|
| 86 |
|
| 87 |
beforeEach(async () => {
|
| 88 |
const { PatternMemory } = await import('../mcp/memory/PatternMemory.js');
|
| 89 |
+
patternMemory = new PatternMemory(new SqlJsStorageAdapter(db));
|
| 90 |
});
|
| 91 |
|
| 92 |
it('should record query patterns', async () => {
|
|
|
|
| 213 |
|
| 214 |
beforeEach(async () => {
|
| 215 |
const { FailureMemory } = await import('../mcp/memory/FailureMemory.js');
|
| 216 |
+
failureMemory = new FailureMemory(new SqlJsStorageAdapter(db));
|
| 217 |
});
|
| 218 |
|
| 219 |
it('should record failures with context', async () => {
|
package-lock.json
CHANGED
|
@@ -115,11 +115,48 @@
|
|
| 115 |
"@types/uuid": "^9.0.7",
|
| 116 |
"@types/ws": "^8.5.10",
|
| 117 |
"@types/xml2js": "^0.4.14",
|
|
|
|
|
|
|
| 118 |
"esbuild": "^0.24.2",
|
|
|
|
| 119 |
"tsx": "^4.20.6",
|
| 120 |
"typescript": "~5.8.2"
|
| 121 |
}
|
| 122 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
"apps/backend/node_modules/@napi-rs/canvas": {
|
| 124 |
"version": "0.1.84",
|
| 125 |
"license": "MIT",
|
|
@@ -151,6 +188,473 @@
|
|
| 151 |
"undici-types": "~6.21.0"
|
| 152 |
}
|
| 153 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
"apps/backend/node_modules/pdfjs-dist": {
|
| 155 |
"version": "5.4.449",
|
| 156 |
"license": "Apache-2.0",
|
|
@@ -161,6 +665,36 @@
|
|
| 161 |
"@napi-rs/canvas": "^0.1.81"
|
| 162 |
}
|
| 163 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
"apps/backend/packages/domain-types": {
|
| 165 |
"name": "@widget-tdc/domain-types",
|
| 166 |
"version": "1.0.0",
|
|
@@ -3325,6 +3859,46 @@
|
|
| 3325 |
"node": ">=18.18.0"
|
| 3326 |
}
|
| 3327 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3328 |
"node_modules/@humanwhocodes/module-importer": {
|
| 3329 |
"version": "1.0.1",
|
| 3330 |
"dev": true,
|
|
@@ -3337,6 +3911,14 @@
|
|
| 3337 |
"url": "https://github.com/sponsors/nzakas"
|
| 3338 |
}
|
| 3339 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3340 |
"node_modules/@humanwhocodes/retry": {
|
| 3341 |
"version": "0.4.3",
|
| 3342 |
"dev": true,
|
|
@@ -7500,6 +8082,16 @@
|
|
| 7500 |
"url": "https://github.com/sponsors/ljharb"
|
| 7501 |
}
|
| 7502 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7503 |
"node_modules/array.prototype.findlast": {
|
| 7504 |
"version": "1.2.5",
|
| 7505 |
"dev": true,
|
|
@@ -9638,6 +10230,19 @@
|
|
| 9638 |
"node": ">=0.3.1"
|
| 9639 |
}
|
| 9640 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9641 |
"node_modules/dlv": {
|
| 9642 |
"version": "1.1.3",
|
| 9643 |
"license": "MIT"
|
|
@@ -11885,6 +12490,37 @@
|
|
| 11885 |
"url": "https://github.com/sponsors/ljharb"
|
| 11886 |
}
|
| 11887 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11888 |
"node_modules/gopd": {
|
| 11889 |
"version": "1.2.0",
|
| 11890 |
"license": "MIT",
|
|
@@ -12655,6 +13291,16 @@
|
|
| 12655 |
"node": ">=0.10.0"
|
| 12656 |
}
|
| 12657 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12658 |
"node_modules/is-plain-obj": {
|
| 12659 |
"version": "4.1.0",
|
| 12660 |
"license": "MIT",
|
|
@@ -17154,6 +17800,16 @@
|
|
| 17154 |
"node": ">=18"
|
| 17155 |
}
|
| 17156 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17157 |
"node_modules/smart-buffer": {
|
| 17158 |
"version": "4.2.0",
|
| 17159 |
"license": "MIT",
|
|
@@ -17999,6 +18655,13 @@
|
|
| 17999 |
"version": "1.0.0",
|
| 18000 |
"license": "MIT"
|
| 18001 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18002 |
"node_modules/thenify": {
|
| 18003 |
"version": "3.3.1",
|
| 18004 |
"license": "MIT",
|
|
|
|
| 115 |
"@types/uuid": "^9.0.7",
|
| 116 |
"@types/ws": "^8.5.10",
|
| 117 |
"@types/xml2js": "^0.4.14",
|
| 118 |
+
"@typescript-eslint/eslint-plugin": "^7.16.0",
|
| 119 |
+
"@typescript-eslint/parser": "^7.16.0",
|
| 120 |
"esbuild": "^0.24.2",
|
| 121 |
+
"eslint": "^8.57.0",
|
| 122 |
"tsx": "^4.20.6",
|
| 123 |
"typescript": "~5.8.2"
|
| 124 |
}
|
| 125 |
},
|
| 126 |
+
"apps/backend/node_modules/@eslint/eslintrc": {
|
| 127 |
+
"version": "2.1.4",
|
| 128 |
+
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
|
| 129 |
+
"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
|
| 130 |
+
"dev": true,
|
| 131 |
+
"license": "MIT",
|
| 132 |
+
"dependencies": {
|
| 133 |
+
"ajv": "^6.12.4",
|
| 134 |
+
"debug": "^4.3.2",
|
| 135 |
+
"espree": "^9.6.0",
|
| 136 |
+
"globals": "^13.19.0",
|
| 137 |
+
"ignore": "^5.2.0",
|
| 138 |
+
"import-fresh": "^3.2.1",
|
| 139 |
+
"js-yaml": "^4.1.0",
|
| 140 |
+
"minimatch": "^3.1.2",
|
| 141 |
+
"strip-json-comments": "^3.1.1"
|
| 142 |
+
},
|
| 143 |
+
"engines": {
|
| 144 |
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
| 145 |
+
},
|
| 146 |
+
"funding": {
|
| 147 |
+
"url": "https://opencollective.com/eslint"
|
| 148 |
+
}
|
| 149 |
+
},
|
| 150 |
+
"apps/backend/node_modules/@eslint/js": {
|
| 151 |
+
"version": "8.57.0",
|
| 152 |
+
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
|
| 153 |
+
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
|
| 154 |
+
"dev": true,
|
| 155 |
+
"license": "MIT",
|
| 156 |
+
"engines": {
|
| 157 |
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
| 158 |
+
}
|
| 159 |
+
},
|
| 160 |
"apps/backend/node_modules/@napi-rs/canvas": {
|
| 161 |
"version": "0.1.84",
|
| 162 |
"license": "MIT",
|
|
|
|
| 188 |
"undici-types": "~6.21.0"
|
| 189 |
}
|
| 190 |
},
|
| 191 |
+
"apps/backend/node_modules/@typescript-eslint/eslint-plugin": {
|
| 192 |
+
"version": "7.16.0",
|
| 193 |
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz",
|
| 194 |
+
"integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==",
|
| 195 |
+
"dev": true,
|
| 196 |
+
"license": "MIT",
|
| 197 |
+
"dependencies": {
|
| 198 |
+
"@eslint-community/regexpp": "^4.10.0",
|
| 199 |
+
"@typescript-eslint/scope-manager": "7.16.0",
|
| 200 |
+
"@typescript-eslint/type-utils": "7.16.0",
|
| 201 |
+
"@typescript-eslint/utils": "7.16.0",
|
| 202 |
+
"@typescript-eslint/visitor-keys": "7.16.0",
|
| 203 |
+
"graphemer": "^1.4.0",
|
| 204 |
+
"ignore": "^5.3.1",
|
| 205 |
+
"natural-compare": "^1.4.0",
|
| 206 |
+
"ts-api-utils": "^1.3.0"
|
| 207 |
+
},
|
| 208 |
+
"engines": {
|
| 209 |
+
"node": "^18.18.0 || >=20.0.0"
|
| 210 |
+
},
|
| 211 |
+
"funding": {
|
| 212 |
+
"type": "opencollective",
|
| 213 |
+
"url": "https://opencollective.com/typescript-eslint"
|
| 214 |
+
},
|
| 215 |
+
"peerDependencies": {
|
| 216 |
+
"@typescript-eslint/parser": "^7.0.0",
|
| 217 |
+
"eslint": "^8.56.0"
|
| 218 |
+
},
|
| 219 |
+
"peerDependenciesMeta": {
|
| 220 |
+
"typescript": {
|
| 221 |
+
"optional": true
|
| 222 |
+
}
|
| 223 |
+
}
|
| 224 |
+
},
|
| 225 |
+
"apps/backend/node_modules/@typescript-eslint/eslint-plugin/node_modules/ts-api-utils": {
|
| 226 |
+
"version": "1.4.3",
|
| 227 |
+
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
|
| 228 |
+
"integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
|
| 229 |
+
"dev": true,
|
| 230 |
+
"license": "MIT",
|
| 231 |
+
"engines": {
|
| 232 |
+
"node": ">=16"
|
| 233 |
+
},
|
| 234 |
+
"peerDependencies": {
|
| 235 |
+
"typescript": ">=4.2.0"
|
| 236 |
+
}
|
| 237 |
+
},
|
| 238 |
+
"apps/backend/node_modules/@typescript-eslint/parser": {
|
| 239 |
+
"version": "7.16.0",
|
| 240 |
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz",
|
| 241 |
+
"integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==",
|
| 242 |
+
"dev": true,
|
| 243 |
+
"license": "BSD-2-Clause",
|
| 244 |
+
"peer": true,
|
| 245 |
+
"dependencies": {
|
| 246 |
+
"@typescript-eslint/scope-manager": "7.16.0",
|
| 247 |
+
"@typescript-eslint/types": "7.16.0",
|
| 248 |
+
"@typescript-eslint/typescript-estree": "7.16.0",
|
| 249 |
+
"@typescript-eslint/visitor-keys": "7.16.0",
|
| 250 |
+
"debug": "^4.3.4"
|
| 251 |
+
},
|
| 252 |
+
"engines": {
|
| 253 |
+
"node": "^18.18.0 || >=20.0.0"
|
| 254 |
+
},
|
| 255 |
+
"funding": {
|
| 256 |
+
"type": "opencollective",
|
| 257 |
+
"url": "https://opencollective.com/typescript-eslint"
|
| 258 |
+
},
|
| 259 |
+
"peerDependencies": {
|
| 260 |
+
"eslint": "^8.56.0"
|
| 261 |
+
},
|
| 262 |
+
"peerDependenciesMeta": {
|
| 263 |
+
"typescript": {
|
| 264 |
+
"optional": true
|
| 265 |
+
}
|
| 266 |
+
}
|
| 267 |
+
},
|
| 268 |
+
"apps/backend/node_modules/@typescript-eslint/scope-manager": {
|
| 269 |
+
"version": "7.16.0",
|
| 270 |
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz",
|
| 271 |
+
"integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==",
|
| 272 |
+
"dev": true,
|
| 273 |
+
"license": "MIT",
|
| 274 |
+
"dependencies": {
|
| 275 |
+
"@typescript-eslint/types": "7.16.0",
|
| 276 |
+
"@typescript-eslint/visitor-keys": "7.16.0"
|
| 277 |
+
},
|
| 278 |
+
"engines": {
|
| 279 |
+
"node": "^18.18.0 || >=20.0.0"
|
| 280 |
+
},
|
| 281 |
+
"funding": {
|
| 282 |
+
"type": "opencollective",
|
| 283 |
+
"url": "https://opencollective.com/typescript-eslint"
|
| 284 |
+
}
|
| 285 |
+
},
|
| 286 |
+
"apps/backend/node_modules/@typescript-eslint/type-utils": {
|
| 287 |
+
"version": "7.16.0",
|
| 288 |
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz",
|
| 289 |
+
"integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==",
|
| 290 |
+
"dev": true,
|
| 291 |
+
"license": "MIT",
|
| 292 |
+
"dependencies": {
|
| 293 |
+
"@typescript-eslint/typescript-estree": "7.16.0",
|
| 294 |
+
"@typescript-eslint/utils": "7.16.0",
|
| 295 |
+
"debug": "^4.3.4",
|
| 296 |
+
"ts-api-utils": "^1.3.0"
|
| 297 |
+
},
|
| 298 |
+
"engines": {
|
| 299 |
+
"node": "^18.18.0 || >=20.0.0"
|
| 300 |
+
},
|
| 301 |
+
"funding": {
|
| 302 |
+
"type": "opencollective",
|
| 303 |
+
"url": "https://opencollective.com/typescript-eslint"
|
| 304 |
+
},
|
| 305 |
+
"peerDependencies": {
|
| 306 |
+
"eslint": "^8.56.0"
|
| 307 |
+
},
|
| 308 |
+
"peerDependenciesMeta": {
|
| 309 |
+
"typescript": {
|
| 310 |
+
"optional": true
|
| 311 |
+
}
|
| 312 |
+
}
|
| 313 |
+
},
|
| 314 |
+
"apps/backend/node_modules/@typescript-eslint/type-utils/node_modules/ts-api-utils": {
|
| 315 |
+
"version": "1.4.3",
|
| 316 |
+
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
|
| 317 |
+
"integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
|
| 318 |
+
"dev": true,
|
| 319 |
+
"license": "MIT",
|
| 320 |
+
"engines": {
|
| 321 |
+
"node": ">=16"
|
| 322 |
+
},
|
| 323 |
+
"peerDependencies": {
|
| 324 |
+
"typescript": ">=4.2.0"
|
| 325 |
+
}
|
| 326 |
+
},
|
| 327 |
+
"apps/backend/node_modules/@typescript-eslint/types": {
|
| 328 |
+
"version": "7.16.0",
|
| 329 |
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz",
|
| 330 |
+
"integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==",
|
| 331 |
+
"dev": true,
|
| 332 |
+
"license": "MIT",
|
| 333 |
+
"engines": {
|
| 334 |
+
"node": "^18.18.0 || >=20.0.0"
|
| 335 |
+
},
|
| 336 |
+
"funding": {
|
| 337 |
+
"type": "opencollective",
|
| 338 |
+
"url": "https://opencollective.com/typescript-eslint"
|
| 339 |
+
}
|
| 340 |
+
},
|
| 341 |
+
"apps/backend/node_modules/@typescript-eslint/typescript-estree": {
|
| 342 |
+
"version": "7.16.0",
|
| 343 |
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz",
|
| 344 |
+
"integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==",
|
| 345 |
+
"dev": true,
|
| 346 |
+
"license": "BSD-2-Clause",
|
| 347 |
+
"dependencies": {
|
| 348 |
+
"@typescript-eslint/types": "7.16.0",
|
| 349 |
+
"@typescript-eslint/visitor-keys": "7.16.0",
|
| 350 |
+
"debug": "^4.3.4",
|
| 351 |
+
"globby": "^11.1.0",
|
| 352 |
+
"is-glob": "^4.0.3",
|
| 353 |
+
"minimatch": "^9.0.4",
|
| 354 |
+
"semver": "^7.6.0",
|
| 355 |
+
"ts-api-utils": "^1.3.0"
|
| 356 |
+
},
|
| 357 |
+
"engines": {
|
| 358 |
+
"node": "^18.18.0 || >=20.0.0"
|
| 359 |
+
},
|
| 360 |
+
"funding": {
|
| 361 |
+
"type": "opencollective",
|
| 362 |
+
"url": "https://opencollective.com/typescript-eslint"
|
| 363 |
+
},
|
| 364 |
+
"peerDependenciesMeta": {
|
| 365 |
+
"typescript": {
|
| 366 |
+
"optional": true
|
| 367 |
+
}
|
| 368 |
+
}
|
| 369 |
+
},
|
| 370 |
+
"apps/backend/node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
| 371 |
+
"version": "2.0.2",
|
| 372 |
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
| 373 |
+
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
| 374 |
+
"dev": true,
|
| 375 |
+
"license": "MIT",
|
| 376 |
+
"dependencies": {
|
| 377 |
+
"balanced-match": "^1.0.0"
|
| 378 |
+
}
|
| 379 |
+
},
|
| 380 |
+
"apps/backend/node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
|
| 381 |
+
"version": "9.0.5",
|
| 382 |
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
| 383 |
+
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
| 384 |
+
"dev": true,
|
| 385 |
+
"license": "ISC",
|
| 386 |
+
"dependencies": {
|
| 387 |
+
"brace-expansion": "^2.0.1"
|
| 388 |
+
},
|
| 389 |
+
"engines": {
|
| 390 |
+
"node": ">=16 || 14 >=14.17"
|
| 391 |
+
},
|
| 392 |
+
"funding": {
|
| 393 |
+
"url": "https://github.com/sponsors/isaacs"
|
| 394 |
+
}
|
| 395 |
+
},
|
| 396 |
+
"apps/backend/node_modules/@typescript-eslint/typescript-estree/node_modules/ts-api-utils": {
|
| 397 |
+
"version": "1.4.3",
|
| 398 |
+
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
|
| 399 |
+
"integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
|
| 400 |
+
"dev": true,
|
| 401 |
+
"license": "MIT",
|
| 402 |
+
"engines": {
|
| 403 |
+
"node": ">=16"
|
| 404 |
+
},
|
| 405 |
+
"peerDependencies": {
|
| 406 |
+
"typescript": ">=4.2.0"
|
| 407 |
+
}
|
| 408 |
+
},
|
| 409 |
+
"apps/backend/node_modules/@typescript-eslint/utils": {
|
| 410 |
+
"version": "7.16.0",
|
| 411 |
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz",
|
| 412 |
+
"integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==",
|
| 413 |
+
"dev": true,
|
| 414 |
+
"license": "MIT",
|
| 415 |
+
"dependencies": {
|
| 416 |
+
"@eslint-community/eslint-utils": "^4.4.0",
|
| 417 |
+
"@typescript-eslint/scope-manager": "7.16.0",
|
| 418 |
+
"@typescript-eslint/types": "7.16.0",
|
| 419 |
+
"@typescript-eslint/typescript-estree": "7.16.0"
|
| 420 |
+
},
|
| 421 |
+
"engines": {
|
| 422 |
+
"node": "^18.18.0 || >=20.0.0"
|
| 423 |
+
},
|
| 424 |
+
"funding": {
|
| 425 |
+
"type": "opencollective",
|
| 426 |
+
"url": "https://opencollective.com/typescript-eslint"
|
| 427 |
+
},
|
| 428 |
+
"peerDependencies": {
|
| 429 |
+
"eslint": "^8.56.0"
|
| 430 |
+
}
|
| 431 |
+
},
|
| 432 |
+
"apps/backend/node_modules/@typescript-eslint/visitor-keys": {
|
| 433 |
+
"version": "7.16.0",
|
| 434 |
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz",
|
| 435 |
+
"integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==",
|
| 436 |
+
"dev": true,
|
| 437 |
+
"license": "MIT",
|
| 438 |
+
"dependencies": {
|
| 439 |
+
"@typescript-eslint/types": "7.16.0",
|
| 440 |
+
"eslint-visitor-keys": "^3.4.3"
|
| 441 |
+
},
|
| 442 |
+
"engines": {
|
| 443 |
+
"node": "^18.18.0 || >=20.0.0"
|
| 444 |
+
},
|
| 445 |
+
"funding": {
|
| 446 |
+
"type": "opencollective",
|
| 447 |
+
"url": "https://opencollective.com/typescript-eslint"
|
| 448 |
+
}
|
| 449 |
+
},
|
| 450 |
+
"apps/backend/node_modules/ajv": {
|
| 451 |
+
"version": "6.12.6",
|
| 452 |
+
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
| 453 |
+
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
| 454 |
+
"dev": true,
|
| 455 |
+
"license": "MIT",
|
| 456 |
+
"dependencies": {
|
| 457 |
+
"fast-deep-equal": "^3.1.1",
|
| 458 |
+
"fast-json-stable-stringify": "^2.0.0",
|
| 459 |
+
"json-schema-traverse": "^0.4.1",
|
| 460 |
+
"uri-js": "^4.2.2"
|
| 461 |
+
},
|
| 462 |
+
"funding": {
|
| 463 |
+
"type": "github",
|
| 464 |
+
"url": "https://github.com/sponsors/epoberezkin"
|
| 465 |
+
}
|
| 466 |
+
},
|
| 467 |
+
"apps/backend/node_modules/brace-expansion": {
|
| 468 |
+
"version": "1.1.12",
|
| 469 |
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
| 470 |
+
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
| 471 |
+
"dev": true,
|
| 472 |
+
"license": "MIT",
|
| 473 |
+
"dependencies": {
|
| 474 |
+
"balanced-match": "^1.0.0",
|
| 475 |
+
"concat-map": "0.0.1"
|
| 476 |
+
}
|
| 477 |
+
},
|
| 478 |
+
"apps/backend/node_modules/doctrine": {
|
| 479 |
+
"version": "3.0.0",
|
| 480 |
+
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
| 481 |
+
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
|
| 482 |
+
"dev": true,
|
| 483 |
+
"license": "Apache-2.0",
|
| 484 |
+
"dependencies": {
|
| 485 |
+
"esutils": "^2.0.2"
|
| 486 |
+
},
|
| 487 |
+
"engines": {
|
| 488 |
+
"node": ">=6.0.0"
|
| 489 |
+
}
|
| 490 |
+
},
|
| 491 |
+
"apps/backend/node_modules/eslint": {
|
| 492 |
+
"version": "8.57.0",
|
| 493 |
+
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
|
| 494 |
+
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
|
| 495 |
+
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
| 496 |
+
"dev": true,
|
| 497 |
+
"license": "MIT",
|
| 498 |
+
"peer": true,
|
| 499 |
+
"dependencies": {
|
| 500 |
+
"@eslint-community/eslint-utils": "^4.2.0",
|
| 501 |
+
"@eslint-community/regexpp": "^4.6.1",
|
| 502 |
+
"@eslint/eslintrc": "^2.1.4",
|
| 503 |
+
"@eslint/js": "8.57.0",
|
| 504 |
+
"@humanwhocodes/config-array": "^0.11.14",
|
| 505 |
+
"@humanwhocodes/module-importer": "^1.0.1",
|
| 506 |
+
"@nodelib/fs.walk": "^1.2.8",
|
| 507 |
+
"@ungap/structured-clone": "^1.2.0",
|
| 508 |
+
"ajv": "^6.12.4",
|
| 509 |
+
"chalk": "^4.0.0",
|
| 510 |
+
"cross-spawn": "^7.0.2",
|
| 511 |
+
"debug": "^4.3.2",
|
| 512 |
+
"doctrine": "^3.0.0",
|
| 513 |
+
"escape-string-regexp": "^4.0.0",
|
| 514 |
+
"eslint-scope": "^7.2.2",
|
| 515 |
+
"eslint-visitor-keys": "^3.4.3",
|
| 516 |
+
"espree": "^9.6.1",
|
| 517 |
+
"esquery": "^1.4.2",
|
| 518 |
+
"esutils": "^2.0.2",
|
| 519 |
+
"fast-deep-equal": "^3.1.3",
|
| 520 |
+
"file-entry-cache": "^6.0.1",
|
| 521 |
+
"find-up": "^5.0.0",
|
| 522 |
+
"glob-parent": "^6.0.2",
|
| 523 |
+
"globals": "^13.19.0",
|
| 524 |
+
"graphemer": "^1.4.0",
|
| 525 |
+
"ignore": "^5.2.0",
|
| 526 |
+
"imurmurhash": "^0.1.4",
|
| 527 |
+
"is-glob": "^4.0.0",
|
| 528 |
+
"is-path-inside": "^3.0.3",
|
| 529 |
+
"js-yaml": "^4.1.0",
|
| 530 |
+
"json-stable-stringify-without-jsonify": "^1.0.1",
|
| 531 |
+
"levn": "^0.4.1",
|
| 532 |
+
"lodash.merge": "^4.6.2",
|
| 533 |
+
"minimatch": "^3.1.2",
|
| 534 |
+
"natural-compare": "^1.4.0",
|
| 535 |
+
"optionator": "^0.9.3",
|
| 536 |
+
"strip-ansi": "^6.0.1",
|
| 537 |
+
"text-table": "^0.2.0"
|
| 538 |
+
},
|
| 539 |
+
"bin": {
|
| 540 |
+
"eslint": "bin/eslint.js"
|
| 541 |
+
},
|
| 542 |
+
"engines": {
|
| 543 |
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
| 544 |
+
},
|
| 545 |
+
"funding": {
|
| 546 |
+
"url": "https://opencollective.com/eslint"
|
| 547 |
+
}
|
| 548 |
+
},
|
| 549 |
+
"apps/backend/node_modules/eslint-scope": {
|
| 550 |
+
"version": "7.2.2",
|
| 551 |
+
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
|
| 552 |
+
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
|
| 553 |
+
"dev": true,
|
| 554 |
+
"license": "BSD-2-Clause",
|
| 555 |
+
"dependencies": {
|
| 556 |
+
"esrecurse": "^4.3.0",
|
| 557 |
+
"estraverse": "^5.2.0"
|
| 558 |
+
},
|
| 559 |
+
"engines": {
|
| 560 |
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
| 561 |
+
},
|
| 562 |
+
"funding": {
|
| 563 |
+
"url": "https://opencollective.com/eslint"
|
| 564 |
+
}
|
| 565 |
+
},
|
| 566 |
+
"apps/backend/node_modules/espree": {
|
| 567 |
+
"version": "9.6.1",
|
| 568 |
+
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
|
| 569 |
+
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
|
| 570 |
+
"dev": true,
|
| 571 |
+
"license": "BSD-2-Clause",
|
| 572 |
+
"dependencies": {
|
| 573 |
+
"acorn": "^8.9.0",
|
| 574 |
+
"acorn-jsx": "^5.3.2",
|
| 575 |
+
"eslint-visitor-keys": "^3.4.1"
|
| 576 |
+
},
|
| 577 |
+
"engines": {
|
| 578 |
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
| 579 |
+
},
|
| 580 |
+
"funding": {
|
| 581 |
+
"url": "https://opencollective.com/eslint"
|
| 582 |
+
}
|
| 583 |
+
},
|
| 584 |
+
"apps/backend/node_modules/file-entry-cache": {
|
| 585 |
+
"version": "6.0.1",
|
| 586 |
+
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
| 587 |
+
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
|
| 588 |
+
"dev": true,
|
| 589 |
+
"license": "MIT",
|
| 590 |
+
"dependencies": {
|
| 591 |
+
"flat-cache": "^3.0.4"
|
| 592 |
+
},
|
| 593 |
+
"engines": {
|
| 594 |
+
"node": "^10.12.0 || >=12.0.0"
|
| 595 |
+
}
|
| 596 |
+
},
|
| 597 |
+
"apps/backend/node_modules/flat-cache": {
|
| 598 |
+
"version": "3.2.0",
|
| 599 |
+
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
|
| 600 |
+
"integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
|
| 601 |
+
"dev": true,
|
| 602 |
+
"license": "MIT",
|
| 603 |
+
"dependencies": {
|
| 604 |
+
"flatted": "^3.2.9",
|
| 605 |
+
"keyv": "^4.5.3",
|
| 606 |
+
"rimraf": "^3.0.2"
|
| 607 |
+
},
|
| 608 |
+
"engines": {
|
| 609 |
+
"node": "^10.12.0 || >=12.0.0"
|
| 610 |
+
}
|
| 611 |
+
},
|
| 612 |
+
"apps/backend/node_modules/globals": {
|
| 613 |
+
"version": "13.24.0",
|
| 614 |
+
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
|
| 615 |
+
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
|
| 616 |
+
"dev": true,
|
| 617 |
+
"license": "MIT",
|
| 618 |
+
"dependencies": {
|
| 619 |
+
"type-fest": "^0.20.2"
|
| 620 |
+
},
|
| 621 |
+
"engines": {
|
| 622 |
+
"node": ">=8"
|
| 623 |
+
},
|
| 624 |
+
"funding": {
|
| 625 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 626 |
+
}
|
| 627 |
+
},
|
| 628 |
+
"apps/backend/node_modules/ignore": {
|
| 629 |
+
"version": "5.3.2",
|
| 630 |
+
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
| 631 |
+
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
|
| 632 |
+
"dev": true,
|
| 633 |
+
"license": "MIT",
|
| 634 |
+
"engines": {
|
| 635 |
+
"node": ">= 4"
|
| 636 |
+
}
|
| 637 |
+
},
|
| 638 |
+
"apps/backend/node_modules/json-schema-traverse": {
|
| 639 |
+
"version": "0.4.1",
|
| 640 |
+
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
| 641 |
+
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
| 642 |
+
"dev": true,
|
| 643 |
+
"license": "MIT"
|
| 644 |
+
},
|
| 645 |
+
"apps/backend/node_modules/minimatch": {
|
| 646 |
+
"version": "3.1.2",
|
| 647 |
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
| 648 |
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
| 649 |
+
"dev": true,
|
| 650 |
+
"license": "ISC",
|
| 651 |
+
"dependencies": {
|
| 652 |
+
"brace-expansion": "^1.1.7"
|
| 653 |
+
},
|
| 654 |
+
"engines": {
|
| 655 |
+
"node": "*"
|
| 656 |
+
}
|
| 657 |
+
},
|
| 658 |
"apps/backend/node_modules/pdfjs-dist": {
|
| 659 |
"version": "5.4.449",
|
| 660 |
"license": "Apache-2.0",
|
|
|
|
| 665 |
"@napi-rs/canvas": "^0.1.81"
|
| 666 |
}
|
| 667 |
},
|
| 668 |
+
"apps/backend/node_modules/rimraf": {
|
| 669 |
+
"version": "3.0.2",
|
| 670 |
+
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
| 671 |
+
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
| 672 |
+
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
| 673 |
+
"dev": true,
|
| 674 |
+
"license": "ISC",
|
| 675 |
+
"dependencies": {
|
| 676 |
+
"glob": "^7.1.3"
|
| 677 |
+
},
|
| 678 |
+
"bin": {
|
| 679 |
+
"rimraf": "bin.js"
|
| 680 |
+
},
|
| 681 |
+
"funding": {
|
| 682 |
+
"url": "https://github.com/sponsors/isaacs"
|
| 683 |
+
}
|
| 684 |
+
},
|
| 685 |
+
"apps/backend/node_modules/type-fest": {
|
| 686 |
+
"version": "0.20.2",
|
| 687 |
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
| 688 |
+
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
| 689 |
+
"dev": true,
|
| 690 |
+
"license": "(MIT OR CC0-1.0)",
|
| 691 |
+
"engines": {
|
| 692 |
+
"node": ">=10"
|
| 693 |
+
},
|
| 694 |
+
"funding": {
|
| 695 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 696 |
+
}
|
| 697 |
+
},
|
| 698 |
"apps/backend/packages/domain-types": {
|
| 699 |
"name": "@widget-tdc/domain-types",
|
| 700 |
"version": "1.0.0",
|
|
|
|
| 3859 |
"node": ">=18.18.0"
|
| 3860 |
}
|
| 3861 |
},
|
| 3862 |
+
"node_modules/@humanwhocodes/config-array": {
|
| 3863 |
+
"version": "0.11.14",
|
| 3864 |
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
| 3865 |
+
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
|
| 3866 |
+
"deprecated": "Use @eslint/config-array instead",
|
| 3867 |
+
"dev": true,
|
| 3868 |
+
"license": "Apache-2.0",
|
| 3869 |
+
"dependencies": {
|
| 3870 |
+
"@humanwhocodes/object-schema": "^2.0.2",
|
| 3871 |
+
"debug": "^4.3.1",
|
| 3872 |
+
"minimatch": "^3.0.5"
|
| 3873 |
+
},
|
| 3874 |
+
"engines": {
|
| 3875 |
+
"node": ">=10.10.0"
|
| 3876 |
+
}
|
| 3877 |
+
},
|
| 3878 |
+
"node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
|
| 3879 |
+
"version": "1.1.12",
|
| 3880 |
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
| 3881 |
+
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
| 3882 |
+
"dev": true,
|
| 3883 |
+
"license": "MIT",
|
| 3884 |
+
"dependencies": {
|
| 3885 |
+
"balanced-match": "^1.0.0",
|
| 3886 |
+
"concat-map": "0.0.1"
|
| 3887 |
+
}
|
| 3888 |
+
},
|
| 3889 |
+
"node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
|
| 3890 |
+
"version": "3.1.2",
|
| 3891 |
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
| 3892 |
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
| 3893 |
+
"dev": true,
|
| 3894 |
+
"license": "ISC",
|
| 3895 |
+
"dependencies": {
|
| 3896 |
+
"brace-expansion": "^1.1.7"
|
| 3897 |
+
},
|
| 3898 |
+
"engines": {
|
| 3899 |
+
"node": "*"
|
| 3900 |
+
}
|
| 3901 |
+
},
|
| 3902 |
"node_modules/@humanwhocodes/module-importer": {
|
| 3903 |
"version": "1.0.1",
|
| 3904 |
"dev": true,
|
|
|
|
| 3911 |
"url": "https://github.com/sponsors/nzakas"
|
| 3912 |
}
|
| 3913 |
},
|
| 3914 |
+
"node_modules/@humanwhocodes/object-schema": {
|
| 3915 |
+
"version": "2.0.3",
|
| 3916 |
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
|
| 3917 |
+
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
|
| 3918 |
+
"deprecated": "Use @eslint/object-schema instead",
|
| 3919 |
+
"dev": true,
|
| 3920 |
+
"license": "BSD-3-Clause"
|
| 3921 |
+
},
|
| 3922 |
"node_modules/@humanwhocodes/retry": {
|
| 3923 |
"version": "0.4.3",
|
| 3924 |
"dev": true,
|
|
|
|
| 8082 |
"url": "https://github.com/sponsors/ljharb"
|
| 8083 |
}
|
| 8084 |
},
|
| 8085 |
+
"node_modules/array-union": {
|
| 8086 |
+
"version": "2.1.0",
|
| 8087 |
+
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
| 8088 |
+
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
|
| 8089 |
+
"dev": true,
|
| 8090 |
+
"license": "MIT",
|
| 8091 |
+
"engines": {
|
| 8092 |
+
"node": ">=8"
|
| 8093 |
+
}
|
| 8094 |
+
},
|
| 8095 |
"node_modules/array.prototype.findlast": {
|
| 8096 |
"version": "1.2.5",
|
| 8097 |
"dev": true,
|
|
|
|
| 10230 |
"node": ">=0.3.1"
|
| 10231 |
}
|
| 10232 |
},
|
| 10233 |
+
"node_modules/dir-glob": {
|
| 10234 |
+
"version": "3.0.1",
|
| 10235 |
+
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
| 10236 |
+
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
|
| 10237 |
+
"dev": true,
|
| 10238 |
+
"license": "MIT",
|
| 10239 |
+
"dependencies": {
|
| 10240 |
+
"path-type": "^4.0.0"
|
| 10241 |
+
},
|
| 10242 |
+
"engines": {
|
| 10243 |
+
"node": ">=8"
|
| 10244 |
+
}
|
| 10245 |
+
},
|
| 10246 |
"node_modules/dlv": {
|
| 10247 |
"version": "1.1.3",
|
| 10248 |
"license": "MIT"
|
|
|
|
| 12490 |
"url": "https://github.com/sponsors/ljharb"
|
| 12491 |
}
|
| 12492 |
},
|
| 12493 |
+
"node_modules/globby": {
|
| 12494 |
+
"version": "11.1.0",
|
| 12495 |
+
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
|
| 12496 |
+
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
|
| 12497 |
+
"dev": true,
|
| 12498 |
+
"license": "MIT",
|
| 12499 |
+
"dependencies": {
|
| 12500 |
+
"array-union": "^2.1.0",
|
| 12501 |
+
"dir-glob": "^3.0.1",
|
| 12502 |
+
"fast-glob": "^3.2.9",
|
| 12503 |
+
"ignore": "^5.2.0",
|
| 12504 |
+
"merge2": "^1.4.1",
|
| 12505 |
+
"slash": "^3.0.0"
|
| 12506 |
+
},
|
| 12507 |
+
"engines": {
|
| 12508 |
+
"node": ">=10"
|
| 12509 |
+
},
|
| 12510 |
+
"funding": {
|
| 12511 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 12512 |
+
}
|
| 12513 |
+
},
|
| 12514 |
+
"node_modules/globby/node_modules/ignore": {
|
| 12515 |
+
"version": "5.3.2",
|
| 12516 |
+
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
| 12517 |
+
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
|
| 12518 |
+
"dev": true,
|
| 12519 |
+
"license": "MIT",
|
| 12520 |
+
"engines": {
|
| 12521 |
+
"node": ">= 4"
|
| 12522 |
+
}
|
| 12523 |
+
},
|
| 12524 |
"node_modules/gopd": {
|
| 12525 |
"version": "1.2.0",
|
| 12526 |
"license": "MIT",
|
|
|
|
| 13291 |
"node": ">=0.10.0"
|
| 13292 |
}
|
| 13293 |
},
|
| 13294 |
+
"node_modules/is-path-inside": {
|
| 13295 |
+
"version": "3.0.3",
|
| 13296 |
+
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
|
| 13297 |
+
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
|
| 13298 |
+
"dev": true,
|
| 13299 |
+
"license": "MIT",
|
| 13300 |
+
"engines": {
|
| 13301 |
+
"node": ">=8"
|
| 13302 |
+
}
|
| 13303 |
+
},
|
| 13304 |
"node_modules/is-plain-obj": {
|
| 13305 |
"version": "4.1.0",
|
| 13306 |
"license": "MIT",
|
|
|
|
| 17800 |
"node": ">=18"
|
| 17801 |
}
|
| 17802 |
},
|
| 17803 |
+
"node_modules/slash": {
|
| 17804 |
+
"version": "3.0.0",
|
| 17805 |
+
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
| 17806 |
+
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
| 17807 |
+
"dev": true,
|
| 17808 |
+
"license": "MIT",
|
| 17809 |
+
"engines": {
|
| 17810 |
+
"node": ">=8"
|
| 17811 |
+
}
|
| 17812 |
+
},
|
| 17813 |
"node_modules/smart-buffer": {
|
| 17814 |
"version": "4.2.0",
|
| 17815 |
"license": "MIT",
|
|
|
|
| 18655 |
"version": "1.0.0",
|
| 18656 |
"license": "MIT"
|
| 18657 |
},
|
| 18658 |
+
"node_modules/text-table": {
|
| 18659 |
+
"version": "0.2.0",
|
| 18660 |
+
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
| 18661 |
+
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
| 18662 |
+
"dev": true,
|
| 18663 |
+
"license": "MIT"
|
| 18664 |
+
},
|
| 18665 |
"node_modules/thenify": {
|
| 18666 |
"version": "3.3.1",
|
| 18667 |
"license": "MIT",
|