orquesta-agent 0.2.64 → 0.2.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,6 +32,7 @@ function switchTab(tab) {
32
32
  document.getElementById('tab-' + tab)?.classList.add('active')
33
33
  document.querySelector(`[data-tab="${tab}"]`)?.classList.add('active')
34
34
  if (tab === 'health') loadHealth()
35
+ if (tab === 'logs') loadAllLogs()
35
36
  }
36
37
 
37
38
  // ── System Info ────────────────────────────────────────────────────
@@ -242,7 +243,7 @@ function connectLogStream(id) {
242
243
  const { logs } = JSON.parse(e.data)
243
244
  const el = document.getElementById('logs-' + id)
244
245
  if (el) {
245
- el.textContent = logs.join('\n')
246
+ el.innerHTML = colorizeLogs(logs)
246
247
  el.scrollTop = el.scrollHeight
247
248
  }
248
249
  } catch {}
@@ -318,6 +319,86 @@ async function saveAgent(e) {
318
319
  } catch (e2) { alert(e2.message) }
319
320
  }
320
321
 
322
+ // ── All Logs Tab ───────────────────────────────────────────────────
323
+
324
+ let allLogsSources = new Map()
325
+
326
+ function loadAllLogs() {
327
+ const grid = document.getElementById('all-logs-grid')
328
+ if (!grid) return
329
+
330
+ // Close existing streams
331
+ for (const es of allLogsSources.values()) es.close()
332
+ allLogsSources.clear()
333
+
334
+ if (agents.length === 0) {
335
+ grid.innerHTML = '<div class="empty-state"><p>No agents running</p></div>'
336
+ return
337
+ }
338
+
339
+ const runningAgents = agents.filter(a => a.status === 'running')
340
+ if (runningAgents.length === 0) {
341
+ grid.innerHTML = '<div class="empty-state"><p>No agents currently running</p></div>'
342
+ return
343
+ }
344
+
345
+ grid.innerHTML = runningAgents.map(a => {
346
+ const isDiscovered = a.id.startsWith('discovered-')
347
+ const name = isDiscovered ? a.workingDir.split('/').pop() || a.name : a.name
348
+ return `<div class="all-logs-card">
349
+ <div class="all-logs-card-header">
350
+ <span class="agent-name-pill"><span class="badge-dot pulse"></span>${esc(name)}</span>
351
+ <span style="font-size:11px;color:var(--text-500)">PID ${a.pid || '?'}</span>
352
+ </div>
353
+ <div class="log-viewer" id="all-logs-${a.id}"></div>
354
+ </div>`
355
+ }).join('')
356
+
357
+ // Connect SSE for each agent
358
+ for (const a of runningAgents) {
359
+ const es = new EventSource(`${API}/agents/${a.id}/logs/stream`)
360
+ es.onmessage = (e) => {
361
+ try {
362
+ const { logs } = JSON.parse(e.data)
363
+ const el = document.getElementById('all-logs-' + a.id)
364
+ if (el) {
365
+ el.innerHTML = colorizeLogs(logs)
366
+ el.scrollTop = el.scrollHeight
367
+ }
368
+ } catch {}
369
+ }
370
+ allLogsSources.set(a.id, es)
371
+ }
372
+ }
373
+
374
+ // ANSI color code → HTML span conversion
375
+ function colorizeLogs(lines) {
376
+ return lines.map(line => {
377
+ let html = esc(line)
378
+ // Strip ANSI codes and apply CSS classes
379
+ html = html
380
+ // Colors
381
+ .replace(/\x1b\[32m|\x1b\[39m/g, '') // strip raw escapes from esc()
382
+ .replace(/&#x1b;\[32m/g, '<span class="ansi-green">')
383
+ .replace(/&#x1b;\[33m/g, '<span class="ansi-yellow">')
384
+ .replace(/&#x1b;\[34m/g, '<span class="ansi-blue">')
385
+ .replace(/&#x1b;\[31m/g, '<span class="ansi-red">')
386
+ .replace(/&#x1b;\[36m/g, '<span class="ansi-cyan">')
387
+ .replace(/&#x1b;\[35m/g, '<span class="ansi-magenta">')
388
+ .replace(/&#x1b;\[1m/g, '<span class="ansi-bold">')
389
+ .replace(/&#x1b;\[2m/g, '<span class="ansi-dim">')
390
+ .replace(/&#x1b;\[0?m/g, '</span>')
391
+ .replace(/&#x1b;\[39m/g, '</span>')
392
+ // Highlight patterns
393
+ if (html.includes('✓') || html.includes('✅')) html = `<span class="log-success">${html}</span>`
394
+ else if (html.includes('⚠') || html.includes('WARN')) html = `<span class="log-warn">${html}</span>`
395
+ else if (html.includes('✗') || html.includes('❌') || html.includes('ERROR')) html = `<span class="log-error">${html}</span>`
396
+ // Dim timestamps
397
+ html = html.replace(/\[(\d{2}:\d{2}:\d{2})\]/g, '<span class="log-timestamp">[$1]</span>')
398
+ return `<div class="log-line">${html}</div>`
399
+ }).join('')
400
+ }
401
+
321
402
  // ── Fullscreen Logs ────────────────────────────────────────────────
322
403
 
323
404
  let fullscreenLogId = null
@@ -336,7 +417,7 @@ function openFullscreenLogs(id, name) {
336
417
  const { logs } = JSON.parse(e.data)
337
418
  const el = document.getElementById('logs-fullscreen-content')
338
419
  if (el) {
339
- el.textContent = logs.join('\n')
420
+ el.innerHTML = colorizeLogs(logs)
340
421
  el.scrollTop = el.scrollHeight
341
422
  }
342
423
  } catch {}
@@ -26,6 +26,10 @@
26
26
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 10h.01M10 10h.01M6 14h12"/></svg>
27
27
  Agents
28
28
  </button>
29
+ <button class="nav-item" data-tab="logs" onclick="switchTab('logs')">
30
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
31
+ All Logs
32
+ </button>
29
33
  <button class="nav-item" data-tab="health" onclick="switchTab('health')">
30
34
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
31
35
  System
@@ -74,6 +78,15 @@
74
78
  <div id="agents-container"></div>
75
79
  </div>
76
80
 
81
+ <!-- All Logs Tab -->
82
+ <div id="tab-logs" class="tab-content">
83
+ <div class="page-header">
84
+ <h1>All Logs</h1>
85
+ <p class="page-subtitle">Real-time logs from all agents</p>
86
+ </div>
87
+ <div id="all-logs-grid" class="all-logs-grid"></div>
88
+ </div>
89
+
77
90
  <!-- Health Tab -->
78
91
  <div id="tab-health" class="tab-content">
79
92
  <div class="page-header">
@@ -619,6 +619,66 @@ body {
619
619
  border-top: 1px solid var(--bg-700);
620
620
  }
621
621
 
622
+ /* ── All Logs Grid ──────────────────────────────────── */
623
+ .all-logs-grid {
624
+ display: grid;
625
+ grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
626
+ gap: 12px;
627
+ }
628
+
629
+ .all-logs-card {
630
+ background: rgba(24,24,27,0.5);
631
+ border: 1px solid var(--bg-700);
632
+ border-radius: 12px;
633
+ overflow: hidden;
634
+ backdrop-filter: blur(10px);
635
+ }
636
+
637
+ .all-logs-card-header {
638
+ display: flex;
639
+ justify-content: space-between;
640
+ align-items: center;
641
+ padding: 10px 14px;
642
+ background: var(--bg-900);
643
+ border-bottom: 1px solid var(--bg-700);
644
+ }
645
+
646
+ .all-logs-card-header .agent-name-pill {
647
+ display: flex;
648
+ align-items: center;
649
+ gap: 6px;
650
+ font-size: 13px;
651
+ font-weight: 600;
652
+ }
653
+
654
+ .all-logs-card-header .agent-name-pill .badge-dot {
655
+ width: 6px;
656
+ height: 6px;
657
+ border-radius: 50%;
658
+ background: var(--green);
659
+ }
660
+
661
+ .all-logs-card .log-viewer {
662
+ height: 350px;
663
+ border-radius: 0;
664
+ }
665
+
666
+ /* ── Log Colors (ANSI) ──────────────────────────────── */
667
+ .log-viewer .log-line { margin: 0; }
668
+ .log-viewer .ansi-green { color: #4ade80; }
669
+ .log-viewer .ansi-red { color: #f87171; }
670
+ .log-viewer .ansi-yellow { color: #facc15; }
671
+ .log-viewer .ansi-blue { color: #60a5fa; }
672
+ .log-viewer .ansi-cyan { color: #22d3ee; }
673
+ .log-viewer .ansi-magenta { color: #c084fc; }
674
+ .log-viewer .ansi-bold { font-weight: 700; }
675
+ .log-viewer .ansi-dim { opacity: 0.6; }
676
+ .log-viewer .log-timestamp { color: #52525b; }
677
+ .log-viewer .log-success { color: #4ade80; }
678
+ .log-viewer .log-warn { color: #facc15; }
679
+ .log-viewer .log-error { color: #f87171; }
680
+ .log-viewer .log-info { color: #60a5fa; }
681
+
622
682
  /* ── Fullscreen Logs ────────────────────────────────── */
623
683
  .logs-fullscreen {
624
684
  display: none;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-agent",
3
- "version": "0.2.64",
3
+ "version": "0.2.65",
4
4
  "description": "Local agent for Orquesta - connects your VM to the Orquesta dashboard",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",