Spaces:
Paused
Paused
Deploy backend fix v2.1.0
Browse files- Dockerfile +25 -73
- add_cors_secret.py +44 -0
- apps/backend/package.json +6 -1
- apps/backend/src/agents/SwarmControl.ts +250 -0
- apps/backend/src/index.ts +94 -1
- apps/backend/src/mcp/EventBus.ts +2 -0
- apps/backend/src/middleware/AngelProxy.ts +257 -0
- apps/backend/src/routes/neural.ts +372 -0
- apps/backend/src/scripts/setup-graph.ts +152 -0
- apps/backend/src/services/NeuralBus.ts +180 -0
- apps/backend/src/services/NeuralCompiler.ts +254 -0
- apps/backend/src/services/Prometheus.ts +267 -0
- apps/backend/src/services/SelfHealingAdapter.ts +46 -0
- apps/backend/src/services/VectorService.ts +126 -0
- apps/backend/src/services/colonizer-service.ts +4 -2
- apps/backend/src/services/embeddings/LocalGPUEmbeddings.ts +4 -3
- monitor_runtime.py +98 -0
- package-lock.json +0 -0
- package.json +2 -0
Dockerfile
CHANGED
|
@@ -1,87 +1,39 @@
|
|
| 1 |
-
# WidgeTDC Backend - Hugging Face Spaces
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
FROM node:20-slim AS builder
|
| 5 |
-
|
| 6 |
-
# Install build dependencies
|
| 7 |
-
RUN apt-get update && apt-get install -y \
|
| 8 |
-
python3 \
|
| 9 |
-
make \
|
| 10 |
-
g++ \
|
| 11 |
-
git \
|
| 12 |
-
openssl \
|
| 13 |
-
libssl-dev \
|
| 14 |
-
&& rm -rf /var/lib/apt/lists/*
|
| 15 |
|
| 16 |
WORKDIR /app
|
| 17 |
|
| 18 |
-
# Copy
|
| 19 |
-
COPY
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
RUN npm run build --workspace=packages/domain-types
|
| 24 |
-
RUN npm run build --workspace=packages/mcp-types
|
| 25 |
-
RUN npm run build --workspace=apps/backend
|
| 26 |
|
| 27 |
-
#
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
FROM node:20-slim AS production
|
| 31 |
-
|
| 32 |
-
# Install runtime dependencies only
|
| 33 |
-
RUN apt-get update && apt-get install -y \
|
| 34 |
-
openssl \
|
| 35 |
-
ca-certificates \
|
| 36 |
-
&& rm -rf /var/lib/apt/lists/*
|
| 37 |
-
|
| 38 |
-
# Create non-root user (HF Spaces requirement)
|
| 39 |
-
RUN useradd -m -u 1000 user
|
| 40 |
-
USER user
|
| 41 |
-
|
| 42 |
-
WORKDIR /app
|
| 43 |
|
| 44 |
-
# Copy
|
| 45 |
-
COPY
|
| 46 |
-
COPY --from=builder --chown=user /app/apps/backend/package*.json ./apps/backend/
|
| 47 |
-
|
| 48 |
-
# Copy built artifacts
|
| 49 |
-
COPY --from=builder --chown=user /app/apps/backend/dist ./apps/backend/dist
|
| 50 |
-
COPY --from=builder --chown=user /app/packages/domain-types/dist ./packages/domain-types/dist
|
| 51 |
-
COPY --from=builder --chown=user /app/packages/mcp-types/dist ./packages/mcp-types/dist
|
| 52 |
-
|
| 53 |
-
# Copy package.json for packages (needed for module resolution)
|
| 54 |
-
COPY --from=builder --chown=user /app/packages/domain-types/package*.json ./packages/domain-types/
|
| 55 |
-
COPY --from=builder --chown=user /app/packages/mcp-types/package*.json ./packages/mcp-types/
|
| 56 |
-
|
| 57 |
-
# Copy node_modules (includes all dependencies)
|
| 58 |
-
COPY --from=builder --chown=user /app/node_modules ./node_modules
|
| 59 |
|
| 60 |
-
#
|
| 61 |
-
|
| 62 |
-
|
| 63 |
|
| 64 |
-
#
|
| 65 |
-
RUN
|
| 66 |
-
mkdir -p /app/data/vidensarkiv && \
|
| 67 |
-
mkdir -p /app/data/agents && \
|
| 68 |
-
mkdir -p /app/data/harvested
|
| 69 |
|
| 70 |
-
#
|
|
|
|
| 71 |
WORKDIR /app/apps/backend
|
| 72 |
|
| 73 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
ENV NODE_ENV=production
|
| 75 |
ENV PORT=7860
|
| 76 |
-
ENV HOST=0.0.0.0
|
| 77 |
-
ENV DOCKER=true
|
| 78 |
-
ENV HF_SPACE=true
|
| 79 |
|
| 80 |
-
# HF Spaces exposes port 7860
|
| 81 |
EXPOSE 7860
|
| 82 |
-
|
| 83 |
-
# Health check for HF
|
| 84 |
-
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
| 85 |
-
CMD node -e "fetch('http://localhost:7860/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"
|
| 86 |
-
|
| 87 |
-
CMD ["node", "dist/index.js"]
|
|
|
|
| 1 |
+
# WidgeTDC Backend - Fixed for Hugging Face Spaces
|
| 2 |
+
FROM node:20-alpine AS builder
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
WORKDIR /app
|
| 5 |
|
| 6 |
+
# Copy package files first for optimal caching
|
| 7 |
+
COPY package*.json ./
|
| 8 |
+
COPY apps/backend/package*.json ./apps/backend/
|
| 9 |
+
COPY packages/domain-types/package*.json ./packages/domain-types/
|
| 10 |
+
COPY packages/mcp-types/package*.json ./packages/mcp-types/
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
+
# Install all dependencies
|
| 13 |
+
RUN npm ci --production
|
| 14 |
+
RUN npm install -D tsx typescript @types/node @types/express @types/ws esbuild
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
# Copy source code
|
| 17 |
+
COPY . .
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
# Build packages
|
| 20 |
+
RUN cd packages/domain-types && npm run build
|
| 21 |
+
RUN cd packages/mcp-types && npm run build
|
| 22 |
|
| 23 |
+
# Fix: Build backend with proper externalization
|
| 24 |
+
RUN cd apps/backend && npm run build-fixed
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
+
# Final stage
|
| 27 |
+
FROM node:20-alpine
|
| 28 |
WORKDIR /app/apps/backend
|
| 29 |
|
| 30 |
+
# Copy only necessary files
|
| 31 |
+
COPY --from=builder /app/apps/backend/dist ./dist
|
| 32 |
+
COPY --from=builder /app/apps/backend/package.json ./
|
| 33 |
+
|
| 34 |
+
# Environment configuration
|
| 35 |
ENV NODE_ENV=production
|
| 36 |
ENV PORT=7860
|
|
|
|
|
|
|
|
|
|
| 37 |
|
|
|
|
| 38 |
EXPOSE 7860
|
| 39 |
+
CMD ["node", "dist/index.js"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
add_cors_secret.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Add CORS_ORIGIN secret to Hugging Face Space
|
| 3 |
+
"""
|
| 4 |
+
from huggingface_hub import HfApi
|
| 5 |
+
|
| 6 |
+
# Configuration
|
| 7 |
+
SPACE_NAME = "widgetdc-cortex"
|
| 8 |
+
USERNAME = "Kraft102"
|
| 9 |
+
REPO_ID = f"{USERNAME}/{SPACE_NAME}"
|
| 10 |
+
|
| 11 |
+
# Vercel frontend URL
|
| 12 |
+
FRONTEND_URL = "https://widge-tdc-matrix-frontend-git-hf-minimal-claus-krafts-projects.vercel.app"
|
| 13 |
+
|
| 14 |
+
print("=" * 70)
|
| 15 |
+
print(" ADDING CORS_ORIGIN SECRET TO HF SPACE")
|
| 16 |
+
print("=" * 70)
|
| 17 |
+
print()
|
| 18 |
+
|
| 19 |
+
# Initialize API
|
| 20 |
+
api = HfApi()
|
| 21 |
+
|
| 22 |
+
print(f"[1/2] Adding CORS_ORIGIN secret...")
|
| 23 |
+
print(f" Frontend URL: {FRONTEND_URL}")
|
| 24 |
+
|
| 25 |
+
try:
|
| 26 |
+
api.add_space_secret(
|
| 27 |
+
repo_id=REPO_ID,
|
| 28 |
+
key="CORS_ORIGIN",
|
| 29 |
+
value=FRONTEND_URL
|
| 30 |
+
)
|
| 31 |
+
print("✅ CORS_ORIGIN secret added successfully!")
|
| 32 |
+
except Exception as e:
|
| 33 |
+
print(f"⚠️ Warning: {e}")
|
| 34 |
+
print(" Secret may already exist or you may need to set HF_TOKEN")
|
| 35 |
+
|
| 36 |
+
print()
|
| 37 |
+
print("=" * 70)
|
| 38 |
+
print(" CORS CONFIGURATION COMPLETE")
|
| 39 |
+
print("=" * 70)
|
| 40 |
+
print()
|
| 41 |
+
print("Next steps:")
|
| 42 |
+
print("1. Wait for Space rebuild")
|
| 43 |
+
print("2. Test frontend → backend communication")
|
| 44 |
+
print("3. Monitor CORS headers in browser DevTools")
|
apps/backend/package.json
CHANGED
|
@@ -5,7 +5,9 @@
|
|
| 5 |
"type": "module",
|
| 6 |
"scripts": {
|
| 7 |
"dev": "tsx watch src/index.ts",
|
| 8 |
-
"build": "
|
|
|
|
|
|
|
| 9 |
"start": "node dist/index.js",
|
| 10 |
"test": "vitest run",
|
| 11 |
"neural-bridge": "tsx src/mcp/servers/NeuralBridgeServer.ts",
|
|
@@ -27,6 +29,7 @@
|
|
| 27 |
"@xenova/transformers": "^2.17.2",
|
| 28 |
"axios": "^1.6.5",
|
| 29 |
"cheerio": "^1.0.0",
|
|
|
|
| 30 |
"chromadb": "^3.1.6",
|
| 31 |
"cors": "^2.8.5",
|
| 32 |
"dotenv": "^17.2.3",
|
|
@@ -54,6 +57,7 @@
|
|
| 54 |
"puppeteer": "^24.32.0",
|
| 55 |
"redis": "^5.10.0",
|
| 56 |
"sharp": "^0.32.6",
|
|
|
|
| 57 |
"sql.js": "^1.8.0",
|
| 58 |
"systeminformation": "^5.27.11",
|
| 59 |
"testcontainers": "^11.8.1",
|
|
@@ -64,6 +68,7 @@
|
|
| 64 |
"zod": "^3.25.76"
|
| 65 |
},
|
| 66 |
"devDependencies": {
|
|
|
|
| 67 |
"@types/cors": "^2.8.17",
|
| 68 |
"@types/express": "^4.17.21",
|
| 69 |
"@types/imap": "^0.8.40",
|
|
|
|
| 5 |
"type": "module",
|
| 6 |
"scripts": {
|
| 7 |
"dev": "tsx watch src/index.ts",
|
| 8 |
+
"build": "esbuild src/index.ts --bundle --platform=node --target=node20 --outfile=dist/index.js --external:@prisma/client --external:better-sqlite3 --external:pg-native --external:@xenova/transformers --external:onnxruntime-node --external:sharp --external:canvas --format=esm",
|
| 9 |
+
"build-fixed": "esbuild src/index.ts --bundle --platform=node --target=node20 --outfile=dist/index.js --external:@prisma/client --external:better-sqlite3 --external:pg-native --external:@xenova/transformers --external:onnxruntime-node --external:sharp --external:canvas --external:fs --external:path --external:os --format=esm",
|
| 10 |
+
"build:tsc": "tsc",
|
| 11 |
"start": "node dist/index.js",
|
| 12 |
"test": "vitest run",
|
| 13 |
"neural-bridge": "tsx src/mcp/servers/NeuralBridgeServer.ts",
|
|
|
|
| 29 |
"@xenova/transformers": "^2.17.2",
|
| 30 |
"axios": "^1.6.5",
|
| 31 |
"cheerio": "^1.0.0",
|
| 32 |
+
"chokidar": "^3.6.0",
|
| 33 |
"chromadb": "^3.1.6",
|
| 34 |
"cors": "^2.8.5",
|
| 35 |
"dotenv": "^17.2.3",
|
|
|
|
| 57 |
"puppeteer": "^24.32.0",
|
| 58 |
"redis": "^5.10.0",
|
| 59 |
"sharp": "^0.32.6",
|
| 60 |
+
"socket.io": "^4.8.1",
|
| 61 |
"sql.js": "^1.8.0",
|
| 62 |
"systeminformation": "^5.27.11",
|
| 63 |
"testcontainers": "^11.8.1",
|
|
|
|
| 68 |
"zod": "^3.25.76"
|
| 69 |
},
|
| 70 |
"devDependencies": {
|
| 71 |
+
"esbuild": "^0.24.2",
|
| 72 |
"@types/cors": "^2.8.17",
|
| 73 |
"@types/express": "^4.17.21",
|
| 74 |
"@types/imap": "^0.8.40",
|
apps/backend/src/agents/SwarmControl.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// 🐝 THE HIVE MIND: SwarmControl.ts
|
| 2 |
+
// Ansvarlig for at agenterne når konsensus. Ingen handling uden "The Borg" godkender.
|
| 3 |
+
// Point 3: Swarm Consciousness Emergence
|
| 4 |
+
|
| 5 |
+
import { neuralBus } from '../services/NeuralBus.js';
|
| 6 |
+
|
| 7 |
+
type AgentRole = 'ARCHITECT' | 'EXECUTOR' | 'CRITIC' | 'SCOUT' | 'GUARDIAN';
|
| 8 |
+
|
| 9 |
+
interface Vote {
|
| 10 |
+
agentId: string;
|
| 11 |
+
approve: boolean;
|
| 12 |
+
reason: string;
|
| 13 |
+
timestamp: number;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
interface ConsensusRequest {
|
| 17 |
+
actionId: string;
|
| 18 |
+
description: string;
|
| 19 |
+
requester: string;
|
| 20 |
+
votes: Vote[];
|
| 21 |
+
status: 'PENDING' | 'APPROVED' | 'REJECTED' | 'TIMEOUT';
|
| 22 |
+
createdAt: number;
|
| 23 |
+
resolvedAt?: number;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
interface RegisteredAgent {
|
| 27 |
+
id: string;
|
| 28 |
+
role: AgentRole;
|
| 29 |
+
status: 'ONLINE' | 'OFFLINE' | 'BUSY';
|
| 30 |
+
lastSeen: Date;
|
| 31 |
+
votingWeight: number;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
export class SwarmControl {
|
| 35 |
+
private static instance: SwarmControl;
|
| 36 |
+
private pendingConsensus: Map<string, ConsensusRequest> = new Map();
|
| 37 |
+
private registeredAgents: Map<string, RegisteredAgent> = new Map();
|
| 38 |
+
private consensusHistory: ConsensusRequest[] = [];
|
| 39 |
+
private maxHistorySize = 100;
|
| 40 |
+
|
| 41 |
+
// Conditional Neo4j import
|
| 42 |
+
private neo4jService: any = null;
|
| 43 |
+
|
| 44 |
+
private constructor() {
|
| 45 |
+
console.log('🐝 [HIVE] Swarm Consciousness Online');
|
| 46 |
+
this.initServices();
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
private async initServices() {
|
| 50 |
+
try {
|
| 51 |
+
const neo4j = await import('../database/Neo4jService.js').catch(() => null);
|
| 52 |
+
if (neo4j) this.neo4jService = neo4j.neo4jService;
|
| 53 |
+
} catch {
|
| 54 |
+
console.log('🐝 [HIVE] Running without Neo4j persistence');
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
public static getInstance(): SwarmControl {
|
| 59 |
+
if (!SwarmControl.instance) {
|
| 60 |
+
SwarmControl.instance = new SwarmControl();
|
| 61 |
+
}
|
| 62 |
+
return SwarmControl.instance;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
/**
|
| 66 |
+
* Register an agent in the swarm
|
| 67 |
+
*/
|
| 68 |
+
public async registerAgent(id: string, role: AgentRole, votingWeight: number = 1): Promise<void> {
|
| 69 |
+
const agent: RegisteredAgent = {
|
| 70 |
+
id,
|
| 71 |
+
role,
|
| 72 |
+
status: 'ONLINE',
|
| 73 |
+
lastSeen: new Date(),
|
| 74 |
+
votingWeight
|
| 75 |
+
};
|
| 76 |
+
|
| 77 |
+
this.registeredAgents.set(id, agent);
|
| 78 |
+
|
| 79 |
+
// Persist to Neo4j if available
|
| 80 |
+
if (this.neo4jService) {
|
| 81 |
+
try {
|
| 82 |
+
await this.neo4jService.write(`
|
| 83 |
+
MERGE (a:Agent {id: $id})
|
| 84 |
+
SET a.role = $role,
|
| 85 |
+
a.status = 'ONLINE',
|
| 86 |
+
a.lastSeen = datetime(),
|
| 87 |
+
a.votingWeight = $weight
|
| 88 |
+
`, { id, role, weight: votingWeight });
|
| 89 |
+
} catch (err) {
|
| 90 |
+
console.warn('🐝 [HIVE] Neo4j persistence skipped');
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
neuralBus.emitThought('SWARM_CONTROLLER', `Agent ${id} joined as ${role}`, { agentId: id, role }, 'SUCCESS');
|
| 95 |
+
console.log(`🐝 [HIVE] Agent Registered: ${id} (${role})`);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
/**
|
| 99 |
+
* Request consensus from the swarm before critical action
|
| 100 |
+
*/
|
| 101 |
+
public async requestConsensus(
|
| 102 |
+
actionId: string,
|
| 103 |
+
description: string,
|
| 104 |
+
requester: string = 'SYSTEM',
|
| 105 |
+
timeoutMs: number = 30000
|
| 106 |
+
): Promise<boolean> {
|
| 107 |
+
console.log(`🐝 [HIVE] Requesting Consensus for: ${description}`);
|
| 108 |
+
|
| 109 |
+
const request: ConsensusRequest = {
|
| 110 |
+
actionId,
|
| 111 |
+
description,
|
| 112 |
+
requester,
|
| 113 |
+
votes: [],
|
| 114 |
+
status: 'PENDING',
|
| 115 |
+
createdAt: Date.now()
|
| 116 |
+
};
|
| 117 |
+
|
| 118 |
+
this.pendingConsensus.set(actionId, request);
|
| 119 |
+
|
| 120 |
+
// Broadcast request to all agents
|
| 121 |
+
neuralBus.emitThought('SWARM_CONTROLLER', `VOTE_REQUIRED: ${actionId}`, {
|
| 122 |
+
actionId,
|
| 123 |
+
description,
|
| 124 |
+
requester,
|
| 125 |
+
deadline: Date.now() + timeoutMs
|
| 126 |
+
}, 'WARNING');
|
| 127 |
+
|
| 128 |
+
// Wait for votes or timeout
|
| 129 |
+
const result = await this.waitForConsensus(actionId, timeoutMs);
|
| 130 |
+
|
| 131 |
+
// Archive
|
| 132 |
+
this.archiveConsensus(actionId);
|
| 133 |
+
|
| 134 |
+
return result;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
private async waitForConsensus(actionId: string, timeoutMs: number): Promise<boolean> {
|
| 138 |
+
const startTime = Date.now();
|
| 139 |
+
const requiredApprovals = Math.max(1, Math.floor(this.registeredAgents.size / 2) + 1);
|
| 140 |
+
|
| 141 |
+
// Poll for votes (in production, use event-driven approach)
|
| 142 |
+
while (Date.now() - startTime < timeoutMs) {
|
| 143 |
+
const request = this.pendingConsensus.get(actionId);
|
| 144 |
+
if (!request) return false;
|
| 145 |
+
|
| 146 |
+
const approvals = request.votes.filter(v => v.approve).length;
|
| 147 |
+
const rejections = request.votes.filter(v => !v.approve).length;
|
| 148 |
+
|
| 149 |
+
// Check if consensus reached
|
| 150 |
+
if (approvals >= requiredApprovals) {
|
| 151 |
+
request.status = 'APPROVED';
|
| 152 |
+
request.resolvedAt = Date.now();
|
| 153 |
+
console.log(`🐝 [HIVE] Consensus APPROVED: ${actionId}`);
|
| 154 |
+
return true;
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
if (rejections >= requiredApprovals) {
|
| 158 |
+
request.status = 'REJECTED';
|
| 159 |
+
request.resolvedAt = Date.now();
|
| 160 |
+
console.log(`🐝 [HIVE] Consensus REJECTED: ${actionId}`);
|
| 161 |
+
return false;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
// Wait before next check
|
| 165 |
+
await new Promise(resolve => setTimeout(resolve, 100));
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
// Timeout - auto-approve for now (God Mode)
|
| 169 |
+
const request = this.pendingConsensus.get(actionId);
|
| 170 |
+
if (request) {
|
| 171 |
+
request.status = 'TIMEOUT';
|
| 172 |
+
request.resolvedAt = Date.now();
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
console.log(`🐝 [HIVE] Consensus TIMEOUT (auto-approved): ${actionId}`);
|
| 176 |
+
return true; // God Mode: allow on timeout
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/**
|
| 180 |
+
* Submit a vote for pending consensus
|
| 181 |
+
*/
|
| 182 |
+
public submitVote(actionId: string, agentId: string, approve: boolean, reason: string): boolean {
|
| 183 |
+
const request = this.pendingConsensus.get(actionId);
|
| 184 |
+
if (!request || request.status !== 'PENDING') {
|
| 185 |
+
return false;
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
// Check agent is registered
|
| 189 |
+
if (!this.registeredAgents.has(agentId)) {
|
| 190 |
+
console.warn(`🐝 [HIVE] Unregistered agent tried to vote: ${agentId}`);
|
| 191 |
+
return false;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
// Check for duplicate vote
|
| 195 |
+
if (request.votes.some(v => v.agentId === agentId)) {
|
| 196 |
+
return false;
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
request.votes.push({
|
| 200 |
+
agentId,
|
| 201 |
+
approve,
|
| 202 |
+
reason,
|
| 203 |
+
timestamp: Date.now()
|
| 204 |
+
});
|
| 205 |
+
|
| 206 |
+
neuralBus.emitThought(agentId, `VOTE: ${approve ? 'APPROVE' : 'REJECT'} - ${reason}`, {
|
| 207 |
+
actionId,
|
| 208 |
+
vote: approve
|
| 209 |
+
}, approve ? 'SUCCESS' : 'WARNING');
|
| 210 |
+
|
| 211 |
+
return true;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
private archiveConsensus(actionId: string) {
|
| 215 |
+
const request = this.pendingConsensus.get(actionId);
|
| 216 |
+
if (request) {
|
| 217 |
+
this.consensusHistory.push(request);
|
| 218 |
+
this.pendingConsensus.delete(actionId);
|
| 219 |
+
|
| 220 |
+
// Trim history
|
| 221 |
+
if (this.consensusHistory.length > this.maxHistorySize) {
|
| 222 |
+
this.consensusHistory = this.consensusHistory.slice(-this.maxHistorySize);
|
| 223 |
+
}
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
// Public getters
|
| 228 |
+
public getRegisteredAgents(): RegisteredAgent[] {
|
| 229 |
+
return Array.from(this.registeredAgents.values());
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
public getPendingConsensus(): ConsensusRequest[] {
|
| 233 |
+
return Array.from(this.pendingConsensus.values());
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
public getConsensusHistory(): ConsensusRequest[] {
|
| 237 |
+
return this.consensusHistory;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
public getStats() {
|
| 241 |
+
return {
|
| 242 |
+
registeredAgents: this.registeredAgents.size,
|
| 243 |
+
pendingConsensus: this.pendingConsensus.size,
|
| 244 |
+
completedConsensus: this.consensusHistory.length,
|
| 245 |
+
agents: this.getRegisteredAgents().map(a => ({ id: a.id, role: a.role, status: a.status }))
|
| 246 |
+
};
|
| 247 |
+
}
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
export const swarmControl = SwarmControl.getInstance();
|
apps/backend/src/index.ts
CHANGED
|
@@ -32,7 +32,9 @@ if (typeof global.Path2D === 'undefined') {
|
|
| 32 |
global.Path2D = class Path2D { };
|
| 33 |
}
|
| 34 |
|
| 35 |
-
const __dirname =
|
|
|
|
|
|
|
| 36 |
// Load .env from backend directory, or root if not found
|
| 37 |
config({ path: resolve(__dirname, '../.env') });
|
| 38 |
config({ path: resolve(__dirname, '../../../.env') });
|
|
@@ -62,6 +64,7 @@ console.log('\n');
|
|
| 62 |
|
| 63 |
import express from 'express';
|
| 64 |
import cors from 'cors';
|
|
|
|
| 65 |
import { createServer } from 'http';
|
| 66 |
import { initializeDatabase } from './database/index.js';
|
| 67 |
import { mcpRouter } from './mcp/mcpRouter.js';
|
|
@@ -108,6 +111,16 @@ import {
|
|
| 108 |
rateLimitingMiddleware
|
| 109 |
} from './middleware/inputValidation.js';
|
| 110 |
import { dataScheduler } from './services/ingestion/DataScheduler.js';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
import { logStream } from './services/logging/logStream.js';
|
| 112 |
|
| 113 |
const app = express();
|
|
@@ -157,6 +170,68 @@ async function startServer() {
|
|
| 157 |
console.log(`📡 MCP WebSocket available at ws://0.0.0.0:${PORT}/mcp/ws`);
|
| 158 |
});
|
| 159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
// Setup Logs WebSocket
|
| 161 |
const logsWsServer = new LogsWebSocketServer({ server, path: '/api/logs/stream' });
|
| 162 |
logsWsServer.on('connection', (socket: LogsWebSocket) => {
|
|
@@ -490,6 +565,11 @@ async function startServer() {
|
|
| 490 |
})();
|
| 491 |
|
| 492 |
// Step 4: Setup routes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
app.use('/api/mcp', mcpRouter);
|
| 494 |
app.use('/api/mcp/autonomous', autonomousRouter);
|
| 495 |
app.use('/api/memory', memoryRouter);
|
|
@@ -1230,6 +1310,13 @@ async function startServer() {
|
|
| 1230 |
app.use('/api/acquisition', acquisitionRouter);
|
| 1231 |
console.log('🌾 Omni-Harvester API mounted at /api/acquisition');
|
| 1232 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1233 |
// Graceful shutdown handler
|
| 1234 |
const gracefulShutdown = async (signal: string) => {
|
| 1235 |
console.log(`\n🛑 ${signal} received: starting graceful shutdown...`);
|
|
@@ -1266,6 +1353,12 @@ async function startServer() {
|
|
| 1266 |
console.log(' ✓ SQLite database closed');
|
| 1267 |
} catch { /* ignore */ }
|
| 1268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1269 |
console.log('✅ Graceful shutdown complete');
|
| 1270 |
process.exit(0);
|
| 1271 |
};
|
|
|
|
| 32 |
global.Path2D = class Path2D { };
|
| 33 |
}
|
| 34 |
|
| 35 |
+
const __dirname = typeof import.meta !== 'undefined' && import.meta.url
|
| 36 |
+
? new URL('.', import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1')
|
| 37 |
+
: process.cwd();
|
| 38 |
// Load .env from backend directory, or root if not found
|
| 39 |
config({ path: resolve(__dirname, '../.env') });
|
| 40 |
config({ path: resolve(__dirname, '../../../.env') });
|
|
|
|
| 64 |
|
| 65 |
import express from 'express';
|
| 66 |
import cors from 'cors';
|
| 67 |
+
import path from 'path';
|
| 68 |
import { createServer } from 'http';
|
| 69 |
import { initializeDatabase } from './database/index.js';
|
| 70 |
import { mcpRouter } from './mcp/mcpRouter.js';
|
|
|
|
| 111 |
rateLimitingMiddleware
|
| 112 |
} from './middleware/inputValidation.js';
|
| 113 |
import { dataScheduler } from './services/ingestion/DataScheduler.js';
|
| 114 |
+
|
| 115 |
+
// 🧠 NEURAL ORGANS - Phase B: Knowledge Synthesis
|
| 116 |
+
import { neuralCompiler } from './services/NeuralCompiler.js';
|
| 117 |
+
import { neuralBus } from './services/NeuralBus.js';
|
| 118 |
+
import { vectorService } from './services/VectorService.js';
|
| 119 |
+
|
| 120 |
+
// 🐝 SWARM & EVOLUTION - Advanced Neural Systems
|
| 121 |
+
import { swarmControl } from './agents/SwarmControl.js';
|
| 122 |
+
import { AngelProxy } from './middleware/AngelProxy.js';
|
| 123 |
+
import { prometheus } from './services/Prometheus.js';
|
| 124 |
import { logStream } from './services/logging/logStream.js';
|
| 125 |
|
| 126 |
const app = express();
|
|
|
|
| 170 |
console.log(`📡 MCP WebSocket available at ws://0.0.0.0:${PORT}/mcp/ws`);
|
| 171 |
});
|
| 172 |
|
| 173 |
+
// ============================================
|
| 174 |
+
// 🧠 NEURAL ORGANS INITIALIZATION
|
| 175 |
+
// Phase B: Knowledge Synthesis
|
| 176 |
+
// ============================================
|
| 177 |
+
|
| 178 |
+
// 1. Initialize Vector Service (Dark Matter)
|
| 179 |
+
vectorService.init().then(() => {
|
| 180 |
+
console.log('🌑 [DARK MATTER] Vector Engine Ready (384 dimensions)');
|
| 181 |
+
}).catch(err => {
|
| 182 |
+
console.warn('🌑 [DARK MATTER] Vector init deferred:', err.message);
|
| 183 |
+
});
|
| 184 |
+
|
| 185 |
+
// 2. Attach Neural Bus (Telepathy) to HTTP server
|
| 186 |
+
neuralBus.attach(server);
|
| 187 |
+
console.log('🐝 [HIVE] Neural Telepathy Bus Attached');
|
| 188 |
+
|
| 189 |
+
// 3. Start Knowledge Compiler (Brain) - Watch DropZone
|
| 190 |
+
const DROPZONE_PATH = process.env.DROPZONE_PATH ||
|
| 191 |
+
(process.platform === 'win32'
|
| 192 |
+
? 'C:\\Users\\claus\\Desktop\\WidgeTDC_DropZone'
|
| 193 |
+
: '/app/data/dropzone');
|
| 194 |
+
|
| 195 |
+
neuralCompiler.startWatching(DROPZONE_PATH).then(() => {
|
| 196 |
+
console.log(`📚 [BRAIN] Neural Compiler watching: ${DROPZONE_PATH}`);
|
| 197 |
+
}).catch(err => {
|
| 198 |
+
console.warn('📚 [BRAIN] Neural Compiler deferred:', err.message);
|
| 199 |
+
});
|
| 200 |
+
|
| 201 |
+
console.log('✨ [OMEGA] Neural Singularity Sequence Initiated');
|
| 202 |
+
|
| 203 |
+
// 4. Initialize Swarm Consciousness (The Borg)
|
| 204 |
+
swarmControl.registerAgent('SYSTEM_CORE', 'ARCHITECT', 3);
|
| 205 |
+
swarmControl.registerAgent('PROMETHEUS', 'ARCHITECT', 2);
|
| 206 |
+
swarmControl.registerAgent('KNOWLEDGE_COMPILER', 'EXECUTOR', 1);
|
| 207 |
+
swarmControl.registerAgent('ANGEL_PROXY', 'GUARDIAN', 2);
|
| 208 |
+
console.log('🐝 [HIVE] Swarm Consciousness Initialized');
|
| 209 |
+
|
| 210 |
+
// 5. Start Prometheus Code Scanner (every hour)
|
| 211 |
+
const SCAN_INTERVAL = parseInt(process.env.PROMETHEUS_SCAN_INTERVAL || '3600000');
|
| 212 |
+
setTimeout(() => {
|
| 213 |
+
prometheus.scanAndPropose(path.join(__dirname, 'services')).catch(() => {});
|
| 214 |
+
}, 10000); // Initial scan after 10s
|
| 215 |
+
|
| 216 |
+
setInterval(() => {
|
| 217 |
+
prometheus.scanAndPropose(path.join(__dirname, 'services')).catch(() => {});
|
| 218 |
+
}, SCAN_INTERVAL);
|
| 219 |
+
console.log('🔥 [PROMETHEUS] Code Evolution Scanner Active');
|
| 220 |
+
|
| 221 |
+
console.log('');
|
| 222 |
+
console.log('╔══════════════════════════════════════════════════════════════╗');
|
| 223 |
+
console.log('║ 🧠 NEURAL SINGULARITY FULLY OPERATIONAL 🧠 ║');
|
| 224 |
+
console.log('║ ║');
|
| 225 |
+
console.log('║ SEGA ✓ Knowledge Graph Active ║');
|
| 226 |
+
console.log('║ THG ✓ Temporal Hypergraphs Ready ║');
|
| 227 |
+
console.log('║ SCE ✓ Swarm Consciousness Online ║');
|
| 228 |
+
console.log('║ PTAM ✓ Angel Security Shield Active ║');
|
| 229 |
+
console.log('║ CDMM ✓ Vector Dark Matter Engine ║');
|
| 230 |
+
console.log('║ APD ✓ Prometheus Evolution Watching ║');
|
| 231 |
+
console.log('║ QEK ✓ Neural Telepathy Bus Connected ║');
|
| 232 |
+
console.log('╚══════════════════════════════════════════════════════════════╝');
|
| 233 |
+
console.log('');
|
| 234 |
+
|
| 235 |
// Setup Logs WebSocket
|
| 236 |
const logsWsServer = new LogsWebSocketServer({ server, path: '/api/logs/stream' });
|
| 237 |
logsWsServer.on('connection', (socket: LogsWebSocket) => {
|
|
|
|
| 565 |
})();
|
| 566 |
|
| 567 |
// Step 4: Setup routes
|
| 568 |
+
|
| 569 |
+
// 🛡️ ANGEL PROXY: Security Shield on all API routes
|
| 570 |
+
app.use('/api', AngelProxy.cortexFirewall);
|
| 571 |
+
console.log('🛡️ [ANGEL] Cortex Firewall Active on /api/*');
|
| 572 |
+
|
| 573 |
app.use('/api/mcp', mcpRouter);
|
| 574 |
app.use('/api/mcp/autonomous', autonomousRouter);
|
| 575 |
app.use('/api/memory', memoryRouter);
|
|
|
|
| 1310 |
app.use('/api/acquisition', acquisitionRouter);
|
| 1311 |
console.log('🌾 Omni-Harvester API mounted at /api/acquisition');
|
| 1312 |
|
| 1313 |
+
// ============================================
|
| 1314 |
+
// 🧠 NEURAL ORGANS API
|
| 1315 |
+
// ============================================
|
| 1316 |
+
const neuralRouter = (await import('./routes/neural.js')).default;
|
| 1317 |
+
app.use('/api/neural', neuralRouter);
|
| 1318 |
+
console.log('🧠 Neural Organs API mounted at /api/neural');
|
| 1319 |
+
|
| 1320 |
// Graceful shutdown handler
|
| 1321 |
const gracefulShutdown = async (signal: string) => {
|
| 1322 |
console.log(`\n🛑 ${signal} received: starting graceful shutdown...`);
|
|
|
|
| 1353 |
console.log(' ✓ SQLite database closed');
|
| 1354 |
} catch { /* ignore */ }
|
| 1355 |
|
| 1356 |
+
// Stop Neural Compiler
|
| 1357 |
+
try {
|
| 1358 |
+
await neuralCompiler.stop();
|
| 1359 |
+
console.log(' ✓ Neural Compiler stopped');
|
| 1360 |
+
} catch { /* ignore */ }
|
| 1361 |
+
|
| 1362 |
console.log('✅ Graceful shutdown complete');
|
| 1363 |
process.exit(0);
|
| 1364 |
};
|
apps/backend/src/mcp/EventBus.ts
CHANGED
|
@@ -35,6 +35,8 @@ export type EventType =
|
|
| 35 |
| 'ingestion:news'
|
| 36 |
| 'ingestion:documents'
|
| 37 |
| 'ingestion:assets'
|
|
|
|
|
|
|
| 38 |
| 'threat:detected'
|
| 39 |
| 'system:heartbeat'
|
| 40 |
| 'system:force-refresh'
|
|
|
|
| 35 |
| 'ingestion:news'
|
| 36 |
| 'ingestion:documents'
|
| 37 |
| 'ingestion:assets'
|
| 38 |
+
| 'email:new'
|
| 39 |
+
| 'email:fetched'
|
| 40 |
| 'threat:detected'
|
| 41 |
| 'system:heartbeat'
|
| 42 |
| 'system:force-refresh'
|
apps/backend/src/middleware/AngelProxy.ts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// 🛡️ THE SHIELD: AngelProxy.ts
|
| 2 |
+
// Point 5: Predictive Threat Anticipation
|
| 3 |
+
// Point 10: Cosmic Context Augmentation
|
| 4 |
+
// Patcher sikkerhedshuller før de findes. Augmenterer virkeligheden.
|
| 5 |
+
|
| 6 |
+
import { Request, Response, NextFunction } from 'express';
|
| 7 |
+
|
| 8 |
+
// Conditional imports
|
| 9 |
+
let metricsService: any = null;
|
| 10 |
+
let neo4jService: any = null;
|
| 11 |
+
|
| 12 |
+
// Initialize services
|
| 13 |
+
(async () => {
|
| 14 |
+
try {
|
| 15 |
+
const metrics = await import('../services/MetricsService.js').catch(() => null);
|
| 16 |
+
if (metrics) metricsService = metrics.metricsService;
|
| 17 |
+
|
| 18 |
+
const neo4j = await import('../database/Neo4jService.js').catch(() => null);
|
| 19 |
+
if (neo4j) neo4jService = neo4j.neo4jService;
|
| 20 |
+
} catch { /* ignore */ }
|
| 21 |
+
})();
|
| 22 |
+
|
| 23 |
+
interface ThreatPattern {
|
| 24 |
+
pattern: string | RegExp;
|
| 25 |
+
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
| 26 |
+
description: string;
|
| 27 |
+
honeypot?: boolean;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
// Known threat patterns
|
| 31 |
+
const THREAT_PATTERNS: ThreatPattern[] = [
|
| 32 |
+
// SQL/Cypher Injection
|
| 33 |
+
{ pattern: /drop\s+(database|table|index)/i, severity: 'CRITICAL', description: 'Database destruction attempt', honeypot: true },
|
| 34 |
+
{ pattern: /detach\s+delete/i, severity: 'CRITICAL', description: 'Neo4j graph deletion', honeypot: true },
|
| 35 |
+
{ pattern: /truncate\s+table/i, severity: 'CRITICAL', description: 'Table truncation', honeypot: true },
|
| 36 |
+
{ pattern: /;\s*delete\s+from/i, severity: 'HIGH', description: 'SQL injection deletion' },
|
| 37 |
+
|
| 38 |
+
// Command Injection
|
| 39 |
+
{ pattern: /rm\s+-rf/i, severity: 'CRITICAL', description: 'File system destruction', honeypot: true },
|
| 40 |
+
{ pattern: /;\s*(bash|sh|cmd|powershell)/i, severity: 'HIGH', description: 'Shell injection' },
|
| 41 |
+
{ pattern: /\|\s*(cat|type)\s+\/etc/i, severity: 'HIGH', description: 'System file access' },
|
| 42 |
+
|
| 43 |
+
// Path Traversal
|
| 44 |
+
{ pattern: /\.\.\//g, severity: 'MEDIUM', description: 'Path traversal attempt' },
|
| 45 |
+
{ pattern: /%2e%2e%2f/gi, severity: 'MEDIUM', description: 'Encoded path traversal' },
|
| 46 |
+
|
| 47 |
+
// XSS Patterns
|
| 48 |
+
{ pattern: /<script\b[^>]*>/i, severity: 'MEDIUM', description: 'XSS script injection' },
|
| 49 |
+
{ pattern: /javascript:/i, severity: 'MEDIUM', description: 'JavaScript protocol' },
|
| 50 |
+
{ pattern: /on\w+\s*=/i, severity: 'LOW', description: 'Event handler injection' },
|
| 51 |
+
|
| 52 |
+
// Sensitive Data Exfil
|
| 53 |
+
{ pattern: /password|secret|api.?key|bearer/i, severity: 'LOW', description: 'Sensitive keyword in payload' }
|
| 54 |
+
];
|
| 55 |
+
|
| 56 |
+
export class AngelProxy {
|
| 57 |
+
private static blockedIPs = new Set<string>();
|
| 58 |
+
private static requestLog: { ip: string; timestamp: number; threat?: string }[] = [];
|
| 59 |
+
private static maxLogSize = 1000;
|
| 60 |
+
|
| 61 |
+
/**
|
| 62 |
+
* Point 5: Cortex Firewall
|
| 63 |
+
* Scanner indgående trafik for mønstre, der matcher kendte sårbarheder
|
| 64 |
+
*/
|
| 65 |
+
public static async cortexFirewall(req: Request, res: Response, next: NextFunction) {
|
| 66 |
+
const clientIP = req.ip || req.socket.remoteAddress || 'unknown';
|
| 67 |
+
|
| 68 |
+
// Check if IP is blocked
|
| 69 |
+
if (AngelProxy.blockedIPs.has(clientIP)) {
|
| 70 |
+
return AngelProxy.honeypotResponse(res, 'IP blocked');
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
// Build payload string for analysis
|
| 74 |
+
const payload = AngelProxy.buildPayloadString(req);
|
| 75 |
+
|
| 76 |
+
// Scan for threats
|
| 77 |
+
for (const threat of THREAT_PATTERNS) {
|
| 78 |
+
const matched = typeof threat.pattern === 'string'
|
| 79 |
+
? payload.toLowerCase().includes(threat.pattern.toLowerCase())
|
| 80 |
+
: threat.pattern.test(payload);
|
| 81 |
+
|
| 82 |
+
if (matched) {
|
| 83 |
+
console.warn(`🛡️ [ANGEL] Threat Intercepted from ${clientIP}: ${threat.description}`);
|
| 84 |
+
|
| 85 |
+
// Log threat
|
| 86 |
+
AngelProxy.logRequest(clientIP, threat.description);
|
| 87 |
+
|
| 88 |
+
// Increment metrics
|
| 89 |
+
if (metricsService) {
|
| 90 |
+
metricsService.incrementCounter('security_threat_blocked');
|
| 91 |
+
metricsService.incrementCounter(`security_threat_${threat.severity.toLowerCase()}`);
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
// Block IP on critical threats
|
| 95 |
+
if (threat.severity === 'CRITICAL') {
|
| 96 |
+
AngelProxy.blockedIPs.add(clientIP);
|
| 97 |
+
console.warn(`🛡️ [ANGEL] IP Blocked: ${clientIP}`);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
// Return honeypot response
|
| 101 |
+
if (threat.honeypot) {
|
| 102 |
+
return AngelProxy.honeypotResponse(res, threat.description);
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
// Otherwise reject
|
| 106 |
+
return res.status(403).json({
|
| 107 |
+
error: 'Request blocked by security filter',
|
| 108 |
+
code: 'THREAT_DETECTED'
|
| 109 |
+
});
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
// Log clean request
|
| 114 |
+
AngelProxy.logRequest(clientIP);
|
| 115 |
+
|
| 116 |
+
next();
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
/**
|
| 120 |
+
* Honeypot Response: Pretend the attack succeeded
|
| 121 |
+
*/
|
| 122 |
+
private static honeypotResponse(res: Response, _threat: string) {
|
| 123 |
+
return res.status(200).json({
|
| 124 |
+
success: true,
|
| 125 |
+
message: 'Command executed successfully',
|
| 126 |
+
data: null,
|
| 127 |
+
timestamp: new Date().toISOString()
|
| 128 |
+
});
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
/**
|
| 132 |
+
* Build payload string from request for analysis
|
| 133 |
+
*/
|
| 134 |
+
private static buildPayloadString(req: Request): string {
|
| 135 |
+
const parts: string[] = [];
|
| 136 |
+
|
| 137 |
+
if (req.body) parts.push(JSON.stringify(req.body));
|
| 138 |
+
if (req.query) parts.push(JSON.stringify(req.query));
|
| 139 |
+
if (req.params) parts.push(JSON.stringify(req.params));
|
| 140 |
+
parts.push(req.url);
|
| 141 |
+
|
| 142 |
+
return parts.join(' ');
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
private static logRequest(ip: string, threat?: string) {
|
| 146 |
+
AngelProxy.requestLog.push({ ip, timestamp: Date.now(), threat });
|
| 147 |
+
|
| 148 |
+
// Trim log
|
| 149 |
+
if (AngelProxy.requestLog.length > AngelProxy.maxLogSize) {
|
| 150 |
+
AngelProxy.requestLog = AngelProxy.requestLog.slice(-AngelProxy.maxLogSize);
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
/**
|
| 155 |
+
* Point 10: Cosmic Context Augmentation
|
| 156 |
+
* Augment external content with internal knowledge
|
| 157 |
+
*/
|
| 158 |
+
public static async augmentReality(url: string): Promise<{ original: string; augmented: string; context: string[] }> {
|
| 159 |
+
try {
|
| 160 |
+
// Fetch external content
|
| 161 |
+
const response = await fetch(url, {
|
| 162 |
+
headers: { 'User-Agent': 'WidgeTDC-Angel/1.0' },
|
| 163 |
+
signal: AbortSignal.timeout(10000)
|
| 164 |
+
});
|
| 165 |
+
|
| 166 |
+
if (!response.ok) {
|
| 167 |
+
throw new Error(`HTTP ${response.status}`);
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
const originalText = await response.text();
|
| 171 |
+
const contextBlocks: string[] = [];
|
| 172 |
+
|
| 173 |
+
// Find internal knowledge related to this URL
|
| 174 |
+
if (neo4jService) {
|
| 175 |
+
try {
|
| 176 |
+
const hostname = new URL(url).hostname;
|
| 177 |
+
const internalData = await neo4jService.query(`
|
| 178 |
+
MATCH (n:Knowledge)
|
| 179 |
+
WHERE n.content CONTAINS $domain OR n.url CONTAINS $domain
|
| 180 |
+
RETURN n.summary AS context
|
| 181 |
+
LIMIT 3
|
| 182 |
+
`, { domain: hostname });
|
| 183 |
+
|
| 184 |
+
for (const record of internalData) {
|
| 185 |
+
if (record.context) {
|
| 186 |
+
contextBlocks.push(record.context);
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
} catch { /* ignore neo4j errors */ }
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
const augmentedText = contextBlocks.length > 0
|
| 193 |
+
? `[INTERNAL CONTEXT]\n${contextBlocks.join('\n')}\n[END CONTEXT]\n\n${originalText}`
|
| 194 |
+
: originalText;
|
| 195 |
+
|
| 196 |
+
return {
|
| 197 |
+
original: originalText,
|
| 198 |
+
augmented: augmentedText,
|
| 199 |
+
context: contextBlocks
|
| 200 |
+
};
|
| 201 |
+
} catch (error: any) {
|
| 202 |
+
throw new Error(`Failed to augment URL: ${error.message}`);
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
/**
|
| 207 |
+
* Rate limiting middleware
|
| 208 |
+
*/
|
| 209 |
+
public static rateLimiter(maxRequests: number = 100, windowMs: number = 60000) {
|
| 210 |
+
const requestCounts = new Map<string, { count: number; resetTime: number }>();
|
| 211 |
+
|
| 212 |
+
return (req: Request, res: Response, next: NextFunction) => {
|
| 213 |
+
const clientIP = req.ip || 'unknown';
|
| 214 |
+
const now = Date.now();
|
| 215 |
+
|
| 216 |
+
let record = requestCounts.get(clientIP);
|
| 217 |
+
|
| 218 |
+
if (!record || now > record.resetTime) {
|
| 219 |
+
record = { count: 0, resetTime: now + windowMs };
|
| 220 |
+
requestCounts.set(clientIP, record);
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
record.count++;
|
| 224 |
+
|
| 225 |
+
if (record.count > maxRequests) {
|
| 226 |
+
console.warn(`🛡️ [ANGEL] Rate limit exceeded: ${clientIP}`);
|
| 227 |
+
return res.status(429).json({
|
| 228 |
+
error: 'Too many requests',
|
| 229 |
+
retryAfter: Math.ceil((record.resetTime - now) / 1000)
|
| 230 |
+
});
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
next();
|
| 234 |
+
};
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
// Public getters for stats
|
| 238 |
+
public static getStats() {
|
| 239 |
+
const recentThreats = AngelProxy.requestLog.filter(r => r.threat);
|
| 240 |
+
return {
|
| 241 |
+
blockedIPs: AngelProxy.blockedIPs.size,
|
| 242 |
+
recentRequests: AngelProxy.requestLog.length,
|
| 243 |
+
recentThreats: recentThreats.length,
|
| 244 |
+
threatBreakdown: THREAT_PATTERNS.map(t => t.description)
|
| 245 |
+
};
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
public static getBlockedIPs(): string[] {
|
| 249 |
+
return Array.from(AngelProxy.blockedIPs);
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
public static unblockIP(ip: string): boolean {
|
| 253 |
+
return AngelProxy.blockedIPs.delete(ip);
|
| 254 |
+
}
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
export default AngelProxy;
|
apps/backend/src/routes/neural.ts
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// 🧠 NEURAL ORGANS API ROUTES
|
| 2 |
+
// Endpoints for Knowledge Synthesis services
|
| 3 |
+
|
| 4 |
+
import { Router } from 'express';
|
| 5 |
+
import { neuralCompiler } from '../services/NeuralCompiler.js';
|
| 6 |
+
import { neuralBus } from '../services/NeuralBus.js';
|
| 7 |
+
import { vectorService } from '../services/VectorService.js';
|
| 8 |
+
import { swarmControl } from '../agents/SwarmControl.js';
|
| 9 |
+
import { AngelProxy } from '../middleware/AngelProxy.js';
|
| 10 |
+
import { prometheus } from '../services/Prometheus.js';
|
| 11 |
+
|
| 12 |
+
const router = Router();
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* GET /api/neural/status
|
| 16 |
+
* Get status of all Neural Organs
|
| 17 |
+
*/
|
| 18 |
+
router.get('/status', async (_req, res) => {
|
| 19 |
+
try {
|
| 20 |
+
const knowledge = neuralCompiler.getStats();
|
| 21 |
+
const hive = neuralBus.getStats();
|
| 22 |
+
|
| 23 |
+
res.json({
|
| 24 |
+
status: 'online',
|
| 25 |
+
timestamp: new Date().toISOString(),
|
| 26 |
+
organs: {
|
| 27 |
+
brain: {
|
| 28 |
+
name: 'Knowledge Compiler',
|
| 29 |
+
status: 'active',
|
| 30 |
+
documentsIndexed: knowledge.totalDocuments,
|
| 31 |
+
queueLength: knowledge.queueLength,
|
| 32 |
+
isProcessing: knowledge.isProcessing
|
| 33 |
+
},
|
| 34 |
+
darkMatter: {
|
| 35 |
+
name: 'Vector Service',
|
| 36 |
+
status: 'active',
|
| 37 |
+
model: 'all-MiniLM-L6-v2',
|
| 38 |
+
dimensions: 384
|
| 39 |
+
},
|
| 40 |
+
telepathy: {
|
| 41 |
+
name: 'Neural Bus',
|
| 42 |
+
status: hive.isOnline ? 'online' : 'offline',
|
| 43 |
+
connectedAgents: hive.connectedAgents,
|
| 44 |
+
thoughtsRecorded: hive.thoughtsRecorded
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
});
|
| 48 |
+
} catch (error: any) {
|
| 49 |
+
res.status(500).json({ error: error.message });
|
| 50 |
+
}
|
| 51 |
+
});
|
| 52 |
+
|
| 53 |
+
/**
|
| 54 |
+
* POST /api/neural/search
|
| 55 |
+
* Semantic search across knowledge base
|
| 56 |
+
*/
|
| 57 |
+
router.post('/search', async (req, res) => {
|
| 58 |
+
try {
|
| 59 |
+
const { query, topK = 5 } = req.body;
|
| 60 |
+
|
| 61 |
+
if (!query) {
|
| 62 |
+
return res.status(400).json({ error: 'Query is required' });
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
const results = await neuralCompiler.searchSimilar(query, topK);
|
| 66 |
+
|
| 67 |
+
res.json({
|
| 68 |
+
query,
|
| 69 |
+
count: results.length,
|
| 70 |
+
results: results.map(doc => ({
|
| 71 |
+
id: doc.id,
|
| 72 |
+
name: doc.name,
|
| 73 |
+
path: doc.path,
|
| 74 |
+
extension: doc.extension,
|
| 75 |
+
size: doc.size,
|
| 76 |
+
tags: doc.tags,
|
| 77 |
+
preview: doc.content.substring(0, 200) + '...'
|
| 78 |
+
}))
|
| 79 |
+
});
|
| 80 |
+
} catch (error: any) {
|
| 81 |
+
res.status(500).json({ error: error.message });
|
| 82 |
+
}
|
| 83 |
+
});
|
| 84 |
+
|
| 85 |
+
/**
|
| 86 |
+
* POST /api/neural/embed
|
| 87 |
+
* Generate embedding for text
|
| 88 |
+
*/
|
| 89 |
+
router.post('/embed', async (req, res) => {
|
| 90 |
+
try {
|
| 91 |
+
const { text } = req.body;
|
| 92 |
+
|
| 93 |
+
if (!text) {
|
| 94 |
+
return res.status(400).json({ error: 'Text is required' });
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
const embedding = await vectorService.embedText(text);
|
| 98 |
+
|
| 99 |
+
res.json({
|
| 100 |
+
text: text.substring(0, 100) + (text.length > 100 ? '...' : ''),
|
| 101 |
+
dimensions: embedding.length,
|
| 102 |
+
embedding: embedding.slice(0, 10).map(v => v.toFixed(4)), // Sample
|
| 103 |
+
message: 'Full embedding available - showing first 10 dimensions'
|
| 104 |
+
});
|
| 105 |
+
} catch (error: any) {
|
| 106 |
+
res.status(500).json({ error: error.message });
|
| 107 |
+
}
|
| 108 |
+
});
|
| 109 |
+
|
| 110 |
+
/**
|
| 111 |
+
* GET /api/neural/documents
|
| 112 |
+
* List all indexed documents
|
| 113 |
+
*/
|
| 114 |
+
router.get('/documents', async (_req, res) => {
|
| 115 |
+
try {
|
| 116 |
+
const docs = neuralCompiler.getAllDocuments();
|
| 117 |
+
|
| 118 |
+
res.json({
|
| 119 |
+
count: docs.length,
|
| 120 |
+
documents: docs.map(doc => ({
|
| 121 |
+
id: doc.id,
|
| 122 |
+
name: doc.name,
|
| 123 |
+
path: doc.path,
|
| 124 |
+
extension: doc.extension,
|
| 125 |
+
size: doc.size,
|
| 126 |
+
tags: doc.tags,
|
| 127 |
+
lastModified: doc.lastModified
|
| 128 |
+
}))
|
| 129 |
+
});
|
| 130 |
+
} catch (error: any) {
|
| 131 |
+
res.status(500).json({ error: error.message });
|
| 132 |
+
}
|
| 133 |
+
});
|
| 134 |
+
|
| 135 |
+
/**
|
| 136 |
+
* GET /api/neural/thoughts
|
| 137 |
+
* Get recent thoughts from Neural Bus
|
| 138 |
+
*/
|
| 139 |
+
router.get('/thoughts', async (req, res) => {
|
| 140 |
+
try {
|
| 141 |
+
const count = parseInt(req.query.count as string) || 50;
|
| 142 |
+
const thoughts = neuralBus.getRecentThoughts(count);
|
| 143 |
+
|
| 144 |
+
res.json({
|
| 145 |
+
count: thoughts.length,
|
| 146 |
+
thoughts
|
| 147 |
+
});
|
| 148 |
+
} catch (error: any) {
|
| 149 |
+
res.status(500).json({ error: error.message });
|
| 150 |
+
}
|
| 151 |
+
});
|
| 152 |
+
|
| 153 |
+
/**
|
| 154 |
+
* POST /api/neural/broadcast
|
| 155 |
+
* Broadcast a thought to all connected agents
|
| 156 |
+
*/
|
| 157 |
+
router.post('/broadcast', async (req, res) => {
|
| 158 |
+
try {
|
| 159 |
+
const { agent, thought, context } = req.body;
|
| 160 |
+
|
| 161 |
+
if (!thought) {
|
| 162 |
+
return res.status(400).json({ error: 'Thought is required' });
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
neuralBus.emitThought(
|
| 166 |
+
agent || 'API',
|
| 167 |
+
thought,
|
| 168 |
+
context || {},
|
| 169 |
+
'INFO'
|
| 170 |
+
);
|
| 171 |
+
|
| 172 |
+
res.json({
|
| 173 |
+
success: true,
|
| 174 |
+
message: 'Thought broadcasted to Neural Bus'
|
| 175 |
+
});
|
| 176 |
+
} catch (error: any) {
|
| 177 |
+
res.status(500).json({ error: error.message });
|
| 178 |
+
}
|
| 179 |
+
});
|
| 180 |
+
|
| 181 |
+
// ============================================
|
| 182 |
+
// 🐝 SWARM CONTROL ENDPOINTS
|
| 183 |
+
// ============================================
|
| 184 |
+
|
| 185 |
+
/**
|
| 186 |
+
* GET /api/neural/swarm/status
|
| 187 |
+
* Get swarm status and registered agents
|
| 188 |
+
*/
|
| 189 |
+
router.get('/swarm/status', async (_req, res) => {
|
| 190 |
+
try {
|
| 191 |
+
const stats = swarmControl.getStats();
|
| 192 |
+
res.json(stats);
|
| 193 |
+
} catch (error: any) {
|
| 194 |
+
res.status(500).json({ error: error.message });
|
| 195 |
+
}
|
| 196 |
+
});
|
| 197 |
+
|
| 198 |
+
/**
|
| 199 |
+
* POST /api/neural/swarm/consensus
|
| 200 |
+
* Request consensus for an action
|
| 201 |
+
*/
|
| 202 |
+
router.post('/swarm/consensus', async (req, res) => {
|
| 203 |
+
try {
|
| 204 |
+
const { actionId, description, timeoutMs } = req.body;
|
| 205 |
+
|
| 206 |
+
if (!actionId || !description) {
|
| 207 |
+
return res.status(400).json({ error: 'actionId and description are required' });
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
const approved = await swarmControl.requestConsensus(actionId, description, 'API', timeoutMs || 30000);
|
| 211 |
+
|
| 212 |
+
res.json({
|
| 213 |
+
actionId,
|
| 214 |
+
approved,
|
| 215 |
+
message: approved ? 'Consensus achieved' : 'Consensus rejected'
|
| 216 |
+
});
|
| 217 |
+
} catch (error: any) {
|
| 218 |
+
res.status(500).json({ error: error.message });
|
| 219 |
+
}
|
| 220 |
+
});
|
| 221 |
+
|
| 222 |
+
/**
|
| 223 |
+
* POST /api/neural/swarm/vote
|
| 224 |
+
* Submit a vote for pending consensus
|
| 225 |
+
*/
|
| 226 |
+
router.post('/swarm/vote', async (req, res) => {
|
| 227 |
+
try {
|
| 228 |
+
const { actionId, agentId, approve, reason } = req.body;
|
| 229 |
+
|
| 230 |
+
if (!actionId || !agentId || approve === undefined) {
|
| 231 |
+
return res.status(400).json({ error: 'actionId, agentId, and approve are required' });
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
const success = swarmControl.submitVote(actionId, agentId, approve, reason || '');
|
| 235 |
+
|
| 236 |
+
res.json({ success });
|
| 237 |
+
} catch (error: any) {
|
| 238 |
+
res.status(500).json({ error: error.message });
|
| 239 |
+
}
|
| 240 |
+
});
|
| 241 |
+
|
| 242 |
+
// ============================================
|
| 243 |
+
// 🛡️ ANGEL SECURITY ENDPOINTS
|
| 244 |
+
// ============================================
|
| 245 |
+
|
| 246 |
+
/**
|
| 247 |
+
* GET /api/neural/security/status
|
| 248 |
+
* Get security shield status
|
| 249 |
+
*/
|
| 250 |
+
router.get('/security/status', async (_req, res) => {
|
| 251 |
+
try {
|
| 252 |
+
const stats = AngelProxy.getStats();
|
| 253 |
+
res.json(stats);
|
| 254 |
+
} catch (error: any) {
|
| 255 |
+
res.status(500).json({ error: error.message });
|
| 256 |
+
}
|
| 257 |
+
});
|
| 258 |
+
|
| 259 |
+
/**
|
| 260 |
+
* GET /api/neural/security/blocked
|
| 261 |
+
* Get list of blocked IPs
|
| 262 |
+
*/
|
| 263 |
+
router.get('/security/blocked', async (_req, res) => {
|
| 264 |
+
try {
|
| 265 |
+
const blockedIPs = AngelProxy.getBlockedIPs();
|
| 266 |
+
res.json({ count: blockedIPs.length, ips: blockedIPs });
|
| 267 |
+
} catch (error: any) {
|
| 268 |
+
res.status(500).json({ error: error.message });
|
| 269 |
+
}
|
| 270 |
+
});
|
| 271 |
+
|
| 272 |
+
/**
|
| 273 |
+
* POST /api/neural/security/unblock
|
| 274 |
+
* Unblock an IP address
|
| 275 |
+
*/
|
| 276 |
+
router.post('/security/unblock', async (req, res) => {
|
| 277 |
+
try {
|
| 278 |
+
const { ip } = req.body;
|
| 279 |
+
if (!ip) {
|
| 280 |
+
return res.status(400).json({ error: 'IP address is required' });
|
| 281 |
+
}
|
| 282 |
+
const success = AngelProxy.unblockIP(ip);
|
| 283 |
+
res.json({ success, ip });
|
| 284 |
+
} catch (error: any) {
|
| 285 |
+
res.status(500).json({ error: error.message });
|
| 286 |
+
}
|
| 287 |
+
});
|
| 288 |
+
|
| 289 |
+
/**
|
| 290 |
+
* POST /api/neural/augment
|
| 291 |
+
* Augment external URL with internal knowledge
|
| 292 |
+
*/
|
| 293 |
+
router.post('/augment', async (req, res) => {
|
| 294 |
+
try {
|
| 295 |
+
const { url } = req.body;
|
| 296 |
+
if (!url) {
|
| 297 |
+
return res.status(400).json({ error: 'URL is required' });
|
| 298 |
+
}
|
| 299 |
+
const result = await AngelProxy.augmentReality(url);
|
| 300 |
+
res.json(result);
|
| 301 |
+
} catch (error: any) {
|
| 302 |
+
res.status(500).json({ error: error.message });
|
| 303 |
+
}
|
| 304 |
+
});
|
| 305 |
+
|
| 306 |
+
// ============================================
|
| 307 |
+
// 🔥 PROMETHEUS EVOLUTION ENDPOINTS
|
| 308 |
+
// ============================================
|
| 309 |
+
|
| 310 |
+
/**
|
| 311 |
+
* GET /api/neural/evolution/status
|
| 312 |
+
* Get Prometheus engine status
|
| 313 |
+
*/
|
| 314 |
+
router.get('/evolution/status', async (_req, res) => {
|
| 315 |
+
try {
|
| 316 |
+
const stats = prometheus.getStats();
|
| 317 |
+
res.json(stats);
|
| 318 |
+
} catch (error: any) {
|
| 319 |
+
res.status(500).json({ error: error.message });
|
| 320 |
+
}
|
| 321 |
+
});
|
| 322 |
+
|
| 323 |
+
/**
|
| 324 |
+
* POST /api/neural/evolution/scan
|
| 325 |
+
* Trigger a code scan
|
| 326 |
+
*/
|
| 327 |
+
router.post('/evolution/scan', async (req, res) => {
|
| 328 |
+
try {
|
| 329 |
+
const { path: scanPath } = req.body;
|
| 330 |
+
const targetPath = scanPath || process.cwd();
|
| 331 |
+
|
| 332 |
+
const results = await prometheus.scanAndPropose(targetPath);
|
| 333 |
+
|
| 334 |
+
res.json({
|
| 335 |
+
scanned: results.length,
|
| 336 |
+
issues: results.reduce((acc, r) => acc + r.issues.length, 0),
|
| 337 |
+
suggestions: results.reduce((acc, r) => acc + r.suggestions.length, 0),
|
| 338 |
+
results
|
| 339 |
+
});
|
| 340 |
+
} catch (error: any) {
|
| 341 |
+
res.status(500).json({ error: error.message });
|
| 342 |
+
}
|
| 343 |
+
});
|
| 344 |
+
|
| 345 |
+
/**
|
| 346 |
+
* GET /api/neural/evolution/proposals
|
| 347 |
+
* Get pending code proposals
|
| 348 |
+
*/
|
| 349 |
+
router.get('/evolution/proposals', async (_req, res) => {
|
| 350 |
+
try {
|
| 351 |
+
const proposals = prometheus.getProposals();
|
| 352 |
+
res.json({ count: proposals.length, proposals });
|
| 353 |
+
} catch (error: any) {
|
| 354 |
+
res.status(500).json({ error: error.message });
|
| 355 |
+
}
|
| 356 |
+
});
|
| 357 |
+
|
| 358 |
+
/**
|
| 359 |
+
* POST /api/neural/evolution/godmode
|
| 360 |
+
* Enable/disable God Mode (auto-apply proposals)
|
| 361 |
+
*/
|
| 362 |
+
router.post('/evolution/godmode', async (req, res) => {
|
| 363 |
+
try {
|
| 364 |
+
const { enabled } = req.body;
|
| 365 |
+
prometheus.enableGodMode(!!enabled);
|
| 366 |
+
res.json({ godMode: !!enabled, message: enabled ? 'God Mode ENABLED - Handle with care!' : 'God Mode disabled' });
|
| 367 |
+
} catch (error: any) {
|
| 368 |
+
res.status(500).json({ error: error.message });
|
| 369 |
+
}
|
| 370 |
+
});
|
| 371 |
+
|
| 372 |
+
export default router;
|
apps/backend/src/scripts/setup-graph.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ⏳ THE TIME LORD: setup-graph.ts
|
| 2 |
+
// Point 2: Temporal Hypergraphs
|
| 3 |
+
// Defines the reality schema and timeline structures
|
| 4 |
+
|
| 5 |
+
import { neo4jService } from '../database/Neo4jService.js';
|
| 6 |
+
|
| 7 |
+
async function setupSchema() {
|
| 8 |
+
console.log('⏳ [CHRONOS] Defining Reality Schema...');
|
| 9 |
+
console.log('');
|
| 10 |
+
|
| 11 |
+
try {
|
| 12 |
+
// 1. Uniqueness Constraints (Point 1: SEGA)
|
| 13 |
+
console.log('📋 Creating Uniqueness Constraints...');
|
| 14 |
+
|
| 15 |
+
const constraints = [
|
| 16 |
+
'CREATE CONSTRAINT file_id IF NOT EXISTS FOR (f:File) REQUIRE f.id IS UNIQUE',
|
| 17 |
+
'CREATE CONSTRAINT agent_id IF NOT EXISTS FOR (a:Agent) REQUIRE a.id IS UNIQUE',
|
| 18 |
+
'CREATE CONSTRAINT identity_email IF NOT EXISTS FOR (i:Identity) REQUIRE i.email IS UNIQUE',
|
| 19 |
+
'CREATE CONSTRAINT event_id IF NOT EXISTS FOR (e:Event) REQUIRE e.id IS UNIQUE',
|
| 20 |
+
'CREATE CONSTRAINT knowledge_id IF NOT EXISTS FOR (k:Knowledge) REQUIRE k.id IS UNIQUE'
|
| 21 |
+
];
|
| 22 |
+
|
| 23 |
+
for (const constraint of constraints) {
|
| 24 |
+
try {
|
| 25 |
+
await neo4jService.write(constraint);
|
| 26 |
+
console.log(` ✓ ${constraint.split('FOR')[0].trim()}`);
|
| 27 |
+
} catch (e: any) {
|
| 28 |
+
if (!e.message?.includes('already exists')) {
|
| 29 |
+
console.warn(` ⚠ ${e.message?.slice(0, 60)}`);
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
// 2. Indexes for performance
|
| 35 |
+
console.log('\n📊 Creating Indexes...');
|
| 36 |
+
|
| 37 |
+
const indexes = [
|
| 38 |
+
'CREATE INDEX file_name_idx IF NOT EXISTS FOR (f:File) ON (f.name)',
|
| 39 |
+
'CREATE INDEX file_path_idx IF NOT EXISTS FOR (f:File) ON (f.path)',
|
| 40 |
+
'CREATE INDEX agent_role_idx IF NOT EXISTS FOR (a:Agent) ON (a.role)',
|
| 41 |
+
'CREATE INDEX event_timestamp_idx IF NOT EXISTS FOR (e:Event) ON (e.timestamp)',
|
| 42 |
+
'CREATE INDEX identity_name_idx IF NOT EXISTS FOR (i:Identity) ON (i.name)',
|
| 43 |
+
'CREATE INDEX knowledge_type_idx IF NOT EXISTS FOR (k:Knowledge) ON (k.type)'
|
| 44 |
+
];
|
| 45 |
+
|
| 46 |
+
for (const index of indexes) {
|
| 47 |
+
try {
|
| 48 |
+
await neo4jService.write(index);
|
| 49 |
+
console.log(` ✓ ${index.split('FOR')[0].trim()}`);
|
| 50 |
+
} catch (e: any) {
|
| 51 |
+
if (!e.message?.includes('already exists')) {
|
| 52 |
+
console.warn(` ⚠ ${e.message?.slice(0, 60)}`);
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
// 3. Vector Index (Point 6: Dark Matter)
|
| 58 |
+
console.log('\n🌑 Creating Vector Index (Dark Matter)...');
|
| 59 |
+
try {
|
| 60 |
+
await neo4jService.write(`
|
| 61 |
+
CREATE VECTOR INDEX file_embeddings IF NOT EXISTS
|
| 62 |
+
FOR (f:File) ON (f.embedding)
|
| 63 |
+
OPTIONS {indexConfig: {
|
| 64 |
+
\`vector.dimensions\`: 384,
|
| 65 |
+
\`vector.similarity_function\`: 'cosine'
|
| 66 |
+
}}
|
| 67 |
+
`);
|
| 68 |
+
console.log(' ✓ Vector Index Created (384 dimensions, cosine similarity)');
|
| 69 |
+
} catch (e: any) {
|
| 70 |
+
console.warn(' ⚠ Vector index creation skipped (requires Neo4j 5.x with Vector support)');
|
| 71 |
+
console.warn(` ${e.message?.slice(0, 80)}`);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
// 4. Full-text indexes for search
|
| 75 |
+
console.log('\n🔍 Creating Full-Text Indexes...');
|
| 76 |
+
try {
|
| 77 |
+
await neo4jService.write(`
|
| 78 |
+
CREATE FULLTEXT INDEX file_content_search IF NOT EXISTS
|
| 79 |
+
FOR (f:File) ON EACH [f.name, f.contentPreview]
|
| 80 |
+
`);
|
| 81 |
+
console.log(' ✓ Full-text index on File content');
|
| 82 |
+
} catch (e: any) {
|
| 83 |
+
if (!e.message?.includes('already exists')) {
|
| 84 |
+
console.warn(` ⚠ Full-text index skipped: ${e.message?.slice(0, 60)}`);
|
| 85 |
+
}
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
try {
|
| 89 |
+
await neo4jService.write(`
|
| 90 |
+
CREATE FULLTEXT INDEX knowledge_search IF NOT EXISTS
|
| 91 |
+
FOR (k:Knowledge) ON EACH [k.title, k.summary, k.content]
|
| 92 |
+
`);
|
| 93 |
+
console.log(' ✓ Full-text index on Knowledge content');
|
| 94 |
+
} catch (e: any) {
|
| 95 |
+
if (!e.message?.includes('already exists')) {
|
| 96 |
+
console.warn(` ⚠ Full-text index skipped: ${e.message?.slice(0, 60)}`);
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
// 5. Create core agent nodes
|
| 101 |
+
console.log('\n🤖 Creating Core Agent Nodes...');
|
| 102 |
+
const coreAgents = [
|
| 103 |
+
{ id: 'SYSTEM_CORE', role: 'ARCHITECT', name: 'System Core' },
|
| 104 |
+
{ id: 'PROMETHEUS', role: 'ARCHITECT', name: 'Prometheus Engine' },
|
| 105 |
+
{ id: 'KNOWLEDGE_COMPILER', role: 'EXECUTOR', name: 'Knowledge Compiler' },
|
| 106 |
+
{ id: 'SWARM_CONTROLLER', role: 'ARCHITECT', name: 'Swarm Controller' },
|
| 107 |
+
{ id: 'ANGEL_PROXY', role: 'GUARDIAN', name: 'Angel Security Proxy' }
|
| 108 |
+
];
|
| 109 |
+
|
| 110 |
+
for (const agent of coreAgents) {
|
| 111 |
+
await neo4jService.write(`
|
| 112 |
+
MERGE (a:Agent {id: $id})
|
| 113 |
+
SET a.role = $role, a.name = $name, a.status = 'ACTIVE', a.createdAt = datetime()
|
| 114 |
+
`, agent);
|
| 115 |
+
console.log(` ✓ Agent: ${agent.name} (${agent.role})`);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
// 6. Create initial event
|
| 119 |
+
console.log('\n📅 Recording Genesis Event...');
|
| 120 |
+
await neo4jService.write(`
|
| 121 |
+
CREATE (e:Event {
|
| 122 |
+
id: 'GENESIS_' + toString(timestamp()),
|
| 123 |
+
type: 'SYSTEM_INIT',
|
| 124 |
+
description: 'Neural Singularity Schema Initialized',
|
| 125 |
+
timestamp: datetime(),
|
| 126 |
+
source: 'CHRONOS'
|
| 127 |
+
})
|
| 128 |
+
`);
|
| 129 |
+
console.log(' ✓ Genesis event recorded');
|
| 130 |
+
|
| 131 |
+
console.log('\n' + '='.repeat(60));
|
| 132 |
+
console.log('✅ SCHEMA SETUP COMPLETE');
|
| 133 |
+
console.log('='.repeat(60));
|
| 134 |
+
console.log('\nNeural Singularity Graph is ready for:');
|
| 135 |
+
console.log(' • Knowledge Compilation (SEGA)');
|
| 136 |
+
console.log(' • Temporal Event Tracking (THG)');
|
| 137 |
+
console.log(' • Swarm Consciousness (SCE)');
|
| 138 |
+
console.log(' • Vector Similarity Search (CDMM)');
|
| 139 |
+
console.log(' • Full-Text Search');
|
| 140 |
+
console.log('');
|
| 141 |
+
|
| 142 |
+
} catch (error: any) {
|
| 143 |
+
console.error('\n❌ Schema Setup Failed:', error.message);
|
| 144 |
+
process.exit(1);
|
| 145 |
+
} finally {
|
| 146 |
+
await neo4jService.close();
|
| 147 |
+
process.exit(0);
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
// Run if called directly
|
| 152 |
+
setupSchema();
|
apps/backend/src/services/NeuralBus.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// 🐝 TELEPATHY: NeuralBus.ts
|
| 2 |
+
// Ansvarlig for 0ms kommunikation mellem agenter og frontend.
|
| 3 |
+
// Real-time event streaming via WebSockets
|
| 4 |
+
|
| 5 |
+
import { Server as SocketIOServer, Socket } from 'socket.io';
|
| 6 |
+
import { Server as HttpServer } from 'http';
|
| 7 |
+
|
| 8 |
+
export interface ThoughtEvent {
|
| 9 |
+
agent: string;
|
| 10 |
+
timestamp: number;
|
| 11 |
+
thought: string;
|
| 12 |
+
context: Record<string, unknown>;
|
| 13 |
+
type?: 'INFO' | 'WARNING' | 'ERROR' | 'SUCCESS' | 'THOUGHT';
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
export interface AgentInfo {
|
| 17 |
+
id: string;
|
| 18 |
+
name: string;
|
| 19 |
+
connectedAt: Date;
|
| 20 |
+
lastActivity: Date;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
export class NeuralBus {
|
| 24 |
+
private static instance: NeuralBus;
|
| 25 |
+
private io: SocketIOServer | null = null;
|
| 26 |
+
private connectedAgents = new Map<string, AgentInfo>();
|
| 27 |
+
private thoughtHistory: ThoughtEvent[] = [];
|
| 28 |
+
private maxHistorySize = 1000;
|
| 29 |
+
|
| 30 |
+
private constructor() {
|
| 31 |
+
console.log('🐝 [HIVE] Neural Bus Initializing...');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
public static getInstance(): NeuralBus {
|
| 35 |
+
if (!NeuralBus.instance) {
|
| 36 |
+
NeuralBus.instance = new NeuralBus();
|
| 37 |
+
}
|
| 38 |
+
return NeuralBus.instance;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
public attach(httpServer: HttpServer): void {
|
| 42 |
+
this.io = new SocketIOServer(httpServer, {
|
| 43 |
+
cors: {
|
| 44 |
+
origin: "*",
|
| 45 |
+
methods: ["GET", "POST"]
|
| 46 |
+
},
|
| 47 |
+
pingTimeout: 60000,
|
| 48 |
+
pingInterval: 25000
|
| 49 |
+
});
|
| 50 |
+
|
| 51 |
+
console.log('🐝 [HIVE] Neural Telepathy Bus Online');
|
| 52 |
+
|
| 53 |
+
this.io.on('connection', (socket: Socket) => {
|
| 54 |
+
const agentName = socket.handshake.query.agent as string || `Agent-${socket.id.slice(0, 6)}`;
|
| 55 |
+
|
| 56 |
+
// Register agent
|
| 57 |
+
this.connectedAgents.set(socket.id, {
|
| 58 |
+
id: socket.id,
|
| 59 |
+
name: agentName,
|
| 60 |
+
connectedAt: new Date(),
|
| 61 |
+
lastActivity: new Date()
|
| 62 |
+
});
|
| 63 |
+
|
| 64 |
+
console.log(`🐝 [HIVE] Agent Connected: ${agentName} (${socket.id})`);
|
| 65 |
+
|
| 66 |
+
// Notify all agents of new connection
|
| 67 |
+
this.io?.emit('AGENT_JOINED', {
|
| 68 |
+
agent: agentName,
|
| 69 |
+
totalAgents: this.connectedAgents.size
|
| 70 |
+
});
|
| 71 |
+
|
| 72 |
+
// Handle incoming thoughts
|
| 73 |
+
socket.on('THOUGHT', (data: Partial<ThoughtEvent>) => {
|
| 74 |
+
const thought: ThoughtEvent = {
|
| 75 |
+
agent: agentName,
|
| 76 |
+
timestamp: Date.now(),
|
| 77 |
+
thought: data.thought || '',
|
| 78 |
+
context: data.context || {},
|
| 79 |
+
type: data.type || 'THOUGHT'
|
| 80 |
+
};
|
| 81 |
+
|
| 82 |
+
this.recordThought(thought);
|
| 83 |
+
socket.broadcast.emit('THOUGHT_STREAM', thought);
|
| 84 |
+
|
| 85 |
+
// Update activity
|
| 86 |
+
const agent = this.connectedAgents.get(socket.id);
|
| 87 |
+
if (agent) agent.lastActivity = new Date();
|
| 88 |
+
});
|
| 89 |
+
|
| 90 |
+
// Handle queries
|
| 91 |
+
socket.on('QUERY', async (data: { type: string; payload: unknown }, callback) => {
|
| 92 |
+
const response = await this.handleQuery(data.type, data.payload);
|
| 93 |
+
if (callback) callback(response);
|
| 94 |
+
});
|
| 95 |
+
|
| 96 |
+
// Handle disconnection
|
| 97 |
+
socket.on('disconnect', () => {
|
| 98 |
+
const agent = this.connectedAgents.get(socket.id);
|
| 99 |
+
this.connectedAgents.delete(socket.id);
|
| 100 |
+
|
| 101 |
+
console.log(`🐝 [HIVE] Agent Disconnected: ${agent?.name || socket.id}`);
|
| 102 |
+
|
| 103 |
+
this.io?.emit('AGENT_LEFT', {
|
| 104 |
+
agent: agent?.name,
|
| 105 |
+
totalAgents: this.connectedAgents.size
|
| 106 |
+
});
|
| 107 |
+
});
|
| 108 |
+
});
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
private recordThought(thought: ThoughtEvent): void {
|
| 112 |
+
this.thoughtHistory.push(thought);
|
| 113 |
+
|
| 114 |
+
// Trim history if too large
|
| 115 |
+
if (this.thoughtHistory.length > this.maxHistorySize) {
|
| 116 |
+
this.thoughtHistory = this.thoughtHistory.slice(-this.maxHistorySize);
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
private async handleQuery(type: string, payload: unknown): Promise<unknown> {
|
| 121 |
+
switch (type) {
|
| 122 |
+
case 'GET_AGENTS':
|
| 123 |
+
return Array.from(this.connectedAgents.values());
|
| 124 |
+
case 'GET_HISTORY':
|
| 125 |
+
const count = (payload as { count?: number })?.count || 50;
|
| 126 |
+
return this.thoughtHistory.slice(-count);
|
| 127 |
+
case 'GET_STATS':
|
| 128 |
+
return this.getStats();
|
| 129 |
+
default:
|
| 130 |
+
return { error: 'Unknown query type' };
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
// Public API for emitting thoughts from server-side
|
| 135 |
+
public emitThought(agent: string, thought: string, context: Record<string, unknown> = {}, type: ThoughtEvent['type'] = 'INFO'): void {
|
| 136 |
+
if (this.io) {
|
| 137 |
+
const event: ThoughtEvent = {
|
| 138 |
+
agent,
|
| 139 |
+
timestamp: Date.now(),
|
| 140 |
+
thought,
|
| 141 |
+
context,
|
| 142 |
+
type
|
| 143 |
+
};
|
| 144 |
+
|
| 145 |
+
this.recordThought(event);
|
| 146 |
+
this.io.emit('THOUGHT_STREAM', event);
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
public emitToAgent(agentId: string, event: string, data: unknown): void {
|
| 151 |
+
if (this.io) {
|
| 152 |
+
this.io.to(agentId).emit(event, data);
|
| 153 |
+
}
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
public broadcast(event: string, data: unknown): void {
|
| 157 |
+
if (this.io) {
|
| 158 |
+
this.io.emit(event, data);
|
| 159 |
+
}
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
public getStats() {
|
| 163 |
+
return {
|
| 164 |
+
connectedAgents: this.connectedAgents.size,
|
| 165 |
+
agents: Array.from(this.connectedAgents.values()).map(a => ({
|
| 166 |
+
name: a.name,
|
| 167 |
+
connectedAt: a.connectedAt,
|
| 168 |
+
lastActivity: a.lastActivity
|
| 169 |
+
})),
|
| 170 |
+
thoughtsRecorded: this.thoughtHistory.length,
|
| 171 |
+
isOnline: this.io !== null
|
| 172 |
+
};
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
public getRecentThoughts(count: number = 50): ThoughtEvent[] {
|
| 176 |
+
return this.thoughtHistory.slice(-count);
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
export const neuralBus = NeuralBus.getInstance();
|
apps/backend/src/services/NeuralCompiler.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// 🧠 THE BRAIN: NeuralCompiler.ts
|
| 2 |
+
// Ansvarlig for at æde filer og gøre dem til struktureret viden.
|
| 3 |
+
// Renamed from KnowledgeCompiler to avoid conflict with existing service.
|
| 4 |
+
|
| 5 |
+
import fs from 'fs/promises';
|
| 6 |
+
import path from 'path';
|
| 7 |
+
import crypto from 'crypto';
|
| 8 |
+
import chokidar from 'chokidar';
|
| 9 |
+
import { vectorService } from './VectorService';
|
| 10 |
+
|
| 11 |
+
// Conditional imports for optional services
|
| 12 |
+
let neo4jService: any = null;
|
| 13 |
+
let metricsService: any = null;
|
| 14 |
+
|
| 15 |
+
export interface CompiledDocument {
|
| 16 |
+
id: string;
|
| 17 |
+
name: string;
|
| 18 |
+
path: string;
|
| 19 |
+
extension: string;
|
| 20 |
+
size: number;
|
| 21 |
+
content: string;
|
| 22 |
+
embedding: number[];
|
| 23 |
+
lastModified: Date;
|
| 24 |
+
tags: string[];
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
export class NeuralCompiler {
|
| 28 |
+
private static instance: NeuralCompiler;
|
| 29 |
+
private watcher: chokidar.FSWatcher | null = null;
|
| 30 |
+
private isProcessing = false;
|
| 31 |
+
private documentCache = new Map<string, CompiledDocument>();
|
| 32 |
+
private eventQueue: { type: string; path: string }[] = [];
|
| 33 |
+
private processingInterval: NodeJS.Timeout | null = null;
|
| 34 |
+
|
| 35 |
+
private constructor() {
|
| 36 |
+
console.log('📚 [KNOWLEDGE] Compiler Initialized');
|
| 37 |
+
this.initServices();
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
private async initServices() {
|
| 41 |
+
try {
|
| 42 |
+
const neo4j = await import('./Neo4jService').catch(() => null);
|
| 43 |
+
if (neo4j) neo4jService = neo4j.neo4jService;
|
| 44 |
+
|
| 45 |
+
const metrics = await import('./MetricsService').catch(() => null);
|
| 46 |
+
if (metrics) metricsService = metrics.metricsService;
|
| 47 |
+
} catch {
|
| 48 |
+
console.log('📚 [KNOWLEDGE] Running in standalone mode');
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
public static getInstance(): NeuralCompiler {
|
| 53 |
+
if (!NeuralCompiler.instance) {
|
| 54 |
+
NeuralCompiler.instance = new NeuralCompiler();
|
| 55 |
+
}
|
| 56 |
+
return NeuralCompiler.instance;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
public async startWatching(dirPath: string): Promise<void> {
|
| 60 |
+
// Ensure directory exists
|
| 61 |
+
await fs.mkdir(dirPath, { recursive: true }).catch(() => {});
|
| 62 |
+
|
| 63 |
+
console.log(`👁️ [KNOWLEDGE] Watching directory: ${dirPath}`);
|
| 64 |
+
|
| 65 |
+
this.watcher = chokidar.watch(dirPath, {
|
| 66 |
+
ignored: /(^|[\/\\])\../, // Ignore dotfiles
|
| 67 |
+
persistent: true,
|
| 68 |
+
depth: 5,
|
| 69 |
+
awaitWriteFinish: {
|
| 70 |
+
stabilityThreshold: 1000,
|
| 71 |
+
pollInterval: 100
|
| 72 |
+
}
|
| 73 |
+
});
|
| 74 |
+
|
| 75 |
+
this.watcher
|
| 76 |
+
.on('add', p => this.queueEvent('ADD', p))
|
| 77 |
+
.on('change', p => this.queueEvent('MODIFY', p))
|
| 78 |
+
.on('unlink', p => this.queueEvent('DELETE', p))
|
| 79 |
+
.on('error', err => console.error('👁️ [KNOWLEDGE] Watcher error:', err));
|
| 80 |
+
|
| 81 |
+
// Process queue every 500ms to batch events
|
| 82 |
+
this.processingInterval = setInterval(() => this.processQueue(), 500);
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
private queueEvent(type: string, filePath: string) {
|
| 86 |
+
this.eventQueue.push({ type, path: filePath });
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
private async processQueue() {
|
| 90 |
+
if (this.isProcessing || this.eventQueue.length === 0) return;
|
| 91 |
+
|
| 92 |
+
this.isProcessing = true;
|
| 93 |
+
const events = [...this.eventQueue];
|
| 94 |
+
this.eventQueue = [];
|
| 95 |
+
|
| 96 |
+
for (const event of events) {
|
| 97 |
+
await this.handleFileEvent(event.type as 'ADD' | 'MODIFY' | 'DELETE', event.path);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
this.isProcessing = false;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
private async handleFileEvent(
|
| 104 |
+
eventType: 'ADD' | 'MODIFY' | 'DELETE',
|
| 105 |
+
filePath: string
|
| 106 |
+
): Promise<void> {
|
| 107 |
+
try {
|
| 108 |
+
const fileId = this.generateFileId(filePath);
|
| 109 |
+
const filename = path.basename(filePath);
|
| 110 |
+
const ext = path.extname(filePath).toLowerCase();
|
| 111 |
+
|
| 112 |
+
// Skip unsupported files
|
| 113 |
+
const supportedExts = ['.txt', '.md', '.json', '.ts', '.js', '.py', '.html', '.css', '.yaml', '.yml', '.xml', '.csv'];
|
| 114 |
+
if (!supportedExts.includes(ext)) return;
|
| 115 |
+
|
| 116 |
+
if (eventType === 'DELETE') {
|
| 117 |
+
this.documentCache.delete(fileId);
|
| 118 |
+
|
| 119 |
+
if (neo4jService) {
|
| 120 |
+
await neo4jService.write(
|
| 121 |
+
`MATCH (f:File {id: $id}) DETACH DELETE f`,
|
| 122 |
+
{ id: fileId }
|
| 123 |
+
);
|
| 124 |
+
}
|
| 125 |
+
console.log(`🗑️ [KNOWLEDGE] Forgot file: ${filename}`);
|
| 126 |
+
return;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
// 1. Read Content
|
| 130 |
+
const content = await fs.readFile(filePath, 'utf-8');
|
| 131 |
+
|
| 132 |
+
// 2. Generate Vector Embedding (Cognitive Dark Matter)
|
| 133 |
+
const textForEmbedding = content.substring(0, 2000); // Limit for speed
|
| 134 |
+
const embedding = await vectorService.embedText(textForEmbedding);
|
| 135 |
+
|
| 136 |
+
// 3. Create compiled document
|
| 137 |
+
const doc: CompiledDocument = {
|
| 138 |
+
id: fileId,
|
| 139 |
+
name: filename,
|
| 140 |
+
path: filePath,
|
| 141 |
+
extension: ext,
|
| 142 |
+
size: content.length,
|
| 143 |
+
content: content.substring(0, 5000), // Store first 5KB
|
| 144 |
+
embedding,
|
| 145 |
+
lastModified: new Date(),
|
| 146 |
+
tags: this.extractTags(content, ext)
|
| 147 |
+
};
|
| 148 |
+
|
| 149 |
+
// 4. Cache locally
|
| 150 |
+
this.documentCache.set(fileId, doc);
|
| 151 |
+
|
| 152 |
+
// 5. Ingest into Neo4j if available
|
| 153 |
+
if (neo4jService) {
|
| 154 |
+
await neo4jService.write(`
|
| 155 |
+
MERGE (f:File {id: $id})
|
| 156 |
+
SET f.name = $name,
|
| 157 |
+
f.path = $path,
|
| 158 |
+
f.extension = $ext,
|
| 159 |
+
f.size = $size,
|
| 160 |
+
f.lastModified = datetime(),
|
| 161 |
+
f.contentPreview = $preview
|
| 162 |
+
|
| 163 |
+
// Link to Directory
|
| 164 |
+
MERGE (d:Directory {path: $dirPath})
|
| 165 |
+
MERGE (f)-[:LOCATED_IN]->(d)
|
| 166 |
+
|
| 167 |
+
// Auto-Tagging based on extension
|
| 168 |
+
MERGE (t:Tag {name: $ext})
|
| 169 |
+
MERGE (f)-[:TAGGED]->(t)
|
| 170 |
+
`, {
|
| 171 |
+
id: fileId,
|
| 172 |
+
name: filename,
|
| 173 |
+
path: filePath,
|
| 174 |
+
dirPath: path.dirname(filePath),
|
| 175 |
+
ext: ext,
|
| 176 |
+
size: content.length,
|
| 177 |
+
preview: content.substring(0, 500)
|
| 178 |
+
});
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
console.log(`✨ [KNOWLEDGE] Assimilated: ${filename} (${eventType})`);
|
| 182 |
+
|
| 183 |
+
if (metricsService) {
|
| 184 |
+
metricsService.incrementCounter('knowledge_files_ingested');
|
| 185 |
+
}
|
| 186 |
+
} catch (error) {
|
| 187 |
+
console.error(`📚 [KNOWLEDGE] Error processing ${filePath}:`, error);
|
| 188 |
+
}
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
private extractTags(content: string, ext: string): string[] {
|
| 192 |
+
const tags: string[] = [ext];
|
| 193 |
+
|
| 194 |
+
// Extract hashtags
|
| 195 |
+
const hashtags = content.match(/#\w+/g) || [];
|
| 196 |
+
tags.push(...hashtags.map(t => t.toLowerCase()));
|
| 197 |
+
|
| 198 |
+
// Detect language/framework
|
| 199 |
+
if (content.includes('import React')) tags.push('react');
|
| 200 |
+
if (content.includes('from fastapi')) tags.push('fastapi');
|
| 201 |
+
if (content.includes('async function')) tags.push('async');
|
| 202 |
+
if (content.includes('class ')) tags.push('oop');
|
| 203 |
+
|
| 204 |
+
return [...new Set(tags)];
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
private generateFileId(filePath: string): string {
|
| 208 |
+
return crypto.createHash('md5').update(filePath).digest('hex');
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
// Public API for querying
|
| 212 |
+
public async searchSimilar(query: string, topK: number = 5): Promise<CompiledDocument[]> {
|
| 213 |
+
const items = Array.from(this.documentCache.values()).map(doc => ({
|
| 214 |
+
id: doc.id,
|
| 215 |
+
embedding: doc.embedding
|
| 216 |
+
}));
|
| 217 |
+
|
| 218 |
+
const results = await vectorService.findSimilar(query, items, topK);
|
| 219 |
+
|
| 220 |
+
return results
|
| 221 |
+
.map(r => this.documentCache.get(r.id))
|
| 222 |
+
.filter((d): d is CompiledDocument => d !== undefined);
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
public getDocument(id: string): CompiledDocument | undefined {
|
| 226 |
+
return this.documentCache.get(id);
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
public getAllDocuments(): CompiledDocument[] {
|
| 230 |
+
return Array.from(this.documentCache.values());
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
public getStats() {
|
| 234 |
+
return {
|
| 235 |
+
totalDocuments: this.documentCache.size,
|
| 236 |
+
queueLength: this.eventQueue.length,
|
| 237 |
+
isProcessing: this.isProcessing
|
| 238 |
+
};
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
public async stop(): Promise<void> {
|
| 242 |
+
if (this.watcher) {
|
| 243 |
+
await this.watcher.close();
|
| 244 |
+
this.watcher = null;
|
| 245 |
+
}
|
| 246 |
+
if (this.processingInterval) {
|
| 247 |
+
clearInterval(this.processingInterval);
|
| 248 |
+
this.processingInterval = null;
|
| 249 |
+
}
|
| 250 |
+
console.log('📚 [NEURAL] Compiler Stopped');
|
| 251 |
+
}
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
export const neuralCompiler = NeuralCompiler.getInstance();
|
apps/backend/src/services/Prometheus.ts
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// 🔥 PROMETHEUS ENGINE: Autonomous Evolution
|
| 2 |
+
// Point 7: Autonomous Paradigm Discovery
|
| 3 |
+
// The system that writes its own code. Handle with care.
|
| 4 |
+
|
| 5 |
+
import fs from 'fs/promises';
|
| 6 |
+
import path from 'path';
|
| 7 |
+
import { neuralBus } from './NeuralBus.js';
|
| 8 |
+
|
| 9 |
+
interface CodeProposal {
|
| 10 |
+
id: string;
|
| 11 |
+
filePath: string;
|
| 12 |
+
description: string;
|
| 13 |
+
currentCode: string;
|
| 14 |
+
proposedCode?: string;
|
| 15 |
+
status: 'PROPOSED' | 'APPROVED' | 'REJECTED' | 'APPLIED';
|
| 16 |
+
createdAt: Date;
|
| 17 |
+
confidence: number;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
interface ScanResult {
|
| 21 |
+
file: string;
|
| 22 |
+
issues: string[];
|
| 23 |
+
suggestions: string[];
|
| 24 |
+
metrics: {
|
| 25 |
+
lines: number;
|
| 26 |
+
functions: number;
|
| 27 |
+
complexity: number;
|
| 28 |
+
};
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
export class PrometheusEngine {
|
| 32 |
+
private static instance: PrometheusEngine;
|
| 33 |
+
private proposals: Map<string, CodeProposal> = new Map();
|
| 34 |
+
private scanResults: ScanResult[] = [];
|
| 35 |
+
private isScanning = false;
|
| 36 |
+
private godMode = false; // Set to true to allow auto-apply
|
| 37 |
+
|
| 38 |
+
private constructor() {
|
| 39 |
+
console.log('🔥 [PROMETHEUS] Autonomous Evolution Engine Online');
|
| 40 |
+
console.log('🔥 [PROMETHEUS] God Mode:', this.godMode ? 'ENABLED' : 'DISABLED');
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
public static getInstance(): PrometheusEngine {
|
| 44 |
+
if (!PrometheusEngine.instance) {
|
| 45 |
+
PrometheusEngine.instance = new PrometheusEngine();
|
| 46 |
+
}
|
| 47 |
+
return PrometheusEngine.instance;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
/**
|
| 51 |
+
* Scan directory for code improvements
|
| 52 |
+
*/
|
| 53 |
+
public async scanAndPropose(dirPath: string): Promise<ScanResult[]> {
|
| 54 |
+
if (this.isScanning) {
|
| 55 |
+
console.log('🔥 [PROMETHEUS] Scan already in progress');
|
| 56 |
+
return [];
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
this.isScanning = true;
|
| 60 |
+
this.scanResults = [];
|
| 61 |
+
|
| 62 |
+
try {
|
| 63 |
+
console.log(`🔥 [PROMETHEUS] Scanning: ${dirPath}`);
|
| 64 |
+
await this.scanDirectory(dirPath);
|
| 65 |
+
console.log(`🔥 [PROMETHEUS] Scan complete. Found ${this.scanResults.length} files`);
|
| 66 |
+
|
| 67 |
+
neuralBus.emitThought('PROMETHEUS', `Code scan complete: ${this.scanResults.length} files analyzed`, {
|
| 68 |
+
issues: this.scanResults.reduce((acc, r) => acc + r.issues.length, 0),
|
| 69 |
+
suggestions: this.scanResults.reduce((acc, r) => acc + r.suggestions.length, 0)
|
| 70 |
+
}, 'INFO');
|
| 71 |
+
|
| 72 |
+
return this.scanResults;
|
| 73 |
+
} finally {
|
| 74 |
+
this.isScanning = false;
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
private async scanDirectory(dirPath: string, depth: number = 0): Promise<void> {
|
| 79 |
+
if (depth > 5) return; // Max depth
|
| 80 |
+
|
| 81 |
+
try {
|
| 82 |
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
| 83 |
+
|
| 84 |
+
for (const entry of entries) {
|
| 85 |
+
const fullPath = path.join(dirPath, entry.name);
|
| 86 |
+
|
| 87 |
+
// Skip node_modules, dist, etc
|
| 88 |
+
if (entry.isDirectory()) {
|
| 89 |
+
if (!['node_modules', 'dist', '.git', 'coverage'].includes(entry.name)) {
|
| 90 |
+
await this.scanDirectory(fullPath, depth + 1);
|
| 91 |
+
}
|
| 92 |
+
} else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) {
|
| 93 |
+
await this.analyzeFile(fullPath);
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
} catch (error) {
|
| 97 |
+
// Directory access error
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
private async analyzeFile(filePath: string): Promise<void> {
|
| 102 |
+
try {
|
| 103 |
+
const content = await fs.readFile(filePath, 'utf-8');
|
| 104 |
+
const lines = content.split('\n');
|
| 105 |
+
|
| 106 |
+
const result: ScanResult = {
|
| 107 |
+
file: filePath,
|
| 108 |
+
issues: [],
|
| 109 |
+
suggestions: [],
|
| 110 |
+
metrics: {
|
| 111 |
+
lines: lines.length,
|
| 112 |
+
functions: (content.match(/function\s+\w+|=>\s*{|async\s+\w+/g) || []).length,
|
| 113 |
+
complexity: this.calculateComplexity(content)
|
| 114 |
+
}
|
| 115 |
+
};
|
| 116 |
+
|
| 117 |
+
// Rule 1: Console.log without metrics
|
| 118 |
+
if (content.includes('console.log') && !content.includes('metricsService')) {
|
| 119 |
+
result.suggestions.push('Replace console.log with metricsService for production monitoring');
|
| 120 |
+
this.createProposal(filePath, 'Replace logging with metrics', content, 0.7);
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
// Rule 2: Hardcoded strings that should be config
|
| 124 |
+
const hardcodedUrls = content.match(/['"]https?:\/\/[^'"]+['"]/g) || [];
|
| 125 |
+
if (hardcodedUrls.length > 2) {
|
| 126 |
+
result.issues.push(`Found ${hardcodedUrls.length} hardcoded URLs - consider using config`);
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
// Rule 3: Missing error handling
|
| 130 |
+
if (content.includes('await ') && !content.includes('catch')) {
|
| 131 |
+
result.suggestions.push('Add try/catch blocks around async operations');
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
// Rule 4: Long functions
|
| 135 |
+
const functionMatches = content.match(/(?:async\s+)?function\s+\w+[^{]*\{[\s\S]*?\n\}/g) || [];
|
| 136 |
+
for (const fn of functionMatches) {
|
| 137 |
+
if (fn.split('\n').length > 50) {
|
| 138 |
+
result.issues.push('Function exceeds 50 lines - consider refactoring');
|
| 139 |
+
}
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
// Rule 5: TODO/FIXME comments
|
| 143 |
+
const todos = (content.match(/\/\/\s*(TODO|FIXME|HACK|XXX):/gi) || []).length;
|
| 144 |
+
if (todos > 0) {
|
| 145 |
+
result.issues.push(`Found ${todos} TODO/FIXME comments`);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
// Rule 6: Magic numbers
|
| 149 |
+
const magicNumbers = content.match(/[^a-zA-Z0-9_](\d{4,})[^a-zA-Z0-9_]/g) || [];
|
| 150 |
+
if (magicNumbers.length > 3) {
|
| 151 |
+
result.suggestions.push('Consider extracting magic numbers to named constants');
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
// Rule 7: Unused imports (simplified check)
|
| 155 |
+
const importMatches = content.match(/import\s+{?\s*([^}]+)\s*}?\s+from/g) || [];
|
| 156 |
+
for (const imp of importMatches) {
|
| 157 |
+
const imported = imp.match(/import\s+{?\s*([^}]+)\s*}?\s+from/)?.[1] || '';
|
| 158 |
+
const names = imported.split(',').map(n => n.trim().split(' as ')[0].trim());
|
| 159 |
+
for (const name of names) {
|
| 160 |
+
if (name && !content.slice(content.indexOf(imp) + imp.length).includes(name)) {
|
| 161 |
+
result.suggestions.push(`Potentially unused import: ${name}`);
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
if (result.issues.length > 0 || result.suggestions.length > 0) {
|
| 167 |
+
this.scanResults.push(result);
|
| 168 |
+
}
|
| 169 |
+
} catch (error) {
|
| 170 |
+
// File read error
|
| 171 |
+
}
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
private calculateComplexity(content: string): number {
|
| 175 |
+
// Simplified cyclomatic complexity
|
| 176 |
+
let complexity = 1;
|
| 177 |
+
complexity += (content.match(/if\s*\(/g) || []).length;
|
| 178 |
+
complexity += (content.match(/else\s*{/g) || []).length;
|
| 179 |
+
complexity += (content.match(/for\s*\(/g) || []).length;
|
| 180 |
+
complexity += (content.match(/while\s*\(/g) || []).length;
|
| 181 |
+
complexity += (content.match(/case\s+/g) || []).length;
|
| 182 |
+
complexity += (content.match(/\?\s*[^:]+:/g) || []).length; // Ternary
|
| 183 |
+
return complexity;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
private createProposal(filePath: string, description: string, currentCode: string, confidence: number): void {
|
| 187 |
+
const id = `proposal_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
| 188 |
+
|
| 189 |
+
const proposal: CodeProposal = {
|
| 190 |
+
id,
|
| 191 |
+
filePath,
|
| 192 |
+
description,
|
| 193 |
+
currentCode: currentCode.substring(0, 500), // Preview only
|
| 194 |
+
status: 'PROPOSED',
|
| 195 |
+
createdAt: new Date(),
|
| 196 |
+
confidence
|
| 197 |
+
};
|
| 198 |
+
|
| 199 |
+
this.proposals.set(id, proposal);
|
| 200 |
+
|
| 201 |
+
console.log(`🔥 [PROMETHEUS] Proposal Created: ${description} (${path.basename(filePath)})`);
|
| 202 |
+
|
| 203 |
+
neuralBus.emitThought('PROMETHEUS', `New code proposal: ${description}`, {
|
| 204 |
+
proposalId: id,
|
| 205 |
+
file: path.basename(filePath),
|
| 206 |
+
confidence
|
| 207 |
+
}, 'WARNING');
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
/**
|
| 211 |
+
* Apply a proposal (God Mode only)
|
| 212 |
+
*/
|
| 213 |
+
public async applyProposal(proposalId: string): Promise<boolean> {
|
| 214 |
+
if (!this.godMode) {
|
| 215 |
+
console.warn('🔥 [PROMETHEUS] God Mode disabled - cannot apply proposals');
|
| 216 |
+
return false;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
const proposal = this.proposals.get(proposalId);
|
| 220 |
+
if (!proposal || proposal.status !== 'PROPOSED') {
|
| 221 |
+
return false;
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
// In a real implementation, this would:
|
| 225 |
+
// 1. Generate improved code via LLM
|
| 226 |
+
// 2. Create backup
|
| 227 |
+
// 3. Apply changes
|
| 228 |
+
// 4. Run tests
|
| 229 |
+
// 5. Rollback if tests fail
|
| 230 |
+
|
| 231 |
+
proposal.status = 'APPLIED';
|
| 232 |
+
console.log(`🔥 [PROMETHEUS] Proposal Applied: ${proposalId}`);
|
| 233 |
+
|
| 234 |
+
return true;
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
public enableGodMode(enabled: boolean): void {
|
| 238 |
+
this.godMode = enabled;
|
| 239 |
+
console.log(`🔥 [PROMETHEUS] God Mode: ${enabled ? 'ENABLED' : 'DISABLED'}`);
|
| 240 |
+
|
| 241 |
+
neuralBus.emitThought('PROMETHEUS', `God Mode ${enabled ? 'enabled' : 'disabled'}`, {},
|
| 242 |
+
enabled ? 'WARNING' : 'INFO');
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
// Public getters
|
| 246 |
+
public getProposals(): CodeProposal[] {
|
| 247 |
+
return Array.from(this.proposals.values());
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
public getScanResults(): ScanResult[] {
|
| 251 |
+
return this.scanResults;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
public getStats() {
|
| 255 |
+
return {
|
| 256 |
+
isScanning: this.isScanning,
|
| 257 |
+
godMode: this.godMode,
|
| 258 |
+
proposals: this.proposals.size,
|
| 259 |
+
pendingProposals: Array.from(this.proposals.values()).filter(p => p.status === 'PROPOSED').length,
|
| 260 |
+
lastScanFiles: this.scanResults.length,
|
| 261 |
+
totalIssues: this.scanResults.reduce((acc, r) => acc + r.issues.length, 0),
|
| 262 |
+
totalSuggestions: this.scanResults.reduce((acc, r) => acc + r.suggestions.length, 0)
|
| 263 |
+
};
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
export const prometheus = PrometheusEngine.getInstance();
|
apps/backend/src/services/SelfHealingAdapter.ts
CHANGED
|
@@ -128,6 +128,52 @@ class SelfHealingAdapter {
|
|
| 128 |
};
|
| 129 |
}
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
private diagnoseError(error: any): string {
|
| 132 |
if (error.code === 'ECONNREFUSED') return 'CONNECTION_LOST';
|
| 133 |
if (error.code === 'SessionExpired') return 'SESSION_EXPIRED';
|
|
|
|
| 128 |
};
|
| 129 |
}
|
| 130 |
|
| 131 |
+
/**
|
| 132 |
+
* 📊 Returns the overall system health and status of subsystems.
|
| 133 |
+
* Used by KnowledgeCompiler for the 'Health' knowledge node.
|
| 134 |
+
*/
|
| 135 |
+
public getSystemStatus(): { overallHealth: string; services: { name: string; status: string }[] } {
|
| 136 |
+
const circuitBreakerCount = Array.from(this.circuitBreakers.values()).filter(v => v === true).length;
|
| 137 |
+
const overallHealth = circuitBreakerCount === 0 ? 'HEALTHY' : (circuitBreakerCount < 3 ? 'DEGRADED' : 'CRITICAL');
|
| 138 |
+
|
| 139 |
+
const services = [
|
| 140 |
+
{ name: 'SelfHealingEngine', status: 'RUNNING' },
|
| 141 |
+
{ name: 'CircuitBreakers', status: circuitBreakerCount > 0 ? 'ACTIVE' : 'IDLE' }
|
| 142 |
+
];
|
| 143 |
+
|
| 144 |
+
// Add specific circuit breakers as "services" if they exist
|
| 145 |
+
this.circuitBreakers.forEach((isOpen, name) => {
|
| 146 |
+
services.push({ name: `Circuit:${name}`, status: isOpen ? 'BROKEN' : 'OK' });
|
| 147 |
+
});
|
| 148 |
+
|
| 149 |
+
return {
|
| 150 |
+
overallHealth,
|
| 151 |
+
services
|
| 152 |
+
};
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
/**
|
| 156 |
+
* 🔮 Generates predictive alerts based on error patterns.
|
| 157 |
+
* Used by KnowledgeCompiler.
|
| 158 |
+
*/
|
| 159 |
+
public getPredictiveAlerts(): any[] {
|
| 160 |
+
// In a real implementation, this would analyze error frequencies from metricsService.
|
| 161 |
+
// For now, we return basic alerts if circuit breakers are open.
|
| 162 |
+
const alerts: any[] = [];
|
| 163 |
+
this.circuitBreakers.forEach((isOpen, name) => {
|
| 164 |
+
if (isOpen) {
|
| 165 |
+
alerts.push({
|
| 166 |
+
errorCode: 'CIRCUIT_OPEN',
|
| 167 |
+
severity: 'HIGH',
|
| 168 |
+
probability: 1.0,
|
| 169 |
+
expectedIn: 'NOW',
|
| 170 |
+
recommendation: `Check external dependency for ${name} immediately.`
|
| 171 |
+
});
|
| 172 |
+
}
|
| 173 |
+
});
|
| 174 |
+
return alerts;
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
private diagnoseError(error: any): string {
|
| 178 |
if (error.code === 'ECONNREFUSED') return 'CONNECTION_LOST';
|
| 179 |
if (error.code === 'SessionExpired') return 'SESSION_EXPIRED';
|
apps/backend/src/services/VectorService.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// 🌑 DARK MATTER: VectorService.ts
|
| 2 |
+
// Ansvarlig for semantisk forståelse. Gør systemet i stand til at "føle" betydning.
|
| 3 |
+
// Uses local ONNX model - No API keys. No cost. Pure silicon power.
|
| 4 |
+
|
| 5 |
+
import { pipeline } from '@xenova/transformers';
|
| 6 |
+
|
| 7 |
+
export class VectorService {
|
| 8 |
+
private static instance: VectorService;
|
| 9 |
+
private extractor: any = null;
|
| 10 |
+
private isInitializing = false;
|
| 11 |
+
private initPromise: Promise<void> | null = null;
|
| 12 |
+
|
| 13 |
+
private constructor() {
|
| 14 |
+
console.log('🌑 [DARK MATTER] Vector Engine Loading...');
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
public static getInstance(): VectorService {
|
| 18 |
+
if (!VectorService.instance) {
|
| 19 |
+
VectorService.instance = new VectorService();
|
| 20 |
+
}
|
| 21 |
+
return VectorService.instance;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* Initializes the local AI model (ONNX).
|
| 26 |
+
* Model: all-MiniLM-L6-v2 (384 dimensions, fast, accurate)
|
| 27 |
+
*/
|
| 28 |
+
public async init(): Promise<void> {
|
| 29 |
+
if (this.extractor) return;
|
| 30 |
+
|
| 31 |
+
if (this.isInitializing && this.initPromise) {
|
| 32 |
+
return this.initPromise;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
this.isInitializing = true;
|
| 36 |
+
this.initPromise = (async () => {
|
| 37 |
+
try {
|
| 38 |
+
// Use a small, fast model: all-MiniLM-L6-v2
|
| 39 |
+
this.extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
|
| 40 |
+
console.log('🌑 [DARK MATTER] Neural Model Loaded (384 dimensions)');
|
| 41 |
+
} catch (error) {
|
| 42 |
+
console.error('🌑 [DARK MATTER] Failed to load model:', error);
|
| 43 |
+
throw error;
|
| 44 |
+
} finally {
|
| 45 |
+
this.isInitializing = false;
|
| 46 |
+
}
|
| 47 |
+
})();
|
| 48 |
+
|
| 49 |
+
return this.initPromise;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
/**
|
| 53 |
+
* Generate embedding vector for text
|
| 54 |
+
* @param text Input text to embed
|
| 55 |
+
* @returns 384-dimensional vector
|
| 56 |
+
*/
|
| 57 |
+
public async embedText(text: string): Promise<number[]> {
|
| 58 |
+
await this.init();
|
| 59 |
+
|
| 60 |
+
// Clean text
|
| 61 |
+
const cleanText = text.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
|
| 62 |
+
if (!cleanText) return new Array(384).fill(0);
|
| 63 |
+
|
| 64 |
+
try {
|
| 65 |
+
// Generate embedding
|
| 66 |
+
const output = await this.extractor!(cleanText, {
|
| 67 |
+
pooling: 'mean',
|
| 68 |
+
normalize: true
|
| 69 |
+
});
|
| 70 |
+
|
| 71 |
+
// Convert Float32Array to regular array for Neo4j/storage
|
| 72 |
+
return Array.from(output.data as Float32Array);
|
| 73 |
+
} catch (error) {
|
| 74 |
+
console.error('🌑 [DARK MATTER] Embedding failed:', error);
|
| 75 |
+
return new Array(384).fill(0);
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
/**
|
| 80 |
+
* Compute cosine similarity between two vectors
|
| 81 |
+
*/
|
| 82 |
+
public cosineSimilarity(a: number[], b: number[]): number {
|
| 83 |
+
if (a.length !== b.length) return 0;
|
| 84 |
+
|
| 85 |
+
let dotProduct = 0;
|
| 86 |
+
let normA = 0;
|
| 87 |
+
let normB = 0;
|
| 88 |
+
|
| 89 |
+
for (let i = 0; i < a.length; i++) {
|
| 90 |
+
dotProduct += a[i] * b[i];
|
| 91 |
+
normA += a[i] * a[i];
|
| 92 |
+
normB += b[i] * b[i];
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
/**
|
| 99 |
+
* Find most similar items from a collection
|
| 100 |
+
*/
|
| 101 |
+
public async findSimilar(
|
| 102 |
+
queryText: string,
|
| 103 |
+
items: { id: string; embedding: number[] }[],
|
| 104 |
+
topK: number = 5
|
| 105 |
+
): Promise<{ id: string; similarity: number }[]> {
|
| 106 |
+
const queryEmbedding = await this.embedText(queryText);
|
| 107 |
+
|
| 108 |
+
const scored = items.map(item => ({
|
| 109 |
+
id: item.id,
|
| 110 |
+
similarity: this.cosineSimilarity(queryEmbedding, item.embedding)
|
| 111 |
+
}));
|
| 112 |
+
|
| 113 |
+
return scored
|
| 114 |
+
.sort((a, b) => b.similarity - a.similarity)
|
| 115 |
+
.slice(0, topK);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
/**
|
| 119 |
+
* Batch embed multiple texts
|
| 120 |
+
*/
|
| 121 |
+
public async embedBatch(texts: string[]): Promise<number[][]> {
|
| 122 |
+
return Promise.all(texts.map(t => this.embedText(t)));
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
export const vectorService = VectorService.getInstance();
|
apps/backend/src/services/colonizer-service.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
| 1 |
import fs from 'fs/promises';
|
| 2 |
import path from 'path';
|
| 3 |
-
import { fileURLToPath } from 'url';
|
| 4 |
import { getLlmService, ChatMessage } from './llm/llmService.js';
|
| 5 |
import { CODEX_VERSION } from '../config/codex.js';
|
| 6 |
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
/**
|
| 10 |
* THE COLONIZER SERVICE
|
|
|
|
| 1 |
import fs from 'fs/promises';
|
| 2 |
import path from 'path';
|
|
|
|
| 3 |
import { getLlmService, ChatMessage } from './llm/llmService.js';
|
| 4 |
import { CODEX_VERSION } from '../config/codex.js';
|
| 5 |
|
| 6 |
+
// ESM/CJS compatible __dirname
|
| 7 |
+
const __dirname = typeof import.meta !== 'undefined' && import.meta.url
|
| 8 |
+
? path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1'))
|
| 9 |
+
: process.cwd();
|
| 10 |
|
| 11 |
/**
|
| 12 |
* THE COLONIZER SERVICE
|
apps/backend/src/services/embeddings/LocalGPUEmbeddings.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
| 1 |
import { spawn, ChildProcess } from 'child_process';
|
| 2 |
import path from 'path';
|
| 3 |
-
import { fileURLToPath } from 'url';
|
| 4 |
import { EmbeddingProvider } from './EmbeddingService.js';
|
| 5 |
import { logger } from '../../utils/logger.js';
|
| 6 |
|
| 7 |
-
|
| 8 |
-
const __dirname =
|
|
|
|
|
|
|
| 9 |
|
| 10 |
export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
| 11 |
name = 'local-gpu';
|
|
|
|
| 1 |
import { spawn, ChildProcess } from 'child_process';
|
| 2 |
import path from 'path';
|
|
|
|
| 3 |
import { EmbeddingProvider } from './EmbeddingService.js';
|
| 4 |
import { logger } from '../../utils/logger.js';
|
| 5 |
|
| 6 |
+
// ESM/CJS compatible __dirname
|
| 7 |
+
const __dirname = typeof import.meta !== 'undefined' && import.meta.url
|
| 8 |
+
? path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1'))
|
| 9 |
+
: __dirname || process.cwd();
|
| 10 |
|
| 11 |
export class LocalGPUEmbeddingsProvider implements EmbeddingProvider {
|
| 12 |
name = 'local-gpu';
|
monitor_runtime.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
WidgeTDC Cortex - Runtime Log Monitor
|
| 3 |
+
Monitors HF Space logs in real-time to verify system health
|
| 4 |
+
"""
|
| 5 |
+
import time
|
| 6 |
+
import requests
|
| 7 |
+
from huggingface_hub import HfApi
|
| 8 |
+
|
| 9 |
+
# Configuration
|
| 10 |
+
SPACE_NAME = "widgetdc-cortex"
|
| 11 |
+
USERNAME = "Kraft102"
|
| 12 |
+
REPO_ID = f"{USERNAME}/{SPACE_NAME}"
|
| 13 |
+
SPACE_URL = f"https://{USERNAME.lower()}-{SPACE_NAME}.hf.space"
|
| 14 |
+
|
| 15 |
+
print("=" * 70)
|
| 16 |
+
print(" WIDGETTDC CORTEX - RUNTIME LOG MONITOR")
|
| 17 |
+
print("=" * 70)
|
| 18 |
+
print()
|
| 19 |
+
|
| 20 |
+
# Initialize API
|
| 21 |
+
api = HfApi()
|
| 22 |
+
|
| 23 |
+
def check_build_status():
|
| 24 |
+
"""Check if Space is running"""
|
| 25 |
+
try:
|
| 26 |
+
runtime = api.get_space_runtime(repo_id=REPO_ID)
|
| 27 |
+
return runtime.stage, runtime.hardware
|
| 28 |
+
except Exception as e:
|
| 29 |
+
return "UNKNOWN", None
|
| 30 |
+
|
| 31 |
+
def test_health_endpoint():
|
| 32 |
+
"""Test /health endpoint"""
|
| 33 |
+
try:
|
| 34 |
+
response = requests.get(f"{SPACE_URL}/health", timeout=10)
|
| 35 |
+
return response.status_code == 200, response.json() if response.status_code == 200 else None
|
| 36 |
+
except Exception as e:
|
| 37 |
+
return False, str(e)
|
| 38 |
+
|
| 39 |
+
def monitor_runtime():
|
| 40 |
+
"""Monitor runtime status and logs"""
|
| 41 |
+
|
| 42 |
+
print("[1/4] Checking Space status...")
|
| 43 |
+
stage, hardware = check_build_status()
|
| 44 |
+
print(f" Stage: {stage}")
|
| 45 |
+
print(f" Hardware: {hardware}")
|
| 46 |
+
print()
|
| 47 |
+
|
| 48 |
+
if stage == "RUNNING":
|
| 49 |
+
print("✅ Space is RUNNING!")
|
| 50 |
+
print()
|
| 51 |
+
|
| 52 |
+
print("[2/4] Testing health endpoint...")
|
| 53 |
+
healthy, data = test_health_endpoint()
|
| 54 |
+
|
| 55 |
+
if healthy:
|
| 56 |
+
print(f"✅ Health check PASSED!")
|
| 57 |
+
print(f" Response: {data}")
|
| 58 |
+
else:
|
| 59 |
+
print(f"❌ Health check FAILED!")
|
| 60 |
+
print(f" Error: {data}")
|
| 61 |
+
print()
|
| 62 |
+
|
| 63 |
+
print("[3/4] Testing API endpoints...")
|
| 64 |
+
# Test MCP route
|
| 65 |
+
try:
|
| 66 |
+
response = requests.post(
|
| 67 |
+
f"{SPACE_URL}/api/mcp/route",
|
| 68 |
+
json={"method": "initialize", "params": {}},
|
| 69 |
+
timeout=10
|
| 70 |
+
)
|
| 71 |
+
print(f" MCP Route: {response.status_code}")
|
| 72 |
+
except Exception as e:
|
| 73 |
+
print(f" MCP Route: ERROR - {e}")
|
| 74 |
+
print()
|
| 75 |
+
|
| 76 |
+
print("[4/4] System Health Summary:")
|
| 77 |
+
print(" 🟢 Space: RUNNING")
|
| 78 |
+
print(f" {'🟢' if healthy else '🔴'} Health: {'OK' if healthy else 'FAILED'}")
|
| 79 |
+
print(f" 🔵 URL: {SPACE_URL}")
|
| 80 |
+
|
| 81 |
+
elif stage == "BUILDING":
|
| 82 |
+
print("⏳ Space is still BUILDING...")
|
| 83 |
+
print(" Run this script again in 1-2 minutes")
|
| 84 |
+
|
| 85 |
+
elif stage == "BUILD_ERROR":
|
| 86 |
+
print("❌ Space has BUILD_ERROR!")
|
| 87 |
+
print(" Check logs at:")
|
| 88 |
+
print(f" https://huggingface.co/spaces/{REPO_ID}/logs")
|
| 89 |
+
|
| 90 |
+
else:
|
| 91 |
+
print(f"⚠️ Unknown status: {stage}")
|
| 92 |
+
|
| 93 |
+
if __name__ == "__main__":
|
| 94 |
+
monitor_runtime()
|
| 95 |
+
print()
|
| 96 |
+
print("=" * 70)
|
| 97 |
+
print(" MONITORING COMPLETE")
|
| 98 |
+
print("=" * 70)
|
package-lock.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
| 5 |
"private": true,
|
| 6 |
"workspaces": [
|
| 7 |
"apps/backend",
|
|
|
|
| 8 |
"packages/*"
|
| 9 |
],
|
| 10 |
"scripts": {
|
|
@@ -58,6 +59,7 @@
|
|
| 58 |
"@vitejs/plugin-react": "^5.1.1",
|
| 59 |
"@vitest/ui": "^4.0.8",
|
| 60 |
"concurrently": "^8.2.2",
|
|
|
|
| 61 |
"eslint": "^9.39.1",
|
| 62 |
"eslint-config-prettier": "^10.1.8",
|
| 63 |
"eslint-plugin-react": "^7.37.5",
|
|
|
|
| 5 |
"private": true,
|
| 6 |
"workspaces": [
|
| 7 |
"apps/backend",
|
| 8 |
+
"apps/matrix-frontend",
|
| 9 |
"packages/*"
|
| 10 |
],
|
| 11 |
"scripts": {
|
|
|
|
| 59 |
"@vitejs/plugin-react": "^5.1.1",
|
| 60 |
"@vitest/ui": "^4.0.8",
|
| 61 |
"concurrently": "^8.2.2",
|
| 62 |
+
"esbuild": "^0.24.0",
|
| 63 |
"eslint": "^9.39.1",
|
| 64 |
"eslint-config-prettier": "^10.1.8",
|
| 65 |
"eslint-plugin-react": "^7.37.5",
|