diff --git a/otel-collector.yaml b/otel-collector.yaml deleted file mode 100644 index 19b3769..0000000 --- a/otel-collector.yaml +++ /dev/null @@ -1,28 +0,0 @@ -receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - -processors: - batch: - timeout: 1s - send_batch_size: 50 - -exporters: - otlphttp/dashboard: - endpoint: http://dashboard:3000 - encoding: json - tls: - insecure: true - -service: - pipelines: - traces: - receivers: [otlp] - processors: [batch] - exporters: [otlphttp/dashboard] - - telemetry: - logs: - level: info diff --git a/public/index.html b/public/index.html index 6a82bc7..c71207f 100644 --- a/public/index.html +++ b/public/index.html @@ -42,27 +42,6 @@ margin-right: 2px; } - .btn-reset { - padding: 2px 8px; - font-size: 10px; - font-family: var(--font-sans); - font-weight: 500; - color: var(--color-muted); - background: transparent; - border: 1px solid rgba(139, 148, 158, 0.35); - border-radius: 4px; - cursor: pointer; - text-transform: none; - letter-spacing: normal; - transition: background 0.15s, border-color 0.15s, color 0.15s; - } - - .btn-reset:hover { - color: var(--color-text); - background: rgba(139, 148, 158, 0.1); - border-color: rgba(139, 148, 158, 0.4); - } - .panel-content { flex: 1; overflow-y: auto; @@ -543,7 +522,6 @@ Dashboard Status: CONNECTING -
@@ -649,13 +627,15 @@ switch (eventType) { case 'agent.created': - return 'Agent ' + (attrs['agent.id'] || '') + ' provisioned'; + return (attrs['user.id'] || '') + ' → ' + (attrs['agent.id'] || ''); case 'agent.terminated': return 'Agent ' + (attrs['agent.id'] || '') + ' terminated'; case 'request.allowed': return (attrs['http.host'] || '') + ' -- allowed'; case 'request.denied': return (attrs['http.host'] || '') + ' -- denied' + (attrs['denial.reason'] ? ': ' + attrs['denial.reason'] : ''); + case 'agent.prompted': + return (attrs['user.id'] || 'user') + ' → ' + (attrs['agent.id'] || 'agent'); default: if (attrs['agent.id']) return eventType + ' for ' + attrs['agent.id']; return eventType; @@ -694,12 +674,6 @@ const attrs = event.attributes || {}; const agentId = attrs['agent.id'] || ''; - // Demo reset — clear all local state - if (eventType === 'demo.reset') { - clearAllDashboardState(); - return; - } - // User lifecycle. Event names are speculative until the backend // settles its vocabulary; the handler shape stays the same. if (eventType === 'user.created') { @@ -711,12 +685,11 @@ if (userId) Topology.removeUserNode(userId); } - // User actions — light up the user→platform edge. - if (eventType === 'agent.requested' || eventType === 'prompt.sent' || eventType === 'permission.change_requested' || eventType === 'termination.requested') { + // agent.prompted: user→platform then platform→agent sequentially + if (eventType === 'agent.prompted') { const userId = attrs['user.id'] || ''; - if (userId) { - Topology.highlightEdge(userId, Topology.EDGE_HIGHLIGHT_MS); - } + if (userId) Topology.highlightEdge(userId, Topology.EDGE_HIGHLIGHT_MS); + if (agentId) setTimeout(() => Topology.highlightEdge('platform-' + agentId, Topology.EDGE_HIGHLIGHT_MS), 400); } // Agent lifecycle. Light up the platform→agent edge: the platform is @@ -731,11 +704,6 @@ Topology.removeAgentRuntimeNode(agentId); } - // Prompt delivered: platform exec'd into the agent via the supervisor. - if (eventType === 'agent.prompted') { - if (agentId) Topology.highlightEdge('platform-' + agentId, Topology.EDGE_HIGHLIGHT_MS); - } - // Gateway decisions if (eventType === 'auth.allowed' || eventType === 'request.allowed' || eventType === 'request.denied' || eventType === 'permission.checked') { const decision = { @@ -866,27 +834,6 @@ else startRegistryPolling(); }); - // ============================================================ - // Reset button - // ============================================================ - document.getElementById('btn-reset-demo').addEventListener('click', async () => { - const btn = document.getElementById('btn-reset-demo'); - btn.disabled = true; - btn.textContent = 'Resetting...'; - - try { - const res = await fetch('/api/reset', { method: 'POST' }); - if (res.ok) { - clearAllDashboardState(); - } - } catch (err) { - console.error('Reset failed:', err); - } finally { - btn.disabled = false; - btn.textContent = 'Reset Demo'; - } - }); - // ============================================================ // Init // ============================================================ diff --git a/server.js b/server.js index 7b17ecd..d8f68d3 100644 --- a/server.js +++ b/server.js @@ -123,12 +123,8 @@ const server = http.createServer(async (req, res) => { attributes: body.attributes || {}, received_at: new Date().toISOString(), }; - if (ev.event_type === 'demo.reset') { - events = []; - } else { - events.push(ev); - if (events.length > MAX_EVENTS) events = events.slice(-MAX_EVENTS); - } + events.push(ev); + if (events.length > MAX_EVENTS) events = events.slice(-MAX_EVENTS); broadcastEvent(ev); return sendJson(res, 200, {}); } @@ -138,12 +134,8 @@ const server = http.createServer(async (req, res) => { const body = await parseBody(req); for (const ev of translateOtlpLogs(body)) { ev.received_at = new Date().toISOString(); - if (ev.event_type === 'demo.reset') { - events = []; - } else { - events.push(ev); - if (events.length > MAX_EVENTS) events = events.slice(-MAX_EVENTS); - } + events.push(ev); + if (events.length > MAX_EVENTS) events = events.slice(-MAX_EVENTS); broadcastEvent(ev); } return sendJson(res, 200, { partialSuccess: {} }); @@ -228,48 +220,6 @@ const server = http.createServer(async (req, res) => { } } - // POST /api/reset - proxy to demo-admin reset endpoint - if (pathName === '/api/reset' && method === 'POST') { - const adminHost = process.env.ADMIN_HOST || 'demo-admin'; - const adminPort = process.env.ADMIN_PORT || '8080'; - return new Promise((resolve) => { - let responded = false; - const proxyReq = http.request({ - hostname: adminHost, - port: parseInt(adminPort), - path: '/api/reset', - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - timeout: 30000, - }, (proxyRes) => { - let body = ''; - proxyRes.on('data', chunk => { body += chunk; }); - proxyRes.on('end', () => { - if (responded) return; - responded = true; - events = []; - res.writeHead(proxyRes.statusCode, { 'Content-Type': 'application/json' }); - res.end(body); - resolve(); - }); - }); - proxyReq.on('error', (err) => { - if (responded) return; - responded = true; - sendJson(res, 502, { error: 'Failed to reach demo-admin: ' + err.message }); - resolve(); - }); - proxyReq.on('timeout', () => { - proxyReq.destroy(); - if (responded) return; - responded = true; - sendJson(res, 504, { error: 'demo-admin request timed out' }); - resolve(); - }); - proxyReq.end(); - }); - } - // Serve static files for everything else serveStatic(req, res); } catch (e) { @@ -283,8 +233,9 @@ const wss = new WebSocketServer({ server, path: '/ws' }); wss.on('connection', (ws) => { wsClients.add(ws); - // Send event history on connect - ws.send(JSON.stringify({ type: 'history', events })); + // Send event history on connect, sorted chronologically + const sortedEvents = [...events].sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0)); + ws.send(JSON.stringify({ type: 'history', events: sortedEvents })); ws.on('close', () => { wsClients.delete(ws); diff --git a/translate.js b/translate.js index e11a9da..0864dc0 100644 --- a/translate.js +++ b/translate.js @@ -53,7 +53,7 @@ function translateOtlpLogs(otlp) { } } } - return out; + return out.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0)); } // ─── Jaeger trace export → flat events ───────────────────────────────────── @@ -198,7 +198,7 @@ function translateOtlpTraces(payload) { } } } - return out; + return out.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0)); } module.exports = { translateOtlpLogs, translateJaegerTraces, translateOtlpTraces };