Files
boc/SIL/observer.mjs
T
Bernt 05ed037fe8 pilot.landvex.com: HTTPS + Full Stack Verified
- DNS: pilot.landvex.com -> 16.170.83.169
- TLS: Let's Encrypt certificate (expires 2026-09-30)
- Nginx: reverse proxy with SSL termination
- API: https://pilot.landvex.com/api/v1/missions
- UI: https://pilot.landvex.com/
- Upload: POST /api/v1/missions/import (multipart/form-data)

Verified:
 https://pilot.landvex.com/health
 https://pilot.landvex.com/version
 https://pilot.landvex.com/api/v1/missions (list)
 https://pilot.landvex.com/api/v1/missions/:id (get)
 POST /api/v1/missions/import (video upload)
 UI loads with title 'LandveX Intelligence Lab'

Next: Pilot 001 — Break the system!
2026-07-02 17:34:19 +00:00

299 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// ═══════════════════════════════════════════════════════════════════════════
// SIL Observer - Kontinuerlig systemobservation
// Skannar kodbas, databaser, och infrastruktur för att uppdatera grafen
// ═══════════════════════════════════════════════════════════════════════════
import { execSync } from 'child_process';
import { readFileSync, existsSync } from 'fs';
import { globSync } from 'glob';
class SILObserver {
constructor(graphPath) {
this.graph = JSON.parse(readFileSync(graphPath, 'utf8'));
this.changes = [];
}
// ── Git-observation ─────────────────────────────────────────────────────
observeGit(repoPath) {
console.log(`🔍 Skannar git-repo: ${repoPath}`);
try {
// Senaste commit
const lastCommit = execSync('git log -1 --format="%H|%an|%ae|%at|%s"', {
cwd: repoPath,
encoding: 'utf8'
}).trim();
const [hash, author, email, timestamp, message] = lastCommit.split('|');
// Ändrade filer
const changedFiles = execSync('git diff-tree --no-commit-id --name-only -r HEAD', {
cwd: repoPath,
encoding: 'utf8'
}).trim().split('\n').filter(f => f);
// Nya endpoints från ändrade filer
const newEndpoints = this.extractEndpointsFromFiles(changedFiles, repoPath);
// Nya tabeller från migrationer
const newTables = this.extractTablesFromMigrations(changedFiles, repoPath);
this.changes.push({
type: 'git_commit',
source: 'git',
confidence: 0.99,
data: {
hash,
author,
timestamp: new Date(parseInt(timestamp) * 1000).toISOString(),
message,
changedFiles,
newEndpoints,
newTables
}
});
console.log(` ✅ Hittade ${changedFiles.length} ändrade filer`);
console.log(`${newEndpoints.length} nya endpoints`);
console.log(`${newTables.length} nya tabeller`);
} catch (e) {
console.error(` ❌ Git-fel: ${e.message}`);
}
}
extractEndpointsFromFiles(files, repoPath) {
const endpoints = [];
for (const file of files) {
if (!file.match(/\.(ts|js|tsx|jsx|rs)$/)) continue;
const fullPath = `${repoPath}/${file}`;
if (!existsSync(fullPath)) continue;
const content = readFileSync(fullPath, 'utf8');
// Hitta API-endpoints (Express/Fastify/Axum)
const endpointMatches = content.matchAll(/\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)/g);
for (const match of endpointMatches) {
endpoints.push({
method: match[1].toUpperCase(),
path: match[2],
file,
confidence: 0.98
});
}
// Hitta fetch/anrop till externa API:er
const apiMatches = content.matchAll(/fetch\s*\(\s*['"`]([^'"`]+)/g);
for (const match of apiMatches) {
if (match[1].includes('api.quixzoom.com') || match[1].includes('http')) {
endpoints.push({
type: 'external_call',
url: match[1],
file,
confidence: 0.95
});
}
}
}
return endpoints;
}
extractTablesFromMigrations(files, repoPath) {
const tables = [];
for (const file of files) {
if (!file.includes('migration') && !file.includes('schema')) continue;
const fullPath = `${repoPath}/${file}`;
if (!existsSync(fullPath)) continue;
const content = readFileSync(fullPath, 'utf8');
// Hitta CREATE TABLE
const tableMatches = content.matchAll(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"']?(\w+)[`"']?/gi);
for (const match of tableMatches) {
tables.push({
name: match[1],
operation: 'create',
file,
confidence: 0.97
});
}
// Hitta ALTER TABLE
const alterMatches = content.matchAll(/ALTER\s+TABLE\s+[`"']?(\w+)[`"']?/gi);
for (const match of alterMatches) {
tables.push({
name: match[1],
operation: 'alter',
file,
confidence: 0.97
});
}
}
return tables;
}
// ── AST-observation ─────────────────────────────────────────────────────
observeAST(repoPath) {
console.log(`🔍 Skannar AST: ${repoPath}`);
// Hitta alla TypeScript/JavaScript-filer
const files = globSync('**/*.{ts,tsx,js,jsx}', { cwd: repoPath });
const imports = [];
const functionCalls = [];
for (const file of files.slice(0, 100)) { // Begränsa för prestanda
const fullPath = `${repoPath}/${file}`;
if (!existsSync(fullPath)) continue;
const content = readFileSync(fullPath, 'utf8');
// Hitta imports
const importMatches = content.matchAll(/import\s+.*?\s+from\s+['"`]([^'"`]+)['"`]/g);
for (const match of importMatches) {
imports.push({
source: match[1],
file,
confidence: 0.99
});
}
// Hitta funktionsanrop till externa tjänster
const callMatches = content.matchAll(/(\w+)\s*\(\s*['"`]([^'"`]*(?:api|http|stripe|ses)[^'"`]*)['"`]/gi);
for (const match of callMatches) {
functionCalls.push({
function: match[1],
argument: match[2],
file,
confidence: 0.90
});
}
}
this.changes.push({
type: 'ast_analysis',
source: 'ast',
confidence: 0.98,
data: {
filesScanned: files.length,
imports: imports.slice(0, 50),
functionCalls: functionCalls.slice(0, 50)
}
});
console.log(` ✅ Skannade ${files.length} filer`);
console.log(` ✅ Hittade ${imports.length} imports`);
console.log(` ✅ Hittade ${functionCalls.length} funktionsanrop`);
}
// ── Miljövariabler ──────────────────────────────────────────────────────
observeEnvironment() {
console.log(`🔍 Skannar miljövariabler`);
const envVars = {};
// Hitta .env-filer
const envFiles = globSync('**/.env*', { cwd: '/home/bernt' });
for (const file of envFiles) {
try {
const content = readFileSync(`/home/bernt/${file}`, 'utf8');
const vars = content.match(/^\w+=[^\n]+/gm) || [];
envVars[file] = vars.map(v => v.split('=')[0]);
} catch (e) {
// Ignorera filer vi inte kan läsa
}
}
this.changes.push({
type: 'environment',
source: 'env_files',
confidence: 0.85,
data: {
files: envFiles,
variables: envVars
}
});
console.log(` ✅ Hittade ${envFiles.length} .env-filer`);
}
// ── Sammanställning ─────────────────────────────────────────────────────
generateReport() {
console.log(`\n📊 OBSERVER-RAPPORT\n`);
console.log(`Händelser: ${this.changes.length}`);
console.log(`Källor: ${[...new Set(this.changes.map(c => c.source))].join(', ')}`);
console.log(`Genomsnittlig confidence: ${(this.changes.reduce((a, c) => a + c.confidence, 0) / this.changes.length).toFixed(2)}`);
// Föreslagna graf-uppdateringar
console.log(`\n📝 Föreslagna uppdateringar av kunskapsgrafen:\n`);
for (const change of this.changes) {
switch (change.type) {
case 'git_commit':
if (change.data.newEndpoints.length > 0) {
console.log(` Nya endpoints:`);
change.data.newEndpoints.forEach(e => {
console.log(` ${e.method} ${e.path} (${e.confidence})`);
});
}
if (change.data.newTables.length > 0) {
console.log(` Nya/ändrade tabeller:`);
change.data.newTables.forEach(t => {
console.log(` ${t.operation.toUpperCase()} ${t.name} (${t.confidence})`);
});
}
break;
case 'ast_analysis':
console.log(` 🔍 AST-analys visar ${change.data.imports.length} imports`);
break;
case 'environment':
console.log(` 🔧 Miljövariabler från ${Object.keys(change.data.variables).length} filer`);
break;
}
}
return this.changes;
}
}
// ── Main ──────────────────────────────────────────────────────────────────
const observer = new SILObserver('/home/bernt/.openclaw/workspace/SYSTEM_GRAPH.json');
console.log('🚀 SIL Observer startar...\n');
// Observera quiXzoom frontend
if (existsSync('/home/bernt/repos/quixzoom.com')) {
observer.observeGit('/home/bernt/repos/quixzoom.com');
observer.observeAST('/home/bernt/repos/quixzoom.com');
}
// Observera ouroboros-identity
if (existsSync('/home/bernt/rust/ouroboros-identity')) {
observer.observeGit('/home/bernt/rust/ouroboros-identity');
}
// Miljövariabler
observer.observeEnvironment();
// Generera rapport
const changes = observer.generateReport();
console.log(`\n✅ Observer klar. ${changes.length} händelser identifierade.`);
console.log(` Kör graph-engine.mjs för att se påverkan.`);