@skyhook-io/radar-app 1.13.1 → 1.13.2
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 +2 -2
- package/src/App.tsx +19 -1
- package/src/RadarApp.tsx +2 -2
- package/src/api/diagnose.test.ts +268 -0
- package/src/api/diagnose.ts +72 -9
- package/src/components/diagnose/AISettings.tsx +1 -1
- package/src/components/diagnose/AgentSetupNotice.tsx +5 -5
- package/src/components/diagnose/ApplyDialog.test.tsx +72 -0
- package/src/components/diagnose/DiagnoseContext.tsx +12 -17
- package/src/components/diagnose/DiagnoseSurface.test.tsx +211 -16
- package/src/components/diagnose/DiagnoseSurface.tsx +464 -133
- package/src/components/diagnose/Home.test.tsx +293 -0
- package/src/components/diagnose/Home.tsx +289 -119
- package/src/components/diagnose/InvestigationEvidencePane.test.tsx +2170 -0
- package/src/components/diagnose/InvestigationEvidencePane.tsx +2253 -0
- package/src/components/diagnose/InvestigationResourceEvidence.test.tsx +257 -0
- package/src/components/diagnose/InvestigationResourceEvidence.tsx +214 -0
- package/src/components/diagnose/InvestigationView.test.ts +17 -0
- package/src/components/diagnose/InvestigationView.tsx +1900 -393
- package/src/components/diagnose/LocalDiagnoseAction.tsx +42 -25
- package/src/components/diagnose/agentCatalog.ts +1 -1
- package/src/components/diagnose/diagnoseEvidenceTypes.ts +151 -0
- package/src/components/diagnose/investigationEvidence.test.ts +3109 -0
- package/src/components/diagnose/investigationEvidence.ts +3492 -0
- package/src/components/diagnose/investigationEvidencePresentation.test.ts +447 -0
- package/src/components/diagnose/investigationEvidencePresentation.ts +167 -0
- package/src/components/diagnose/investigationExplanation.test.ts +63 -0
- package/src/components/diagnose/investigationExplanation.ts +22 -0
- package/src/components/diagnose/investigationResourceEvidenceModel.ts +322 -0
- package/src/components/diagnose/investigationSourceFocus.test.ts +143 -0
- package/src/components/diagnose/investigationSourceFocus.ts +98 -0
- package/src/components/diagnose/investigationState.test.ts +695 -0
- package/src/components/diagnose/investigationState.ts +451 -0
- package/src/components/diagnose/parts.test.tsx +864 -3
- package/src/components/diagnose/parts.tsx +1337 -541
- package/src/components/diagnose/target.test.ts +39 -0
- package/src/components/diagnose/target.ts +36 -0
- package/src/components/diagnose/useDisclosureReveal.ts +117 -0
- package/src/components/home/MCPSetupDialog.tsx +2 -2
- package/src/components/home/mcpToolCatalog.test.ts +22 -0
- package/src/components/home/mcpToolCatalog.ts +3 -2
- package/src/components/issues/IssuesPane.tsx +5 -1
- package/src/components/settings/SettingsDialog.tsx +11 -13
- package/src/components/workload/WorkloadView.tsx +1 -1
- package/src/context/DiagnoseCustomization.tsx +11 -8
- package/src/index.css +63 -79
- package/src/index.ts +1 -1
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { Loader2, Sparkles } from "lucide-react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
useDiagnoseLayout,
|
|
5
|
-
runTargetKey,
|
|
6
|
-
} from "./DiagnoseContext";
|
|
2
|
+
import { useDiagnose, useDiagnoseLayout } from "./DiagnoseContext";
|
|
3
|
+
import { runTargetKey } from "./target";
|
|
7
4
|
import { Tooltip } from "../ui/Tooltip";
|
|
8
5
|
import type { RenderDiagnoseAction } from "../../context/DiagnoseCustomization";
|
|
9
6
|
|
|
@@ -13,18 +10,20 @@ import type { RenderDiagnoseAction } from "../../context/DiagnoseCustomization";
|
|
|
13
10
|
// this slot with their own action.
|
|
14
11
|
//
|
|
15
12
|
// Adaptive by health: on a resource with a live problem it reads as a prominent
|
|
16
|
-
// "
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
13
|
+
// "Investigate" action; when the resource is fine or health is unknown it shrinks
|
|
14
|
+
// to a quiet colored-icon affordance ("ask my agent about this") — so it never
|
|
15
|
+
// implies "something is wrong here" on a healthy resource. The tooltip leads with
|
|
16
|
+
// the BYO framing (the user's OWN agent, locally) — unless the agent is hosted,
|
|
17
|
+
// where those claims would be false.
|
|
21
18
|
function DiagnoseResourceButton({
|
|
22
19
|
kind,
|
|
20
|
+
group,
|
|
23
21
|
namespace,
|
|
24
22
|
name,
|
|
25
23
|
health,
|
|
26
24
|
}: {
|
|
27
25
|
kind: string;
|
|
26
|
+
group?: string;
|
|
28
27
|
namespace: string;
|
|
29
28
|
name: string;
|
|
30
29
|
health?: "problem" | "healthy" | "unknown";
|
|
@@ -37,17 +36,18 @@ function DiagnoseResourceButton({
|
|
|
37
36
|
if (d.setupState === "off") return null;
|
|
38
37
|
const ready = d.available;
|
|
39
38
|
const problem = health === "problem";
|
|
40
|
-
const running =
|
|
39
|
+
const running =
|
|
40
|
+
ready && runningKeys.has(runTargetKey(kind, namespace, name, group ?? ""));
|
|
41
41
|
const tooltip = !ready
|
|
42
|
-
? "Set up AI
|
|
42
|
+
? "Set up AI investigations — runs your own agent locally."
|
|
43
43
|
: running
|
|
44
44
|
? `${d.agentLabel} is investigating this resource — click to watch it live.`
|
|
45
45
|
: d.hosted
|
|
46
46
|
? problem
|
|
47
|
-
? `
|
|
47
|
+
? `Investigate with ${d.agentLabel} — reads this resource's spec, events & logs to look for the cause.`
|
|
48
48
|
: `Ask ${d.agentLabel} about this resource — reads its spec, events & logs.`
|
|
49
49
|
: problem
|
|
50
|
-
? `
|
|
50
|
+
? `Investigate with your own ${d.agentLabel} — runs locally and reads this resource's spec, events & logs.`
|
|
51
51
|
: `Ask your own ${d.agentLabel} about this resource — runs locally, reads its spec, events & logs.`;
|
|
52
52
|
// While an investigation is live, the button advertises it (and clicking focuses
|
|
53
53
|
// the existing run rather than starting a new one — openInvestigation dedups).
|
|
@@ -56,15 +56,22 @@ function DiagnoseResourceButton({
|
|
|
56
56
|
<Tooltip content={tooltip} position="bottom">
|
|
57
57
|
<button
|
|
58
58
|
onClick={() =>
|
|
59
|
-
ready
|
|
59
|
+
ready
|
|
60
|
+
? d.openInvestigation({
|
|
61
|
+
kind,
|
|
62
|
+
group: group ?? "",
|
|
63
|
+
namespace,
|
|
64
|
+
name,
|
|
65
|
+
})
|
|
66
|
+
: d.openHome()
|
|
60
67
|
}
|
|
61
68
|
aria-label={
|
|
62
69
|
!ready
|
|
63
|
-
? "Set up AI
|
|
70
|
+
? "Set up AI investigations"
|
|
64
71
|
: running
|
|
65
72
|
? "Investigation running — click to view"
|
|
66
73
|
: problem
|
|
67
|
-
? "
|
|
74
|
+
? "Investigate with AI"
|
|
68
75
|
: "Ask AI about this resource"
|
|
69
76
|
}
|
|
70
77
|
className={
|
|
@@ -78,7 +85,7 @@ function DiagnoseResourceButton({
|
|
|
78
85
|
) : (
|
|
79
86
|
<Sparkles className="h-3.5 w-3.5 text-accent" />
|
|
80
87
|
)}
|
|
81
|
-
{running ? "Investigating…" : problem && "
|
|
88
|
+
{running ? "Investigating…" : problem && "Investigate"}
|
|
82
89
|
</button>
|
|
83
90
|
</Tooltip>
|
|
84
91
|
);
|
|
@@ -86,27 +93,31 @@ function DiagnoseResourceButton({
|
|
|
86
93
|
|
|
87
94
|
export const defaultDiagnoseAction: RenderDiagnoseAction = ({
|
|
88
95
|
kind,
|
|
96
|
+
group,
|
|
89
97
|
namespace,
|
|
90
98
|
name,
|
|
91
99
|
health,
|
|
92
100
|
}) => (
|
|
93
101
|
<DiagnoseResourceButton
|
|
94
102
|
kind={kind}
|
|
103
|
+
group={group}
|
|
95
104
|
namespace={namespace}
|
|
96
105
|
name={name}
|
|
97
106
|
health={health}
|
|
98
107
|
/>
|
|
99
108
|
);
|
|
100
109
|
|
|
101
|
-
// Compact per-issue "
|
|
110
|
+
// Compact per-issue "Investigate" action for the Issues queue — launches an
|
|
102
111
|
// investigation for the issue's subject from where the problem is surfaced.
|
|
103
112
|
// stopPropagation so it doesn't toggle the issue row it lives in.
|
|
104
113
|
export function IssueDiagnoseButton({
|
|
105
114
|
kind,
|
|
115
|
+
group,
|
|
106
116
|
namespace,
|
|
107
117
|
name,
|
|
108
118
|
}: {
|
|
109
119
|
kind: string;
|
|
120
|
+
group?: string;
|
|
110
121
|
namespace: string;
|
|
111
122
|
name: string;
|
|
112
123
|
}) {
|
|
@@ -117,25 +128,31 @@ export function IssueDiagnoseButton({
|
|
|
117
128
|
<Tooltip
|
|
118
129
|
content={
|
|
119
130
|
d.setupState === "needs-restart"
|
|
120
|
-
? "Restart Radar to enable AI
|
|
131
|
+
? "Restart Radar to enable AI investigations — a supported agent is installed"
|
|
121
132
|
: !ready
|
|
122
|
-
? "Set up AI
|
|
133
|
+
? "Set up AI investigations — install a local agent"
|
|
123
134
|
: d.hosted
|
|
124
|
-
? `Sends this resource's context to ${d.agentLabel}
|
|
125
|
-
: `Runs ${d.agentLabel} on your machine
|
|
135
|
+
? `Sends this resource's context to ${d.agentLabel} for investigation`
|
|
136
|
+
: `Runs ${d.agentLabel} on your machine to investigate this resource`
|
|
126
137
|
}
|
|
127
138
|
position="left"
|
|
128
139
|
>
|
|
129
140
|
<button
|
|
130
141
|
onClick={(e) => {
|
|
131
142
|
e.stopPropagation();
|
|
132
|
-
if (ready)
|
|
143
|
+
if (ready)
|
|
144
|
+
d.openInvestigation({
|
|
145
|
+
kind,
|
|
146
|
+
group: group ?? "",
|
|
147
|
+
namespace,
|
|
148
|
+
name,
|
|
149
|
+
});
|
|
133
150
|
else d.openHome();
|
|
134
151
|
}}
|
|
135
152
|
className="flex shrink-0 items-center gap-1 rounded-md border border-theme-border px-2 py-1 text-xs text-theme-text-secondary hover:bg-theme-hover hover:text-theme-text-primary"
|
|
136
153
|
>
|
|
137
154
|
<Sparkles className="h-3 w-3 text-accent" />
|
|
138
|
-
|
|
155
|
+
Investigate
|
|
139
156
|
</button>
|
|
140
157
|
</Tooltip>
|
|
141
158
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Install metadata for the agent CLIs Radar can drive. Used by the setup notice
|
|
2
|
-
// shown when AI
|
|
2
|
+
// shown when AI investigations are eligible but no agent is installed. Names match the
|
|
3
3
|
// backend's supported set (see internal/ai/detect.go knownAgents + isSupportedAgent).
|
|
4
4
|
export interface AgentInstall {
|
|
5
5
|
name: string; // stable id, matches AgentInfo.name
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural contracts emitted by Radar evidence tools and consumed by the
|
|
3
|
+
* investigation projection. These deliberately exclude any presentation or
|
|
4
|
+
* transport envelope so the live Diagnose UI is not coupled to another route.
|
|
5
|
+
*/
|
|
6
|
+
export interface DiagnosisFilteredLogs {
|
|
7
|
+
lines: string[] | null;
|
|
8
|
+
totalLines: number;
|
|
9
|
+
matchedLines: number;
|
|
10
|
+
fallback: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface DiagnosisPodLogEntry {
|
|
14
|
+
pod: string;
|
|
15
|
+
container: string;
|
|
16
|
+
logs?: DiagnosisFilteredLogs;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DiagnosisPodContainerRef {
|
|
21
|
+
pod: string;
|
|
22
|
+
container: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DiagnosisCrashCause {
|
|
26
|
+
pods: string[];
|
|
27
|
+
container: string;
|
|
28
|
+
state: string;
|
|
29
|
+
reason?: string;
|
|
30
|
+
exitCode: number;
|
|
31
|
+
logLine: string;
|
|
32
|
+
logSource: string;
|
|
33
|
+
logLineSelection:
|
|
34
|
+
| "fatal_pattern"
|
|
35
|
+
| "traceback_header_only"
|
|
36
|
+
| "last_matched_line"
|
|
37
|
+
| "log_tail"
|
|
38
|
+
| string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface DiagnosisStartupBlocker {
|
|
42
|
+
kind: string;
|
|
43
|
+
name: string;
|
|
44
|
+
reason: string;
|
|
45
|
+
severity: string;
|
|
46
|
+
message: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface DiagnosisResourceRef {
|
|
50
|
+
kind: string;
|
|
51
|
+
group?: string;
|
|
52
|
+
namespace?: string;
|
|
53
|
+
name: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface DiagnosisResourceContext {
|
|
57
|
+
tier: "basic" | "diagnostic";
|
|
58
|
+
owner?: DiagnosisResourceRef;
|
|
59
|
+
managedBy?: DiagnosisResourceRef[];
|
|
60
|
+
exposes?: DiagnosisResourceRef[];
|
|
61
|
+
selectedBy?: DiagnosisResourceRef[];
|
|
62
|
+
referencedBy?: {
|
|
63
|
+
total: number;
|
|
64
|
+
items?: Array<DiagnosisResourceRef & { paths?: string[] }>;
|
|
65
|
+
truncated?: boolean;
|
|
66
|
+
};
|
|
67
|
+
uses?: {
|
|
68
|
+
configMaps?: DiagnosisResourceRef[];
|
|
69
|
+
secrets?: DiagnosisResourceRef[];
|
|
70
|
+
serviceAccount?: DiagnosisResourceRef;
|
|
71
|
+
pvcs?: DiagnosisResourceRef[];
|
|
72
|
+
};
|
|
73
|
+
runsOn?: DiagnosisResourceRef;
|
|
74
|
+
scaledBy?: DiagnosisResourceRef[];
|
|
75
|
+
statusSummary?: {
|
|
76
|
+
phase?: string;
|
|
77
|
+
conditions?: Array<{
|
|
78
|
+
type: string;
|
|
79
|
+
status: string;
|
|
80
|
+
reason?: string;
|
|
81
|
+
message?: string;
|
|
82
|
+
lastTransitionTime?: string;
|
|
83
|
+
}>;
|
|
84
|
+
};
|
|
85
|
+
workloadSummary?: {
|
|
86
|
+
replicas?: {
|
|
87
|
+
desired?: number;
|
|
88
|
+
ready?: number;
|
|
89
|
+
available?: number;
|
|
90
|
+
updated?: number;
|
|
91
|
+
unavailable?: number;
|
|
92
|
+
};
|
|
93
|
+
rolloutRisk?: {
|
|
94
|
+
reason: string;
|
|
95
|
+
replicas: number;
|
|
96
|
+
maxSurge: string;
|
|
97
|
+
maxUnavailable: string;
|
|
98
|
+
resolvedMaxSurge: number;
|
|
99
|
+
resolvedMaxUnavailable: number;
|
|
100
|
+
message: string;
|
|
101
|
+
action: string;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
appReferences?: {
|
|
105
|
+
staleSecretEnvTruncated?: boolean;
|
|
106
|
+
};
|
|
107
|
+
omitted?: Array<{
|
|
108
|
+
field: string;
|
|
109
|
+
reason: string;
|
|
110
|
+
}>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface DiagnosisChangeContext {
|
|
114
|
+
changed: boolean;
|
|
115
|
+
what?: string;
|
|
116
|
+
when?: string;
|
|
117
|
+
evidence?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface DiagnosisDNSContext {
|
|
121
|
+
signals?: string[];
|
|
122
|
+
coreDNSFindings?: Array<{
|
|
123
|
+
kind: string;
|
|
124
|
+
namespace: string;
|
|
125
|
+
name: string;
|
|
126
|
+
severity: string;
|
|
127
|
+
reason: string;
|
|
128
|
+
message?: string;
|
|
129
|
+
}>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type DiagnosisEvidenceTone =
|
|
133
|
+
"error" | "alert" | "warning" | "info" | "neutral";
|
|
134
|
+
|
|
135
|
+
export interface DiagnosisEvidenceLimitationBase {
|
|
136
|
+
source: string;
|
|
137
|
+
message: string;
|
|
138
|
+
kind: "error" | "truncated" | "unknown";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function diagnosisSeverityTone(severity: string): DiagnosisEvidenceTone {
|
|
142
|
+
const value = severity.toLowerCase();
|
|
143
|
+
if (value === "critical" || value === "error" || value === "failed")
|
|
144
|
+
return "error";
|
|
145
|
+
if (value === "high" || value === "alert") return "alert";
|
|
146
|
+
if (value === "medium" || value === "warning" || value === "warn")
|
|
147
|
+
return "warning";
|
|
148
|
+
if (value === "low" || value === "info" || value === "informational")
|
|
149
|
+
return "info";
|
|
150
|
+
return "neutral";
|
|
151
|
+
}
|