@burdenoff/microfe-vibecontrols 2026.623.3 → 2026.623.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.
Files changed (29) hide show
  1. package/dist/VibeControlsRoutes.js +161 -149
  2. package/dist/VibeControlsRoutes.js.map +1 -1
  3. package/dist/components/agents/AgentCard.js +1 -1
  4. package/dist/components/agents/AgentCard.js.map +1 -1
  5. package/dist/components/agents/AgentForm.js +2 -2
  6. package/dist/components/agents/AgentForm.js.map +1 -1
  7. package/dist/components/catalog/CatalogCard.js +9 -12
  8. package/dist/components/catalog/CatalogCard.js.map +1 -1
  9. package/dist/components/idp-components/ComponentCard.js +9 -12
  10. package/dist/components/idp-components/ComponentCard.js.map +1 -1
  11. package/dist/components/notes/NoteCard.js +9 -12
  12. package/dist/components/notes/NoteCard.js.map +1 -1
  13. package/dist/components/sessions/SessionCard.js +9 -12
  14. package/dist/components/sessions/SessionCard.js.map +1 -1
  15. package/dist/components/shared/AgentSetupBanner.js +2 -2
  16. package/dist/components/shared/AgentSetupBanner.js.map +1 -1
  17. package/dist/components/shared/Card.js +1 -1
  18. package/dist/components/shared/Card.js.map +1 -1
  19. package/dist/components/shared/InstallTabs.js +17 -56
  20. package/dist/components/shared/InstallTabs.js.map +1 -1
  21. package/dist/components/shared/StatCard.js +1 -1
  22. package/dist/components/shared/StatCard.js.map +1 -1
  23. package/dist/components/tunnels/TunnelCard.js +1 -1
  24. package/dist/components/tunnels/TunnelCard.js.map +1 -1
  25. package/dist/pages/GettingStartedPage.js +130 -114
  26. package/dist/pages/GettingStartedPage.js.map +1 -1
  27. package/dist/translations/en.js +27 -52
  28. package/dist/translations/en.js.map +1 -1
  29. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"VibeControlsRoutes.js","names":[],"sources":["../src/VibeControlsRoutes.tsx"],"sourcesContent":["import React, { Suspense, useEffect } from 'react';\nimport { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useTr } from './shared/hooks/useTr';\n\n// Lazy load - All pages (required for @/ alias resolution when consumed by host app)\nconst OverviewPage = React.lazy(() =>\n import('./pages/OverviewPage').then((m) => ({ default: m.OverviewPage }))\n);\n\n// Other pages\nconst AgentsLandingPage = React.lazy(() =>\n import('./pages/AgentsLandingPage').then((m) => ({ default: m.AgentsLandingPage }))\n);\nconst AgentDetailsPage = React.lazy(() =>\n import('./pages/AgentDetailsPage').then((m) => ({ default: m.AgentDetailsPage }))\n);\nconst TargetDetailsPage = React.lazy(() =>\n import('./pages/TargetDetailsPage').then((m) => ({ default: m.TargetDetailsPage }))\n);\nconst SessionsPage = React.lazy(() =>\n import('./pages/SessionsPage').then((m) => ({ default: m.SessionsPage }))\n);\nconst SessionDetailsPage = React.lazy(() =>\n import('./pages/SessionDetailsPage').then((m) => ({ default: m.SessionDetailsPage }))\n);\nconst VibesPage = React.lazy(() =>\n import('./pages/VibesPage').then((m) => ({ default: m.VibesPage }))\n);\nconst VibeDetailsPage = React.lazy(() =>\n import('./pages/VibeDetailsPage').then((m) => ({ default: m.VibeDetailsPage }))\n);\nconst NoteDetailsPage = React.lazy(() =>\n import('./pages/NoteDetailsPage').then((m) => ({ default: m.NoteDetailsPage }))\n);\nconst TunnelsLandingPage = React.lazy(() =>\n import('./pages/TunnelsLandingPage').then((m) => ({ default: m.TunnelsLandingPage }))\n);\nconst TunnelDetailsPage = React.lazy(() =>\n import('./pages/tunnels/TunnelDetailsPage').then((m) => ({ default: m.TunnelDetailsPage }))\n);\nconst TunnelCreateWizard = React.lazy(() =>\n import('./components/tunnels/TunnelCreateWizard').then((m) => ({ default: m.TunnelCreateWizard }))\n);\nconst SettingsPage = React.lazy(() =>\n import('./pages/SettingsPage').then((m) => ({ default: m.SettingsPage }))\n);\nconst HealthPage = React.lazy(() =>\n import('./pages/HealthPage').then((m) => ({ default: m.HealthPage }))\n);\nconst GettingStartedPage = React.lazy(() =>\n import('./pages/GettingStartedPage').then((m) => ({ default: m.GettingStartedPage }))\n);\nconst SharedSessionPage = React.lazy(() =>\n import('./pages/SharedSessionPage').then((m) => ({ default: m.SharedSessionPage }))\n);\nconst VibeDeckPage = React.lazy(() =>\n import('./pages/VibeDeckPage').then((m) => ({ default: m.VibeDeckPage }))\n);\nconst SharedVibeDeckPage = React.lazy(() =>\n import('./pages/SharedVibeDeckPage').then((m) => ({ default: m.SharedVibeDeckPage }))\n);\nconst VibeCalendarPage = React.lazy(() =>\n import('./pages/VibeCalendarPage').then((m) => ({ default: m.VibeCalendarPage }))\n);\nconst VibeTaskDetailPage = React.lazy(() =>\n import('./pages/VibeTaskDetailPage').then((m) => ({ default: m.VibeTaskDetailPage }))\n);\nconst ScheduledJobDetailPage = React.lazy(() =>\n import('./pages/ScheduledJobDetailPage').then((m) => ({ default: m.ScheduledJobDetailPage }))\n);\nconst ActionsPage = React.lazy(() =>\n import('./pages/ActionsPage').then((m) => ({ default: m.ActionsPage }))\n);\nconst VibeAuditPage = React.lazy(() =>\n import('./pages/VibeAuditPage').then((m) => ({ default: m.VibeAuditPage }))\n);\nconst AnalyticsPage = React.lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\nconst VibeDocsPage = React.lazy(() =>\n import('./pages/VibeDocsPage').then((m) => ({ default: m.VibeDocsPage }))\n);\nconst LogsPage = React.lazy(() =>\n import('./pages/LogsPage').then((m) => ({ default: m.LogsPage }))\n);\n\n// Catalog, Components, Templates pages\nconst CatalogPage = React.lazy(() =>\n import('./pages/CatalogPage').then((m) => ({ default: m.CatalogPage }))\n);\nconst CatalogDetailPage = React.lazy(() =>\n import('./pages/CatalogDetailPage').then((m) => ({ default: m.CatalogDetailPage }))\n);\nconst CatalogDocsPage = React.lazy(() =>\n import('./pages/CatalogDocsPage').then((m) => ({ default: m.CatalogDocsPage }))\n);\nconst ComponentsPage = React.lazy(() =>\n import('./pages/ComponentsPage').then((m) => ({ default: m.ComponentsPage }))\n);\nconst EnvTopologyPage = React.lazy(() =>\n import('./pages/EnvTopologyPage').then((m) => ({ default: m.EnvTopologyPage }))\n);\nconst WebhooksPage = React.lazy(() =>\n import('./pages/WebhooksPage').then((m) => ({ default: m.WebhooksPage }))\n);\nconst ComponentDetailPage = React.lazy(() =>\n import('./pages/ComponentDetailPage').then((m) => ({ default: m.ComponentDetailPage }))\n);\nconst ComponentDocsPage = React.lazy(() =>\n import('./pages/ComponentDocsPage').then((m) => ({ default: m.ComponentDocsPage }))\n);\nconst TemplatesPage = React.lazy(() =>\n import('./pages/TemplatesPage').then((m) => ({ default: m.TemplatesPage }))\n);\nconst TemplateDetailPage = React.lazy(() =>\n import('./pages/TemplateDetailPage').then((m) => ({ default: m.TemplateDetailPage }))\n);\nconst PluginsCatalogPage = React.lazy(() =>\n import('./pages/PluginsCatalogPage').then((m) => ({ default: m.PluginsCatalogPage }))\n);\n\n// AI pages\nconst AIPage = React.lazy(() => import('./pages/ai/AIPage').then((m) => ({ default: m.AIPage })));\nconst AISessionDetailPage = React.lazy(() =>\n import('./pages/ai/AISessionDetailPage').then((m) => ({ default: m.AISessionDetailPage }))\n);\nconst AgentManagerPage = React.lazy(() =>\n import('./pages/ai/AgentManagerPage').then((m) => ({ default: m.AgentManagerPage }))\n);\nconst AIBookmarksPage = React.lazy(() =>\n import('./pages/ai/AIBookmarksPage').then((m) => ({ default: m.AIBookmarksPage }))\n);\nconst AIBuilderPage = React.lazy(() =>\n import('./pages/ai/AIBuilderPage').then((m) => ({ default: m.AIBuilderPage }))\n);\n\n// Loading fallback component\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[200px]\">\n <div className=\"animate-pulse bg-muted rounded w-full h-full\" />\n </div>\n );\n}\n\n/**\n * Internal router for the VibeControls microfrontend.\n * This component handles routing within the microfrontend using React Router v7.\n *\n * Routes:\n * - / → Overview page (dashboard)\n * - /agents → Agents management page\n * - /agents/:id → Agent details page\n * - /sessions → Sessions management page\n * - /sessions/:id → Session details page\n * - /vibes → Vibes (projects) management page\n * - /vibes/:id → Vibe details page (includes Notes tab)\n * - /settings → Settings page\n * - /webhooks → Legacy redirect to Developer Portal webhooks\n * - /health → Health monitoring page\n * - /* → Catch-all redirects to overview\n */\nfunction ScrollToTop() {\n const { pathname } = useLocation();\n useEffect(() => {\n // The scrollable container is the <main> element with overflow-auto\n // inside AppShellLayout, not the window itself.\n const main = document.querySelector('main');\n if (main) main.scrollTop = 0;\n }, [pathname]);\n return null;\n}\n\n/**\n * Legacy redirect: /ai-tools/<tab> → /ai-tools/ai/<tab>.\n *\n * Before the Plan / AI mode toggle the page was reachable directly at\n * /ai-tools/<tab>. New shape is /ai-tools/<mode>/<tab>. We keep the\n * legacy links working by mapping the captured `:tab` param: if it\n * resolves to a known AI sub-tab, redirect there; if it's literally\n * \"plan\", route to the Plan page; otherwise fall back to AI overview.\n */\nconst VALID_AI_TABS = new Set<string>([\n 'overview',\n 'templates',\n 'contexts',\n 'prompts',\n 'sessions',\n 'tasks',\n 'stats',\n 'playground',\n 'config',\n]);\n\nfunction LegacyAIToolsRedirect() {\n const { tab } = useParams<{ tab?: string }>();\n if (tab === 'plan') {\n return <Navigate to=\"/vibecontrols/ai-tools/plan\" replace />;\n }\n if (tab && VALID_AI_TABS.has(tab)) {\n return <Navigate to={`/vibecontrols/ai-tools/ai/${tab}`} replace />;\n }\n return <Navigate to=\"/vibecontrols/ai-tools/ai/overview\" replace />;\n}\n\n/**\n * Fallback shown when a feature flag is OFF.\n */\nfunction FeatureDisabledFallback() {\n const tr = useTr();\n return (\n <div className=\"flex items-center justify-center h-full min-h-[200px] text-text-secondary text-sm\">\n {tr(\n 'vibecontrols.pages.featureDisabled',\n 'This feature is currently disabled. You can enable it from Feature Previews.'\n )}\n </div>\n );\n}\n\n/**\n * Helper: wraps a page element in a FeatureGate.\n * defaultValue={true} means the feature shows by default (before flags are seeded).\n * Only hides when a flag is explicitly set to false.\n */\nfunction gated(flag: string, element: React.ReactNode, defaultValue = true): React.ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={defaultValue} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\nexport function VibeControlsRoutes() {\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <ScrollToTop />\n <Routes>\n {/* Default route - overview page */}\n <Route index element={gated('vibecontrols.overview', <OverviewPage />)} />\n\n {/* Core pages */}\n {/* Plugins catalog — searchable grid of every published plugin. */}\n <Route path=\"plugins\" element={gated('vibecontrols.vibes', <PluginsCatalogPage />)} />\n\n <Route path=\"agents\" element={<AgentsLandingPage />} />\n <Route path=\"agents/new\" element={<Navigate to=\"../agents?create=true\" replace />} />\n <Route path=\"agents/:id\" element={<AgentDetailsPage />} />\n <Route path=\"sessions\" element={<SessionsPage />} />\n <Route path=\"vibes\" element={gated('vibecontrols.vibes', <VibesPage />)} />\n <Route path=\"vibes/new\" element={<Navigate to=\"../vibes?create=true\" replace />} />\n <Route path=\"vibes/:id\" element={gated('vibecontrols.vibes', <VibeDetailsPage />)} />\n <Route path=\"vibes/:id/docs/*\" element={gated('vibecontrols.vibes', <VibeDocsPage />)} />\n <Route path=\"notes/:id\" element={gated('vibecontrols.vibes', <NoteDetailsPage />)} />\n <Route path=\"sessions/:id\" element={<SessionDetailsPage />} />\n {/* Agent Manager — multi-session tabbed UI (replaces old AI page) */}\n <Route path=\"ai\" element={gated('vibecontrols.ai', <AgentManagerPage />)} />\n {/* Saved messages — listed BEFORE ai/:tab so \"bookmarks\" is not consumed as a tab. */}\n <Route path=\"ai/bookmarks\" element={gated('vibecontrols.ai', <AIBookmarksPage />)} />\n {/* AI Builder — describe → scaffold → test in sandbox → publish to catalog. */}\n <Route path=\"ai-builder\" element={gated('vibecontrols.ai', <AIBuilderPage />)} />\n <Route path=\"ai/:tab\" element={gated('vibecontrols.ai', <AgentManagerPage />)} />\n <Route\n path=\"ai/sessions/:sessionId\"\n element={gated('vibecontrols.ai', <AgentManagerPage />)}\n />\n {/* AI Tools — Plan / AI modes:\n /ai-tools → /ai-tools/ai/overview (redirect)\n /ai-tools/plan → Plan mode (plannotator iframe)\n /ai-tools/ai → /ai-tools/ai/overview (redirect)\n /ai-tools/ai/:tab → AI mode with active sub-tab\n Legacy /ai-tools/<tab> URLs from before the Plan mode rollout\n redirect into the new shape via LegacyAIToolsRedirect. */}\n <Route path=\"ai-tools\" element={<Navigate to=\"ai/overview\" replace />} />\n <Route path=\"ai-tools/plan\" element={gated('vibecontrols.ai', <AIPage />)} />\n <Route path=\"ai-tools/ai\" element={<Navigate to=\"overview\" replace />} />\n <Route path=\"ai-tools/ai/:tab\" element={gated('vibecontrols.ai', <AIPage />)} />\n <Route path=\"ai-tools/:tab\" element={<LegacyAIToolsRedirect />} />\n <Route\n path=\"ai-tools/sessions/:sessionId\"\n element={gated('vibecontrols.ai', <AISessionDetailPage />)}\n />\n\n {/* Targets list lives in the Targets & Agents landing; per-target\n detail is its own page (mirrors agents/:id). */}\n <Route path=\"targets\" element={<Navigate to=\"../agents?tab=targets\" replace />} />\n <Route path=\"targets/:id\" element={<TargetDetailsPage />} />\n {/* Agent Graph is also a tab inside the Targets & Agents landing. */}\n <Route path=\"agent-graph\" element={<Navigate to=\"../agents?tab=graph\" replace />} />\n\n {/* Catalog */}\n <Route path=\"catalogs\" element={gated('vibecontrols.catalogs', <CatalogPage />)} />\n <Route\n path=\"catalogs/:id\"\n element={gated('vibecontrols.catalogs', <CatalogDetailPage />)}\n />\n <Route\n path=\"catalogs/:id/docs/*\"\n element={gated('vibecontrols.catalogs', <CatalogDocsPage />)}\n />\n <Route path=\"components\" element={gated('vibecontrols.components', <ComponentsPage />)} />\n <Route\n path=\"environments\"\n element={gated('vibecontrols.components', <EnvTopologyPage />)}\n />\n <Route\n path=\"components/:id\"\n element={gated('vibecontrols.components', <ComponentDetailPage />)}\n />\n <Route\n path=\"components/:id/docs/*\"\n element={gated('vibecontrols.components', <ComponentDocsPage />)}\n />\n <Route path=\"templates\" element={gated('vibecontrols.templates', <TemplatesPage />)} />\n <Route\n path=\"templates/:id\"\n element={gated('vibecontrols.templates', <TemplateDetailPage />)}\n />\n\n {/* Automation */}\n <Route path=\"actions\" element={gated('vibecontrols.actions', <ActionsPage />)} />\n <Route path=\"vibedeck\" element={gated('vibecontrols.vibedeck', <VibeDeckPage />)} />\n <Route path=\"calendar\" element={gated('vibecontrols.calendar', <VibeCalendarPage />)} />\n <Route\n path=\"calendar/:viewType\"\n element={gated('vibecontrols.calendar', <VibeCalendarPage />)}\n />\n <Route\n path=\"calendar/task/:taskId\"\n element={gated('vibecontrols.calendar', <VibeTaskDetailPage />)}\n />\n {/* ScheduledJob (wspace-scheduler-svc) — separate id space\n from VibeCalendarTask above, so it needs its own route. */}\n <Route\n path=\"calendar/scheduled-job/:jobId\"\n element={gated('vibecontrols.calendar', <ScheduledJobDetailPage />)}\n />\n <Route path=\"webhooks\" element={gated('vibecontrols.webhooks', <WebhooksPage />)} />\n <Route path=\"webhooks/new\" element={gated('vibecontrols.webhooks', <WebhooksPage />)} />\n <Route path=\"webhooks/:id\" element={gated('vibecontrols.webhooks', <WebhooksPage />)} />\n {/* GitOps consolidated into Vibe detail tab — old /gitops routes redirect to vibes list. */}\n <Route path=\"gitops\" element={<Navigate to=\"../vibes\" replace />} />\n <Route path=\"gitops/:tab\" element={<Navigate to=\"../vibes\" replace />} />\n\n {/* Infrastructure */}\n <Route path=\"tunnels\" element={gated('vibecontrols.tunnels', <TunnelsLandingPage />)} />\n <Route path=\"tunnels/new\" element={gated('vibecontrols.tunnels', <TunnelCreateWizard />)} />\n <Route\n path=\"tunnels/:tunnelId\"\n element={gated('vibecontrols.tunnels', <TunnelDetailsPage />)}\n />\n\n {/* Insights */}\n <Route path=\"health\" element={gated('vibecontrols.health', <HealthPage />)} />\n <Route path=\"analytics\" element={gated('vibecontrols.analytics', <AnalyticsPage />)} />\n <Route path=\"audit\" element={gated('vibecontrols.audit-log', <VibeAuditPage />, false)} />\n <Route path=\"logs\" element={<LogsPage />} />\n\n {/* Settings */}\n <Route path=\"settings\" element={gated('vibecontrols.settings', <SettingsPage />)} />\n\n {/* Setup */}\n {/* Not gated — onboarding flow must always be reachable */}\n <Route path=\"getting-started\" element={<GettingStartedPage />} />\n\n {/* Shared public routes (no gating — accessible via public links) */}\n <Route path=\"share/:token\" element={<SharedSessionPage />} />\n <Route path=\"deck/:token\" element={<SharedVibeDeckPage />} />\n\n {/* Legacy redirects */}\n <Route path=\"access\" element={<Navigate to=\"../agents?tab=targets\" replace />} />\n <Route path=\"ssh-targets\" element={<Navigate to=\"../agents?tab=targets\" replace />} />\n <Route path=\"ssh-terminals\" element={<Navigate to=\"../agents?tab=targets\" replace />} />\n\n {/* Catch-all route - redirect to overview */}\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VibeControlsRoutes.js","names":[],"sources":["../src/VibeControlsRoutes.tsx"],"sourcesContent":["import React, { Suspense, useEffect } from 'react';\nimport { Navigate, Route, Routes, useLocation, useParams, useNavigate } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { NotFoundPage } from '@burdenoff/fe-libs/shared/pages';\n\n/** Catch-all renderer: a friendly 404 inside the content area (was a broken\n * redirect to \"\" that left the page blank). \"Back to home\" returns to the\n * VibeControls overview via SPA navigation. */\nfunction VibeNotFound() {\n const navigate = useNavigate();\n return (\n <NotFoundPage homeLabel=\"Back to VibeControls\" onGoHome={() => navigate('/vibecontrols')} />\n );\n}\nimport { useTr } from './shared/hooks/useTr';\n\n// Lazy load - All pages (required for @/ alias resolution when consumed by host app)\nconst OverviewPage = React.lazy(() =>\n import('./pages/OverviewPage').then((m) => ({ default: m.OverviewPage }))\n);\n\n// Other pages\nconst AgentsLandingPage = React.lazy(() =>\n import('./pages/AgentsLandingPage').then((m) => ({ default: m.AgentsLandingPage }))\n);\nconst AgentDetailsPage = React.lazy(() =>\n import('./pages/AgentDetailsPage').then((m) => ({ default: m.AgentDetailsPage }))\n);\nconst TargetDetailsPage = React.lazy(() =>\n import('./pages/TargetDetailsPage').then((m) => ({ default: m.TargetDetailsPage }))\n);\nconst SessionsPage = React.lazy(() =>\n import('./pages/SessionsPage').then((m) => ({ default: m.SessionsPage }))\n);\nconst SessionDetailsPage = React.lazy(() =>\n import('./pages/SessionDetailsPage').then((m) => ({ default: m.SessionDetailsPage }))\n);\nconst VibesPage = React.lazy(() =>\n import('./pages/VibesPage').then((m) => ({ default: m.VibesPage }))\n);\nconst VibeDetailsPage = React.lazy(() =>\n import('./pages/VibeDetailsPage').then((m) => ({ default: m.VibeDetailsPage }))\n);\nconst NoteDetailsPage = React.lazy(() =>\n import('./pages/NoteDetailsPage').then((m) => ({ default: m.NoteDetailsPage }))\n);\nconst TunnelsLandingPage = React.lazy(() =>\n import('./pages/TunnelsLandingPage').then((m) => ({ default: m.TunnelsLandingPage }))\n);\nconst TunnelDetailsPage = React.lazy(() =>\n import('./pages/tunnels/TunnelDetailsPage').then((m) => ({ default: m.TunnelDetailsPage }))\n);\nconst TunnelCreateWizard = React.lazy(() =>\n import('./components/tunnels/TunnelCreateWizard').then((m) => ({ default: m.TunnelCreateWizard }))\n);\nconst SettingsPage = React.lazy(() =>\n import('./pages/SettingsPage').then((m) => ({ default: m.SettingsPage }))\n);\nconst HealthPage = React.lazy(() =>\n import('./pages/HealthPage').then((m) => ({ default: m.HealthPage }))\n);\nconst GettingStartedPage = React.lazy(() =>\n import('./pages/GettingStartedPage').then((m) => ({ default: m.GettingStartedPage }))\n);\nconst SharedSessionPage = React.lazy(() =>\n import('./pages/SharedSessionPage').then((m) => ({ default: m.SharedSessionPage }))\n);\nconst VibeDeckPage = React.lazy(() =>\n import('./pages/VibeDeckPage').then((m) => ({ default: m.VibeDeckPage }))\n);\nconst SharedVibeDeckPage = React.lazy(() =>\n import('./pages/SharedVibeDeckPage').then((m) => ({ default: m.SharedVibeDeckPage }))\n);\nconst VibeCalendarPage = React.lazy(() =>\n import('./pages/VibeCalendarPage').then((m) => ({ default: m.VibeCalendarPage }))\n);\nconst VibeTaskDetailPage = React.lazy(() =>\n import('./pages/VibeTaskDetailPage').then((m) => ({ default: m.VibeTaskDetailPage }))\n);\nconst ScheduledJobDetailPage = React.lazy(() =>\n import('./pages/ScheduledJobDetailPage').then((m) => ({ default: m.ScheduledJobDetailPage }))\n);\nconst ActionsPage = React.lazy(() =>\n import('./pages/ActionsPage').then((m) => ({ default: m.ActionsPage }))\n);\nconst VibeAuditPage = React.lazy(() =>\n import('./pages/VibeAuditPage').then((m) => ({ default: m.VibeAuditPage }))\n);\nconst AnalyticsPage = React.lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\nconst VibeDocsPage = React.lazy(() =>\n import('./pages/VibeDocsPage').then((m) => ({ default: m.VibeDocsPage }))\n);\nconst LogsPage = React.lazy(() =>\n import('./pages/LogsPage').then((m) => ({ default: m.LogsPage }))\n);\n\n// Catalog, Components, Templates pages\nconst CatalogPage = React.lazy(() =>\n import('./pages/CatalogPage').then((m) => ({ default: m.CatalogPage }))\n);\nconst CatalogDetailPage = React.lazy(() =>\n import('./pages/CatalogDetailPage').then((m) => ({ default: m.CatalogDetailPage }))\n);\nconst CatalogDocsPage = React.lazy(() =>\n import('./pages/CatalogDocsPage').then((m) => ({ default: m.CatalogDocsPage }))\n);\nconst ComponentsPage = React.lazy(() =>\n import('./pages/ComponentsPage').then((m) => ({ default: m.ComponentsPage }))\n);\nconst EnvTopologyPage = React.lazy(() =>\n import('./pages/EnvTopologyPage').then((m) => ({ default: m.EnvTopologyPage }))\n);\nconst WebhooksPage = React.lazy(() =>\n import('./pages/WebhooksPage').then((m) => ({ default: m.WebhooksPage }))\n);\nconst ComponentDetailPage = React.lazy(() =>\n import('./pages/ComponentDetailPage').then((m) => ({ default: m.ComponentDetailPage }))\n);\nconst ComponentDocsPage = React.lazy(() =>\n import('./pages/ComponentDocsPage').then((m) => ({ default: m.ComponentDocsPage }))\n);\nconst TemplatesPage = React.lazy(() =>\n import('./pages/TemplatesPage').then((m) => ({ default: m.TemplatesPage }))\n);\nconst TemplateDetailPage = React.lazy(() =>\n import('./pages/TemplateDetailPage').then((m) => ({ default: m.TemplateDetailPage }))\n);\nconst PluginsCatalogPage = React.lazy(() =>\n import('./pages/PluginsCatalogPage').then((m) => ({ default: m.PluginsCatalogPage }))\n);\n\n// AI pages\nconst AIPage = React.lazy(() => import('./pages/ai/AIPage').then((m) => ({ default: m.AIPage })));\nconst AISessionDetailPage = React.lazy(() =>\n import('./pages/ai/AISessionDetailPage').then((m) => ({ default: m.AISessionDetailPage }))\n);\nconst AgentManagerPage = React.lazy(() =>\n import('./pages/ai/AgentManagerPage').then((m) => ({ default: m.AgentManagerPage }))\n);\nconst AIBookmarksPage = React.lazy(() =>\n import('./pages/ai/AIBookmarksPage').then((m) => ({ default: m.AIBookmarksPage }))\n);\nconst AIBuilderPage = React.lazy(() =>\n import('./pages/ai/AIBuilderPage').then((m) => ({ default: m.AIBuilderPage }))\n);\n\n// Loading fallback component\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[200px]\">\n <div className=\"animate-pulse bg-muted rounded w-full h-full\" />\n </div>\n );\n}\n\n/**\n * Internal router for the VibeControls microfrontend.\n * This component handles routing within the microfrontend using React Router v7.\n *\n * Routes:\n * - / → Overview page (dashboard)\n * - /agents → Agents management page\n * - /agents/:id → Agent details page\n * - /sessions → Sessions management page\n * - /sessions/:id → Session details page\n * - /vibes → Vibes (projects) management page\n * - /vibes/:id → Vibe details page (includes Notes tab)\n * - /settings → Settings page\n * - /webhooks → Legacy redirect to Developer Portal webhooks\n * - /health → Health monitoring page\n * - /* → Catch-all redirects to overview\n */\nfunction ScrollToTop() {\n const { pathname } = useLocation();\n useEffect(() => {\n // The scrollable container is the <main> element with overflow-auto\n // inside AppShellLayout, not the window itself.\n const main = document.querySelector('main');\n if (main) main.scrollTop = 0;\n }, [pathname]);\n return null;\n}\n\n/**\n * Legacy redirect: /ai-tools/<tab> → /ai-tools/ai/<tab>.\n *\n * Before the Plan / AI mode toggle the page was reachable directly at\n * /ai-tools/<tab>. New shape is /ai-tools/<mode>/<tab>. We keep the\n * legacy links working by mapping the captured `:tab` param: if it\n * resolves to a known AI sub-tab, redirect there; if it's literally\n * \"plan\", route to the Plan page; otherwise fall back to AI overview.\n */\nconst VALID_AI_TABS = new Set<string>([\n 'overview',\n 'templates',\n 'contexts',\n 'prompts',\n 'sessions',\n 'tasks',\n 'stats',\n 'playground',\n 'config',\n]);\n\nfunction LegacyAIToolsRedirect() {\n const { tab } = useParams<{ tab?: string }>();\n if (tab === 'plan') {\n return <Navigate to=\"/vibecontrols/ai-tools/plan\" replace />;\n }\n if (tab && VALID_AI_TABS.has(tab)) {\n return <Navigate to={`/vibecontrols/ai-tools/ai/${tab}`} replace />;\n }\n return <Navigate to=\"/vibecontrols/ai-tools/ai/overview\" replace />;\n}\n\n/**\n * Fallback shown when a feature flag is OFF.\n */\nfunction FeatureDisabledFallback() {\n const tr = useTr();\n return (\n <div className=\"flex items-center justify-center h-full min-h-[200px] text-text-secondary text-sm\">\n {tr(\n 'vibecontrols.pages.featureDisabled',\n 'This feature is currently disabled. You can enable it from Feature Previews.'\n )}\n </div>\n );\n}\n\n/**\n * Helper: wraps a page element in a FeatureGate.\n * defaultValue={true} means the feature shows by default (before flags are seeded).\n * Only hides when a flag is explicitly set to false.\n */\nfunction gated(flag: string, element: React.ReactNode, defaultValue = true): React.ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={defaultValue} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\nexport function VibeControlsRoutes() {\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <ScrollToTop />\n <Routes>\n {/* Default route - overview page */}\n <Route index element={gated('vibecontrols.overview', <OverviewPage />)} />\n {/* Legacy /dashboard alias → overview */}\n <Route path=\"dashboard\" element={<Navigate to=\"/vibecontrols\" replace />} />\n\n {/* Core pages */}\n {/* Plugins catalog — searchable grid of every published plugin. */}\n <Route path=\"plugins\" element={gated('vibecontrols.vibes', <PluginsCatalogPage />)} />\n\n <Route path=\"agents\" element={<AgentsLandingPage />} />\n <Route path=\"agents/new\" element={<Navigate to=\"../agents?create=true\" replace />} />\n <Route path=\"agents/:id\" element={<AgentDetailsPage />} />\n <Route path=\"sessions\" element={<SessionsPage />} />\n <Route path=\"vibes\" element={gated('vibecontrols.vibes', <VibesPage />)} />\n <Route path=\"vibes/new\" element={<Navigate to=\"../vibes?create=true\" replace />} />\n <Route path=\"vibes/:id\" element={gated('vibecontrols.vibes', <VibeDetailsPage />)} />\n <Route path=\"vibes/:id/docs/*\" element={gated('vibecontrols.vibes', <VibeDocsPage />)} />\n <Route path=\"notes/:id\" element={gated('vibecontrols.vibes', <NoteDetailsPage />)} />\n <Route path=\"sessions/:id\" element={<SessionDetailsPage />} />\n {/* Agent Manager — multi-session tabbed UI (replaces old AI page) */}\n <Route path=\"ai\" element={gated('vibecontrols.ai', <AgentManagerPage />)} />\n {/* Saved messages — listed BEFORE ai/:tab so \"bookmarks\" is not consumed as a tab. */}\n <Route path=\"ai/bookmarks\" element={gated('vibecontrols.ai', <AIBookmarksPage />)} />\n {/* AI Builder — describe → scaffold → test in sandbox → publish to catalog. */}\n <Route path=\"ai-builder\" element={gated('vibecontrols.ai', <AIBuilderPage />)} />\n <Route path=\"ai/:tab\" element={gated('vibecontrols.ai', <AgentManagerPage />)} />\n <Route\n path=\"ai/sessions/:sessionId\"\n element={gated('vibecontrols.ai', <AgentManagerPage />)}\n />\n {/* AI Tools — Plan / AI modes:\n /ai-tools → /ai-tools/ai/overview (redirect)\n /ai-tools/plan → Plan mode (plannotator iframe)\n /ai-tools/ai → /ai-tools/ai/overview (redirect)\n /ai-tools/ai/:tab → AI mode with active sub-tab\n Legacy /ai-tools/<tab> URLs from before the Plan mode rollout\n redirect into the new shape via LegacyAIToolsRedirect. */}\n <Route path=\"ai-tools\" element={<Navigate to=\"ai/overview\" replace />} />\n <Route path=\"ai-tools/plan\" element={gated('vibecontrols.ai', <AIPage />)} />\n <Route path=\"ai-tools/ai\" element={<Navigate to=\"overview\" replace />} />\n <Route path=\"ai-tools/ai/:tab\" element={gated('vibecontrols.ai', <AIPage />)} />\n <Route path=\"ai-tools/:tab\" element={<LegacyAIToolsRedirect />} />\n <Route\n path=\"ai-tools/sessions/:sessionId\"\n element={gated('vibecontrols.ai', <AISessionDetailPage />)}\n />\n\n {/* Targets list lives in the Targets & Agents landing; per-target\n detail is its own page (mirrors agents/:id). */}\n <Route path=\"targets\" element={<Navigate to=\"../agents?tab=targets\" replace />} />\n <Route path=\"targets/:id\" element={<TargetDetailsPage />} />\n {/* Agent Graph is also a tab inside the Targets & Agents landing. */}\n <Route path=\"agent-graph\" element={<Navigate to=\"../agents?tab=graph\" replace />} />\n\n {/* Catalog */}\n <Route path=\"catalogs\" element={gated('vibecontrols.catalogs', <CatalogPage />)} />\n <Route\n path=\"catalogs/:id\"\n element={gated('vibecontrols.catalogs', <CatalogDetailPage />)}\n />\n <Route\n path=\"catalogs/:id/docs/*\"\n element={gated('vibecontrols.catalogs', <CatalogDocsPage />)}\n />\n <Route path=\"components\" element={gated('vibecontrols.components', <ComponentsPage />)} />\n <Route\n path=\"environments\"\n element={gated('vibecontrols.components', <EnvTopologyPage />)}\n />\n <Route\n path=\"components/:id\"\n element={gated('vibecontrols.components', <ComponentDetailPage />)}\n />\n <Route\n path=\"components/:id/docs/*\"\n element={gated('vibecontrols.components', <ComponentDocsPage />)}\n />\n <Route path=\"templates\" element={gated('vibecontrols.templates', <TemplatesPage />)} />\n <Route\n path=\"templates/:id\"\n element={gated('vibecontrols.templates', <TemplateDetailPage />)}\n />\n\n {/* Automation */}\n <Route path=\"actions\" element={gated('vibecontrols.actions', <ActionsPage />)} />\n <Route path=\"vibedeck\" element={gated('vibecontrols.vibedeck', <VibeDeckPage />)} />\n <Route path=\"calendar\" element={gated('vibecontrols.calendar', <VibeCalendarPage />)} />\n <Route\n path=\"calendar/:viewType\"\n element={gated('vibecontrols.calendar', <VibeCalendarPage />)}\n />\n <Route\n path=\"calendar/task/:taskId\"\n element={gated('vibecontrols.calendar', <VibeTaskDetailPage />)}\n />\n {/* ScheduledJob (wspace-scheduler-svc) — separate id space\n from VibeCalendarTask above, so it needs its own route. */}\n <Route\n path=\"calendar/scheduled-job/:jobId\"\n element={gated('vibecontrols.calendar', <ScheduledJobDetailPage />)}\n />\n <Route path=\"webhooks\" element={gated('vibecontrols.webhooks', <WebhooksPage />)} />\n <Route path=\"webhooks/new\" element={gated('vibecontrols.webhooks', <WebhooksPage />)} />\n <Route path=\"webhooks/:id\" element={gated('vibecontrols.webhooks', <WebhooksPage />)} />\n {/* GitOps consolidated into Vibe detail tab — old /gitops routes redirect to vibes list. */}\n <Route path=\"gitops\" element={<Navigate to=\"../vibes\" replace />} />\n <Route path=\"gitops/:tab\" element={<Navigate to=\"../vibes\" replace />} />\n\n {/* Infrastructure */}\n <Route path=\"tunnels\" element={gated('vibecontrols.tunnels', <TunnelsLandingPage />)} />\n <Route path=\"tunnels/new\" element={gated('vibecontrols.tunnels', <TunnelCreateWizard />)} />\n <Route\n path=\"tunnels/:tunnelId\"\n element={gated('vibecontrols.tunnels', <TunnelDetailsPage />)}\n />\n\n {/* Insights */}\n <Route path=\"health\" element={gated('vibecontrols.health', <HealthPage />)} />\n <Route path=\"analytics\" element={gated('vibecontrols.analytics', <AnalyticsPage />)} />\n <Route path=\"audit\" element={gated('vibecontrols.audit-log', <VibeAuditPage />, false)} />\n <Route path=\"logs\" element={<LogsPage />} />\n\n {/* Settings */}\n <Route path=\"settings\" element={gated('vibecontrols.settings', <SettingsPage />)} />\n\n {/* Setup */}\n {/* Not gated — onboarding flow must always be reachable */}\n <Route path=\"getting-started\" element={<GettingStartedPage />} />\n\n {/* Shared public routes (no gating — accessible via public links) */}\n <Route path=\"share/:token\" element={<SharedSessionPage />} />\n <Route path=\"deck/:token\" element={<SharedVibeDeckPage />} />\n\n {/* Legacy redirects */}\n <Route path=\"access\" element={<Navigate to=\"../agents?tab=targets\" replace />} />\n <Route path=\"ssh-targets\" element={<Navigate to=\"../agents?tab=targets\" replace />} />\n <Route path=\"ssh-terminals\" element={<Navigate to=\"../agents?tab=targets\" replace />} />\n\n {/* Catch-all route - redirect to overview */}\n <Route path=\"*\" element={<VibeNotFound />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
@@ -48,7 +48,7 @@ var v = ({ agent: v, onSelect: y, onConfigure: b, onSettings: x, onStart: S, onS
48
48
  }), /* @__PURE__ */ g("div", {
49
49
  role: "button",
50
50
  tabIndex: 0,
51
- className: `bg-bg-surface border rounded-card shadow-elevation-1 p-5 h-full hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas ${E ? "border-transparent ring-1 ring-[var(--color-focus-ring)] bg-[var(--color-accent-soft)]" : "border-border-subtle"} ${O}`,
51
+ className: `bg-bg-surface border rounded-card shadow-elevation-1 p-5 h-full hover:border-border-strong hover:shadow-elevation-2 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas ${E ? "border-transparent ring-1 ring-[var(--color-focus-ring)] bg-[var(--color-accent-soft)]" : "border-border-subtle"} ${O}`,
52
52
  onClick: () => y?.(v),
53
53
  onKeyDown: (e) => {
54
54
  e.target === e.currentTarget && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), y?.(v));
@@ -1 +1 @@
1
- {"version":3,"file":"AgentCard.js","names":[],"sources":["../../../src/components/agents/AgentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { Server, Clock, Cpu, Play, Square, Settings, Globe, Link, RefreshCw } from 'lucide-react';\nimport { Checkbox } from '@burdenoff/fe-libs/ui';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport type { Agent } from './types';\nimport { getAgentStatus } from './agentStatus';\nimport { classifyAgentHealth } from '../../utils/agentHealth';\nimport { ActionMenu, type ActionMenuItem } from '../shared/ActionMenu';\nimport { PlatformIcon } from '../shared/PlatformIcon';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface AgentCardProps {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onConfigure?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onEditTunnelUrl?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n className?: string;\n /**\n * Last error from /health/ready or the live probe — shown in the badge\n * tooltip when the agent is degraded so users know WHY without curling\n * the tunnel themselves.\n */\n healthErrorMessage?: string | null;\n}\n\n/**\n * AgentCard - Display agent information in a card format\n *\n * @example\n * ```tsx\n * <AgentCard\n * agent={myAgent}\n * onSelect={handleSelect}\n * onConfigure={handleConfigure}\n * />\n * ```\n */\nexport const AgentCard: FC<AgentCardProps> = ({\n agent,\n onSelect,\n onConfigure,\n onSettings,\n onStart,\n onStop,\n onDisconnect,\n onEditTunnelUrl,\n selected = false,\n onToggleSelect,\n className = '',\n healthErrorMessage,\n}) => {\n const tr = useTr();\n // Support both onConfigure and onSettings\n const handleConfigure = onConfigure || onSettings;\n\n const status = getAgentStatus(agent);\n\n const formatLastHeartbeat = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('agentsPage.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('agentsPage.justNow', 'Just now');\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return `${Math.floor(diff / 86400000)}d ago`;\n };\n\n const menuItems: ActionMenuItem[] = [];\n if (status === 'ACTIVE' && onStop) {\n menuItems.push({\n key: 'stop',\n label: tr('agentsPage.stopAgent', 'Stop Agent'),\n icon: Square,\n onSelect: () => onStop(agent),\n });\n }\n if ((status === 'STOPPED' || status === 'OFFLINE') && onStart) {\n menuItems.push({\n key: 'start',\n label: tr('agentsPage.startAgent', 'Start Agent'),\n icon: Play,\n onSelect: () => onStart(agent),\n });\n }\n if (status === 'DEGRADED' && onStart) {\n // Reconnect on a degraded agent fires the same startVibecontrolsAgent\n // mutation as a fresh start — svc-side that POSTs lifecycle/start to the\n // agent's tunnel, which usually clears transient failures (tunnel hiccup,\n // finalize retry). Labelled \"Reconnect\" not \"Start\" so the user knows\n // it's a recovery action, not creating a new instance.\n menuItems.push({\n key: 'reconnect',\n label: tr('agentsPage.reconnectAgent', 'Reconnect'),\n icon: RefreshCw,\n onSelect: () => onStart(agent),\n });\n }\n if (\n (status === 'ACTIVE' ||\n status === 'STOPPED' ||\n status === 'OFFLINE' ||\n status === 'DEGRADED') &&\n onDisconnect\n ) {\n menuItems.push({\n key: 'disconnect',\n label:\n status === 'OFFLINE'\n ? tr('agentsPage.clearConnection', 'Clear Connection')\n : tr('agentsPage.disconnect', 'Disconnect'),\n icon: Square,\n onSelect: () => onDisconnect(agent),\n variant: 'danger',\n });\n }\n if (onEditTunnelUrl) {\n menuItems.push({\n key: 'edit-tunnel',\n label: tr('agentsPage.editTunnel', 'Edit Tunnel'),\n icon: Link,\n onSelect: () => onEditTunnelUrl(agent),\n });\n }\n if (handleConfigure) {\n menuItems.push({\n key: 'settings',\n label: tr('agentsPage.settings', 'Settings'),\n icon: Settings,\n onSelect: () => handleConfigure(agent),\n });\n }\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={`bg-bg-surface border rounded-card shadow-elevation-1 p-5 h-full hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas ${\n selected\n ? 'border-transparent ring-1 ring-[var(--color-focus-ring)] bg-[var(--color-accent-soft)]'\n : 'border-border-subtle'\n } ${className}`}\n onClick={() => onSelect?.(agent)}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return; // children (menu, checkbox, links) handle their own keys\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onSelect?.(agent);\n }\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {/* Selection Checkbox */}\n {onToggleSelect && (\n <span\n className=\"flex flex-shrink-0 items-center justify-center max-md:min-h-11 max-md:min-w-11 max-md:-m-2 md:mt-0.5\"\n onClick={(e) => {\n e.stopPropagation();\n if (e.target === e.currentTarget) onToggleSelect();\n }}\n >\n <Checkbox\n checked={selected}\n onCheckedChange={() => onToggleSelect()}\n onClick={(e) => e.stopPropagation()}\n aria-label={tr('agentsPage.selectAgent', 'Select {{name}}', { name: agent.name })}\n />\n </span>\n )}\n <div className=\"flex-shrink-0 size-9 sm:size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Server className=\"size-4 sm:size-5 text-action-primary-bg\" aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate min-w-0 text-sm sm:text-base tracking-tight\">\n {agent.name}\n </h3>\n <PlatformIcon\n platform={agent.platform}\n className=\"size-4 flex-shrink-0 text-text-secondary\"\n />\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary truncate\">\n {agent.hostname || `ID: ${agent.id}`}\n </p>\n {(() => {\n // Always-visible (not just hover) guidance when degraded, so the\n // user knows whether it's self-healing or needs their action.\n const health = classifyAgentHealth(status, healthErrorMessage ?? null);\n if (health.phase === 'none') return null;\n return (\n <p\n className={`mt-1 flex items-start gap-1 text-xs ${\n health.phase === 'attention'\n ? 'text-status-warning-text'\n : 'text-content-tertiary'\n }`}\n >\n {health.inProgress && (\n <RefreshCw\n className=\"size-3 mt-0.5 flex-shrink-0 animate-spin\"\n aria-hidden=\"true\"\n />\n )}\n <span>\n <span className=\"font-medium\">{health.label}.</span> {health.actionHint}\n </span>\n </p>\n );\n })()}\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <AgentStatusBadge\n status={status}\n reason={healthErrorMessage ?? null}\n linkTo={`/vibecontrols/agents/${agent.id}?tab=connection`}\n onClick={(event) => {\n // The card itself has a `onSelect` click target that\n // routes to the agent detail. Stop propagation so a\n // badge click doesn't *also* fire that handler — the\n // anchor's href already routes to the right tab and\n // React Router picks it up via the surrounding shell.\n event.stopPropagation();\n }}\n />\n {(handleConfigure || onStart || onStop) && menuItems.length > 0 && (\n <ActionMenu\n ariaLabel={tr('agentsPage.agentActionsMenu', 'Agent actions menu')}\n title={tr('agentsPage.agentActions', 'Agent actions')}\n align=\"end\"\n items={menuItems}\n />\n )}\n </div>\n </div>\n\n <div className=\"mt-5 grid grid-cols-2 gap-3 text-sm\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"size-4\" aria-hidden=\"true\" />\n <span className=\"tabular-nums\">{formatLastHeartbeat(agent.lastHeartbeat)}</span>\n </div>\n {agent.architecture && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Cpu className=\"size-4\" aria-hidden=\"true\" />\n <span>{agent.architecture}</span>\n </div>\n )}\n </div>\n\n {agent.tunnelUrl && (\n // Plain \"Tunnel: <link>\" row. The colored status chip that used\n // to sit here was a redundant second status surface — the\n // canonical state lives in the <AgentStatusBadge> top-right.\n // The Globe glyph stays as a neutral icon (no status color).\n <div className=\"mt-4 flex items-center gap-2 text-sm\">\n <Globe className=\"size-4 flex-shrink-0 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"text-text-secondary flex-shrink-0\">Tunnel:</span>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n aria-label={tr('agentsPage.tunnelOpensInNewTab', '{{url}} (opens in new tab)', {\n url: agent.tunnelUrl,\n })}\n className=\"min-w-0 truncate font-mono text-xs text-text-link hover:text-text-linkHover hover:underline\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n )}\n\n {agent.capabilities && agent.capabilities.length > 0 && (\n <div className=\"mt-4 flex flex-wrap gap-1.5\">\n {agent.capabilities.slice(0, 4).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-full\"\n >\n {cap}\n </span>\n ))}\n {agent.capabilities.length > 4 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-full\">\n +{agent.capabilities.length - 4}\n </span>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentCard.js","names":[],"sources":["../../../src/components/agents/AgentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { Server, Clock, Cpu, Play, Square, Settings, Globe, Link, RefreshCw } from 'lucide-react';\nimport { Checkbox } from '@burdenoff/fe-libs/ui';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport type { Agent } from './types';\nimport { getAgentStatus } from './agentStatus';\nimport { classifyAgentHealth } from '../../utils/agentHealth';\nimport { ActionMenu, type ActionMenuItem } from '../shared/ActionMenu';\nimport { PlatformIcon } from '../shared/PlatformIcon';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface AgentCardProps {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onConfigure?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onEditTunnelUrl?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n className?: string;\n /**\n * Last error from /health/ready or the live probe — shown in the badge\n * tooltip when the agent is degraded so users know WHY without curling\n * the tunnel themselves.\n */\n healthErrorMessage?: string | null;\n}\n\n/**\n * AgentCard - Display agent information in a card format\n *\n * @example\n * ```tsx\n * <AgentCard\n * agent={myAgent}\n * onSelect={handleSelect}\n * onConfigure={handleConfigure}\n * />\n * ```\n */\nexport const AgentCard: FC<AgentCardProps> = ({\n agent,\n onSelect,\n onConfigure,\n onSettings,\n onStart,\n onStop,\n onDisconnect,\n onEditTunnelUrl,\n selected = false,\n onToggleSelect,\n className = '',\n healthErrorMessage,\n}) => {\n const tr = useTr();\n // Support both onConfigure and onSettings\n const handleConfigure = onConfigure || onSettings;\n\n const status = getAgentStatus(agent);\n\n const formatLastHeartbeat = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('agentsPage.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('agentsPage.justNow', 'Just now');\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return `${Math.floor(diff / 86400000)}d ago`;\n };\n\n const menuItems: ActionMenuItem[] = [];\n if (status === 'ACTIVE' && onStop) {\n menuItems.push({\n key: 'stop',\n label: tr('agentsPage.stopAgent', 'Stop Agent'),\n icon: Square,\n onSelect: () => onStop(agent),\n });\n }\n if ((status === 'STOPPED' || status === 'OFFLINE') && onStart) {\n menuItems.push({\n key: 'start',\n label: tr('agentsPage.startAgent', 'Start Agent'),\n icon: Play,\n onSelect: () => onStart(agent),\n });\n }\n if (status === 'DEGRADED' && onStart) {\n // Reconnect on a degraded agent fires the same startVibecontrolsAgent\n // mutation as a fresh start — svc-side that POSTs lifecycle/start to the\n // agent's tunnel, which usually clears transient failures (tunnel hiccup,\n // finalize retry). Labelled \"Reconnect\" not \"Start\" so the user knows\n // it's a recovery action, not creating a new instance.\n menuItems.push({\n key: 'reconnect',\n label: tr('agentsPage.reconnectAgent', 'Reconnect'),\n icon: RefreshCw,\n onSelect: () => onStart(agent),\n });\n }\n if (\n (status === 'ACTIVE' ||\n status === 'STOPPED' ||\n status === 'OFFLINE' ||\n status === 'DEGRADED') &&\n onDisconnect\n ) {\n menuItems.push({\n key: 'disconnect',\n label:\n status === 'OFFLINE'\n ? tr('agentsPage.clearConnection', 'Clear Connection')\n : tr('agentsPage.disconnect', 'Disconnect'),\n icon: Square,\n onSelect: () => onDisconnect(agent),\n variant: 'danger',\n });\n }\n if (onEditTunnelUrl) {\n menuItems.push({\n key: 'edit-tunnel',\n label: tr('agentsPage.editTunnel', 'Edit Tunnel'),\n icon: Link,\n onSelect: () => onEditTunnelUrl(agent),\n });\n }\n if (handleConfigure) {\n menuItems.push({\n key: 'settings',\n label: tr('agentsPage.settings', 'Settings'),\n icon: Settings,\n onSelect: () => handleConfigure(agent),\n });\n }\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={`bg-bg-surface border rounded-card shadow-elevation-1 p-5 h-full hover:border-border-strong hover:shadow-elevation-2 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas ${\n selected\n ? 'border-transparent ring-1 ring-[var(--color-focus-ring)] bg-[var(--color-accent-soft)]'\n : 'border-border-subtle'\n } ${className}`}\n onClick={() => onSelect?.(agent)}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return; // children (menu, checkbox, links) handle their own keys\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onSelect?.(agent);\n }\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {/* Selection Checkbox */}\n {onToggleSelect && (\n <span\n className=\"flex flex-shrink-0 items-center justify-center max-md:min-h-11 max-md:min-w-11 max-md:-m-2 md:mt-0.5\"\n onClick={(e) => {\n e.stopPropagation();\n if (e.target === e.currentTarget) onToggleSelect();\n }}\n >\n <Checkbox\n checked={selected}\n onCheckedChange={() => onToggleSelect()}\n onClick={(e) => e.stopPropagation()}\n aria-label={tr('agentsPage.selectAgent', 'Select {{name}}', { name: agent.name })}\n />\n </span>\n )}\n <div className=\"flex-shrink-0 size-9 sm:size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Server className=\"size-4 sm:size-5 text-action-primary-bg\" aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate min-w-0 text-sm sm:text-base tracking-tight\">\n {agent.name}\n </h3>\n <PlatformIcon\n platform={agent.platform}\n className=\"size-4 flex-shrink-0 text-text-secondary\"\n />\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary truncate\">\n {agent.hostname || `ID: ${agent.id}`}\n </p>\n {(() => {\n // Always-visible (not just hover) guidance when degraded, so the\n // user knows whether it's self-healing or needs their action.\n const health = classifyAgentHealth(status, healthErrorMessage ?? null);\n if (health.phase === 'none') return null;\n return (\n <p\n className={`mt-1 flex items-start gap-1 text-xs ${\n health.phase === 'attention'\n ? 'text-status-warning-text'\n : 'text-content-tertiary'\n }`}\n >\n {health.inProgress && (\n <RefreshCw\n className=\"size-3 mt-0.5 flex-shrink-0 animate-spin\"\n aria-hidden=\"true\"\n />\n )}\n <span>\n <span className=\"font-medium\">{health.label}.</span> {health.actionHint}\n </span>\n </p>\n );\n })()}\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <AgentStatusBadge\n status={status}\n reason={healthErrorMessage ?? null}\n linkTo={`/vibecontrols/agents/${agent.id}?tab=connection`}\n onClick={(event) => {\n // The card itself has a `onSelect` click target that\n // routes to the agent detail. Stop propagation so a\n // badge click doesn't *also* fire that handler — the\n // anchor's href already routes to the right tab and\n // React Router picks it up via the surrounding shell.\n event.stopPropagation();\n }}\n />\n {(handleConfigure || onStart || onStop) && menuItems.length > 0 && (\n <ActionMenu\n ariaLabel={tr('agentsPage.agentActionsMenu', 'Agent actions menu')}\n title={tr('agentsPage.agentActions', 'Agent actions')}\n align=\"end\"\n items={menuItems}\n />\n )}\n </div>\n </div>\n\n <div className=\"mt-5 grid grid-cols-2 gap-3 text-sm\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"size-4\" aria-hidden=\"true\" />\n <span className=\"tabular-nums\">{formatLastHeartbeat(agent.lastHeartbeat)}</span>\n </div>\n {agent.architecture && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Cpu className=\"size-4\" aria-hidden=\"true\" />\n <span>{agent.architecture}</span>\n </div>\n )}\n </div>\n\n {agent.tunnelUrl && (\n // Plain \"Tunnel: <link>\" row. The colored status chip that used\n // to sit here was a redundant second status surface — the\n // canonical state lives in the <AgentStatusBadge> top-right.\n // The Globe glyph stays as a neutral icon (no status color).\n <div className=\"mt-4 flex items-center gap-2 text-sm\">\n <Globe className=\"size-4 flex-shrink-0 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"text-text-secondary flex-shrink-0\">Tunnel:</span>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n aria-label={tr('agentsPage.tunnelOpensInNewTab', '{{url}} (opens in new tab)', {\n url: agent.tunnelUrl,\n })}\n className=\"min-w-0 truncate font-mono text-xs text-text-link hover:text-text-linkHover hover:underline\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n )}\n\n {agent.capabilities && agent.capabilities.length > 0 && (\n <div className=\"mt-4 flex flex-wrap gap-1.5\">\n {agent.capabilities.slice(0, 4).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-full\"\n >\n {cap}\n </span>\n ))}\n {agent.capabilities.length > 4 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-full\">\n +{agent.capabilities.length - 4}\n </span>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -196,8 +196,8 @@ function Oe({ onSubmit: Oe, onCancel: h, isLoading: g, onNavigate: ke, targets:
196
196
  children: /* @__PURE__ */ d("div", {
197
197
  className: "space-y-2",
198
198
  children: [[{
199
- label: y("agentsPage.installStep1", "1. Install the agent CLI"),
200
- cmd: "curl -fsSL https://app.vibecontrols.com/install | bash && hash -r 2>/dev/null"
199
+ label: y("agentsPage.installStep1", "1. Install the agent globally"),
200
+ cmd: "bun install -g @vibecontrols/agent"
201
201
  }, {
202
202
  label: y("agentsPage.installStep2", "2. Start the agent (prints tunnel URL + API key)"),
203
203
  cmd: "vibe start"
@@ -1 +1 @@
1
- {"version":3,"file":"AgentForm.js","names":[],"sources":["../../../src/components/agents/AgentForm.tsx"],"sourcesContent":["import { useEffect, useRef, useState, useCallback } from 'react';\nimport type { ReactNode } from 'react';\nimport {\n Eye,\n EyeOff,\n CheckCircle2,\n XCircle,\n Loader2,\n ChevronRight,\n ChevronLeft,\n ChevronDown,\n Copy,\n Check,\n Terminal,\n Download,\n RefreshCw,\n Plus,\n Settings2,\n} from 'lucide-react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Stepper,\n} from '@burdenoff/fe-libs/ui';\nimport { useProbeAgentTunnelLazyQuery } from '@/generated/wspace-operations';\nimport { SearchableSelect, MotionTabPanel } from '@/components/shared';\nimport type { SearchableSelectOption } from '@/components/shared';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useField } from '../../shared/hooks/useField';\nimport { isQuotaExhaustedError, wasQuotaHandledLocally } from '@/utils/quotaUtils';\n\ntype WizardStage = 'details' | 'validate';\n\ninterface TargetOption {\n id: string;\n name: string;\n type: string;\n status: string;\n}\n\ninterface AgentFormProps {\n onSubmit: (data: Record<string, unknown>) => Promise<{ id: string } | void>;\n onCancel: () => void;\n isLoading?: boolean;\n /** Navigate to a path (used to go to agent details after creation) */\n onNavigate?: (path: string) => void;\n /** Available targets for the agent to be installed on */\n targets?: TargetOption[];\n /** Whether targets are still loading */\n targetsLoading?: boolean;\n /**\n * Create a new target inline. When provided, the form shows a\n * \"+ Create new target\" affordance that lets users create a DIRECT\n * target on the fly without leaving the dialog. Must return the new\n * target so the form can auto-select it.\n */\n onCreateTarget?: (name: string) => Promise<TargetOption | null>;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the configure-stage actions. */\n extraSection?: ReactNode;\n}\n\nexport function AgentForm({\n onSubmit,\n onCancel,\n isLoading,\n onNavigate,\n targets,\n targetsLoading,\n onCreateTarget,\n extraSection,\n}: AgentFormProps) {\n const tr = useTr();\n const [stage, setStage] = useState<WizardStage>('details');\n const [showApiKey, setShowApiKey] = useState(false);\n const [showInstallInstructions, setShowInstallInstructions] = useState(false);\n const [copiedCommand, setCopiedCommand] = useState<string | null>(null);\n\n // Stage 1: Basic details\n const [name, setName] = useState('');\n // Tracks whether the operator has typed into the name field. Once true,\n // we stop overwriting the value from auto-fill sources (target select +\n // identity probe) so we never clobber their input.\n const [nameEdited, setNameEdited] = useState(false);\n const [tunnelUrl, setTunnelUrl] = useState('');\n const [agentApiKey, setAgentApiKey] = useState('');\n const [selectedTargetId, setSelectedTargetId] = useState('');\n\n // When the operator picks a target, autofill the agent name with the\n // target's display name. Skip if they've already typed something into\n // the name field. The svc append a `-2`/`-3` suffix on collision so\n // it's safe to suggest a duplicate; the actual stored name comes back\n // on the create response.\n useEffect(() => {\n if (nameEdited) return;\n if (!selectedTargetId) return;\n const picked = (targets ?? []).find((t) => t.id === selectedTargetId);\n if (picked?.name) setName(picked.name);\n }, [selectedTargetId, targets, nameEdited]);\n\n // Inline target creation state\n const [showInlineTarget, setShowInlineTarget] = useState(false);\n const [inlineTargetName, setInlineTargetName] = useState('');\n const [creatingTarget, setCreatingTarget] = useState(false);\n const [inlineTargetError, setInlineTargetError] = useState<string | null>(null);\n\n // Stage 2: Validation (via backend-proxied GraphQL query)\n const [validating, setValidating] = useState(false);\n const [healthResult, setHealthResult] = useState<{\n ok: boolean;\n data?: { status: string; version: string; uptime: number };\n error?: string;\n } | null>(null);\n const [identityResult, setIdentityResult] = useState<{\n ok: boolean;\n data?: { hostname: string; platform: string; arch: string };\n error?: string;\n } | null>(null);\n\n // Auto-detected fields (editable overrides)\n const [detectedHostname, setDetectedHostname] = useState('');\n const [detectedPlatform, setDetectedPlatform] = useState('linux');\n const [detectedArch, setDetectedArch] = useState('x86_64');\n const [detectedVersion, setDetectedVersion] = useState('1.0.0');\n\n // Error state for creation failures\n const [createError, setCreateError] = useState<string | null>(null);\n\n // Advanced options (Phase 5B: per-daemon profile name).\n // Defaults to \"default\" (autofilled but editable). Disclosure is closed\n // by default — users only need to touch this for multi-profile setups.\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [profile, setProfile] = useState('default');\n\n // Accessible label/hint/error wiring for every form control. The hook\n // only checks truthiness of `hint` to chain aria-describedby to the\n // always-visible hint <p id={field.hintId}> below each input.\n const HAS_HINT = 'visible';\n const nameField = useField({ hint: HAS_HINT });\n const inlineTargetField = useField({ error: inlineTargetError });\n const tunnelField = useField({ hint: HAS_HINT });\n const apiKeyField = useField({ hint: HAS_HINT });\n const profileField = useField({ hint: HAS_HINT });\n const platformField = useField();\n const archField = useField();\n\n const copyToClipboard = useCallback((text: string, id: string) => {\n navigator.clipboard.writeText(text);\n setCopiedCommand(id);\n setTimeout(() => setCopiedCommand(null), 2000);\n }, []);\n\n const normalizeUrl = (url: string) => {\n let u = url.trim();\n if (u.endsWith('/')) u = u.slice(0, -1);\n return u;\n };\n\n const [runProbe] = useProbeAgentTunnelLazyQuery({ fetchPolicy: 'network-only' });\n\n // Validate the agent through the svc-proxied `probeAgentTunnel` query.\n // svc fans out to /health + /api/profiles/default/agent/identity over\n // its own egress and returns a combined AgentProbeResult — the\n // operator's API key never touches the browser→tunnel path.\n const validateAgent = async () => {\n const baseUrl = normalizeUrl(tunnelUrl);\n if (!baseUrl) return;\n\n setValidating(true);\n setHealthResult(null);\n setIdentityResult(null);\n\n try {\n const { data, error } = await runProbe({\n variables: { url: baseUrl, agentApiKey: agentApiKey || null },\n });\n const probe = data?.probeAgentTunnel;\n if (error || !probe) {\n const msg = error?.message ?? 'Probe failed';\n setHealthResult({ ok: false, error: msg });\n setIdentityResult({ ok: false, error: msg });\n } else {\n setHealthResult(\n probe.healthOk\n ? {\n ok: true,\n data: {\n status: probe.healthStatus ?? 'healthy',\n version: probe.agentVersion ?? 'unknown',\n uptime: probe.uptime ?? 0,\n },\n }\n : { ok: false, error: probe.healthError ?? 'Health check failed' }\n );\n if (probe.agentVersion) setDetectedVersion(probe.agentVersion);\n\n setIdentityResult(\n probe.identityOk\n ? {\n ok: true,\n data: {\n hostname: probe.hostname ?? '',\n platform: probe.platform ?? 'linux',\n arch: probe.architecture ?? 'x86_64',\n },\n }\n : { ok: false, error: probe.identityError ?? 'Identity check failed' }\n );\n if (probe.identityOk) {\n if (probe.platform) setDetectedPlatform(probe.platform);\n if (probe.architecture) setDetectedArch(probe.architecture);\n // Auto-fill name from probe hostname unless (a) the operator\n // has manually typed one, or (b) a target is already selected\n // (target wins as the canonical autogen value).\n if (probe.hostname) {\n setDetectedHostname(probe.hostname);\n if (!nameEdited && !selectedTargetId) setName(probe.hostname);\n }\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'Probe failed';\n setHealthResult({ ok: false, error: msg });\n setIdentityResult({ ok: false, error: msg });\n }\n\n setValidating(false);\n };\n\n // Refs let us short-circuit double-submits before React re-renders\n // the button as disabled. The parent passes `isLoading` (driven by\n // `useState`) and we wire it to the button's `disabled` prop, but\n // `setState` is asynchronous — between the user's first click and\n // React's next commit there is a microtask window in which the button\n // is still clickable. Without this ref a fast double-click fires\n // `addAgentFromTunnel` twice in parallel; the svc sees one in-flight\n // and one fresh call, the second one wins the (workspaceId, name)\n // unique-resolution race, and the workspace ends up with two agent\n // rows (the original autogen + the operator's custom name).\n const submittingRef = useRef(false);\n\n // Create the agent and close the modal\n const handleCreateAgent = async () => {\n if (submittingRef.current) return;\n submittingRef.current = true;\n setCreateError(null);\n\n // Pipe the chosen profile through `metadata.profile`. Phase 5A (svc) is\n // adding first-class AgentProfile rows; in the meantime the backend\n // also reads `metadata.profile` so the create path keeps working\n // regardless of which side of the cut-over the cluster is on.\n const trimmedProfile = profile.trim() || 'default';\n const data: Record<string, unknown> = {\n name: name || detectedHostname || 'Unnamed Agent',\n hostname: detectedHostname || 'unknown',\n platform: detectedPlatform,\n architecture: detectedArch,\n version: detectedVersion,\n tunnelUrl: normalizeUrl(tunnelUrl),\n tunnelStatus: 'ACTIVE',\n agentApiKey,\n metadata: { profile: trimmedProfile },\n ...(selectedTargetId ? { targetId: selectedTargetId } : {}),\n };\n\n try {\n const result = await onSubmit(data);\n const newAgentId = result?.id || null;\n\n if (!newAgentId) {\n // Mutation succeeded but returned no agent ID — treat as failure\n setCreateError('Agent creation returned no ID. The agent may already exist.');\n return;\n }\n\n // Close the modal — the agent list will refetch automatically\n if (onNavigate) {\n onNavigate(`/agents/${newAgentId}`);\n } else {\n onCancel();\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to create agent';\n setCreateError(message);\n } finally {\n submittingRef.current = false;\n }\n };\n\n const isValidationPassed = healthResult?.ok && identityResult?.ok;\n const hasTargets = targets && targets.length > 0;\n // The user must either pick or inline-create a target if inline creation\n // is enabled; otherwise we only require it when targets already exist.\n const targetRequiredButMissing = onCreateTarget\n ? selectedTargetId.length === 0\n : hasTargets && selectedTargetId.length === 0;\n const canProceedToValidate = tunnelUrl.trim().length > 0 && !targetRequiredButMissing;\n\n const handleInlineCreateTarget = useCallback(async () => {\n if (!onCreateTarget || !inlineTargetName.trim()) return;\n setCreatingTarget(true);\n setInlineTargetError(null);\n try {\n const created = await onCreateTarget(inlineTargetName.trim());\n if (created) {\n setSelectedTargetId(created.id);\n setShowInlineTarget(false);\n setInlineTargetName('');\n } else if (wasQuotaHandledLocally()) {\n // Parent surfaced the quota modal — collapse the inline row\n // quietly so the user isn't left with a vague \"try again\"\n // message under an explicit upgrade-your-plan dialog.\n setShowInlineTarget(false);\n setInlineTargetName('');\n } else {\n setInlineTargetError(\n tr('agentsPage.inlineTargetFailed', 'Failed to create target. Please try again.')\n );\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n // Quota dialog will (or already has) opened at the parent;\n // suppress the generic inline error so the two don't compete.\n setShowInlineTarget(false);\n setInlineTargetName('');\n } else {\n setInlineTargetError(err instanceof Error ? err.message : 'Failed to create target');\n }\n } finally {\n setCreatingTarget(false);\n }\n }, [onCreateTarget, inlineTargetName, tr]);\n\n const renderCopyButton = (text: string, id: string) => {\n const copied = copiedCommand === id;\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => copyToClipboard(text, id)}\n aria-label={copied ? tr('agentsPage.copied', 'Copied') : tr('agentsPage.copy', 'Copy')}\n className=\"shrink-0\"\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-text\" aria-hidden=\"true\" />\n ) : (\n <Copy className=\"size-3.5 text-text-secondary\" aria-hidden=\"true\" />\n )}\n </Button>\n );\n };\n\n const requiredMark = (\n <span aria-hidden=\"true\" className=\"text-status-error-text\">\n {' '}\n *\n </span>\n );\n\n // Stepper\n const steps = [\n { key: 'details', label: tr('agentsPage.stepSetup', 'Setup') },\n { key: 'validate', label: tr('agentsPage.stepValidate', 'Validate') },\n ];\n\n const currentStepIndex = steps.findIndex((s) => s.key === stage);\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !isLoading) onCancel();\n }}\n >\n {/* Multi-field data-entry dialog: a stray click on the scrim must not\n discard typed input. Escape and the explicit Cancel/X still close. */}\n <ResponsiveDialogContent\n className=\"sm:max-w-xl md:max-h-[min(90vh,52rem)] md:overflow-y-auto\"\n onPointerDownOutside={(event) => event.preventDefault()}\n >\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('agentsPage.addNewAgent', 'Add New Agent')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'agentsPage.addNewAgentDescription',\n 'Connect a running vibe agent to this workspace.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <Stepper\n steps={steps}\n active={currentStepIndex}\n onStepClick={(index) => {\n const target = steps[index];\n if (target) setStage(target.key === 'validate' ? 'validate' : 'details');\n }}\n />\n\n <MotionTabPanel tabKey={stage}>\n {stage === 'details' ? (\n <div className=\"space-y-4\">\n {/* Install instructions (collapsible) */}\n <div className=\"border border-border-default rounded-lg overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowInstallInstructions(!showInstallInstructions)}\n aria-expanded={showInstallInstructions}\n className=\"w-full flex items-center justify-between px-4 py-3 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"flex items-center gap-2\">\n <Download className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.installInstructions', 'Agent Install Instructions')}\n </span>\n </div>\n <ChevronRight\n aria-hidden=\"true\"\n className={`size-4 text-text-secondary transition-transform ${showInstallInstructions ? 'rotate-90' : ''}`}\n />\n </button>\n {showInstallInstructions && (\n <div className=\"px-4 py-3 space-y-3 border-t border-border-default\">\n <div className=\"space-y-2\">\n {[\n {\n label: tr('agentsPage.installStep1', '1. Install the agent CLI'),\n // `&& hash -r` refreshes the shell so the new `vibe`\n // resolves immediately (no manual hash -r needed).\n cmd: 'curl -fsSL https://app.vibecontrols.com/install | bash && hash -r 2>/dev/null',\n },\n {\n label: tr(\n 'agentsPage.installStep2',\n '2. Start the agent (prints tunnel URL + API key)'\n ),\n cmd: 'vibe start',\n },\n ].map((step) => (\n <div key={step.cmd}>\n <p className=\"text-xs text-text-secondary mb-1\">{step.label}</p>\n <div className=\"flex items-center gap-2 bg-bg-sunken rounded-md px-3 py-1.5 font-mono text-xs text-text-primary\">\n <Terminal\n className=\"size-3 text-text-muted flex-shrink-0\"\n aria-hidden=\"true\"\n />\n <code className=\"flex-1 min-w-0 break-words\">{step.cmd}</code>\n {renderCopyButton(step.cmd, step.cmd)}\n </div>\n </div>\n ))}\n <p className=\"text-xs text-text-secondary pt-1\">\n {tr(\n 'agentsPage.installHint',\n 'After running vibe start, copy the printed tunnel URL and API key into the fields above.'\n )}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {/* Agent Name — visible upfront so operators see (and can\n override) the autogen before validation. The autofill\n chain still runs in the background: picking a target\n pulls in its name; clicking Validate falls back to the\n probed hostname when the user hasn't typed anything.\n The svc dedupes inside the workspace via\n resolveUniqueAgentName(), so suffixes like `-2` may\n still get appended at submit time even when the user\n picked their own string. */}\n <div>\n <label\n htmlFor={nameField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.name', 'Name')}\n </label>\n <Input\n {...nameField.inputProps}\n type=\"text\"\n value={name}\n onChange={(e) => {\n setName(e.target.value);\n setNameEdited(true);\n }}\n placeholder={tr(\n 'agentsPage.namePlaceholderUpfront',\n 'My laptop, prod jump-host, …'\n )}\n className=\"text-base md:text-sm\"\n data-testid=\"agent-form-name-input\"\n />\n <p id={nameField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {tr(\n 'agentsPage.nameHint',\n \"How this agent shows up in the UI. Defaults to the target's name (or the machine's hostname after Validate) — leave blank to accept the autogen.\"\n )}\n </p>\n </div>\n\n {/* Target Selection — with inline creation */}\n <div>\n <span className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('agentsPage.target', 'Target')}\n {requiredMark}\n </span>\n\n {hasTargets && !showInlineTarget && (\n <>\n <SearchableSelect\n placeholder={tr('agentsPage.selectTarget', 'Select a target...')}\n searchPlaceholder={tr('agentsPage.searchTargets', 'Search targets...')}\n options={(targets ?? []).map(\n (tgt): SearchableSelectOption => ({\n value: tgt.id,\n label: tgt.name,\n description: `${tgt.type}${tgt.status ? ` · ${tgt.status}` : ''}`,\n })\n )}\n value={selectedTargetId}\n onChange={setSelectedTargetId}\n clearable\n />\n <p className=\"mt-1.5 text-xs text-text-muted\">\n {tr(\n 'agentsPage.targetDescription',\n 'The target machine this agent will be installed on.'\n )}{' '}\n {onCreateTarget && (\n <button\n type=\"button\"\n onClick={() => {\n setShowInlineTarget(true);\n setInlineTargetName(name.trim() || '');\n }}\n className=\"text-text-link hover:text-text-linkHover hover:underline inline-flex items-center gap-1 rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <Plus className=\"size-3\" aria-hidden=\"true\" />\n {tr('agentsPage.createNewTarget', 'Create a new target')}\n </button>\n )}\n </p>\n </>\n )}\n\n {/* Empty state prompt (no targets exist yet) */}\n {!hasTargets && !targetsLoading && onCreateTarget && !showInlineTarget && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg\">\n <p className=\"text-sm text-text-secondary mb-2\">\n {tr(\n 'agentsPage.noTargetsInlinePrompt',\n \"You don't have any targets yet. Create one now to install this agent on it.\"\n )}\n </p>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => {\n setShowInlineTarget(true);\n setInlineTargetName(name.trim() || '');\n }}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('agentsPage.createFirstTarget', 'Create first target')}\n </Button>\n </div>\n )}\n\n {/* Inline creation form */}\n {showInlineTarget && onCreateTarget && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\n 'agentsPage.newDirectTarget',\n 'New target (Direct — you can change the type later)'\n )}\n </p>\n <div>\n <label\n htmlFor={inlineTargetField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.targetName', 'Target name')}\n </label>\n <Input\n {...inlineTargetField.inputProps}\n type=\"text\"\n value={inlineTargetName}\n onChange={(e) => setInlineTargetName(e.target.value)}\n placeholder={tr('agentsPage.targetName', 'Target name')}\n className=\"text-base md:text-sm\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n void handleInlineCreateTarget();\n }\n }}\n />\n {inlineTargetError && (\n <p\n id={inlineTargetField.errorId}\n role=\"alert\"\n className=\"mt-1.5 text-sm text-status-error-text\"\n >\n {inlineTargetError}\n </p>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void handleInlineCreateTarget()}\n disabled={!inlineTargetName.trim() || creatingTarget}\n aria-busy={creatingTarget}\n >\n {creatingTarget ? (\n <Loader2 className=\"size-3.5 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Check className=\"size-3.5\" aria-hidden=\"true\" />\n )}\n {tr('agentsPage.create', 'Create')}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n setShowInlineTarget(false);\n setInlineTargetName('');\n setInlineTargetError(null);\n }}\n >\n {tr('agentsPage.cancel', 'Cancel')}\n </Button>\n </div>\n </div>\n )}\n\n {/* Loading placeholder */}\n {targetsLoading && !hasTargets && (\n <div\n role=\"status\"\n className=\"p-3 bg-bg-sunken border border-border-default rounded-lg flex items-center gap-2 text-sm text-text-secondary\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n {tr('agentsPage.loadingTargets', 'Loading targets...')}\n </div>\n )}\n </div>\n\n {/* API/Tunnel URL */}\n <div>\n <label\n htmlFor={tunnelField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.apiTunnelUrl', 'API / Tunnel URL')}\n {requiredMark}\n </label>\n <Input\n {...tunnelField.inputProps}\n type=\"url\"\n value={tunnelUrl}\n onChange={(e) => setTunnelUrl(e.target.value)}\n placeholder=\"https://your-tunnel.trycloudflare.com\"\n aria-required=\"true\"\n className=\"text-base md:text-sm font-mono\"\n />\n <p id={tunnelField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {tr(\n 'agentsPage.tunnelUrlHint',\n \"The agent's tunnel URL or local API URL (e.g. http://localhost:3005)\"\n )}\n </p>\n </div>\n\n {/* Agent API Key */}\n <div>\n <label\n htmlFor={apiKeyField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.agentApiKey', 'Agent API Key')}\n {requiredMark}\n </label>\n <div className=\"relative\">\n <Input\n {...apiKeyField.inputProps}\n type={showApiKey ? 'text' : 'password'}\n value={agentApiKey}\n onChange={(e) => setAgentApiKey(e.target.value)}\n placeholder=\"vcak_...\"\n aria-required=\"true\"\n className=\"pr-10 text-base md:text-sm font-mono\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setShowApiKey(!showApiKey)}\n aria-label={\n showApiKey\n ? tr('agentsPage.hideApiKey', 'Hide API key')\n : tr('agentsPage.showApiKey', 'Show API key')\n }\n aria-pressed={showApiKey}\n className=\"absolute right-1 top-1/2 -translate-y-1/2\"\n >\n {showApiKey ? (\n <EyeOff className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n ) : (\n <Eye className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n )}\n </Button>\n </div>\n <p id={apiKeyField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {tr('agentsPage.apiKeyHint', 'Run')}{' '}\n <code className=\"bg-bg-sunken px-1 rounded\">vibe key</code>{' '}\n {tr(\n 'agentsPage.apiKeyHintSuffix',\n 'on the agent to get this. Changes on restart.'\n )}\n </p>\n </div>\n\n {/* Advanced options — per-daemon profile name (Phase 5B) */}\n <div className=\"border border-border-default rounded-lg overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between px-4 py-2.5 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n aria-expanded={showAdvanced}\n data-testid=\"agent-form-advanced-toggle\"\n >\n <div className=\"flex items-center gap-2\">\n <Settings2 className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.advancedOptions', 'Advanced options')}\n </span>\n </div>\n <ChevronDown\n aria-hidden=\"true\"\n className={`size-4 text-text-secondary transition-transform ${showAdvanced ? 'rotate-180' : ''}`}\n />\n </button>\n {showAdvanced && (\n <div className=\"px-4 py-3 space-y-3 border-t border-border-default\">\n <div>\n <label\n htmlFor={profileField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.profileName', 'Profile name')}\n </label>\n <Input\n {...profileField.inputProps}\n type=\"text\"\n value={profile}\n onChange={(e) => setProfile(e.target.value)}\n placeholder=\"default\"\n className=\"text-base md:text-sm font-mono\"\n data-testid=\"agent-form-profile-input\"\n />\n <p id={profileField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {tr(\n 'agentsPage.profileNameHint',\n 'Each profile runs an isolated CLI on the daemon. Leave as \"default\" unless you want this agent to host multiple parallel profiles.'\n )}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {extraSection && (\n <div className=\"border-t border-border-default pt-4 mt-4\">{extraSection}</div>\n )}\n\n {/* Actions */}\n <ResponsiveDialogFooter className=\"pt-2\">\n <Button type=\"button\" variant=\"outline\" onClick={onCancel}>\n {tr('agentsPage.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n disabled={!canProceedToValidate}\n onClick={() => {\n setStage('validate');\n validateAgent();\n }}\n >\n {tr('agentsPage.nextValidate', 'Next: Validate')}\n <ChevronRight className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </ResponsiveDialogFooter>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Validation Results */}\n <div className=\"space-y-3\">\n {/* Health Check */}\n <div\n role=\"status\"\n className={`flex items-center gap-3 p-3 rounded-lg border ${\n healthResult === null\n ? 'border-border-default bg-bg-sunken'\n : healthResult.ok\n ? 'border-status-success-border bg-status-success-bg-subtle'\n : 'border-status-error-border bg-status-error-bg-subtle'\n }`}\n >\n {validating && !healthResult ? (\n <Loader2\n className=\"size-5 text-text-secondary animate-spin\"\n aria-hidden=\"true\"\n />\n ) : healthResult?.ok ? (\n <CheckCircle2 className=\"size-5 text-status-success-text\" aria-hidden=\"true\" />\n ) : healthResult ? (\n <XCircle className=\"size-5 text-status-error-text\" aria-hidden=\"true\" />\n ) : (\n <div\n className=\"size-5 rounded-full border-2 border-border-default\"\n aria-hidden=\"true\"\n />\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.healthCheck', 'Health Check')}\n </p>\n {healthResult?.ok && healthResult.data && (\n <p className=\"text-xs text-text-secondary break-words\">\n Status: {healthResult.data.status} | Version: {healthResult.data.version} |\n Uptime: {Math.round(healthResult.data.uptime)}s\n </p>\n )}\n {healthResult && !healthResult.ok && (\n <p className=\"text-xs text-status-error-text break-words\">\n {healthResult.error}\n </p>\n )}\n </div>\n </div>\n\n {/* API Access Check */}\n <div\n role=\"status\"\n className={`flex items-center gap-3 p-3 rounded-lg border ${\n identityResult === null\n ? 'border-border-default bg-bg-sunken'\n : identityResult.ok\n ? 'border-status-success-border bg-status-success-bg-subtle'\n : 'border-status-error-border bg-status-error-bg-subtle'\n }`}\n >\n {validating && !identityResult ? (\n <Loader2\n className=\"size-5 text-text-secondary animate-spin\"\n aria-hidden=\"true\"\n />\n ) : identityResult?.ok ? (\n <CheckCircle2 className=\"size-5 text-status-success-text\" aria-hidden=\"true\" />\n ) : identityResult ? (\n <XCircle className=\"size-5 text-status-error-text\" aria-hidden=\"true\" />\n ) : (\n <div\n className=\"size-5 rounded-full border-2 border-border-default\"\n aria-hidden=\"true\"\n />\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.apiAccess', 'API Access')}\n </p>\n {identityResult?.ok && identityResult.data && (\n <p className=\"text-xs text-text-secondary break-words\">\n Host: {identityResult.data.hostname} | {identityResult.data.platform} /{' '}\n {identityResult.data.arch}\n </p>\n )}\n {identityResult && !identityResult.ok && (\n <p className=\"text-xs text-status-error-text break-words\">\n {identityResult.error}\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Retry button */}\n {!validating && (!healthResult?.ok || !identityResult?.ok) && (\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={validateAgent}>\n <RefreshCw className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('agentsPage.retryValidation', 'Retry validation')}\n </Button>\n )}\n\n {/* Detected info (editable overrides) — Name lives upfront\n now; this section shows the validate-time fields only. */}\n {isValidationPassed && (\n <div className=\"space-y-3 pt-2\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.detectedAgentInfo', 'Detected Agent Info')}\n </p>\n\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor={platformField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.platform', 'Platform')}\n </label>\n <Select value={detectedPlatform} onValueChange={setDetectedPlatform}>\n <SelectTrigger id={platformField.id} className=\"text-base md:text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"linux\">Linux</SelectItem>\n <SelectItem value=\"darwin\">macOS</SelectItem>\n <SelectItem value=\"windows\">Windows</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <label\n htmlFor={archField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.architecture', 'Architecture')}\n </label>\n <Select value={detectedArch} onValueChange={setDetectedArch}>\n <SelectTrigger id={archField.id} className=\"text-base md:text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"x86_64\">x86_64</SelectItem>\n <SelectItem value=\"arm64\">arm64</SelectItem>\n <SelectItem value=\"aarch64\">aarch64</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n </div>\n )}\n\n {/* Creation error */}\n {createError && (\n <div\n role=\"alert\"\n className=\"p-3 bg-status-error-bg-subtle border border-status-error-border rounded-lg\"\n >\n <div className=\"flex items-center gap-2\">\n <XCircle\n className=\"size-5 text-status-error-text flex-shrink-0\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm text-status-error-text break-words\">{createError}</p>\n </div>\n <div className=\"flex flex-wrap items-center gap-2 mt-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n setCreateError(null);\n // Reset form to allow entering different credentials\n setStage('details');\n setTunnelUrl('');\n setAgentApiKey('');\n setHealthResult(null);\n setIdentityResult(null);\n }}\n >\n <RefreshCw className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('agentsPage.tryDifferentAgent', 'Try Different Agent')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={onCancel}>\n {tr('agentsPage.close', 'Close')}\n </Button>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <ResponsiveDialogFooter className=\"pt-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => setStage('details')}>\n <ChevronLeft className=\"size-4\" aria-hidden=\"true\" />\n {tr('agentsPage.back', 'Back')}\n </Button>\n <Button\n type=\"button\"\n disabled={!isValidationPassed || isLoading || validating}\n onClick={handleCreateAgent}\n aria-busy={isLoading}\n >\n {isLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n {tr('agentsPage.adding', 'Adding...')}\n </>\n ) : (\n <>\n {tr('agentsPage.addAgent', 'Add Agent')}\n <ChevronRight className=\"size-4\" aria-hidden=\"true\" />\n </>\n )}\n </Button>\n </ResponsiveDialogFooter>\n </div>\n )}\n </MotionTabPanel>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentForm.js","names":[],"sources":["../../../src/components/agents/AgentForm.tsx"],"sourcesContent":["import { useEffect, useRef, useState, useCallback } from 'react';\nimport type { ReactNode } from 'react';\nimport {\n Eye,\n EyeOff,\n CheckCircle2,\n XCircle,\n Loader2,\n ChevronRight,\n ChevronLeft,\n ChevronDown,\n Copy,\n Check,\n Terminal,\n Download,\n RefreshCw,\n Plus,\n Settings2,\n} from 'lucide-react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Stepper,\n} from '@burdenoff/fe-libs/ui';\nimport { useProbeAgentTunnelLazyQuery } from '@/generated/wspace-operations';\nimport { SearchableSelect, MotionTabPanel } from '@/components/shared';\nimport type { SearchableSelectOption } from '@/components/shared';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useField } from '../../shared/hooks/useField';\nimport { isQuotaExhaustedError, wasQuotaHandledLocally } from '@/utils/quotaUtils';\n\ntype WizardStage = 'details' | 'validate';\n\ninterface TargetOption {\n id: string;\n name: string;\n type: string;\n status: string;\n}\n\ninterface AgentFormProps {\n onSubmit: (data: Record<string, unknown>) => Promise<{ id: string } | void>;\n onCancel: () => void;\n isLoading?: boolean;\n /** Navigate to a path (used to go to agent details after creation) */\n onNavigate?: (path: string) => void;\n /** Available targets for the agent to be installed on */\n targets?: TargetOption[];\n /** Whether targets are still loading */\n targetsLoading?: boolean;\n /**\n * Create a new target inline. When provided, the form shows a\n * \"+ Create new target\" affordance that lets users create a DIRECT\n * target on the fly without leaving the dialog. Must return the new\n * target so the form can auto-select it.\n */\n onCreateTarget?: (name: string) => Promise<TargetOption | null>;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the configure-stage actions. */\n extraSection?: ReactNode;\n}\n\nexport function AgentForm({\n onSubmit,\n onCancel,\n isLoading,\n onNavigate,\n targets,\n targetsLoading,\n onCreateTarget,\n extraSection,\n}: AgentFormProps) {\n const tr = useTr();\n const [stage, setStage] = useState<WizardStage>('details');\n const [showApiKey, setShowApiKey] = useState(false);\n const [showInstallInstructions, setShowInstallInstructions] = useState(false);\n const [copiedCommand, setCopiedCommand] = useState<string | null>(null);\n\n // Stage 1: Basic details\n const [name, setName] = useState('');\n // Tracks whether the operator has typed into the name field. Once true,\n // we stop overwriting the value from auto-fill sources (target select +\n // identity probe) so we never clobber their input.\n const [nameEdited, setNameEdited] = useState(false);\n const [tunnelUrl, setTunnelUrl] = useState('');\n const [agentApiKey, setAgentApiKey] = useState('');\n const [selectedTargetId, setSelectedTargetId] = useState('');\n\n // When the operator picks a target, autofill the agent name with the\n // target's display name. Skip if they've already typed something into\n // the name field. The svc append a `-2`/`-3` suffix on collision so\n // it's safe to suggest a duplicate; the actual stored name comes back\n // on the create response.\n useEffect(() => {\n if (nameEdited) return;\n if (!selectedTargetId) return;\n const picked = (targets ?? []).find((t) => t.id === selectedTargetId);\n if (picked?.name) setName(picked.name);\n }, [selectedTargetId, targets, nameEdited]);\n\n // Inline target creation state\n const [showInlineTarget, setShowInlineTarget] = useState(false);\n const [inlineTargetName, setInlineTargetName] = useState('');\n const [creatingTarget, setCreatingTarget] = useState(false);\n const [inlineTargetError, setInlineTargetError] = useState<string | null>(null);\n\n // Stage 2: Validation (via backend-proxied GraphQL query)\n const [validating, setValidating] = useState(false);\n const [healthResult, setHealthResult] = useState<{\n ok: boolean;\n data?: { status: string; version: string; uptime: number };\n error?: string;\n } | null>(null);\n const [identityResult, setIdentityResult] = useState<{\n ok: boolean;\n data?: { hostname: string; platform: string; arch: string };\n error?: string;\n } | null>(null);\n\n // Auto-detected fields (editable overrides)\n const [detectedHostname, setDetectedHostname] = useState('');\n const [detectedPlatform, setDetectedPlatform] = useState('linux');\n const [detectedArch, setDetectedArch] = useState('x86_64');\n const [detectedVersion, setDetectedVersion] = useState('1.0.0');\n\n // Error state for creation failures\n const [createError, setCreateError] = useState<string | null>(null);\n\n // Advanced options (Phase 5B: per-daemon profile name).\n // Defaults to \"default\" (autofilled but editable). Disclosure is closed\n // by default — users only need to touch this for multi-profile setups.\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [profile, setProfile] = useState('default');\n\n // Accessible label/hint/error wiring for every form control. The hook\n // only checks truthiness of `hint` to chain aria-describedby to the\n // always-visible hint <p id={field.hintId}> below each input.\n const HAS_HINT = 'visible';\n const nameField = useField({ hint: HAS_HINT });\n const inlineTargetField = useField({ error: inlineTargetError });\n const tunnelField = useField({ hint: HAS_HINT });\n const apiKeyField = useField({ hint: HAS_HINT });\n const profileField = useField({ hint: HAS_HINT });\n const platformField = useField();\n const archField = useField();\n\n const copyToClipboard = useCallback((text: string, id: string) => {\n navigator.clipboard.writeText(text);\n setCopiedCommand(id);\n setTimeout(() => setCopiedCommand(null), 2000);\n }, []);\n\n const normalizeUrl = (url: string) => {\n let u = url.trim();\n if (u.endsWith('/')) u = u.slice(0, -1);\n return u;\n };\n\n const [runProbe] = useProbeAgentTunnelLazyQuery({ fetchPolicy: 'network-only' });\n\n // Validate the agent through the svc-proxied `probeAgentTunnel` query.\n // svc fans out to /health + /api/profiles/default/agent/identity over\n // its own egress and returns a combined AgentProbeResult — the\n // operator's API key never touches the browser→tunnel path.\n const validateAgent = async () => {\n const baseUrl = normalizeUrl(tunnelUrl);\n if (!baseUrl) return;\n\n setValidating(true);\n setHealthResult(null);\n setIdentityResult(null);\n\n try {\n const { data, error } = await runProbe({\n variables: { url: baseUrl, agentApiKey: agentApiKey || null },\n });\n const probe = data?.probeAgentTunnel;\n if (error || !probe) {\n const msg = error?.message ?? 'Probe failed';\n setHealthResult({ ok: false, error: msg });\n setIdentityResult({ ok: false, error: msg });\n } else {\n setHealthResult(\n probe.healthOk\n ? {\n ok: true,\n data: {\n status: probe.healthStatus ?? 'healthy',\n version: probe.agentVersion ?? 'unknown',\n uptime: probe.uptime ?? 0,\n },\n }\n : { ok: false, error: probe.healthError ?? 'Health check failed' }\n );\n if (probe.agentVersion) setDetectedVersion(probe.agentVersion);\n\n setIdentityResult(\n probe.identityOk\n ? {\n ok: true,\n data: {\n hostname: probe.hostname ?? '',\n platform: probe.platform ?? 'linux',\n arch: probe.architecture ?? 'x86_64',\n },\n }\n : { ok: false, error: probe.identityError ?? 'Identity check failed' }\n );\n if (probe.identityOk) {\n if (probe.platform) setDetectedPlatform(probe.platform);\n if (probe.architecture) setDetectedArch(probe.architecture);\n // Auto-fill name from probe hostname unless (a) the operator\n // has manually typed one, or (b) a target is already selected\n // (target wins as the canonical autogen value).\n if (probe.hostname) {\n setDetectedHostname(probe.hostname);\n if (!nameEdited && !selectedTargetId) setName(probe.hostname);\n }\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'Probe failed';\n setHealthResult({ ok: false, error: msg });\n setIdentityResult({ ok: false, error: msg });\n }\n\n setValidating(false);\n };\n\n // Refs let us short-circuit double-submits before React re-renders\n // the button as disabled. The parent passes `isLoading` (driven by\n // `useState`) and we wire it to the button's `disabled` prop, but\n // `setState` is asynchronous — between the user's first click and\n // React's next commit there is a microtask window in which the button\n // is still clickable. Without this ref a fast double-click fires\n // `addAgentFromTunnel` twice in parallel; the svc sees one in-flight\n // and one fresh call, the second one wins the (workspaceId, name)\n // unique-resolution race, and the workspace ends up with two agent\n // rows (the original autogen + the operator's custom name).\n const submittingRef = useRef(false);\n\n // Create the agent and close the modal\n const handleCreateAgent = async () => {\n if (submittingRef.current) return;\n submittingRef.current = true;\n setCreateError(null);\n\n // Pipe the chosen profile through `metadata.profile`. Phase 5A (svc) is\n // adding first-class AgentProfile rows; in the meantime the backend\n // also reads `metadata.profile` so the create path keeps working\n // regardless of which side of the cut-over the cluster is on.\n const trimmedProfile = profile.trim() || 'default';\n const data: Record<string, unknown> = {\n name: name || detectedHostname || 'Unnamed Agent',\n hostname: detectedHostname || 'unknown',\n platform: detectedPlatform,\n architecture: detectedArch,\n version: detectedVersion,\n tunnelUrl: normalizeUrl(tunnelUrl),\n tunnelStatus: 'ACTIVE',\n agentApiKey,\n metadata: { profile: trimmedProfile },\n ...(selectedTargetId ? { targetId: selectedTargetId } : {}),\n };\n\n try {\n const result = await onSubmit(data);\n const newAgentId = result?.id || null;\n\n if (!newAgentId) {\n // Mutation succeeded but returned no agent ID — treat as failure\n setCreateError('Agent creation returned no ID. The agent may already exist.');\n return;\n }\n\n // Close the modal — the agent list will refetch automatically\n if (onNavigate) {\n onNavigate(`/agents/${newAgentId}`);\n } else {\n onCancel();\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to create agent';\n setCreateError(message);\n } finally {\n submittingRef.current = false;\n }\n };\n\n const isValidationPassed = healthResult?.ok && identityResult?.ok;\n const hasTargets = targets && targets.length > 0;\n // The user must either pick or inline-create a target if inline creation\n // is enabled; otherwise we only require it when targets already exist.\n const targetRequiredButMissing = onCreateTarget\n ? selectedTargetId.length === 0\n : hasTargets && selectedTargetId.length === 0;\n const canProceedToValidate = tunnelUrl.trim().length > 0 && !targetRequiredButMissing;\n\n const handleInlineCreateTarget = useCallback(async () => {\n if (!onCreateTarget || !inlineTargetName.trim()) return;\n setCreatingTarget(true);\n setInlineTargetError(null);\n try {\n const created = await onCreateTarget(inlineTargetName.trim());\n if (created) {\n setSelectedTargetId(created.id);\n setShowInlineTarget(false);\n setInlineTargetName('');\n } else if (wasQuotaHandledLocally()) {\n // Parent surfaced the quota modal — collapse the inline row\n // quietly so the user isn't left with a vague \"try again\"\n // message under an explicit upgrade-your-plan dialog.\n setShowInlineTarget(false);\n setInlineTargetName('');\n } else {\n setInlineTargetError(\n tr('agentsPage.inlineTargetFailed', 'Failed to create target. Please try again.')\n );\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n // Quota dialog will (or already has) opened at the parent;\n // suppress the generic inline error so the two don't compete.\n setShowInlineTarget(false);\n setInlineTargetName('');\n } else {\n setInlineTargetError(err instanceof Error ? err.message : 'Failed to create target');\n }\n } finally {\n setCreatingTarget(false);\n }\n }, [onCreateTarget, inlineTargetName, tr]);\n\n const renderCopyButton = (text: string, id: string) => {\n const copied = copiedCommand === id;\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => copyToClipboard(text, id)}\n aria-label={copied ? tr('agentsPage.copied', 'Copied') : tr('agentsPage.copy', 'Copy')}\n className=\"shrink-0\"\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-text\" aria-hidden=\"true\" />\n ) : (\n <Copy className=\"size-3.5 text-text-secondary\" aria-hidden=\"true\" />\n )}\n </Button>\n );\n };\n\n const requiredMark = (\n <span aria-hidden=\"true\" className=\"text-status-error-text\">\n {' '}\n *\n </span>\n );\n\n // Stepper\n const steps = [\n { key: 'details', label: tr('agentsPage.stepSetup', 'Setup') },\n { key: 'validate', label: tr('agentsPage.stepValidate', 'Validate') },\n ];\n\n const currentStepIndex = steps.findIndex((s) => s.key === stage);\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !isLoading) onCancel();\n }}\n >\n {/* Multi-field data-entry dialog: a stray click on the scrim must not\n discard typed input. Escape and the explicit Cancel/X still close. */}\n <ResponsiveDialogContent\n className=\"sm:max-w-xl md:max-h-[min(90vh,52rem)] md:overflow-y-auto\"\n onPointerDownOutside={(event) => event.preventDefault()}\n >\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('agentsPage.addNewAgent', 'Add New Agent')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'agentsPage.addNewAgentDescription',\n 'Connect a running vibe agent to this workspace.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <Stepper\n steps={steps}\n active={currentStepIndex}\n onStepClick={(index) => {\n const target = steps[index];\n if (target) setStage(target.key === 'validate' ? 'validate' : 'details');\n }}\n />\n\n <MotionTabPanel tabKey={stage}>\n {stage === 'details' ? (\n <div className=\"space-y-4\">\n {/* Install instructions (collapsible) */}\n <div className=\"border border-border-default rounded-lg overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowInstallInstructions(!showInstallInstructions)}\n aria-expanded={showInstallInstructions}\n className=\"w-full flex items-center justify-between px-4 py-3 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"flex items-center gap-2\">\n <Download className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.installInstructions', 'Agent Install Instructions')}\n </span>\n </div>\n <ChevronRight\n aria-hidden=\"true\"\n className={`size-4 text-text-secondary transition-transform ${showInstallInstructions ? 'rotate-90' : ''}`}\n />\n </button>\n {showInstallInstructions && (\n <div className=\"px-4 py-3 space-y-3 border-t border-border-default\">\n <div className=\"space-y-2\">\n {[\n {\n label: tr('agentsPage.installStep1', '1. Install the agent globally'),\n // Documented quick install — bun is the platform\n // default. See docs.vibecontrols.com/users/installing-the-agent\n cmd: 'bun install -g @vibecontrols/agent',\n },\n {\n label: tr(\n 'agentsPage.installStep2',\n '2. Start the agent (prints tunnel URL + API key)'\n ),\n cmd: 'vibe start',\n },\n ].map((step) => (\n <div key={step.cmd}>\n <p className=\"text-xs text-text-secondary mb-1\">{step.label}</p>\n <div className=\"flex items-center gap-2 bg-bg-sunken rounded-md px-3 py-1.5 font-mono text-xs text-text-primary\">\n <Terminal\n className=\"size-3 text-text-muted flex-shrink-0\"\n aria-hidden=\"true\"\n />\n <code className=\"flex-1 min-w-0 break-words\">{step.cmd}</code>\n {renderCopyButton(step.cmd, step.cmd)}\n </div>\n </div>\n ))}\n <p className=\"text-xs text-text-secondary pt-1\">\n {tr(\n 'agentsPage.installHint',\n 'After running vibe start, copy the printed tunnel URL and API key into the fields above.'\n )}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {/* Agent Name — visible upfront so operators see (and can\n override) the autogen before validation. The autofill\n chain still runs in the background: picking a target\n pulls in its name; clicking Validate falls back to the\n probed hostname when the user hasn't typed anything.\n The svc dedupes inside the workspace via\n resolveUniqueAgentName(), so suffixes like `-2` may\n still get appended at submit time even when the user\n picked their own string. */}\n <div>\n <label\n htmlFor={nameField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.name', 'Name')}\n </label>\n <Input\n {...nameField.inputProps}\n type=\"text\"\n value={name}\n onChange={(e) => {\n setName(e.target.value);\n setNameEdited(true);\n }}\n placeholder={tr(\n 'agentsPage.namePlaceholderUpfront',\n 'My laptop, prod jump-host, …'\n )}\n className=\"text-base md:text-sm\"\n data-testid=\"agent-form-name-input\"\n />\n <p id={nameField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {tr(\n 'agentsPage.nameHint',\n \"How this agent shows up in the UI. Defaults to the target's name (or the machine's hostname after Validate) — leave blank to accept the autogen.\"\n )}\n </p>\n </div>\n\n {/* Target Selection — with inline creation */}\n <div>\n <span className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('agentsPage.target', 'Target')}\n {requiredMark}\n </span>\n\n {hasTargets && !showInlineTarget && (\n <>\n <SearchableSelect\n placeholder={tr('agentsPage.selectTarget', 'Select a target...')}\n searchPlaceholder={tr('agentsPage.searchTargets', 'Search targets...')}\n options={(targets ?? []).map(\n (tgt): SearchableSelectOption => ({\n value: tgt.id,\n label: tgt.name,\n description: `${tgt.type}${tgt.status ? ` · ${tgt.status}` : ''}`,\n })\n )}\n value={selectedTargetId}\n onChange={setSelectedTargetId}\n clearable\n />\n <p className=\"mt-1.5 text-xs text-text-muted\">\n {tr(\n 'agentsPage.targetDescription',\n 'The target machine this agent will be installed on.'\n )}{' '}\n {onCreateTarget && (\n <button\n type=\"button\"\n onClick={() => {\n setShowInlineTarget(true);\n setInlineTargetName(name.trim() || '');\n }}\n className=\"text-text-link hover:text-text-linkHover hover:underline inline-flex items-center gap-1 rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <Plus className=\"size-3\" aria-hidden=\"true\" />\n {tr('agentsPage.createNewTarget', 'Create a new target')}\n </button>\n )}\n </p>\n </>\n )}\n\n {/* Empty state prompt (no targets exist yet) */}\n {!hasTargets && !targetsLoading && onCreateTarget && !showInlineTarget && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg\">\n <p className=\"text-sm text-text-secondary mb-2\">\n {tr(\n 'agentsPage.noTargetsInlinePrompt',\n \"You don't have any targets yet. Create one now to install this agent on it.\"\n )}\n </p>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => {\n setShowInlineTarget(true);\n setInlineTargetName(name.trim() || '');\n }}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('agentsPage.createFirstTarget', 'Create first target')}\n </Button>\n </div>\n )}\n\n {/* Inline creation form */}\n {showInlineTarget && onCreateTarget && (\n <div className=\"p-3 bg-bg-sunken border border-border-default rounded-lg space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\n 'agentsPage.newDirectTarget',\n 'New target (Direct — you can change the type later)'\n )}\n </p>\n <div>\n <label\n htmlFor={inlineTargetField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.targetName', 'Target name')}\n </label>\n <Input\n {...inlineTargetField.inputProps}\n type=\"text\"\n value={inlineTargetName}\n onChange={(e) => setInlineTargetName(e.target.value)}\n placeholder={tr('agentsPage.targetName', 'Target name')}\n className=\"text-base md:text-sm\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n void handleInlineCreateTarget();\n }\n }}\n />\n {inlineTargetError && (\n <p\n id={inlineTargetField.errorId}\n role=\"alert\"\n className=\"mt-1.5 text-sm text-status-error-text\"\n >\n {inlineTargetError}\n </p>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void handleInlineCreateTarget()}\n disabled={!inlineTargetName.trim() || creatingTarget}\n aria-busy={creatingTarget}\n >\n {creatingTarget ? (\n <Loader2 className=\"size-3.5 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Check className=\"size-3.5\" aria-hidden=\"true\" />\n )}\n {tr('agentsPage.create', 'Create')}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n setShowInlineTarget(false);\n setInlineTargetName('');\n setInlineTargetError(null);\n }}\n >\n {tr('agentsPage.cancel', 'Cancel')}\n </Button>\n </div>\n </div>\n )}\n\n {/* Loading placeholder */}\n {targetsLoading && !hasTargets && (\n <div\n role=\"status\"\n className=\"p-3 bg-bg-sunken border border-border-default rounded-lg flex items-center gap-2 text-sm text-text-secondary\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n {tr('agentsPage.loadingTargets', 'Loading targets...')}\n </div>\n )}\n </div>\n\n {/* API/Tunnel URL */}\n <div>\n <label\n htmlFor={tunnelField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.apiTunnelUrl', 'API / Tunnel URL')}\n {requiredMark}\n </label>\n <Input\n {...tunnelField.inputProps}\n type=\"url\"\n value={tunnelUrl}\n onChange={(e) => setTunnelUrl(e.target.value)}\n placeholder=\"https://your-tunnel.trycloudflare.com\"\n aria-required=\"true\"\n className=\"text-base md:text-sm font-mono\"\n />\n <p id={tunnelField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {tr(\n 'agentsPage.tunnelUrlHint',\n \"The agent's tunnel URL or local API URL (e.g. http://localhost:3005)\"\n )}\n </p>\n </div>\n\n {/* Agent API Key */}\n <div>\n <label\n htmlFor={apiKeyField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.agentApiKey', 'Agent API Key')}\n {requiredMark}\n </label>\n <div className=\"relative\">\n <Input\n {...apiKeyField.inputProps}\n type={showApiKey ? 'text' : 'password'}\n value={agentApiKey}\n onChange={(e) => setAgentApiKey(e.target.value)}\n placeholder=\"vcak_...\"\n aria-required=\"true\"\n className=\"pr-10 text-base md:text-sm font-mono\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setShowApiKey(!showApiKey)}\n aria-label={\n showApiKey\n ? tr('agentsPage.hideApiKey', 'Hide API key')\n : tr('agentsPage.showApiKey', 'Show API key')\n }\n aria-pressed={showApiKey}\n className=\"absolute right-1 top-1/2 -translate-y-1/2\"\n >\n {showApiKey ? (\n <EyeOff className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n ) : (\n <Eye className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n )}\n </Button>\n </div>\n <p id={apiKeyField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {tr('agentsPage.apiKeyHint', 'Run')}{' '}\n <code className=\"bg-bg-sunken px-1 rounded\">vibe key</code>{' '}\n {tr(\n 'agentsPage.apiKeyHintSuffix',\n 'on the agent to get this. Changes on restart.'\n )}\n </p>\n </div>\n\n {/* Advanced options — per-daemon profile name (Phase 5B) */}\n <div className=\"border border-border-default rounded-lg overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between px-4 py-2.5 bg-bg-sunken hover:bg-bg-sunken/80 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n aria-expanded={showAdvanced}\n data-testid=\"agent-form-advanced-toggle\"\n >\n <div className=\"flex items-center gap-2\">\n <Settings2 className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.advancedOptions', 'Advanced options')}\n </span>\n </div>\n <ChevronDown\n aria-hidden=\"true\"\n className={`size-4 text-text-secondary transition-transform ${showAdvanced ? 'rotate-180' : ''}`}\n />\n </button>\n {showAdvanced && (\n <div className=\"px-4 py-3 space-y-3 border-t border-border-default\">\n <div>\n <label\n htmlFor={profileField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.profileName', 'Profile name')}\n </label>\n <Input\n {...profileField.inputProps}\n type=\"text\"\n value={profile}\n onChange={(e) => setProfile(e.target.value)}\n placeholder=\"default\"\n className=\"text-base md:text-sm font-mono\"\n data-testid=\"agent-form-profile-input\"\n />\n <p id={profileField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {tr(\n 'agentsPage.profileNameHint',\n 'Each profile runs an isolated CLI on the daemon. Leave as \"default\" unless you want this agent to host multiple parallel profiles.'\n )}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {extraSection && (\n <div className=\"border-t border-border-default pt-4 mt-4\">{extraSection}</div>\n )}\n\n {/* Actions */}\n <ResponsiveDialogFooter className=\"pt-2\">\n <Button type=\"button\" variant=\"outline\" onClick={onCancel}>\n {tr('agentsPage.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n disabled={!canProceedToValidate}\n onClick={() => {\n setStage('validate');\n validateAgent();\n }}\n >\n {tr('agentsPage.nextValidate', 'Next: Validate')}\n <ChevronRight className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </ResponsiveDialogFooter>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Validation Results */}\n <div className=\"space-y-3\">\n {/* Health Check */}\n <div\n role=\"status\"\n className={`flex items-center gap-3 p-3 rounded-lg border ${\n healthResult === null\n ? 'border-border-default bg-bg-sunken'\n : healthResult.ok\n ? 'border-status-success-border bg-status-success-bg-subtle'\n : 'border-status-error-border bg-status-error-bg-subtle'\n }`}\n >\n {validating && !healthResult ? (\n <Loader2\n className=\"size-5 text-text-secondary animate-spin\"\n aria-hidden=\"true\"\n />\n ) : healthResult?.ok ? (\n <CheckCircle2 className=\"size-5 text-status-success-text\" aria-hidden=\"true\" />\n ) : healthResult ? (\n <XCircle className=\"size-5 text-status-error-text\" aria-hidden=\"true\" />\n ) : (\n <div\n className=\"size-5 rounded-full border-2 border-border-default\"\n aria-hidden=\"true\"\n />\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.healthCheck', 'Health Check')}\n </p>\n {healthResult?.ok && healthResult.data && (\n <p className=\"text-xs text-text-secondary break-words\">\n Status: {healthResult.data.status} | Version: {healthResult.data.version} |\n Uptime: {Math.round(healthResult.data.uptime)}s\n </p>\n )}\n {healthResult && !healthResult.ok && (\n <p className=\"text-xs text-status-error-text break-words\">\n {healthResult.error}\n </p>\n )}\n </div>\n </div>\n\n {/* API Access Check */}\n <div\n role=\"status\"\n className={`flex items-center gap-3 p-3 rounded-lg border ${\n identityResult === null\n ? 'border-border-default bg-bg-sunken'\n : identityResult.ok\n ? 'border-status-success-border bg-status-success-bg-subtle'\n : 'border-status-error-border bg-status-error-bg-subtle'\n }`}\n >\n {validating && !identityResult ? (\n <Loader2\n className=\"size-5 text-text-secondary animate-spin\"\n aria-hidden=\"true\"\n />\n ) : identityResult?.ok ? (\n <CheckCircle2 className=\"size-5 text-status-success-text\" aria-hidden=\"true\" />\n ) : identityResult ? (\n <XCircle className=\"size-5 text-status-error-text\" aria-hidden=\"true\" />\n ) : (\n <div\n className=\"size-5 rounded-full border-2 border-border-default\"\n aria-hidden=\"true\"\n />\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.apiAccess', 'API Access')}\n </p>\n {identityResult?.ok && identityResult.data && (\n <p className=\"text-xs text-text-secondary break-words\">\n Host: {identityResult.data.hostname} | {identityResult.data.platform} /{' '}\n {identityResult.data.arch}\n </p>\n )}\n {identityResult && !identityResult.ok && (\n <p className=\"text-xs text-status-error-text break-words\">\n {identityResult.error}\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Retry button */}\n {!validating && (!healthResult?.ok || !identityResult?.ok) && (\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={validateAgent}>\n <RefreshCw className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('agentsPage.retryValidation', 'Retry validation')}\n </Button>\n )}\n\n {/* Detected info (editable overrides) — Name lives upfront\n now; this section shows the validate-time fields only. */}\n {isValidationPassed && (\n <div className=\"space-y-3 pt-2\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('agentsPage.detectedAgentInfo', 'Detected Agent Info')}\n </p>\n\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor={platformField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.platform', 'Platform')}\n </label>\n <Select value={detectedPlatform} onValueChange={setDetectedPlatform}>\n <SelectTrigger id={platformField.id} className=\"text-base md:text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"linux\">Linux</SelectItem>\n <SelectItem value=\"darwin\">macOS</SelectItem>\n <SelectItem value=\"windows\">Windows</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <label\n htmlFor={archField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('agentsPage.architecture', 'Architecture')}\n </label>\n <Select value={detectedArch} onValueChange={setDetectedArch}>\n <SelectTrigger id={archField.id} className=\"text-base md:text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"x86_64\">x86_64</SelectItem>\n <SelectItem value=\"arm64\">arm64</SelectItem>\n <SelectItem value=\"aarch64\">aarch64</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n </div>\n )}\n\n {/* Creation error */}\n {createError && (\n <div\n role=\"alert\"\n className=\"p-3 bg-status-error-bg-subtle border border-status-error-border rounded-lg\"\n >\n <div className=\"flex items-center gap-2\">\n <XCircle\n className=\"size-5 text-status-error-text flex-shrink-0\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm text-status-error-text break-words\">{createError}</p>\n </div>\n <div className=\"flex flex-wrap items-center gap-2 mt-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n setCreateError(null);\n // Reset form to allow entering different credentials\n setStage('details');\n setTunnelUrl('');\n setAgentApiKey('');\n setHealthResult(null);\n setIdentityResult(null);\n }}\n >\n <RefreshCw className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('agentsPage.tryDifferentAgent', 'Try Different Agent')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={onCancel}>\n {tr('agentsPage.close', 'Close')}\n </Button>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <ResponsiveDialogFooter className=\"pt-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => setStage('details')}>\n <ChevronLeft className=\"size-4\" aria-hidden=\"true\" />\n {tr('agentsPage.back', 'Back')}\n </Button>\n <Button\n type=\"button\"\n disabled={!isValidationPassed || isLoading || validating}\n onClick={handleCreateAgent}\n aria-busy={isLoading}\n >\n {isLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n {tr('agentsPage.adding', 'Adding...')}\n </>\n ) : (\n <>\n {tr('agentsPage.addAgent', 'Add Agent')}\n <ChevronRight className=\"size-4\" aria-hidden=\"true\" />\n </>\n )}\n </Button>\n </ResponsiveDialogFooter>\n </div>\n )}\n </MotionTabPanel>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -26,18 +26,15 @@ var g = {
26
26
  return n === e ? t : n;
27
27
  }, [x, S] = e(!1), C = g[i.visibility] ?? g.PRIVATE, w = C.icon;
28
28
  return /* @__PURE__ */ h(p.div, {
29
- whileHover: {
30
- y: -2,
31
- transition: {
32
- duration: .15,
33
- ease: [
34
- .4,
35
- 0,
36
- .2,
37
- 1
38
- ]
39
- }
40
- },
29
+ whileHover: { transition: {
30
+ duration: .15,
31
+ ease: [
32
+ .4,
33
+ 0,
34
+ .2,
35
+ 1
36
+ ]
37
+ } },
41
38
  whileTap: { scale: .98 },
42
39
  className: `bg-bg-surface border border-border-default rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${v}`,
43
40
  onClick: () => a?.(i),