@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
@@ -1,4 +1,4 @@
1
- import { BottomDock as K8sBottomDock, DockTab, useDock } from '@skyhook-io/k8s-ui'
1
+ import { BottomDock as K8sBottomDock, DockTab, useDock, Input } from '@skyhook-io/k8s-ui'
2
2
  import { TerminalTab } from './TerminalTab'
3
3
  import { LogsTab } from './LogsTab'
4
4
  import { WorkloadLogsTab } from './WorkloadLogsTab'
@@ -70,8 +70,7 @@ function TrafficFlowSearchHeader() {
70
70
  <div className="flex-1 flex items-center ml-2">
71
71
  <div className="relative">
72
72
  <Search className="absolute left-1.5 top-1/2 -translate-y-1/2 w-3 h-3 text-theme-text-tertiary" />
73
- <input
74
- type="text"
73
+ <Input
75
74
  value={search}
76
75
  onChange={e => setSearch(e.target.value)}
77
76
  placeholder="Filter flows..."
@@ -2,6 +2,7 @@
2
2
  export {
3
3
  DockProvider,
4
4
  useDock,
5
+ useDockReservedHeight,
5
6
  useOpenTerminal,
6
7
  useOpenLogs,
7
8
  useOpenWorkloadLogs,
@@ -19,7 +19,7 @@ export function TerminalTab({ namespace, podName, containerName, containers, isA
19
19
  const response = await fetch(apiUrl(`/pods/${namespace}/${podName}/debug`), {
20
20
  method: 'POST',
21
21
  headers: { 'Content-Type': 'application/json' },
22
- body: JSON.stringify({ targetContainer, image: 'busybox:latest' }),
22
+ body: JSON.stringify({ targetContainer }),
23
23
  })
24
24
  if (!response.ok) {
25
25
  const err = await response.json().catch(() => ({ error: 'Unknown error' }))
@@ -1,4 +1,5 @@
1
1
  import { WorkloadLogsViewer } from '../logs/WorkloadLogsViewer'
2
+ import { ScheduledWorkloadLogsViewer } from '../logs/ScheduledWorkloadLogsViewer'
2
3
 
3
4
  interface WorkloadLogsTabProps {
4
5
  namespace: string
@@ -11,13 +12,28 @@ export function WorkloadLogsTab({
11
12
  workloadKind,
12
13
  workloadName,
13
14
  }: WorkloadLogsTabProps) {
15
+ const normalizedKind = workloadKind.toLowerCase()
16
+ const isScheduled = normalizedKind === 'cronjob' || normalizedKind === 'cronjobs' ||
17
+ normalizedKind === 'cronworkflow' || normalizedKind === 'cronworkflows' ||
18
+ normalizedKind === 'workflowtemplate' || normalizedKind === 'workflowtemplates' ||
19
+ normalizedKind === 'clusterworkflowtemplate' || normalizedKind === 'clusterworkflowtemplates' ||
20
+ normalizedKind === 'scaledjob' || normalizedKind === 'scaledjobs'
21
+
14
22
  return (
15
23
  <div className="h-full">
16
- <WorkloadLogsViewer
17
- kind={workloadKind}
18
- namespace={namespace}
19
- name={workloadName}
20
- />
24
+ {isScheduled ? (
25
+ <ScheduledWorkloadLogsViewer
26
+ kind={workloadKind}
27
+ namespace={namespace}
28
+ name={workloadName}
29
+ />
30
+ ) : (
31
+ <WorkloadLogsViewer
32
+ kind={workloadKind}
33
+ namespace={namespace}
34
+ name={workloadName}
35
+ />
36
+ )}
21
37
  </div>
22
38
  )
23
39
  }
@@ -1,2 +1,2 @@
1
- export { DockProvider, useDock, useOpenTerminal, useOpenLogs, useOpenWorkloadLogs, useOpenNodeTerminal, useOpenLocalTerminal } from './DockContext'
1
+ export { DockProvider, useDock, useDockReservedHeight, useOpenTerminal, useOpenLogs, useOpenWorkloadLogs, useOpenNodeTerminal, useOpenLocalTerminal } from './DockContext'
2
2
  export { BottomDock } from './BottomDock'
@@ -0,0 +1,170 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { WorkflowExecutionActivity } from '@skyhook-io/k8s-ui/utils/workflow-execution'
3
+ import { activityPreviewItems, effectiveDefinitionResource, isDirectRunKind, retentionHistoryCopy, runMessageNeedsDisclosure, workflowDefinitionParameters, workflowDefinitionTarget, workflowRunArguments } from './BatchExecutionView'
4
+
5
+ function activity(id: string, tone: WorkflowExecutionActivity['tone'] = 'success'): WorkflowExecutionActivity {
6
+ return { id, at: '2026-01-01T00:00:00Z', label: id, tone }
7
+ }
8
+
9
+ describe('activityPreviewItems', () => {
10
+ it('keeps a stable chronological prefix so expansion only appends activity', () => {
11
+ const items = [
12
+ activity('workflow-started', 'info'),
13
+ ...Array.from({ length: 14 }, (_, index) => activity(`node-${index}`)),
14
+ activity('workflow-finished'),
15
+ ]
16
+
17
+ expect(activityPreviewItems(items)).toEqual(items.slice(0, 8))
18
+ })
19
+
20
+ it('keeps large failed histories capped while preserving chronological prefix ordering', () => {
21
+ const items = Array.from({ length: 500 }, (_, index) => activity(`event-${index}`, index === 499 ? 'danger' : 'success'))
22
+
23
+ expect(activityPreviewItems(items)).toEqual(items.slice(0, 8))
24
+ })
25
+ })
26
+
27
+ describe('retentionHistoryCopy', () => {
28
+ it('puts configured history limits beside runs and calls out a reached phase limit', () => {
29
+ const resource = { spec: { successfulJobsHistoryLimit: 1, failedJobsHistoryLimit: 3 } }
30
+
31
+ expect(retentionHistoryCopy('CronJob', resource, { succeeded: 1, failed: 0 })).toBe(
32
+ 'Keeps latest 1 succeeded / latest 3 failed. Success limit reached.',
33
+ )
34
+ })
35
+
36
+ it('uses controller defaults for scheduled workflows', () => {
37
+ expect(retentionHistoryCopy('CronWorkflow', { spec: {} }, { succeeded: 1, failed: 1 })).toBe(
38
+ 'Keeps latest 3 succeeded / latest 1 failed. Failure limit reached.',
39
+ )
40
+ })
41
+
42
+ it('does not invent limits for ScaledJobs that do not configure them', () => {
43
+ expect(retentionHistoryCopy('ScaledJob', { spec: {} }, { succeeded: 4, failed: 2 })).toBeNull()
44
+ })
45
+
46
+ it('does not show retention settings for template-level histories', () => {
47
+ expect(retentionHistoryCopy('WorkflowTemplate', { spec: {} }, { succeeded: 4, failed: 2 })).toBeNull()
48
+ })
49
+ })
50
+
51
+ describe('runMessageNeedsDisclosure', () => {
52
+ it('keeps short successful messages inline', () => {
53
+ expect(runMessageNeedsDisclosure('Reached expected number of succeeded pods')).toBe(false)
54
+ })
55
+
56
+ it('keeps short failures inline and discloses multiline or long messages', () => {
57
+ expect(runMessageNeedsDisclosure('permission denied')).toBe(false)
58
+ expect(runMessageNeedsDisclosure('first line\nsecond line')).toBe(true)
59
+ expect(runMessageNeedsDisclosure('x'.repeat(141))).toBe(true)
60
+ })
61
+ })
62
+
63
+ describe('workflow parameters', () => {
64
+ const definition = {
65
+ spec: {
66
+ arguments: {
67
+ parameters: [
68
+ { name: 'region', value: 'us-east-1', description: 'Deployment region' },
69
+ { name: 'mode' },
70
+ ],
71
+ },
72
+ },
73
+ }
74
+
75
+ it('reads the input contract from workflow definitions', () => {
76
+ expect(workflowDefinitionParameters('WorkflowTemplate', definition)).toEqual(definition.spec.arguments.parameters)
77
+ expect(workflowDefinitionParameters('Workflow', definition)).toEqual([])
78
+ })
79
+
80
+ it('shows only selected-run arguments that differ from definition defaults', () => {
81
+ const run = {
82
+ spec: {
83
+ arguments: {
84
+ parameters: [
85
+ { name: 'region', value: 'eu-west-1' },
86
+ { name: 'mode', value: 'safe' },
87
+ { name: 'unchanged', value: 'same' },
88
+ ],
89
+ },
90
+ },
91
+ }
92
+ const definitionWithUnchanged = {
93
+ spec: {
94
+ arguments: {
95
+ parameters: [...definition.spec.arguments.parameters, { name: 'unchanged', value: 'same' }],
96
+ },
97
+ },
98
+ }
99
+
100
+ expect(workflowRunArguments(run, 'WorkflowTemplate', definitionWithUnchanged).map((parameter) => parameter.name)).toEqual(['region', 'mode'])
101
+ expect(workflowRunArguments(run, 'Workflow', run)).toHaveLength(3)
102
+ })
103
+ })
104
+
105
+ describe('referenced CronWorkflow definitions', () => {
106
+ const cronWorkflow = {
107
+ metadata: { namespace: 'jobs' },
108
+ spec: {
109
+ workflowSpec: {
110
+ workflowTemplateRef: { name: 'shared' },
111
+ arguments: { parameters: [{ name: 'region', value: 'eu' }] },
112
+ },
113
+ },
114
+ }
115
+
116
+ it('fetches the referenced namespaced definition', () => {
117
+ expect(workflowDefinitionTarget('CronWorkflow', cronWorkflow)).toEqual({
118
+ kind: 'workflowtemplates',
119
+ namespace: 'jobs',
120
+ name: 'shared',
121
+ group: 'argoproj.io',
122
+ })
123
+ })
124
+
125
+ it('combines referenced execution details with CronWorkflow overrides', () => {
126
+ const referenced = {
127
+ spec: {
128
+ entrypoint: 'main',
129
+ serviceAccountName: 'base-account',
130
+ arguments: { parameters: [{ name: 'region', value: 'us', description: 'Target region' }, { name: 'mode', value: 'safe' }] },
131
+ templates: [{ name: 'main', container: { image: 'example/shared:v1' } }],
132
+ },
133
+ }
134
+
135
+ const effective = effectiveDefinitionResource('CronWorkflow', cronWorkflow, referenced)
136
+
137
+ expect(effective.spec.workflowSpec).toMatchObject({
138
+ entrypoint: 'main',
139
+ serviceAccountName: 'base-account',
140
+ templates: referenced.spec.templates,
141
+ arguments: {
142
+ parameters: [
143
+ { name: 'region', value: 'eu', description: 'Target region' },
144
+ { name: 'mode', value: 'safe' },
145
+ ],
146
+ },
147
+ })
148
+ })
149
+
150
+ it('replaces valueFrom when a schedule supplies a concrete value', () => {
151
+ const referenced = {
152
+ spec: {
153
+ arguments: { parameters: [{ name: 'region', valueFrom: { configMapKeyRef: { name: 'defaults', key: 'region' } } }] },
154
+ },
155
+ }
156
+
157
+ const effective = effectiveDefinitionResource('CronWorkflow', cronWorkflow, referenced)
158
+
159
+ expect(effective.spec.workflowSpec.arguments.parameters[0]).toEqual({ name: 'region', value: 'eu' })
160
+ })
161
+ })
162
+
163
+ describe('direct run identity', () => {
164
+ it('suppresses captured configuration only when the workload is the run itself', () => {
165
+ expect(isDirectRunKind('Job', 'jobs')).toBe(true)
166
+ expect(isDirectRunKind('Workflow', 'workflows')).toBe(true)
167
+ expect(isDirectRunKind('CronJob', 'jobs')).toBe(false)
168
+ expect(isDirectRunKind('WorkflowTemplate', 'workflows')).toBe(false)
169
+ })
170
+ })