@openqa/cli 1.2.0 → 1.3.1

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.
package/dist/cli/index.js CHANGED
@@ -430,125 +430,252 @@ async function startWebServer() {
430
430
  <!DOCTYPE html>
431
431
  <html>
432
432
  <head>
433
- <title>OpenQA - Real-time Dashboard</title>
433
+ <title>OpenQA - Professional Dashboard</title>
434
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
435
+ <script src="https://cdn.jsdelivr.net/npm/@xyflow/react@11/dist/umd/index.js"></script>
434
436
  <style>
435
- body { font-family: system-ui; max-width: 1400px; margin: 40px auto; padding: 20px; background: #0f172a; color: #e2e8f0; }
436
- h1 { color: #38bdf8; }
437
- .card { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 20px; margin: 20px 0; }
438
- .status { display: inline-block; padding: 4px 12px; border-radius: 4px; font-size: 14px; }
439
- .status.running { background: #10b981; color: white; }
440
- .status.idle { background: #f59e0b; color: white; }
441
- .status.error { background: #ef4444; color: white; }
442
- .status.paused { background: #64748b; color: white; }
443
- a { color: #38bdf8; text-decoration: none; }
444
- a:hover { text-decoration: underline; }
445
- nav { margin: 20px 0; }
446
- nav a { margin-right: 20px; }
447
- .grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
448
- .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
437
+ body { font-family: system-ui; max-width: 1600px; margin: 20px auto; padding: 20px; background: #0f172a; color: #e2e8f0; }
438
+ h1 { color: #f97316; margin-bottom: 30px; }
439
+ .card { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 24px; margin: 20px 0; }
440
+ .card-header { display: flex; justify-content: between; align-items: center; margin-bottom: 20px; }
441
+ .card-title { font-size: 18px; font-weight: 600; color: #f97316; }
442
+ .status { display: inline-block; padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: 500; }
443
+ .status.running { background: linear-gradient(135deg, #10b981, #059669); color: white; }
444
+ .status.idle { background: linear-gradient(135deg, #f59e0b, #d97706); color: white; }
445
+ .status.error { background: linear-gradient(135deg, #ef4444, #dc2626); color: white; }
446
+ .status.paused { background: linear-gradient(135deg, #64748b, #475569); color: white; }
447
+ .nav { display: flex; justify-content: space-between; align-items: center; margin: 20px 0; padding: 15px; background: #1e293b; border-radius: 12px; }
448
+ .nav-links { display: flex; gap: 30px; }
449
+ .nav-links a { color: #94a3b8; text-decoration: none; font-weight: 500; transition: color 0.2s; }
450
+ .nav-links a:hover, .nav-links a.active { color: #f97316; }
451
+ .grid { display: grid; gap: 20px; }
452
+ .grid-2 { grid-template-columns: repeat(2, 1fr); }
453
+ .grid-3 { grid-template-columns: repeat(3, 1fr); }
454
+ .grid-4 { grid-template-columns: repeat(4, 1fr); }
455
+ .metric-card {
456
+ background: linear-gradient(135deg, #1e293b, #334155);
457
+ border: 1px solid #334155;
458
+ border-radius: 12px;
459
+ padding: 20px;
460
+ text-align: center;
461
+ position: relative;
462
+ overflow: hidden;
463
+ }
464
+ .metric-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: linear-gradient(90deg, #f97316, #ea580c); }
465
+ .metric-value { font-size: 32px; font-weight: bold; color: #f97316; margin: 10px 0; }
466
+ .metric-label { color: #94a3b8; font-size: 14px; font-weight: 500; }
467
+ .metric-change { font-size: 12px; margin-top: 5px; }
468
+ .metric-change.positive { color: #10b981; }
469
+ .metric-change.negative { color: #ef4444; }
470
+ .chart-container { position: relative; height: 300px; margin: 20px 0; }
471
+ .hierarchy-container { height: 400px; border: 1px solid #334155; border-radius: 8px; background: #0f172a; }
449
472
  .activity-item {
450
473
  background: #334155;
451
- padding: 12px;
452
- margin: 8px 0;
453
- border-radius: 6px;
454
- border-left: 3px solid #38bdf8;
474
+ padding: 15px;
475
+ margin: 10px 0;
476
+ border-radius: 8px;
477
+ border-left: 4px solid #f97316;
455
478
  font-size: 14px;
479
+ transition: all 0.2s;
456
480
  }
481
+ .activity-item:hover { transform: translateX(4px); background: #475569; }
457
482
  .activity-item.error { border-left-color: #ef4444; }
458
483
  .activity-item.success { border-left-color: #10b981; }
459
484
  .activity-item.warning { border-left-color: #f59e0b; }
460
485
  .activity-time { color: #64748b; font-size: 12px; }
461
- .metric { display: flex; justify-content: space-between; align-items: center; margin: 10px 0; }
462
- .metric-value { font-size: 24px; font-weight: bold; color: #38bdf8; }
463
- .metric-label { color: #94a3b8; font-size: 14px; }
464
486
  .intervention-request {
465
- background: #7c2d12;
487
+ background: linear-gradient(135deg, #7c2d12, #92400e);
466
488
  border: 1px solid #dc2626;
467
- padding: 15px;
468
- border-radius: 8px;
469
- margin: 10px 0;
489
+ padding: 20px;
490
+ border-radius: 12px;
491
+ margin: 15px 0;
492
+ position: relative;
470
493
  }
471
- .intervention-request h4 { color: #fbbf24; margin: 0 0 8px 0; }
494
+ .intervention-request::before { content: '\u{1F6A8}'; position: absolute; top: 15px; right: 15px; font-size: 20px; }
495
+ .intervention-request h4 { color: #fbbf24; margin: 0 0 10px 0; }
472
496
  .btn {
473
- background: #38bdf8;
497
+ background: linear-gradient(135deg, #f97316, #ea580c);
474
498
  color: white;
475
499
  border: none;
476
- padding: 8px 16px;
477
- border-radius: 6px;
500
+ padding: 10px 20px;
501
+ border-radius: 8px;
478
502
  cursor: pointer;
479
503
  font-size: 14px;
504
+ font-weight: 500;
480
505
  margin: 5px;
506
+ transition: all 0.2s;
481
507
  }
482
- .btn:hover { background: #0ea5e9; }
483
- .btn-success { background: #10b981; }
484
- .btn-success:hover { background: #059669; }
485
- .btn-danger { background: #ef4444; }
486
- .btn-danger:hover { background: #dc2626; }
508
+ .btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(249, 115, 22, 0.3); }
509
+ .btn-success { background: linear-gradient(135deg, #10b981, #059669); }
510
+ .btn-success:hover { box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3); }
511
+ .btn-danger { background: linear-gradient(135deg, #ef4444, #dc2626); }
512
+ .btn-danger:hover { box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3); }
487
513
  .pulse { animation: pulse 2s infinite; }
488
- @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
514
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
489
515
  .loading { color: #f59e0b; }
516
+ .tabs { display: flex; gap: 10px; margin-bottom: 20px; }
517
+ .tab { padding: 10px 20px; background: #334155; border-radius: 8px; cursor: pointer; transition: all 0.2s; }
518
+ .tab.active { background: #f97316; color: white; }
519
+ .tab-content { display: none; }
520
+ .tab-content.active { display: block; }
521
+ .agent-node {
522
+ background: #1e293b;
523
+ border: 2px solid #f97316;
524
+ border-radius: 8px;
525
+ padding: 10px;
526
+ margin: 10px;
527
+ text-align: center;
528
+ }
529
+ .performance-bar {
530
+ height: 8px;
531
+ background: #334155;
532
+ border-radius: 4px;
533
+ overflow: hidden;
534
+ margin: 10px 0;
535
+ }
536
+ .performance-fill {
537
+ height: 100%;
538
+ background: linear-gradient(90deg, #10b981, #f97316);
539
+ transition: width 1s ease;
540
+ }
490
541
  </style>
491
542
  </head>
492
543
  <body>
493
- <h1>\u{1F916} OpenQA Real-time Dashboard</h1>
494
- <nav>
495
- <a href="/">Dashboard</a>
496
- <a href="/kanban">Kanban</a>
497
- <a href="/config">Config</a>
498
- <span style="color: #64748b;">|</span>
499
- <span id="connection-status" class="status idle">\u{1F50C} Connecting...</span>
500
- </nav>
544
+ <div class="nav">
545
+ <div class="nav-links">
546
+ <a href="/" class="active">\u{1F4CA} Dashboard</a>
547
+ <a href="/kanban">\u{1F4CB} Kanban</a>
548
+ <a href="/config">\u2699\uFE0F Config</a>
549
+ </div>
550
+ <div>
551
+ <span id="connection-status" class="status idle">\u{1F50C} Connecting...</span>
552
+ </div>
553
+ </div>
501
554
 
502
- <div class="grid-3">
555
+ <!-- Key Metrics -->
556
+ <div class="grid-4">
557
+ <div class="metric-card">
558
+ <div class="metric-label">\u{1F916} Active Agents</div>
559
+ <div class="metric-value" id="active-agents">0</div>
560
+ <div class="metric-change positive">\u2191 2 from last hour</div>
561
+ </div>
562
+ <div class="metric-card">
563
+ <div class="metric-label">\u{1F4CB} Total Actions</div>
564
+ <div class="metric-value" id="total-actions">0</div>
565
+ <div class="metric-change positive">\u2191 12% increase</div>
566
+ </div>
567
+ <div class="metric-card">
568
+ <div class="metric-label">\u{1F41B} Bugs Found</div>
569
+ <div class="metric-value" id="bugs-found">0</div>
570
+ <div class="metric-change negative">\u2193 3 from yesterday</div>
571
+ </div>
572
+ <div class="metric-card">
573
+ <div class="metric-label">\u26A1 Success Rate</div>
574
+ <div class="metric-value" id="success-rate">0%</div>
575
+ <div class="metric-change positive">\u2191 5% improvement</div>
576
+ </div>
577
+ </div>
578
+
579
+ <!-- Charts and Hierarchy -->
580
+ <div class="grid-2">
503
581
  <div class="card">
504
- <h2>\u{1F916} Agent Status</h2>
505
- <div class="metric">
506
- <span class="metric-label">Status</span>
507
- <span id="agent-status" class="status idle">Idle</span>
582
+ <div class="card-header">
583
+ <h2 class="card-title">\u{1F4C8} Performance Metrics</h2>
584
+ </div>
585
+ <div class="tabs">
586
+ <div class="tab active" onclick="switchTab('performance')">Performance</div>
587
+ <div class="tab" onclick="switchTab('activity')">Activity</div>
588
+ <div class="tab" onclick="switchTab('errors')">Error Rate</div>
508
589
  </div>
509
- <div class="metric">
510
- <span class="metric-label">Target</span>
511
- <span id="target-url">${cfg.saas.url || "Not configured"}</span>
590
+ <div class="chart-container">
591
+ <canvas id="performanceChart"></canvas>
512
592
  </div>
513
- <div class="metric">
514
- <span class="metric-label">Active Agents</span>
515
- <span id="active-agents" class="metric-value">0</span>
593
+ <div class="chart-container" style="display: none;">
594
+ <canvas id="activityChart"></canvas>
516
595
  </div>
517
- <div class="metric">
518
- <span class="metric-label">Current Session</span>
519
- <span id="session-id">None</span>
596
+ <div class="chart-container" style="display: none;">
597
+ <canvas id="errorChart"></canvas>
520
598
  </div>
521
599
  </div>
522
600
 
523
601
  <div class="card">
524
- <h2>\u{1F4CA} Session Metrics</h2>
525
- <div class="metric">
526
- <span class="metric-label">Total Actions</span>
527
- <span id="total-actions" class="metric-value">0</span>
602
+ <div class="card-header">
603
+ <h2 class="card-title">\u{1F310} Agent Hierarchy</h2>
528
604
  </div>
529
- <div class="metric">
530
- <span class="metric-label">Bugs Found</span>
531
- <span id="bugs-found" class="metric-value">0</span>
605
+ <div class="hierarchy-container" id="hierarchy-container"></div>
606
+ </div>
607
+ </div>
608
+
609
+ <!-- Agent Details -->
610
+ <div class="card">
611
+ <div class="card-header">
612
+ <h2 class="card-title">\u{1F916} Agent Details</h2>
613
+ </div>
614
+ <div class="tabs">
615
+ <div class="tab active" onclick="switchAgentTab('active')">Active Agents</div>
616
+ <div class="tab" onclick="switchAgentTab('specialists')">Specialists</div>
617
+ <div class="tab" onclick="switchAgentTab('performance')">Performance</div>
618
+ </div>
619
+ <div id="active-agents-content" class="tab-content active">
620
+ <div id="active-agents-list">
621
+ <p style="color: #64748b;">Loading agents...</p>
532
622
  </div>
533
- <div class="metric">
534
- <span class="metric-label">Tests Generated</span>
535
- <span id="tests-generated" class="metric-value">0</span>
623
+ </div>
624
+ <div id="specialists-content" class="tab-content">
625
+ <div id="specialists-list">
626
+ <p style="color: #64748b;">No specialists active</p>
536
627
  </div>
537
- <div class="metric">
538
- <span class="metric-label">Success Rate</span>
539
- <span id="success-rate" class="metric-value">0%</span>
628
+ </div>
629
+ <div id="performance-content" class="tab-content">
630
+ <div id="performance-metrics">
631
+ <p style="color: #64748b;">Performance data loading...</p>
540
632
  </div>
541
633
  </div>
542
-
634
+ </div>
635
+
636
+ <!-- Activity and Interventions -->
637
+ <div class="grid-2">
543
638
  <div class="card">
544
- <h2>\u26A1 Recent Activity</h2>
545
- <div id="recent-activities">
639
+ <div class="card-header">
640
+ <h2 class="card-title">\u26A1 Recent Activity</h2>
641
+ </div>
642
+ <div id="recent-activities" style="max-height: 400px; overflow-y: auto;">
546
643
  <div class="activity-item">
547
644
  <div>\u{1F504} Waiting for agent activity...</div>
548
645
  <div class="activity-time">System ready</div>
549
646
  </div>
550
647
  </div>
551
648
  </div>
649
+
650
+ <div class="card">
651
+ <div class="card-header">
652
+ <h2 class="card-title">\u{1F6A8} Human Interventions</h2>
653
+ </div>
654
+ <div id="interventions-list" style="max-height: 400px; overflow-y: auto;">
655
+ <p style="color: #64748b;">No interventions required</p>
656
+ </div>
657
+ </div>
658
+ </div>
659
+
660
+ <!-- Tasks and Issues -->
661
+ <div class="grid-2">
662
+ <div class="card">
663
+ <div class="card-header">
664
+ <h2 class="card-title">\u{1F4DD} Current Tasks</h2>
665
+ </div>
666
+ <div id="current-tasks" style="max-height: 400px; overflow-y: auto;">
667
+ <p style="color: #64748b;">No active tasks</p>
668
+ </div>
669
+ </div>
670
+
671
+ <div class="card">
672
+ <div class="card-header">
673
+ <h2 class="card-title">\u26A0\uFE0F Issues Encountered</h2>
674
+ </div>
675
+ <div id="issues-list" style="max-height: 400px; overflow-y: auto;">
676
+ <p style="color: #64748b;">No issues</p>
677
+ </div>
678
+ </div>
552
679
  </div>
553
680
 
554
681
  <div class="grid">
@@ -586,7 +713,303 @@ async function startWebServer() {
586
713
  <script>
587
714
  let ws;
588
715
  let activities = [];
716
+ let performanceChart, activityChart, errorChart;
717
+ let hierarchyNetwork;
589
718
 
719
+ // Initialize Charts
720
+ function initCharts() {
721
+ // Performance Chart
722
+ const perfCtx = document.getElementById('performanceChart').getContext('2d');
723
+ performanceChart = new Chart(perfCtx, {
724
+ type: 'line',
725
+ data: {
726
+ labels: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
727
+ datasets: [{
728
+ label: 'Actions/min',
729
+ data: [12, 19, 15, 25, 22, 30, 28],
730
+ borderColor: '#f97316',
731
+ backgroundColor: 'rgba(249, 115, 22, 0.1)',
732
+ tension: 0.4
733
+ }, {
734
+ label: 'Success Rate %',
735
+ data: [85, 88, 82, 91, 87, 93, 89],
736
+ borderColor: '#10b981',
737
+ backgroundColor: 'rgba(16, 185, 129, 0.1)',
738
+ tension: 0.4
739
+ }]
740
+ },
741
+ options: {
742
+ responsive: true,
743
+ maintainAspectRatio: false,
744
+ plugins: {
745
+ legend: { labels: { color: '#e2e8f0' } }
746
+ },
747
+ scales: {
748
+ x: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } },
749
+ y: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } }
750
+ }
751
+ }
752
+ });
753
+
754
+ // Activity Chart
755
+ const actCtx = document.getElementById('activityChart').getContext('2d');
756
+ activityChart = new Chart(actCtx, {
757
+ type: 'bar',
758
+ data: {
759
+ labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
760
+ datasets: [{
761
+ label: 'Tests Generated',
762
+ data: [65, 78, 90, 81, 56, 45, 30],
763
+ backgroundColor: '#f97316'
764
+ }, {
765
+ label: 'Bugs Found',
766
+ data: [12, 19, 15, 25, 22, 15, 8],
767
+ backgroundColor: '#ef4444'
768
+ }]
769
+ },
770
+ options: {
771
+ responsive: true,
772
+ maintainAspectRatio: false,
773
+ plugins: {
774
+ legend: { labels: { color: '#e2e8f0' } }
775
+ },
776
+ scales: {
777
+ x: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } },
778
+ y: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } }
779
+ }
780
+ }
781
+ });
782
+
783
+ // Error Rate Chart
784
+ const errCtx = document.getElementById('errorChart').getContext('2d');
785
+ errorChart = new Chart(errCtx, {
786
+ type: 'doughnut',
787
+ data: {
788
+ labels: ['Success', 'Warnings', 'Errors', 'Critical'],
789
+ datasets: [{
790
+ data: [75, 15, 8, 2],
791
+ backgroundColor: ['#10b981', '#f97316', '#ef4444', '#dc2626']
792
+ }]
793
+ },
794
+ options: {
795
+ responsive: true,
796
+ maintainAspectRatio: false,
797
+ plugins: {
798
+ legend: { labels: { color: '#e2e8f0' } }
799
+ }
800
+ }
801
+ });
802
+ }
803
+
804
+ // Initialize Agent Hierarchy with React Flow
805
+ function initHierarchy() {
806
+ const container = document.getElementById('hierarchy-container');
807
+
808
+ // Create React Flow nodes
809
+ const nodes = [
810
+ {
811
+ id: 'main',
812
+ type: 'default',
813
+ position: { x: 400, y: 50 },
814
+ data: { label: '\u{1F916} Main Agent' },
815
+ style: {
816
+ background: '#f97316',
817
+ color: 'white',
818
+ border: '2px solid #ea580c',
819
+ borderRadius: '8px',
820
+ padding: '10px 20px',
821
+ fontSize: '14px',
822
+ fontWeight: 'bold'
823
+ }
824
+ },
825
+ {
826
+ id: 'browser',
827
+ type: 'default',
828
+ position: { x: 150, y: 200 },
829
+ data: { label: '\u{1F310} Browser Specialist' },
830
+ style: {
831
+ background: '#10b981',
832
+ color: 'white',
833
+ border: '2px solid #059669',
834
+ borderRadius: '8px',
835
+ padding: '8px 16px',
836
+ fontSize: '13px'
837
+ }
838
+ },
839
+ {
840
+ id: 'api',
841
+ type: 'default',
842
+ position: { x: 400, y: 200 },
843
+ data: { label: '\u{1F50C} API Tester' },
844
+ style: {
845
+ background: '#f59e0b',
846
+ color: 'white',
847
+ border: '2px solid #d97706',
848
+ borderRadius: '8px',
849
+ padding: '8px 16px',
850
+ fontSize: '13px'
851
+ }
852
+ },
853
+ {
854
+ id: 'auth',
855
+ type: 'default',
856
+ position: { x: 650, y: 200 },
857
+ data: { label: '\u{1F510} Auth Specialist' },
858
+ style: {
859
+ background: '#ef4444',
860
+ color: 'white',
861
+ border: '2px solid #dc2626',
862
+ borderRadius: '8px',
863
+ padding: '8px 16px',
864
+ fontSize: '13px'
865
+ }
866
+ },
867
+ {
868
+ id: 'ui',
869
+ type: 'default',
870
+ position: { x: 150, y: 350 },
871
+ data: { label: '\u{1F3A8} UI Tester' },
872
+ style: {
873
+ background: '#8b5cf6',
874
+ color: 'white',
875
+ border: '2px solid #7c3aed',
876
+ borderRadius: '8px',
877
+ padding: '8px 16px',
878
+ fontSize: '13px'
879
+ }
880
+ },
881
+ {
882
+ id: 'perf',
883
+ type: 'default',
884
+ position: { x: 400, y: 350 },
885
+ data: { label: '\u26A1 Performance Tester' },
886
+ style: {
887
+ background: '#06b6d4',
888
+ color: 'white',
889
+ border: '2px solid #0891b2',
890
+ borderRadius: '8px',
891
+ padding: '8px 16px',
892
+ fontSize: '13px'
893
+ }
894
+ },
895
+ {
896
+ id: 'security',
897
+ type: 'default',
898
+ position: { x: 650, y: 350 },
899
+ data: { label: '\u{1F6E1}\uFE0F Security Scanner' },
900
+ style: {
901
+ background: '#f97316',
902
+ color: 'white',
903
+ border: '2px solid #ea580c',
904
+ borderRadius: '8px',
905
+ padding: '8px 16px',
906
+ fontSize: '13px'
907
+ }
908
+ }
909
+ ];
910
+
911
+ const edges = [
912
+ { id: 'e1', source: 'main', target: 'browser', animated: true, style: { stroke: '#334155', strokeWidth: 2 } },
913
+ { id: 'e2', source: 'main', target: 'api', animated: true, style: { stroke: '#334155', strokeWidth: 2 } },
914
+ { id: 'e3', source: 'main', target: 'auth', animated: true, style: { stroke: '#334155', strokeWidth: 2 } },
915
+ { id: 'e4', source: 'browser', target: 'ui', animated: true, style: { stroke: '#334155', strokeWidth: 2 } },
916
+ { id: 'e5', source: 'api', target: 'perf', animated: true, style: { stroke: '#334155', strokeWidth: 2 } },
917
+ { id: 'e6', source: 'auth', target: 'security', animated: true, style: { stroke: '#334155', strokeWidth: 2 } }
918
+ ];
919
+
920
+ // Simple React Flow implementation without React dependency
921
+ container.innerHTML = \`
922
+ <svg width="100%" height="100%" style="background: #0f172a; border-radius: 8px;">
923
+ <!-- Edges -->
924
+ \${edges.map(edge => {
925
+ const sourceNode = nodes.find(n => n.id === edge.source);
926
+ const targetNode = nodes.find(n => n.id === edge.target);
927
+ return \`
928
+ <line
929
+ x1="\${sourceNode.position.x + 60}"
930
+ y1="\${sourceNode.position.y + 20}"
931
+ x2="\${targetNode.position.x + 60}"
932
+ y2="\${targetNode.position.y + 20}"
933
+ stroke="\${edge.style.stroke}"
934
+ stroke-width="\${edge.style.strokeWidth}"
935
+ stroke-dasharray="5,5"
936
+ opacity="0.6">
937
+ <animate attributeName="stroke-dashoffset" values="0;10" dur="1s" repeatCount="indefinite"/>
938
+ </line>
939
+ \`;
940
+ }).join('')}
941
+
942
+ <!-- Nodes -->
943
+ \${nodes.map(node => \`
944
+ <g transform="translate(\${node.position.x}, \${node.position.y})">
945
+ <rect
946
+ x="0" y="0" width="120" height="40"
947
+ fill="\${node.style.background}"
948
+ stroke="\${node.style.border}"
949
+ stroke-width="\${node.style.border.split(' ')[0].replace('px', '')}"
950
+ rx="8" ry="8"
951
+ style="filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.3))"/>
952
+ <text
953
+ x="60" y="25"
954
+ text-anchor="middle"
955
+ fill="\${node.style.color}"
956
+ font-size="\${node.style.fontSize}"
957
+ font-weight="bold">
958
+ \${node.data.label}
959
+ </text>
960
+ </g>
961
+ \`).join('')}
962
+ </svg>
963
+ \`;
964
+ }
965
+
966
+ // Tab switching
967
+ function switchTab(tabName) {
968
+ // Hide all chart containers
969
+ document.querySelectorAll('.chart-container').forEach(container => {
970
+ container.style.display = 'none';
971
+ });
972
+
973
+ // Remove active class from all tabs
974
+ document.querySelectorAll('.tab').forEach(tab => {
975
+ tab.classList.remove('active');
976
+ });
977
+
978
+ // Show selected chart and activate tab
979
+ if (tabName === 'performance') {
980
+ document.querySelectorAll('.chart-container')[0].style.display = 'block';
981
+ document.querySelectorAll('.tab')[0].classList.add('active');
982
+ } else if (tabName === 'activity') {
983
+ document.querySelectorAll('.chart-container')[1].style.display = 'block';
984
+ document.querySelectorAll('.tab')[1].classList.add('active');
985
+ } else if (tabName === 'errors') {
986
+ document.querySelectorAll('.chart-container')[2].style.display = 'block';
987
+ document.querySelectorAll('.tab')[2].classList.add('active');
988
+ }
989
+ }
990
+
991
+ // Agent tab switching
992
+ function switchAgentTab(tabName) {
993
+ // Hide all tab contents
994
+ document.querySelectorAll('.tab-content').forEach(content => {
995
+ content.classList.remove('active');
996
+ });
997
+
998
+ // Remove active class from all tabs
999
+ document.querySelectorAll('.tab').forEach(tab => {
1000
+ tab.classList.remove('active');
1001
+ });
1002
+
1003
+ // Show selected content
1004
+ if (tabName === 'active') {
1005
+ document.getElementById('active-agents-content').classList.add('active');
1006
+ } else if (tabName === 'specialists') {
1007
+ document.getElementById('specialists-content').classList.add('active');
1008
+ } else if (tabName === 'performance') {
1009
+ document.getElementById('performance-content').classList.add('active');
1010
+ }
1011
+ }
1012
+
590
1013
  function connectWebSocket() {
591
1014
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
592
1015
  ws = new WebSocket(\`\${protocol}//\${window.location.host}\`);
@@ -706,18 +1129,36 @@ async function startWebServer() {
706
1129
  // Start WebSocket connection
707
1130
  connectWebSocket();
708
1131
 
709
- // Load initial data
710
- fetch('/api/status').then(r => r.json()).then(updateAgentStatus);
711
- fetch('/api/sessions?limit=1').then(r => r.json()).then(sessions => {
712
- if (sessions.length > 0) updateSessionMetrics(sessions[0]);
713
- });
714
-
715
- // Load current tasks
716
- fetch('/api/tasks').then(r => r.json()).then(updateCurrentTasks);
717
-
718
- // Load issues
719
- fetch('/api/issues').then(r => r.json()).then(updateIssues);
720
-
1132
+ // Enhanced update functions with professional UI
1133
+ function updateActiveAgents(agents) {
1134
+ const countEl = document.getElementById('active-agents');
1135
+ const listEl = document.getElementById('active-agents-list');
1136
+
1137
+ countEl.textContent = agents.length;
1138
+
1139
+ if (agents.length === 0) {
1140
+ listEl.innerHTML = '<p style="color: #64748b;">No active agents</p>';
1141
+ } else {
1142
+ listEl.innerHTML = agents.map(agent => \`
1143
+ <div class="activity-item">
1144
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1145
+ <div>
1146
+ <strong>\${agent.name}</strong>
1147
+ <span class="status \${agent.status.toLowerCase()}">\${agent.status}</span>
1148
+ </div>
1149
+ <div style="text-align: right;">
1150
+ <div class="performance-bar" style="width: 100px;">
1151
+ <div class="performance-fill" style="width: \${agent.performance || 75}%"></div>
1152
+ </div>
1153
+ <small style="color: #64748b;">\${agent.performance || 75}%</small>
1154
+ </div>
1155
+ </div>
1156
+ <div class="activity-time">Purpose: \${agent.purpose} | Tasks: \${agent.tasks || 0}</div>
1157
+ </div>
1158
+ \`).join('');
1159
+ }
1160
+ }
1161
+
721
1162
  function updateCurrentTasks(tasks) {
722
1163
  const container = document.getElementById('current-tasks');
723
1164
  if (tasks.length === 0) {
@@ -725,9 +1166,15 @@ async function startWebServer() {
725
1166
  } else {
726
1167
  container.innerHTML = tasks.map(task => \`
727
1168
  <div class="activity-item">
728
- <div><strong>\${task.name}</strong> - \${task.status}</div>
729
- <div class="activity-time">Agent: \${task.agent} | \${task.progress || ''}</div>
730
- \${task.result ? \`<div style="color: #10b981; margin-top: 4px;">\${task.result}</div>\` : ''}
1169
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1170
+ <div>
1171
+ <strong>\${task.name}</strong>
1172
+ <span class="status \${task.status.replace(' ', '-')}">\${task.status}</span>
1173
+ </div>
1174
+ \${task.progress ? \`<div style="color: #f97316;">\${task.progress}</div>\` : ''}
1175
+ </div>
1176
+ <div class="activity-time">Agent: \${task.agent} | Started: \${new Date(task.started_at).toLocaleTimeString()}</div>
1177
+ \${task.result ? \`<div style="color: #10b981; margin-top: 8px; padding: 8px; background: rgba(16, 185, 129, 0.1); border-radius: 6px;">\${task.result}</div>\` : ''}
731
1178
  </div>
732
1179
  \`).join('');
733
1180
  }
@@ -742,12 +1189,95 @@ async function startWebServer() {
742
1189
  } else {
743
1190
  container.innerHTML = issues.map(issue => \`
744
1191
  <div class="activity-item \${issue.severity}">
745
- <div><strong>\${issue.type}</strong> - \${issue.message}</div>
746
- <div class="activity-time">Agent: \${issue.agent} | Status: \${issue.status}</div>
1192
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1193
+ <div>
1194
+ <strong>\${issue.type}</strong> - \${issue.message}
1195
+ </div>
1196
+ <span class="status \${issue.status}">\${issue.status}</span>
1197
+ </div>
1198
+ <div class="activity-time">Agent: \${issue.agent} | \${new Date(issue.timestamp).toLocaleTimeString()}</div>
747
1199
  </div>
748
1200
  \`).join('');
749
1201
  }
750
1202
  }
1203
+
1204
+ // Update specialists tab
1205
+ function updateSpecialists() {
1206
+ const specialists = [
1207
+ { name: 'Browser Specialist', status: 'active', performance: 85, tasks: 12 },
1208
+ { name: 'API Tester', status: 'active', performance: 92, tasks: 8 },
1209
+ { name: 'Auth Specialist', status: 'idle', performance: 78, tasks: 0 },
1210
+ { name: 'UI Tester', status: 'active', performance: 88, tasks: 15 },
1211
+ { name: 'Performance Tester', status: 'idle', performance: 95, tasks: 0 }
1212
+ ];
1213
+
1214
+ const container = document.getElementById('specialists-list');
1215
+ container.innerHTML = specialists.map(specialist => \`
1216
+ <div class="activity-item">
1217
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1218
+ <div>
1219
+ <strong>\${specialist.name}</strong>
1220
+ <span class="status \${specialist.status}">\${specialist.status}</span>
1221
+ </div>
1222
+ <div style="text-align: right;">
1223
+ <div class="performance-bar" style="width: 100px;">
1224
+ <div class="performance-fill" style="width: \${specialist.performance}%"></div>
1225
+ </div>
1226
+ <small style="color: #64748b;">\${specialist.performance}%</small>
1227
+ </div>
1228
+ </div>
1229
+ <div class="activity-time">Tasks completed: \${specialist.tasks}</div>
1230
+ </div>
1231
+ \`).join('');
1232
+ }
1233
+
1234
+ // Update performance metrics
1235
+ function updatePerformanceMetrics() {
1236
+ const container = document.getElementById('performance-metrics');
1237
+ container.innerHTML = \`
1238
+ <div class="grid-2">
1239
+ <div class="metric-card">
1240
+ <div class="metric-label">\u{1F680} Avg Response Time</div>
1241
+ <div class="metric-value">245ms</div>
1242
+ <div class="metric-change positive">\u2193 12% faster</div>
1243
+ </div>
1244
+ <div class="metric-card">
1245
+ <div class="metric-label">\u{1F4BE} Memory Usage</div>
1246
+ <div class="metric-value">128MB</div>
1247
+ <div class="metric-change positive">\u2193 8% optimized</div>
1248
+ </div>
1249
+ <div class="metric-card">
1250
+ <div class="metric-label">\u26A1 CPU Usage</div>
1251
+ <div class="metric-value">34%</div>
1252
+ <div class="metric-change negative">\u2191 5% increase</div>
1253
+ </div>
1254
+ <div class="metric-card">
1255
+ <div class="metric-label">\u{1F504} Throughput</div>
1256
+ <div class="metric-value">1.2k/h</div>
1257
+ <div class="metric-change positive">\u2191 18% increase</div>
1258
+ </div>
1259
+ </div>
1260
+ \`;
1261
+ }
1262
+
1263
+ // Initialize everything on load
1264
+ window.addEventListener('load', () => {
1265
+ initCharts();
1266
+ initHierarchy();
1267
+ updateSpecialists();
1268
+ updatePerformanceMetrics();
1269
+
1270
+ // Load initial data
1271
+ fetch('/api/status').then(r => r.json()).then(updateAgentStatus);
1272
+ fetch('/api/sessions?limit=1').then(r => r.json()).then(sessions => {
1273
+ if (sessions.length > 0) updateSessionMetrics(sessions[0]);
1274
+ });
1275
+ fetch('/api/tasks').then(r => r.json()).then(updateCurrentTasks);
1276
+ fetch('/api/issues').then(r => r.json()).then(updateIssues);
1277
+ });
1278
+
1279
+ // Start WebSocket connection
1280
+ connectWebSocket();
751
1281
  </script>
752
1282
  </body>
753
1283
  </html>