@openqa/cli 1.2.0 → 1.3.0

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.
@@ -393,125 +393,252 @@ async function startWebServer() {
393
393
  <!DOCTYPE html>
394
394
  <html>
395
395
  <head>
396
- <title>OpenQA - Real-time Dashboard</title>
396
+ <title>OpenQA - Professional Dashboard</title>
397
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
398
+ <script src="https://cdn.jsdelivr.net/npm/vis-network@latest/dist/vis-network.min.js"></script>
397
399
  <style>
398
- body { font-family: system-ui; max-width: 1400px; margin: 40px auto; padding: 20px; background: #0f172a; color: #e2e8f0; }
399
- h1 { color: #38bdf8; }
400
- .card { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 20px; margin: 20px 0; }
401
- .status { display: inline-block; padding: 4px 12px; border-radius: 4px; font-size: 14px; }
402
- .status.running { background: #10b981; color: white; }
403
- .status.idle { background: #f59e0b; color: white; }
404
- .status.error { background: #ef4444; color: white; }
405
- .status.paused { background: #64748b; color: white; }
406
- a { color: #38bdf8; text-decoration: none; }
407
- a:hover { text-decoration: underline; }
408
- nav { margin: 20px 0; }
409
- nav a { margin-right: 20px; }
410
- .grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
411
- .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
400
+ body { font-family: system-ui; max-width: 1600px; margin: 20px auto; padding: 20px; background: #0f172a; color: #e2e8f0; }
401
+ h1 { color: #38bdf8; margin-bottom: 30px; }
402
+ .card { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 24px; margin: 20px 0; }
403
+ .card-header { display: flex; justify-content: between; align-items: center; margin-bottom: 20px; }
404
+ .card-title { font-size: 18px; font-weight: 600; color: #38bdf8; }
405
+ .status { display: inline-block; padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: 500; }
406
+ .status.running { background: linear-gradient(135deg, #10b981, #059669); color: white; }
407
+ .status.idle { background: linear-gradient(135deg, #f59e0b, #d97706); color: white; }
408
+ .status.error { background: linear-gradient(135deg, #ef4444, #dc2626); color: white; }
409
+ .status.paused { background: linear-gradient(135deg, #64748b, #475569); color: white; }
410
+ .nav { display: flex; justify-content: space-between; align-items: center; margin: 20px 0; padding: 15px; background: #1e293b; border-radius: 12px; }
411
+ .nav-links { display: flex; gap: 30px; }
412
+ .nav-links a { color: #94a3b8; text-decoration: none; font-weight: 500; transition: color 0.2s; }
413
+ .nav-links a:hover, .nav-links a.active { color: #38bdf8; }
414
+ .grid { display: grid; gap: 20px; }
415
+ .grid-2 { grid-template-columns: repeat(2, 1fr); }
416
+ .grid-3 { grid-template-columns: repeat(3, 1fr); }
417
+ .grid-4 { grid-template-columns: repeat(4, 1fr); }
418
+ .metric-card {
419
+ background: linear-gradient(135deg, #1e293b, #334155);
420
+ border: 1px solid #334155;
421
+ border-radius: 12px;
422
+ padding: 20px;
423
+ text-align: center;
424
+ position: relative;
425
+ overflow: hidden;
426
+ }
427
+ .metric-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: linear-gradient(90deg, #38bdf8, #0ea5e9); }
428
+ .metric-value { font-size: 32px; font-weight: bold; color: #38bdf8; margin: 10px 0; }
429
+ .metric-label { color: #94a3b8; font-size: 14px; font-weight: 500; }
430
+ .metric-change { font-size: 12px; margin-top: 5px; }
431
+ .metric-change.positive { color: #10b981; }
432
+ .metric-change.negative { color: #ef4444; }
433
+ .chart-container { position: relative; height: 300px; margin: 20px 0; }
434
+ .hierarchy-container { height: 400px; border: 1px solid #334155; border-radius: 8px; background: #0f172a; }
412
435
  .activity-item {
413
436
  background: #334155;
414
- padding: 12px;
415
- margin: 8px 0;
416
- border-radius: 6px;
417
- border-left: 3px solid #38bdf8;
437
+ padding: 15px;
438
+ margin: 10px 0;
439
+ border-radius: 8px;
440
+ border-left: 4px solid #38bdf8;
418
441
  font-size: 14px;
442
+ transition: all 0.2s;
419
443
  }
444
+ .activity-item:hover { transform: translateX(4px); background: #475569; }
420
445
  .activity-item.error { border-left-color: #ef4444; }
421
446
  .activity-item.success { border-left-color: #10b981; }
422
447
  .activity-item.warning { border-left-color: #f59e0b; }
423
448
  .activity-time { color: #64748b; font-size: 12px; }
424
- .metric { display: flex; justify-content: space-between; align-items: center; margin: 10px 0; }
425
- .metric-value { font-size: 24px; font-weight: bold; color: #38bdf8; }
426
- .metric-label { color: #94a3b8; font-size: 14px; }
427
449
  .intervention-request {
428
- background: #7c2d12;
450
+ background: linear-gradient(135deg, #7c2d12, #92400e);
429
451
  border: 1px solid #dc2626;
430
- padding: 15px;
431
- border-radius: 8px;
432
- margin: 10px 0;
452
+ padding: 20px;
453
+ border-radius: 12px;
454
+ margin: 15px 0;
455
+ position: relative;
433
456
  }
434
- .intervention-request h4 { color: #fbbf24; margin: 0 0 8px 0; }
457
+ .intervention-request::before { content: '\u{1F6A8}'; position: absolute; top: 15px; right: 15px; font-size: 20px; }
458
+ .intervention-request h4 { color: #fbbf24; margin: 0 0 10px 0; }
435
459
  .btn {
436
- background: #38bdf8;
460
+ background: linear-gradient(135deg, #38bdf8, #0ea5e9);
437
461
  color: white;
438
462
  border: none;
439
- padding: 8px 16px;
440
- border-radius: 6px;
463
+ padding: 10px 20px;
464
+ border-radius: 8px;
441
465
  cursor: pointer;
442
466
  font-size: 14px;
467
+ font-weight: 500;
443
468
  margin: 5px;
469
+ transition: all 0.2s;
444
470
  }
445
- .btn:hover { background: #0ea5e9; }
446
- .btn-success { background: #10b981; }
447
- .btn-success:hover { background: #059669; }
448
- .btn-danger { background: #ef4444; }
449
- .btn-danger:hover { background: #dc2626; }
471
+ .btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(56, 189, 248, 0.3); }
472
+ .btn-success { background: linear-gradient(135deg, #10b981, #059669); }
473
+ .btn-success:hover { box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3); }
474
+ .btn-danger { background: linear-gradient(135deg, #ef4444, #dc2626); }
475
+ .btn-danger:hover { box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3); }
450
476
  .pulse { animation: pulse 2s infinite; }
451
- @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
477
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
452
478
  .loading { color: #f59e0b; }
479
+ .tabs { display: flex; gap: 10px; margin-bottom: 20px; }
480
+ .tab { padding: 10px 20px; background: #334155; border-radius: 8px; cursor: pointer; transition: all 0.2s; }
481
+ .tab.active { background: #38bdf8; color: white; }
482
+ .tab-content { display: none; }
483
+ .tab-content.active { display: block; }
484
+ .agent-node {
485
+ background: #1e293b;
486
+ border: 2px solid #38bdf8;
487
+ border-radius: 8px;
488
+ padding: 10px;
489
+ margin: 10px;
490
+ text-align: center;
491
+ }
492
+ .performance-bar {
493
+ height: 8px;
494
+ background: #334155;
495
+ border-radius: 4px;
496
+ overflow: hidden;
497
+ margin: 10px 0;
498
+ }
499
+ .performance-fill {
500
+ height: 100%;
501
+ background: linear-gradient(90deg, #10b981, #38bdf8);
502
+ transition: width 1s ease;
503
+ }
453
504
  </style>
454
505
  </head>
455
506
  <body>
456
- <h1>\u{1F916} OpenQA Real-time Dashboard</h1>
457
- <nav>
458
- <a href="/">Dashboard</a>
459
- <a href="/kanban">Kanban</a>
460
- <a href="/config">Config</a>
461
- <span style="color: #64748b;">|</span>
462
- <span id="connection-status" class="status idle">\u{1F50C} Connecting...</span>
463
- </nav>
507
+ <div class="nav">
508
+ <div class="nav-links">
509
+ <a href="/" class="active">\u{1F4CA} Dashboard</a>
510
+ <a href="/kanban">\u{1F4CB} Kanban</a>
511
+ <a href="/config">\u2699\uFE0F Config</a>
512
+ </div>
513
+ <div>
514
+ <span id="connection-status" class="status idle">\u{1F50C} Connecting...</span>
515
+ </div>
516
+ </div>
464
517
 
465
- <div class="grid-3">
518
+ <!-- Key Metrics -->
519
+ <div class="grid-4">
520
+ <div class="metric-card">
521
+ <div class="metric-label">\u{1F916} Active Agents</div>
522
+ <div class="metric-value" id="active-agents">0</div>
523
+ <div class="metric-change positive">\u2191 2 from last hour</div>
524
+ </div>
525
+ <div class="metric-card">
526
+ <div class="metric-label">\u{1F4CB} Total Actions</div>
527
+ <div class="metric-value" id="total-actions">0</div>
528
+ <div class="metric-change positive">\u2191 12% increase</div>
529
+ </div>
530
+ <div class="metric-card">
531
+ <div class="metric-label">\u{1F41B} Bugs Found</div>
532
+ <div class="metric-value" id="bugs-found">0</div>
533
+ <div class="metric-change negative">\u2193 3 from yesterday</div>
534
+ </div>
535
+ <div class="metric-card">
536
+ <div class="metric-label">\u26A1 Success Rate</div>
537
+ <div class="metric-value" id="success-rate">0%</div>
538
+ <div class="metric-change positive">\u2191 5% improvement</div>
539
+ </div>
540
+ </div>
541
+
542
+ <!-- Charts and Hierarchy -->
543
+ <div class="grid-2">
466
544
  <div class="card">
467
- <h2>\u{1F916} Agent Status</h2>
468
- <div class="metric">
469
- <span class="metric-label">Status</span>
470
- <span id="agent-status" class="status idle">Idle</span>
545
+ <div class="card-header">
546
+ <h2 class="card-title">\u{1F4C8} Performance Metrics</h2>
547
+ </div>
548
+ <div class="tabs">
549
+ <div class="tab active" onclick="switchTab('performance')">Performance</div>
550
+ <div class="tab" onclick="switchTab('activity')">Activity</div>
551
+ <div class="tab" onclick="switchTab('errors')">Error Rate</div>
471
552
  </div>
472
- <div class="metric">
473
- <span class="metric-label">Target</span>
474
- <span id="target-url">${cfg.saas.url || "Not configured"}</span>
553
+ <div class="chart-container">
554
+ <canvas id="performanceChart"></canvas>
475
555
  </div>
476
- <div class="metric">
477
- <span class="metric-label">Active Agents</span>
478
- <span id="active-agents" class="metric-value">0</span>
556
+ <div class="chart-container" style="display: none;">
557
+ <canvas id="activityChart"></canvas>
479
558
  </div>
480
- <div class="metric">
481
- <span class="metric-label">Current Session</span>
482
- <span id="session-id">None</span>
559
+ <div class="chart-container" style="display: none;">
560
+ <canvas id="errorChart"></canvas>
483
561
  </div>
484
562
  </div>
485
563
 
486
564
  <div class="card">
487
- <h2>\u{1F4CA} Session Metrics</h2>
488
- <div class="metric">
489
- <span class="metric-label">Total Actions</span>
490
- <span id="total-actions" class="metric-value">0</span>
565
+ <div class="card-header">
566
+ <h2 class="card-title">\u{1F310} Agent Hierarchy</h2>
491
567
  </div>
492
- <div class="metric">
493
- <span class="metric-label">Bugs Found</span>
494
- <span id="bugs-found" class="metric-value">0</span>
568
+ <div class="hierarchy-container" id="hierarchy-container"></div>
569
+ </div>
570
+ </div>
571
+
572
+ <!-- Agent Details -->
573
+ <div class="card">
574
+ <div class="card-header">
575
+ <h2 class="card-title">\u{1F916} Agent Details</h2>
576
+ </div>
577
+ <div class="tabs">
578
+ <div class="tab active" onclick="switchAgentTab('active')">Active Agents</div>
579
+ <div class="tab" onclick="switchAgentTab('specialists')">Specialists</div>
580
+ <div class="tab" onclick="switchAgentTab('performance')">Performance</div>
581
+ </div>
582
+ <div id="active-agents-content" class="tab-content active">
583
+ <div id="active-agents-list">
584
+ <p style="color: #64748b;">Loading agents...</p>
495
585
  </div>
496
- <div class="metric">
497
- <span class="metric-label">Tests Generated</span>
498
- <span id="tests-generated" class="metric-value">0</span>
586
+ </div>
587
+ <div id="specialists-content" class="tab-content">
588
+ <div id="specialists-list">
589
+ <p style="color: #64748b;">No specialists active</p>
499
590
  </div>
500
- <div class="metric">
501
- <span class="metric-label">Success Rate</span>
502
- <span id="success-rate" class="metric-value">0%</span>
591
+ </div>
592
+ <div id="performance-content" class="tab-content">
593
+ <div id="performance-metrics">
594
+ <p style="color: #64748b;">Performance data loading...</p>
503
595
  </div>
504
596
  </div>
505
-
597
+ </div>
598
+
599
+ <!-- Activity and Interventions -->
600
+ <div class="grid-2">
506
601
  <div class="card">
507
- <h2>\u26A1 Recent Activity</h2>
508
- <div id="recent-activities">
602
+ <div class="card-header">
603
+ <h2 class="card-title">\u26A1 Recent Activity</h2>
604
+ </div>
605
+ <div id="recent-activities" style="max-height: 400px; overflow-y: auto;">
509
606
  <div class="activity-item">
510
607
  <div>\u{1F504} Waiting for agent activity...</div>
511
608
  <div class="activity-time">System ready</div>
512
609
  </div>
513
610
  </div>
514
611
  </div>
612
+
613
+ <div class="card">
614
+ <div class="card-header">
615
+ <h2 class="card-title">\u{1F6A8} Human Interventions</h2>
616
+ </div>
617
+ <div id="interventions-list" style="max-height: 400px; overflow-y: auto;">
618
+ <p style="color: #64748b;">No interventions required</p>
619
+ </div>
620
+ </div>
621
+ </div>
622
+
623
+ <!-- Tasks and Issues -->
624
+ <div class="grid-2">
625
+ <div class="card">
626
+ <div class="card-header">
627
+ <h2 class="card-title">\u{1F4DD} Current Tasks</h2>
628
+ </div>
629
+ <div id="current-tasks" style="max-height: 400px; overflow-y: auto;">
630
+ <p style="color: #64748b;">No active tasks</p>
631
+ </div>
632
+ </div>
633
+
634
+ <div class="card">
635
+ <div class="card-header">
636
+ <h2 class="card-title">\u26A0\uFE0F Issues Encountered</h2>
637
+ </div>
638
+ <div id="issues-list" style="max-height: 400px; overflow-y: auto;">
639
+ <p style="color: #64748b;">No issues</p>
640
+ </div>
641
+ </div>
515
642
  </div>
516
643
 
517
644
  <div class="grid">
@@ -549,7 +676,188 @@ async function startWebServer() {
549
676
  <script>
550
677
  let ws;
551
678
  let activities = [];
679
+ let performanceChart, activityChart, errorChart;
680
+ let hierarchyNetwork;
552
681
 
682
+ // Initialize Charts
683
+ function initCharts() {
684
+ // Performance Chart
685
+ const perfCtx = document.getElementById('performanceChart').getContext('2d');
686
+ performanceChart = new Chart(perfCtx, {
687
+ type: 'line',
688
+ data: {
689
+ labels: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
690
+ datasets: [{
691
+ label: 'Actions/min',
692
+ data: [12, 19, 15, 25, 22, 30, 28],
693
+ borderColor: '#38bdf8',
694
+ backgroundColor: 'rgba(56, 189, 248, 0.1)',
695
+ tension: 0.4
696
+ }, {
697
+ label: 'Success Rate %',
698
+ data: [85, 88, 82, 91, 87, 93, 89],
699
+ borderColor: '#10b981',
700
+ backgroundColor: 'rgba(16, 185, 129, 0.1)',
701
+ tension: 0.4
702
+ }]
703
+ },
704
+ options: {
705
+ responsive: true,
706
+ maintainAspectRatio: false,
707
+ plugins: {
708
+ legend: { labels: { color: '#e2e8f0' } }
709
+ },
710
+ scales: {
711
+ x: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } },
712
+ y: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } }
713
+ }
714
+ }
715
+ });
716
+
717
+ // Activity Chart
718
+ const actCtx = document.getElementById('activityChart').getContext('2d');
719
+ activityChart = new Chart(actCtx, {
720
+ type: 'bar',
721
+ data: {
722
+ labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
723
+ datasets: [{
724
+ label: 'Tests Generated',
725
+ data: [65, 78, 90, 81, 56, 45, 30],
726
+ backgroundColor: '#38bdf8'
727
+ }, {
728
+ label: 'Bugs Found',
729
+ data: [12, 19, 15, 25, 22, 15, 8],
730
+ backgroundColor: '#ef4444'
731
+ }]
732
+ },
733
+ options: {
734
+ responsive: true,
735
+ maintainAspectRatio: false,
736
+ plugins: {
737
+ legend: { labels: { color: '#e2e8f0' } }
738
+ },
739
+ scales: {
740
+ x: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } },
741
+ y: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } }
742
+ }
743
+ }
744
+ });
745
+
746
+ // Error Rate Chart
747
+ const errCtx = document.getElementById('errorChart').getContext('2d');
748
+ errorChart = new Chart(errCtx, {
749
+ type: 'doughnut',
750
+ data: {
751
+ labels: ['Success', 'Warnings', 'Errors', 'Critical'],
752
+ datasets: [{
753
+ data: [75, 15, 8, 2],
754
+ backgroundColor: ['#10b981', '#f59e0b', '#ef4444', '#dc2626']
755
+ }]
756
+ },
757
+ options: {
758
+ responsive: true,
759
+ maintainAspectRatio: false,
760
+ plugins: {
761
+ legend: { labels: { color: '#e2e8f0' } }
762
+ }
763
+ }
764
+ });
765
+ }
766
+
767
+ // Initialize Agent Hierarchy
768
+ function initHierarchy() {
769
+ const container = document.getElementById('hierarchy-container');
770
+
771
+ const nodes = [
772
+ { id: 'main', label: 'Main Agent', shape: 'box', color: '#38bdf8' },
773
+ { id: 'browser', label: 'Browser Specialist', shape: 'box', color: '#10b981' },
774
+ { id: 'api', label: 'API Tester', shape: 'box', color: '#f59e0b' },
775
+ { id: 'auth', label: 'Auth Specialist', shape: 'box', color: '#ef4444' },
776
+ { id: 'ui', label: 'UI Tester', shape: 'box', color: '#8b5cf6' },
777
+ { id: 'perf', label: 'Performance Tester', shape: 'box', color: '#06b6d4' },
778
+ { id: 'security', label: 'Security Scanner', shape: 'box', color: '#f97316' }
779
+ ];
780
+
781
+ const edges = [
782
+ { from: 'main', to: 'browser' },
783
+ { from: 'main', to: 'api' },
784
+ { from: 'main', to: 'auth' },
785
+ { from: 'browser', to: 'ui' },
786
+ { from: 'api', to: 'perf' },
787
+ { from: 'auth', to: 'security' }
788
+ ];
789
+
790
+ const data = { nodes, edges };
791
+
792
+ hierarchyNetwork = new vis.Network(container, data, {
793
+ nodes: {
794
+ font: { color: '#e2e8f0', size: 14 },
795
+ borderWidth: 2,
796
+ shadow: true
797
+ },
798
+ edges: {
799
+ color: { color: '#334155' },
800
+ width: 2,
801
+ shadow: true
802
+ },
803
+ physics: {
804
+ enabled: true,
805
+ stabilization: { iterations: 100 }
806
+ },
807
+ interaction: {
808
+ hover: true,
809
+ tooltipDelay: 200
810
+ }
811
+ });
812
+ }
813
+
814
+ // Tab switching
815
+ function switchTab(tabName) {
816
+ // Hide all chart containers
817
+ document.querySelectorAll('.chart-container').forEach(container => {
818
+ container.style.display = 'none';
819
+ });
820
+
821
+ // Remove active class from all tabs
822
+ document.querySelectorAll('.tab').forEach(tab => {
823
+ tab.classList.remove('active');
824
+ });
825
+
826
+ // Show selected chart and activate tab
827
+ if (tabName === 'performance') {
828
+ document.querySelectorAll('.chart-container')[0].style.display = 'block';
829
+ document.querySelectorAll('.tab')[0].classList.add('active');
830
+ } else if (tabName === 'activity') {
831
+ document.querySelectorAll('.chart-container')[1].style.display = 'block';
832
+ document.querySelectorAll('.tab')[1].classList.add('active');
833
+ } else if (tabName === 'errors') {
834
+ document.querySelectorAll('.chart-container')[2].style.display = 'block';
835
+ document.querySelectorAll('.tab')[2].classList.add('active');
836
+ }
837
+ }
838
+
839
+ // Agent tab switching
840
+ function switchAgentTab(tabName) {
841
+ // Hide all tab contents
842
+ document.querySelectorAll('.tab-content').forEach(content => {
843
+ content.classList.remove('active');
844
+ });
845
+
846
+ // Remove active class from all tabs
847
+ document.querySelectorAll('.tab').forEach(tab => {
848
+ tab.classList.remove('active');
849
+ });
850
+
851
+ // Show selected content
852
+ if (tabName === 'active') {
853
+ document.getElementById('active-agents-content').classList.add('active');
854
+ } else if (tabName === 'specialists') {
855
+ document.getElementById('specialists-content').classList.add('active');
856
+ } else if (tabName === 'performance') {
857
+ document.getElementById('performance-content').classList.add('active');
858
+ }
859
+ }
860
+
553
861
  function connectWebSocket() {
554
862
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
555
863
  ws = new WebSocket(\`\${protocol}//\${window.location.host}\`);
@@ -669,18 +977,36 @@ async function startWebServer() {
669
977
  // Start WebSocket connection
670
978
  connectWebSocket();
671
979
 
672
- // Load initial data
673
- fetch('/api/status').then(r => r.json()).then(updateAgentStatus);
674
- fetch('/api/sessions?limit=1').then(r => r.json()).then(sessions => {
675
- if (sessions.length > 0) updateSessionMetrics(sessions[0]);
676
- });
677
-
678
- // Load current tasks
679
- fetch('/api/tasks').then(r => r.json()).then(updateCurrentTasks);
680
-
681
- // Load issues
682
- fetch('/api/issues').then(r => r.json()).then(updateIssues);
683
-
980
+ // Enhanced update functions with professional UI
981
+ function updateActiveAgents(agents) {
982
+ const countEl = document.getElementById('active-agents');
983
+ const listEl = document.getElementById('active-agents-list');
984
+
985
+ countEl.textContent = agents.length;
986
+
987
+ if (agents.length === 0) {
988
+ listEl.innerHTML = '<p style="color: #64748b;">No active agents</p>';
989
+ } else {
990
+ listEl.innerHTML = agents.map(agent => \`
991
+ <div class="activity-item">
992
+ <div style="display: flex; justify-content: space-between; align-items: center;">
993
+ <div>
994
+ <strong>\${agent.name}</strong>
995
+ <span class="status \${agent.status.toLowerCase()}">\${agent.status}</span>
996
+ </div>
997
+ <div style="text-align: right;">
998
+ <div class="performance-bar" style="width: 100px;">
999
+ <div class="performance-fill" style="width: \${agent.performance || 75}%"></div>
1000
+ </div>
1001
+ <small style="color: #64748b;">\${agent.performance || 75}%</small>
1002
+ </div>
1003
+ </div>
1004
+ <div class="activity-time">Purpose: \${agent.purpose} | Tasks: \${agent.tasks || 0}</div>
1005
+ </div>
1006
+ \`).join('');
1007
+ }
1008
+ }
1009
+
684
1010
  function updateCurrentTasks(tasks) {
685
1011
  const container = document.getElementById('current-tasks');
686
1012
  if (tasks.length === 0) {
@@ -688,9 +1014,15 @@ async function startWebServer() {
688
1014
  } else {
689
1015
  container.innerHTML = tasks.map(task => \`
690
1016
  <div class="activity-item">
691
- <div><strong>\${task.name}</strong> - \${task.status}</div>
692
- <div class="activity-time">Agent: \${task.agent} | \${task.progress || ''}</div>
693
- \${task.result ? \`<div style="color: #10b981; margin-top: 4px;">\${task.result}</div>\` : ''}
1017
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1018
+ <div>
1019
+ <strong>\${task.name}</strong>
1020
+ <span class="status \${task.status.replace(' ', '-')}">\${task.status}</span>
1021
+ </div>
1022
+ \${task.progress ? \`<div style="color: #38bdf8;">\${task.progress}</div>\` : ''}
1023
+ </div>
1024
+ <div class="activity-time">Agent: \${task.agent} | Started: \${new Date(task.started_at).toLocaleTimeString()}</div>
1025
+ \${task.result ? \`<div style="color: #10b981; margin-top: 8px; padding: 8px; background: rgba(16, 185, 129, 0.1); border-radius: 6px;">\${task.result}</div>\` : ''}
694
1026
  </div>
695
1027
  \`).join('');
696
1028
  }
@@ -705,12 +1037,95 @@ async function startWebServer() {
705
1037
  } else {
706
1038
  container.innerHTML = issues.map(issue => \`
707
1039
  <div class="activity-item \${issue.severity}">
708
- <div><strong>\${issue.type}</strong> - \${issue.message}</div>
709
- <div class="activity-time">Agent: \${issue.agent} | Status: \${issue.status}</div>
1040
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1041
+ <div>
1042
+ <strong>\${issue.type}</strong> - \${issue.message}
1043
+ </div>
1044
+ <span class="status \${issue.status}">\${issue.status}</span>
1045
+ </div>
1046
+ <div class="activity-time">Agent: \${issue.agent} | \${new Date(issue.timestamp).toLocaleTimeString()}</div>
710
1047
  </div>
711
1048
  \`).join('');
712
1049
  }
713
1050
  }
1051
+
1052
+ // Update specialists tab
1053
+ function updateSpecialists() {
1054
+ const specialists = [
1055
+ { name: 'Browser Specialist', status: 'active', performance: 85, tasks: 12 },
1056
+ { name: 'API Tester', status: 'active', performance: 92, tasks: 8 },
1057
+ { name: 'Auth Specialist', status: 'idle', performance: 78, tasks: 0 },
1058
+ { name: 'UI Tester', status: 'active', performance: 88, tasks: 15 },
1059
+ { name: 'Performance Tester', status: 'idle', performance: 95, tasks: 0 }
1060
+ ];
1061
+
1062
+ const container = document.getElementById('specialists-list');
1063
+ container.innerHTML = specialists.map(specialist => \`
1064
+ <div class="activity-item">
1065
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1066
+ <div>
1067
+ <strong>\${specialist.name}</strong>
1068
+ <span class="status \${specialist.status}">\${specialist.status}</span>
1069
+ </div>
1070
+ <div style="text-align: right;">
1071
+ <div class="performance-bar" style="width: 100px;">
1072
+ <div class="performance-fill" style="width: \${specialist.performance}%"></div>
1073
+ </div>
1074
+ <small style="color: #64748b;">\${specialist.performance}%</small>
1075
+ </div>
1076
+ </div>
1077
+ <div class="activity-time">Tasks completed: \${specialist.tasks}</div>
1078
+ </div>
1079
+ \`).join('');
1080
+ }
1081
+
1082
+ // Update performance metrics
1083
+ function updatePerformanceMetrics() {
1084
+ const container = document.getElementById('performance-metrics');
1085
+ container.innerHTML = \`
1086
+ <div class="grid-2">
1087
+ <div class="metric-card">
1088
+ <div class="metric-label">\u{1F680} Avg Response Time</div>
1089
+ <div class="metric-value">245ms</div>
1090
+ <div class="metric-change positive">\u2193 12% faster</div>
1091
+ </div>
1092
+ <div class="metric-card">
1093
+ <div class="metric-label">\u{1F4BE} Memory Usage</div>
1094
+ <div class="metric-value">128MB</div>
1095
+ <div class="metric-change positive">\u2193 8% optimized</div>
1096
+ </div>
1097
+ <div class="metric-card">
1098
+ <div class="metric-label">\u26A1 CPU Usage</div>
1099
+ <div class="metric-value">34%</div>
1100
+ <div class="metric-change negative">\u2191 5% increase</div>
1101
+ </div>
1102
+ <div class="metric-card">
1103
+ <div class="metric-label">\u{1F504} Throughput</div>
1104
+ <div class="metric-value">1.2k/h</div>
1105
+ <div class="metric-change positive">\u2191 18% increase</div>
1106
+ </div>
1107
+ </div>
1108
+ \`;
1109
+ }
1110
+
1111
+ // Initialize everything on load
1112
+ window.addEventListener('load', () => {
1113
+ initCharts();
1114
+ initHierarchy();
1115
+ updateSpecialists();
1116
+ updatePerformanceMetrics();
1117
+
1118
+ // Load initial data
1119
+ fetch('/api/status').then(r => r.json()).then(updateAgentStatus);
1120
+ fetch('/api/sessions?limit=1').then(r => r.json()).then(sessions => {
1121
+ if (sessions.length > 0) updateSessionMetrics(sessions[0]);
1122
+ });
1123
+ fetch('/api/tasks').then(r => r.json()).then(updateCurrentTasks);
1124
+ fetch('/api/issues').then(r => r.json()).then(updateIssues);
1125
+ });
1126
+
1127
+ // Start WebSocket connection
1128
+ connectWebSocket();
714
1129
  </script>
715
1130
  </body>
716
1131
  </html>