@skyhook-io/radar-app 1.9.3 → 1.9.5

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.
@@ -1341,7 +1341,7 @@ export function EmptyState({
1341
1341
  }: {
1342
1342
  icon: ComponentType<{ className?: string }>;
1343
1343
  title: string;
1344
- detail: string;
1344
+ detail: ReactNode;
1345
1345
  action?: ReactNode;
1346
1346
  }) {
1347
1347
  return (
@@ -1528,7 +1528,14 @@ export function integrationBlock(
1528
1528
  <EmptyState
1529
1529
  icon={Gauge}
1530
1530
  title="Capacity needs a newer Radar"
1531
- detail="This cluster's Radar predates the Capacity view (added in Radar v1.9.0). Upgrade the in-cluster Radar to enable it."
1531
+ detail={
1532
+ <>
1533
+ This cluster&rsquo;s Radar predates the Capacity view (added in
1534
+ Radar v1.9.0).
1535
+ <br />
1536
+ Upgrade the in-cluster Radar to enable it.
1537
+ </>
1538
+ }
1532
1539
  />
1533
1540
  );
1534
1541
  return (
@@ -30,6 +30,7 @@ import { AgentSetupNotice } from "./AgentSetupNotice";
30
30
  import { ConsentCard } from "./parts";
31
31
  import { buildLaunchCommand, launchAgentLabel, openInTerminal } from "./launch";
32
32
  import { type RunSummary, type ExecutionProfile } from "../../api/diagnose";
33
+ import { routePath } from "../../api/config";
33
34
 
34
35
  function capWord(s: string): string {
35
36
  return s ? s[0].toUpperCase() + s.slice(1) : s;
@@ -66,7 +67,7 @@ function InvestigationMenu({ run }: { run: RunSummary }) {
66
67
  const [open, setOpen] = useState(false);
67
68
  const [copied, setCopied] = useState(false);
68
69
  const label = launchAgentLabel(run);
69
- const command = buildLaunchCommand(run, `${window.location.origin}/mcp`);
70
+ const command = buildLaunchCommand(run, `${window.location.origin}${routePath('/mcp')}`);
70
71
  // No resumable session yet (or stale run) → nothing to hand off.
71
72
  if (!command) return null;
72
73
 
@@ -10,12 +10,17 @@ import { clsx } from 'clsx'
10
10
  import { formatCPUMillicores, formatMemoryMiB } from '../../utils/format'
11
11
  import { useCapabilitiesContext } from '../../contexts/CapabilitiesContext'
12
12
  import { MCPSetupDialog } from './MCPSetupDialog'
13
- import { pluralize, parseContextName } from '@skyhook-io/k8s-ui'
13
+ import { assetUrl, pluralize, parseContextName } from '@skyhook-io/k8s-ui'
14
14
  import { Tooltip } from '../ui/Tooltip'
15
+ import { routePath } from '../../api/config'
15
16
  import gkeIcon from '../../assets/platform-icons/google_kubernetes_engine.png'
16
17
  import eksIcon from '../../assets/platform-icons/aws_eks.png'
17
18
  import aksIcon from '../../assets/platform-icons/azure-aks.svg'
18
19
 
20
+ const gkeIconUrl = assetUrl(gkeIcon)
21
+ const eksIconUrl = assetUrl(eksIcon)
22
+ const aksIconUrl = assetUrl(aksIcon)
23
+
19
24
  interface ClusterHealthCardProps {
20
25
  health: DashboardResponse['health']
21
26
  counts: DashboardResponse['resourceCounts']
@@ -74,13 +79,13 @@ function MetricsUnavailableHint({ platform, metricsServerAvailable }: { platform
74
79
  function getPlatformInfo(platform: string): { name: string; icon: string | null } {
75
80
  const platformLower = platform.toLowerCase()
76
81
  if (platformLower.includes('gke') || platformLower.includes('google')) {
77
- return { name: 'Google Kubernetes Engine', icon: gkeIcon }
82
+ return { name: 'Google Kubernetes Engine', icon: gkeIconUrl }
78
83
  }
79
84
  if (platformLower.includes('eks') || platformLower.includes('amazon') || platformLower.includes('aws')) {
80
- return { name: 'Amazon EKS', icon: eksIcon }
85
+ return { name: 'Amazon EKS', icon: eksIconUrl }
81
86
  }
82
87
  if (platformLower.includes('aks') || platformLower.includes('azure')) {
83
- return { name: 'Azure Kubernetes Service', icon: aksIcon }
88
+ return { name: 'Azure Kubernetes Service', icon: aksIconUrl }
84
89
  }
85
90
  if (platformLower.includes('openshift')) {
86
91
  return { name: 'OpenShift', icon: null }
@@ -138,7 +143,7 @@ export function ClusterHealthCard({
138
143
  const mcpEnabled = caps.mcpEnabled
139
144
  const isCloud = deployment.mode === 'cloud'
140
145
  const isInCluster = deployment.mode === 'in-cluster' || deployment.mode === 'cloud'
141
- const mcpUrl = `${window.location.origin}/mcp`
146
+ const mcpUrl = `${window.location.origin}${routePath('/mcp')}`
142
147
  // In Cloud, MCP is org-wide and PAT-authed (api.radarhq.io/mcp). The OSS
143
148
  // "this binary is your local MCP server" framing is wrong there — Cloud
144
149
  // surfaces MCP from the hub Home dashboard instead.
@@ -20,6 +20,9 @@ import {
20
20
  import { clsx } from "clsx";
21
21
  import type { MainView } from "../../types";
22
22
  import { Tooltip } from "../ui/Tooltip";
23
+ import { assetUrl } from "@skyhook-io/k8s-ui";
24
+
25
+ const radarLogoUrl = assetUrl("/images/radar/radar-icon.svg");
23
26
 
24
27
  // The views the rail can navigate to. Broader than k8s-ui's ExtendedMainView
25
28
  // (which omits 'applications') — it mirrors the navigable subset of App.tsx's
@@ -233,7 +236,7 @@ function BrandRow({
233
236
  <span className="flex w-14 shrink-0 items-center justify-center">
234
237
  <span className="relative w-7 h-7 rounded-lg overflow-hidden bg-emerald-500/10 border border-emerald-500/20">
235
238
  <img
236
- src="/images/radar/radar-icon.svg"
239
+ src={radarLogoUrl}
237
240
  alt=""
238
241
  aria-hidden
239
242
  className="w-full h-full p-0.5"
@@ -2,6 +2,7 @@ import { useState, useRef, useEffect, useMemo, useCallback } from 'react'
2
2
  import { createPortal } from 'react-dom'
3
3
  import { X, Folder, File, Link2, ChevronRight, ChevronDown, AlertTriangle, Loader2, Search, Download, HardDrive, Shield, ShieldCheck, Terminal, Copy, Check, RefreshCw } from 'lucide-react'
4
4
  import radarLoadingIcon from '@skyhook-io/k8s-ui/assets/radar/radar-icon-loading.svg'
5
+ import { assetUrl } from '@skyhook-io/k8s-ui'
5
6
  import { clsx } from 'clsx'
6
7
  import { useImageMetadata, ApiError } from '../../api/client'
7
8
  import type { FileNode, ImageFilesystem } from '../../types'
@@ -11,6 +12,8 @@ import { Tooltip } from '../ui/Tooltip'
11
12
  import { apiUrl, getAuthHeaders, getCredentialsMode } from '../../api/config'
12
13
  import { Input } from '@skyhook-io/k8s-ui'
13
14
 
15
+ const radarLoadingIconUrl = assetUrl(radarLoadingIcon)
16
+
14
17
  // Manual fetch function for filesystem (not a hook - gives us full control)
15
18
  async function fetchImageFilesystem(
16
19
  image: string,
@@ -182,7 +185,7 @@ export function ImageFilesystemModal({
182
185
  {/* Loading state */}
183
186
  {isLoading && (
184
187
  <div className="flex flex-col items-center justify-center gap-3 h-64">
185
- <img src={radarLoadingIcon} alt="" aria-hidden className="w-11 h-11" />
188
+ <img src={radarLoadingIconUrl} alt="" aria-hidden className="w-11 h-11" />
186
189
  <span className="text-sm text-theme-text-secondary">
187
190
  {isLoadingMetadata ? 'Checking image…' : 'Downloading image layers…'}
188
191
  </span>
@@ -9,7 +9,7 @@ import {
9
9
  import { clsx } from 'clsx'
10
10
  import { useAnimatedUnmount } from '../../hooks/useAnimatedUnmount'
11
11
  import { TRANSITION_BACKDROP, TRANSITION_PANEL } from '../../utils/animation'
12
- import { apiUrl, getAuthHeaders, getCredentialsMode } from '../../api/config'
12
+ import { apiUrl, getAuthHeaders, getCredentialsMode, routePath } from '../../api/config'
13
13
  import {
14
14
  useCloudRole, useVersionCheck, useClusterInfo, usePrometheusStatus, useArgoStatus,
15
15
  } from '../../api/client'
@@ -20,6 +20,14 @@ import { AISettingsSection, type AIDraft } from '../diagnose/AISettings'
20
20
  import { MyPermissionsContent } from './MyPermissionsDialog'
21
21
  import { useDiagnose } from '../diagnose/DiagnoseContext'
22
22
 
23
+ // The loopback URL an MCP client is told to connect to. Shared by the overview
24
+ // row and the MCP section: both must carry the base path, or the URL they
25
+ // advertise 404s under a subpath deployment.
26
+ function mcpLoopbackUrl(): string {
27
+ const port = Number(window.location.port) || 80
28
+ return `http://localhost:${port}${routePath('/mcp')}`
29
+ }
30
+
23
31
  interface Config {
24
32
  kubeconfig?: string
25
33
  kubeconfigDirs?: string[]
@@ -842,8 +850,7 @@ function OverviewPanel({ active, onNavigate }: { active: boolean; onNavigate: (s
842
850
  const agentLabel =
843
851
  diag.agents.find((a) => a.name === diag.selectedAgent)?.label ?? diag.agents[0]?.label
844
852
  const mcpOn = capabilities.mcpEnabled
845
- const port = Number(window.location.port) || 80
846
- const mcpUrl = `http://localhost:${port}/mcp`
853
+ const mcpUrl = mcpLoopbackUrl()
847
854
 
848
855
  const rows: OverviewRow[] = [
849
856
  {
@@ -964,8 +971,10 @@ function AIUnavailableNotice() {
964
971
  <div className="rounded-md border border-theme-border bg-theme-elevated/50 p-3">
965
972
  <p className="text-sm font-medium text-theme-text-primary">No supported agent CLI found</p>
966
973
  <p className="mt-1 text-xs text-theme-text-tertiary">
967
- Install <span className="text-theme-text-secondary">Claude Code</span> or{' '}
968
- <span className="text-theme-text-secondary">Codex</span>, then restart Radar — this tab
974
+ Install <span className="text-theme-text-secondary">Claude Code</span>,{' '}
975
+ <span className="text-theme-text-secondary">Codex</span>, or{' '}
976
+ <span className="text-theme-text-secondary">Cursor</span> (
977
+ <span className="font-mono">cursor-agent</span>), then restart Radar — this tab
969
978
  will show the agent, model, and effort controls.
970
979
  </p>
971
980
  </div>
@@ -1117,7 +1126,7 @@ function MCPSection({
1117
1126
  const [copied, setCopied] = useState(false)
1118
1127
 
1119
1128
  const currentPort = Number(window.location.port) || 80
1120
- const mcpUrl = `http://localhost:${currentPort}/mcp`
1129
+ const mcpUrl = mcpLoopbackUrl()
1121
1130
 
1122
1131
  const handleCopy = () => {
1123
1132
  navigator.clipboard.writeText(mcpUrl)
@@ -1,10 +1,12 @@
1
1
  import { useState, useEffect } from 'react'
2
2
  import type { TrafficSourcesResponse, TrafficWizardState } from '../../types'
3
3
  import { CheckCircle2, XCircle, AlertTriangle, Copy, ExternalLink, ArrowRight, ArrowLeft, Package } from 'lucide-react'
4
- import { PaneLoader } from '@skyhook-io/k8s-ui'
4
+ import { assetUrl, PaneLoader } from '@skyhook-io/k8s-ui'
5
5
  import radarLoadingIcon from '@skyhook-io/k8s-ui/assets/radar/radar-icon-loading.svg'
6
6
  import { InstallWizard } from '../helm/InstallWizard'
7
7
 
8
+ const radarLoadingIconUrl = assetUrl(radarLoadingIcon)
9
+
8
10
  interface TrafficWizardProps {
9
11
  state: TrafficWizardState
10
12
  setState: (state: TrafficWizardState) => void
@@ -100,7 +102,7 @@ export function TrafficWizard({
100
102
  <div className="flex items-center justify-center h-full w-full">
101
103
  <div className="max-w-md w-full p-6 space-y-6">
102
104
  <div className="flex flex-col items-center gap-3">
103
- <img src={radarLoadingIcon} alt="" aria-hidden className="h-11 w-11" />
105
+ <img src={radarLoadingIconUrl} alt="" aria-hidden className="h-11 w-11" />
104
106
  <h2 className="text-lg font-medium text-theme-text-primary">Waiting for traffic source…</h2>
105
107
  <p className="text-sm text-theme-text-secondary">
106
108
  Checking for availability
package/src/main.tsx CHANGED
@@ -153,8 +153,14 @@ window.addEventListener('mouseup', (e: MouseEvent) => {
153
153
  // tab, so it opts into per-view document.title. Library consumers (e.g.
154
154
  // radar-hub-web) render <RadarApp apiBase="..." basename="..." /> WITHOUT this
155
155
  // flag, keeping their own tab title.
156
+ const runtimeConfig = window.__RADAR_RUNTIME_CONFIG__
157
+
156
158
  ReactDOM.createRoot(document.getElementById('radar')!).render(
157
159
  <React.StrictMode>
158
- <RadarApp manageDocumentTitle />
160
+ <RadarApp
161
+ apiBase={runtimeConfig?.apiBase}
162
+ basename={runtimeConfig?.basePath}
163
+ manageDocumentTitle
164
+ />
159
165
  </React.StrictMode>
160
166
  )
package/src/vite-env.d.ts CHANGED
@@ -1 +1,9 @@
1
1
  /// <reference types="vite/client" />
2
+
3
+ interface Window {
4
+ __RADAR_RUNTIME_CONFIG__?: {
5
+ basePath?: string
6
+ apiBase?: string
7
+ assetBase?: string
8
+ }
9
+ }