@skyhook-io/radar-app 1.8.9 → 1.8.10
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyhook-io/radar-app",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.10",
|
|
4
4
|
"description": "Radar's full web UI as a reusable React component. Used by Radar's own binary and by external consumers like Radar Cloud.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"diff": "^9.0.0",
|
|
36
36
|
"monaco-editor": "^0.55.1",
|
|
37
37
|
"react-markdown": "^10.1.0",
|
|
38
|
-
"react-virtuoso": "^4.18.
|
|
38
|
+
"react-virtuoso": "^4.18.11",
|
|
39
39
|
"remark-gfm": "^4.0.1",
|
|
40
40
|
"shiki": "^4.2.0",
|
|
41
41
|
"yaml": "^2.9.0"
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@tailwindcss/typography": "^0.5.20",
|
|
60
60
|
"@tailwindcss/vite": "^4.3.1",
|
|
61
61
|
"@tanstack/react-query": "^5.101.2",
|
|
62
|
-
"@types/node": "^26.
|
|
62
|
+
"@types/node": "^26.1.1",
|
|
63
63
|
"@types/react": "^19.2.17",
|
|
64
64
|
"@types/react-dom": "^19.2.3",
|
|
65
65
|
"@vitejs/plugin-react": "^6.0.2",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"tailwindcss": "^4.3.1",
|
|
81
81
|
"typescript": "^6.0.2",
|
|
82
82
|
"typescript-eslint": "^8.62.0",
|
|
83
|
-
"vite": "^8.
|
|
83
|
+
"vite": "^8.1.5"
|
|
84
84
|
},
|
|
85
85
|
"sideEffects": [
|
|
86
86
|
"*.css",
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
agentLabelFor,
|
|
24
24
|
openDiagnoseSettings,
|
|
25
25
|
} from "./DiagnoseContext";
|
|
26
|
-
import {
|
|
26
|
+
import { useDiagnoseCustomization } from "../../context/DiagnoseCustomization";
|
|
27
27
|
import { InvestigationView } from "./InvestigationView";
|
|
28
28
|
import { RecentList } from "./Home";
|
|
29
29
|
import { ConsentCard } from "./parts";
|
|
@@ -137,7 +137,11 @@ function InvestigationMenu({ run }: { run: RunSummary }) {
|
|
|
137
137
|
// Helm drawers, so it no longer floats viewport-fixed or DOM-measures the chrome.
|
|
138
138
|
export function DiagnoseSurface({ topInset = 0 }: { topInset?: number }) {
|
|
139
139
|
const d = useDiagnose();
|
|
140
|
-
|
|
140
|
+
// Injected settings action: undefined = Radar's own Settings dialog;
|
|
141
|
+
// null = hide the gear + links.
|
|
142
|
+
const { consentCopy, onOpenSettings: hostOpenSettings } = useDiagnoseCustomization();
|
|
143
|
+
const openSettings =
|
|
144
|
+
hostOpenSettings === undefined ? openDiagnoseSettings : hostOpenSettings;
|
|
141
145
|
const {
|
|
142
146
|
maximized,
|
|
143
147
|
setMaximized,
|
|
@@ -206,7 +210,7 @@ export function DiagnoseSurface({ topInset = 0 }: { topInset?: number }) {
|
|
|
206
210
|
agent={d.selectedAgent}
|
|
207
211
|
isolated={d.isolated}
|
|
208
212
|
copy={consentCopy}
|
|
209
|
-
onOpenSettings={
|
|
213
|
+
onOpenSettings={openSettings ?? undefined}
|
|
210
214
|
onApprove={d.approveConsent}
|
|
211
215
|
onCancel={d.cancelConsent}
|
|
212
216
|
/>
|
|
@@ -306,15 +310,17 @@ export function DiagnoseSurface({ topInset = 0 }: { topInset?: number }) {
|
|
|
306
310
|
</div>
|
|
307
311
|
<div className="flex items-center gap-1 text-xs text-theme-text-tertiary">
|
|
308
312
|
<span className="truncate">{configLine}</span>
|
|
309
|
-
|
|
310
|
-
<
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
313
|
+
{openSettings && (
|
|
314
|
+
<Tooltip content="AI settings" position="bottom">
|
|
315
|
+
<button
|
|
316
|
+
onClick={openSettings}
|
|
317
|
+
className="shrink-0 rounded p-0.5 text-theme-text-tertiary hover:text-theme-text-primary"
|
|
318
|
+
aria-label="AI settings"
|
|
319
|
+
>
|
|
320
|
+
<Settings2 className="h-3 w-3" />
|
|
321
|
+
</button>
|
|
322
|
+
</Tooltip>
|
|
323
|
+
)}
|
|
318
324
|
</div>
|
|
319
325
|
</div>
|
|
320
326
|
</div>
|
|
@@ -96,13 +96,13 @@ export const MCP_TOOL_CATALOG: MCPToolInfo[] = [
|
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
name: 'get_pod_logs',
|
|
99
|
-
desc: '
|
|
99
|
+
desc: 'Pod logs with secret redaction. Without grep, prioritizes errors and warnings and falls back to recent tail lines; grep returns only regex matches instead.',
|
|
100
100
|
params: [
|
|
101
101
|
{ arg: 'namespace', required: true, desc: 'pod namespace' },
|
|
102
102
|
{ arg: 'name', required: true, desc: 'pod name' },
|
|
103
103
|
{ arg: 'container', desc: 'container name (defaults to first)' },
|
|
104
104
|
{ arg: 'tail_lines', desc: 'lines from end (default 200)' },
|
|
105
|
-
{ arg: 'grep', desc: 'regex to
|
|
105
|
+
{ arg: 'grep', desc: 'regex matches to return instead of diagnostic auto-filtering' },
|
|
106
106
|
{ arg: 'since', desc: 'only logs newer than this duration (e.g. 30s, 10m, 1h)' },
|
|
107
107
|
{ arg: 'previous', desc: 'logs from the previous terminated container (CrashLoopBackOff)' },
|
|
108
108
|
],
|
|
@@ -194,11 +194,17 @@ export const MCP_TOOL_CATALOG: MCPToolInfo[] = [
|
|
|
194
194
|
},
|
|
195
195
|
{
|
|
196
196
|
name: 'get_subject_permissions',
|
|
197
|
-
desc: 'Effective RBAC for a ServiceAccount, User, or Group
|
|
197
|
+
desc: 'Effective RBAC for a ServiceAccount, User, or Group. Returns the full bindings/rules dump by default, or an authoritative SubjectAccessReview answer when verb and resource are supplied for a ServiceAccount.',
|
|
198
198
|
params: [
|
|
199
199
|
{ arg: 'kind', required: true, desc: 'ServiceAccount, User, or Group' },
|
|
200
200
|
{ arg: 'name', required: true, desc: 'subject name' },
|
|
201
201
|
{ arg: 'namespace', desc: 'required for ServiceAccount; omit for User/Group' },
|
|
202
|
+
{ arg: 'verb', desc: 'access check: Kubernetes API verb; requires resource' },
|
|
203
|
+
{ arg: 'resource', desc: 'access check: plural API resource; requires verb' },
|
|
204
|
+
{ arg: 'group', desc: 'access check: API group; omit for core/v1' },
|
|
205
|
+
{ arg: 'resource_namespace', desc: 'access check target; defaults to subject namespace' },
|
|
206
|
+
{ arg: 'subresource', desc: 'access check subresource, e.g. log for pods/log' },
|
|
207
|
+
{ arg: 'resource_name', desc: 'access check target resource name' },
|
|
202
208
|
],
|
|
203
209
|
},
|
|
204
210
|
{
|
|
@@ -237,14 +243,14 @@ export const MCP_TOOL_CATALOG: MCPToolInfo[] = [
|
|
|
237
243
|
},
|
|
238
244
|
{
|
|
239
245
|
name: 'get_workload_logs',
|
|
240
|
-
desc: 'Aggregated
|
|
246
|
+
desc: 'Aggregated logs across all pods of a workload. Without grep, filters for diagnostic relevance; grep returns only matching timestamp-prefixed lines instead.',
|
|
241
247
|
params: [
|
|
242
248
|
{ arg: 'kind', desc: 'deployment (default), statefulset, daemonset, job, or workflow' },
|
|
243
249
|
{ arg: 'namespace', required: true, desc: 'workload namespace' },
|
|
244
250
|
{ arg: 'name', required: true, desc: 'workload name' },
|
|
245
251
|
{ arg: 'container', desc: 'specific container (defaults to all)' },
|
|
246
252
|
{ arg: 'tail_lines', desc: 'lines per pod (default 100)' },
|
|
247
|
-
{ arg: 'grep', desc: 'regex to
|
|
253
|
+
{ arg: 'grep', desc: 'regex matches to return instead of diagnostic auto-filtering' },
|
|
248
254
|
{ arg: 'since', desc: 'only logs newer than this duration' },
|
|
249
255
|
{ arg: 'previous', desc: 'logs from the previous terminated container' },
|
|
250
256
|
],
|
|
@@ -288,7 +288,7 @@ function useActionsBarProps(kind: string, namespace: string, name: string) {
|
|
|
288
288
|
const uncordonMutation = useUncordonNode()
|
|
289
289
|
const drainMutation = useDrainNode()
|
|
290
290
|
|
|
291
|
-
const renderDiagnose = useDiagnoseCustomization()
|
|
291
|
+
const { renderAction: renderDiagnose } = useDiagnoseCustomization()
|
|
292
292
|
|
|
293
293
|
return {
|
|
294
294
|
canExec,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Default (no provider): Radar renders no Diagnose button — OSS stays
|
|
11
11
|
// agent-free.
|
|
12
|
-
import { createContext, useContext } from 'react';
|
|
12
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
13
13
|
import type { ReactNode } from 'react';
|
|
14
14
|
|
|
15
15
|
/** Render prop for the resource-level Diagnose action. */
|
|
@@ -46,31 +46,48 @@ export type DiagnoseConsentCopy = {
|
|
|
46
46
|
approveLabel?: string;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// One context for the whole customization seam — the values are host config
|
|
50
|
+
// set once at mount, so per-value re-render isolation buys nothing.
|
|
51
|
+
export interface DiagnoseCustomization {
|
|
52
|
+
renderAction: RenderDiagnoseAction | undefined;
|
|
53
|
+
consentCopy: DiagnoseConsentCopy | undefined;
|
|
54
|
+
// undefined = default (CustomEvent → Radar's own Settings dialog);
|
|
55
|
+
// null = hide the settings affordances.
|
|
56
|
+
onOpenSettings: (() => void) | null | undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const DEFAULTS: DiagnoseCustomization = {
|
|
60
|
+
renderAction: undefined,
|
|
61
|
+
consentCopy: undefined,
|
|
62
|
+
onOpenSettings: undefined,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const DiagnoseCustomizationContext = createContext<DiagnoseCustomization>(DEFAULTS);
|
|
51
66
|
|
|
52
67
|
export function DiagnoseCustomizationProvider({
|
|
53
68
|
value,
|
|
54
69
|
consentCopy,
|
|
70
|
+
onOpenSettings,
|
|
55
71
|
children,
|
|
56
72
|
}: {
|
|
57
73
|
value: RenderDiagnoseAction | undefined;
|
|
58
74
|
consentCopy?: DiagnoseConsentCopy;
|
|
75
|
+
/** Where "AI settings" affordances lead. Omit for Radar's own Settings
|
|
76
|
+
* dialog; pass `null` to hide them. */
|
|
77
|
+
onOpenSettings?: (() => void) | null;
|
|
59
78
|
children: ReactNode;
|
|
60
79
|
}) {
|
|
80
|
+
const ctx = useMemo(
|
|
81
|
+
() => ({ renderAction: value, consentCopy, onOpenSettings }),
|
|
82
|
+
[value, consentCopy, onOpenSettings],
|
|
83
|
+
);
|
|
61
84
|
return (
|
|
62
|
-
<DiagnoseCustomizationContext.Provider value={
|
|
63
|
-
|
|
64
|
-
{children}
|
|
65
|
-
</DiagnoseConsentCopyContext.Provider>
|
|
85
|
+
<DiagnoseCustomizationContext.Provider value={ctx}>
|
|
86
|
+
{children}
|
|
66
87
|
</DiagnoseCustomizationContext.Provider>
|
|
67
88
|
);
|
|
68
89
|
}
|
|
69
90
|
|
|
70
|
-
export function useDiagnoseCustomization():
|
|
91
|
+
export function useDiagnoseCustomization(): DiagnoseCustomization {
|
|
71
92
|
return useContext(DiagnoseCustomizationContext);
|
|
72
93
|
}
|
|
73
|
-
|
|
74
|
-
export function useDiagnoseConsentCopy(): DiagnoseConsentCopy | undefined {
|
|
75
|
-
return useContext(DiagnoseConsentCopyContext);
|
|
76
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,24 @@ export type {
|
|
|
30
30
|
RenderDiagnoseAction,
|
|
31
31
|
DiagnoseConsentCopy,
|
|
32
32
|
} from './context/DiagnoseCustomization';
|
|
33
|
+
|
|
34
|
+
// Standalone AI-diagnose surface — mount the investigation panel outside a
|
|
35
|
+
// full <RadarApp>. No router dependency, no client-side cluster state: the
|
|
36
|
+
// backend set via setApiBase() picks the cluster, so hosts remount
|
|
37
|
+
// <DiagnoseProvider key={cluster}> to switch. Mount order: ThemeProvider >
|
|
38
|
+
// DiagnoseCustomizationProvider > DiagnoseProvider > DiagnoseSurface, under a
|
|
39
|
+
// @tanstack/react-query QueryClientProvider.
|
|
40
|
+
export {
|
|
41
|
+
DiagnoseProvider,
|
|
42
|
+
useDiagnose,
|
|
43
|
+
useDiagnoseLayout,
|
|
44
|
+
} from './components/diagnose/DiagnoseContext';
|
|
45
|
+
export type { Target as DiagnoseTarget } from './components/diagnose/DiagnoseContext';
|
|
46
|
+
export { DiagnoseSurface } from './components/diagnose/DiagnoseSurface';
|
|
47
|
+
export { DiagnoseCustomizationProvider } from './context/DiagnoseCustomization';
|
|
48
|
+
// The panel reads Radar's ThemeContext (throws unprovided). It follows
|
|
49
|
+
// localStorage['radar-theme'], so host and panel flip together.
|
|
50
|
+
export { ThemeProvider } from './context/ThemeContext';
|
|
33
51
|
export { ShortcutHelpOverlay } from './components/ui/ShortcutHelpOverlay';
|
|
34
52
|
|
|
35
53
|
// Shared cluster-switcher primitive — re-exported from @skyhook-io/k8s-ui so
|