'use strict'; 'require view'; 'require dom'; 'require poll'; 'require ui'; 'require ai-insights.api as api'; 'require secubox/kiss-theme'; /** * AI Insights Dashboard - v1.0.0 * Unified view across all SecuBox AI agents */ return view.extend({ cveCache: [], load: function() { var self = this; var link = document.createElement('link'); link.rel = 'stylesheet'; link.href = L.resource('ai-insights/dashboard.css'); document.head.appendChild(link); return Promise.all([ api.getOverview().catch(function() { return {}; }), api.getCVEFeed(10).catch(function() { return { cves: [] }; }) ]).then(function(results) { self.cveCache = (results[1] || {}).cves || []; return results[0]; }); }, render: function(data) { var self = this; var s = data.status || {}; var p = data.posture || {}; var alerts = data.alerts || []; var agents = s.agents || {}; var view = E('div', { 'class': 'ai-view' }, [ // Header E('div', { 'class': 'ai-header' }, [ E('div', { 'class': 'ai-title' }, 'AI Security Insights'), E('div', { 'class': 'ai-status' }, [ E('span', { 'class': 'ai-dot ' + (s.localai === 'online' ? 'online' : 'offline') }), 'LocalAI: ' + (s.localai || 'offline') ]) ]), // Posture Score (hero) E('div', { 'class': 'ai-posture-card ' + api.getPostureColor(p.score || 0), 'id': 'ai-posture' }, [ E('div', { 'class': 'ai-posture-score' }, [ E('div', { 'class': 'ai-score-value' }, String(p.score || 0)), E('div', { 'class': 'ai-score-label' }, 'Security Score') ]), E('div', { 'class': 'ai-posture-info' }, [ E('div', { 'class': 'ai-posture-label' }, api.getPostureLabel(p.score || 0)), E('div', { 'class': 'ai-posture-factors' }, p.factors || 'Calculating...') ]) ]), // Stats row E('div', { 'class': 'ai-stats', 'id': 'ai-stats' }, this.renderStats(s)), // Agents grid E('div', { 'class': 'ai-agents-grid', 'id': 'ai-agents' }, this.renderAgents(agents)), // Actions card E('div', { 'class': 'ai-card' }, [ E('div', { 'class': 'ai-card-header' }, 'Actions'), E('div', { 'class': 'ai-card-body' }, this.renderActions()) ]), // Alerts card E('div', { 'class': 'ai-card' }, [ E('div', { 'class': 'ai-card-header' }, [ 'Recent Activity', E('span', { 'class': 'ai-badge' }, String(alerts.length)) ]), E('div', { 'class': 'ai-card-body', 'id': 'ai-alerts' }, this.renderAlerts(alerts)) ]), // CVE Feed card E('div', { 'class': 'ai-card' }, [ E('div', { 'class': 'ai-card-header' }, [ '⚠️ CVE Feed (Recent)', E('span', { 'class': 'ai-badge warning' }, String(this.cveCache.length)) ]), E('div', { 'class': 'ai-card-body', 'id': 'ai-cves' }, this.renderCVEs(this.cveCache)) ]) ]); poll.add(L.bind(this.pollData, this), 15); return KissTheme.wrap([view], 'admin/secubox/ai/insights'); }, renderStats: function(s) { var agents = s.agents || {}; var online = Object.values(agents).filter(function(a) { return a.status === 'online'; }).length; var totalAlerts = Object.values(agents).reduce(function(sum, a) { return sum + (a.alerts || 0); }, 0); var statItems = [ { label: 'Agents Online', value: online + '/4', type: online === 4 ? 'success' : online > 0 ? 'warning' : 'danger' }, { label: 'LocalAI', value: s.localai === 'online' ? 'OK' : 'OFF', type: s.localai === 'online' ? 'success' : 'danger' }, { label: 'Pending', value: totalAlerts, type: totalAlerts > 10 ? 'danger' : totalAlerts > 0 ? 'warning' : 'success' }, { label: 'Memories', value: s.memories || 0, type: '' } ]; return statItems.map(function(st) { return E('div', { 'class': 'ai-stat ' + st.type }, [ E('div', { 'class': 'ai-stat-value' }, String(st.value)), E('div', { 'class': 'ai-stat-label' }, st.label) ]); }); }, renderAgents: function(agents) { var agentList = ['threat_analyst', 'dns_guard', 'network_anomaly', 'cve_triage']; return agentList.map(function(id) { var agent = agents[id] || {}; var isOnline = agent.status === 'online'; return E('div', { 'class': 'ai-agent-card ' + (isOnline ? 'online' : 'offline') }, [ E('div', { 'class': 'ai-agent-icon' }, api.getAgentIcon(id)), E('div', { 'class': 'ai-agent-info' }, [ E('div', { 'class': 'ai-agent-name' }, api.getAgentName(id)), E('div', { 'class': 'ai-agent-status' }, [ E('span', { 'class': 'ai-dot ' + (isOnline ? 'online' : 'offline') }), isOnline ? 'Running' : 'Stopped' ]) ]), E('div', { 'class': 'ai-agent-alerts' }, [ E('span', { 'class': 'ai-badge ' + (agent.alerts > 0 ? 'warning' : '') }, String(agent.alerts || 0)), E('span', {}, ' alerts') ]) ]); }); }, renderActions: function() { var self = this; return E('div', { 'class': 'ai-actions' }, [ E('button', { 'class': 'ai-btn ai-btn-primary', 'click': function() { self.runAllAgents(); } }, 'Run All Agents'), E('button', { 'class': 'ai-btn ai-btn-secondary', 'click': function() { self.getAIAnalysis(); } }, 'AI Analysis'), E('button', { 'class': 'ai-btn ai-btn-info', 'click': function() { self.showTimeline(); } }, 'View Timeline'), E('a', { 'class': 'ai-btn ai-btn-outline', 'href': L.url('admin/secubox/ai/localrecall') }, 'LocalRecall') ]); }, renderAlerts: function(alerts) { if (!alerts || !alerts.length) { return E('div', { 'class': 'ai-empty' }, 'No recent activity from AI agents'); } return E('div', { 'class': 'ai-alerts-list' }, alerts.slice(0, 15).map(function(alert) { var data = alert.data || {}; var source = alert.source || 'unknown'; var type = alert.type || 'alert'; return E('div', { 'class': 'ai-alert-item ' + type }, [ E('div', { 'class': 'ai-alert-icon' }, api.getAgentIcon(source)), E('div', { 'class': 'ai-alert-content' }, [ E('div', { 'class': 'ai-alert-source' }, api.getAgentName(source)), E('div', { 'class': 'ai-alert-message' }, data.message || data.type || type) ]), E('div', { 'class': 'ai-alert-time' }, api.formatRelativeTime(data.timestamp)) ]); })); }, renderCVEs: function(cves) { if (!cves || !cves.length) { return E('div', { 'class': 'ai-empty' }, 'Loading CVE feed...'); } return E('div', { 'class': 'ai-cve-list' }, cves.slice(0, 10).map(function(cve) { var score = cve.score || 0; var severity = score >= 9.0 ? 'critical' : score >= 7.0 ? 'high' : score >= 4.0 ? 'medium' : 'low'; var severityClass = score >= 9.0 ? 'danger' : score >= 7.0 ? 'warning' : score >= 4.0 ? 'caution' : 'success'; return E('div', { 'class': 'ai-cve-item' }, [ E('div', { 'class': 'ai-cve-score ' + severityClass }, [ E('span', { 'class': 'score-value' }, score.toFixed(1)), E('span', { 'class': 'score-label' }, severity.toUpperCase()) ]), E('div', { 'class': 'ai-cve-content' }, [ E('div', { 'class': 'ai-cve-id' }, [ E('a', { 'href': 'https://nvd.nist.gov/vuln/detail/' + cve.id, 'target': '_blank' }, cve.id) ]), E('div', { 'class': 'ai-cve-desc' }, (cve.description || '').substring(0, 120) + '...') ]), E('div', { 'class': 'ai-cve-date' }, cve.published || '') ]); })); }, runAllAgents: function() { ui.showModal('Running Agents', [ E('p', { 'class': 'spinning' }, 'Starting all AI agents...') ]); api.runAll().then(function(result) { ui.hideModal(); var started = Object.values(result).filter(function(s) { return s === 'started'; }).length; ui.addNotification(null, E('p', {}, 'Started ' + started + ' agents'), 'success'); }).catch(function() { ui.hideModal(); ui.addNotification(null, E('p', {}, 'Failed to start agents'), 'error'); }); }, getAIAnalysis: function() { ui.showModal('AI Analysis', [ E('p', { 'class': 'spinning' }, 'Generating security analysis (may take up to 60s)...') ]); api.analyze().then(function(result) { ui.hideModal(); if (result.analysis) { ui.showModal('Security Analysis', [ E('div', { 'style': 'white-space: pre-wrap; max-height: 400px; overflow-y: auto;' }, result.analysis), E('div', { 'class': 'right' }, E('button', { 'class': 'btn', 'click': ui.hideModal }, 'Close')) ]); } else { ui.addNotification(null, E('p', {}, 'Error: ' + (result.error || 'Analysis failed')), 'error'); } }).catch(function() { ui.hideModal(); ui.addNotification(null, E('p', {}, 'Analysis failed'), 'error'); }); }, showTimeline: function() { api.getTimeline(24).then(function(result) { var timeline = result.timeline || []; ui.showModal('Security Timeline (24h)', [ E('div', { 'style': 'max-height: 400px; overflow-y: auto;' }, timeline.length ? E('div', { 'class': 'ai-timeline' }, timeline.map(function(e) { return E('div', { 'class': 'ai-timeline-item' }, [ E('div', { 'class': 'ai-timeline-time' }, e.time), E('div', { 'class': 'ai-timeline-source' }, e.source), E('div', { 'class': 'ai-timeline-msg' }, e.message) ]); })) : E('p', {}, 'No events in the last 24 hours') ), E('div', { 'class': 'right' }, E('button', { 'class': 'btn', 'click': ui.hideModal }, 'Close')) ]); }); }, pollData: function() { var self = this; return api.getOverview().then(function(data) { var s = data.status || {}; var p = data.posture || {}; var alerts = data.alerts || []; var agents = s.agents || {}; var el = document.getElementById('ai-stats'); if (el) dom.content(el, self.renderStats(s)); el = document.getElementById('ai-agents'); if (el) dom.content(el, self.renderAgents(agents)); el = document.getElementById('ai-alerts'); if (el) dom.content(el, self.renderAlerts(alerts)); // Update posture card el = document.getElementById('ai-posture'); if (el) { el.className = 'ai-posture-card ' + api.getPostureColor(p.score || 0); var scoreEl = el.querySelector('.ai-score-value'); var labelEl = el.querySelector('.ai-posture-label'); var factorsEl = el.querySelector('.ai-posture-factors'); if (scoreEl) scoreEl.textContent = String(p.score || 0); if (labelEl) labelEl.textContent = api.getPostureLabel(p.score || 0); if (factorsEl) factorsEl.textContent = p.factors || ''; } }); }, handleSaveApply: null, handleSave: null, handleReset: null });