Spaces:
Paused
Paused
Deploy from GitHub Actions 2025-12-16_04-55-23
Browse files
apps/backend/src/index.ts
CHANGED
|
@@ -171,7 +171,6 @@ async function startServer() {
|
|
| 171 |
// Cloud defaults like 7860 break the internal reflex arc.
|
| 172 |
const PORT = 3001;
|
| 173 |
|
| 174 |
-
// Start server IMMEDIATELY
|
| 175 |
server.listen(PORT, '0.0.0.0', () => {
|
| 176 |
console.log(`π Backend server running on http://0.0.0.0:${PORT}`);
|
| 177 |
console.log(`π‘ MCP WebSocket available at ws://0.0.0.0:${PORT}/mcp/ws`);
|
|
@@ -1227,21 +1226,29 @@ async function startServer() {
|
|
| 1227 |
* GET /api/evolution/graph/stats
|
| 1228 |
* Get Neo4j graph statistics for 3D visualization
|
| 1229 |
* π NEURAL LINK ENDPOINT
|
|
|
|
|
|
|
|
|
|
| 1230 |
*/
|
| 1231 |
app.get('/api/evolution/graph/stats', async (_req, res) => {
|
| 1232 |
try {
|
| 1233 |
const { neo4jService } = await import('./database/Neo4jService.js');
|
| 1234 |
|
| 1235 |
-
// 1. Fetch Stats
|
| 1236 |
-
|
| 1237 |
-
|
| 1238 |
-
|
| 1239 |
-
|
| 1240 |
-
|
| 1241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1242 |
const stats = {
|
| 1243 |
-
nodes:
|
| 1244 |
-
relationships:
|
| 1245 |
};
|
| 1246 |
|
| 1247 |
// 2. Fetch Sample Nodes for Visualization
|
|
|
|
| 171 |
// Cloud defaults like 7860 break the internal reflex arc.
|
| 172 |
const PORT = 3001;
|
| 173 |
|
|
|
|
| 174 |
server.listen(PORT, '0.0.0.0', () => {
|
| 175 |
console.log(`π Backend server running on http://0.0.0.0:${PORT}`);
|
| 176 |
console.log(`π‘ MCP WebSocket available at ws://0.0.0.0:${PORT}/mcp/ws`);
|
|
|
|
| 1226 |
* GET /api/evolution/graph/stats
|
| 1227 |
* Get Neo4j graph statistics for 3D visualization
|
| 1228 |
* π NEURAL LINK ENDPOINT
|
| 1229 |
+
*
|
| 1230 |
+
* OPTIMIZED: Uses separate lightweight queries to avoid memory overflow
|
| 1231 |
+
* on Neo4j AuraDB free tier (824 MiB limit)
|
| 1232 |
*/
|
| 1233 |
app.get('/api/evolution/graph/stats', async (_req, res) => {
|
| 1234 |
try {
|
| 1235 |
const { neo4jService } = await import('./database/Neo4jService.js');
|
| 1236 |
|
| 1237 |
+
// 1. Fetch Stats using memory-efficient separate queries
|
| 1238 |
+
// Note: The original MATCH (n) OPTIONAL MATCH ()-[r]->() creates a cross-product
|
| 1239 |
+
// that exceeds Neo4j AuraDB's memory limits on large graphs (150K+ nodes)
|
| 1240 |
+
const nodeCountQuery = `MATCH (n) RETURN count(n) as nodes`;
|
| 1241 |
+
const relCountQuery = `MATCH ()-[r]->() RETURN count(r) as relationships`;
|
| 1242 |
+
|
| 1243 |
+
// Run queries in parallel for better performance
|
| 1244 |
+
const [nodeResult, relResult] = await Promise.all([
|
| 1245 |
+
neo4jService.runQuery(nodeCountQuery),
|
| 1246 |
+
neo4jService.runQuery(relCountQuery)
|
| 1247 |
+
]);
|
| 1248 |
+
|
| 1249 |
const stats = {
|
| 1250 |
+
nodes: nodeResult[0]?.nodes?.toNumber ? nodeResult[0].nodes.toNumber() : (nodeResult[0]?.nodes || 0),
|
| 1251 |
+
relationships: relResult[0]?.relationships?.toNumber ? relResult[0].relationships.toNumber() : (relResult[0]?.relationships || 0)
|
| 1252 |
};
|
| 1253 |
|
| 1254 |
// 2. Fetch Sample Nodes for Visualization
|
apps/backend/src/mcp/servers/NeuralBridgeServer.ts
CHANGED
|
@@ -1471,7 +1471,8 @@ class NeuralBridgeServer {
|
|
| 1471 |
break;
|
| 1472 |
|
| 1473 |
case 'backend':
|
| 1474 |
-
const
|
|
|
|
| 1475 |
signal: AbortSignal.timeout(5000)
|
| 1476 |
});
|
| 1477 |
success = backendResponse.ok;
|
|
|
|
| 1471 |
break;
|
| 1472 |
|
| 1473 |
case 'backend':
|
| 1474 |
+
const backendPort = process.env.PORT || 7860;
|
| 1475 |
+
const backendResponse = await fetch(`http://localhost:${backendPort}/api/health`, {
|
| 1476 |
signal: AbortSignal.timeout(5000)
|
| 1477 |
});
|
| 1478 |
success = backendResponse.ok;
|
apps/backend/src/services/CognitiveErrorIntelligence.ts
CHANGED
|
@@ -33,6 +33,9 @@ import { logger } from '../utils/logger.js';
|
|
| 33 |
|
| 34 |
const log = logger.child({ module: 'CognitiveErrorIntelligence' });
|
| 35 |
|
|
|
|
|
|
|
|
|
|
| 36 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 37 |
// TYPES
|
| 38 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -558,8 +561,11 @@ export class CognitiveErrorIntelligence extends EventEmitter {
|
|
| 558 |
});
|
| 559 |
});
|
| 560 |
} else if (action.apiCall) {
|
| 561 |
-
// Execute API call
|
| 562 |
-
const
|
|
|
|
|
|
|
|
|
|
| 563 |
method: action.apiCall.method,
|
| 564 |
headers: { 'Content-Type': 'application/json' },
|
| 565 |
body: action.apiCall.body ? JSON.stringify(action.apiCall.body) : undefined
|
|
@@ -709,7 +715,7 @@ export class CognitiveErrorIntelligence extends EventEmitter {
|
|
| 709 |
name: 'Restart Redis Connection',
|
| 710 |
description: 'Reconnect to Redis when connection is lost',
|
| 711 |
errorPatterns: ['ECONNREFUSED', 'redis', 'connection refused'],
|
| 712 |
-
apiCall: { endpoint: '
|
| 713 |
requiresApproval: false,
|
| 714 |
riskLevel: 'low',
|
| 715 |
successRate: 0.85,
|
|
@@ -720,7 +726,7 @@ export class CognitiveErrorIntelligence extends EventEmitter {
|
|
| 720 |
name: 'Clear Memory Cache',
|
| 721 |
description: 'Clear in-memory caches when memory is low',
|
| 722 |
errorPatterns: ['heap', 'memory', 'OOM'],
|
| 723 |
-
apiCall: { endpoint: '
|
| 724 |
requiresApproval: false,
|
| 725 |
riskLevel: 'low',
|
| 726 |
successRate: 0.9,
|
|
@@ -731,7 +737,7 @@ export class CognitiveErrorIntelligence extends EventEmitter {
|
|
| 731 |
name: 'Retry Database Connection',
|
| 732 |
description: 'Attempt to reconnect to database',
|
| 733 |
errorPatterns: ['database', 'postgres', 'neo4j', 'SQLSTATE'],
|
| 734 |
-
apiCall: { endpoint: '
|
| 735 |
requiresApproval: false,
|
| 736 |
riskLevel: 'medium',
|
| 737 |
successRate: 0.75,
|
|
|
|
| 33 |
|
| 34 |
const log = logger.child({ module: 'CognitiveErrorIntelligence' });
|
| 35 |
|
| 36 |
+
// Dynamic base URL for self-healing API calls
|
| 37 |
+
const getBaseUrl = () => `http://localhost:${process.env.PORT || 7860}`;
|
| 38 |
+
|
| 39 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 40 |
// TYPES
|
| 41 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 561 |
});
|
| 562 |
});
|
| 563 |
} else if (action.apiCall) {
|
| 564 |
+
// Execute API call - prepend base URL to relative paths
|
| 565 |
+
const endpoint = action.apiCall.endpoint.startsWith('/')
|
| 566 |
+
? `${getBaseUrl()}${action.apiCall.endpoint}`
|
| 567 |
+
: action.apiCall.endpoint;
|
| 568 |
+
const response = await fetch(endpoint, {
|
| 569 |
method: action.apiCall.method,
|
| 570 |
headers: { 'Content-Type': 'application/json' },
|
| 571 |
body: action.apiCall.body ? JSON.stringify(action.apiCall.body) : undefined
|
|
|
|
| 715 |
name: 'Restart Redis Connection',
|
| 716 |
description: 'Reconnect to Redis when connection is lost',
|
| 717 |
errorPatterns: ['ECONNREFUSED', 'redis', 'connection refused'],
|
| 718 |
+
apiCall: { endpoint: '/api/healing/service/redis', method: 'POST' },
|
| 719 |
requiresApproval: false,
|
| 720 |
riskLevel: 'low',
|
| 721 |
successRate: 0.85,
|
|
|
|
| 726 |
name: 'Clear Memory Cache',
|
| 727 |
description: 'Clear in-memory caches when memory is low',
|
| 728 |
errorPatterns: ['heap', 'memory', 'OOM'],
|
| 729 |
+
apiCall: { endpoint: '/api/system/clear-cache', method: 'POST' },
|
| 730 |
requiresApproval: false,
|
| 731 |
riskLevel: 'low',
|
| 732 |
successRate: 0.9,
|
|
|
|
| 737 |
name: 'Retry Database Connection',
|
| 738 |
description: 'Attempt to reconnect to database',
|
| 739 |
errorPatterns: ['database', 'postgres', 'neo4j', 'SQLSTATE'],
|
| 740 |
+
apiCall: { endpoint: '/api/healing/service/database', method: 'POST' },
|
| 741 |
requiresApproval: false,
|
| 742 |
riskLevel: 'medium',
|
| 743 |
successRate: 0.75,
|
apps/backend/src/services/NudgeService.ts
CHANGED
|
@@ -249,17 +249,15 @@ class NudgeService {
|
|
| 249 |
|
| 250 |
try {
|
| 251 |
if (this.neo4jAdapter) {
|
| 252 |
-
// Get current graph stats
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
MATCH ()-[r]->()
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
const
|
| 261 |
-
const nodeCount = stats?.nodeCount || 0;
|
| 262 |
-
const relCount = stats?.relCount || 0;
|
| 263 |
|
| 264 |
// Create evolution event node
|
| 265 |
await this.neo4jAdapter.runQuery(`
|
|
@@ -332,7 +330,7 @@ class NudgeService {
|
|
| 332 |
|
| 333 |
try {
|
| 334 |
// Trigger compilation via HTTP to self
|
| 335 |
-
const port = process.env.PORT ||
|
| 336 |
const response = await fetch(`http://localhost:${port}/api/knowledge/compile`, {
|
| 337 |
method: 'POST',
|
| 338 |
headers: { 'Content-Type': 'application/json' }
|
|
|
|
| 249 |
|
| 250 |
try {
|
| 251 |
if (this.neo4jAdapter) {
|
| 252 |
+
// Get current graph stats - use separate queries to avoid memory overflow
|
| 253 |
+
// on Neo4j AuraDB free tier (824 MiB limit with 150K+ nodes)
|
| 254 |
+
const [nodeResult, relResult] = await Promise.all([
|
| 255 |
+
this.neo4jAdapter.runQuery(`MATCH (n) RETURN count(n) as nodeCount`),
|
| 256 |
+
this.neo4jAdapter.runQuery(`MATCH ()-[r]->() RETURN count(r) as relCount`)
|
| 257 |
+
]);
|
| 258 |
+
|
| 259 |
+
const nodeCount = nodeResult?.[0]?.nodeCount || 0;
|
| 260 |
+
const relCount = relResult?.[0]?.relCount || 0;
|
|
|
|
|
|
|
| 261 |
|
| 262 |
// Create evolution event node
|
| 263 |
await this.neo4jAdapter.runQuery(`
|
|
|
|
| 330 |
|
| 331 |
try {
|
| 332 |
// Trigger compilation via HTTP to self
|
| 333 |
+
const port = process.env.PORT || 7860;
|
| 334 |
const response = await fetch(`http://localhost:${port}/api/knowledge/compile`, {
|
| 335 |
method: 'POST',
|
| 336 |
headers: { 'Content-Type': 'application/json' }
|
apps/backend/src/services/agent/HansPedderAgentController.ts
CHANGED
|
@@ -99,6 +99,13 @@ export class HansPedderAgentController {
|
|
| 99 |
|
| 100 |
eventBus.on('threat:detected', () => {
|
| 101 |
this.healthMetrics.lastIngestion = new Date();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
});
|
| 103 |
|
| 104 |
eventBus.on('system:heartbeat', (data: any) => {
|
|
@@ -240,7 +247,8 @@ export class HansPedderAgentController {
|
|
| 240 |
|
| 241 |
try {
|
| 242 |
// Check if MCP tools endpoint responds (indicates server is ready)
|
| 243 |
-
|
|
|
|
| 244 |
const baseUrl = process.env.API_BASE_URL || `http://localhost:${port}`;
|
| 245 |
const response = await fetch(`${baseUrl}/api/mcp/tools`, {
|
| 246 |
method: 'GET',
|
|
@@ -275,8 +283,8 @@ export class HansPedderAgentController {
|
|
| 275 |
const name = 'apiEndpoints';
|
| 276 |
|
| 277 |
try {
|
| 278 |
-
// Test key API endpoints
|
| 279 |
-
const port = process.env.PORT ||
|
| 280 |
const baseUrl = process.env.API_BASE_URL || `http://localhost:${port}`;
|
| 281 |
const endpoints = [
|
| 282 |
{ url: `${baseUrl}/health`, method: 'GET' },
|
|
@@ -405,7 +413,7 @@ export class HansPedderAgentController {
|
|
| 405 |
logger.info(' β API endpoints unreachable - checking backend status...');
|
| 406 |
fixAction = 'Health check on correct backend port';
|
| 407 |
try {
|
| 408 |
-
const backendPort = process.env.PORT || '
|
| 409 |
const testUrl = `http://localhost:${backendPort}/health`;
|
| 410 |
const response = await fetch(testUrl, { signal: AbortSignal.timeout(3000) });
|
| 411 |
if (response.ok) {
|
|
|
|
| 99 |
|
| 100 |
eventBus.on('threat:detected', () => {
|
| 101 |
this.healthMetrics.lastIngestion = new Date();
|
| 102 |
+
this.healthMetrics.dataflowOk = true;
|
| 103 |
+
});
|
| 104 |
+
|
| 105 |
+
// Also track threat feed broadcasts (from OpenPhish etc.)
|
| 106 |
+
eventBus.on('threats:broadcast', () => {
|
| 107 |
+
this.healthMetrics.lastIngestion = new Date();
|
| 108 |
+
this.healthMetrics.dataflowOk = true;
|
| 109 |
});
|
| 110 |
|
| 111 |
eventBus.on('system:heartbeat', (data: any) => {
|
|
|
|
| 247 |
|
| 248 |
try {
|
| 249 |
// Check if MCP tools endpoint responds (indicates server is ready)
|
| 250 |
+
// Default to 7860 which is the standard port for WidgeTDC backend (both local and HuggingFace)
|
| 251 |
+
const port = process.env.PORT || 7860;
|
| 252 |
const baseUrl = process.env.API_BASE_URL || `http://localhost:${port}`;
|
| 253 |
const response = await fetch(`${baseUrl}/api/mcp/tools`, {
|
| 254 |
method: 'GET',
|
|
|
|
| 283 |
const name = 'apiEndpoints';
|
| 284 |
|
| 285 |
try {
|
| 286 |
+
// Test key API endpoints - default to 7860 which is standard for WidgeTDC
|
| 287 |
+
const port = process.env.PORT || 7860;
|
| 288 |
const baseUrl = process.env.API_BASE_URL || `http://localhost:${port}`;
|
| 289 |
const endpoints = [
|
| 290 |
{ url: `${baseUrl}/health`, method: 'GET' },
|
|
|
|
| 413 |
logger.info(' β API endpoints unreachable - checking backend status...');
|
| 414 |
fixAction = 'Health check on correct backend port';
|
| 415 |
try {
|
| 416 |
+
const backendPort = process.env.PORT || '7860';
|
| 417 |
const testUrl = `http://localhost:${backendPort}/health`;
|
| 418 |
const response = await fetch(testUrl, { signal: AbortSignal.timeout(3000) });
|
| 419 |
if (response.ok) {
|