@skyhook-io/radar-app 1.13.3 → 1.13.5
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/package.json +6 -6
- package/src/App.tsx +68 -23
- package/src/RadarApp.tsx +17 -1
- package/src/api/client.ts +28 -10
- package/src/api/diagnose.ts +61 -15
- package/src/components/ConnectionErrorView.test.tsx +30 -0
- package/src/components/ConnectionErrorView.tsx +31 -19
- package/src/components/diagnose/AgentCase.tsx +131 -0
- package/src/components/diagnose/DiagnoseContext.test.ts +95 -0
- package/src/components/diagnose/DiagnoseContext.tsx +402 -77
- package/src/components/diagnose/DiagnoseSurface.test.tsx +53 -54
- package/src/components/diagnose/DiagnoseSurface.tsx +198 -286
- package/src/components/diagnose/Home.test.tsx +23 -1
- package/src/components/diagnose/Home.tsx +49 -3
- package/src/components/diagnose/InvestigationEvidencePane.test.tsx +1446 -5
- package/src/components/diagnose/InvestigationEvidencePane.tsx +1373 -126
- package/src/components/diagnose/InvestigationView.tsx +227 -5
- package/src/components/diagnose/LocalDiagnoseAction.tsx +2 -3
- package/src/components/diagnose/diagnoseEvidenceTypes.ts +22 -1
- package/src/components/diagnose/investigationCase.test.tsx +1568 -0
- package/src/components/diagnose/investigationCase.ts +439 -0
- package/src/components/diagnose/investigationEvidence.test.ts +3525 -17
- package/src/components/diagnose/investigationEvidence.ts +2246 -166
- package/src/components/diagnose/investigationEvidenceKinds.ts +218 -0
- package/src/components/diagnose/investigationEvidencePresentation.test.ts +31 -0
- package/src/components/diagnose/investigationEvidencePresentation.ts +1 -0
- package/src/components/diagnose/investigationMetrics.test.ts +712 -0
- package/src/components/diagnose/investigationMetrics.ts +393 -0
- package/src/components/diagnose/investigationSourceFocus.ts +6 -0
- package/src/components/diagnose/investigationState.test.ts +322 -0
- package/src/components/diagnose/investigationState.ts +147 -25
- package/src/components/diagnose/parts.test.tsx +537 -1
- package/src/components/diagnose/parts.tsx +486 -42
- package/src/components/resource/HPACharts.render.test.tsx +77 -0
- package/src/components/resource/HPACharts.tsx +32 -25
- package/src/components/resource/PrometheusChartsGrid.tsx +181 -79
- package/src/components/resources/PodFilePreview.test.tsx +131 -0
- package/src/components/resources/PodFilePreview.tsx +394 -0
- package/src/components/resources/PodFilesystemModal.tsx +157 -67
- package/src/components/resources/ResourcesView.tsx +27 -2
- package/src/context/DiagnoseCustomization.test.tsx +26 -0
- package/src/context/DiagnoseCustomization.tsx +14 -2
- package/src/index.ts +8 -5
- package/src/utils/shell-safe.test.ts +25 -1
- package/src/utils/shell-safe.ts +16 -0
|
@@ -30,6 +30,8 @@ import {
|
|
|
30
30
|
investigationEvidenceInputsEqual,
|
|
31
31
|
investigationEvidenceCoverageLimited,
|
|
32
32
|
investigationEvidenceConflictsWithHealthy,
|
|
33
|
+
investigationHealthConflictExplainedBy,
|
|
34
|
+
investigationLiveCaseTurnIndex,
|
|
33
35
|
investigationEndedBeforeConclusion,
|
|
34
36
|
type InvestigationHistoryUnavailableState,
|
|
35
37
|
investigationHistoryUnavailablePresentation,
|
|
@@ -78,6 +80,7 @@ import {
|
|
|
78
80
|
AssessmentSources,
|
|
79
81
|
ApplyDialog,
|
|
80
82
|
appendThinking,
|
|
83
|
+
mergeStartupSignal,
|
|
81
84
|
upsertTool,
|
|
82
85
|
type Turn,
|
|
83
86
|
} from "./parts";
|
|
@@ -88,9 +91,16 @@ import {
|
|
|
88
91
|
projectInvestigationEvidence,
|
|
89
92
|
resolveInvestigationRootCauseEvidence,
|
|
90
93
|
} from "./investigationEvidence";
|
|
94
|
+
import {
|
|
95
|
+
resolveInvestigationCase,
|
|
96
|
+
investigationCaseItemsStillRendered,
|
|
97
|
+
mergeInvestigationCases,
|
|
98
|
+
type InvestigationCaseResolution,
|
|
99
|
+
} from "./investigationCase";
|
|
91
100
|
import {
|
|
92
101
|
InvestigationEvidencePane,
|
|
93
102
|
partitionInvestigationEvidence,
|
|
103
|
+
type InvestigationTimelineScope,
|
|
94
104
|
} from "./InvestigationEvidencePane";
|
|
95
105
|
import type { DiagnosisResourceRef } from "./diagnoseEvidenceTypes";
|
|
96
106
|
import { formatInvestigationTarget } from "./target";
|
|
@@ -153,12 +163,15 @@ export function InvestigationView({
|
|
|
153
163
|
agentLabel,
|
|
154
164
|
maximized,
|
|
155
165
|
onOpenResource,
|
|
166
|
+
onOpenTimeline,
|
|
156
167
|
}: {
|
|
157
168
|
run: RunSummary;
|
|
158
169
|
agentLabel: string;
|
|
159
170
|
maximized: boolean;
|
|
160
171
|
/** Opens an unambiguous evidence subject in Radar's native resource views. */
|
|
161
172
|
onOpenResource?: (ref: DiagnosisResourceRef) => void;
|
|
173
|
+
/** Opens Radar's Timeline filtered to the resource a changes card is about. */
|
|
174
|
+
onOpenTimeline?: (scope: InvestigationTimelineScope) => void;
|
|
162
175
|
}) {
|
|
163
176
|
const { kind, namespace, name } = run;
|
|
164
177
|
// Apply is off for hosted agents (read-only server-side). Keyed on the selected
|
|
@@ -473,6 +486,14 @@ export function InvestigationView({
|
|
|
473
486
|
},
|
|
474
487
|
]);
|
|
475
488
|
break;
|
|
489
|
+
case "phase":
|
|
490
|
+
// Startup phases only feed the pending status line; a replayed
|
|
491
|
+
// finished turn is not running, so nothing shows for it.
|
|
492
|
+
updateLast((t) => {
|
|
493
|
+
const startup = mergeStartupSignal(t.startup, ev);
|
|
494
|
+
return startup === t.startup ? t : { ...t, startup };
|
|
495
|
+
});
|
|
496
|
+
break;
|
|
476
497
|
case "thinking":
|
|
477
498
|
if (ev.token) {
|
|
478
499
|
if (live) {
|
|
@@ -963,15 +984,114 @@ export function InvestigationView({
|
|
|
963
984
|
: undefined,
|
|
964
985
|
[currentAssessment, currentAssessmentIdx, projection],
|
|
965
986
|
);
|
|
987
|
+
// The agent's case binds for every assessment, healthy and inconclusive
|
|
988
|
+
// included, so it is resolved independently of the root cause.
|
|
989
|
+
const investigationCase = useMemo(
|
|
990
|
+
() =>
|
|
991
|
+
currentAssessment?.diagnosis
|
|
992
|
+
? resolveInvestigationCase(
|
|
993
|
+
projection,
|
|
994
|
+
currentAssessment.diagnosis,
|
|
995
|
+
currentAssessmentIdx,
|
|
996
|
+
)
|
|
997
|
+
: undefined,
|
|
998
|
+
[currentAssessment, currentAssessmentIdx, projection],
|
|
999
|
+
);
|
|
1000
|
+
// A follow-up answer that cites evidence takes over the pane's case; the
|
|
1001
|
+
// assessment's own items are then listed read-only under it.
|
|
1002
|
+
const liveCaseTurnIdx = investigationLiveCaseTurnIndex(
|
|
1003
|
+
turns,
|
|
1004
|
+
currentAssessmentIdx,
|
|
1005
|
+
);
|
|
1006
|
+
const liveCaseIsCurrentAssessment = liveCaseTurnIdx === currentAssessmentIdx;
|
|
1007
|
+
// The qualification banner and the "Used for assessment" markers describe the
|
|
1008
|
+
// assessment on screen, so they always read its own resolution. A later turn's
|
|
1009
|
+
// citations only widen the selection (below): replacing the resolution would
|
|
1010
|
+
// drop the assessment's qualification and push its cited evidence back into
|
|
1011
|
+
// the withheld set.
|
|
1012
|
+
const paneResolution = rootCauseEvidenceResolution;
|
|
1013
|
+
// The live turn's own resolution stays available for the live turn's own
|
|
1014
|
+
// receipt: its sources are its, not the assessment's.
|
|
1015
|
+
const liveTurnResolution = useMemo(() => {
|
|
1016
|
+
if (liveCaseIsCurrentAssessment) return rootCauseEvidenceResolution;
|
|
1017
|
+
const diagnosis = turns[liveCaseTurnIdx]?.diagnosis;
|
|
1018
|
+
return diagnosis?.rootCause
|
|
1019
|
+
? resolveInvestigationRootCauseEvidence(
|
|
1020
|
+
projection,
|
|
1021
|
+
diagnosis.rootCauseEvidence,
|
|
1022
|
+
liveCaseTurnIdx,
|
|
1023
|
+
)
|
|
1024
|
+
: undefined;
|
|
1025
|
+
}, [
|
|
1026
|
+
liveCaseIsCurrentAssessment,
|
|
1027
|
+
rootCauseEvidenceResolution,
|
|
1028
|
+
turns,
|
|
1029
|
+
liveCaseTurnIdx,
|
|
1030
|
+
projection,
|
|
1031
|
+
]);
|
|
1032
|
+
const followUpSelectedGroupIds = useMemo(() => {
|
|
1033
|
+
if (liveCaseIsCurrentAssessment) return undefined;
|
|
1034
|
+
if (liveTurnResolution?.status !== "linked") return undefined;
|
|
1035
|
+
return liveTurnResolution.links.flatMap((link) =>
|
|
1036
|
+
link.originalGroupId
|
|
1037
|
+
? [{ groupId: link.originalGroupId, source: link.source }]
|
|
1038
|
+
: [],
|
|
1039
|
+
);
|
|
1040
|
+
}, [liveCaseIsCurrentAssessment, liveTurnResolution]);
|
|
1041
|
+
// The live turn's own case, before anything is carried onto it. Its source
|
|
1042
|
+
// receipt must list what IT cited, not what the merge brought along.
|
|
1043
|
+
const liveTurnCase = useMemo(
|
|
1044
|
+
() =>
|
|
1045
|
+
liveCaseIsCurrentAssessment
|
|
1046
|
+
? investigationCase
|
|
1047
|
+
: resolveInvestigationCase(
|
|
1048
|
+
projection,
|
|
1049
|
+
turns[liveCaseTurnIdx]?.diagnosis,
|
|
1050
|
+
liveCaseTurnIdx,
|
|
1051
|
+
),
|
|
1052
|
+
[
|
|
1053
|
+
liveCaseIsCurrentAssessment,
|
|
1054
|
+
investigationCase,
|
|
1055
|
+
projection,
|
|
1056
|
+
turns,
|
|
1057
|
+
liveCaseTurnIdx,
|
|
1058
|
+
],
|
|
1059
|
+
);
|
|
1060
|
+
const paneCase = useMemo(() => {
|
|
1061
|
+
const live = liveTurnCase;
|
|
1062
|
+
const earlier: InvestigationCaseResolution[] = [];
|
|
1063
|
+
for (let i = turns.length - 1; i >= 0; i -= 1) {
|
|
1064
|
+
const diagnosis = turns[i]?.diagnosis;
|
|
1065
|
+
if (i === liveCaseTurnIdx || !diagnosis) continue;
|
|
1066
|
+
// Reuse the assessment's own resolution rather than resolving it a
|
|
1067
|
+
// second time: same projection, same result, and it keeps the items the
|
|
1068
|
+
// conflict banner reasons about the ones the merge received.
|
|
1069
|
+
earlier.push(
|
|
1070
|
+
i === currentAssessmentIdx && investigationCase
|
|
1071
|
+
? investigationCase
|
|
1072
|
+
: resolveInvestigationCase(projection, diagnosis, i),
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
return mergeInvestigationCases(live, earlier);
|
|
1076
|
+
}, [
|
|
1077
|
+
liveTurnCase,
|
|
1078
|
+
investigationCase,
|
|
1079
|
+
currentAssessmentIdx,
|
|
1080
|
+
projection,
|
|
1081
|
+
turns,
|
|
1082
|
+
liveCaseTurnIdx,
|
|
1083
|
+
]);
|
|
966
1084
|
const visibleEvidenceGroupIds = useMemo(
|
|
967
1085
|
() =>
|
|
968
1086
|
new Set(
|
|
969
1087
|
partitionInvestigationEvidence(
|
|
970
1088
|
projection.groups,
|
|
971
|
-
|
|
1089
|
+
paneResolution,
|
|
1090
|
+
paneCase,
|
|
1091
|
+
followUpSelectedGroupIds,
|
|
972
1092
|
).collectionByGroup.keys(),
|
|
973
1093
|
),
|
|
974
|
-
[projection.groups,
|
|
1094
|
+
[projection.groups, paneResolution, paneCase, followUpSelectedGroupIds],
|
|
975
1095
|
);
|
|
976
1096
|
const evidenceStepIdsByTurn = useMemo(
|
|
977
1097
|
() =>
|
|
@@ -1269,6 +1389,32 @@ export function InvestigationView({
|
|
|
1269
1389
|
const currentAssessmentEvidenceConflict =
|
|
1270
1390
|
currentAssessment?.diagnosis?.healthy === true &&
|
|
1271
1391
|
investigationEvidenceConflictsWithHealthy(projection);
|
|
1392
|
+
// The banner qualifies THIS assessment, so only this assessment's own case
|
|
1393
|
+
// may reframe it. `paneCase` also carries a later answer's items and notes
|
|
1394
|
+
// inherited from superseded assessments; neither of those spoke about this
|
|
1395
|
+
// verdict, and letting them soften it would let an unrelated follow-up
|
|
1396
|
+
// retire a warning the reader still needs.
|
|
1397
|
+
const currentAssessmentEvidenceConflictExplainedBy = useMemo(() => {
|
|
1398
|
+
if (!currentAssessmentEvidenceConflict) return undefined;
|
|
1399
|
+
// Two conditions, and both are required. The note must belong to THIS
|
|
1400
|
+
// assessment — a later answer or a superseded assessment did not speak
|
|
1401
|
+
// about this verdict. And it must survive into the case the pane actually
|
|
1402
|
+
// renders: the merge lets a later turn take over a group, and a banner
|
|
1403
|
+
// that points at a note the reader cannot find is worse than no banner.
|
|
1404
|
+
const qualifying = investigationCaseItemsStillRendered(
|
|
1405
|
+
investigationCase?.items,
|
|
1406
|
+
paneCase?.items,
|
|
1407
|
+
);
|
|
1408
|
+
return (
|
|
1409
|
+
investigationHealthConflictExplainedBy(projection, qualifying) ??
|
|
1410
|
+
undefined
|
|
1411
|
+
);
|
|
1412
|
+
}, [
|
|
1413
|
+
currentAssessmentEvidenceConflict,
|
|
1414
|
+
projection,
|
|
1415
|
+
investigationCase,
|
|
1416
|
+
paneCase,
|
|
1417
|
+
]);
|
|
1272
1418
|
const hasEvidenceCollectedAfterAssessment =
|
|
1273
1419
|
currentAssessmentIdx >= 0 &&
|
|
1274
1420
|
projection.sources.some(
|
|
@@ -1676,6 +1822,7 @@ export function InvestigationView({
|
|
|
1676
1822
|
<Fragment key={index}>
|
|
1677
1823
|
<TurnView
|
|
1678
1824
|
turn={turn}
|
|
1825
|
+
agentLabel={agentLabel}
|
|
1679
1826
|
turnIndex={index}
|
|
1680
1827
|
evidenceStepIds={evidenceStepIdsByTurn.get(index)}
|
|
1681
1828
|
onViewEvidence={viewEvidenceSource}
|
|
@@ -1700,6 +1847,49 @@ export function InvestigationView({
|
|
|
1700
1847
|
: undefined
|
|
1701
1848
|
}
|
|
1702
1849
|
hideConclusion={assessmentIndexes.includes(index)}
|
|
1850
|
+
assessmentSources={(() => {
|
|
1851
|
+
// Answer turns show what they cited; the live
|
|
1852
|
+
// one also drives Findings, earlier ones are
|
|
1853
|
+
// read-only.
|
|
1854
|
+
if (
|
|
1855
|
+
!turn.question ||
|
|
1856
|
+
turn.verify ||
|
|
1857
|
+
turn.apply ||
|
|
1858
|
+
turn.status !== "done" ||
|
|
1859
|
+
!turn.diagnosis
|
|
1860
|
+
)
|
|
1861
|
+
return undefined;
|
|
1862
|
+
const answerCase =
|
|
1863
|
+
index === liveCaseTurnIdx
|
|
1864
|
+
? liveTurnCase
|
|
1865
|
+
: resolveInvestigationCase(
|
|
1866
|
+
projection,
|
|
1867
|
+
turn.diagnosis,
|
|
1868
|
+
index,
|
|
1869
|
+
);
|
|
1870
|
+
const answerResolution =
|
|
1871
|
+
index === liveCaseTurnIdx
|
|
1872
|
+
? liveTurnResolution
|
|
1873
|
+
: undefined;
|
|
1874
|
+
return answerCase?.items.length ||
|
|
1875
|
+
answerResolution?.links.length ||
|
|
1876
|
+
turn.diagnosis?.unlinkedEvidence ||
|
|
1877
|
+
turn.diagnosis?.evidenceMalformed ? (
|
|
1878
|
+
<AssessmentSources
|
|
1879
|
+
renderedGroupIds={visibleEvidenceGroupIds}
|
|
1880
|
+
resolution={answerResolution}
|
|
1881
|
+
investigationCase={answerCase}
|
|
1882
|
+
unlinkedEvidence={
|
|
1883
|
+
turn.diagnosis?.unlinkedEvidence
|
|
1884
|
+
}
|
|
1885
|
+
evidenceMalformed={
|
|
1886
|
+
turn.diagnosis?.evidenceMalformed
|
|
1887
|
+
}
|
|
1888
|
+
readOnly={index !== liveCaseTurnIdx}
|
|
1889
|
+
onViewSource={viewActivitySource}
|
|
1890
|
+
/>
|
|
1891
|
+
) : undefined;
|
|
1892
|
+
})()}
|
|
1703
1893
|
/>
|
|
1704
1894
|
{showSplitWorkspace &&
|
|
1705
1895
|
index === currentAssessmentIdx &&
|
|
@@ -1938,9 +2128,24 @@ export function InvestigationView({
|
|
|
1938
2128
|
}
|
|
1939
2129
|
diagnosis={currentAssessment.diagnosis}
|
|
1940
2130
|
assessmentSources={
|
|
1941
|
-
|
|
2131
|
+
// An assessment whose every note was rejected has no
|
|
2132
|
+
// items and no links, and the loss is the only thing
|
|
2133
|
+
// there is to say about it.
|
|
2134
|
+
rootCauseEvidenceResolution?.links.length ||
|
|
2135
|
+
investigationCase?.items.length ||
|
|
2136
|
+
currentAssessment.diagnosis.unlinkedEvidence ||
|
|
2137
|
+
currentAssessment.diagnosis.evidenceMalformed ? (
|
|
1942
2138
|
<AssessmentSources
|
|
2139
|
+
renderedGroupIds={visibleEvidenceGroupIds}
|
|
1943
2140
|
resolution={rootCauseEvidenceResolution}
|
|
2141
|
+
investigationCase={investigationCase}
|
|
2142
|
+
unlinkedEvidence={
|
|
2143
|
+
currentAssessment.diagnosis?.unlinkedEvidence
|
|
2144
|
+
}
|
|
2145
|
+
evidenceMalformed={
|
|
2146
|
+
currentAssessment.diagnosis?.evidenceMalformed
|
|
2147
|
+
}
|
|
2148
|
+
readOnly={!liveCaseIsCurrentAssessment}
|
|
1944
2149
|
onViewSource={viewActivitySource}
|
|
1945
2150
|
/>
|
|
1946
2151
|
) : undefined
|
|
@@ -1979,6 +2184,9 @@ export function InvestigationView({
|
|
|
1979
2184
|
showDisclaimer={false}
|
|
1980
2185
|
coverageLimited={currentAssessmentCoverageLimited}
|
|
1981
2186
|
evidenceConflict={currentAssessmentEvidenceConflict}
|
|
2187
|
+
evidenceConflictExplainedBy={
|
|
2188
|
+
currentAssessmentEvidenceConflictExplainedBy
|
|
2189
|
+
}
|
|
1982
2190
|
/>
|
|
1983
2191
|
) : (
|
|
1984
2192
|
<div className="mt-2 flex items-center gap-2 rounded-md bg-theme-surface/60 px-2.5 py-2 text-xs text-theme-text-tertiary">
|
|
@@ -2042,15 +2250,26 @@ export function InvestigationView({
|
|
|
2042
2250
|
diagnosis: turns[index].diagnosis!,
|
|
2043
2251
|
explanation: explanationFor(turns[index]),
|
|
2044
2252
|
sources: (() => {
|
|
2253
|
+
// Each earlier assessment owns its own items; they
|
|
2254
|
+
// render read-only here and never annotate cards.
|
|
2045
2255
|
const resolution =
|
|
2046
2256
|
resolveInvestigationRootCauseEvidence(
|
|
2047
2257
|
projection,
|
|
2048
2258
|
turns[index].diagnosis!.rootCauseEvidence,
|
|
2049
2259
|
index,
|
|
2050
2260
|
);
|
|
2051
|
-
|
|
2261
|
+
const earlierCase = resolveInvestigationCase(
|
|
2262
|
+
projection,
|
|
2263
|
+
turns[index].diagnosis,
|
|
2264
|
+
index,
|
|
2265
|
+
);
|
|
2266
|
+
return resolution.links.length ||
|
|
2267
|
+
earlierCase.items.length ? (
|
|
2052
2268
|
<AssessmentSources
|
|
2269
|
+
renderedGroupIds={visibleEvidenceGroupIds}
|
|
2053
2270
|
resolution={resolution}
|
|
2271
|
+
investigationCase={earlierCase}
|
|
2272
|
+
readOnly
|
|
2054
2273
|
onViewSource={viewActivitySource}
|
|
2055
2274
|
/>
|
|
2056
2275
|
) : undefined;
|
|
@@ -2060,7 +2279,9 @@ export function InvestigationView({
|
|
|
2060
2279
|
|
|
2061
2280
|
<InvestigationEvidencePane
|
|
2062
2281
|
projection={projection}
|
|
2063
|
-
rootCauseEvidence={
|
|
2282
|
+
rootCauseEvidence={paneResolution}
|
|
2283
|
+
alsoSelectedGroupIds={followUpSelectedGroupIds}
|
|
2284
|
+
investigationCase={paneCase}
|
|
2064
2285
|
collecting={
|
|
2065
2286
|
explanationRequest?.status !== "running" &&
|
|
2066
2287
|
(requestPending ||
|
|
@@ -2074,6 +2295,7 @@ export function InvestigationView({
|
|
|
2074
2295
|
onViewSource={viewActivitySource}
|
|
2075
2296
|
onViewActivity={viewActivity}
|
|
2076
2297
|
onOpenResource={stale ? undefined : onOpenResource}
|
|
2298
|
+
onOpenTimeline={stale ? undefined : onOpenTimeline}
|
|
2077
2299
|
revealRequest={evidenceRevealRequest}
|
|
2078
2300
|
onRevealReady={revealEvidenceSource}
|
|
2079
2301
|
afterEvidence={
|
|
@@ -162,10 +162,9 @@ export function IssueDiagnoseButton({
|
|
|
162
162
|
// investigations). Self-hides when no agent CLI is present.
|
|
163
163
|
export function GlobalDiagnoseButton() {
|
|
164
164
|
const d = useDiagnose();
|
|
165
|
-
const {
|
|
165
|
+
const { runningCount } = useDiagnoseLayout();
|
|
166
166
|
if (d.setupState === "off") return null;
|
|
167
167
|
const ready = d.available;
|
|
168
|
-
const runningCount = runningKeys.size;
|
|
169
168
|
const agentSuffix = d.hosted
|
|
170
169
|
? `powered by ${d.agentLabel}`
|
|
171
170
|
: `runs your own ${d.agentLabel} locally`;
|
|
@@ -181,7 +180,7 @@ export function GlobalDiagnoseButton() {
|
|
|
181
180
|
position="bottom"
|
|
182
181
|
>
|
|
183
182
|
<button
|
|
184
|
-
onClick={d.
|
|
183
|
+
onClick={() => d.openWorkspace()}
|
|
185
184
|
className="relative rounded-md bg-theme-elevated p-1.5 text-theme-text-secondary transition-colors hover:bg-theme-hover hover:text-theme-text-primary"
|
|
186
185
|
aria-label={
|
|
187
186
|
runningCount > 0
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* investigation projection. These deliberately exclude any presentation or
|
|
4
4
|
* transport envelope so the live Diagnose UI is not coupled to another route.
|
|
5
5
|
*/
|
|
6
|
+
import type { HPADiagnosisView } from "@skyhook-io/k8s-ui";
|
|
7
|
+
|
|
6
8
|
export interface DiagnosisFilteredLogs {
|
|
7
9
|
lines: string[] | null;
|
|
8
10
|
totalLines: number;
|
|
@@ -53,6 +55,25 @@ export interface DiagnosisResourceRef {
|
|
|
53
55
|
name: string;
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
/** The HPA diagnosis Radar computes for the scaler itself, attached to the workload it scales. */
|
|
59
|
+
export interface DiagnosisHPASummary extends HPADiagnosisView {
|
|
60
|
+
target?: DiagnosisResourceRef;
|
|
61
|
+
metrics?: Array<{
|
|
62
|
+
type: string;
|
|
63
|
+
name: string;
|
|
64
|
+
current?: string;
|
|
65
|
+
target?: string;
|
|
66
|
+
status: string;
|
|
67
|
+
}>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface DiagnosisScalerRef extends DiagnosisResourceRef {
|
|
71
|
+
/** The KEDA ScaledObject that owns this HPA, when KEDA created it. */
|
|
72
|
+
managedBy?: DiagnosisResourceRef;
|
|
73
|
+
/** Present only for an HPA the caller may read; KEDA scalers carry the ref alone. */
|
|
74
|
+
hpaSummary?: DiagnosisHPASummary;
|
|
75
|
+
}
|
|
76
|
+
|
|
56
77
|
export interface DiagnosisResourceContext {
|
|
57
78
|
tier: "basic" | "diagnostic";
|
|
58
79
|
owner?: DiagnosisResourceRef;
|
|
@@ -71,7 +92,7 @@ export interface DiagnosisResourceContext {
|
|
|
71
92
|
pvcs?: DiagnosisResourceRef[];
|
|
72
93
|
};
|
|
73
94
|
runsOn?: DiagnosisResourceRef;
|
|
74
|
-
scaledBy?:
|
|
95
|
+
scaledBy?: DiagnosisScalerRef[];
|
|
75
96
|
statusSummary?: {
|
|
76
97
|
phase?: string;
|
|
77
98
|
conditions?: Array<{
|