diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/dashboard/public/jarvis.html b/dashboard/public/jarvis.html index d12e2ed..59e2500 100644 --- a/dashboard/public/jarvis.html +++ b/dashboard/public/jarvis.html @@ -1058,14 +1058,8 @@ document.addEventListener('DOMContentLoaded', () => { .then(r => r.json()) .then(data => { D = data; init(); connectSSE(); }) .catch(() => { - // API not ready yet — use inline data as fallback if available - if (D && D.meta) { init(); } - else { document.getElementById('bootLines').innerHTML = '
Waiting for first sweep...
'; } - // Retry after a delay - setTimeout(() => { - fetch('/api/data').then(r => r.json()).then(data => { D = data; init(); connectSSE(); }).catch(() => {}); - }, 10000); - connectSSE(); + // Should not reach here — server routes to loading.html when no data + if (D && D.meta) { init(); connectSSE(); } }); } else if (D && D.meta) { // File mode: use inline data diff --git a/dashboard/public/loading.html b/dashboard/public/loading.html new file mode 100644 index 0000000..196f6ab --- /dev/null +++ b/dashboard/public/loading.html @@ -0,0 +1,154 @@ + + + + + +CRUCIX — Initializing + + + + + +
+
+ CX +
+ +
+ +
+ + COLLECTING DATA... +
+ +
+ +
+
+
+ + + + diff --git a/package.json b/package.json index 2ac3593..5d9085e 100644 --- a/package.json +++ b/package.json @@ -4,18 +4,22 @@ "description": "Local intelligence engine — 26 OSINT sources, live dashboard, auto-refresh, optional LLM layer.", "type": "module", "scripts": { + "start": "node server.mjs", "dev": "node --trace-warnings server.mjs", "sweep": "node apis/briefing.mjs", "inject": "node dashboard/inject.mjs", "brief": "node apis/briefing.mjs", "brief:save": "node apis/save-briefing.mjs", - "diag": "node diag.mjs" + "diag": "node diag.mjs", + "clean": "node scripts/clean.mjs", + "fresh-start": "npm run clean && npm start" }, "keywords": ["osint", "intelligence", "dashboard", "geopolitical"], "author": "Crucix", "license": "ISC", "engines": { - "node": ">=22" + "node": ">=22", + "npm": ">=10" }, "dependencies": { "express": "^5.1.0" diff --git a/scripts/clean.mjs b/scripts/clean.mjs new file mode 100644 index 0000000..1d87f42 --- /dev/null +++ b/scripts/clean.mjs @@ -0,0 +1,18 @@ +import { rm, access } from 'fs/promises'; +import { join } from 'path'; + +const targets = [ + 'runs/latest.json', + 'runs/memory', +]; + +for (const target of targets) { + const full = join(process.cwd(), target); + try { + await access(full); + await rm(full, { recursive: true }); + console.log(`removed: ${target}`); + } catch { + // not found — skip silently + } +} diff --git a/server.mjs b/server.mjs index f6173f2..86e26b5 100644 --- a/server.mjs +++ b/server.mjs @@ -29,6 +29,7 @@ for (const dir of [RUNS_DIR, MEMORY_DIR, join(MEMORY_DIR, 'cold')]) { // === State === let currentData = null; // Current synthesized dashboard data let lastSweepTime = null; // Timestamp of last sweep +let sweepStartedAt = null; // Timestamp when current/last sweep started let sweepInProgress = false; const startTime = Date.now(); const sseClients = new Set(); @@ -230,9 +231,13 @@ if (discordAlerter.isConfigured) { const app = express(); app.use(express.static(join(ROOT, 'dashboard/public'))); -// Serve jarvis.html as the root page +// Serve loading page until first sweep completes, then the dashboard app.get('/', (req, res) => { - res.sendFile(join(ROOT, 'dashboard/public/jarvis.html')); + if (!currentData) { + res.sendFile(join(ROOT, 'dashboard/public/loading.html')); + } else { + res.sendFile(join(ROOT, 'dashboard/public/jarvis.html')); + } }); // API: current data @@ -251,6 +256,7 @@ app.get('/api/health', (req, res) => { ? new Date(new Date(lastSweepTime).getTime() + config.refreshIntervalMinutes * 60000).toISOString() : null, sweepInProgress, + sweepStartedAt, sourcesOk: currentData?.meta?.sourcesOk || 0, sourcesFailed: currentData?.meta?.sourcesFailed || 0, llmEnabled: !!config.llm.provider, @@ -288,7 +294,8 @@ async function runSweepCycle() { } sweepInProgress = true; - broadcast({ type: 'sweep_start', timestamp: new Date().toISOString() }); + sweepStartedAt = new Date().toISOString(); + broadcast({ type: 'sweep_start', timestamp: sweepStartedAt }); console.log(`\n${'='.repeat(60)}`); console.log(`[Crucix] Starting sweep at ${new Date().toLocaleTimeString()}`); console.log(`${'='.repeat(60)}`);