@dadado/agent-kit-cli 4.8.2 → 4.8.4
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/dashboard/dashboard.html +459 -77
- package/dashboard/lib/guards.mjs +19 -1
- package/dashboard/lib/semantic-model.mjs +38 -23
- package/dashboard/lib/triage-heading.mjs +20 -0
- package/dashboard/start-broadcast.mjs +23 -23
- package/dashboard/start.mjs +22 -28
- package/dist/index.js +176 -24
- package/package.json +1 -1
package/dashboard/dashboard.html
CHANGED
|
@@ -503,6 +503,9 @@ a:focus-visible,
|
|
|
503
503
|
background: var(--border);
|
|
504
504
|
}
|
|
505
505
|
.nav-more-group-label {
|
|
506
|
+
display: flex;
|
|
507
|
+
align-items: center;
|
|
508
|
+
gap: var(--mc-chrome-label-gap);
|
|
506
509
|
padding: 6px 10px 2px;
|
|
507
510
|
font-size: var(--mc-chrome-subtitle-size);
|
|
508
511
|
font-weight: var(--mc-chrome-subtitle-weight);
|
|
@@ -800,25 +803,122 @@ a:focus-visible,
|
|
|
800
803
|
}
|
|
801
804
|
.terminal-item:hover { background: var(--bg-card-hover); }
|
|
802
805
|
|
|
803
|
-
/* =====
|
|
806
|
+
/* ===== Healthcenter ===== */
|
|
807
|
+
.healthcenter {
|
|
808
|
+
display: flex;
|
|
809
|
+
flex-direction: column;
|
|
810
|
+
gap: 10px;
|
|
811
|
+
}
|
|
812
|
+
.healthcenter-presence {
|
|
813
|
+
display: flex;
|
|
814
|
+
align-items: center;
|
|
815
|
+
gap: 8px;
|
|
816
|
+
font-size: 11px;
|
|
817
|
+
color: var(--text-muted);
|
|
818
|
+
}
|
|
819
|
+
.healthcenter-presence .dot-pulse {
|
|
820
|
+
animation: pulse 1.6s ease-in-out infinite;
|
|
821
|
+
}
|
|
804
822
|
.health-grid {
|
|
805
823
|
display: grid;
|
|
806
|
-
grid-template-columns: repeat(auto-fill, minmax(
|
|
824
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
807
825
|
gap: 8px;
|
|
808
826
|
}
|
|
827
|
+
.health-card {
|
|
828
|
+
display: flex;
|
|
829
|
+
flex-direction: column;
|
|
830
|
+
gap: 0;
|
|
831
|
+
border-radius: var(--mc-radius-sm, 6px);
|
|
832
|
+
background: var(--bg-card);
|
|
833
|
+
border: 1px solid var(--border);
|
|
834
|
+
overflow: hidden;
|
|
835
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
836
|
+
}
|
|
837
|
+
.health-card:hover,
|
|
838
|
+
.health-card.is-expanded {
|
|
839
|
+
border-color: var(--border-active);
|
|
840
|
+
}
|
|
841
|
+
.health-card[data-severity="ok"] { border-left: 3px solid var(--green, #3fb950); }
|
|
842
|
+
.health-card[data-severity="warning"] { border-left: 3px solid var(--yellow, #d29922); }
|
|
843
|
+
.health-card[data-severity="degraded"] { border-left: 3px solid var(--orange, #db6d28); }
|
|
844
|
+
.health-card[data-severity="error"] { border-left: 3px solid var(--red, #f85149); }
|
|
809
845
|
.health-item {
|
|
810
846
|
display: flex;
|
|
811
847
|
align-items: center;
|
|
812
848
|
gap: 8px;
|
|
813
|
-
padding:
|
|
814
|
-
border-radius: 6px;
|
|
815
|
-
background: var(--bg-card);
|
|
816
|
-
border: 1px solid var(--border);
|
|
849
|
+
padding: 10px 12px;
|
|
817
850
|
font-size: 12px;
|
|
818
851
|
color: var(--text-secondary);
|
|
819
|
-
|
|
852
|
+
cursor: pointer;
|
|
853
|
+
background: transparent;
|
|
854
|
+
border: none;
|
|
855
|
+
width: 100%;
|
|
856
|
+
text-align: left;
|
|
857
|
+
font-family: inherit;
|
|
858
|
+
transition: opacity 0.3s ease, transform 0.3s ease, background 0.15s ease;
|
|
859
|
+
}
|
|
860
|
+
.health-item:hover { background: var(--bg-card-hover); }
|
|
861
|
+
.health-item-label {
|
|
862
|
+
flex: 1;
|
|
863
|
+
min-width: 0;
|
|
864
|
+
color: var(--text-primary);
|
|
865
|
+
}
|
|
866
|
+
.health-item-sev {
|
|
867
|
+
font-size: 10px;
|
|
868
|
+
text-transform: uppercase;
|
|
869
|
+
letter-spacing: 0.04em;
|
|
870
|
+
color: var(--text-muted);
|
|
871
|
+
flex-shrink: 0;
|
|
872
|
+
}
|
|
873
|
+
.health-item-chevron {
|
|
874
|
+
color: var(--text-muted);
|
|
875
|
+
font-size: 10px;
|
|
876
|
+
transition: transform 0.15s ease;
|
|
877
|
+
flex-shrink: 0;
|
|
878
|
+
}
|
|
879
|
+
.health-card.is-expanded .health-item-chevron {
|
|
880
|
+
transform: rotate(180deg);
|
|
881
|
+
}
|
|
882
|
+
.health-detail {
|
|
883
|
+
display: none;
|
|
884
|
+
padding: 0 12px 12px;
|
|
885
|
+
border-top: 1px solid var(--border);
|
|
886
|
+
background: var(--bg-secondary);
|
|
887
|
+
}
|
|
888
|
+
.health-card.is-expanded .health-detail {
|
|
889
|
+
display: block;
|
|
890
|
+
animation: fadeIn 0.2s ease;
|
|
891
|
+
}
|
|
892
|
+
.health-detail-copy {
|
|
893
|
+
font-size: 11px;
|
|
894
|
+
color: var(--text-secondary);
|
|
895
|
+
margin: 10px 0 8px;
|
|
896
|
+
line-height: 1.4;
|
|
897
|
+
}
|
|
898
|
+
.health-detail-actions {
|
|
899
|
+
display: flex;
|
|
900
|
+
flex-wrap: wrap;
|
|
901
|
+
gap: 6px;
|
|
902
|
+
}
|
|
903
|
+
.health-autofix-btn {
|
|
904
|
+
font-size: 11px;
|
|
905
|
+
padding: 4px 10px;
|
|
906
|
+
border-radius: 4px;
|
|
907
|
+
border: 1px solid var(--border-active);
|
|
908
|
+
background: var(--bg-card);
|
|
909
|
+
color: var(--text-primary);
|
|
910
|
+
cursor: pointer;
|
|
911
|
+
font-family: inherit;
|
|
912
|
+
}
|
|
913
|
+
.health-autofix-btn:hover {
|
|
914
|
+
background: var(--bg-card-hover);
|
|
915
|
+
}
|
|
916
|
+
html[data-dashboard-skin="cursor"] .health-card {
|
|
917
|
+
border-radius: 8px;
|
|
918
|
+
}
|
|
919
|
+
html[data-dashboard-skin="legacy"] .health-card {
|
|
920
|
+
border-radius: 4px;
|
|
820
921
|
}
|
|
821
|
-
.health-item:hover { border-color: var(--border-active); }
|
|
822
922
|
|
|
823
923
|
/* ===== Git ===== */
|
|
824
924
|
.git-info {
|
|
@@ -1448,7 +1548,7 @@ a:focus-visible,
|
|
|
1448
1548
|
vertical-align: middle;
|
|
1449
1549
|
}
|
|
1450
1550
|
|
|
1451
|
-
/* ===== Health Investigation Message ===== */
|
|
1551
|
+
/* ===== Health Investigation Message (legacy; Healthcenter uses .health-detail) ===== */
|
|
1452
1552
|
.health-message {
|
|
1453
1553
|
font-size: 11px;
|
|
1454
1554
|
color: var(--text-secondary);
|
|
@@ -2031,8 +2131,8 @@ a:focus-visible,
|
|
|
2031
2131
|
grid-template-columns: 1fr 1fr;
|
|
2032
2132
|
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
|
2033
2133
|
grid-template-areas:
|
|
2034
|
-
"now
|
|
2035
|
-
"
|
|
2134
|
+
"now attention"
|
|
2135
|
+
"checklist monitor";
|
|
2036
2136
|
}
|
|
2037
2137
|
|
|
2038
2138
|
#section-overview.active #now-execution-panel,
|
|
@@ -2662,10 +2762,10 @@ a:focus-visible,
|
|
|
2662
2762
|
font: inherit;
|
|
2663
2763
|
line-height: 1.4;
|
|
2664
2764
|
}
|
|
2765
|
+
/* Base hover/focus: keyboard-visible ring; kind rules below set colors (no unset --accent → yellow). */
|
|
2665
2766
|
.flight-log-card:hover,
|
|
2666
2767
|
.flight-log-card:focus-visible {
|
|
2667
|
-
|
|
2668
|
-
outline: 2px solid var(--accent, var(--yellow));
|
|
2768
|
+
outline: 2px solid var(--border);
|
|
2669
2769
|
outline-offset: 1px;
|
|
2670
2770
|
}
|
|
2671
2771
|
.flight-log-card-current {
|
|
@@ -2679,6 +2779,11 @@ a:focus-visible,
|
|
|
2679
2779
|
.flight-log-card-current.flight-log-kind-residual .flight-log-card-label {
|
|
2680
2780
|
color: var(--yellow);
|
|
2681
2781
|
}
|
|
2782
|
+
.flight-log-card-current.flight-log-kind-residual:hover,
|
|
2783
|
+
.flight-log-card-current.flight-log-kind-residual:focus-visible {
|
|
2784
|
+
border-color: var(--yellow);
|
|
2785
|
+
outline-color: var(--yellow);
|
|
2786
|
+
}
|
|
2682
2787
|
.flight-log-card-current.flight-log-kind-advice {
|
|
2683
2788
|
border-color: var(--blue);
|
|
2684
2789
|
background: var(--blue-bg);
|
|
@@ -2686,6 +2791,11 @@ a:focus-visible,
|
|
|
2686
2791
|
.flight-log-card-current.flight-log-kind-advice .flight-log-card-label {
|
|
2687
2792
|
color: var(--blue);
|
|
2688
2793
|
}
|
|
2794
|
+
.flight-log-card-current.flight-log-kind-advice:hover,
|
|
2795
|
+
.flight-log-card-current.flight-log-kind-advice:focus-visible {
|
|
2796
|
+
border-color: var(--blue);
|
|
2797
|
+
outline-color: var(--blue);
|
|
2798
|
+
}
|
|
2689
2799
|
.flight-log-card-current.flight-log-kind-ok {
|
|
2690
2800
|
border-color: color-mix(in srgb, var(--green) 55%, var(--border));
|
|
2691
2801
|
background: color-mix(in srgb, var(--green-bg) 40%, var(--bg-elevated, var(--bg-card)));
|
|
@@ -2693,6 +2803,11 @@ a:focus-visible,
|
|
|
2693
2803
|
.flight-log-card-current.flight-log-kind-ok .flight-log-card-label {
|
|
2694
2804
|
color: var(--green);
|
|
2695
2805
|
}
|
|
2806
|
+
.flight-log-card-current.flight-log-kind-ok:hover,
|
|
2807
|
+
.flight-log-card-current.flight-log-kind-ok:focus-visible {
|
|
2808
|
+
border-color: var(--green);
|
|
2809
|
+
outline-color: var(--green);
|
|
2810
|
+
}
|
|
2696
2811
|
.flight-log-card-current.flight-log-kind-warning {
|
|
2697
2812
|
border-color: color-mix(in srgb, var(--orange, var(--yellow)) 70%, var(--border));
|
|
2698
2813
|
background: color-mix(in srgb, var(--orange-bg, var(--yellow-bg)) 55%, var(--bg-elevated, var(--bg-card)));
|
|
@@ -2700,6 +2815,11 @@ a:focus-visible,
|
|
|
2700
2815
|
.flight-log-card-current.flight-log-kind-warning .flight-log-card-label {
|
|
2701
2816
|
color: var(--orange, var(--yellow));
|
|
2702
2817
|
}
|
|
2818
|
+
.flight-log-card-current.flight-log-kind-warning:hover,
|
|
2819
|
+
.flight-log-card-current.flight-log-kind-warning:focus-visible {
|
|
2820
|
+
border-color: var(--orange, var(--yellow));
|
|
2821
|
+
outline-color: var(--orange, var(--yellow));
|
|
2822
|
+
}
|
|
2703
2823
|
.flight-log-card-past {
|
|
2704
2824
|
padding: 8px 10px;
|
|
2705
2825
|
font-size: 11px;
|
|
@@ -2709,15 +2829,35 @@ a:focus-visible,
|
|
|
2709
2829
|
.flight-log-card-past.flight-log-kind-residual {
|
|
2710
2830
|
border-color: color-mix(in srgb, var(--yellow) 35%, var(--border));
|
|
2711
2831
|
}
|
|
2832
|
+
.flight-log-card-past.flight-log-kind-residual:hover,
|
|
2833
|
+
.flight-log-card-past.flight-log-kind-residual:focus-visible {
|
|
2834
|
+
border-color: color-mix(in srgb, var(--yellow) 55%, var(--border));
|
|
2835
|
+
outline-color: color-mix(in srgb, var(--yellow) 55%, var(--border));
|
|
2836
|
+
}
|
|
2712
2837
|
.flight-log-card-past.flight-log-kind-advice {
|
|
2713
2838
|
border-color: color-mix(in srgb, var(--blue) 35%, var(--border));
|
|
2714
2839
|
}
|
|
2840
|
+
.flight-log-card-past.flight-log-kind-advice:hover,
|
|
2841
|
+
.flight-log-card-past.flight-log-kind-advice:focus-visible {
|
|
2842
|
+
border-color: color-mix(in srgb, var(--blue) 55%, var(--border));
|
|
2843
|
+
outline-color: color-mix(in srgb, var(--blue) 55%, var(--border));
|
|
2844
|
+
}
|
|
2715
2845
|
.flight-log-card-past.flight-log-kind-ok {
|
|
2716
2846
|
border-color: color-mix(in srgb, var(--green) 30%, var(--border));
|
|
2717
2847
|
}
|
|
2848
|
+
.flight-log-card-past.flight-log-kind-ok:hover,
|
|
2849
|
+
.flight-log-card-past.flight-log-kind-ok:focus-visible {
|
|
2850
|
+
border-color: color-mix(in srgb, var(--green) 50%, var(--border));
|
|
2851
|
+
outline-color: color-mix(in srgb, var(--green) 50%, var(--border));
|
|
2852
|
+
}
|
|
2718
2853
|
.flight-log-card-past.flight-log-kind-warning {
|
|
2719
2854
|
border-color: color-mix(in srgb, var(--orange, var(--yellow)) 30%, var(--border));
|
|
2720
2855
|
}
|
|
2856
|
+
.flight-log-card-past.flight-log-kind-warning:hover,
|
|
2857
|
+
.flight-log-card-past.flight-log-kind-warning:focus-visible {
|
|
2858
|
+
border-color: color-mix(in srgb, var(--orange, var(--yellow)) 50%, var(--border));
|
|
2859
|
+
outline-color: color-mix(in srgb, var(--orange, var(--yellow)) 50%, var(--border));
|
|
2860
|
+
}
|
|
2721
2861
|
.flight-log-card-warning {
|
|
2722
2862
|
padding: 10px 12px;
|
|
2723
2863
|
border-color: color-mix(in srgb, var(--orange, var(--yellow)) 70%, var(--border));
|
|
@@ -2728,6 +2868,11 @@ a:focus-visible,
|
|
|
2728
2868
|
.flight-log-card-warning .flight-log-card-label {
|
|
2729
2869
|
color: var(--orange, var(--yellow));
|
|
2730
2870
|
}
|
|
2871
|
+
.flight-log-card-warning:hover,
|
|
2872
|
+
.flight-log-card-warning:focus-visible {
|
|
2873
|
+
border-color: var(--orange, var(--yellow));
|
|
2874
|
+
outline-color: var(--orange, var(--yellow));
|
|
2875
|
+
}
|
|
2731
2876
|
.flight-log-card-label {
|
|
2732
2877
|
display: block;
|
|
2733
2878
|
font-weight: 600;
|
|
@@ -2894,7 +3039,7 @@ a:focus-visible,
|
|
|
2894
3039
|
</a>
|
|
2895
3040
|
<div class="nav-more-separator" role="separator"></div>
|
|
2896
3041
|
<div class="nav-more-group" role="group" aria-labelledby="navSkinsLabel">
|
|
2897
|
-
<div class="nav-more-group-label" id="navSkinsLabel">Skins</div>
|
|
3042
|
+
<div class="nav-more-group-label" id="navSkinsLabel" data-icon="skins">Skins</div>
|
|
2898
3043
|
<button type="button" class="nav-more-item" role="menuitemradio" aria-checked="true" data-dashboard-skin-option="legacy" tabindex="0" onclick="return setDashboardSkin('legacy')">
|
|
2899
3044
|
Legacy
|
|
2900
3045
|
</button>
|
|
@@ -2914,18 +3059,18 @@ a:focus-visible,
|
|
|
2914
3059
|
<div class="top-tabs-row">
|
|
2915
3060
|
<div class="cockpit-anchors" role="navigation" aria-label="Cockpit sections">
|
|
2916
3061
|
<a class="top-nav-anchor active" href="#now-execution-panel" data-anchor="now-execution-panel" data-icon="current-mission" onclick="return goCockpitAnchor('now-execution-panel')" aria-current="true">
|
|
2917
|
-
<span class="top-nav-anchor-label">
|
|
3062
|
+
<span class="top-nav-anchor-label">Current mission</span>
|
|
2918
3063
|
<span class="top-tab-badge" id="navOverviewBadge">0</span>
|
|
2919
3064
|
</a>
|
|
2920
|
-
<a class="top-nav-anchor" href="#hero-activity" data-anchor="hero-activity" data-icon="monitor" onclick="return goCockpitAnchor('hero-activity')">
|
|
2921
|
-
<span class="top-nav-anchor-label">Crew Monitor</span>
|
|
2922
|
-
</a>
|
|
2923
3065
|
<a class="top-nav-anchor" href="#attention-panel" data-anchor="attention-panel" data-icon="field-report" onclick="return goCockpitAnchor('attention-panel')">
|
|
2924
3066
|
<span class="top-nav-anchor-label">Flight Log</span>
|
|
2925
3067
|
</a>
|
|
2926
3068
|
<a class="top-nav-anchor" href="#recent-plans-panel" data-anchor="recent-plans-panel" data-icon="checklist" onclick="return goCockpitAnchor('recent-plans-panel')">
|
|
2927
3069
|
<span class="top-nav-anchor-label">Checklist</span>
|
|
2928
3070
|
</a>
|
|
3071
|
+
<a class="top-nav-anchor" href="#hero-activity" data-anchor="hero-activity" data-icon="monitor" onclick="return goCockpitAnchor('hero-activity')">
|
|
3072
|
+
<span class="top-nav-anchor-label">Crew Monitor</span>
|
|
3073
|
+
</a>
|
|
2929
3074
|
</div>
|
|
2930
3075
|
</div>
|
|
2931
3076
|
</nav>
|
|
@@ -3295,6 +3440,11 @@ function injectCockpitNavIcons() {
|
|
|
3295
3440
|
if (!kind) return;
|
|
3296
3441
|
el.insertAdjacentHTML('afterbegin', spaceIconSvg(kind, { decorative: true }));
|
|
3297
3442
|
});
|
|
3443
|
+
// Skins group label: palette glyph (distinct from Skills gear).
|
|
3444
|
+
const skinsLabel = document.getElementById('navSkinsLabel');
|
|
3445
|
+
if (skinsLabel && !skinsLabel.querySelector('.space-icon')) {
|
|
3446
|
+
skinsLabel.insertAdjacentHTML('afterbegin', spaceIconSvg('skins', { decorative: true }));
|
|
3447
|
+
}
|
|
3298
3448
|
}
|
|
3299
3449
|
|
|
3300
3450
|
// ===== Cockpit nav + more-menu keyboard / outside click =====
|
|
@@ -4481,9 +4631,85 @@ function toggleAgentDetails(id) {
|
|
|
4481
4631
|
}
|
|
4482
4632
|
}
|
|
4483
4633
|
|
|
4484
|
-
// =====
|
|
4634
|
+
// ===== Healthcenter: per-check expand + Autofix map (copy-only) =====
|
|
4635
|
+
/** Fixed v1 check set: plans, handoff, agents, commands, memory, git, config. */
|
|
4636
|
+
const HEALTH_CHECK_META = {
|
|
4637
|
+
plans: {
|
|
4638
|
+
okDetail: 'Plans directory is present with at least one plan file.',
|
|
4639
|
+
failDetail: 'No plans under .cursor/plans/, or the plans directory is missing.',
|
|
4640
|
+
autofix: { text: '/start-project', subject: '/start-project', destination: 'chatInput', label: 'Autofix' },
|
|
4641
|
+
},
|
|
4642
|
+
handoff: {
|
|
4643
|
+
okDetail: 'HANDOFF.md is present and parseable (idle Plan is still healthy).',
|
|
4644
|
+
failDetail: 'HANDOFF.md is missing or not parseable.',
|
|
4645
|
+
autofix: { text: '/continue-plan', subject: '/continue-plan', destination: 'chatInput', label: 'Autofix' },
|
|
4646
|
+
},
|
|
4647
|
+
agents: {
|
|
4648
|
+
okDetail: 'At least one agent is cataloged under .cursor/agents/.',
|
|
4649
|
+
failDetail: 'No agents found. Kit install or onboard may be incomplete.',
|
|
4650
|
+
autofix: { text: '/agent-kit-onboard', subject: '/agent-kit-onboard', destination: 'chatInput', label: 'Autofix' },
|
|
4651
|
+
},
|
|
4652
|
+
commands: {
|
|
4653
|
+
okDetail: 'Slash commands are available under .cursor/commands/.',
|
|
4654
|
+
failDetail: 'No commands under .cursor/commands/.',
|
|
4655
|
+
autofix: { text: '.cursor/commands/', subject: 'path .cursor/commands/', destination: 'filePicker', label: 'Copy path' },
|
|
4656
|
+
},
|
|
4657
|
+
memory: {
|
|
4658
|
+
okDetail: 'Memory has recorded errors and/or decisions.',
|
|
4659
|
+
failDetail: 'Memory index has no errors or decisions yet (optional until first WRITE).',
|
|
4660
|
+
autofix: { text: '/agent-kit-onboard', subject: '/agent-kit-onboard', destination: 'chatInput', label: 'Autofix' },
|
|
4661
|
+
},
|
|
4662
|
+
git: {
|
|
4663
|
+
okDetail: 'Git branch signal is available.',
|
|
4664
|
+
failDetail: 'Git repository signal missing (no branch).',
|
|
4665
|
+
autofix: { text: 'git status', subject: 'git status', destination: 'terminal', label: 'Autofix' },
|
|
4666
|
+
},
|
|
4667
|
+
config: {
|
|
4668
|
+
okDetail: 'Workspace config is loaded.',
|
|
4669
|
+
failDetail: 'Workspace config is missing or not loaded.',
|
|
4670
|
+
autofix: { text: 'agent-kit doctor', subject: 'agent-kit doctor', destination: 'terminal', label: 'Autofix' },
|
|
4671
|
+
},
|
|
4672
|
+
};
|
|
4673
|
+
|
|
4674
|
+
function healthCheckSeverity(check, aggregateStatus) {
|
|
4675
|
+
if (check && check.ok) return 'ok';
|
|
4676
|
+
if (aggregateStatus === 'degraded') return 'degraded';
|
|
4677
|
+
if (aggregateStatus === 'error') return 'error';
|
|
4678
|
+
return 'warning';
|
|
4679
|
+
}
|
|
4680
|
+
|
|
4681
|
+
function healthSeverityLabel(sev) {
|
|
4682
|
+
if (sev === 'ok') return 'ok';
|
|
4683
|
+
if (sev === 'warning') return 'warn';
|
|
4684
|
+
if (sev === 'degraded') return 'degraded';
|
|
4685
|
+
if (sev === 'error') return 'error';
|
|
4686
|
+
return sev || 'unknown';
|
|
4687
|
+
}
|
|
4688
|
+
|
|
4689
|
+
function healthDotClass(sev) {
|
|
4690
|
+
if (sev === 'ok') return 'dot-green';
|
|
4691
|
+
if (sev === 'warning') return 'dot-yellow';
|
|
4692
|
+
if (sev === 'degraded') return 'dot-yellow';
|
|
4693
|
+
return 'dot-red';
|
|
4694
|
+
}
|
|
4695
|
+
|
|
4696
|
+
function toggleHealthCheck(checkId) {
|
|
4697
|
+
const card = document.getElementById('health-card-' + checkId);
|
|
4698
|
+
if (!card) return;
|
|
4699
|
+
const open = card.classList.toggle('is-expanded');
|
|
4700
|
+
const btn = card.querySelector('.health-item');
|
|
4701
|
+
if (btn) btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
4702
|
+
}
|
|
4703
|
+
|
|
4704
|
+
function healthCheckKeydown(event, checkId) {
|
|
4705
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
4706
|
+
event.preventDefault();
|
|
4707
|
+
toggleHealthCheck(checkId);
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
/** @deprecated Prefer toggleHealthCheck; kept for any leftover callers. */
|
|
4485
4712
|
function showHealthInfo(el, ok) {
|
|
4486
|
-
// Remove any existing message siblings
|
|
4487
4713
|
const parent = el.parentElement;
|
|
4488
4714
|
const existing = parent.querySelector('.health-message');
|
|
4489
4715
|
if (existing) { existing.remove(); return; }
|
|
@@ -5290,9 +5516,9 @@ function nowMetaIconSvg(kind, opts) {
|
|
|
5290
5516
|
/**
|
|
5291
5517
|
* Space-theme Cockpit/nav icon set (inline SVG, no font/fetch/dependency).
|
|
5292
5518
|
* Shared chrome SVG shell with header refresh / nowMetaIconSvg:
|
|
5293
|
-
* viewBox 0 0 16 16, stroke
|
|
5519
|
+
* viewBox 0 0 16 16, stroke 1.5, round caps/joins, CSS box via --mc-chrome-icon-size.
|
|
5294
5520
|
* Static path markup only; never concatenate untrusted text into the SVG.
|
|
5295
|
-
* @param {'current-mission'|'monitor'|'field-report'|'checklist'|'more-sections'|'overview'|'plans'|'activity'|'agents'|'skills'|'commands'|'health'|'git'|'memory'|'terminals'|'processes'|'config'} kind
|
|
5521
|
+
* @param {'current-mission'|'monitor'|'field-report'|'checklist'|'more-sections'|'overview'|'plans'|'activity'|'agents'|'skills'|'skins'|'commands'|'health'|'git'|'memory'|'terminals'|'processes'|'config'} kind
|
|
5296
5522
|
* @param {{ decorative?: boolean }} [opts] decorative true (default): aria-hidden next to a visible label.
|
|
5297
5523
|
* decorative false: role=img + aria-label + title for icon-only controls (e.g. more-sections).
|
|
5298
5524
|
*/
|
|
@@ -5310,6 +5536,7 @@ function spaceIconSvg(kind, opts) {
|
|
|
5310
5536
|
activity: 'Activity',
|
|
5311
5537
|
agents: 'Agents',
|
|
5312
5538
|
skills: 'Skills',
|
|
5539
|
+
skins: 'Skins',
|
|
5313
5540
|
commands: 'Commands',
|
|
5314
5541
|
health: 'Health',
|
|
5315
5542
|
git: 'Git',
|
|
@@ -5319,7 +5546,7 @@ function spaceIconSvg(kind, opts) {
|
|
|
5319
5546
|
config: 'Config',
|
|
5320
5547
|
};
|
|
5321
5548
|
const paths = {
|
|
5322
|
-
// Rocket (mission)
|
|
5549
|
+
// Rocket (mission)
|
|
5323
5550
|
'current-mission':
|
|
5324
5551
|
'<path d="M8 2c1.4 2 2.5 4 2.5 6a2.5 2.5 0 01-5 0C5.5 6 6.6 4 8 2z"/>' +
|
|
5325
5552
|
'<circle cx="8" cy="7.5" r=".9"/>' +
|
|
@@ -5336,20 +5563,20 @@ function spaceIconSvg(kind, opts) {
|
|
|
5336
5563
|
'<rect x="4.5" y="3.5" width="7" height="10" rx="1.2"/>' +
|
|
5337
5564
|
'<path d="M6.5 3.5V2.5a1.5 1.5 0 013 0v1"/>' +
|
|
5338
5565
|
'<path d="M6.5 7h3M6.5 9.5h3"/>',
|
|
5339
|
-
// Checklist lines
|
|
5566
|
+
// Checklist lines
|
|
5340
5567
|
checklist:
|
|
5341
5568
|
'<path d="M3.5 4.5h9M3.5 8h6.5M3.5 11.5h5.5"/>' +
|
|
5342
5569
|
'<path d="M10.5 10.5l1.2 1.2L14 9.5"/>',
|
|
5343
|
-
//
|
|
5570
|
+
// Ellipsis (more sections) — radii sized for stroke 1.5
|
|
5344
5571
|
'more-sections':
|
|
5345
|
-
'<circle cx="4" cy="8" r=".
|
|
5346
|
-
'<circle cx="8" cy="8" r=".
|
|
5347
|
-
'<circle cx="12" cy="8" r=".
|
|
5348
|
-
// House (overview / home)
|
|
5572
|
+
'<circle cx="4" cy="8" r="1.25"/>' +
|
|
5573
|
+
'<circle cx="8" cy="8" r="1.25"/>' +
|
|
5574
|
+
'<circle cx="12" cy="8" r="1.25"/>',
|
|
5575
|
+
// House (overview / home) — roof + walls + door as one coherent glyph
|
|
5349
5576
|
overview:
|
|
5350
|
-
'<path d="M2.5
|
|
5351
|
-
'<path d="M4.
|
|
5352
|
-
'<path d="
|
|
5577
|
+
'<path d="M2.5 7.5L8 2.75l5.5 4.75"/>' +
|
|
5578
|
+
'<path d="M4.25 7.75v6.5h7.5v-6.5"/>' +
|
|
5579
|
+
'<path d="M6.75 14.25v-3.25h2.5v3.25"/>',
|
|
5353
5580
|
// Document (plans)
|
|
5354
5581
|
plans:
|
|
5355
5582
|
'<path d="M4.5 2.5h5l4 4v8.5a1 1 0 01-1 1h-8a1 1 0 01-1-1v-12a1 1 0 011-1z"/>' +
|
|
@@ -5365,6 +5592,10 @@ function spaceIconSvg(kind, opts) {
|
|
|
5365
5592
|
skills:
|
|
5366
5593
|
'<circle cx="8" cy="8" r="2"/>' +
|
|
5367
5594
|
'<path d="M10.5 8a2.5 2.5 0 000-2.5l.8-.8M5.5 8a2.5 2.5 0 010 2.5l-.8.8M8 5.5l-.5-1M8 10.5l-.5 1M10.5 6.5l1-.5M5.5 9.5l-1 .5M10.5 9.5l1 .5M5.5 6.5l-1-.5"/>',
|
|
5595
|
+
// Overlapping swatches (skins) — distinct from Skills gear
|
|
5596
|
+
skins:
|
|
5597
|
+
'<rect x="2.5" y="3.5" width="7" height="7" rx="1.25"/>' +
|
|
5598
|
+
'<rect x="6.5" y="5.5" width="7" height="7" rx="1.25"/>',
|
|
5368
5599
|
// Terminal (commands)
|
|
5369
5600
|
commands:
|
|
5370
5601
|
'<path d="M2.5 3.5l5 5-5 5"/>' +
|
|
@@ -5405,7 +5636,7 @@ function spaceIconSvg(kind, opts) {
|
|
|
5405
5636
|
const a11y = decorative
|
|
5406
5637
|
? 'aria-hidden="true"'
|
|
5407
5638
|
: `role="img" aria-label="${label}" title="${label}"`;
|
|
5408
|
-
return `<svg class="space-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="
|
|
5639
|
+
return `<svg class="space-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" ${a11y}>${body}</svg>`;
|
|
5409
5640
|
}
|
|
5410
5641
|
|
|
5411
5642
|
/**
|
|
@@ -5582,7 +5813,7 @@ function renderFlightLogWarningCard(warning, idx) {
|
|
|
5582
5813
|
/**
|
|
5583
5814
|
* Quiet-state open-triage row (untriaged external review). Prompt chrome;
|
|
5584
5815
|
* per-row Copy triage + path only. No Review all / Resolve all.
|
|
5585
|
-
*
|
|
5816
|
+
* Kind class from flightLogKindClassName('prompt') (SoT map; not a literal).
|
|
5586
5817
|
* @param {{ id?: string, label?: string, sourcePath?: string, action?: { target?: string, label?: string, subject?: string } }} item
|
|
5587
5818
|
* @param {number} idx
|
|
5588
5819
|
*/
|
|
@@ -5603,8 +5834,9 @@ function renderFlightLogQuietOpenTriageCard(item, idx) {
|
|
|
5603
5834
|
const copyTriageHandler = copyForPasteHandler(triageCmd, 'triage command', 'chatInput');
|
|
5604
5835
|
const copyPathHandler = copyRepoPathHandler(sourcePath);
|
|
5605
5836
|
const aria = `Review: ${label}`;
|
|
5837
|
+
const kindClass = flightLogKindClassName('prompt');
|
|
5606
5838
|
return `
|
|
5607
|
-
<div class="flight-log-card flight-log-card-current
|
|
5839
|
+
<div class="flight-log-card flight-log-card-current ${kindClass}" role="listitem" tabindex="0" data-focus-key="flight-log-open-triage-${idx}" data-flight-log-kind="prompt" aria-label="${escapeAttr(aria)}">
|
|
5608
5840
|
<span class="flight-log-card-label">Review</span>
|
|
5609
5841
|
<div class="flight-log-card-text">${escapeHtml(label)}</div>
|
|
5610
5842
|
<div class="flight-log-card-meta">${escapeHtml(sourcePath)}</div>
|
|
@@ -5616,6 +5848,43 @@ function renderFlightLogQuietOpenTriageCard(item, idx) {
|
|
|
5616
5848
|
`;
|
|
5617
5849
|
}
|
|
5618
5850
|
|
|
5851
|
+
/**
|
|
5852
|
+
* Resolve Live Gaps text for Flight Log render + fingerprint (same fallback chain).
|
|
5853
|
+
* Prefer fl.current; with Plan:none, fall back to now.gaps then system.handoff.gaps.
|
|
5854
|
+
*/
|
|
5855
|
+
function resolveFlightLogCurrent(d) {
|
|
5856
|
+
const fl = d?.missionControl?.flightLog ?? d?.flightLog ?? null;
|
|
5857
|
+
if (typeof fl?.current === 'string' && fl.current.trim()) return fl.current.trim();
|
|
5858
|
+
const nowGaps = d?.missionControl?.now?.gaps ?? d?.now?.gaps;
|
|
5859
|
+
if (typeof nowGaps === 'string' && nowGaps.trim()) return nowGaps.trim();
|
|
5860
|
+
const handoffGaps = d?.system?.handoff?.gaps;
|
|
5861
|
+
if (typeof handoffGaps === 'string' && handoffGaps.trim()) return handoffGaps.trim();
|
|
5862
|
+
return null;
|
|
5863
|
+
}
|
|
5864
|
+
|
|
5865
|
+
function flightLogHasPastEntries(fl) {
|
|
5866
|
+
if (!fl || !Array.isArray(fl.past)) return false;
|
|
5867
|
+
return fl.past.some((e) => {
|
|
5868
|
+
if (typeof e === 'string') return Boolean(e.trim());
|
|
5869
|
+
return Boolean(e && typeof e.text === 'string' && e.text.trim());
|
|
5870
|
+
});
|
|
5871
|
+
}
|
|
5872
|
+
|
|
5873
|
+
/** Match renderFlightLogWarningCard: text-less warnings do not count. */
|
|
5874
|
+
function flightLogHasWarningEntries(fl) {
|
|
5875
|
+
if (!fl || !Array.isArray(fl.warnings)) return false;
|
|
5876
|
+
return fl.warnings.some((w) => {
|
|
5877
|
+
if (typeof w === 'string') return Boolean(w.trim());
|
|
5878
|
+
return Boolean(w && typeof w.text === 'string' && w.text.trim());
|
|
5879
|
+
});
|
|
5880
|
+
}
|
|
5881
|
+
|
|
5882
|
+
/** Shared quiet gate for renderAttentionPanel + flightLogFingerprint. */
|
|
5883
|
+
function isFlightLogQuiet(d) {
|
|
5884
|
+
const fl = d?.missionControl?.flightLog ?? d?.flightLog ?? null;
|
|
5885
|
+
return !resolveFlightLogCurrent(d) && !flightLogHasPastEntries(fl) && !flightLogHasWarningEntries(fl);
|
|
5886
|
+
}
|
|
5887
|
+
|
|
5619
5888
|
/**
|
|
5620
5889
|
* Flight Log panel: HANDOFF Gaps log (live large + earlier smaller) + Warnings lane.
|
|
5621
5890
|
* When Gaps + Warnings are empty, may show bounded quiet open-triage rows.
|
|
@@ -5626,22 +5895,22 @@ function renderAttentionPanel(d, attentionChanged) {
|
|
|
5626
5895
|
const sourcePath =
|
|
5627
5896
|
(typeof fl?.sourcePath === 'string' && fl.sourcePath.trim()) ||
|
|
5628
5897
|
'.cursor/HANDOFF.md';
|
|
5629
|
-
const current =
|
|
5630
|
-
typeof fl?.current === 'string' && fl.current.trim()
|
|
5631
|
-
? fl.current.trim()
|
|
5632
|
-
: typeof d.missionControl?.now?.gaps === 'string' && d.missionControl.now.gaps.trim()
|
|
5633
|
-
? d.missionControl.now.gaps.trim()
|
|
5634
|
-
: typeof d.system?.handoff?.gaps === 'string' && d.system.handoff.gaps.trim()
|
|
5635
|
-
? d.system.handoff.gaps.trim()
|
|
5636
|
-
: null;
|
|
5898
|
+
const current = resolveFlightLogCurrent(d);
|
|
5637
5899
|
const past = Array.isArray(fl?.past) ? fl.past : [];
|
|
5638
5900
|
const warnings = Array.isArray(fl?.warnings) ? fl.warnings : [];
|
|
5901
|
+
const quietCap =
|
|
5902
|
+
typeof d.missionControl?.flightLogQuietOpenTriagesCap === 'number' &&
|
|
5903
|
+
d.missionControl.flightLogQuietOpenTriagesCap > 0
|
|
5904
|
+
? Math.floor(d.missionControl.flightLogQuietOpenTriagesCap)
|
|
5905
|
+
: 0;
|
|
5639
5906
|
const quietOpenTriages = Array.isArray(fl?.quietOpenTriages)
|
|
5640
5907
|
? fl.quietOpenTriages
|
|
5641
5908
|
: Array.isArray(d.missionControl?.attention)
|
|
5642
|
-
? d.missionControl.attention
|
|
5643
|
-
|
|
5644
|
-
|
|
5909
|
+
? d.missionControl.attention
|
|
5910
|
+
.filter(
|
|
5911
|
+
(i) => i && i.kind === 'report' && typeof i.sourcePath === 'string' && i.sourcePath.trim(),
|
|
5912
|
+
)
|
|
5913
|
+
.slice(0, quietCap)
|
|
5645
5914
|
: [];
|
|
5646
5915
|
const pastCards = past
|
|
5647
5916
|
.filter((e) => e && typeof e.text === 'string' && e.text.trim())
|
|
@@ -5675,6 +5944,7 @@ function renderAttentionPanel(d, attentionChanged) {
|
|
|
5675
5944
|
const hasCurrent = Boolean(current);
|
|
5676
5945
|
const hasPast = pastCards.length > 0;
|
|
5677
5946
|
const hasWarnings = warningCards.length > 0;
|
|
5947
|
+
// Keep local flags aligned with isFlightLogQuiet(d) for the quiet branch.
|
|
5678
5948
|
if (!hasCurrent && !hasPast && !hasWarnings) {
|
|
5679
5949
|
if (hasOpenTriages) {
|
|
5680
5950
|
body = `<div class="flight-log-stack" role="list" aria-label="Reviews awaiting triage">${openTriageCards}</div>`;
|
|
@@ -5780,18 +6050,48 @@ function previousStageElapsedTitle(now) {
|
|
|
5780
6050
|
return `Previous step elapsed ${formatElapsedPlain(row.elapsedMs)}`;
|
|
5781
6051
|
}
|
|
5782
6052
|
|
|
5783
|
-
|
|
6053
|
+
/**
|
|
6054
|
+
* Fingerprint Flight Log for SSE re-render. Accepts full dashboard `d` or a
|
|
6055
|
+
* slim prev snapshot `{ flightLog, now, system }` so quiet-gate matches render.
|
|
6056
|
+
*/
|
|
6057
|
+
function flightLogFingerprint(d) {
|
|
6058
|
+
if (!d || typeof d !== 'object') return '';
|
|
6059
|
+
const fl = d.missionControl?.flightLog ?? d.flightLog ?? null;
|
|
5784
6060
|
if (!fl || typeof fl !== 'object') return '';
|
|
5785
|
-
const current =
|
|
6061
|
+
const current = resolveFlightLogCurrent(d) || '';
|
|
5786
6062
|
const past = Array.isArray(fl.past)
|
|
5787
|
-
? fl.past
|
|
6063
|
+
? fl.past
|
|
6064
|
+
.map((e) =>
|
|
6065
|
+
typeof e === 'string' ? e : e && typeof e.text === 'string' ? e.text : '',
|
|
6066
|
+
)
|
|
6067
|
+
.join('\n')
|
|
5788
6068
|
: '';
|
|
5789
6069
|
const warnings = Array.isArray(fl.warnings)
|
|
5790
|
-
? fl.warnings
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
6070
|
+
? fl.warnings
|
|
6071
|
+
.map((w) =>
|
|
6072
|
+
typeof w === 'string'
|
|
6073
|
+
? w
|
|
6074
|
+
: w && (w.id || w.text)
|
|
6075
|
+
? String(w.id || w.text)
|
|
6076
|
+
: '',
|
|
6077
|
+
)
|
|
6078
|
+
.join('\n')
|
|
5794
6079
|
: '';
|
|
6080
|
+
// Quiet-only: include open-triage ids when Gaps + Warnings are empty so a new
|
|
6081
|
+
// monitor does not flash the non-quiet Flight Log card with no visible change.
|
|
6082
|
+
// Must use the same gate as renderAttentionPanel (Plan:none Gaps fallback).
|
|
6083
|
+
const openTriages =
|
|
6084
|
+
isFlightLogQuiet(d) && Array.isArray(fl.quietOpenTriages)
|
|
6085
|
+
? fl.quietOpenTriages
|
|
6086
|
+
.map((i) =>
|
|
6087
|
+
typeof i === 'string'
|
|
6088
|
+
? i
|
|
6089
|
+
: i && (i.id || i.sourcePath)
|
|
6090
|
+
? String(i.id || i.sourcePath)
|
|
6091
|
+
: '',
|
|
6092
|
+
)
|
|
6093
|
+
.join('\n')
|
|
6094
|
+
: '';
|
|
5795
6095
|
return `${current}\0${past}\0${warnings}\0${openTriages}`;
|
|
5796
6096
|
}
|
|
5797
6097
|
|
|
@@ -6085,8 +6385,12 @@ function renderUnsafe() {
|
|
|
6085
6385
|
const nowChanged = prevData && nowFingerprint(d.missionControl?.now) !== nowFingerprint(prevData.now);
|
|
6086
6386
|
const attentionChanged =
|
|
6087
6387
|
prevData &&
|
|
6088
|
-
flightLogFingerprint(d
|
|
6089
|
-
flightLogFingerprint(
|
|
6388
|
+
flightLogFingerprint(d) !==
|
|
6389
|
+
flightLogFingerprint({
|
|
6390
|
+
flightLog: prevData.flightLog,
|
|
6391
|
+
now: prevData.now,
|
|
6392
|
+
system: prevData.system,
|
|
6393
|
+
});
|
|
6090
6394
|
prevData = snapshotPrevData(d);
|
|
6091
6395
|
|
|
6092
6396
|
dataLoading = false;
|
|
@@ -6193,6 +6497,10 @@ function renderUnsafe() {
|
|
|
6193
6497
|
<div class="overview-stack">
|
|
6194
6498
|
${renderNowExecutionPanel(d, !!(nowChanged || handoffChanged))}
|
|
6195
6499
|
|
|
6500
|
+
${renderAttentionPanel(d, !!attentionChanged)}
|
|
6501
|
+
|
|
6502
|
+
${renderRecentPlanCards(d)}
|
|
6503
|
+
|
|
6196
6504
|
<div class="section-hero" id="hero-activity">
|
|
6197
6505
|
<div class="live-activity-header">
|
|
6198
6506
|
<span class="card-title">${spaceIconSvg('monitor')}Crew Monitor</span>
|
|
@@ -6225,10 +6533,6 @@ function renderUnsafe() {
|
|
|
6225
6533
|
}).join('')}
|
|
6226
6534
|
</div>
|
|
6227
6535
|
</div>
|
|
6228
|
-
|
|
6229
|
-
${renderAttentionPanel(d, !!attentionChanged)}
|
|
6230
|
-
|
|
6231
|
-
${renderRecentPlanCards(d)}
|
|
6232
6536
|
</div>
|
|
6233
6537
|
</div>
|
|
6234
6538
|
`);
|
|
@@ -6321,30 +6625,108 @@ function renderUnsafe() {
|
|
|
6321
6625
|
</div>
|
|
6322
6626
|
`);
|
|
6323
6627
|
|
|
6324
|
-
// ===== Health =====
|
|
6628
|
+
// ===== Healthcenter (More → Health) =====
|
|
6325
6629
|
const checks = health.checks || [];
|
|
6630
|
+
const healthStatus = health.status || 'ok';
|
|
6631
|
+
const passing = checks.filter(c => c.ok).length;
|
|
6632
|
+
const isSnapshotError = healthStatus === 'error' && checks.length === 0;
|
|
6633
|
+
const isOffline = !isSnapshotError && checks.length === 0;
|
|
6634
|
+
const presenceTone = healthStatus === 'ok' ? 'green' : healthStatus === 'warning' ? 'yellow' : 'red';
|
|
6635
|
+
const presencePulse = healthStatus !== 'ok' ? ' dot-pulse' : '';
|
|
6636
|
+
const presenceLabel = isSnapshotError
|
|
6637
|
+
? 'Snapshot error'
|
|
6638
|
+
: isOffline
|
|
6639
|
+
? 'Health offline'
|
|
6640
|
+
: healthStatus === 'ok'
|
|
6641
|
+
? 'Live'
|
|
6642
|
+
: healthStatus === 'warning'
|
|
6643
|
+
? 'Warning'
|
|
6644
|
+
: healthStatus === 'degraded'
|
|
6645
|
+
? 'Degraded'
|
|
6646
|
+
: 'Error';
|
|
6647
|
+
const subtitle = isSnapshotError
|
|
6648
|
+
? 'Snapshot unavailable'
|
|
6649
|
+
: isOffline
|
|
6650
|
+
? 'No health data'
|
|
6651
|
+
: `${passing}/${checks.length} checks passing`;
|
|
6652
|
+
|
|
6653
|
+
let healthBody = '';
|
|
6654
|
+
if (isSnapshotError) {
|
|
6655
|
+
healthBody = renderEmptyStateCta({
|
|
6656
|
+
headline: 'Health snapshot error',
|
|
6657
|
+
support: (typeof d.error === 'string' && d.error)
|
|
6658
|
+
|| 'The dashboard could not load health checks (serve/snapshot failure). This is not a per-check fail.',
|
|
6659
|
+
iconKind: 'health',
|
|
6660
|
+
cta: {
|
|
6661
|
+
text: 'agent-kit doctor',
|
|
6662
|
+
subject: 'agent-kit doctor',
|
|
6663
|
+
destination: 'terminal',
|
|
6664
|
+
label: 'Autofix: copy agent-kit doctor',
|
|
6665
|
+
focusKey: 'cta-health-snapshot-error',
|
|
6666
|
+
},
|
|
6667
|
+
});
|
|
6668
|
+
} else if (isOffline) {
|
|
6669
|
+
healthBody = renderEmptyStateCta({
|
|
6670
|
+
headline: 'Health offline',
|
|
6671
|
+
support: 'No health data available yet. Refresh after the snapshot loads, or run doctor if the panel stays empty.',
|
|
6672
|
+
iconKind: 'health',
|
|
6673
|
+
cta: {
|
|
6674
|
+
text: 'agent-kit doctor',
|
|
6675
|
+
subject: 'agent-kit doctor',
|
|
6676
|
+
destination: 'terminal',
|
|
6677
|
+
label: 'Autofix: copy agent-kit doctor',
|
|
6678
|
+
focusKey: 'cta-health-offline',
|
|
6679
|
+
},
|
|
6680
|
+
});
|
|
6681
|
+
} else {
|
|
6682
|
+
healthBody = `
|
|
6683
|
+
<div class="health-grid" role="list">
|
|
6684
|
+
${checks.map(c => {
|
|
6685
|
+
const id = c.id || c.label || 'check';
|
|
6686
|
+
const meta = HEALTH_CHECK_META[id] || {
|
|
6687
|
+
okDetail: 'Check is passing.',
|
|
6688
|
+
failDetail: 'Check is failing. Review configuration for this item.',
|
|
6689
|
+
autofix: null,
|
|
6690
|
+
};
|
|
6691
|
+
const sev = healthCheckSeverity(c, healthStatus);
|
|
6692
|
+
const detail = c.ok ? meta.okDetail : meta.failDetail;
|
|
6693
|
+
const autofix = !c.ok && meta.autofix ? meta.autofix : null;
|
|
6694
|
+
const autofixBtn = autofix
|
|
6695
|
+
? `<button type="button" class="health-autofix-btn" data-focus-key="health-autofix-${escapeAttr(id)}" onclick="event.stopPropagation();${copyForPasteHandler(autofix.text, autofix.subject, autofix.destination)}" title="${escapeAttr(copyActionTitle(autofix.subject, autofix.destination))}">${escapeHtml(autofix.label || 'Autofix')}</button>`
|
|
6696
|
+
: (c.ok
|
|
6697
|
+
? `<span class="health-detail-copy" style="margin:0">No Autofix needed.</span>`
|
|
6698
|
+
: `<span class="health-detail-copy" style="margin:0">No Autofix mapped for this check.</span>`);
|
|
6699
|
+
return `
|
|
6700
|
+
<div class="health-card" role="listitem" id="health-card-${escapeAttr(id)}" data-check-id="${escapeAttr(id)}" data-severity="${escapeAttr(sev)}">
|
|
6701
|
+
<button type="button" class="health-item" aria-expanded="false" aria-controls="health-detail-${escapeAttr(id)}" aria-label="${escapeAttr(c.label)}: ${escapeAttr(healthSeverityLabel(sev))}" onclick="toggleHealthCheck('${escapeJsString(id)}')" onkeydown="healthCheckKeydown(event, '${escapeJsString(id)}')">
|
|
6702
|
+
<span class="dot ${healthDotClass(sev)}"></span>
|
|
6703
|
+
<span class="health-item-label">${escapeHtml(c.label)}</span>
|
|
6704
|
+
<span class="health-item-sev">${escapeHtml(healthSeverityLabel(sev))}</span>
|
|
6705
|
+
<span class="health-item-chevron" aria-hidden="true">▾</span>
|
|
6706
|
+
</button>
|
|
6707
|
+
<div class="health-detail" id="health-detail-${escapeAttr(id)}">
|
|
6708
|
+
<p class="health-detail-copy">${escapeHtml(detail)}</p>
|
|
6709
|
+
<div class="health-detail-actions">${autofixBtn}</div>
|
|
6710
|
+
</div>
|
|
6711
|
+
</div>`;
|
|
6712
|
+
}).join('')}
|
|
6713
|
+
</div>`;
|
|
6714
|
+
}
|
|
6715
|
+
|
|
6326
6716
|
parts.push(`
|
|
6327
6717
|
<div class="content-section" id="section-health">
|
|
6328
6718
|
<div class="section-title">
|
|
6329
|
-
<span class="dot dot-${
|
|
6719
|
+
<span class="dot dot-${presenceTone}${healthStatus !== 'ok' ? ' dot-pulse' : ''}"></span>
|
|
6330
6720
|
Health
|
|
6331
|
-
<span class="section-subtitle">${
|
|
6721
|
+
<span class="section-subtitle">${escapeHtml(subtitle)}</span>
|
|
6332
6722
|
</div>
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
${checks.map(c => `
|
|
6341
|
-
<div style="position:relative">
|
|
6342
|
-
<div class="health-item" role="button" tabindex="0" aria-label="Show info for ${escapeAttr(c.label)}" onclick="showHealthInfo(this, ${c.ok})">
|
|
6343
|
-
<span class="dot dot-${c.ok ? 'green' : 'red'}"></span>
|
|
6344
|
-
<span>${escapeHtml(c.label)}</span>
|
|
6345
|
-
</div>
|
|
6346
|
-
</div>
|
|
6347
|
-
`).join('')}
|
|
6723
|
+
<div class="healthcenter">
|
|
6724
|
+
<div class="healthcenter-presence" role="status" aria-live="polite">
|
|
6725
|
+
<span class="dot dot-${presenceTone}${presencePulse}"></span>
|
|
6726
|
+
<span>${escapeHtml(presenceLabel)}</span>
|
|
6727
|
+
<span>· same seven checks · copy-only Autofix</span>
|
|
6728
|
+
</div>
|
|
6729
|
+
${healthBody}
|
|
6348
6730
|
</div>
|
|
6349
6731
|
</div>
|
|
6350
6732
|
`);
|