Files
boc/SIL/auto-updater.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

396 lines
12 KiB
JavaScript

#!/usr/bin/env node
// ═══════════════════════════════════════════════════════════════════════════
// SIL Auto-Updater
// Uppdaterar kunskapsgrafen från verifierbara källor
// ═══════════════════════════════════════════════════════════════════════════
import { execSync } from 'child_process';
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { globSync } from 'glob';
class AutoUpdater {
constructor(graphPath) {
this.graphPath = graphPath;
this.graph = JSON.parse(readFileSync(graphPath, 'utf8'));
this.updates = [];
// Källklassificering
this.sourceRules = {
'AST': { trust: 0.99, autoUpdate: true },
'DB_MIGRATION': { trust: 0.97, autoUpdate: true },
'OPENAPI': { trust: 0.96, autoUpdate: true },
'K8S_MANIFEST': { trust: 0.95, autoUpdate: true },
'TERRAFORM': { trust: 0.95, autoUpdate: true },
'CI_CD': { trust: 0.94, autoUpdate: true },
'README': { trust: 0.50, autoUpdate: false },
'CODE_COMMENT': { trust: 0.40, autoUpdate: false },
'AI_INFERENCE': { trust: 0.60, autoUpdate: false }
};
}
// ── Git-baserad uppdatering ─────────────────────────────────────────────
updateFromGit(repoPath) {
console.log(`🔄 Uppdaterar från git: ${repoPath}`);
try {
// Hämta senaste commits
const commits = execSync(
'git log --since="7 days ago" --format="%H|%an|%ae|%at|%s"',
{ cwd: repoPath, encoding: 'utf8' }
).trim().split('\n').filter(c => c);
for (const commit of commits) {
const [hash, author, email, timestamp, message] = commit.split('|');
// Hämta ändrade filer
const files = execSync(
`git diff-tree --no-commit-id --name-only -r ${hash}`,
{ cwd: repoPath, encoding: 'utf8' }
).trim().split('\n').filter(f => f);
// Analysera varje fil
for (const file of files) {
const update = this.analyzeFile(file, hash, message, repoPath);
if (update) {
this.updates.push(update);
}
}
}
console.log(`${this.updates.length} uppdateringar identifierade`);
} catch (e) {
console.error(` ❌ Git-fel: ${e.message}`);
}
}
analyzeFile(file, commitHash, commitMessage, repoPath) {
// API-endpoints (hög confidence)
if (file.match(/\.(ts|js|tsx|jsx|rs)$/)) {
const endpoints = this.extractEndpoints(file, repoPath);
if (endpoints.length > 0) {
return {
type: 'endpoint',
source: 'AST',
confidence: 0.99,
file,
commit: commitHash,
commitMessage,
data: endpoints,
autoUpdate: true
};
}
}
// Databasmigreringar (hög confidence)
if (file.includes('migration') || file.includes('schema')) {
const tables = this.extractTables(file, repoPath);
if (tables.length > 0) {
return {
type: 'table',
source: 'DB_MIGRATION',
confidence: 0.97,
file,
commit: commitHash,
commitMessage,
data: tables,
autoUpdate: true
};
}
}
// OpenAPI-specifikationer (hög confidence)
if (file.match(/openapi|swagger|\.yaml$|\.yml$/)) {
return {
type: 'api_contract',
source: 'OPENAPI',
confidence: 0.96,
file,
commit: commitHash,
commitMessage,
autoUpdate: true
};
}
// Kubernetes-manifest (hög confidence)
if (file.match(/\.yaml$|\.yml$/) && file.includes('k8s')) {
return {
type: 'k8s_resource',
source: 'K8S_MANIFEST',
confidence: 0.95,
file,
commit: commitHash,
commitMessage,
autoUpdate: true
};
}
// Terraform (hög confidence)
if (file.endsWith('.tf')) {
return {
type: 'infrastructure',
source: 'TERRAFORM',
confidence: 0.95,
file,
commit: commitHash,
commitMessage,
autoUpdate: true
};
}
// README (låg confidence, manuell review)
if (file.includes('README')) {
return {
type: 'documentation',
source: 'README',
confidence: 0.50,
file,
commit: commitHash,
commitMessage,
autoUpdate: false
};
}
return null;
}
extractEndpoints(file, repoPath) {
const endpoints = [];
const fullPath = `${repoPath}/${file}`;
if (!existsSync(fullPath)) return endpoints;
const content = readFileSync(fullPath, 'utf8');
// Express/Fastify/Axum routes
const matches = content.matchAll(/\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)/g);
for (const match of matches) {
endpoints.push({
method: match[1].toUpperCase(),
path: match[2]
});
}
return endpoints;
}
extractTables(file, repoPath) {
const tables = [];
const fullPath = `${repoPath}/${file}`;
if (!existsSync(fullPath)) return tables;
const content = readFileSync(fullPath, 'utf8');
// CREATE TABLE
const matches = content.matchAll(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"']?(\w+)[`"']?/gi);
for (const match of matches) {
tables.push({
name: match[1],
operation: 'create'
});
}
// ALTER TABLE
const alterMatches = content.matchAll(/ALTER\s+TABLE\s+[`"']?(\w+)[`"']?/gi);
for (const match of alterMatches) {
tables.push({
name: match[1],
operation: 'alter'
});
}
return tables;
}
// ── Tillämpa uppdateringar ──────────────────────────────────────────────
applyUpdates() {
console.log('\n📝 Tillämpar uppdateringar\n');
let autoUpdated = 0;
let manualReview = 0;
for (const update of this.updates) {
const rule = this.sourceRules[update.source];
if (rule && rule.autoUpdate && update.autoUpdate) {
// Auto-uppdatera grafen
this.applyToGraph(update);
autoUpdated++;
console.log(` ✅ AUTO: ${update.type} från ${update.source} (${update.file})`);
} else {
// Kräver manuell review
manualReview++;
console.log(` ⚠️ MANUELL: ${update.type} från ${update.source} (${update.file})`);
}
}
console.log(`\n Sammanfattning:`);
console.log(` Auto-uppdaterade: ${autoUpdated}`);
console.log(` Kräver review: ${manualReview}`);
}
applyToGraph(update) {
switch (update.type) {
case 'endpoint':
// Lägg till nya endpoints till befintlig service
for (const endpoint of update.data) {
const serviceNode = this.findServiceForFile(update.file);
if (serviceNode) {
if (!serviceNode.endpoints) serviceNode.endpoints = [];
if (!serviceNode.endpoints.includes(`${endpoint.method} ${endpoint.path}`)) {
serviceNode.endpoints.push(`${endpoint.method} ${endpoint.path}`);
// Lägg till metadata om "Varför?"
if (!serviceNode.endpointHistory) serviceNode.endpointHistory = [];
serviceNode.endpointHistory.push({
endpoint: `${endpoint.method} ${endpoint.path}`,
added: new Date().toISOString(),
commit: update.commit,
commitMessage: update.commitMessage,
confidence: update.confidence,
source: update.source
});
}
}
}
break;
case 'table':
// Uppdatera databasnoden
const dbNode = this.graph.nodes.find(n => n.type === 'database');
if (dbNode) {
for (const table of update.data) {
if (!dbNode.tables.includes(table.name)) {
dbNode.tables.push(table.name);
// Lägg till "Varför?"
if (!dbNode.tableHistory) dbNode.tableHistory = [];
dbNode.tableHistory.push({
table: table.name,
operation: table.operation,
added: new Date().toISOString(),
commit: update.commit,
commitMessage: update.commitMessage,
confidence: update.confidence,
source: update.source
});
}
}
}
break;
}
}
findServiceForFile(file) {
// Mappa fil till service baserat på sökväg
if (file.includes('auth')) return this.graph.nodes.find(n => n.id === 'auth');
if (file.includes('wallet')) return this.graph.nodes.find(n => n.id === 'wallet');
if (file.includes('mission')) return this.graph.nodes.find(n => n.id === 'mission');
if (file.includes('kyc')) return this.graph.nodes.find(n => n.id === 'kyc');
return null;
}
// ── Spara graf ──────────────────────────────────────────────────────────
saveGraph() {
this.graph.last_updated = new Date().toISOString();
this.graph.update_source = 'auto_updater';
writeFileSync(this.graphPath, JSON.stringify(this.graph, null, 2));
console.log(`\n💾 Graf sparad till ${this.graphPath}`);
}
// ── Reality Check ───────────────────────────────────────────────────────
runRealityCheck() {
console.log('\n🔍 Reality Check\n');
const issues = [];
// Kolla att alla services har endpoints
const services = this.graph.nodes.filter(n => n.type === 'service');
for (const service of services) {
if (!service.endpoints || service.endpoints.length === 0) {
issues.push({
type: 'missing_endpoints',
component: service.label,
severity: 'medium',
message: 'Service har inga dokumenterade endpoints'
});
}
}
// Kolla att alla tabeller har en service
const db = this.graph.nodes.find(n => n.type === 'database');
if (db && db.tables) {
for (const table of db.tables) {
const usedBy = this.graph.edges.filter(e =>
e.to === table || (e.relation && e.relation.includes(table))
);
if (usedBy.length === 0) {
issues.push({
type: 'orphan_table',
component: table,
severity: 'low',
message: 'Tabell verkar inte användas av någon service'
});
}
}
}
// Kolla konfidensnivåer
const lowConfidence = this.graph.edges.filter(e =>
e.confidence && e.confidence < 0.70
);
if (lowConfidence.length > 0) {
issues.push({
type: 'low_confidence',
component: `${lowConfidence.length} relationer`,
severity: 'info',
message: 'Relationer med låg confidence behöver verifieras'
});
}
if (issues.length === 0) {
console.log(' ✅ Inga avvikelser hittade');
} else {
for (const issue of issues) {
const icon = issue.severity === 'high' ? '🔴' : issue.severity === 'medium' ? '🟡' : '🟢';
console.log(` ${icon} ${issue.type}: ${issue.message}`);
}
}
return issues;
}
}
// ── Main ──────────────────────────────────────────────────────────────────
const updater = new AutoUpdater('/home/bernt/.openclaw/workspace/SYSTEM_GRAPH.json');
console.log('🚀 SIL Auto-Updater\n');
// Uppdatera från quiXzoom
if (existsSync('/home/bernt/repos/quixzoom.com')) {
updater.updateFromGit('/home/bernt/repos/quixzoom.com');
}
// Uppdatera från ouroboros-identity
if (existsSync('/home/bernt/rust/ouroboros-identity')) {
updater.updateFromGit('/home/bernt/rust/ouroboros-identity');
}
// Tillämpa uppdateringar
updater.applyUpdates();
// Kör reality check
updater.runRealityCheck();
// Spara
updater.saveGraph();
console.log('\n✅ Auto-updater klar');