@skyhook-io/radar-app 0.2.2 → 0.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.
Files changed (177) hide show
  1. package/README.md +7 -1
  2. package/package.json +33 -25
  3. package/src/App.tsx +1449 -382
  4. package/src/RadarApp.tsx +132 -19
  5. package/src/api/apiResources.ts +1 -1
  6. package/src/api/client.argoResourceSync.test.ts +69 -0
  7. package/src/api/client.delta.test.ts +89 -0
  8. package/src/api/client.deltaSync.test.ts +216 -0
  9. package/src/api/client.metrics.test.ts +106 -0
  10. package/src/api/client.rightsizing.test.ts +32 -0
  11. package/src/api/client.ts +2730 -271
  12. package/src/api/client.yaml.test.ts +45 -0
  13. package/src/api/diagnose.ts +289 -0
  14. package/src/api/quotas.ts +16 -0
  15. package/src/api/rbac.ts +57 -0
  16. package/src/api/timelineSource.test.ts +217 -0
  17. package/src/api/timelineSource.ts +582 -0
  18. package/src/components/ConnectionErrorView.tsx +186 -70
  19. package/src/components/ContextSwitcher.tsx +63 -18
  20. package/src/components/DebugOverlay.tsx +5 -3
  21. package/src/components/NamespaceSwitcher.tsx +41 -0
  22. package/src/components/UserMenu.tsx +69 -21
  23. package/src/components/applications/ApplicationsView.tsx +936 -0
  24. package/src/components/audit/AuditSettingsDialog.tsx +79 -17
  25. package/src/components/audit/AuditView.tsx +65 -62
  26. package/src/components/compare/CompareViewRoute.tsx +124 -0
  27. package/src/components/compare/useCompareCandidates.ts +27 -0
  28. package/src/components/compare/useCompareLauncher.tsx +79 -0
  29. package/src/components/cost/ApplicationCostTab.test.ts +204 -0
  30. package/src/components/cost/ApplicationCostTab.tsx +571 -0
  31. package/src/components/cost/CostTrendChart.tsx +106 -75
  32. package/src/components/cost/CostView.test.ts +12 -0
  33. package/src/components/cost/CostView.tsx +507 -223
  34. package/src/components/cost/CostViewTabs.test.tsx +21 -0
  35. package/src/components/cost/CostViewTabs.tsx +40 -0
  36. package/src/components/cost/CurrentAllocationUse.test.ts +21 -0
  37. package/src/components/cost/CurrentAllocationUse.tsx +126 -0
  38. package/src/components/cost/WorkloadCostTab.test.ts +153 -0
  39. package/src/components/cost/WorkloadCostTab.tsx +372 -0
  40. package/src/components/cost/cloud-console.test.ts +39 -0
  41. package/src/components/cost/cloud-console.ts +81 -0
  42. package/src/components/cost/errors.ts +8 -0
  43. package/src/components/cost/format.test.ts +27 -0
  44. package/src/components/cost/format.ts +46 -0
  45. package/src/components/cost/kinds.ts +5 -0
  46. package/src/components/curl/ServiceCurlButton.tsx +445 -0
  47. package/src/components/diagnose/AISettings.tsx +147 -0
  48. package/src/components/diagnose/DiagnoseContext.tsx +495 -0
  49. package/src/components/diagnose/DiagnoseSurface.tsx +394 -0
  50. package/src/components/diagnose/Home.tsx +163 -0
  51. package/src/components/diagnose/InvestigationView.tsx +622 -0
  52. package/src/components/diagnose/LocalDiagnoseAction.tsx +162 -0
  53. package/src/components/diagnose/launch.ts +65 -0
  54. package/src/components/diagnose/parts.tsx +1756 -0
  55. package/src/components/dock/BottomDock.tsx +2 -3
  56. package/src/components/dock/DockContext.tsx +1 -0
  57. package/src/components/dock/TerminalTab.tsx +1 -1
  58. package/src/components/dock/WorkloadLogsTab.tsx +21 -5
  59. package/src/components/dock/index.ts +1 -1
  60. package/src/components/execution/BatchExecutionView.test.ts +170 -0
  61. package/src/components/execution/BatchExecutionView.tsx +1329 -0
  62. package/src/components/execution/batch-run-actions.test.ts +48 -0
  63. package/src/components/execution/batch-run-actions.ts +24 -0
  64. package/src/components/execution/batch-timeline.test.ts +57 -0
  65. package/src/components/execution/batch-timeline.ts +46 -0
  66. package/src/components/execution/execution-definition.test.ts +208 -0
  67. package/src/components/execution/execution-definition.ts +245 -0
  68. package/src/components/gitops/ArgoResourceDiffLoader.tsx +23 -0
  69. package/src/components/gitops/GitOpsView.tsx +1042 -0
  70. package/src/components/gitops/RevisionMetaChip.tsx +63 -0
  71. package/src/components/helm/ChartBrowser.tsx +87 -31
  72. package/src/components/helm/HelmCompareRoute.tsx +1341 -0
  73. package/src/components/helm/HelmReleaseDrawer.test.ts +17 -0
  74. package/src/components/helm/HelmReleaseDrawer.tsx +1073 -102
  75. package/src/components/helm/HelmView.tsx +237 -96
  76. package/src/components/helm/InstallWizard.tsx +94 -38
  77. package/src/components/helm/ManifestDiffViewer.tsx +8 -27
  78. package/src/components/helm/OwnedResources.tsx +34 -59
  79. package/src/components/helm/RevisionHistory.tsx +52 -3
  80. package/src/components/helm/RoleGatedPanel.tsx +3 -3
  81. package/src/components/helm/TrackChartSourceDialog.tsx +185 -0
  82. package/src/components/helm/ValuesDiffPreview.tsx +17 -7
  83. package/src/components/helm/ValuesViewer.tsx +49 -53
  84. package/src/components/helm/helm-utils.ts +4 -0
  85. package/src/components/home/ActivitySummary.tsx +4 -1
  86. package/src/components/home/ClusterHealthCard.tsx +56 -42
  87. package/src/components/home/CostCard.tsx +21 -36
  88. package/src/components/home/GitOpsControllersCard.tsx +110 -0
  89. package/src/components/home/HelmSummary.tsx +3 -1
  90. package/src/components/home/HomeView.tsx +339 -105
  91. package/src/components/home/MCPSetupDialog.tsx +29 -87
  92. package/src/components/home/TrafficSummary.tsx +2 -2
  93. package/src/components/home/mcpToolCatalog.ts +333 -0
  94. package/src/components/issues/IssuesPane.tsx +151 -0
  95. package/src/components/logs/LogsViewer.tsx +4 -1
  96. package/src/components/logs/ScheduledWorkloadLogsViewer.tsx +135 -0
  97. package/src/components/logs/WorkloadLogsViewer.tsx +4 -1
  98. package/src/components/nav/PrimaryNavRail.tsx +285 -0
  99. package/src/components/portforward/PortForwardButton.tsx +118 -47
  100. package/src/components/portforward/PortForwardManager.tsx +253 -131
  101. package/src/components/resource/HPACharts.tsx +237 -0
  102. package/src/components/resource/PVCUsageBar.tsx +59 -0
  103. package/src/components/resource/PrometheusCharts.tsx +160 -584
  104. package/src/components/resource/PrometheusChartsGrid.tsx +270 -0
  105. package/src/components/resource/RestartChart.tsx +133 -0
  106. package/src/components/resource/RightsizingStrip.test.ts +109 -0
  107. package/src/components/resource/RightsizingStrip.tsx +363 -0
  108. package/src/components/resource-drawer/ResourceDrawer.tsx +3 -1
  109. package/src/components/resources/CompositeRenderer.tsx +101 -0
  110. package/src/components/resources/ImageFilesystemModal.tsx +19 -12
  111. package/src/components/resources/PodFilesystemModal.tsx +6 -5
  112. package/src/components/resources/ResourceDetailDrawer.tsx +13 -3
  113. package/src/components/resources/ResourcesView.tsx +194 -17
  114. package/src/components/resources/renderers/CronWorkflowRenderer.tsx +1 -0
  115. package/src/components/resources/renderers/HPARenderer.tsx +20 -1
  116. package/src/components/resources/renderers/NamespaceRenderer.tsx +31 -0
  117. package/src/components/resources/renderers/NodeRenderer.tsx +10 -4
  118. package/src/components/resources/renderers/PVCRenderer.tsx +19 -1
  119. package/src/components/resources/renderers/PodRenderer.tsx +30 -6
  120. package/src/components/resources/renderers/RoleBindingRenderer.tsx +45 -1
  121. package/src/components/resources/renderers/RoleRenderer.tsx +27 -1
  122. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +28 -1
  123. package/src/components/resources/renderers/ServiceRenderer.tsx +81 -8
  124. package/src/components/resources/renderers/WorkloadRenderer.tsx +51 -4
  125. package/src/components/resources/renderers/index.ts +2 -0
  126. package/src/components/resources/resource-utils.ts +2 -1
  127. package/src/components/rightsizing/RightsizingScanView.tsx +938 -0
  128. package/src/components/rightsizing/copy.test.ts +56 -0
  129. package/src/components/rightsizing/model.test.ts +227 -0
  130. package/src/components/rightsizing/model.ts +158 -0
  131. package/src/components/rightsizing/presentation.test.ts +104 -0
  132. package/src/components/rightsizing/presentation.ts +94 -0
  133. package/src/components/settings/MyPermissionsDialog.tsx +241 -0
  134. package/src/components/settings/SettingsDialog.tsx +1505 -165
  135. package/src/components/shared/CreateResourceDialog.tsx +9 -2
  136. package/src/components/shared/LargeClusterNamespacePicker.tsx +3 -3
  137. package/src/components/timeline/LocalTimelineScrubber.tsx +212 -0
  138. package/src/components/timeline/RetainedTimelineScrubber.tsx +311 -0
  139. package/src/components/timeline/TimelineList.tsx +86 -13
  140. package/src/components/timeline/TimelineSwimlanes.tsx +9 -1299
  141. package/src/components/timeline/TimelineView.tsx +873 -24
  142. package/src/components/timeline/TimelineView.urlparams.test.ts +335 -0
  143. package/src/components/traffic/TrafficFilterSidebar.tsx +10 -45
  144. package/src/components/traffic/TrafficFlowList.tsx +29 -15
  145. package/src/components/traffic/TrafficGraph.tsx +42 -24
  146. package/src/components/traffic/TrafficView.tsx +32 -19
  147. package/src/components/ui/CommandPalette.tsx +8 -215
  148. package/src/components/ui/DiagnosticsOverlay.tsx +219 -9
  149. package/src/components/ui/Markdown.tsx +3 -3
  150. package/src/components/ui/Omnibar.tsx +602 -0
  151. package/src/components/ui/RadarOmnibar.tsx +52 -0
  152. package/src/components/ui/SearchSyntaxHelp.tsx +89 -0
  153. package/src/components/ui/ShortcutHelpOverlay.tsx +3 -2
  154. package/src/components/ui/UpdateNotification.tsx +48 -36
  155. package/src/components/ui/command-items.ts +178 -0
  156. package/src/components/workload/WorkloadView.tsx +1342 -158
  157. package/src/context/ConnectionContext.tsx +146 -21
  158. package/src/context/DiagnoseCustomization.tsx +93 -0
  159. package/src/context/NavCustomization.tsx +75 -0
  160. package/src/context/TimelineSource.tsx +50 -0
  161. package/src/contexts/CapabilitiesContext.tsx +32 -8
  162. package/src/filter/FilterLocationBridge.tsx +30 -0
  163. package/src/hooks/useClusterLoadState.ts +73 -0
  164. package/src/hooks/useDocumentTitle.ts +25 -0
  165. package/src/hooks/useEventSource.ts +6 -0
  166. package/src/hooks/useKeyboardShortcuts.tsx +1 -0
  167. package/src/hooks/useMediaQuery.ts +21 -0
  168. package/src/hooks/useNavRailPinned.ts +46 -0
  169. package/src/hooks/useRecentResources.ts +49 -0
  170. package/src/index.css +162 -1
  171. package/src/index.ts +73 -1
  172. package/src/main.tsx +7 -5
  173. package/src/types/clusterLoadState.ts +33 -0
  174. package/src/types.ts +2 -0
  175. package/src/utils/auditBadges.ts +53 -0
  176. package/src/utils/navigation.ts +64 -1
  177. package/src/components/ui/NamespaceSelector.tsx +0 -436
@@ -0,0 +1,162 @@
1
+ import { Loader2, Sparkles } from "lucide-react";
2
+ import { useDiagnose, useDiagnoseLayout, runTargetKey } from "./DiagnoseContext";
3
+ import { Tooltip } from "../ui/Tooltip";
4
+ import type { RenderDiagnoseAction } from "../../context/DiagnoseCustomization";
5
+
6
+ // The per-resource AI entry point. It no longer owns a panel — it just dispatches
7
+ // to the single app-level AI surface (DiagnoseContext), opening a new investigation
8
+ // for this resource. Self-hides when no agent CLI is present. Hosts can override
9
+ // this slot with their own action.
10
+ //
11
+ // Adaptive by health: on a resource with a live problem it reads as a prominent
12
+ // "Diagnose" (find the root cause); when the resource is fine or health is unknown
13
+ // it shrinks to a quiet colored-icon affordance ("ask my agent about this") — so it
14
+ // never implies "something is wrong here" on a healthy resource. The tooltip leads
15
+ // with the BYO framing (the user's OWN agent, locally) — unless the agent is
16
+ // hosted, where those claims would be false.
17
+ function DiagnoseResourceButton({
18
+ kind,
19
+ namespace,
20
+ name,
21
+ health,
22
+ }: {
23
+ kind: string;
24
+ namespace: string;
25
+ name: string;
26
+ health?: "problem" | "healthy" | "unknown";
27
+ }) {
28
+ const d = useDiagnose();
29
+ const { runningKeys } = useDiagnoseLayout();
30
+ if (!d.available) return null;
31
+ const problem = health === "problem";
32
+ const running = runningKeys.has(runTargetKey(kind, namespace, name));
33
+ const tooltip = running
34
+ ? `${d.agentLabel} is investigating this resource — click to watch it live.`
35
+ : d.hosted
36
+ ? problem
37
+ ? `Diagnose with ${d.agentLabel} — reads this resource's spec, events & logs to find the root cause.`
38
+ : `Ask ${d.agentLabel} about this resource — reads its spec, events & logs.`
39
+ : problem
40
+ ? `Diagnose with your own ${d.agentLabel} — runs locally, reads this resource's spec, events & logs to find the root cause.`
41
+ : `Ask your own ${d.agentLabel} about this resource — runs locally, reads its spec, events & logs.`;
42
+ // While an investigation is live, the button advertises it (and clicking focuses
43
+ // the existing run rather than starting a new one — openInvestigation dedups).
44
+ const showLabel = problem || running;
45
+ return (
46
+ <Tooltip content={tooltip} position="bottom">
47
+ <button
48
+ onClick={() => d.openInvestigation({ kind, namespace, name })}
49
+ aria-label={
50
+ running
51
+ ? "Investigation running — click to view"
52
+ : problem
53
+ ? "Diagnose with AI"
54
+ : "Ask AI about this resource"
55
+ }
56
+ className={
57
+ showLabel
58
+ ? "inline-flex items-center gap-1.5 rounded-lg border border-accent/40 bg-accent/5 px-2.5 py-1.5 text-sm font-medium text-accent hover:bg-accent/10"
59
+ : "inline-flex items-center rounded-lg border border-theme-border p-1.5 text-theme-text-secondary hover:bg-theme-hover hover:text-theme-text-primary"
60
+ }
61
+ >
62
+ {running ? (
63
+ <Loader2 className="h-3.5 w-3.5 animate-spin text-accent" />
64
+ ) : (
65
+ <Sparkles className="h-3.5 w-3.5 text-accent" />
66
+ )}
67
+ {running ? "Investigating…" : problem && "Diagnose"}
68
+ </button>
69
+ </Tooltip>
70
+ );
71
+ }
72
+
73
+ export const defaultDiagnoseAction: RenderDiagnoseAction = ({
74
+ kind,
75
+ namespace,
76
+ name,
77
+ health,
78
+ }) => (
79
+ <DiagnoseResourceButton
80
+ kind={kind}
81
+ namespace={namespace}
82
+ name={name}
83
+ health={health}
84
+ />
85
+ );
86
+
87
+ // Compact per-issue "Diagnose" action for the Issues queue — launches an
88
+ // investigation for the issue's subject from where the problem is surfaced.
89
+ // stopPropagation so it doesn't toggle the issue row it lives in.
90
+ export function IssueDiagnoseButton({
91
+ kind,
92
+ namespace,
93
+ name,
94
+ }: {
95
+ kind: string;
96
+ namespace: string;
97
+ name: string;
98
+ }) {
99
+ const d = useDiagnose();
100
+ if (!d.available) return null;
101
+ return (
102
+ <Tooltip
103
+ content={
104
+ d.hosted
105
+ ? `Sends this resource's context to ${d.agentLabel} to find the root cause`
106
+ : `Runs ${d.agentLabel} on your machine and sends it this resource's context to find the root cause`
107
+ }
108
+ position="left"
109
+ >
110
+ <button
111
+ onClick={(e) => {
112
+ e.stopPropagation();
113
+ d.openInvestigation({ kind, namespace, name });
114
+ }}
115
+ 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"
116
+ >
117
+ <Sparkles className="h-3 w-3 text-accent" />
118
+ Diagnose
119
+ </button>
120
+ </Tooltip>
121
+ );
122
+ }
123
+
124
+ // Global top-bar entry into the AI surface (opens its Home / recent
125
+ // investigations). Self-hides when no agent CLI is present.
126
+ export function GlobalDiagnoseButton() {
127
+ const d = useDiagnose();
128
+ const { runningKeys } = useDiagnoseLayout();
129
+ if (!d.available) return null;
130
+ const runningCount = runningKeys.size;
131
+ const agentSuffix = d.hosted
132
+ ? `powered by ${d.agentLabel}`
133
+ : `runs your own ${d.agentLabel} locally`;
134
+ return (
135
+ <Tooltip
136
+ content={
137
+ runningCount > 0
138
+ ? `${runningCount} investigation${runningCount > 1 ? "s" : ""} running — ${agentSuffix}`
139
+ : `AI investigations — ${agentSuffix}`
140
+ }
141
+ position="bottom"
142
+ >
143
+ <button
144
+ onClick={d.openHome}
145
+ 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"
146
+ aria-label={
147
+ runningCount > 0
148
+ ? `AI investigations (${runningCount} running)`
149
+ : "AI investigations"
150
+ }
151
+ >
152
+ <Sparkles className="h-4 w-4 text-accent" />
153
+ {runningCount > 0 && (
154
+ <span className="absolute right-0.5 top-0.5 flex h-2 w-2">
155
+ <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-accent opacity-75" />
156
+ <span className="relative inline-flex h-2 w-2 rounded-full bg-accent" />
157
+ </span>
158
+ )}
159
+ </button>
160
+ </Tooltip>
161
+ );
162
+ }
@@ -0,0 +1,65 @@
1
+ // Builds the shell command that hands an investigation off to the user's own
2
+ // interactive agent CLI, pointed at Radar's MCP. This is the "escape hatch" to the
3
+ // full agent (their config, their other MCPs, interactive approvals) — distinct
4
+ // from the contained in-panel engine. The launched agent gets the full /mcp mount
5
+ // (read+write); its own per-tool approval prompts gate any changes.
6
+ import { type RunSummary } from "../../api/diagnose";
7
+
8
+ // sq POSIX-single-quotes a string so it's a safe single shell argument regardless
9
+ // of content. Findings carry backticks, quotes, and newlines — inside single
10
+ // quotes the shell treats everything literally; embedded single quotes become
11
+ // the standard '\'' sequence. Never build the command without this.
12
+ function sq(s: string): string {
13
+ return "'" + s.replace(/'/g, "'\\''") + "'";
14
+ }
15
+
16
+ export function launchAgentLabel(run: RunSummary): string {
17
+ if (run.agent === "codex") return "Codex";
18
+ if (run.agent === "cursor-agent") return "Cursor";
19
+ return "Claude Code";
20
+ }
21
+
22
+ // openInTerminal asks App (which owns the DockProvider) to open Radar's local
23
+ // terminal running `command`. The AI surface is portaled above the DockProvider,
24
+ // so it can't use the dock hook directly — it dispatches this event instead.
25
+ export function openInTerminal(command: string, title: string) {
26
+ window.dispatchEvent(
27
+ new CustomEvent("radar:open-local-terminal", {
28
+ detail: { command, title },
29
+ }),
30
+ );
31
+ }
32
+
33
+ // buildLaunchCommand resumes the investigation's ACTUAL agent session interactively
34
+ // (not a re-seeded prompt), so the full transcript — every tool call, finding, and
35
+ // the agent's reasoning — carries over. Returns null when there's no resumable
36
+ // session yet (first turn still running) or the run is stale (different cluster).
37
+ // The MCP server is re-attached on resume (it's configured per-launch, not stored
38
+ // in the session).
39
+ export function buildLaunchCommand(
40
+ run: RunSummary,
41
+ mcpUrl: string,
42
+ ): string | null {
43
+ if (!run.sessionId || run.status === "stale") return null;
44
+
45
+ if (run.agent === "cursor-agent") {
46
+ // Cursor's --resume is workspace-scoped, and Radar runs each investigation in a
47
+ // throwaway per-run workspace the user doesn't have — no command can reattach
48
+ // both that session and Radar's MCP in the user's own terminal. So there's no
49
+ // hand-off for Cursor; the in-panel investigation is self-contained.
50
+ return null;
51
+ }
52
+ if (run.agent === "codex") {
53
+ // Codex threads are stored globally by id (cwd-independent); -c re-attaches MCP.
54
+ return `codex resume ${sq(run.sessionId)} -c ${sq(`mcp_servers.radar.url="${mcpUrl}"`)}`;
55
+ }
56
+ // Claude Code: --resume is cwd-scoped, and Radar runs its headless sessions from
57
+ // the home dir (see claudeAgent). The Radar terminal starts there too, but a user
58
+ // pasting this into their own terminal is usually in a project dir — so prefix
59
+ // `cd ~` to resolve the session wherever it's run (harmless in the home-dir case).
60
+ // --mcp-config MERGES radar alongside the user's own servers.
61
+ const cfg = JSON.stringify({
62
+ mcpServers: { radar: { type: "http", url: mcpUrl } },
63
+ });
64
+ return `cd ~ && claude --resume ${sq(run.sessionId)} --mcp-config ${sq(cfg)}`;
65
+ }