@skyhook-io/radar-app 0.1.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 (251) hide show
  1. package/README.md +67 -0
  2. package/package.json +80 -0
  3. package/src/App.tsx +1538 -0
  4. package/src/RadarApp.tsx +145 -0
  5. package/src/api/apiResources.ts +28 -0
  6. package/src/api/client.ts +2583 -0
  7. package/src/api/config.ts +116 -0
  8. package/src/api/traffic.ts +139 -0
  9. package/src/components/ConnectionErrorView.tsx +272 -0
  10. package/src/components/ContextSwitcher.tsx +481 -0
  11. package/src/components/DebugOverlay.tsx +94 -0
  12. package/src/components/UserMenu.tsx +87 -0
  13. package/src/components/audit/AuditSettingsDialog.tsx +162 -0
  14. package/src/components/audit/AuditView.tsx +123 -0
  15. package/src/components/cost/CostTrendChart.tsx +388 -0
  16. package/src/components/cost/CostView.tsx +545 -0
  17. package/src/components/dock/BottomDock.tsx +96 -0
  18. package/src/components/dock/DockContext.tsx +11 -0
  19. package/src/components/dock/LocalTerminalTab.tsx +22 -0
  20. package/src/components/dock/LogsTab.tsx +26 -0
  21. package/src/components/dock/NodeTerminalTab.tsx +50 -0
  22. package/src/components/dock/TerminalTab.tsx +42 -0
  23. package/src/components/dock/TrafficFlowListTab.tsx +18 -0
  24. package/src/components/dock/WorkloadLogsTab.tsx +23 -0
  25. package/src/components/dock/index.ts +2 -0
  26. package/src/components/gitops/GitOpsActions.tsx +1 -0
  27. package/src/components/gitops/GitOpsStatusBadge.tsx +1 -0
  28. package/src/components/gitops/ManagedResourcesList.tsx +1 -0
  29. package/src/components/gitops/SyncCountdown.tsx +1 -0
  30. package/src/components/gitops/index.ts +4 -0
  31. package/src/components/helm/ChartBrowser.tsx +580 -0
  32. package/src/components/helm/HelmReleaseDrawer.tsx +774 -0
  33. package/src/components/helm/HelmView.tsx +475 -0
  34. package/src/components/helm/InstallWizard.tsx +1060 -0
  35. package/src/components/helm/ManifestDiffViewer.tsx +91 -0
  36. package/src/components/helm/ManifestViewer.tsx +61 -0
  37. package/src/components/helm/OwnedResources.tsx +465 -0
  38. package/src/components/helm/RevisionHistory.tsx +167 -0
  39. package/src/components/helm/ValuesDiffPreview.tsx +190 -0
  40. package/src/components/helm/ValuesViewer.tsx +365 -0
  41. package/src/components/helm/helm-utils.ts +37 -0
  42. package/src/components/home/ActivitySummary.tsx +262 -0
  43. package/src/components/home/CertificateHealthCard.tsx +105 -0
  44. package/src/components/home/ClusterHealthCard.tsx +483 -0
  45. package/src/components/home/CostCard.tsx +112 -0
  46. package/src/components/home/HealthRing.tsx +1 -0
  47. package/src/components/home/HelmSummary.tsx +129 -0
  48. package/src/components/home/HomeView.tsx +224 -0
  49. package/src/components/home/MCPSetupDialog.tsx +417 -0
  50. package/src/components/home/NetworkPolicyCoverageCard.tsx +109 -0
  51. package/src/components/home/TopologyPreview.tsx +219 -0
  52. package/src/components/home/TrafficSummary.tsx +154 -0
  53. package/src/components/logs/JsonLogLine.tsx +1 -0
  54. package/src/components/logs/LogCore.tsx +2 -0
  55. package/src/components/logs/LogsViewer.tsx +44 -0
  56. package/src/components/logs/WorkloadLogsViewer.tsx +40 -0
  57. package/src/components/logs/useLogBuffer.ts +2 -0
  58. package/src/components/logs/useLogSearch.ts +1 -0
  59. package/src/components/portforward/PortForwardButton.tsx +375 -0
  60. package/src/components/portforward/PortForwardManager.tsx +871 -0
  61. package/src/components/resource/PrometheusCharts.tsx +687 -0
  62. package/src/components/resource-drawer/ResourceDrawer.tsx +214 -0
  63. package/src/components/resources/ImageFilesystemModal.tsx +745 -0
  64. package/src/components/resources/PodFilesystemModal.tsx +407 -0
  65. package/src/components/resources/ResourceDetailDrawer.tsx +43 -0
  66. package/src/components/resources/ResourcesView.tsx +190 -0
  67. package/src/components/resources/drawer-components.tsx +1 -0
  68. package/src/components/resources/file-browser-utils.ts +35 -0
  69. package/src/components/resources/renderers/AlertRenderer.tsx +1 -0
  70. package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +17 -0
  71. package/src/components/resources/renderers/CNPGBackupRenderer.tsx +1 -0
  72. package/src/components/resources/renderers/CNPGClusterRenderer.tsx +1 -0
  73. package/src/components/resources/renderers/CNPGPoolerRenderer.tsx +1 -0
  74. package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +1 -0
  75. package/src/components/resources/renderers/CertificateRenderer.tsx +1 -0
  76. package/src/components/resources/renderers/CertificateRequestRenderer.tsx +1 -0
  77. package/src/components/resources/renderers/ChallengeRenderer.tsx +1 -0
  78. package/src/components/resources/renderers/ClusterComplianceReportRenderer.tsx +1 -0
  79. package/src/components/resources/renderers/ClusterExternalSecretRenderer.tsx +1 -0
  80. package/src/components/resources/renderers/ClusterIssuerRenderer.tsx +1 -0
  81. package/src/components/resources/renderers/ConfigAuditReportRenderer.tsx +1 -0
  82. package/src/components/resources/renderers/ConfigMapRenderer.tsx +1 -0
  83. package/src/components/resources/renderers/CronJobRenderer.tsx +1 -0
  84. package/src/components/resources/renderers/EventRenderer.tsx +1 -0
  85. package/src/components/resources/renderers/ExposedSecretReportRenderer.tsx +1 -0
  86. package/src/components/resources/renderers/ExternalSecretRenderer.tsx +1 -0
  87. package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +1 -0
  88. package/src/components/resources/renderers/GRPCRouteRenderer.tsx +1 -0
  89. package/src/components/resources/renderers/GatewayClassRenderer.tsx +1 -0
  90. package/src/components/resources/renderers/GatewayRenderer.tsx +1 -0
  91. package/src/components/resources/renderers/GenericRenderer.tsx +1 -0
  92. package/src/components/resources/renderers/GitRepositoryRenderer.tsx +1 -0
  93. package/src/components/resources/renderers/HPARenderer.tsx +1 -0
  94. package/src/components/resources/renderers/HTTPRouteRenderer.tsx +1 -0
  95. package/src/components/resources/renderers/HelmRepositoryRenderer.tsx +1 -0
  96. package/src/components/resources/renderers/IngressClassRenderer.tsx +1 -0
  97. package/src/components/resources/renderers/IngressRenderer.tsx +1 -0
  98. package/src/components/resources/renderers/IstioAuthorizationPolicyRenderer.tsx +1 -0
  99. package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +1 -0
  100. package/src/components/resources/renderers/IstioGatewayRenderer.tsx +1 -0
  101. package/src/components/resources/renderers/IstioPeerAuthenticationRenderer.tsx +1 -0
  102. package/src/components/resources/renderers/IstioServiceEntryRenderer.tsx +1 -0
  103. package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +1 -0
  104. package/src/components/resources/renderers/JobRenderer.tsx +1 -0
  105. package/src/components/resources/renderers/KarpenterEC2NodeClassRenderer.tsx +1 -0
  106. package/src/components/resources/renderers/KarpenterNodeClaimRenderer.tsx +1 -0
  107. package/src/components/resources/renderers/KarpenterNodePoolRenderer.tsx +1 -0
  108. package/src/components/resources/renderers/KedaScaledJobRenderer.tsx +1 -0
  109. package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +1 -0
  110. package/src/components/resources/renderers/KedaTriggerAuthRenderer.tsx +1 -0
  111. package/src/components/resources/renderers/KnativeConfigurationRenderer.tsx +1 -0
  112. package/src/components/resources/renderers/KnativeEventingRenderer.tsx +1 -0
  113. package/src/components/resources/renderers/KnativeFlowRenderer.tsx +1 -0
  114. package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +1 -0
  115. package/src/components/resources/renderers/KnativeRevisionRenderer.tsx +1 -0
  116. package/src/components/resources/renderers/KnativeRouteRenderer.tsx +1 -0
  117. package/src/components/resources/renderers/KnativeServiceRenderer.tsx +1 -0
  118. package/src/components/resources/renderers/KnativeSourceRenderer.tsx +1 -0
  119. package/src/components/resources/renderers/KustomizationRenderer.tsx +1 -0
  120. package/src/components/resources/renderers/KyvernoPolicyReportRenderer.tsx +1 -0
  121. package/src/components/resources/renderers/LeaseRenderer.tsx +1 -0
  122. package/src/components/resources/renderers/NetworkPolicyRenderer.tsx +1 -0
  123. package/src/components/resources/renderers/NodeRenderer.tsx +44 -0
  124. package/src/components/resources/renderers/OCIRepositoryRenderer.tsx +1 -0
  125. package/src/components/resources/renderers/OrderRenderer.tsx +1 -0
  126. package/src/components/resources/renderers/PVCRenderer.tsx +1 -0
  127. package/src/components/resources/renderers/PersistentVolumeRenderer.tsx +1 -0
  128. package/src/components/resources/renderers/PodDisruptionBudgetRenderer.tsx +1 -0
  129. package/src/components/resources/renderers/PodMonitorRenderer.tsx +1 -0
  130. package/src/components/resources/renderers/PodRenderer.tsx +94 -0
  131. package/src/components/resources/renderers/PriorityClassRenderer.tsx +1 -0
  132. package/src/components/resources/renderers/PrometheusRuleRenderer.tsx +1 -0
  133. package/src/components/resources/renderers/ReplicaSetRenderer.tsx +1 -0
  134. package/src/components/resources/renderers/RoleBindingRenderer.tsx +1 -0
  135. package/src/components/resources/renderers/RoleRenderer.tsx +1 -0
  136. package/src/components/resources/renderers/RolloutRenderer.tsx +1 -0
  137. package/src/components/resources/renderers/RuntimeClassRenderer.tsx +1 -0
  138. package/src/components/resources/renderers/SbomReportRenderer.tsx +1 -0
  139. package/src/components/resources/renderers/SealedSecretRenderer.tsx +1 -0
  140. package/src/components/resources/renderers/SecretRenderer.tsx +1 -0
  141. package/src/components/resources/renderers/SecretStoreRenderer.tsx +1 -0
  142. package/src/components/resources/renderers/ServiceAccountRenderer.tsx +1 -0
  143. package/src/components/resources/renderers/ServiceMonitorRenderer.tsx +1 -0
  144. package/src/components/resources/renderers/ServiceRenderer.tsx +26 -0
  145. package/src/components/resources/renderers/SimpleRouteRenderer.tsx +1 -0
  146. package/src/components/resources/renderers/StorageClassRenderer.tsx +1 -0
  147. package/src/components/resources/renderers/TraefikIngressRouteRenderer.tsx +1 -0
  148. package/src/components/resources/renderers/VPARenderer.tsx +1 -0
  149. package/src/components/resources/renderers/VeleroBSLRenderer.tsx +1 -0
  150. package/src/components/resources/renderers/VeleroBackupRenderer.tsx +1 -0
  151. package/src/components/resources/renderers/VeleroRestoreRenderer.tsx +1 -0
  152. package/src/components/resources/renderers/VeleroScheduleRenderer.tsx +1 -0
  153. package/src/components/resources/renderers/VeleroVSLRenderer.tsx +1 -0
  154. package/src/components/resources/renderers/VulnerabilityReportRenderer.tsx +1 -0
  155. package/src/components/resources/renderers/WebhookConfigRenderer.tsx +1 -0
  156. package/src/components/resources/renderers/WorkflowRenderer.tsx +1 -0
  157. package/src/components/resources/renderers/WorkflowTemplateRenderer.tsx +1 -0
  158. package/src/components/resources/renderers/WorkloadRenderer.tsx +52 -0
  159. package/src/components/resources/renderers/argo-cells.tsx +1 -0
  160. package/src/components/resources/renderers/certmanager-cells.tsx +1 -0
  161. package/src/components/resources/renderers/cnpg-cells.tsx +1 -0
  162. package/src/components/resources/renderers/eso-cells.tsx +1 -0
  163. package/src/components/resources/renderers/flux-cells.tsx +1 -0
  164. package/src/components/resources/renderers/index.ts +91 -0
  165. package/src/components/resources/renderers/istio-cells.tsx +1 -0
  166. package/src/components/resources/renderers/karpenter-cells.tsx +1 -0
  167. package/src/components/resources/renderers/keda-cells.tsx +1 -0
  168. package/src/components/resources/renderers/knative-cells.tsx +1 -0
  169. package/src/components/resources/renderers/kyverno-cells.tsx +1 -0
  170. package/src/components/resources/renderers/prometheus-cells.tsx +1 -0
  171. package/src/components/resources/renderers/traefik-cells.tsx +1 -0
  172. package/src/components/resources/renderers/trivy-cells.tsx +1 -0
  173. package/src/components/resources/renderers/trivy-shared.tsx +1 -0
  174. package/src/components/resources/renderers/velero-cells.tsx +1 -0
  175. package/src/components/resources/resource-utils-argo.ts +2 -0
  176. package/src/components/resources/resource-utils-certmanager.ts +2 -0
  177. package/src/components/resources/resource-utils-cnpg.ts +2 -0
  178. package/src/components/resources/resource-utils-eso.ts +2 -0
  179. package/src/components/resources/resource-utils-flux.ts +2 -0
  180. package/src/components/resources/resource-utils-istio.ts +2 -0
  181. package/src/components/resources/resource-utils-karpenter.ts +2 -0
  182. package/src/components/resources/resource-utils-keda.ts +2 -0
  183. package/src/components/resources/resource-utils-knative.ts +2 -0
  184. package/src/components/resources/resource-utils-kyverno.ts +2 -0
  185. package/src/components/resources/resource-utils-prometheus.ts +2 -0
  186. package/src/components/resources/resource-utils-traefik.ts +1 -0
  187. package/src/components/resources/resource-utils-trivy.ts +2 -0
  188. package/src/components/resources/resource-utils-velero.ts +2 -0
  189. package/src/components/resources/resource-utils.ts +5 -0
  190. package/src/components/settings/SettingsDialog.tsx +537 -0
  191. package/src/components/shared/CreateResourceDialog.tsx +17 -0
  192. package/src/components/shared/EditableYamlView.tsx +24 -0
  193. package/src/components/shared/LargeClusterNamespacePicker.tsx +70 -0
  194. package/src/components/shared/ResourceRendererDispatch.tsx +31 -0
  195. package/src/components/timeline/DiffViewer.tsx +1 -0
  196. package/src/components/timeline/TimelineList.tsx +69 -0
  197. package/src/components/timeline/TimelineSwimlanes.tsx +1308 -0
  198. package/src/components/timeline/TimelineView.tsx +157 -0
  199. package/src/components/timeline/shared.tsx +1 -0
  200. package/src/components/traffic/TrafficFilterSidebar.tsx +571 -0
  201. package/src/components/traffic/TrafficFlowList.tsx +415 -0
  202. package/src/components/traffic/TrafficFlowListContext.tsx +68 -0
  203. package/src/components/traffic/TrafficGraph.tsx +1546 -0
  204. package/src/components/traffic/TrafficView.tsx +1213 -0
  205. package/src/components/traffic/TrafficWizard.tsx +386 -0
  206. package/src/components/traffic/index.ts +3 -0
  207. package/src/components/ui/CodeViewer.tsx +8 -0
  208. package/src/components/ui/CommandPalette.tsx +460 -0
  209. package/src/components/ui/ConfirmDialog.tsx +1 -0
  210. package/src/components/ui/DiagnosticsOverlay.tsx +619 -0
  211. package/src/components/ui/ErrorBoundary.tsx +46 -0
  212. package/src/components/ui/ForceDeleteConfirmDialog.tsx +1 -0
  213. package/src/components/ui/Markdown.tsx +108 -0
  214. package/src/components/ui/MetricsChart.tsx +1 -0
  215. package/src/components/ui/NamespaceSelector.tsx +436 -0
  216. package/src/components/ui/ResourceBar.tsx +1 -0
  217. package/src/components/ui/ShortcutHelpOverlay.tsx +301 -0
  218. package/src/components/ui/Toast.tsx +1 -0
  219. package/src/components/ui/Tooltip.tsx +1 -0
  220. package/src/components/ui/UpdateNotification.tsx +299 -0
  221. package/src/components/ui/YamlEditor.tsx +1 -0
  222. package/src/components/workload/WorkloadView.tsx +532 -0
  223. package/src/context/ConnectionContext.tsx +173 -0
  224. package/src/context/ContextSwitchContext.tsx +56 -0
  225. package/src/context/NavCustomization.tsx +62 -0
  226. package/src/context/ThemeContext.tsx +97 -0
  227. package/src/contexts/CapabilitiesContext.tsx +130 -0
  228. package/src/hooks/useAnimatedUnmount.ts +1 -0
  229. package/src/hooks/useDesktopDownload.ts +41 -0
  230. package/src/hooks/useEventSource.ts +262 -0
  231. package/src/hooks/useFavorites.ts +69 -0
  232. package/src/hooks/useKeyboardShortcuts.tsx +7 -0
  233. package/src/hooks/useRefreshAnimation.ts +1 -0
  234. package/src/index.css +243 -0
  235. package/src/index.ts +17 -0
  236. package/src/main.tsx +158 -0
  237. package/src/types/gitops.ts +2 -0
  238. package/src/types.ts +3 -0
  239. package/src/utils/animation.ts +2 -0
  240. package/src/utils/badge-colors.ts +2 -0
  241. package/src/utils/context-name.ts +2 -0
  242. package/src/utils/desktop-download.ts +66 -0
  243. package/src/utils/desktop-open-folder.ts +21 -0
  244. package/src/utils/format.ts +2 -0
  245. package/src/utils/log-format.ts +12 -0
  246. package/src/utils/navigation.ts +23 -0
  247. package/src/utils/resource-hierarchy.ts +2 -0
  248. package/src/utils/resource-icons.ts +2 -0
  249. package/src/utils/skeleton-yaml.ts +2 -0
  250. package/src/utils/traffic-colors.ts +54 -0
  251. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,417 @@
1
+ import { useRef, useEffect, useState, useCallback } from 'react'
2
+ import { X, Copy, Check, Radio, Terminal, MessageSquare, Code2, ChevronRight, Pin } from 'lucide-react'
3
+ import { apiUrl, getAuthHeaders, getCredentialsMode } from '../../api/config'
4
+
5
+ interface MCPSetupDialogProps {
6
+ open: boolean
7
+ onClose: () => void
8
+ mcpUrl: string
9
+ }
10
+
11
+ function CopyButton({ text }: { text: string }) {
12
+ const [copied, setCopied] = useState(false)
13
+
14
+ const handleCopy = () => {
15
+ navigator.clipboard.writeText(text)
16
+ setCopied(true)
17
+ setTimeout(() => setCopied(false), 2000)
18
+ }
19
+
20
+ return (
21
+ <button
22
+ onClick={handleCopy}
23
+ className="absolute top-2 right-2 p-1.5 rounded-md bg-theme-elevated/50 hover:bg-theme-elevated text-theme-text-tertiary hover:text-theme-text-secondary transition-colors"
24
+ title="Copy to clipboard"
25
+ >
26
+ {copied ? <Check className="w-3.5 h-3.5 text-green-500" /> : <Copy className="w-3.5 h-3.5" />}
27
+ </button>
28
+ )
29
+ }
30
+
31
+ function CodeBlock({ children }: { children: string }) {
32
+ return (
33
+ <div className="relative group">
34
+ <pre className="bg-theme-base rounded-md px-3 py-2.5 text-xs font-mono text-theme-text-secondary overflow-x-auto whitespace-pre-wrap break-all">
35
+ {children}
36
+ </pre>
37
+ <CopyButton text={children} />
38
+ </div>
39
+ )
40
+ }
41
+
42
+ export function MCPSetupDialog({ open, onClose, mcpUrl }: MCPSetupDialogProps) {
43
+ const dialogRef = useRef<HTMLDivElement>(null)
44
+ const [isDesktop, setIsDesktop] = useState(false)
45
+ const [portPinned, setPortPinned] = useState(false)
46
+ const [pinning, setPinning] = useState(false)
47
+ const [pinSuccess, setPinSuccess] = useState(false)
48
+ const [pinError, setPinError] = useState('')
49
+
50
+ useEffect(() => {
51
+ if (!open) return
52
+ setPinSuccess(false)
53
+ setPinError('')
54
+ fetch(apiUrl('/config'), { credentials: getCredentialsMode(), headers: getAuthHeaders() })
55
+ .then((res) => res.ok ? res.json() : null)
56
+ .then((data) => {
57
+ if (data) {
58
+ setIsDesktop(data.isDesktop ?? false)
59
+ setPortPinned(data.file?.port != null && data.file.port > 0)
60
+ }
61
+ })
62
+ .catch((err) => console.warn('[mcp-setup] Failed to load config:', err))
63
+ }, [open])
64
+
65
+ const handlePinPort = useCallback(async () => {
66
+ const currentPort = Number(window.location.port) || 80
67
+ setPinning(true)
68
+ setPinError('')
69
+ try {
70
+ const configRes = await fetch(apiUrl('/config'), { credentials: getCredentialsMode(), headers: getAuthHeaders() })
71
+ if (!configRes.ok) {
72
+ setPinError('Failed to load current config')
73
+ return
74
+ }
75
+ const configData = await configRes.json()
76
+ const updated = { ...configData.file, port: currentPort }
77
+ const res = await fetch(apiUrl('/config'), {
78
+ method: 'PUT',
79
+ credentials: getCredentialsMode(),
80
+ headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
81
+ body: JSON.stringify(updated),
82
+ })
83
+ if (res.ok) {
84
+ setPortPinned(true)
85
+ setPinSuccess(true)
86
+ } else {
87
+ setPinError('Failed to save port configuration')
88
+ }
89
+ } catch {
90
+ setPinError('Failed to pin port — check server connection')
91
+ } finally {
92
+ setPinning(false)
93
+ }
94
+ }, [])
95
+
96
+ useEffect(() => {
97
+ if (!open) return
98
+ const handleKeyDown = (e: KeyboardEvent) => {
99
+ if (e.key === 'Escape') onClose()
100
+ }
101
+ document.addEventListener('keydown', handleKeyDown)
102
+ return () => document.removeEventListener('keydown', handleKeyDown)
103
+ }, [open, onClose])
104
+
105
+ useEffect(() => {
106
+ if (open && dialogRef.current) {
107
+ dialogRef.current.focus()
108
+ }
109
+ }, [open])
110
+
111
+ if (!open) return null
112
+
113
+ const currentPort = Number(window.location.port) || 80
114
+
115
+ const claudeDesktopConfig = JSON.stringify({
116
+ mcpServers: {
117
+ radar: {
118
+ type: "http",
119
+ url: mcpUrl,
120
+ }
121
+ }
122
+ }, null, 2)
123
+
124
+ const cursorConfig = JSON.stringify({
125
+ mcpServers: {
126
+ radar: {
127
+ url: mcpUrl,
128
+ }
129
+ }
130
+ }, null, 2)
131
+
132
+ const windsurfConfig = JSON.stringify({
133
+ mcpServers: {
134
+ radar: {
135
+ serverUrl: mcpUrl,
136
+ }
137
+ }
138
+ }, null, 2)
139
+
140
+ const vsCodeConfig = JSON.stringify({
141
+ servers: {
142
+ radar: {
143
+ type: "http",
144
+ url: mcpUrl,
145
+ }
146
+ }
147
+ }, null, 2)
148
+
149
+ const geminiConfig = JSON.stringify({
150
+ mcpServers: {
151
+ radar: {
152
+ httpUrl: mcpUrl,
153
+ }
154
+ }
155
+ }, null, 2)
156
+
157
+ const codexConfig = `[mcp_servers.radar]\nurl = "${mcpUrl}"`
158
+
159
+ const clineConfig = JSON.stringify({
160
+ mcpServers: {
161
+ radar: {
162
+ url: mcpUrl,
163
+ }
164
+ }
165
+ }, null, 2)
166
+
167
+ const jetbrainsConfig = JSON.stringify({
168
+ mcpServers: {
169
+ radar: {
170
+ url: mcpUrl,
171
+ }
172
+ }
173
+ }, null, 2)
174
+
175
+ return (
176
+ <div className="fixed inset-0 z-50 flex items-center justify-center">
177
+ <div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={onClose} />
178
+ <div
179
+ ref={dialogRef}
180
+ tabIndex={-1}
181
+ className="relative dialog max-w-3xl w-full mx-4 outline-none max-h-[85vh] flex flex-col"
182
+ >
183
+ {/* Header */}
184
+ <div className="flex items-center justify-between px-6 py-4 border-b border-theme-border/50 shrink-0">
185
+ <div className="flex items-center gap-3">
186
+ <Radio className="w-5 h-5 text-purple-400" />
187
+ <h3 className="text-lg font-semibold text-theme-text-primary">MCP Server</h3>
188
+ </div>
189
+ <button onClick={onClose} className="p-1.5 hover:bg-theme-elevated rounded-md transition-colors">
190
+ <X className="w-5 h-5 text-theme-text-tertiary" />
191
+ </button>
192
+ </div>
193
+
194
+ {/* Scrollable content */}
195
+ <div className="overflow-y-auto flex-1 min-h-0 px-6 py-5 space-y-6">
196
+ {/* Explanation */}
197
+ <div className="space-y-3">
198
+ <h4 className="text-sm font-semibold text-theme-text-primary">AI meets your cluster</h4>
199
+ <p className="text-sm text-theme-text-secondary leading-relaxed">
200
+ Radar exposes a{' '}
201
+ <a href="https://modelcontextprotocol.io" target="_blank" rel="noopener noreferrer" className="text-purple-400 hover:text-purple-300 underline underline-offset-2">
202
+ Model Context Protocol
203
+ </a>{' '}
204
+ (MCP) server that lets AI assistants query your cluster through Radar.
205
+ Unlike raw kubectl access, Radar gives your AI pre-processed, enriched data —
206
+ topology graphs, health assessments, deduplicated events, filtered logs — so it
207
+ can understand your cluster state quickly without burning through context on
208
+ verbose YAML output.
209
+ </p>
210
+ <p className="text-sm text-theme-text-secondary leading-relaxed">
211
+ Read tools are strictly read-only. Write tools (restart, scale, sync) are
212
+ non-destructive and annotated so your AI client can distinguish them.
213
+ </p>
214
+ </div>
215
+
216
+ {/* Endpoint */}
217
+ <div className="space-y-2">
218
+ <h4 className="text-sm font-semibold text-theme-text-primary">Endpoint</h4>
219
+ <div className="relative">
220
+ <div className="flex items-center gap-3 bg-theme-base rounded-md px-3 py-2.5">
221
+ <span className="badge text-purple-400 bg-purple-500/10">HTTP</span>
222
+ <code className="text-sm font-mono text-theme-text-primary">{mcpUrl}</code>
223
+ </div>
224
+ <CopyButton text={mcpUrl} />
225
+ </div>
226
+
227
+ {isDesktop && !portPinned && (
228
+ <div className="flex items-center gap-2 px-3 py-2 text-xs bg-amber-500/10 border border-amber-500/20 rounded-md">
229
+ <span className="text-amber-700 dark:text-amber-300 flex-1">
230
+ Port changes on every restart. Pin it to keep a stable MCP endpoint.
231
+ </span>
232
+ <button
233
+ onClick={handlePinPort}
234
+ disabled={pinning}
235
+ className="shrink-0 flex items-center gap-1 px-2.5 py-1 text-xs font-medium text-amber-800 dark:text-amber-200 hover:text-amber-900 dark:hover:text-white bg-amber-500/20 hover:bg-amber-500/30 rounded transition-colors disabled:opacity-50"
236
+ >
237
+ <Pin className="w-3 h-3" />
238
+ Pin port {currentPort}
239
+ </button>
240
+ </div>
241
+ )}
242
+
243
+ {isDesktop && pinSuccess && (
244
+ <p className="text-xs text-green-700 dark:text-green-400/80 px-0.5">
245
+ Port {currentPort} pinned. MCP endpoint will remain stable across restarts.
246
+ </p>
247
+ )}
248
+
249
+ {isDesktop && pinError && (
250
+ <p className="text-xs text-red-600 dark:text-red-400 px-0.5">
251
+ {pinError}
252
+ </p>
253
+ )}
254
+
255
+ {isDesktop && (
256
+ <p className="text-xs text-theme-text-tertiary px-0.5">
257
+ You can change the port in{' '}
258
+ <button
259
+ onClick={() => { onClose(); window.dispatchEvent(new Event('radar:open-settings')) }}
260
+ className="text-purple-500 dark:text-purple-400 hover:underline underline-offset-2"
261
+ >
262
+ Settings
263
+ </button>.
264
+ </p>
265
+ )}
266
+ </div>
267
+
268
+ {/* Setup instructions */}
269
+ <div className="space-y-2">
270
+ <h4 className="text-sm font-semibold text-theme-text-primary">Connect your AI tool</h4>
271
+
272
+ {[
273
+ { icon: Terminal, name: 'Claude Code', path: '', config: `claude mcp add radar --transport http ${mcpUrl}` },
274
+ { icon: MessageSquare, name: 'Claude Desktop', path: '~/Library/Application Support/Claude/claude_desktop_config.json', config: claudeDesktopConfig },
275
+ { icon: Code2, name: 'Cursor', path: '~/.cursor/mcp.json', config: cursorConfig },
276
+ { icon: Code2, name: 'Windsurf', path: '~/.codeium/windsurf/mcp_config.json', config: windsurfConfig },
277
+ { icon: Code2, name: 'VS Code Copilot', path: '.vscode/mcp.json', config: vsCodeConfig },
278
+ { icon: Code2, name: 'Cline', path: 'Cline MCP settings (via UI)', config: clineConfig },
279
+ { icon: Code2, name: 'JetBrains AI', path: 'Settings → Tools → AI Assistant → MCP', config: jetbrainsConfig },
280
+ { icon: Terminal, name: 'OpenAI Codex', path: '~/.codex/config.toml', config: codexConfig },
281
+ { icon: Terminal, name: 'Gemini CLI', path: '~/.gemini/settings.json', config: geminiConfig },
282
+ ].map((agent) => (
283
+ <details key={agent.name} className="group rounded-md border border-theme-border/50 bg-theme-base/30">
284
+ <summary className="flex items-center gap-2 px-3 py-2 select-none list-none hover:bg-theme-hover/50 rounded-md transition-colors [&::-webkit-details-marker]:hidden">
285
+ <ChevronRight className="w-3.5 h-3.5 text-theme-text-tertiary transition-transform group-open:rotate-90" />
286
+ <agent.icon className="w-4 h-4 text-theme-text-tertiary" />
287
+ <span className="text-sm font-medium text-theme-text-primary">{agent.name}</span>
288
+ {agent.path && <span className="text-[10px] text-theme-text-tertiary ml-auto">{agent.path}</span>}
289
+ </summary>
290
+ <div className="px-3 pb-3 pt-1">
291
+ <CodeBlock>{agent.config}</CodeBlock>
292
+ </div>
293
+ </details>
294
+ ))}
295
+ </div>
296
+
297
+ {/* Available tools */}
298
+ <div className="space-y-2">
299
+ <h4 className="text-sm font-semibold text-theme-text-primary">Tools</h4>
300
+ <div className="grid grid-cols-1 gap-1.5">
301
+ {[
302
+ { name: 'get_dashboard', desc: 'Get cluster health overview including resource counts, problems (failing pods, unhealthy deployments), recent warning events, and Helm release status. Start here to understand cluster state before drilling into specific resources.', params: [
303
+ { name: 'namespace', required: false, desc: 'filter to a specific namespace' },
304
+ ]},
305
+ { name: 'list_resources', desc: 'List Kubernetes resources of a given kind with minified summaries. Supports all built-in kinds (pods, deployments, services, etc.) and CRDs. Use to discover what\'s running before inspecting individual resources.', params: [
306
+ { name: 'kind', required: true, desc: 'resource kind, e.g. pods, deployments, services' },
307
+ { name: 'namespace', required: false, desc: 'filter to a specific namespace' },
308
+ ]},
309
+ { name: 'get_resource', desc: 'Get detailed information about a single Kubernetes resource. Returns minified spec, status, and metadata. Optionally include related context (events, relationships, metrics, logs) to avoid extra tool calls.', params: [
310
+ { name: 'kind', required: true, desc: 'resource kind, e.g. pod, deployment, service' },
311
+ { name: 'namespace', required: true, desc: 'resource namespace' },
312
+ { name: 'name', required: true, desc: 'resource name' },
313
+ { name: 'include', required: false, desc: 'events, relationships, metrics, logs' },
314
+ ]},
315
+ { name: 'get_topology', desc: 'Get the topology graph showing relationships between Kubernetes resources. Returns nodes and edges representing Deployments, Services, Ingresses, Pods, etc. Use \'traffic\' view for network flow or \'resources\' view for ownership hierarchy. Use \'summary\' format for LLM-friendly text descriptions.', params: [
316
+ { name: 'namespace', required: false, desc: 'filter to a specific namespace' },
317
+ { name: 'view', required: false, desc: 'traffic or resources' },
318
+ { name: 'format', required: false, desc: 'graph (default) or summary (text)' },
319
+ ]},
320
+ { name: 'get_events', desc: 'Get recent Kubernetes warning events, deduplicated and sorted by recency. Useful for diagnosing issues — shows event reason, message, and occurrence count. Filter by resource kind/name to scope to a specific resource.', params: [
321
+ { name: 'namespace', required: false, desc: 'filter to a specific namespace' },
322
+ { name: 'limit', required: false, desc: 'max events to return (default 20)' },
323
+ { name: 'kind', required: false, desc: 'filter to events for this resource kind' },
324
+ { name: 'name', required: false, desc: 'filter to events for this resource name' },
325
+ ]},
326
+ { name: 'get_pod_logs', desc: 'Get filtered log lines from a pod, prioritizing errors and warnings. Returns diagnostically relevant lines (errors, panics, stack traces) or falls back to the last 20 lines if no error patterns match.', params: [
327
+ { name: 'namespace', required: true, desc: 'pod namespace' },
328
+ { name: 'name', required: true, desc: 'pod name' },
329
+ { name: 'container', required: false, desc: 'container name (defaults to first)' },
330
+ { name: 'tail_lines', required: false, desc: 'lines from end (default 200)' },
331
+ ]},
332
+ { name: 'list_namespaces', desc: 'List all Kubernetes namespaces with their status. Use to discover available namespaces before filtering other queries.', params: [] },
333
+ { name: 'get_changes', desc: 'Get recent resource changes (creates, updates, deletes) from the cluster timeline. Use to investigate what changed before an incident. Filter by namespace, resource kind, or specific resource name.', params: [
334
+ { name: 'namespace', required: false, desc: 'filter to a specific namespace' },
335
+ { name: 'kind', required: false, desc: 'filter to a resource kind (e.g. Deployment)' },
336
+ { name: 'name', required: false, desc: 'filter to a specific resource name' },
337
+ { name: 'since', required: false, desc: 'lookback duration, e.g. 1h, 30m (default 1h)' },
338
+ { name: 'limit', required: false, desc: 'max changes to return (default 20, max 50)' },
339
+ ]},
340
+ { name: 'list_helm_releases', desc: 'List all Helm releases in the cluster with their status and health. Returns release name, namespace, chart, version, status, and resource health.', params: [
341
+ { name: 'namespace', required: false, desc: 'filter to a specific namespace' },
342
+ ]},
343
+ { name: 'get_helm_release', desc: 'Get detailed information about a specific Helm release including owned resources and their status. Optionally include values, revision history, or manifest diff between revisions.', params: [
344
+ { name: 'namespace', required: true, desc: 'release namespace' },
345
+ { name: 'name', required: true, desc: 'release name' },
346
+ { name: 'include', required: false, desc: 'values, history, diff' },
347
+ { name: 'diff_revision_1', required: false, desc: 'first revision for diff' },
348
+ { name: 'diff_revision_2', required: false, desc: 'second revision for diff (defaults to current)' },
349
+ ]},
350
+ { name: 'get_workload_logs', desc: 'Get aggregated, AI-filtered logs from all pods of a workload (Deployment, StatefulSet, or DaemonSet). Logs are collected from all matching pods, filtered for errors/warnings, and deduplicated.', params: [
351
+ { name: 'kind', required: true, desc: 'deployment, statefulset, or daemonset' },
352
+ { name: 'namespace', required: true, desc: 'workload namespace' },
353
+ { name: 'name', required: true, desc: 'workload name' },
354
+ { name: 'container', required: false, desc: 'specific container name' },
355
+ { name: 'tail_lines', required: false, desc: 'lines per pod (default 100)' },
356
+ ]},
357
+ { name: 'manage_workload', desc: 'Perform operations on a workload. \'restart\' triggers a rolling restart, \'scale\' changes the replica count, \'rollback\' reverts to a previous revision.', params: [
358
+ { name: 'action', required: true, desc: 'restart, scale, or rollback' },
359
+ { name: 'kind', required: true, desc: 'deployment, statefulset, or daemonset' },
360
+ { name: 'namespace', required: true, desc: 'workload namespace' },
361
+ { name: 'name', required: true, desc: 'workload name' },
362
+ { name: 'replicas', required: false, desc: 'target replica count (for scale)' },
363
+ { name: 'revision', required: false, desc: 'target revision (for rollback)' },
364
+ ]},
365
+ { name: 'manage_cronjob', desc: 'Perform operations on a CronJob. \'trigger\' creates a manual Job run, \'suspend\' pauses the schedule, \'resume\' re-enables it.', params: [
366
+ { name: 'action', required: true, desc: 'trigger, suspend, or resume' },
367
+ { name: 'namespace', required: true, desc: 'cronjob namespace' },
368
+ { name: 'name', required: true, desc: 'cronjob name' },
369
+ ]},
370
+ { name: 'manage_gitops', desc: 'Perform operations on GitOps resources. ArgoCD: sync, suspend, resume. FluxCD: reconcile, suspend, resume.', params: [
371
+ { name: 'action', required: true, desc: 'sync/reconcile, suspend, or resume' },
372
+ { name: 'tool', required: true, desc: 'argocd or fluxcd' },
373
+ { name: 'namespace', required: true, desc: 'resource namespace' },
374
+ { name: 'name', required: true, desc: 'resource name' },
375
+ { name: 'kind', required: false, desc: 'FluxCD resource kind (e.g. kustomization, helmrelease)' },
376
+ ]},
377
+ ].map((tool) => (
378
+ <div key={tool.name} className="card-inner space-y-1.5">
379
+ <code className="text-[11px] font-mono text-purple-400">{tool.name}</code>
380
+ <p className="text-[11px] text-theme-text-tertiary leading-relaxed">{tool.desc}</p>
381
+ {tool.params.length > 0 && (
382
+ <div className="flex flex-wrap gap-1.5 pt-0.5">
383
+ {tool.params.map((p) => (
384
+ <span key={p.name} className="badge-sm font-mono bg-theme-elevated text-theme-text-secondary" title={p.desc}>
385
+ <span className="text-theme-text-secondary">{p.name}</span>
386
+ {p.required && <span className="text-red-400">*</span>}
387
+ </span>
388
+ ))}
389
+ </div>
390
+ )}
391
+ </div>
392
+ ))}
393
+ </div>
394
+ </div>
395
+ </div>
396
+
397
+ {/* Footer */}
398
+ <div className="flex items-center justify-between px-6 py-3 border-t border-theme-border/50 shrink-0">
399
+ <a
400
+ href="https://github.com/skyhook-io/radar/blob/main/docs/mcp.md"
401
+ target="_blank"
402
+ rel="noopener noreferrer"
403
+ className="text-xs text-theme-text-tertiary hover:text-purple-400 transition-colors"
404
+ >
405
+ Documentation
406
+ </a>
407
+ <button
408
+ onClick={onClose}
409
+ className="px-4 py-2 text-sm font-medium rounded-lg hover:bg-theme-elevated transition-colors text-theme-text-secondary"
410
+ >
411
+ Close
412
+ </button>
413
+ </div>
414
+ </div>
415
+ </div>
416
+ )
417
+ }
@@ -0,0 +1,109 @@
1
+ import type { DashboardNetworkPolicyCoverage } from '../../api/client'
2
+ import { ShieldCheck, ArrowRight } from 'lucide-react'
3
+ import { clsx } from 'clsx'
4
+
5
+ interface NetworkPolicyCoverageCardProps {
6
+ data: DashboardNetworkPolicyCoverage
7
+ onNavigate: () => void
8
+ }
9
+
10
+ export function NetworkPolicyCoverageCard({ data, onNavigate }: NetworkPolicyCoverageCardProps) {
11
+ const percentage = data.totalWorkloads > 0
12
+ ? Math.round((data.coveredWorkloads / data.totalWorkloads) * 100)
13
+ : 0
14
+ const hasPolicies = data.totalPolicies > 0
15
+ const accentColor = !hasPolicies
16
+ ? 'text-theme-text-tertiary'
17
+ : percentage >= 75
18
+ ? 'text-green-500'
19
+ : percentage >= 40
20
+ ? 'text-yellow-500'
21
+ : 'text-red-500'
22
+
23
+ return (
24
+ <button
25
+ onClick={onNavigate}
26
+ className="group h-[260px] rounded-xl bg-theme-surface shadow-theme-sm hover:-translate-y-1 hover:shadow-theme-md transition-all duration-200 text-left animate-fade-in-up"
27
+ >
28
+ <div className="flex flex-col h-full w-full">
29
+ <div className="flex items-center justify-between px-5 py-3 border-b border-theme-border/50">
30
+ <div className="flex items-center gap-2">
31
+ <ShieldCheck className={clsx('w-4 h-4', accentColor)} />
32
+ <span className={clsx('text-xs font-semibold uppercase tracking-wider', accentColor)}>
33
+ Network Policies
34
+ </span>
35
+ <span className={clsx('badge-sm', hasPolicies ? 'bg-theme-elevated text-theme-text-secondary' : 'bg-theme-hover text-theme-text-tertiary')}>
36
+ {data.totalPolicies}
37
+ </span>
38
+ </div>
39
+ </div>
40
+
41
+ <div className="flex-1 min-h-0 flex flex-col items-center justify-center px-4 py-4">
42
+ {!hasPolicies ? (
43
+ <div className="flex flex-col items-center justify-center text-center gap-2">
44
+ <ShieldCheck className="w-8 h-8 text-theme-text-tertiary/40" />
45
+ <span className="text-xs text-theme-text-tertiary">No network policies configured</span>
46
+ </div>
47
+ ) : (
48
+ <>
49
+ <div className="flex items-center gap-3 w-full">
50
+ <div className="flex-1 h-3 rounded-full overflow-hidden bg-theme-hover flex">
51
+ {data.coveredWorkloads > 0 && (
52
+ <div
53
+ className="h-full bg-green-500"
54
+ style={{ width: `${percentage}%` }}
55
+ />
56
+ )}
57
+ {data.totalWorkloads - data.coveredWorkloads > 0 && (
58
+ <div
59
+ className="h-full bg-theme-hover"
60
+ style={{ width: `${100 - percentage}%` }}
61
+ />
62
+ )}
63
+ </div>
64
+ <span className={clsx('text-sm font-semibold tabular-nums', accentColor)}>
65
+ {percentage}%
66
+ </span>
67
+ </div>
68
+
69
+ <div className="grid grid-cols-1 gap-y-2 mt-4 w-full">
70
+ <StatRow label="Policies" value={data.totalPolicies} />
71
+ <StatRow label="Covered workloads" value={data.coveredWorkloads} total={data.totalWorkloads} />
72
+ <StatRow label="Uncovered workloads" value={data.totalWorkloads - data.coveredWorkloads} warn />
73
+ </div>
74
+ </>
75
+ )}
76
+ </div>
77
+
78
+ <div className="px-4 py-1.5 border-t border-theme-border/50 flex items-center justify-end">
79
+ <span className={clsx(
80
+ 'flex items-center gap-1.5 text-[10px] font-semibold uppercase tracking-wider transition-colors',
81
+ accentColor,
82
+ )}>
83
+ View Policies
84
+ <ArrowRight className="w-3.5 h-3.5 transition-transform group-hover:translate-x-0.5" />
85
+ </span>
86
+ </div>
87
+ </div>
88
+ </button>
89
+ )
90
+ }
91
+
92
+ function StatRow({ label, value, total, warn }: {
93
+ label: string
94
+ value: number
95
+ total?: number
96
+ warn?: boolean
97
+ }) {
98
+ return (
99
+ <div className="flex items-center justify-between">
100
+ <span className="text-xs text-theme-text-secondary">{label}</span>
101
+ <span className={clsx(
102
+ 'text-sm font-semibold tabular-nums',
103
+ warn && value > 0 ? 'text-yellow-400' : 'text-theme-text-primary',
104
+ )}>
105
+ {total !== undefined ? `${value}/${total}` : value}
106
+ </span>
107
+ </div>
108
+ )
109
+ }