@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.3
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.
- package/dist/components/actions/ActionCard.js +2 -2
- package/dist/components/actions/ActionCard.js.map +1 -1
- package/dist/components/actions/ActionTypeBadge.js +1 -1
- package/dist/components/actions/ActionTypeBadge.js.map +1 -1
- package/dist/components/actions/ExecutionHistory.js +1 -1
- package/dist/components/actions/ExecutionHistory.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
- package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerChatPane.js +2 -2
- package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerInputArea.js +4 -4
- package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerLayout.js +1 -1
- package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerMessageItem.js +1 -1
- package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerSidebar.js +5 -5
- package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTab.js +1 -1
- package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTabBar.js +1 -1
- package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
- package/dist/components/agent-manager/BridleConfigPanel.js +24 -24
- package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/PluginInstallModal.js +1 -1
- package/dist/components/agent-manager/PluginInstallModal.js.map +1 -1
- package/dist/components/agent-manager/ProfilesPanel.js +5 -5
- package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
- package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
- package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
- package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
- package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionConfigPanel.js +6 -6
- package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionLogsPanel.js +5 -5
- package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionToolsPanel.js +1 -1
- package/dist/components/agent-manager/detail/SessionToolsPanel.js.map +1 -1
- package/dist/components/agent-manager/dialogs/NewSessionDialog.js +2 -2
- package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
- package/dist/components/agent-manager/dialogs/RenameSessionDialog.js +1 -1
- package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
- package/dist/components/agents/AgentCard.js +14 -11
- package/dist/components/agents/AgentCard.js.map +1 -1
- package/dist/components/ai/AIContextsTab.js +1 -1
- package/dist/components/ai/AIContextsTab.js.map +1 -1
- package/dist/components/ai/AIOverviewTab.js +4 -4
- package/dist/components/ai/AIOverviewTab.js.map +1 -1
- package/dist/components/ai/AIPlanPanel.js +4 -4
- package/dist/components/ai/AIPlanPanel.js.map +1 -1
- package/dist/components/ai/AIPlaygroundTab.js +1 -1
- package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
- package/dist/components/ai/AIPromptsTab.js +2 -2
- package/dist/components/ai/AIPromptsTab.js.map +1 -1
- package/dist/components/ai/AIProviderCredentialsCard.js +4 -4
- package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
- package/dist/components/ai/AIQueueTab.js +4 -4
- package/dist/components/ai/AIQueueTab.js.map +1 -1
- package/dist/components/ai/AISessionsTab.js +4 -4
- package/dist/components/ai/AISessionsTab.js.map +1 -1
- package/dist/components/ai/AITasksTab.js +1 -1
- package/dist/components/ai/AITasksTab.js.map +1 -1
- package/dist/components/ai/ClaudeCredentialsCard.js +3 -3
- package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
- package/dist/components/ai/PluginHarnessPicker.js +3 -3
- package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
- package/dist/components/analytics/AnalyticsBreakdownChart.js +8 -8
- package/dist/components/analytics/AnalyticsBreakdownChart.js.map +1 -1
- package/dist/components/analytics/AnalyticsTimeSeriesChart.js +5 -5
- package/dist/components/analytics/AnalyticsTimeSeriesChart.js.map +1 -1
- package/dist/components/audit/AuditDeleteDialog.js +2 -2
- package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
- package/dist/components/audit/AuditExportButton.js +5 -5
- package/dist/components/audit/AuditExportButton.js.map +1 -1
- package/dist/components/audit/AuditLogDetail.js +9 -9
- package/dist/components/audit/AuditLogDetail.js.map +1 -1
- package/dist/components/audit/AuditLogFilters.js +1 -1
- package/dist/components/audit/AuditLogFilters.js.map +1 -1
- package/dist/components/audit/AuditLogStats.js +1 -1
- package/dist/components/audit/AuditLogStats.js.map +1 -1
- package/dist/components/audit/AuditLogTable.js +28 -28
- package/dist/components/audit/AuditLogTable.js.map +1 -1
- package/dist/components/catalog/CatalogCard.js +3 -3
- package/dist/components/catalog/CatalogCard.js.map +1 -1
- package/dist/components/catalog/CatalogForm.js +1 -1
- package/dist/components/catalog/CatalogForm.js.map +1 -1
- package/dist/components/common/ActionAgentTargetNotice.js +6 -6
- package/dist/components/common/ActionAgentTargetNotice.js.map +1 -1
- package/dist/components/common/CommandEditor.js +1 -1
- package/dist/components/common/CommandEditor.js.map +1 -1
- package/dist/components/docs/DocsPageTree.js +1 -1
- package/dist/components/docs/DocsPageTree.js.map +1 -1
- package/dist/components/docs/DocsPreviewPanel.js +1 -1
- package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
- package/dist/components/docs/DocsRevisionHistory.js +4 -4
- package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
- package/dist/components/docs/DocsSettingsPanel.js +7 -7
- package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
- package/dist/components/docs/DocsTabPanel.js +1 -1
- package/dist/components/docs/DocsTabPanel.js.map +1 -1
- package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
- package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
- package/dist/components/graph/MeshDeployWizard.js +3 -3
- package/dist/components/graph/MeshDeployWizard.js.map +1 -1
- package/dist/components/graph/MeshRequestPanel.js +2 -2
- package/dist/components/graph/MeshRequestPanel.js.map +1 -1
- package/dist/components/idp-components/ComponentCard.js +5 -5
- package/dist/components/idp-components/ComponentCard.js.map +1 -1
- package/dist/components/idp-components/ComponentForm.js +1 -1
- package/dist/components/idp-components/ComponentForm.js.map +1 -1
- package/dist/components/idp-components/DependencyGraphTab.js +7 -7
- package/dist/components/idp-components/DependencyGraphTab.js.map +1 -1
- package/dist/components/logs/LogViewer.js +14 -14
- package/dist/components/logs/LogViewer.js.map +1 -1
- package/dist/components/notes/NoteCard.js +1 -1
- package/dist/components/notes/NoteCard.js.map +1 -1
- package/dist/components/notes/NoteEditor.js +2 -2
- package/dist/components/notes/NoteEditor.js.map +1 -1
- package/dist/components/notes/NoteList.js +1 -1
- package/dist/components/notes/NoteList.js.map +1 -1
- package/dist/components/notes/NotesTabPanel.js +1 -1
- package/dist/components/notes/NotesTabPanel.js.map +1 -1
- package/dist/components/scorecard/ScorecardEmptyState.js +1 -1
- package/dist/components/scorecard/ScorecardEmptyState.js.map +1 -1
- package/dist/components/scorecard/ScorecardPanel.js +3 -3
- package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
- package/dist/components/security/SecurityScanList.js +11 -11
- package/dist/components/security/SecurityScanList.js.map +1 -1
- package/dist/components/sessions/SessionCard.js +26 -19
- package/dist/components/sessions/SessionCard.js.map +1 -1
- package/dist/components/sessions/SessionStatusBadge.js +7 -7
- package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
- package/dist/components/shared/AgentSelector.js +4 -4
- package/dist/components/shared/AgentSelector.js.map +1 -1
- package/dist/components/shared/AgentSetupBanner.js +4 -4
- package/dist/components/shared/AgentSetupBanner.js.map +1 -1
- package/dist/components/shared/Card.js +4 -4
- package/dist/components/shared/Card.js.map +1 -1
- package/dist/components/shared/ErrorState.js +15 -9
- package/dist/components/shared/ErrorState.js.map +1 -1
- package/dist/components/shared/FilterDropdown.js +2 -2
- package/dist/components/shared/FilterDropdown.js.map +1 -1
- package/dist/components/shared/PageSkeleton.js +6 -6
- package/dist/components/shared/PageSkeleton.js.map +1 -1
- package/dist/components/shared/SearchInput.js +1 -1
- package/dist/components/shared/SearchInput.js.map +1 -1
- package/dist/components/shared/StatCard.js +7 -7
- package/dist/components/shared/StatCard.js.map +1 -1
- package/dist/components/templates/PublishTemplateForm.js +2 -2
- package/dist/components/templates/PublishTemplateForm.js.map +1 -1
- package/dist/components/templates/TemplateCard.js +5 -5
- package/dist/components/templates/TemplateCard.js.map +1 -1
- package/dist/components/templates/TemplateWizard.js +2 -2
- package/dist/components/templates/TemplateWizard.js.map +1 -1
- package/dist/components/tunnels/DomainVerificationBadge.js +4 -4
- package/dist/components/tunnels/DomainVerificationBadge.js.map +1 -1
- package/dist/components/tunnels/ProviderBadge.js +1 -1
- package/dist/components/tunnels/ProviderBadge.js.map +1 -1
- package/dist/components/tunnels/TunnelCard.js +16 -16
- package/dist/components/tunnels/TunnelCard.js.map +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/components/tunnels/TunnelPublicAccessPanel.js +1 -1
- package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
- package/dist/components/tunnels/TunnelStatusBadge.js +4 -4
- package/dist/components/tunnels/TunnelStatusBadge.js.map +1 -1
- package/dist/components/vibecalendar/LogsDrawer.js +2 -2
- package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
- package/dist/components/vibecalendar/ScheduleTaskModal.js +5 -5
- package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalAgendaView.js +2 -2
- package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalDayView.js +3 -3
- package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalMonthView.js +1 -1
- package/dist/components/vibecalendar/VibeCalMonthView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalWeekView.js +4 -4
- package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalendarHeader.js +4 -4
- package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
- package/dist/components/vibecalendar/VibeTaskBlock.js +2 -2
- package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
- package/dist/components/vibecalendar/types.js +7 -7
- package/dist/components/vibecalendar/types.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonForm.js +9 -9
- package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonTile.js +6 -6
- package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckExecutionLog.js +10 -10
- package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckForm.js +8 -8
- package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +2 -2
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckSwitcher.js +8 -8
- package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
- package/dist/components/vibes/VibeCard.js +22 -18
- package/dist/components/vibes/VibeCard.js.map +1 -1
- package/dist/components/vibes/VibeList.js +16 -12
- package/dist/components/vibes/VibeList.js.map +1 -1
- package/dist/pages/ActionsPage.js +4 -3
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +9 -9
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentsLandingPage.js +4 -4
- package/dist/pages/AgentsLandingPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +14 -14
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/AnalyticsPage.js +8 -8
- package/dist/pages/AnalyticsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +11 -11
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/CatalogPage.js +12 -9
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +7 -7
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js +12 -9
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/EnvTopologyPage.js +2 -2
- package/dist/pages/EnvTopologyPage.js.map +1 -1
- package/dist/pages/GettingStartedPage.js +12 -12
- package/dist/pages/GettingStartedPage.js.map +1 -1
- package/dist/pages/HealthPage.js +12 -12
- package/dist/pages/HealthPage.js.map +1 -1
- package/dist/pages/LogsPage.js +4 -4
- package/dist/pages/LogsPage.js.map +1 -1
- package/dist/pages/NoteDetailsPage.js +5 -5
- package/dist/pages/NoteDetailsPage.js.map +1 -1
- package/dist/pages/OverviewPage.js +76 -43
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginsCatalogPage.js +7 -4
- package/dist/pages/PluginsCatalogPage.js.map +1 -1
- package/dist/pages/ScheduledJobDetailPage.js +7 -7
- package/dist/pages/ScheduledJobDetailPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +21 -21
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +20 -20
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/SettingsPage.js +64 -52
- package/dist/pages/SettingsPage.js.map +1 -1
- package/dist/pages/SharedSessionPage.js +21 -21
- package/dist/pages/SharedSessionPage.js.map +1 -1
- package/dist/pages/SharedVibeDeckPage.js +5 -5
- package/dist/pages/SharedVibeDeckPage.js.map +1 -1
- package/dist/pages/TargetDetailsPage.js +56 -65
- package/dist/pages/TargetDetailsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +4 -4
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +10 -10
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/dist/pages/TemplatesPage.js +6 -5
- package/dist/pages/TemplatesPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js +3 -3
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeCalendarPage.js +3 -3
- package/dist/pages/VibeCalendarPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +3 -3
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +15 -15
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/VibeTaskDetailPage.js +5 -5
- package/dist/pages/VibeTaskDetailPage.js.map +1 -1
- package/dist/pages/VibesPage.js +18 -18
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/WebhooksPage.js +11 -11
- package/dist/pages/WebhooksPage.js.map +1 -1
- package/dist/pages/ai/AIBookmarksPage.js +1 -1
- package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
- package/dist/pages/ai/AISessionDetailPage.js +6 -6
- package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
- package/dist/pages/tunnels/AuditTab.js +1 -1
- package/dist/pages/tunnels/AuditTab.js.map +1 -1
- package/dist/pages/tunnels/DomainsTab.js +1 -1
- package/dist/pages/tunnels/DomainsTab.js.map +1 -1
- package/dist/pages/tunnels/ProvidersTab.js +1 -1
- package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
- package/dist/pages/tunnels/SessionsTab.js +1 -1
- package/dist/pages/tunnels/SessionsTab.js.map +1 -1
- package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
- package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
- package/dist/pages/tunnels/UsageTab.js +1 -1
- package/dist/pages/tunnels/UsageTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +16 -16
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +1 -1
- package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +52 -49
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +9 -9
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -54,7 +54,7 @@ function p({ connection: p, availablePermissions: m, availableScopes: h, onSave:
|
|
|
54
54
|
type: "button",
|
|
55
55
|
"aria-pressed": x.has(e),
|
|
56
56
|
onClick: () => T(e),
|
|
57
|
-
className: `${f} ${x.has(e) ? "border-action-primary-bg bg-
|
|
57
|
+
className: `${f} ${x.has(e) ? "border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary" : "border-border-default text-text-secondary hover:bg-bg-sunken"}`,
|
|
58
58
|
children: e
|
|
59
59
|
}, e)), (m ?? []).length === 0 && /* @__PURE__ */ r("span", {
|
|
60
60
|
className: "text-xs text-text-muted",
|
|
@@ -74,7 +74,7 @@ function p({ connection: p, availablePermissions: m, availableScopes: h, onSave:
|
|
|
74
74
|
type: "button",
|
|
75
75
|
"aria-pressed": C.has(e),
|
|
76
76
|
onClick: () => E(e),
|
|
77
|
-
className: `${f} ${C.has(e) ? "border-action-primary-bg bg-
|
|
77
|
+
className: `${f} ${C.has(e) ? "border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary" : "border-border-default text-text-secondary hover:bg-bg-sunken"}`,
|
|
78
78
|
children: e
|
|
79
79
|
}, e)), (h ?? []).length === 0 && /* @__PURE__ */ r("span", {
|
|
80
80
|
className: "text-xs text-text-muted",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConnectionPermissionsDialog.js","names":[],"sources":["../../../src/components/graph/ConnectionPermissionsDialog.tsx"],"sourcesContent":["/**\n * ConnectionPermissionsDialog — Edit RBAC permissions and scopes for an agent connection edge.\n */\n\nimport { useId, useState } from 'react';\nimport {\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface Connection {\n id: string;\n protocol: string;\n permissions?: string[];\n scopes?: string[];\n label?: string | null;\n description?: string | null;\n sourceAgent: { id: string; name: string };\n target: { id: string; name: string };\n}\n\ninterface Props {\n connection: Connection;\n availablePermissions?: string[];\n availableScopes?: string[];\n onSave: (permissions: string[], scopes: string[]) => void;\n onClose: () => void;\n}\n\nconst CHIP_BASE_CLASS =\n 'rounded-full border px-3 py-1.5 text-xs font-medium transition-colors max-md:min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2';\n\nexport function ConnectionPermissionsDialog({\n connection,\n availablePermissions,\n availableScopes,\n onSave,\n onClose,\n}: Props) {\n const tr = useTr();\n const permissionsLabelId = useId();\n const scopesLabelId = useId();\n const [permissions, setPermissions] = useState<Set<string>>(\n new Set(connection.permissions || [])\n );\n const [scopes, setScopes] = useState<Set<string>>(new Set(connection.scopes || []));\n\n const togglePermission = (p: string) => {\n setPermissions((prev) => {\n const next = new Set(prev);\n if (next.has(p)) next.delete(p);\n else next.add(p);\n return next;\n });\n };\n\n const toggleScope = (s: string) => {\n setScopes((prev) => {\n const next = new Set(prev);\n if (next.has(s)) next.delete(s);\n else next.add(s);\n return next;\n });\n };\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('connectionPermissionsDialog.title', 'Connection Permissions')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n <span className=\"font-medium text-text-primary\">{connection.sourceAgent.name}</span>\n {' → '}\n <span className=\"font-medium text-text-primary\">{connection.target.name}</span>\n <span className=\"ml-2 rounded bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {connection.protocol}\n </span>\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4\">\n {/* Permissions */}\n <div role=\"group\" aria-labelledby={permissionsLabelId}>\n <span\n id={permissionsLabelId}\n className=\"mb-2 block text-sm font-medium text-text-primary\"\n >\n {tr('connectionPermissionsDialog.permissions', 'Permissions')}\n </span>\n <div className=\"flex flex-wrap gap-2\">\n {(availablePermissions ?? []).map((p) => (\n <button\n type=\"button\"\n key={p}\n aria-pressed={permissions.has(p)}\n onClick={() => togglePermission(p)}\n className={`${CHIP_BASE_CLASS} ${\n permissions.has(p)\n ? 'border-action-primary-bg bg-
|
|
1
|
+
{"version":3,"file":"ConnectionPermissionsDialog.js","names":[],"sources":["../../../src/components/graph/ConnectionPermissionsDialog.tsx"],"sourcesContent":["/**\n * ConnectionPermissionsDialog — Edit RBAC permissions and scopes for an agent connection edge.\n */\n\nimport { useId, useState } from 'react';\nimport {\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface Connection {\n id: string;\n protocol: string;\n permissions?: string[];\n scopes?: string[];\n label?: string | null;\n description?: string | null;\n sourceAgent: { id: string; name: string };\n target: { id: string; name: string };\n}\n\ninterface Props {\n connection: Connection;\n availablePermissions?: string[];\n availableScopes?: string[];\n onSave: (permissions: string[], scopes: string[]) => void;\n onClose: () => void;\n}\n\nconst CHIP_BASE_CLASS =\n 'rounded-full border px-3 py-1.5 text-xs font-medium transition-colors max-md:min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2';\n\nexport function ConnectionPermissionsDialog({\n connection,\n availablePermissions,\n availableScopes,\n onSave,\n onClose,\n}: Props) {\n const tr = useTr();\n const permissionsLabelId = useId();\n const scopesLabelId = useId();\n const [permissions, setPermissions] = useState<Set<string>>(\n new Set(connection.permissions || [])\n );\n const [scopes, setScopes] = useState<Set<string>>(new Set(connection.scopes || []));\n\n const togglePermission = (p: string) => {\n setPermissions((prev) => {\n const next = new Set(prev);\n if (next.has(p)) next.delete(p);\n else next.add(p);\n return next;\n });\n };\n\n const toggleScope = (s: string) => {\n setScopes((prev) => {\n const next = new Set(prev);\n if (next.has(s)) next.delete(s);\n else next.add(s);\n return next;\n });\n };\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('connectionPermissionsDialog.title', 'Connection Permissions')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n <span className=\"font-medium text-text-primary\">{connection.sourceAgent.name}</span>\n {' → '}\n <span className=\"font-medium text-text-primary\">{connection.target.name}</span>\n <span className=\"ml-2 rounded bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {connection.protocol}\n </span>\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4\">\n {/* Permissions */}\n <div role=\"group\" aria-labelledby={permissionsLabelId}>\n <span\n id={permissionsLabelId}\n className=\"mb-2 block text-sm font-medium text-text-primary\"\n >\n {tr('connectionPermissionsDialog.permissions', 'Permissions')}\n </span>\n <div className=\"flex flex-wrap gap-2\">\n {(availablePermissions ?? []).map((p) => (\n <button\n type=\"button\"\n key={p}\n aria-pressed={permissions.has(p)}\n onClick={() => togglePermission(p)}\n className={`${CHIP_BASE_CLASS} ${\n permissions.has(p)\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {p}\n </button>\n ))}\n {(availablePermissions ?? []).length === 0 && (\n <span className=\"text-xs text-text-muted\">\n {tr('connectionPermissionsDialog.noPermissions', 'No permissions configured')}\n </span>\n )}\n </div>\n </div>\n\n {/* Scopes */}\n <div role=\"group\" aria-labelledby={scopesLabelId}>\n <span id={scopesLabelId} className=\"mb-2 block text-sm font-medium text-text-primary\">\n {tr('connectionPermissionsDialog.scopes', 'Scopes')}\n </span>\n <div className=\"flex flex-wrap gap-2\">\n {(availableScopes ?? []).map((s) => (\n <button\n type=\"button\"\n key={s}\n aria-pressed={scopes.has(s)}\n onClick={() => toggleScope(s)}\n className={`${CHIP_BASE_CLASS} ${\n scopes.has(s)\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {s}\n </button>\n ))}\n {(availableScopes ?? []).length === 0 && (\n <span className=\"text-xs text-text-muted\">\n {tr('connectionPermissionsDialog.noScopes', 'No scopes configured')}\n </span>\n )}\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={onClose} className=\"max-md:min-h-11\">\n {tr('connectionPermissionsDialog.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => onSave(Array.from(permissions), Array.from(scopes))}\n className=\"max-md:min-h-11\"\n >\n {tr('connectionPermissionsDialog.save', 'Save Permissions')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -251,7 +251,7 @@ function y({ sourceAgents: y, initialSourceAgentId: ve, availablePermissions: b,
|
|
|
251
251
|
type: "button",
|
|
252
252
|
"aria-pressed": T === "ssh",
|
|
253
253
|
onClick: () => E("ssh"),
|
|
254
|
-
className: `${v} ${T === "ssh" ? "border-action-primary-bg bg-
|
|
254
|
+
className: `${v} ${T === "ssh" ? "border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary" : "border-border-default text-text-secondary hover:bg-bg-sunken"}`,
|
|
255
255
|
children: [/* @__PURE__ */ s(ae, {
|
|
256
256
|
className: "size-3.5",
|
|
257
257
|
"aria-hidden": "true"
|
|
@@ -260,7 +260,7 @@ function y({ sourceAgents: y, initialSourceAgentId: ve, availablePermissions: b,
|
|
|
260
260
|
type: "button",
|
|
261
261
|
"aria-pressed": T === "same",
|
|
262
262
|
onClick: () => E("same"),
|
|
263
|
-
className: `${v} ${T === "same" ? "border-action-primary-bg bg-
|
|
263
|
+
className: `${v} ${T === "same" ? "border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary" : "border-border-default text-text-secondary hover:bg-bg-sunken"}`,
|
|
264
264
|
children: [/* @__PURE__ */ s(ie, {
|
|
265
265
|
className: "size-3.5",
|
|
266
266
|
"aria-hidden": "true"
|
|
@@ -368,7 +368,7 @@ function y({ sourceAgents: y, initialSourceAgentId: ve, availablePermissions: b,
|
|
|
368
368
|
type: "button",
|
|
369
369
|
"aria-pressed": F.has(e),
|
|
370
370
|
onClick: () => De(e),
|
|
371
|
-
className: `${_e} ${F.has(e) ? "border-action-primary-bg bg-
|
|
371
|
+
className: `${_e} ${F.has(e) ? "border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary" : "border-border-default text-text-secondary hover:bg-bg-sunken"}`,
|
|
372
372
|
children: e
|
|
373
373
|
}, e))
|
|
374
374
|
})] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeshDeployWizard.js","names":[],"sources":["../../../src/components/graph/MeshDeployWizard.tsx"],"sourcesContent":["/**\n * MeshDeployWizard — deploy a new agent THROUGH the mesh from a chosen source\n * (root) agent. Supports two modes:\n * - \"Remote (SSH)\": deploy onto another machine over SSH (host/user/port/key).\n * - \"Same machine\": deploy a sibling agent on the source agent's own host.\n *\n * Submitting calls `deployAgentViaMesh({ rootAgentId, input })`, reads the\n * `{ jobId }` out of the AgentProxyResponse, then polls\n * `meshDeployJobStatus(rootAgentId, jobId)` on a 2s interval, rendering the\n * job's step list in a progress panel. On completion it calls `onCompleted`\n * so the caller can refetch the mesh graph.\n */\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { Loader2, CheckCircle, XCircle, Check, Server, Laptop } from 'lucide-react';\nimport {\n Button,\n Checkbox,\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 { useTr } from '../../shared/hooks/useTr';\nimport { useField } from '../../shared/hooks/useField';\nimport {\n useDeployAgentViaMeshMutation,\n useMeshDeployJobStatusLazyQuery,\n} from '@/generated/wspace-operations';\n\n// English fallbacks for the mesh-deploy job step labels. Extends the\n// install-job labels with the mesh-specific bootstrap/onboard/peer-probe\n// steps. Translated via `mesh.deploy.steps.{step}` keys.\nconst stepLabels: Record<string, string> = {\n probe: 'Probing target',\n 'install-bun': 'Installing Bun runtime',\n 'install-agent': 'Installing VibeControls Agent',\n autostart: 'Configuring autostart',\n plugins: 'Installing plugins',\n start: 'Starting agent',\n bootstrap: 'Bootstrapping tunnel',\n onboard: 'Onboarding agent',\n 'store-creds': 'Storing credentials',\n 'report-link': 'Linking into mesh',\n 'probe-peer': 'Probing new peer',\n};\n\ninterface DeployJobStep {\n step: string;\n status: string;\n message?: string;\n}\n\ninterface DeployJob {\n status: 'running' | 'completed' | 'failed' | string;\n steps: DeployJobStep[];\n error?: string;\n}\n\ninterface SourceAgentOption {\n id: string;\n name: string;\n hostname: string;\n isActive: boolean;\n}\n\ninterface MeshDeployWizardProps {\n /** Agents that can act as the deploying root. */\n sourceAgents: SourceAgentOption[];\n /** Pre-seeded source (root) agent id (e.g. \"deploy from here\" on a node). */\n initialSourceAgentId?: string | null;\n availablePermissions?: string[];\n onClose: () => void;\n onCompleted: () => void;\n}\n\ntype DeployMode = 'ssh' | 'same';\ntype AuthMethod = 'agent' | 'key';\n\nfunction asRecord(v: unknown): Record<string, unknown> | null {\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null;\n}\nfunction jstr(obj: Record<string, unknown> | null, key: string): string | null {\n const v = obj?.[key];\n return typeof v === 'string' ? v : null;\n}\n\nconst LABEL_CLASS = 'mb-1.5 block text-sm font-medium text-text-primary';\nconst CHIP_BASE_CLASS =\n 'rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors max-md:min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2';\nconst MODE_BUTTON_BASE_CLASS =\n 'inline-flex flex-1 items-center justify-center gap-2 rounded-md border px-3 py-2 text-sm transition-colors max-md:min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2';\n\nexport function MeshDeployWizard({\n sourceAgents,\n initialSourceAgentId,\n availablePermissions,\n onClose,\n onCompleted,\n}: MeshDeployWizardProps) {\n const tr = useTr();\n\n const [sourceAgentId, setSourceAgentId] = useState<string>(\n initialSourceAgentId ?? sourceAgents[0]?.id ?? ''\n );\n const [mode, setMode] = useState<DeployMode>('ssh');\n const [host, setHost] = useState('');\n const [port, setPort] = useState(22);\n const [user, setUser] = useState('');\n const [authMethod, setAuthMethod] = useState<AuthMethod>('agent');\n const [keyPath, setKeyPath] = useState('');\n const [label, setLabel] = useState('');\n const [connectExisting, setConnectExisting] = useState(false);\n const [permissions, setPermissions] = useState<Set<string>>(new Set());\n\n const [jobId, setJobId] = useState<string | null>(null);\n const [job, setJob] = useState<DeployJob | null>(null);\n const [submitError, setSubmitError] = useState<string | null>(null);\n\n const sourceField = useField();\n const hostField = useField();\n const portField = useField();\n const userField = useField();\n const keyField = useField();\n const labelField = useField();\n\n const [deploy, { loading: deploying }] = useDeployAgentViaMeshMutation();\n const [fetchJobStatus] = useMeshDeployJobStatusLazyQuery({ fetchPolicy: 'network-only' });\n\n const canSubmit = useMemo(() => {\n if (!sourceAgentId) return false;\n if (mode === 'ssh') {\n const targetSpec = host.trim();\n return targetSpec.length > 0 && user.trim().length > 0;\n }\n return true;\n }, [sourceAgentId, mode, host, user]);\n\n const togglePermission = useCallback((p: string) => {\n setPermissions((prev) => {\n const next = new Set(prev);\n if (next.has(p)) next.delete(p);\n else next.add(p);\n return next;\n });\n }, []);\n\n // Build the SSH `target` string the backend expects (user@host:port).\n const buildTargetString = useCallback((): string => {\n if (mode === 'same') return 'localhost';\n const trimmedHost = host.trim();\n const trimmedUser = user.trim();\n const base = trimmedUser ? `${trimmedUser}@${trimmedHost}` : trimmedHost;\n return port && port !== 22 ? `${base}:${port}` : base;\n }, [mode, host, user, port]);\n\n const handleSubmit = useCallback(async () => {\n if (!canSubmit) return;\n setSubmitError(null);\n setJob(null);\n setJobId(null);\n try {\n const res = await deploy({\n variables: {\n rootAgentId: sourceAgentId,\n input: {\n target: buildTargetString(),\n permissions: permissions.size > 0 ? Array.from(permissions) : undefined,\n identityFile: mode === 'ssh' && authMethod === 'key' ? keyPath || undefined : undefined,\n label: label || undefined,\n connectExisting,\n },\n },\n });\n const proxy = res.data?.deployAgentViaMesh;\n if (res.error || proxy?.error || proxy?.ok === false) {\n setSubmitError(\n proxy?.error ?? res.error?.message ?? tr('mesh.deploy.failed', 'Deploy failed')\n );\n return;\n }\n const data = asRecord(proxy?.data);\n const newJobId = jstr(data, 'jobId');\n if (!newJobId) {\n setSubmitError(\n tr('mesh.deploy.noJob', 'Deploy started but no job id was returned by the agent.')\n );\n return;\n }\n setJobId(newJobId);\n setJob({ status: jstr(data, 'status') ?? 'running', steps: [] });\n } catch (err) {\n setSubmitError(err instanceof Error ? err.message : String(err));\n }\n }, [\n canSubmit,\n deploy,\n sourceAgentId,\n buildTargetString,\n permissions,\n mode,\n authMethod,\n keyPath,\n label,\n connectExisting,\n tr,\n ]);\n\n // Poll the mesh-deploy job once a jobId is established.\n useEffect(() => {\n if (!jobId || !sourceAgentId) return;\n let cancelled = false;\n\n const poll = () => {\n void fetchJobStatus({ variables: { rootAgentId: sourceAgentId, jobId } })\n .then((result) => {\n if (cancelled) return;\n const proxy = result.data?.meshDeployJobStatus;\n const data = asRecord(proxy?.data);\n if (!data) return;\n const rawSteps = Array.isArray(data.steps) ? (data.steps as DeployJobStep[]) : [];\n setJob({\n status: jstr(data, 'status') ?? 'running',\n steps: rawSteps,\n error: jstr(data, 'error') ?? proxy?.error ?? undefined,\n });\n const status = jstr(data, 'status');\n if (status === 'completed') {\n onCompleted();\n }\n })\n .catch(() => {\n // transient — retry on next interval\n });\n };\n\n poll();\n const interval = setInterval(poll, 2000);\n return () => {\n cancelled = true;\n clearInterval(interval);\n };\n }, [jobId, sourceAgentId, fetchJobStatus, onCompleted]);\n\n const jobRunning =\n job?.status === 'running' ||\n (jobId !== null && job?.status !== 'completed' && job?.status !== 'failed');\n\n const wizardSteps = [\n { key: 'configure', label: tr('mesh.deploy.steps.configure', 'Configure') },\n { key: 'deploy', label: tr('mesh.deploy.steps.deploy', 'Deploy') },\n ];\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !jobRunning) onClose();\n }}\n >\n <ResponsiveDialogContent\n className=\"max-h-[min(85vh,44rem)] overflow-y-auto sm:max-w-lg\"\n showCloseButton={!jobRunning}\n >\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('mesh.deploy.title', 'Deploy Agent via Mesh')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'mesh.deploy.description',\n 'Deploy a new agent through the mesh from a selected source agent.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <Stepper steps={wizardSteps} active={jobId ? 1 : 0} />\n\n {/* Job progress takes over once a job is running */}\n {jobId ? (\n <div className=\"space-y-3\">\n <p role=\"status\" className=\"text-sm text-text-secondary\">\n {tr('mesh.deploy.deploying', 'Deploying through {{agent}}…', {\n agent: sourceAgents.find((a) => a.id === sourceAgentId)?.name ?? 'agent',\n })}\n </p>\n <div className=\"space-y-2\">\n {job && job.steps.length > 0 ? (\n job.steps.map((step, idx) => (\n <div key={`${step.step}-${idx}`} className=\"flex items-center gap-3\">\n {step.status === 'running' && (\n <Loader2 className=\"size-4 animate-spin text-text-link\" aria-hidden=\"true\" />\n )}\n {step.status === 'completed' && (\n <CheckCircle className=\"size-4 text-status-success-text\" aria-hidden=\"true\" />\n )}\n {step.status === 'failed' && (\n <XCircle className=\"size-4 text-status-error-text\" aria-hidden=\"true\" />\n )}\n {step.status === 'skipped' && (\n <Check className=\"size-4 text-text-muted\" aria-hidden=\"true\" />\n )}\n {step.status === 'pending' && (\n <div\n className=\"size-4 rounded-full border-2 border-border-default\"\n aria-hidden=\"true\"\n />\n )}\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {tr(`mesh.deploy.steps.${step.step}`, stepLabels[step.step] ?? step.step)}\n </div>\n {step.message ? (\n <div className=\"text-xs break-words text-text-muted\">{step.message}</div>\n ) : null}\n </div>\n </div>\n ))\n ) : (\n <div className=\"flex items-center gap-3\">\n <Loader2 className=\"size-4 animate-spin text-text-link\" aria-hidden=\"true\" />\n <span className=\"text-sm text-text-secondary\">\n {tr('mesh.deploy.starting', 'Starting deploy job…')}\n </span>\n </div>\n )}\n </div>\n\n {job?.status === 'completed' && (\n <div\n role=\"status\"\n className=\"rounded-lg border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text\"\n >\n {tr('mesh.deploy.success', 'Agent deployed successfully!')}\n </div>\n )}\n {job?.status === 'failed' && (\n <div\n role=\"alert\"\n className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\"\n >\n {job.error ?? tr('mesh.deploy.jobFailed', 'Deploy failed.')}\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onClose}\n disabled={jobRunning}\n aria-busy={jobRunning}\n className=\"w-full max-md:min-h-11\"\n >\n {jobRunning\n ? tr('mesh.deploy.deployingShort', 'Deploying…')\n : tr('common.close', 'Close')}\n </Button>\n </ResponsiveDialogFooter>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Source agent */}\n <div>\n <label className={LABEL_CLASS} htmlFor={sourceField.id}>\n {tr('mesh.deploy.sourceAgent', 'Source agent (deploys from)')}\n </label>\n <Select\n value={sourceAgentId || undefined}\n onValueChange={setSourceAgentId}\n disabled={sourceAgents.length === 0}\n >\n <SelectTrigger id={sourceField.id} className=\"w-full text-base md:text-sm\">\n <SelectValue placeholder={tr('mesh.deploy.noAgents', 'No agents available')} />\n </SelectTrigger>\n <SelectContent>\n {sourceAgents.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name} ({a.hostname}){a.isActive ? '' : ' · offline'}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Mode toggle */}\n <div>\n <span className={LABEL_CLASS}>{tr('mesh.deploy.mode', 'Deploy mode')}</span>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n aria-pressed={mode === 'ssh'}\n onClick={() => setMode('ssh')}\n className={`${MODE_BUTTON_BASE_CLASS} ${\n mode === 'ssh'\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n <Server className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('mesh.deploy.modeSsh', 'Remote (SSH)')}\n </button>\n <button\n type=\"button\"\n aria-pressed={mode === 'same'}\n onClick={() => setMode('same')}\n className={`${MODE_BUTTON_BASE_CLASS} ${\n mode === 'same'\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n <Laptop className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('mesh.deploy.modeSame', 'Same machine')}\n </button>\n </div>\n </div>\n\n {/* SSH fields */}\n {mode === 'ssh' && (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-3\">\n <div className=\"sm:col-span-2\">\n <label className={LABEL_CLASS} htmlFor={hostField.id}>\n {tr('mesh.deploy.host', 'Host / alias')}\n </label>\n <Input\n {...hostField.inputProps}\n type=\"text\"\n value={host}\n onChange={(e) => setHost(e.target.value)}\n placeholder=\"prod-web-01 or 10.0.0.5\"\n className=\"font-mono text-base md:text-sm\"\n />\n </div>\n <div>\n <label className={LABEL_CLASS} htmlFor={portField.id}>\n {tr('mesh.deploy.port', 'Port')}\n </label>\n <Input\n {...portField.inputProps}\n type=\"number\"\n value={port}\n onChange={(e) => setPort(Number(e.target.value) || 22)}\n className=\"text-base md:text-sm\"\n />\n </div>\n </div>\n <div>\n <label className={LABEL_CLASS} htmlFor={userField.id}>\n {tr('mesh.deploy.user', 'User')}\n </label>\n <Input\n {...userField.inputProps}\n type=\"text\"\n value={user}\n onChange={(e) => setUser(e.target.value)}\n placeholder=\"root\"\n className=\"font-mono text-base md:text-sm\"\n />\n </div>\n <div>\n <span className={LABEL_CLASS}>{tr('mesh.deploy.auth', 'Authentication')}</span>\n <div className=\"flex flex-wrap gap-x-4 gap-y-2\">\n <label className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-text-primary max-md:min-h-11\">\n <input\n type=\"radio\"\n name=\"mesh-auth\"\n checked={authMethod === 'agent'}\n onChange={() => setAuthMethod('agent')}\n />\n {tr('mesh.deploy.authAgent', 'SSH agent / default key')}\n </label>\n <label className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-text-primary max-md:min-h-11\">\n <input\n type=\"radio\"\n name=\"mesh-auth\"\n checked={authMethod === 'key'}\n onChange={() => setAuthMethod('key')}\n />\n {tr('mesh.deploy.authKey', 'Key path')}\n </label>\n </div>\n </div>\n {authMethod === 'key' && (\n <div>\n <label className={LABEL_CLASS} htmlFor={keyField.id}>\n {tr('mesh.deploy.keyPath', 'Identity file (key path)')}\n </label>\n <Input\n {...keyField.inputProps}\n type=\"text\"\n value={keyPath}\n onChange={(e) => setKeyPath(e.target.value)}\n placeholder=\"~/.ssh/id_ed25519\"\n className=\"font-mono text-base md:text-sm\"\n />\n </div>\n )}\n </div>\n )}\n\n {/* Label */}\n <div>\n <label className={LABEL_CLASS} htmlFor={labelField.id}>\n {tr('mesh.deploy.label', 'Label (optional)')}\n </label>\n <Input\n {...labelField.inputProps}\n type=\"text\"\n value={label}\n onChange={(e) => setLabel(e.target.value)}\n placeholder={tr('mesh.deploy.labelPlaceholder', 'e.g. Production web tier')}\n className=\"text-base md:text-sm\"\n />\n </div>\n\n {/* Permission chips */}\n {availablePermissions && availablePermissions.length > 0 && (\n <div>\n <span className={LABEL_CLASS}>\n {tr('mesh.deploy.permissions', 'Grant permissions on the new link')}\n </span>\n <div className=\"flex flex-wrap gap-2\">\n {availablePermissions.map((p) => (\n <button\n type=\"button\"\n key={p}\n aria-pressed={permissions.has(p)}\n onClick={() => togglePermission(p)}\n className={`${CHIP_BASE_CLASS} ${\n permissions.has(p)\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {p}\n </button>\n ))}\n </div>\n </div>\n )}\n\n <label className=\"flex cursor-pointer items-start gap-2 text-sm text-text-primary\">\n <Checkbox\n checked={connectExisting}\n onCheckedChange={(checked) => setConnectExisting(checked === true)}\n className=\"mt-0.5\"\n />\n <span>\n {tr(\n 'mesh.deploy.connectExisting',\n 'Connect to an existing agent on the target instead of installing'\n )}\n </span>\n </label>\n\n {submitError && (\n <div\n role=\"alert\"\n className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\"\n >\n {submitError}\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={onClose} className=\"max-md:min-h-11\">\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleSubmit()}\n disabled={!canSubmit || deploying}\n aria-busy={deploying}\n className=\"max-md:min-h-11\"\n >\n {deploying ? <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" /> : null}\n {tr('mesh.deploy.submit', 'Deploy')}\n </Button>\n </ResponsiveDialogFooter>\n </div>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;;AA0CA,IAAM,KAAqC;CACzC,OAAO;CACP,eAAe;CACf,iBAAiB;CACjB,WAAW;CACX,SAAS;CACT,OAAO;CACP,WAAW;CACX,SAAS;CACT,eAAe;CACf,eAAe;CACf,cAAc;CACf;AAkCD,SAAS,EAAS,GAA4C;AAC5D,QAAO,KAAK,OAAO,KAAM,YAAY,CAAC,MAAM,QAAQ,EAAE,GAAI,IAAgC;;AAE5F,SAAS,EAAK,GAAqC,GAA4B;CAC7E,IAAM,IAAI,IAAM;AAChB,QAAO,OAAO,KAAM,WAAW,IAAI;;AAGrC,IAAM,IAAc,sDACd,KACJ,mMACI,IACJ;AAEF,SAAgB,EAAiB,EAC/B,iBACA,0BACA,yBACA,YACA,kBACwB;CACxB,IAAM,IAAK,GAAO,EAEZ,CAAC,GAAe,MAAoB,EACxC,MAAwB,EAAa,IAAI,MAAM,GAChD,EACK,CAAC,GAAM,KAAW,EAAqB,MAAM,EAC7C,CAAC,GAAM,MAAW,EAAS,GAAG,EAC9B,CAAC,GAAM,MAAW,EAAS,GAAG,EAC9B,CAAC,GAAM,MAAW,EAAS,GAAG,EAC9B,CAAC,GAAY,KAAiB,EAAqB,QAAQ,EAC3D,CAAC,GAAS,MAAc,EAAS,GAAG,EACpC,CAAC,GAAO,MAAY,EAAS,GAAG,EAChC,CAAC,GAAiB,MAAsB,EAAS,GAAM,EACvD,CAAC,GAAa,MAAkB,kBAAsB,IAAI,KAAK,CAAC,EAEhE,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAK,KAAU,EAA2B,KAAK,EAChD,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAE7D,IAAc,GAAU,EACxB,IAAY,GAAU,EACtB,IAAY,GAAU,EACtB,IAAY,GAAU,EACtB,IAAW,GAAU,EACrB,IAAa,GAAU,EAEvB,CAAC,GAAQ,EAAE,SAAS,OAAe,GAA+B,EAClE,CAAC,KAAkB,EAAgC,EAAE,aAAa,gBAAgB,CAAC,EAEnF,IAAY,SACX,IACD,MAAS,QACQ,EAAK,MAAM,CACZ,SAAS,KAAK,EAAK,MAAM,CAAC,SAAS,IAEhD,KALoB,IAM1B;EAAC;EAAe;EAAM;EAAM;EAAK,CAAC,EAE/B,KAAmB,GAAa,MAAc;AAClD,MAAgB,MAAS;GACvB,IAAM,IAAO,IAAI,IAAI,EAAK;AAG1B,UAFI,EAAK,IAAI,EAAE,GAAE,EAAK,OAAO,EAAE,GAC1B,EAAK,IAAI,EAAE,EACT;IACP;IACD,EAAE,CAAC,EAGA,IAAoB,QAA0B;AAClD,MAAI,MAAS,OAAQ,QAAO;EAC5B,IAAM,IAAc,EAAK,MAAM,EACzB,IAAc,EAAK,MAAM,EACzB,IAAO,IAAc,GAAG,EAAY,GAAG,MAAgB;AAC7D,SAAO,KAAQ,MAAS,KAAK,GAAG,EAAK,GAAG,MAAS;IAChD;EAAC;EAAM;EAAM;EAAM;EAAK,CAAC,EAEtB,KAAe,EAAY,YAAY;AACtC,SAGL;GAFA,EAAe,KAAK,EACpB,EAAO,KAAK,EACZ,EAAS,KAAK;AACd,OAAI;IACF,IAAM,IAAM,MAAM,EAAO,EACvB,WAAW;KACT,aAAa;KACb,OAAO;MACL,QAAQ,GAAmB;MAC3B,aAAa,EAAY,OAAO,IAAI,MAAM,KAAK,EAAY,GAAG,KAAA;MAC9D,cAAc,MAAS,SAAS,MAAe,SAAQ,KAAuB,KAAA;MAC9E,OAAO,KAAS,KAAA;MAChB;MACD;KACF,EACF,CAAC,EACI,IAAQ,EAAI,MAAM;AACxB,QAAI,EAAI,SAAS,GAAO,SAAS,GAAO,OAAO,IAAO;AACpD,OACE,GAAO,SAAS,EAAI,OAAO,WAAW,EAAG,sBAAsB,gBAAgB,CAChF;AACD;;IAEF,IAAM,IAAO,EAAS,GAAO,KAAK,EAC5B,IAAW,EAAK,GAAM,QAAQ;AACpC,QAAI,CAAC,GAAU;AACb,OACE,EAAG,qBAAqB,0DAA0D,CACnF;AACD;;AAGF,IADA,EAAS,EAAS,EAClB,EAAO;KAAE,QAAQ,EAAK,GAAM,SAAS,IAAI;KAAW,OAAO,EAAE;KAAE,CAAC;YACzD,GAAK;AACZ,MAAe,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI,CAAC;;;IAEjE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAGF,UAAgB;AACd,MAAI,CAAC,KAAS,CAAC,EAAe;EAC9B,IAAI,IAAY,IAEV,UAAa;AACZ,KAAe,EAAE,WAAW;IAAE,aAAa;IAAe;IAAO,EAAE,CAAC,CACtE,MAAM,MAAW;AAChB,QAAI,EAAW;IACf,IAAM,IAAQ,EAAO,MAAM,qBACrB,IAAO,EAAS,GAAO,KAAK;AAClC,QAAI,CAAC,EAAM;IACX,IAAM,IAAW,MAAM,QAAQ,EAAK,MAAM,GAAI,EAAK,QAA4B,EAAE;AAOjF,IANA,EAAO;KACL,QAAQ,EAAK,GAAM,SAAS,IAAI;KAChC,OAAO;KACP,OAAO,EAAK,GAAM,QAAQ,IAAI,GAAO,SAAS,KAAA;KAC/C,CAAC,EACa,EAAK,GAAM,SAAS,KACpB,eACb,GAAa;KAEf,CACD,YAAY,GAEX;;AAGN,KAAM;EACN,IAAM,IAAW,YAAY,GAAM,IAAK;AACxC,eAAa;AAEX,GADA,IAAY,IACZ,cAAc,EAAS;;IAExB;EAAC;EAAO;EAAe;EAAgB;EAAY,CAAC;CAEvD,IAAM,IACJ,GAAK,WAAW,aACf,MAAU,QAAQ,GAAK,WAAW,eAAe,GAAK,WAAW,UAE9D,KAAc,CAClB;EAAE,KAAK;EAAa,OAAO,EAAG,+BAA+B,YAAY;EAAE,EAC3E;EAAE,KAAK;EAAU,OAAO,EAAG,4BAA4B,SAAS;EAAE,CACnE;AAED,QACE,kBAAC,GAAD;EACE,MAAA;EACA,eAAe,MAAS;AACtB,GAAI,CAAC,KAAQ,CAAC,KAAY,GAAS;;YAGrC,kBAAC,IAAD;GACE,WAAU;GACV,iBAAiB,CAAC;aAFpB;IAIE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UACG,EAAG,qBAAqB,wBAAwB,EAC3B,CAAA,EACxB,kBAAC,IAAD,EAAA,UACG,EACC,2BACA,oEACD,EAC2B,CAAA,CACP,EAAA,CAAA;IAEzB,kBAAC,IAAD;KAAS,OAAO;KAAa,QAAQ;KAAiB,CAAA;IAGrD,IACC,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,KAAD;OAAG,MAAK;OAAS,WAAU;iBACxB,EAAG,yBAAyB,gCAAgC,EAC3D,OAAO,EAAa,MAAM,MAAM,EAAE,OAAO,EAAc,EAAE,QAAQ,SAClE,CAAC;OACA,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,KAAO,EAAI,MAAM,SAAS,IACzB,EAAI,MAAM,KAAK,GAAM,MACnB,kBAAC,OAAD;QAAiC,WAAU;kBAA3C;SACG,EAAK,WAAW,aACf,kBAAC,GAAD;UAAS,WAAU;UAAqC,eAAY;UAAS,CAAA;SAE9E,EAAK,WAAW,eACf,kBAAC,IAAD;UAAa,WAAU;UAAkC,eAAY;UAAS,CAAA;SAE/E,EAAK,WAAW,YACf,kBAAC,IAAD;UAAS,WAAU;UAAgC,eAAY;UAAS,CAAA;SAEzE,EAAK,WAAW,aACf,kBAAC,IAAD;UAAO,WAAU;UAAyB,eAAY;UAAS,CAAA;SAEhE,EAAK,WAAW,aACf,kBAAC,OAAD;UACE,WAAU;UACV,eAAY;UACZ,CAAA;SAEJ,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBACZ,EAAG,qBAAqB,EAAK,QAAQ,GAAW,EAAK,SAAS,EAAK,KAAK;WACrE,CAAA,EACL,EAAK,UACJ,kBAAC,OAAD;WAAK,WAAU;qBAAuC,EAAK;WAAc,CAAA,GACvE,KACA;;SACF;UA3BI,GAAG,EAAK,KAAK,GAAG,IA2BpB,CACN,GAEF,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAS,WAAU;SAAqC,eAAY;SAAS,CAAA,EAC7E,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,wBAAwB,uBAAuB;SAC9C,CAAA,CACH;;OAEJ,CAAA;MAEL,GAAK,WAAW,eACf,kBAAC,OAAD;OACE,MAAK;OACL,WAAU;iBAET,EAAG,uBAAuB,+BAA+B;OACtD,CAAA;MAEP,GAAK,WAAW,YACf,kBAAC,OAAD;OACE,MAAK;OACL,WAAU;iBAET,EAAI,SAAS,EAAG,yBAAyB,iBAAiB;OACvD,CAAA;MAGR,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;OACE,MAAK;OACL,SAAQ;OACR,SAAS;OACT,UAAU;OACV,aAAW;OACX,WAAU;iBAET,IACG,EAAG,8BAA8B,aAAa,GAC9C,EAAG,gBAAgB,QAAQ;OACxB,CAAA,EACc,CAAA;MACrB;SAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,WAAW;OAAa,SAAS,EAAY;iBACjD,EAAG,2BAA2B,8BAA8B;OACvD,CAAA,EACR,kBAAC,GAAD;OACE,OAAO,KAAiB,KAAA;OACxB,eAAe;OACf,UAAU,EAAa,WAAW;iBAHpC,CAKE,kBAAC,IAAD;QAAe,IAAI,EAAY;QAAI,WAAU;kBAC3C,kBAAC,IAAD,EAAa,aAAa,EAAG,wBAAwB,sBAAsB,EAAI,CAAA;QACjE,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,EAAa,KAAK,MACjB,kBAAC,IAAD;QAAuB,OAAO,EAAE;kBAAhC;SACG,EAAE;SAAK;SAAG,EAAE;SAAS;SAAE,EAAE,WAAW,KAAK;SAC/B;UAFI,EAAE,GAEN,CACb,EACY,CAAA,CACT;SACL,EAAA,CAAA;MAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;OAAM,WAAW;iBAAc,EAAG,oBAAoB,cAAc;OAAQ,CAAA,EAC5E,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,gBAAc,MAAS;QACvB,eAAe,EAAQ,MAAM;QAC7B,WAAW,GAAG,EAAuB,GACnC,MAAS,QACL,4EACA;kBAPR,CAUE,kBAAC,IAAD;SAAQ,WAAU;SAAW,eAAY;SAAS,CAAA,EACjD,EAAG,uBAAuB,eAAe,CACnC;WACT,kBAAC,UAAD;QACE,MAAK;QACL,gBAAc,MAAS;QACvB,eAAe,EAAQ,OAAO;QAC9B,WAAW,GAAG,EAAuB,GACnC,MAAS,SACL,4EACA;kBAPR,CAUE,kBAAC,IAAD;SAAQ,WAAU;SAAW,eAAY;SAAS,CAAA,EACjD,EAAG,wBAAwB,eAAe,CACpC;UACL;SACF,EAAA,CAAA;MAGL,MAAS,SACR,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,SAAD;WAAO,WAAW;WAAa,SAAS,EAAU;qBAC/C,EAAG,oBAAoB,eAAe;WACjC,CAAA,EACR,kBAAC,GAAD;WACE,GAAI,EAAU;WACd,MAAK;WACL,OAAO;WACP,WAAW,MAAM,GAAQ,EAAE,OAAO,MAAM;WACxC,aAAY;WACZ,WAAU;WACV,CAAA,CACE;aACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UAAO,WAAW;UAAa,SAAS,EAAU;oBAC/C,EAAG,oBAAoB,OAAO;UACzB,CAAA,EACR,kBAAC,GAAD;UACE,GAAI,EAAU;UACd,MAAK;UACL,OAAO;UACP,WAAW,MAAM,GAAQ,OAAO,EAAE,OAAO,MAAM,IAAI,GAAG;UACtD,WAAU;UACV,CAAA,CACE,EAAA,CAAA,CACF;;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAW;SAAa,SAAS,EAAU;mBAC/C,EAAG,oBAAoB,OAAO;SACzB,CAAA,EACR,kBAAC,GAAD;SACE,GAAI,EAAU;SACd,MAAK;SACL,OAAO;SACP,WAAW,MAAM,GAAQ,EAAE,OAAO,MAAM;SACxC,aAAY;SACZ,WAAU;SACV,CAAA,CACE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAW;mBAAc,EAAG,oBAAoB,iBAAiB;SAAQ,CAAA,EAC/E,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,SAAD;UAAO,WAAU;oBAAjB,CACE,kBAAC,SAAD;WACE,MAAK;WACL,MAAK;WACL,SAAS,MAAe;WACxB,gBAAgB,EAAc,QAAQ;WACtC,CAAA,EACD,EAAG,yBAAyB,0BAA0B,CACjD;aACR,kBAAC,SAAD;UAAO,WAAU;oBAAjB,CACE,kBAAC,SAAD;WACE,MAAK;WACL,MAAK;WACL,SAAS,MAAe;WACxB,gBAAgB,EAAc,MAAM;WACpC,CAAA,EACD,EAAG,uBAAuB,WAAW,CAChC;YACJ;WACF,EAAA,CAAA;QACL,MAAe,SACd,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAW;SAAa,SAAS,EAAS;mBAC9C,EAAG,uBAAuB,2BAA2B;SAChD,CAAA,EACR,kBAAC,GAAD;SACE,GAAI,EAAS;SACb,MAAK;SACL,OAAO;SACP,WAAW,MAAM,GAAW,EAAE,OAAO,MAAM;SAC3C,aAAY;SACZ,WAAU;SACV,CAAA,CACE,EAAA,CAAA;QAEJ;;MAIR,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,WAAW;OAAa,SAAS,EAAW;iBAChD,EAAG,qBAAqB,mBAAmB;OACtC,CAAA,EACR,kBAAC,GAAD;OACE,GAAI,EAAW;OACf,MAAK;OACL,OAAO;OACP,WAAW,MAAM,GAAS,EAAE,OAAO,MAAM;OACzC,aAAa,EAAG,gCAAgC,2BAA2B;OAC3E,WAAU;OACV,CAAA,CACE,EAAA,CAAA;MAGL,KAAwB,EAAqB,SAAS,KACrD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;OAAM,WAAW;iBACd,EAAG,2BAA2B,oCAAoC;OAC9D,CAAA,EACP,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAqB,KAAK,MACzB,kBAAC,UAAD;QACE,MAAK;QAEL,gBAAc,EAAY,IAAI,EAAE;QAChC,eAAe,GAAiB,EAAE;QAClC,WAAW,GAAG,GAAgB,GAC5B,EAAY,IAAI,EAAE,GACd,4EACA;kBAGL;QACM,EAVF,EAUE,CACT;OACE,CAAA,CACF,EAAA,CAAA;MAGR,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,IAAD;QACE,SAAS;QACT,kBAAkB,MAAY,GAAmB,MAAY,GAAK;QAClE,WAAU;QACV,CAAA,EACF,kBAAC,QAAD,EAAA,UACG,EACC,+BACA,mEACD,EACI,CAAA,CACD;;MAEP,KACC,kBAAC,OAAD;OACE,MAAK;OACL,WAAU;iBAET;OACG,CAAA;MAGR,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAQ,MAAK;OAAS,SAAQ;OAAU,SAAS;OAAS,WAAU;iBACjE,EAAG,iBAAiB,SAAS;OACvB,CAAA,EACT,kBAAC,GAAD;OACE,MAAK;OACL,eAAe,KAAK,IAAc;OAClC,UAAU,CAAC,KAAa;OACxB,aAAW;OACX,WAAU;iBALZ,CAOG,IAAY,kBAAC,GAAD;QAAS,WAAU;QAAsB,eAAY;QAAS,CAAA,GAAG,MAC7E,EAAG,sBAAsB,SAAS,CAC5B;SACc,EAAA,CAAA;MACrB;;IAEgB;;EACT,CAAA"}
|
|
1
|
+
{"version":3,"file":"MeshDeployWizard.js","names":[],"sources":["../../../src/components/graph/MeshDeployWizard.tsx"],"sourcesContent":["/**\n * MeshDeployWizard — deploy a new agent THROUGH the mesh from a chosen source\n * (root) agent. Supports two modes:\n * - \"Remote (SSH)\": deploy onto another machine over SSH (host/user/port/key).\n * - \"Same machine\": deploy a sibling agent on the source agent's own host.\n *\n * Submitting calls `deployAgentViaMesh({ rootAgentId, input })`, reads the\n * `{ jobId }` out of the AgentProxyResponse, then polls\n * `meshDeployJobStatus(rootAgentId, jobId)` on a 2s interval, rendering the\n * job's step list in a progress panel. On completion it calls `onCompleted`\n * so the caller can refetch the mesh graph.\n */\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { Loader2, CheckCircle, XCircle, Check, Server, Laptop } from 'lucide-react';\nimport {\n Button,\n Checkbox,\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 { useTr } from '../../shared/hooks/useTr';\nimport { useField } from '../../shared/hooks/useField';\nimport {\n useDeployAgentViaMeshMutation,\n useMeshDeployJobStatusLazyQuery,\n} from '@/generated/wspace-operations';\n\n// English fallbacks for the mesh-deploy job step labels. Extends the\n// install-job labels with the mesh-specific bootstrap/onboard/peer-probe\n// steps. Translated via `mesh.deploy.steps.{step}` keys.\nconst stepLabels: Record<string, string> = {\n probe: 'Probing target',\n 'install-bun': 'Installing Bun runtime',\n 'install-agent': 'Installing VibeControls Agent',\n autostart: 'Configuring autostart',\n plugins: 'Installing plugins',\n start: 'Starting agent',\n bootstrap: 'Bootstrapping tunnel',\n onboard: 'Onboarding agent',\n 'store-creds': 'Storing credentials',\n 'report-link': 'Linking into mesh',\n 'probe-peer': 'Probing new peer',\n};\n\ninterface DeployJobStep {\n step: string;\n status: string;\n message?: string;\n}\n\ninterface DeployJob {\n status: 'running' | 'completed' | 'failed' | string;\n steps: DeployJobStep[];\n error?: string;\n}\n\ninterface SourceAgentOption {\n id: string;\n name: string;\n hostname: string;\n isActive: boolean;\n}\n\ninterface MeshDeployWizardProps {\n /** Agents that can act as the deploying root. */\n sourceAgents: SourceAgentOption[];\n /** Pre-seeded source (root) agent id (e.g. \"deploy from here\" on a node). */\n initialSourceAgentId?: string | null;\n availablePermissions?: string[];\n onClose: () => void;\n onCompleted: () => void;\n}\n\ntype DeployMode = 'ssh' | 'same';\ntype AuthMethod = 'agent' | 'key';\n\nfunction asRecord(v: unknown): Record<string, unknown> | null {\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null;\n}\nfunction jstr(obj: Record<string, unknown> | null, key: string): string | null {\n const v = obj?.[key];\n return typeof v === 'string' ? v : null;\n}\n\nconst LABEL_CLASS = 'mb-1.5 block text-sm font-medium text-text-primary';\nconst CHIP_BASE_CLASS =\n 'rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors max-md:min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2';\nconst MODE_BUTTON_BASE_CLASS =\n 'inline-flex flex-1 items-center justify-center gap-2 rounded-md border px-3 py-2 text-sm transition-colors max-md:min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2';\n\nexport function MeshDeployWizard({\n sourceAgents,\n initialSourceAgentId,\n availablePermissions,\n onClose,\n onCompleted,\n}: MeshDeployWizardProps) {\n const tr = useTr();\n\n const [sourceAgentId, setSourceAgentId] = useState<string>(\n initialSourceAgentId ?? sourceAgents[0]?.id ?? ''\n );\n const [mode, setMode] = useState<DeployMode>('ssh');\n const [host, setHost] = useState('');\n const [port, setPort] = useState(22);\n const [user, setUser] = useState('');\n const [authMethod, setAuthMethod] = useState<AuthMethod>('agent');\n const [keyPath, setKeyPath] = useState('');\n const [label, setLabel] = useState('');\n const [connectExisting, setConnectExisting] = useState(false);\n const [permissions, setPermissions] = useState<Set<string>>(new Set());\n\n const [jobId, setJobId] = useState<string | null>(null);\n const [job, setJob] = useState<DeployJob | null>(null);\n const [submitError, setSubmitError] = useState<string | null>(null);\n\n const sourceField = useField();\n const hostField = useField();\n const portField = useField();\n const userField = useField();\n const keyField = useField();\n const labelField = useField();\n\n const [deploy, { loading: deploying }] = useDeployAgentViaMeshMutation();\n const [fetchJobStatus] = useMeshDeployJobStatusLazyQuery({ fetchPolicy: 'network-only' });\n\n const canSubmit = useMemo(() => {\n if (!sourceAgentId) return false;\n if (mode === 'ssh') {\n const targetSpec = host.trim();\n return targetSpec.length > 0 && user.trim().length > 0;\n }\n return true;\n }, [sourceAgentId, mode, host, user]);\n\n const togglePermission = useCallback((p: string) => {\n setPermissions((prev) => {\n const next = new Set(prev);\n if (next.has(p)) next.delete(p);\n else next.add(p);\n return next;\n });\n }, []);\n\n // Build the SSH `target` string the backend expects (user@host:port).\n const buildTargetString = useCallback((): string => {\n if (mode === 'same') return 'localhost';\n const trimmedHost = host.trim();\n const trimmedUser = user.trim();\n const base = trimmedUser ? `${trimmedUser}@${trimmedHost}` : trimmedHost;\n return port && port !== 22 ? `${base}:${port}` : base;\n }, [mode, host, user, port]);\n\n const handleSubmit = useCallback(async () => {\n if (!canSubmit) return;\n setSubmitError(null);\n setJob(null);\n setJobId(null);\n try {\n const res = await deploy({\n variables: {\n rootAgentId: sourceAgentId,\n input: {\n target: buildTargetString(),\n permissions: permissions.size > 0 ? Array.from(permissions) : undefined,\n identityFile: mode === 'ssh' && authMethod === 'key' ? keyPath || undefined : undefined,\n label: label || undefined,\n connectExisting,\n },\n },\n });\n const proxy = res.data?.deployAgentViaMesh;\n if (res.error || proxy?.error || proxy?.ok === false) {\n setSubmitError(\n proxy?.error ?? res.error?.message ?? tr('mesh.deploy.failed', 'Deploy failed')\n );\n return;\n }\n const data = asRecord(proxy?.data);\n const newJobId = jstr(data, 'jobId');\n if (!newJobId) {\n setSubmitError(\n tr('mesh.deploy.noJob', 'Deploy started but no job id was returned by the agent.')\n );\n return;\n }\n setJobId(newJobId);\n setJob({ status: jstr(data, 'status') ?? 'running', steps: [] });\n } catch (err) {\n setSubmitError(err instanceof Error ? err.message : String(err));\n }\n }, [\n canSubmit,\n deploy,\n sourceAgentId,\n buildTargetString,\n permissions,\n mode,\n authMethod,\n keyPath,\n label,\n connectExisting,\n tr,\n ]);\n\n // Poll the mesh-deploy job once a jobId is established.\n useEffect(() => {\n if (!jobId || !sourceAgentId) return;\n let cancelled = false;\n\n const poll = () => {\n void fetchJobStatus({ variables: { rootAgentId: sourceAgentId, jobId } })\n .then((result) => {\n if (cancelled) return;\n const proxy = result.data?.meshDeployJobStatus;\n const data = asRecord(proxy?.data);\n if (!data) return;\n const rawSteps = Array.isArray(data.steps) ? (data.steps as DeployJobStep[]) : [];\n setJob({\n status: jstr(data, 'status') ?? 'running',\n steps: rawSteps,\n error: jstr(data, 'error') ?? proxy?.error ?? undefined,\n });\n const status = jstr(data, 'status');\n if (status === 'completed') {\n onCompleted();\n }\n })\n .catch(() => {\n // transient — retry on next interval\n });\n };\n\n poll();\n const interval = setInterval(poll, 2000);\n return () => {\n cancelled = true;\n clearInterval(interval);\n };\n }, [jobId, sourceAgentId, fetchJobStatus, onCompleted]);\n\n const jobRunning =\n job?.status === 'running' ||\n (jobId !== null && job?.status !== 'completed' && job?.status !== 'failed');\n\n const wizardSteps = [\n { key: 'configure', label: tr('mesh.deploy.steps.configure', 'Configure') },\n { key: 'deploy', label: tr('mesh.deploy.steps.deploy', 'Deploy') },\n ];\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !jobRunning) onClose();\n }}\n >\n <ResponsiveDialogContent\n className=\"max-h-[min(85vh,44rem)] overflow-y-auto sm:max-w-lg\"\n showCloseButton={!jobRunning}\n >\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('mesh.deploy.title', 'Deploy Agent via Mesh')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'mesh.deploy.description',\n 'Deploy a new agent through the mesh from a selected source agent.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <Stepper steps={wizardSteps} active={jobId ? 1 : 0} />\n\n {/* Job progress takes over once a job is running */}\n {jobId ? (\n <div className=\"space-y-3\">\n <p role=\"status\" className=\"text-sm text-text-secondary\">\n {tr('mesh.deploy.deploying', 'Deploying through {{agent}}…', {\n agent: sourceAgents.find((a) => a.id === sourceAgentId)?.name ?? 'agent',\n })}\n </p>\n <div className=\"space-y-2\">\n {job && job.steps.length > 0 ? (\n job.steps.map((step, idx) => (\n <div key={`${step.step}-${idx}`} className=\"flex items-center gap-3\">\n {step.status === 'running' && (\n <Loader2 className=\"size-4 animate-spin text-text-link\" aria-hidden=\"true\" />\n )}\n {step.status === 'completed' && (\n <CheckCircle className=\"size-4 text-status-success-text\" aria-hidden=\"true\" />\n )}\n {step.status === 'failed' && (\n <XCircle className=\"size-4 text-status-error-text\" aria-hidden=\"true\" />\n )}\n {step.status === 'skipped' && (\n <Check className=\"size-4 text-text-muted\" aria-hidden=\"true\" />\n )}\n {step.status === 'pending' && (\n <div\n className=\"size-4 rounded-full border-2 border-border-default\"\n aria-hidden=\"true\"\n />\n )}\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {tr(`mesh.deploy.steps.${step.step}`, stepLabels[step.step] ?? step.step)}\n </div>\n {step.message ? (\n <div className=\"text-xs break-words text-text-muted\">{step.message}</div>\n ) : null}\n </div>\n </div>\n ))\n ) : (\n <div className=\"flex items-center gap-3\">\n <Loader2 className=\"size-4 animate-spin text-text-link\" aria-hidden=\"true\" />\n <span className=\"text-sm text-text-secondary\">\n {tr('mesh.deploy.starting', 'Starting deploy job…')}\n </span>\n </div>\n )}\n </div>\n\n {job?.status === 'completed' && (\n <div\n role=\"status\"\n className=\"rounded-lg border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text\"\n >\n {tr('mesh.deploy.success', 'Agent deployed successfully!')}\n </div>\n )}\n {job?.status === 'failed' && (\n <div\n role=\"alert\"\n className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\"\n >\n {job.error ?? tr('mesh.deploy.jobFailed', 'Deploy failed.')}\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onClose}\n disabled={jobRunning}\n aria-busy={jobRunning}\n className=\"w-full max-md:min-h-11\"\n >\n {jobRunning\n ? tr('mesh.deploy.deployingShort', 'Deploying…')\n : tr('common.close', 'Close')}\n </Button>\n </ResponsiveDialogFooter>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Source agent */}\n <div>\n <label className={LABEL_CLASS} htmlFor={sourceField.id}>\n {tr('mesh.deploy.sourceAgent', 'Source agent (deploys from)')}\n </label>\n <Select\n value={sourceAgentId || undefined}\n onValueChange={setSourceAgentId}\n disabled={sourceAgents.length === 0}\n >\n <SelectTrigger id={sourceField.id} className=\"w-full text-base md:text-sm\">\n <SelectValue placeholder={tr('mesh.deploy.noAgents', 'No agents available')} />\n </SelectTrigger>\n <SelectContent>\n {sourceAgents.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name} ({a.hostname}){a.isActive ? '' : ' · offline'}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Mode toggle */}\n <div>\n <span className={LABEL_CLASS}>{tr('mesh.deploy.mode', 'Deploy mode')}</span>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n aria-pressed={mode === 'ssh'}\n onClick={() => setMode('ssh')}\n className={`${MODE_BUTTON_BASE_CLASS} ${\n mode === 'ssh'\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n <Server className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('mesh.deploy.modeSsh', 'Remote (SSH)')}\n </button>\n <button\n type=\"button\"\n aria-pressed={mode === 'same'}\n onClick={() => setMode('same')}\n className={`${MODE_BUTTON_BASE_CLASS} ${\n mode === 'same'\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n <Laptop className=\"size-3.5\" aria-hidden=\"true\" />\n {tr('mesh.deploy.modeSame', 'Same machine')}\n </button>\n </div>\n </div>\n\n {/* SSH fields */}\n {mode === 'ssh' && (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-3\">\n <div className=\"sm:col-span-2\">\n <label className={LABEL_CLASS} htmlFor={hostField.id}>\n {tr('mesh.deploy.host', 'Host / alias')}\n </label>\n <Input\n {...hostField.inputProps}\n type=\"text\"\n value={host}\n onChange={(e) => setHost(e.target.value)}\n placeholder=\"prod-web-01 or 10.0.0.5\"\n className=\"font-mono text-base md:text-sm\"\n />\n </div>\n <div>\n <label className={LABEL_CLASS} htmlFor={portField.id}>\n {tr('mesh.deploy.port', 'Port')}\n </label>\n <Input\n {...portField.inputProps}\n type=\"number\"\n value={port}\n onChange={(e) => setPort(Number(e.target.value) || 22)}\n className=\"text-base md:text-sm\"\n />\n </div>\n </div>\n <div>\n <label className={LABEL_CLASS} htmlFor={userField.id}>\n {tr('mesh.deploy.user', 'User')}\n </label>\n <Input\n {...userField.inputProps}\n type=\"text\"\n value={user}\n onChange={(e) => setUser(e.target.value)}\n placeholder=\"root\"\n className=\"font-mono text-base md:text-sm\"\n />\n </div>\n <div>\n <span className={LABEL_CLASS}>{tr('mesh.deploy.auth', 'Authentication')}</span>\n <div className=\"flex flex-wrap gap-x-4 gap-y-2\">\n <label className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-text-primary max-md:min-h-11\">\n <input\n type=\"radio\"\n name=\"mesh-auth\"\n checked={authMethod === 'agent'}\n onChange={() => setAuthMethod('agent')}\n />\n {tr('mesh.deploy.authAgent', 'SSH agent / default key')}\n </label>\n <label className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-text-primary max-md:min-h-11\">\n <input\n type=\"radio\"\n name=\"mesh-auth\"\n checked={authMethod === 'key'}\n onChange={() => setAuthMethod('key')}\n />\n {tr('mesh.deploy.authKey', 'Key path')}\n </label>\n </div>\n </div>\n {authMethod === 'key' && (\n <div>\n <label className={LABEL_CLASS} htmlFor={keyField.id}>\n {tr('mesh.deploy.keyPath', 'Identity file (key path)')}\n </label>\n <Input\n {...keyField.inputProps}\n type=\"text\"\n value={keyPath}\n onChange={(e) => setKeyPath(e.target.value)}\n placeholder=\"~/.ssh/id_ed25519\"\n className=\"font-mono text-base md:text-sm\"\n />\n </div>\n )}\n </div>\n )}\n\n {/* Label */}\n <div>\n <label className={LABEL_CLASS} htmlFor={labelField.id}>\n {tr('mesh.deploy.label', 'Label (optional)')}\n </label>\n <Input\n {...labelField.inputProps}\n type=\"text\"\n value={label}\n onChange={(e) => setLabel(e.target.value)}\n placeholder={tr('mesh.deploy.labelPlaceholder', 'e.g. Production web tier')}\n className=\"text-base md:text-sm\"\n />\n </div>\n\n {/* Permission chips */}\n {availablePermissions && availablePermissions.length > 0 && (\n <div>\n <span className={LABEL_CLASS}>\n {tr('mesh.deploy.permissions', 'Grant permissions on the new link')}\n </span>\n <div className=\"flex flex-wrap gap-2\">\n {availablePermissions.map((p) => (\n <button\n type=\"button\"\n key={p}\n aria-pressed={permissions.has(p)}\n onClick={() => togglePermission(p)}\n className={`${CHIP_BASE_CLASS} ${\n permissions.has(p)\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {p}\n </button>\n ))}\n </div>\n </div>\n )}\n\n <label className=\"flex cursor-pointer items-start gap-2 text-sm text-text-primary\">\n <Checkbox\n checked={connectExisting}\n onCheckedChange={(checked) => setConnectExisting(checked === true)}\n className=\"mt-0.5\"\n />\n <span>\n {tr(\n 'mesh.deploy.connectExisting',\n 'Connect to an existing agent on the target instead of installing'\n )}\n </span>\n </label>\n\n {submitError && (\n <div\n role=\"alert\"\n className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\"\n >\n {submitError}\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={onClose} className=\"max-md:min-h-11\">\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleSubmit()}\n disabled={!canSubmit || deploying}\n aria-busy={deploying}\n className=\"max-md:min-h-11\"\n >\n {deploying ? <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" /> : null}\n {tr('mesh.deploy.submit', 'Deploy')}\n </Button>\n </ResponsiveDialogFooter>\n </div>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -146,12 +146,12 @@ function h({ rootAgentId: h, destinations: g, initialDestinationId: _, onClose:
|
|
|
146
146
|
children: [/* @__PURE__ */ l("button", {
|
|
147
147
|
type: "button",
|
|
148
148
|
onClick: () => C("command"),
|
|
149
|
-
className: `px-3 py-2 text-sm rounded-md border transition-colors ${S === "command" ? "border-action-primary-bg bg-
|
|
149
|
+
className: `px-3 py-2 text-sm rounded-md border transition-colors ${S === "command" ? "border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary" : "border-border-default text-text-secondary hover:bg-bg-sunken"}`,
|
|
150
150
|
children: y("mesh.request.command", "Command")
|
|
151
151
|
}), /* @__PURE__ */ l("button", {
|
|
152
152
|
type: "button",
|
|
153
153
|
onClick: () => C("api"),
|
|
154
|
-
className: `px-3 py-2 text-sm rounded-md border transition-colors ${S === "api" ? "border-action-primary-bg bg-
|
|
154
|
+
className: `px-3 py-2 text-sm rounded-md border transition-colors ${S === "api" ? "border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary" : "border-border-default text-text-secondary hover:bg-bg-sunken"}`,
|
|
155
155
|
children: y("mesh.request.api", "API")
|
|
156
156
|
})]
|
|
157
157
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeshRequestPanel.js","names":[],"sources":["../../../src/components/graph/MeshRequestPanel.tsx"],"sourcesContent":["/**\n * MeshRequestPanel — run a request against a destination agent THROUGH the mesh.\n *\n * The backend computes the hop path from the chosen root (source) agent to the\n * destination, so the panel only supplies `rootAgentId` + `destinationAgentId`\n * + the request. Two request modes:\n * - COMMAND: type a `vibe` argv; we POST to\n * `/api/profiles/default/agent/commands/run` with body `{ argv }`.\n * - API: raw method + path + optional JSON body.\n *\n * The AgentProxyResponse is rendered like AgentMaintenanceTab's output pane,\n * and a hops breadcrumb is shown when the response surfaces hop info.\n */\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { Loader2, Play, ChevronRight, Network } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useExecuteAgentMeshRequestMutation } from '@/generated/wspace-operations';\nimport type { MeshAgentUi } from './meshTypes';\n\ntype RequestMode = 'command' | 'api';\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\nfunction asRecord(v: unknown): Record<string, unknown> | null {\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null;\n}\nfunction jstr(obj: Record<string, unknown> | null, key: string): string | null {\n const v = obj?.[key];\n return typeof v === 'string' ? v : null;\n}\n\nfunction extractHops(data: Record<string, unknown> | null): string[] {\n if (!data) return [];\n const raw = data.hops ?? asRecord(data.meta)?.hops;\n if (Array.isArray(raw)) return raw.filter((h): h is string => typeof h === 'string');\n return [];\n}\n\nconst INPUT_CLASS =\n 'px-3 py-2 text-sm rounded-md bg-bg-sunken border border-border-default text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40';\n\ninterface MeshRequestPanelProps {\n /** The root/source agent the request is dispatched from. */\n rootAgentId: string | null;\n /** Candidate destination agents (typically the workspace mesh agents). */\n destinations: MeshAgentUi[];\n /** Pre-selected destination (e.g. clicked node). */\n initialDestinationId?: string | null;\n onClose?: () => void;\n}\n\nexport function MeshRequestPanel({\n rootAgentId,\n destinations,\n initialDestinationId,\n onClose,\n}: MeshRequestPanelProps) {\n const tr = useTr();\n\n const [destinationId, setDestinationId] = useState<string>(\n initialDestinationId ?? destinations[0]?.id ?? ''\n );\n const [mode, setMode] = useState<RequestMode>('command');\n const [argv, setArgv] = useState('status');\n const [method, setMethod] = useState<HttpMethod>('GET');\n const [path, setPath] = useState('/api/health');\n const [bodyText, setBodyText] = useState('');\n const [output, setOutput] = useState<{ body: string; error?: boolean } | null>(null);\n const [hops, setHops] = useState<string[]>([]);\n\n const [execute, { loading }] = useExecuteAgentMeshRequestMutation();\n\n const destName = useCallback(\n (id: string) => destinations.find((d) => d.id === id)?.name ?? id,\n [destinations]\n );\n\n const canRun = useMemo(() => {\n if (!rootAgentId || !destinationId) return false;\n if (mode === 'command') return argv.trim().length > 0;\n return path.trim().length > 0;\n }, [rootAgentId, destinationId, mode, argv, path]);\n\n const handleRun = useCallback(async () => {\n if (!rootAgentId || !destinationId) return;\n setOutput(null);\n setHops([]);\n\n let requestInput: {\n method: string;\n path: string;\n body?: Record<string, unknown>;\n };\n\n if (mode === 'command') {\n const parts = argv.trim().split(/\\s+/).filter(Boolean);\n requestInput = {\n method: 'POST',\n path: '/api/profiles/default/agent/commands/run',\n body: { argv: parts },\n };\n } else {\n let parsedBody: Record<string, unknown> | undefined;\n if (bodyText.trim().length > 0) {\n try {\n const parsed: unknown = JSON.parse(bodyText);\n parsedBody = asRecord(parsed) ?? undefined;\n if (!parsedBody) {\n setOutput({\n body: tr('mesh.request.bodyNotObject', 'Body must be a JSON object.'),\n error: true,\n });\n return;\n }\n } catch {\n setOutput({\n body: tr('mesh.request.invalidJson', 'Request body is not valid JSON.'),\n error: true,\n });\n return;\n }\n }\n requestInput = { method, path: path.trim(), body: parsedBody };\n }\n\n try {\n const res = await execute({\n variables: {\n rootAgentId,\n destinationAgentId: destinationId,\n request: requestInput,\n },\n });\n const proxy = res.data?.executeAgentMeshRequest;\n const data = asRecord(proxy?.data);\n setHops(extractHops(data));\n\n if (res.error || proxy?.error || proxy?.ok === false) {\n setOutput({\n body:\n proxy?.error ??\n res.error?.message ??\n jstr(data, 'error') ??\n tr('mesh.request.failed', 'Request failed'),\n error: true,\n });\n return;\n }\n\n const stdout = jstr(data, 'stdout');\n const text = proxy?.text;\n const pretty = data ? JSON.stringify(data, null, 2) : (text ?? '(no output)');\n setOutput({ body: stdout || text || pretty });\n } catch (err) {\n setOutput({ body: err instanceof Error ? err.message : String(err), error: true });\n }\n }, [rootAgentId, destinationId, mode, argv, method, path, bodyText, execute, tr]);\n\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4 space-y-3\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Network className=\"size-4 text-text-secondary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('mesh.request.title', 'Run mesh request')}\n </h3>\n </div>\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-xs text-text-muted hover:text-text-primary\"\n >\n {tr('common.close', 'Close')}\n </button>\n )}\n </div>\n\n {!rootAgentId && (\n <p className=\"text-xs text-status-warning-text\">\n {tr('mesh.request.noRoot', 'Select a reachable root agent to dispatch from.')}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex flex-col gap-1\">\n <label className=\"text-xs text-text-muted\" htmlFor=\"mesh-dest\">\n {tr('mesh.request.destination', 'Destination agent')}\n </label>\n <select\n id=\"mesh-dest\"\n value={destinationId}\n onChange={(e) => setDestinationId(e.target.value)}\n className={`${INPUT_CLASS} min-w-44`}\n >\n {destinations.length === 0 && (\n <option value=\"\">{tr('mesh.request.noDest', 'No agents')}</option>\n )}\n {destinations.map((d) => (\n <option key={d.id} value={d.id}>\n {d.name} ({d.hostname})\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"flex flex-col gap-1\">\n <span className=\"text-xs text-text-muted\">{tr('mesh.request.mode', 'Mode')}</span>\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={() => setMode('command')}\n className={`px-3 py-2 text-sm rounded-md border transition-colors ${\n mode === 'command'\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {tr('mesh.request.command', 'Command')}\n </button>\n <button\n type=\"button\"\n onClick={() => setMode('api')}\n className={`px-3 py-2 text-sm rounded-md border transition-colors ${\n mode === 'api'\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {tr('mesh.request.api', 'API')}\n </button>\n </div>\n </div>\n </div>\n\n {mode === 'command' ? (\n <div className=\"flex flex-col gap-1\">\n <label className=\"text-xs text-text-muted\" htmlFor=\"mesh-argv\">\n {tr('mesh.request.argv', 'Command (vibe …)')}\n </label>\n <input\n id=\"mesh-argv\"\n type=\"text\"\n value={argv}\n onChange={(e) => setArgv(e.target.value)}\n placeholder=\"status --json\"\n className={`${INPUT_CLASS} font-mono w-full`}\n />\n </div>\n ) : (\n <div className=\"space-y-2\">\n <div className=\"flex gap-2\">\n <select\n value={method}\n onChange={(e) => setMethod(e.target.value as HttpMethod)}\n className={`${INPUT_CLASS} w-28`}\n aria-label={tr('mesh.request.method', 'Method')}\n >\n {(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as HttpMethod[]).map((m) => (\n <option key={m} value={m}>\n {m}\n </option>\n ))}\n </select>\n <input\n type=\"text\"\n value={path}\n onChange={(e) => setPath(e.target.value)}\n placeholder=\"/api/health\"\n className={`${INPUT_CLASS} font-mono flex-1`}\n aria-label={tr('mesh.request.path', 'Path')}\n />\n </div>\n <textarea\n value={bodyText}\n onChange={(e) => setBodyText(e.target.value)}\n placeholder={tr('mesh.request.bodyPlaceholder', 'JSON body (optional)')}\n rows={3}\n className={`${INPUT_CLASS} font-mono w-full resize-y`}\n aria-label={tr('mesh.request.body', 'Request body')}\n />\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={() => void handleRun()}\n disabled={!canRun || loading}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {loading ? <Loader2 className=\"size-4 animate-spin\" /> : <Play className=\"size-4\" />}\n {tr('mesh.request.run', 'Run')}\n </button>\n\n {hops.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-1 text-xs text-text-tertiary\">\n <span className=\"font-medium text-text-secondary\">\n {tr('mesh.request.hops', 'Hops:')}\n </span>\n {hops.map((hop, idx) => (\n <span key={`${hop}-${idx}`} className=\"inline-flex items-center gap-1\">\n <span className=\"rounded bg-bg-sunken px-1.5 py-0.5 text-text-primary\">\n {destName(hop)}\n </span>\n {idx < hops.length - 1 && <ChevronRight className=\"size-3\" />}\n </span>\n ))}\n </div>\n )}\n\n {output && (\n <pre\n className={`text-xs whitespace-pre-wrap break-words max-h-80 overflow-auto rounded-md p-3 ${\n output.error\n ? 'bg-status-error-bg-subtle text-status-error-text'\n : 'bg-bg-sunken text-text-primary'\n }`}\n >\n {output.body}\n </pre>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MeshRequestPanel.js","names":[],"sources":["../../../src/components/graph/MeshRequestPanel.tsx"],"sourcesContent":["/**\n * MeshRequestPanel — run a request against a destination agent THROUGH the mesh.\n *\n * The backend computes the hop path from the chosen root (source) agent to the\n * destination, so the panel only supplies `rootAgentId` + `destinationAgentId`\n * + the request. Two request modes:\n * - COMMAND: type a `vibe` argv; we POST to\n * `/api/profiles/default/agent/commands/run` with body `{ argv }`.\n * - API: raw method + path + optional JSON body.\n *\n * The AgentProxyResponse is rendered like AgentMaintenanceTab's output pane,\n * and a hops breadcrumb is shown when the response surfaces hop info.\n */\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { Loader2, Play, ChevronRight, Network } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useExecuteAgentMeshRequestMutation } from '@/generated/wspace-operations';\nimport type { MeshAgentUi } from './meshTypes';\n\ntype RequestMode = 'command' | 'api';\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\nfunction asRecord(v: unknown): Record<string, unknown> | null {\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null;\n}\nfunction jstr(obj: Record<string, unknown> | null, key: string): string | null {\n const v = obj?.[key];\n return typeof v === 'string' ? v : null;\n}\n\nfunction extractHops(data: Record<string, unknown> | null): string[] {\n if (!data) return [];\n const raw = data.hops ?? asRecord(data.meta)?.hops;\n if (Array.isArray(raw)) return raw.filter((h): h is string => typeof h === 'string');\n return [];\n}\n\nconst INPUT_CLASS =\n 'px-3 py-2 text-sm rounded-md bg-bg-sunken border border-border-default text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40';\n\ninterface MeshRequestPanelProps {\n /** The root/source agent the request is dispatched from. */\n rootAgentId: string | null;\n /** Candidate destination agents (typically the workspace mesh agents). */\n destinations: MeshAgentUi[];\n /** Pre-selected destination (e.g. clicked node). */\n initialDestinationId?: string | null;\n onClose?: () => void;\n}\n\nexport function MeshRequestPanel({\n rootAgentId,\n destinations,\n initialDestinationId,\n onClose,\n}: MeshRequestPanelProps) {\n const tr = useTr();\n\n const [destinationId, setDestinationId] = useState<string>(\n initialDestinationId ?? destinations[0]?.id ?? ''\n );\n const [mode, setMode] = useState<RequestMode>('command');\n const [argv, setArgv] = useState('status');\n const [method, setMethod] = useState<HttpMethod>('GET');\n const [path, setPath] = useState('/api/health');\n const [bodyText, setBodyText] = useState('');\n const [output, setOutput] = useState<{ body: string; error?: boolean } | null>(null);\n const [hops, setHops] = useState<string[]>([]);\n\n const [execute, { loading }] = useExecuteAgentMeshRequestMutation();\n\n const destName = useCallback(\n (id: string) => destinations.find((d) => d.id === id)?.name ?? id,\n [destinations]\n );\n\n const canRun = useMemo(() => {\n if (!rootAgentId || !destinationId) return false;\n if (mode === 'command') return argv.trim().length > 0;\n return path.trim().length > 0;\n }, [rootAgentId, destinationId, mode, argv, path]);\n\n const handleRun = useCallback(async () => {\n if (!rootAgentId || !destinationId) return;\n setOutput(null);\n setHops([]);\n\n let requestInput: {\n method: string;\n path: string;\n body?: Record<string, unknown>;\n };\n\n if (mode === 'command') {\n const parts = argv.trim().split(/\\s+/).filter(Boolean);\n requestInput = {\n method: 'POST',\n path: '/api/profiles/default/agent/commands/run',\n body: { argv: parts },\n };\n } else {\n let parsedBody: Record<string, unknown> | undefined;\n if (bodyText.trim().length > 0) {\n try {\n const parsed: unknown = JSON.parse(bodyText);\n parsedBody = asRecord(parsed) ?? undefined;\n if (!parsedBody) {\n setOutput({\n body: tr('mesh.request.bodyNotObject', 'Body must be a JSON object.'),\n error: true,\n });\n return;\n }\n } catch {\n setOutput({\n body: tr('mesh.request.invalidJson', 'Request body is not valid JSON.'),\n error: true,\n });\n return;\n }\n }\n requestInput = { method, path: path.trim(), body: parsedBody };\n }\n\n try {\n const res = await execute({\n variables: {\n rootAgentId,\n destinationAgentId: destinationId,\n request: requestInput,\n },\n });\n const proxy = res.data?.executeAgentMeshRequest;\n const data = asRecord(proxy?.data);\n setHops(extractHops(data));\n\n if (res.error || proxy?.error || proxy?.ok === false) {\n setOutput({\n body:\n proxy?.error ??\n res.error?.message ??\n jstr(data, 'error') ??\n tr('mesh.request.failed', 'Request failed'),\n error: true,\n });\n return;\n }\n\n const stdout = jstr(data, 'stdout');\n const text = proxy?.text;\n const pretty = data ? JSON.stringify(data, null, 2) : (text ?? '(no output)');\n setOutput({ body: stdout || text || pretty });\n } catch (err) {\n setOutput({ body: err instanceof Error ? err.message : String(err), error: true });\n }\n }, [rootAgentId, destinationId, mode, argv, method, path, bodyText, execute, tr]);\n\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4 space-y-3\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Network className=\"size-4 text-text-secondary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('mesh.request.title', 'Run mesh request')}\n </h3>\n </div>\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-xs text-text-muted hover:text-text-primary\"\n >\n {tr('common.close', 'Close')}\n </button>\n )}\n </div>\n\n {!rootAgentId && (\n <p className=\"text-xs text-status-warning-text\">\n {tr('mesh.request.noRoot', 'Select a reachable root agent to dispatch from.')}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex flex-col gap-1\">\n <label className=\"text-xs text-text-muted\" htmlFor=\"mesh-dest\">\n {tr('mesh.request.destination', 'Destination agent')}\n </label>\n <select\n id=\"mesh-dest\"\n value={destinationId}\n onChange={(e) => setDestinationId(e.target.value)}\n className={`${INPUT_CLASS} min-w-44`}\n >\n {destinations.length === 0 && (\n <option value=\"\">{tr('mesh.request.noDest', 'No agents')}</option>\n )}\n {destinations.map((d) => (\n <option key={d.id} value={d.id}>\n {d.name} ({d.hostname})\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"flex flex-col gap-1\">\n <span className=\"text-xs text-text-muted\">{tr('mesh.request.mode', 'Mode')}</span>\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={() => setMode('command')}\n className={`px-3 py-2 text-sm rounded-md border transition-colors ${\n mode === 'command'\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {tr('mesh.request.command', 'Command')}\n </button>\n <button\n type=\"button\"\n onClick={() => setMode('api')}\n className={`px-3 py-2 text-sm rounded-md border transition-colors ${\n mode === 'api'\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)] text-text-primary'\n : 'border-border-default text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {tr('mesh.request.api', 'API')}\n </button>\n </div>\n </div>\n </div>\n\n {mode === 'command' ? (\n <div className=\"flex flex-col gap-1\">\n <label className=\"text-xs text-text-muted\" htmlFor=\"mesh-argv\">\n {tr('mesh.request.argv', 'Command (vibe …)')}\n </label>\n <input\n id=\"mesh-argv\"\n type=\"text\"\n value={argv}\n onChange={(e) => setArgv(e.target.value)}\n placeholder=\"status --json\"\n className={`${INPUT_CLASS} font-mono w-full`}\n />\n </div>\n ) : (\n <div className=\"space-y-2\">\n <div className=\"flex gap-2\">\n <select\n value={method}\n onChange={(e) => setMethod(e.target.value as HttpMethod)}\n className={`${INPUT_CLASS} w-28`}\n aria-label={tr('mesh.request.method', 'Method')}\n >\n {(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as HttpMethod[]).map((m) => (\n <option key={m} value={m}>\n {m}\n </option>\n ))}\n </select>\n <input\n type=\"text\"\n value={path}\n onChange={(e) => setPath(e.target.value)}\n placeholder=\"/api/health\"\n className={`${INPUT_CLASS} font-mono flex-1`}\n aria-label={tr('mesh.request.path', 'Path')}\n />\n </div>\n <textarea\n value={bodyText}\n onChange={(e) => setBodyText(e.target.value)}\n placeholder={tr('mesh.request.bodyPlaceholder', 'JSON body (optional)')}\n rows={3}\n className={`${INPUT_CLASS} font-mono w-full resize-y`}\n aria-label={tr('mesh.request.body', 'Request body')}\n />\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={() => void handleRun()}\n disabled={!canRun || loading}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {loading ? <Loader2 className=\"size-4 animate-spin\" /> : <Play className=\"size-4\" />}\n {tr('mesh.request.run', 'Run')}\n </button>\n\n {hops.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-1 text-xs text-text-tertiary\">\n <span className=\"font-medium text-text-secondary\">\n {tr('mesh.request.hops', 'Hops:')}\n </span>\n {hops.map((hop, idx) => (\n <span key={`${hop}-${idx}`} className=\"inline-flex items-center gap-1\">\n <span className=\"rounded bg-bg-sunken px-1.5 py-0.5 text-text-primary\">\n {destName(hop)}\n </span>\n {idx < hops.length - 1 && <ChevronRight className=\"size-3\" />}\n </span>\n ))}\n </div>\n )}\n\n {output && (\n <pre\n className={`text-xs whitespace-pre-wrap break-words max-h-80 overflow-auto rounded-md p-3 ${\n output.error\n ? 'bg-status-error-bg-subtle text-status-error-text'\n : 'bg-bg-sunken text-text-primary'\n }`}\n >\n {output.body}\n </pre>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -14,10 +14,10 @@ var v = {
|
|
|
14
14
|
INFRASTRUCTURE: a,
|
|
15
15
|
CUSTOM: r
|
|
16
16
|
}, y = {
|
|
17
|
-
EXPERIMENTAL: "bg-status-warning-bg text-status-warning-text",
|
|
18
|
-
ACTIVE: "bg-status-success-bg text-status-success-text",
|
|
19
|
-
DEPRECATED: "bg-status-error-bg text-status-error-text",
|
|
20
|
-
END_OF_LIFE: "bg-status-error-bg text-status-error-text"
|
|
17
|
+
EXPERIMENTAL: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
18
|
+
ACTIVE: "bg-status-success-bg-subtle text-status-success-text",
|
|
19
|
+
DEPRECATED: "bg-status-error-bg-subtle text-status-error-text",
|
|
20
|
+
END_OF_LIFE: "bg-status-error-bg-subtle text-status-error-text"
|
|
21
21
|
}, b = ({ component: i, onOpen: a, onEdit: o, onArchive: s, className: c = "" }) => {
|
|
22
22
|
let { t: u } = t(), f = (e, t) => {
|
|
23
23
|
let n = u(e);
|
|
@@ -45,7 +45,7 @@ var v = {
|
|
|
45
45
|
children: [/* @__PURE__ */ _("div", {
|
|
46
46
|
className: "flex items-start gap-2 sm:gap-3 min-w-0 flex-1",
|
|
47
47
|
children: [/* @__PURE__ */ g("div", {
|
|
48
|
-
className: "flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-
|
|
48
|
+
className: "flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center",
|
|
49
49
|
children: /* @__PURE__ */ g(S, { className: "size-4 sm:w-5 sm:h-5 text-action-primary-bg" })
|
|
50
50
|
}), /* @__PURE__ */ _("div", {
|
|
51
51
|
className: "min-w-0 flex-1",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComponentCard.js","names":[],"sources":["../../../src/components/idp-components/ComponentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n Box,\n Globe,\n Library,\n Server,\n Smartphone,\n Database,\n Cog,\n Tag,\n MoreVertical,\n Settings,\n Archive,\n Code,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface ComponentCardData {\n id: string;\n name: string;\n description: string | null;\n type:\n | 'SERVICE'\n | 'FRONTEND'\n | 'LIBRARY'\n | 'API'\n | 'MOBILE'\n | 'DATABASE'\n | 'INFRASTRUCTURE'\n | 'CUSTOM';\n lifecycle: 'EXPERIMENTAL' | 'ACTIVE' | 'DEPRECATED' | 'END_OF_LIFE';\n version: string | null;\n language: string | null;\n framework: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ComponentCardProps {\n component: ComponentCardData;\n onOpen?: (component: ComponentCardData) => void;\n onEdit?: (component: ComponentCardData) => void;\n onArchive?: (component: ComponentCardData) => void;\n className?: string;\n}\n\nconst typeIcons: Record<string, typeof Box> = {\n SERVICE: Server,\n FRONTEND: Globe,\n LIBRARY: Library,\n API: Code,\n MOBILE: Smartphone,\n DATABASE: Database,\n INFRASTRUCTURE: Cog,\n CUSTOM: Box,\n};\n\nconst lifecycleStyles: Record<string, string> = {\n EXPERIMENTAL: 'bg-status-warning-bg text-status-warning-text',\n ACTIVE: 'bg-status-success-bg text-status-success-text',\n DEPRECATED: 'bg-status-error-bg text-status-error-text',\n END_OF_LIFE: 'bg-status-error-bg text-status-error-text',\n};\n\nexport const ComponentCard: FC<ComponentCardProps> = ({\n component,\n onOpen,\n onEdit,\n onArchive,\n className = '',\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [showMenu, setShowMenu] = useState(false);\n\n const Icon = typeIcons[component.type] || Box;\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border border-border-default rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${className}`}\n onClick={() => onOpen?.(component)}\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 <div className=\"flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-
|
|
1
|
+
{"version":3,"file":"ComponentCard.js","names":[],"sources":["../../../src/components/idp-components/ComponentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n Box,\n Globe,\n Library,\n Server,\n Smartphone,\n Database,\n Cog,\n Tag,\n MoreVertical,\n Settings,\n Archive,\n Code,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface ComponentCardData {\n id: string;\n name: string;\n description: string | null;\n type:\n | 'SERVICE'\n | 'FRONTEND'\n | 'LIBRARY'\n | 'API'\n | 'MOBILE'\n | 'DATABASE'\n | 'INFRASTRUCTURE'\n | 'CUSTOM';\n lifecycle: 'EXPERIMENTAL' | 'ACTIVE' | 'DEPRECATED' | 'END_OF_LIFE';\n version: string | null;\n language: string | null;\n framework: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ComponentCardProps {\n component: ComponentCardData;\n onOpen?: (component: ComponentCardData) => void;\n onEdit?: (component: ComponentCardData) => void;\n onArchive?: (component: ComponentCardData) => void;\n className?: string;\n}\n\nconst typeIcons: Record<string, typeof Box> = {\n SERVICE: Server,\n FRONTEND: Globe,\n LIBRARY: Library,\n API: Code,\n MOBILE: Smartphone,\n DATABASE: Database,\n INFRASTRUCTURE: Cog,\n CUSTOM: Box,\n};\n\nconst lifecycleStyles: Record<string, string> = {\n EXPERIMENTAL: 'bg-status-warning-bg-subtle text-status-warning-text',\n ACTIVE: 'bg-status-success-bg-subtle text-status-success-text',\n DEPRECATED: 'bg-status-error-bg-subtle text-status-error-text',\n END_OF_LIFE: 'bg-status-error-bg-subtle text-status-error-text',\n};\n\nexport const ComponentCard: FC<ComponentCardProps> = ({\n component,\n onOpen,\n onEdit,\n onArchive,\n className = '',\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [showMenu, setShowMenu] = useState(false);\n\n const Icon = typeIcons[component.type] || Box;\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border border-border-default rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${className}`}\n onClick={() => onOpen?.(component)}\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 <div className=\"flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Icon className=\"size-4 sm:w-5 sm:h-5 text-action-primary-bg\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-text-primary truncate text-sm sm:text-base\">\n {component.name}\n </h3>\n <div className=\"flex items-center gap-2 mt-0.5 flex-wrap\">\n <span className=\"text-xs text-text-secondary\">{component.type}</span>\n <span\n className={`inline-flex items-center px-1.5 py-0.5 text-xs rounded ${lifecycleStyles[component.lifecycle] || 'bg-bg-sunken text-text-secondary'}`}\n >\n {tr(\n `component.lifecycle.${component.lifecycle.toLowerCase()}`,\n component.lifecycle\n )}\n </span>\n </div>\n </div>\n </div>\n {(onEdit || onArchive) && (\n <div className=\"relative flex-shrink-0\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-4 text-text-secondary\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {onEdit && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onEdit(component);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Settings className=\"size-4\" />\n {tr('common.edit', 'Edit')}\n </motion.button>\n )}\n {onArchive && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onArchive(component);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-b-lg\"\n >\n <Archive className=\"size-4\" />\n {tr('common.archive', 'Archive')}\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n\n {component.description && (\n <p className=\"mt-2 text-sm text-text-secondary line-clamp-2\">{component.description}</p>\n )}\n\n {/* Metadata row */}\n <div className=\"mt-3 flex items-center gap-3 text-xs text-text-secondary flex-wrap\">\n {component.version && (\n <span className=\"font-mono bg-bg-sunken px-1.5 py-0.5 rounded\">v{component.version}</span>\n )}\n {component.language && <span>{component.language}</span>}\n {component.framework && <span className=\"text-text-muted\">{component.framework}</span>}\n </div>\n\n {component.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {component.tags.slice(0, 3).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n {component.tags.length > 3 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n +{component.tags.length - 3}\n </span>\n )}\n </div>\n )}\n </motion.div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -30,7 +30,7 @@ var l = ({ isOpen: l, onClose: u, onSubmit: d, initialData: f, title: p, extraSe
|
|
|
30
30
|
className: "space-y-4",
|
|
31
31
|
children: [
|
|
32
32
|
M && /* @__PURE__ */ s("div", {
|
|
33
|
-
className: "p-3 bg-status-error-bg text-status-error-text text-sm rounded-md",
|
|
33
|
+
className: "p-3 bg-status-error-bg-subtle text-status-error-text text-sm rounded-md",
|
|
34
34
|
children: M
|
|
35
35
|
}),
|
|
36
36
|
/* @__PURE__ */ c("div", { children: [
|