@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.2
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/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 +23 -23
- 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 +3 -3
- 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 +6 -6
- 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 +74 -41
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginsCatalogPage.js +6 -3
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BridleConfigPanel.js","names":[],"sources":["../../../src/components/agent-manager/BridleConfigPanel.tsx"],"sourcesContent":["/**\n * Bridle Configuration Manager Panel.\n *\n * Provides a full UI for managing cross-harness AI assistant configurations\n * via the Bridle CLI tool. Supports profile management, skill installation,\n * and config editing across Claude Code, OpenCode, Goose, Amp, Copilot, and Crush.\n */\n\nimport { useState, useCallback } from 'react';\nimport {\n Terminal,\n Copy,\n Check,\n Plus,\n ArrowLeftRight,\n Trash2,\n Download,\n Settings,\n RefreshCw,\n} from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface BridleConfigPanelProps {\n agent: { id: string; profile: string };\n}\n\nconst HARNESSES = [\n {\n name: 'Claude Code',\n id: 'claude',\n config: '~/.claude/',\n color: 'bg-accent-purple-subtle text-accent-purple',\n },\n {\n name: 'OpenCode',\n id: 'opencode',\n config: '~/.config/opencode/',\n color: 'bg-status-info-bg text-status-info-text',\n },\n {\n name: 'Goose',\n id: 'goose',\n config: '~/.config/goose/',\n color: 'bg-status-success-bg text-status-success-text',\n },\n {\n name: 'Amp',\n id: 'amp',\n config: '~/.amp/',\n color: 'bg-status-warning-bg text-status-warning-text',\n },\n {\n name: 'Copilot',\n id: 'copilot',\n config: '~/.copilot/',\n color: 'bg-bg-sunken text-text-secondary',\n },\n {\n name: 'Crush',\n id: 'crush',\n config: '~/.config/crush/',\n color: 'bg-status-error-bg text-status-error-text',\n },\n];\n\ntype PanelView = 'overview' | 'profiles' | 'install' | 'settings';\n\nexport function BridleConfigPanel({ agent: _agent }: BridleConfigPanelProps) {\n const tr = useTr();\n const [view, setView] = useState<PanelView>('overview');\n const [selectedHarness, setSelectedHarness] = useState('claude');\n const [profileName, setProfileName] = useState('');\n const [installSource, setInstallSource] = useState('');\n const [configKey, setConfigKey] = useState('');\n const [configValue, setConfigValue] = useState('');\n const [output, setOutput] = useState('');\n const [copiedCmd, setCopiedCmd] = useState<string | null>(null);\n\n const copyToClipboard = useCallback((text: string) => {\n void navigator.clipboard.writeText(text);\n setCopiedCmd(text);\n setTimeout(() => setCopiedCmd(null), 2000);\n }, []);\n\n const showCommand = (cmd: string) => {\n setOutput(`$ ${cmd}\\n\\nRun this command in your terminal to execute.`);\n copyToClipboard(cmd);\n };\n\n const navItems: Array<{ key: PanelView; label: string; icon: typeof Settings }> = [\n { key: 'overview', label: 'Overview', icon: RefreshCw },\n { key: 'profiles', label: 'Profiles', icon: ArrowLeftRight },\n { key: 'install', label: 'Install', icon: Download },\n { key: 'settings', label: 'Settings', icon: Settings },\n ];\n\n return (\n <div className=\"flex flex-col h-full max-h-[65vh] overflow-hidden\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default flex-shrink-0\">\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"size-4\" />\n {tr('vibecontrols.agentManager.bridle.title', 'Bridle — Cross-Harness Config Manager')}\n </h3>\n <p className=\"text-[11px] text-text-secondary mt-0.5\">\n {tr(\n 'vibecontrols.agentManager.bridle.description',\n 'Manage profiles, skills, and configurations across all AI coding assistants'\n )}\n </p>\n </div>\n </div>\n\n {/* Sub-navigation */}\n <div className=\"flex gap-1 px-4 py-2 border-b border-border-default flex-shrink-0\">\n {navItems.map(({ key, label, icon: Icon }) => (\n <button\n type=\"button\"\n key={key}\n onClick={() => setView(key)}\n className={`flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded transition-colors ${\n view === key\n ? 'bg-action-primary-bg/10 text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-elevated'\n }`}\n >\n <Icon className=\"size-3.5\" />\n {label}\n </button>\n ))}\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-4\">\n {view === 'overview' && (\n <div className=\"space-y-4\">\n {/* Harness Grid */}\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.bridle.supportedHarnesses', 'Supported Harnesses')}\n </h4>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-2\">\n {HARNESSES.map((h) => (\n <button\n type=\"button\"\n key={h.id}\n onClick={() => {\n setSelectedHarness(h.id);\n setView('profiles');\n }}\n className=\"px-3 py-2.5 rounded-md border border-border-default bg-bg-primary hover:bg-bg-elevated transition-colors text-left\"\n >\n <span className=\"text-xs font-medium text-text-primary\">{h.name}</span>\n <span className=\"block text-[10px] text-text-tertiary font-mono mt-0.5\">\n {h.config}\n </span>\n </button>\n ))}\n </div>\n </div>\n\n {/* Quick Status */}\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.bridle.quickActions', 'Quick Actions')}\n </h4>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => showCommand('bridle status')}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-primary hover:bg-bg-elevated transition-colors flex items-center gap-1.5\"\n >\n <RefreshCw className=\"size-3\" />{' '}\n {tr('vibecontrols.agentManager.bridle.showStatus', 'Show Status')}\n </button>\n <button\n type=\"button\"\n onClick={() => showCommand('bridle init')}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-primary hover:bg-bg-elevated transition-colors flex items-center gap-1.5\"\n >\n <Plus className=\"size-3\" />{' '}\n {tr('vibecontrols.agentManager.bridle.initialize', 'Initialize')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {view === 'profiles' && (\n <div className=\"space-y-4\">\n {/* Harness Selector */}\n <div>\n <label\n htmlFor=\"bridleconfigpanel-5\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Harness\n </label>\n <select\n id=\"bridleconfigpanel-5\"\n value={selectedHarness}\n onChange={(e) => setSelectedHarness(e.target.value)}\n className=\"w-full px-3 py-2 text-sm rounded border border-border-default bg-bg-primary text-text-primary\"\n >\n {HARNESSES.map((h) => (\n <option key={h.id} value={h.id}>\n {h.name}\n </option>\n ))}\n </select>\n </div>\n\n {/* Profile Actions */}\n <div className=\"grid grid-cols-2 gap-2\">\n <button\n type=\"button\"\n onClick={() => showCommand(`bridle profile list ${selectedHarness}`)}\n className=\"px-3 py-2 text-xs rounded border border-border-default bg-bg-primary hover:bg-bg-elevated transition-colors text-left\"\n >\n {tr('vibecontrols.agentManager.bridle.listProfiles', 'List Profiles')}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n showCommand(`bridle profile show ${selectedHarness} ${profileName || '<name>'}`)\n }\n className=\"px-3 py-2 text-xs rounded border border-border-default bg-bg-primary hover:bg-bg-elevated transition-colors text-left\"\n >\n {tr('vibecontrols.agentManager.bridle.showProfile', 'Show Profile')}\n </button>\n </div>\n\n {/* Create Profile */}\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.bridle.profileName', 'Profile Name')}\n </label>\n <div className=\"flex gap-2\">\n <input\n aria-label=\"e.g., work, personal, minimal\"\n type=\"text\"\n value={profileName}\n onChange={(e) => setProfileName(e.target.value)}\n placeholder=\"e.g., work, personal, minimal\"\n className=\"flex-1 px-3 py-1.5 text-xs rounded border border-border-default bg-bg-primary text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong\"\n />\n </div>\n <div className=\"flex gap-2 mt-2\">\n <button\n type=\"button\"\n onClick={() =>\n showCommand(`bridle profile create ${selectedHarness} ${profileName}`)\n }\n disabled={!profileName}\n className=\"px-3 py-1.5 text-xs rounded bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50 flex items-center gap-1\"\n >\n <Plus className=\"size-3\" /> Create\n </button>\n <button\n type=\"button\"\n onClick={() =>\n showCommand(\n `bridle profile create ${selectedHarness} ${profileName} --from-current`\n )\n }\n disabled={!profileName}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-primary hover:bg-bg-elevated disabled:opacity-50 flex items-center gap-1\"\n >\n <Copy className=\"size-3\" /> From Current\n </button>\n <button\n type=\"button\"\n onClick={() =>\n showCommand(`bridle profile switch ${selectedHarness} ${profileName}`)\n }\n disabled={!profileName}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-primary hover:bg-bg-elevated disabled:opacity-50 flex items-center gap-1\"\n >\n <ArrowLeftRight className=\"size-3\" /> Switch\n </button>\n <button\n type=\"button\"\n onClick={() =>\n showCommand(`bridle profile delete ${selectedHarness} ${profileName}`)\n }\n disabled={!profileName}\n className=\"px-3 py-1.5 text-xs rounded border border-status-error-bg text-status-error-text hover:bg-status-error-bg/10 disabled:opacity-50 flex items-center gap-1\"\n >\n <Trash2 className=\"size-3\" /> Delete\n </button>\n </div>\n </div>\n </div>\n )}\n\n {view === 'install' && (\n <div className=\"space-y-4\">\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.bridle.installFromGithub', 'Install from GitHub')}\n </h4>\n <p className=\"text-xs text-text-secondary mb-3\">\n {tr(\n 'vibecontrols.agentManager.bridle.installDescription',\n 'Install skills, agents, commands, or MCPs from GitHub repositories.'\n )}\n </p>\n <div className=\"flex gap-2\">\n <input\n aria-label=\"owner/repo or full GitHub URL\"\n type=\"text\"\n value={installSource}\n onChange={(e) => setInstallSource(e.target.value)}\n placeholder=\"owner/repo or full GitHub URL\"\n className=\"flex-1 px-3 py-2 text-sm rounded border border-border-default bg-bg-primary text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong\"\n />\n <button\n type=\"button\"\n onClick={() => showCommand(`bridle install ${installSource}`)}\n disabled={!installSource}\n className=\"px-4 py-2 text-sm rounded bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50 flex items-center gap-1.5\"\n >\n <Download className=\"size-4\" /> Install\n </button>\n </div>\n <p className=\"text-[10px] text-text-tertiary mt-1.5\">\n Use <code className=\"px-1 py-0.5 rounded bg-bg-elevated\">--force</code> to overwrite\n existing installations\n </p>\n </div>\n </div>\n )}\n\n {view === 'settings' && (\n <div className=\"space-y-4\">\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.bridle.configuration', 'Configuration')}\n </h4>\n <div className=\"space-y-3\">\n <div>\n <label\n htmlFor=\"bridleconfigpanel-4\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Key\n </label>\n <input\n id=\"bridleconfigpanel-4\"\n aria-label=\"e.g., editor, default_harness, profile_marker\"\n type=\"text\"\n value={configKey}\n onChange={(e) => setConfigKey(e.target.value)}\n placeholder=\"e.g., editor, default_harness, profile_marker\"\n className=\"w-full px-3 py-1.5 text-xs rounded border border-border-default bg-bg-primary text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong\"\n />\n </div>\n <div>\n <label\n htmlFor=\"bridleconfigpanel-3\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Value\n </label>\n <input\n id=\"bridleconfigpanel-3\"\n aria-label=\"value\"\n type=\"text\"\n value={configValue}\n onChange={(e) => setConfigValue(e.target.value)}\n placeholder=\"value\"\n className=\"w-full px-3 py-1.5 text-xs rounded border border-border-default bg-bg-primary text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong\"\n />\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => showCommand(`bridle config get ${configKey}`)}\n disabled={!configKey}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-primary hover:bg-bg-elevated disabled:opacity-50\"\n >\n Get\n </button>\n <button\n type=\"button\"\n onClick={() => showCommand(`bridle config set ${configKey} ${configValue}`)}\n disabled={!configKey || !configValue}\n className=\"px-3 py-1.5 text-xs rounded bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n Set\n </button>\n </div>\n </div>\n </div>\n\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr(\n 'vibecontrols.agentManager.bridle.availableConfigKeys',\n 'Available Config Keys'\n )}\n </h4>\n <div className=\"space-y-1 text-xs text-text-secondary\">\n <div className=\"flex justify-between px-2 py-1 rounded bg-bg-primary\">\n <code>profile_marker</code>\n <span>Enable debug marker files</span>\n </div>\n <div className=\"flex justify-between px-2 py-1 rounded bg-bg-primary\">\n <code>editor</code>\n <span>Default text editor</span>\n </div>\n <div className=\"flex justify-between px-2 py-1 rounded bg-bg-primary\">\n <code>tui.view</code>\n <span>Initial TUI view</span>\n </div>\n <div className=\"flex justify-between px-2 py-1 rounded bg-bg-primary\">\n <code>default_harness</code>\n <span>Startup harness tab</span>\n </div>\n </div>\n </div>\n </div>\n )}\n\n {/* Output */}\n {output && (\n <div className=\"mt-4 border-t border-border-default pt-3\">\n <div className=\"flex items-center justify-between mb-2\">\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agentManager.bridle.command', 'Command')}\n </h4>\n <button\n type=\"button\"\n onClick={() => setOutput('')}\n className=\"text-[10px] text-text-tertiary hover:text-text-secondary\"\n >\n {tr('vibecontrols.agentManager.bridle.clear', 'Clear')}\n </button>\n </div>\n <pre className=\"p-3 rounded-md bg-bg-sunken text-text-primary text-xs font-mono whitespace-pre-wrap border border-border-default\">\n {output}\n </pre>\n <p className=\"text-[10px] text-text-tertiary mt-1 flex items-center gap-1\">\n {copiedCmd ? (\n <>\n <Check className=\"size-3 text-status-success-text\" />{' '}\n {tr('vibecontrols.agentManager.bridle.copiedToClipboard', 'Copied to clipboard')}\n </>\n ) : (\n tr('vibecontrols.agentManager.bridle.commandCopied', 'Command copied to clipboard')\n )}\n </p>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AA0BA,IAAM,IAAY;CAChB;EACE,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,OAAO;EACR;CACD;EACE,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,OAAO;EACR;CACD;EACE,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,OAAO;EACR;CACD;EACE,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,OAAO;EACR;CACD;EACE,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,OAAO;EACR;CACD;EACE,MAAM;EACN,IAAI;EACJ,QAAQ;EACR,OAAO;EACR;CACF;AAID,SAAgB,EAAkB,EAAE,OAAO,KAAkC;CAC3E,IAAM,IAAK,GAAO,EACZ,CAAC,GAAM,KAAW,EAAoB,WAAW,EACjD,CAAC,GAAiB,KAAsB,EAAS,SAAS,EAC1D,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAe,KAAoB,EAAS,GAAG,EAChD,CAAC,GAAW,KAAgB,EAAS,GAAG,EACxC,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAW,KAAgB,EAAwB,KAAK,EAEzD,IAAkB,GAAa,MAAiB;AAGpD,EAFK,UAAU,UAAU,UAAU,EAAK,EACxC,EAAa,EAAK,EAClB,iBAAiB,EAAa,KAAK,EAAE,IAAK;IACzC,EAAE,CAAC,EAEA,KAAe,MAAgB;AAEnC,EADA,EAAU,KAAK,EAAI,mDAAmD,EACtE,EAAgB,EAAI;IAGhB,IAA4E;EAChF;GAAE,KAAK;GAAY,OAAO;GAAY,MAAM;GAAW;EACvD;GAAE,KAAK;GAAY,OAAO;GAAY,MAAM;GAAgB;EAC5D;GAAE,KAAK;GAAW,OAAO;GAAW,MAAM;GAAU;EACpD;GAAE,KAAK;GAAY,OAAO;GAAY,MAAM;GAAU;EACvD;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAd,CACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAC9B,EAAG,0CAA0C,wCAAwC,CACnF;QACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,gDACA,8EACD;KACC,CAAA,CACA,EAAA,CAAA;IACF,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,KAAK,EAAE,QAAK,UAAO,MAAM,QACjC,kBAAC,UAAD;KACE,MAAK;KAEL,eAAe,EAAQ,EAAI;KAC3B,WAAW,uFACT,MAAS,IACL,qDACA;eAPR,CAUE,kBAAC,GAAD,EAAM,WAAU,YAAa,CAAA,EAC5B,EACM;OAVF,EAUE,CACT;IACE,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,MAAS,cACR,kBAAC,OAAD;MAAK,WAAU;gBAAf,CAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,uDAAuD,sBAAsB;OAC9E,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAU,KAAK,MACd,kBAAC,UAAD;QACE,MAAK;QAEL,eAAe;AAEb,SADA,EAAmB,EAAE,GAAG,EACxB,EAAQ,WAAW;;QAErB,WAAU;kBAPZ,CASE,kBAAC,QAAD;SAAM,WAAU;mBAAyC,EAAE;SAAY,CAAA,EACvE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAE;SACE,CAAA,CACA;UAXF,EAAE,GAWA,CACT;OACE,CAAA,CACF,EAAA,CAAA,EAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,iDAAiD,gBAAgB;OAClE,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAY,gBAAgB;QAC3C,WAAU;kBAHZ;SAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;SAAC;SAChC,EAAG,+CAA+C,cAAc;SAC1D;WACT,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAY,cAAc;QACzC,WAAU;kBAHZ;SAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;SAAC;SAC3B,EAAG,+CAA+C,aAAa;SACzD;UACL;SACF,EAAA,CAAA,CACF;;KAGP,MAAS,cACR,kBAAC,OAAD;MAAK,WAAU;gBAAf;OAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBACX;QAEO,CAAA,EACR,kBAAC,UAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAmB,EAAE,OAAO,MAAM;QACnD,WAAU;kBAET,EAAU,KAAK,MACd,kBAAC,UAAD;SAAmB,OAAO,EAAE;mBACzB,EAAE;SACI,EAFI,EAAE,GAEN,CACT;QACK,CAAA,CACL,EAAA,CAAA;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAY,uBAAuB,IAAkB;SACpE,WAAU;mBAET,EAAG,iDAAiD,gBAAgB;SAC9D,CAAA,EACT,kBAAC,UAAD;SACE,MAAK;SACL,eACE,EAAY,uBAAuB,EAAgB,GAAG,KAAe,WAAW;SAElF,WAAU;mBAET,EAAG,gDAAgD,eAAe;SAC5D,CAAA,CACL;;OAGN,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SAAO,WAAU;mBACd,EAAG,gDAAgD,eAAe;SAC7D,CAAA;QACR,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,SAAD;UACE,cAAW;UACX,MAAK;UACL,OAAO;UACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;UAC/C,aAAY;UACZ,WAAU;UACV,CAAA;SACE,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,UAAD;WACE,MAAK;WACL,eACE,EAAY,yBAAyB,EAAgB,GAAG,IAAc;WAExE,UAAU,CAAC;WACX,WAAU;qBANZ,CAQE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAAA,UACpB;;UACT,kBAAC,UAAD;WACE,MAAK;WACL,eACE,EACE,yBAAyB,EAAgB,GAAG,EAAY,iBACzD;WAEH,UAAU,CAAC;WACX,WAAU;qBARZ,CAUE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAAA,gBACpB;;UACT,kBAAC,UAAD;WACE,MAAK;WACL,eACE,EAAY,yBAAyB,EAAgB,GAAG,IAAc;WAExE,UAAU,CAAC;WACX,WAAU;qBANZ,CAQE,kBAAC,GAAD,EAAgB,WAAU,UAAW,CAAA,EAAA,UAC9B;;UACT,kBAAC,UAAD;WACE,MAAK;WACL,eACE,EAAY,yBAAyB,EAAgB,GAAG,IAAc;WAExE,UAAU,CAAC;WACX,WAAU;qBANZ,CAQE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAAA,UACtB;;UACL;;QACF,EAAA,CAAA;OACF;;KAGP,MAAS,aACR,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD,EAAA,UAAA;OACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,sDAAsD,sBAAsB;QAC7E,CAAA;OACL,kBAAC,KAAD;QAAG,WAAU;kBACV,EACC,uDACA,sEACD;QACC,CAAA;OACJ,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SACE,cAAW;SACX,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAiB,EAAE,OAAO,MAAM;SACjD,aAAY;SACZ,WAAU;SACV,CAAA,EACF,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAY,kBAAkB,IAAgB;SAC7D,UAAU,CAAC;SACX,WAAU;mBAJZ,CAME,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAAA,WACxB;WACL;;OACN,kBAAC,KAAD;QAAG,WAAU;kBAAb;SAAqD;SAC/C,kBAAC,QAAD;UAAM,WAAU;oBAAqC;UAAc,CAAA;;SAErE;;OACA,EAAA,CAAA;MACF,CAAA;KAGP,MAAS,cACR,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,kDAAkD,gBAAgB;OACnE,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA,EACR,kBAAC,SAAD;SACE,IAAG;SACH,cAAW;SACX,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAa,EAAE,OAAO,MAAM;SAC7C,aAAY;SACZ,WAAU;SACV,CAAA,CACE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA,EACR,kBAAC,SAAD;SACE,IAAG;SACH,cAAW;SACX,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;SAC/C,aAAY;SACZ,WAAU;SACV,CAAA,CACE,EAAA,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAY,qBAAqB,IAAY;UAC5D,UAAU,CAAC;UACX,WAAU;oBACX;UAEQ,CAAA,EACT,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAY,qBAAqB,EAAU,GAAG,IAAc;UAC3E,UAAU,CAAC,KAAa,CAAC;UACzB,WAAU;oBACX;UAEQ,CAAA,CACL;;QACF;SACF,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EACC,wDACA,wBACD;OACE,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD,EAAA,UAAM,kBAAqB,CAAA,EAC3B,kBAAC,QAAD,EAAA,UAAM,6BAAgC,CAAA,CAClC;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD,EAAA,UAAM,UAAa,CAAA,EACnB,kBAAC,QAAD,EAAA,UAAM,uBAA0B,CAAA,CAC5B;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD,EAAA,UAAM,YAAe,CAAA,EACrB,kBAAC,QAAD,EAAA,UAAM,oBAAuB,CAAA,CACzB;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD,EAAA,UAAM,mBAAsB,CAAA,EAC5B,kBAAC,QAAD,EAAA,UAAM,uBAA0B,CAAA,CAC5B;;QACF;SACF,EAAA,CAAA,CACF;;KAIP,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,4CAA4C,UAAU;SACvD,CAAA,EACL,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAU,GAAG;SAC5B,WAAU;mBAET,EAAG,0CAA0C,QAAQ;SAC/C,CAAA,CACL;;OACN,kBAAC,OAAD;QAAK,WAAU;kBACZ;QACG,CAAA;OACN,kBAAC,KAAD;QAAG,WAAU;kBACV,IACC,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,GAAD,EAAO,WAAU,mCAAoC,CAAA;SAAC;SACrD,EAAG,sDAAsD,sBAAsB;SAC/E,EAAA,CAAA,GAEH,EAAG,kDAAkD,8BAA8B;QAEnF,CAAA;OACA;;KAEJ;;GACF"}
|
|
1
|
+
{"version":3,"file":"BridleConfigPanel.js","names":[],"sources":["../../../src/components/agent-manager/BridleConfigPanel.tsx"],"sourcesContent":["/**\n * Bridle Configuration Manager Panel.\n *\n * Provides a full UI for managing cross-harness AI assistant configurations\n * via the Bridle CLI tool. Supports profile management, skill installation,\n * and config editing across Claude Code, OpenCode, Goose, Amp, Copilot, and Crush.\n */\n\nimport { useState, useCallback } from 'react';\nimport {\n Terminal,\n Copy,\n Check,\n Plus,\n ArrowLeftRight,\n Trash2,\n Download,\n Settings,\n RefreshCw,\n} from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface BridleConfigPanelProps {\n agent: { id: string; profile: string };\n}\n\nconst HARNESSES = [\n {\n name: 'Claude Code',\n id: 'claude',\n config: '~/.claude/',\n color: 'bg-accent-purple-subtle text-accent-purple',\n },\n {\n name: 'OpenCode',\n id: 'opencode',\n config: '~/.config/opencode/',\n color: 'bg-status-info-bg-subtle text-status-info-text',\n },\n {\n name: 'Goose',\n id: 'goose',\n config: '~/.config/goose/',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n name: 'Amp',\n id: 'amp',\n config: '~/.amp/',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n name: 'Copilot',\n id: 'copilot',\n config: '~/.copilot/',\n color: 'bg-bg-sunken text-text-secondary',\n },\n {\n name: 'Crush',\n id: 'crush',\n config: '~/.config/crush/',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n];\n\ntype PanelView = 'overview' | 'profiles' | 'install' | 'settings';\n\nexport function BridleConfigPanel({ agent: _agent }: BridleConfigPanelProps) {\n const tr = useTr();\n const [view, setView] = useState<PanelView>('overview');\n const [selectedHarness, setSelectedHarness] = useState('claude');\n const [profileName, setProfileName] = useState('');\n const [installSource, setInstallSource] = useState('');\n const [configKey, setConfigKey] = useState('');\n const [configValue, setConfigValue] = useState('');\n const [output, setOutput] = useState('');\n const [copiedCmd, setCopiedCmd] = useState<string | null>(null);\n\n const copyToClipboard = useCallback((text: string) => {\n void navigator.clipboard.writeText(text);\n setCopiedCmd(text);\n setTimeout(() => setCopiedCmd(null), 2000);\n }, []);\n\n const showCommand = (cmd: string) => {\n setOutput(`$ ${cmd}\\n\\nRun this command in your terminal to execute.`);\n copyToClipboard(cmd);\n };\n\n const navItems: Array<{ key: PanelView; label: string; icon: typeof Settings }> = [\n { key: 'overview', label: 'Overview', icon: RefreshCw },\n { key: 'profiles', label: 'Profiles', icon: ArrowLeftRight },\n { key: 'install', label: 'Install', icon: Download },\n { key: 'settings', label: 'Settings', icon: Settings },\n ];\n\n return (\n <div className=\"flex flex-col h-full max-h-[65vh] overflow-hidden\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-default flex-shrink-0\">\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"size-4\" />\n {tr('vibecontrols.agentManager.bridle.title', 'Bridle — Cross-Harness Config Manager')}\n </h3>\n <p className=\"text-[11px] text-text-secondary mt-0.5\">\n {tr(\n 'vibecontrols.agentManager.bridle.description',\n 'Manage profiles, skills, and configurations across all AI coding assistants'\n )}\n </p>\n </div>\n </div>\n\n {/* Sub-navigation */}\n <div className=\"flex gap-1 px-4 py-2 border-b border-border-default flex-shrink-0\">\n {navItems.map(({ key, label, icon: Icon }) => (\n <button\n type=\"button\"\n key={key}\n onClick={() => setView(key)}\n className={`flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded transition-colors ${\n view === key\n ? 'bg-[var(--color-accent-soft)] text-action-primary-bg'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-elevated'\n }`}\n >\n <Icon className=\"size-3.5\" />\n {label}\n </button>\n ))}\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-4\">\n {view === 'overview' && (\n <div className=\"space-y-4\">\n {/* Harness Grid */}\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.bridle.supportedHarnesses', 'Supported Harnesses')}\n </h4>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-2\">\n {HARNESSES.map((h) => (\n <button\n type=\"button\"\n key={h.id}\n onClick={() => {\n setSelectedHarness(h.id);\n setView('profiles');\n }}\n className=\"px-3 py-2.5 rounded-md border border-border-default bg-bg-surface hover:bg-bg-elevated transition-colors text-left\"\n >\n <span className=\"text-xs font-medium text-text-primary\">{h.name}</span>\n <span className=\"block text-[10px] text-text-tertiary font-mono mt-0.5\">\n {h.config}\n </span>\n </button>\n ))}\n </div>\n </div>\n\n {/* Quick Status */}\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.bridle.quickActions', 'Quick Actions')}\n </h4>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => showCommand('bridle status')}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface hover:bg-bg-elevated transition-colors flex items-center gap-1.5\"\n >\n <RefreshCw className=\"size-3\" />{' '}\n {tr('vibecontrols.agentManager.bridle.showStatus', 'Show Status')}\n </button>\n <button\n type=\"button\"\n onClick={() => showCommand('bridle init')}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface hover:bg-bg-elevated transition-colors flex items-center gap-1.5\"\n >\n <Plus className=\"size-3\" />{' '}\n {tr('vibecontrols.agentManager.bridle.initialize', 'Initialize')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {view === 'profiles' && (\n <div className=\"space-y-4\">\n {/* Harness Selector */}\n <div>\n <label\n htmlFor=\"bridleconfigpanel-5\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Harness\n </label>\n <select\n id=\"bridleconfigpanel-5\"\n value={selectedHarness}\n onChange={(e) => setSelectedHarness(e.target.value)}\n className=\"w-full px-3 py-2 text-sm rounded border border-border-default bg-bg-surface text-text-primary\"\n >\n {HARNESSES.map((h) => (\n <option key={h.id} value={h.id}>\n {h.name}\n </option>\n ))}\n </select>\n </div>\n\n {/* Profile Actions */}\n <div className=\"grid grid-cols-2 gap-2\">\n <button\n type=\"button\"\n onClick={() => showCommand(`bridle profile list ${selectedHarness}`)}\n className=\"px-3 py-2 text-xs rounded border border-border-default bg-bg-surface hover:bg-bg-elevated transition-colors text-left\"\n >\n {tr('vibecontrols.agentManager.bridle.listProfiles', 'List Profiles')}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n showCommand(`bridle profile show ${selectedHarness} ${profileName || '<name>'}`)\n }\n className=\"px-3 py-2 text-xs rounded border border-border-default bg-bg-surface hover:bg-bg-elevated transition-colors text-left\"\n >\n {tr('vibecontrols.agentManager.bridle.showProfile', 'Show Profile')}\n </button>\n </div>\n\n {/* Create Profile */}\n <div>\n <label className=\"block text-xs font-medium text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.bridle.profileName', 'Profile Name')}\n </label>\n <div className=\"flex gap-2\">\n <input\n aria-label=\"e.g., work, personal, minimal\"\n type=\"text\"\n value={profileName}\n onChange={(e) => setProfileName(e.target.value)}\n placeholder=\"e.g., work, personal, minimal\"\n className=\"flex-1 px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong\"\n />\n </div>\n <div className=\"flex gap-2 mt-2\">\n <button\n type=\"button\"\n onClick={() =>\n showCommand(`bridle profile create ${selectedHarness} ${profileName}`)\n }\n disabled={!profileName}\n className=\"px-3 py-1.5 text-xs rounded bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50 flex items-center gap-1\"\n >\n <Plus className=\"size-3\" /> Create\n </button>\n <button\n type=\"button\"\n onClick={() =>\n showCommand(\n `bridle profile create ${selectedHarness} ${profileName} --from-current`\n )\n }\n disabled={!profileName}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface hover:bg-bg-elevated disabled:opacity-50 flex items-center gap-1\"\n >\n <Copy className=\"size-3\" /> From Current\n </button>\n <button\n type=\"button\"\n onClick={() =>\n showCommand(`bridle profile switch ${selectedHarness} ${profileName}`)\n }\n disabled={!profileName}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface hover:bg-bg-elevated disabled:opacity-50 flex items-center gap-1\"\n >\n <ArrowLeftRight className=\"size-3\" /> Switch\n </button>\n <button\n type=\"button\"\n onClick={() =>\n showCommand(`bridle profile delete ${selectedHarness} ${profileName}`)\n }\n disabled={!profileName}\n className=\"px-3 py-1.5 text-xs rounded-button border border-status-error-border text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-50 flex items-center gap-1\"\n >\n <Trash2 className=\"size-3\" /> Delete\n </button>\n </div>\n </div>\n </div>\n )}\n\n {view === 'install' && (\n <div className=\"space-y-4\">\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.bridle.installFromGithub', 'Install from GitHub')}\n </h4>\n <p className=\"text-xs text-text-secondary mb-3\">\n {tr(\n 'vibecontrols.agentManager.bridle.installDescription',\n 'Install skills, agents, commands, or MCPs from GitHub repositories.'\n )}\n </p>\n <div className=\"flex gap-2\">\n <input\n aria-label=\"owner/repo or full GitHub URL\"\n type=\"text\"\n value={installSource}\n onChange={(e) => setInstallSource(e.target.value)}\n placeholder=\"owner/repo or full GitHub URL\"\n className=\"flex-1 px-3 py-2 text-sm rounded border border-border-default bg-bg-surface text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong\"\n />\n <button\n type=\"button\"\n onClick={() => showCommand(`bridle install ${installSource}`)}\n disabled={!installSource}\n className=\"px-4 py-2 text-sm rounded bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50 flex items-center gap-1.5\"\n >\n <Download className=\"size-4\" /> Install\n </button>\n </div>\n <p className=\"text-[10px] text-text-tertiary mt-1.5\">\n Use <code className=\"px-1 py-0.5 rounded bg-bg-elevated\">--force</code> to overwrite\n existing installations\n </p>\n </div>\n </div>\n )}\n\n {view === 'settings' && (\n <div className=\"space-y-4\">\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.bridle.configuration', 'Configuration')}\n </h4>\n <div className=\"space-y-3\">\n <div>\n <label\n htmlFor=\"bridleconfigpanel-4\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Key\n </label>\n <input\n id=\"bridleconfigpanel-4\"\n aria-label=\"e.g., editor, default_harness, profile_marker\"\n type=\"text\"\n value={configKey}\n onChange={(e) => setConfigKey(e.target.value)}\n placeholder=\"e.g., editor, default_harness, profile_marker\"\n className=\"w-full px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong\"\n />\n </div>\n <div>\n <label\n htmlFor=\"bridleconfigpanel-3\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Value\n </label>\n <input\n id=\"bridleconfigpanel-3\"\n aria-label=\"value\"\n type=\"text\"\n value={configValue}\n onChange={(e) => setConfigValue(e.target.value)}\n placeholder=\"value\"\n className=\"w-full px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong\"\n />\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => showCommand(`bridle config get ${configKey}`)}\n disabled={!configKey}\n className=\"px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface hover:bg-bg-elevated disabled:opacity-50\"\n >\n Get\n </button>\n <button\n type=\"button\"\n onClick={() => showCommand(`bridle config set ${configKey} ${configValue}`)}\n disabled={!configKey || !configValue}\n className=\"px-3 py-1.5 text-xs rounded bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n Set\n </button>\n </div>\n </div>\n </div>\n\n <div>\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr(\n 'vibecontrols.agentManager.bridle.availableConfigKeys',\n 'Available Config Keys'\n )}\n </h4>\n <div className=\"space-y-1 text-xs text-text-secondary\">\n <div className=\"flex justify-between px-2 py-1 rounded bg-bg-surface\">\n <code>profile_marker</code>\n <span>Enable debug marker files</span>\n </div>\n <div className=\"flex justify-between px-2 py-1 rounded bg-bg-surface\">\n <code>editor</code>\n <span>Default text editor</span>\n </div>\n <div className=\"flex justify-between px-2 py-1 rounded bg-bg-surface\">\n <code>tui.view</code>\n <span>Initial TUI view</span>\n </div>\n <div className=\"flex justify-between px-2 py-1 rounded bg-bg-surface\">\n <code>default_harness</code>\n <span>Startup harness tab</span>\n </div>\n </div>\n </div>\n </div>\n )}\n\n {/* Output */}\n {output && (\n <div className=\"mt-4 border-t border-border-default pt-3\">\n <div className=\"flex items-center justify-between mb-2\">\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agentManager.bridle.command', 'Command')}\n </h4>\n <button\n type=\"button\"\n onClick={() => setOutput('')}\n className=\"text-[10px] text-text-tertiary hover:text-text-secondary\"\n >\n {tr('vibecontrols.agentManager.bridle.clear', 'Clear')}\n </button>\n </div>\n <pre className=\"p-3 rounded-md bg-bg-sunken text-text-primary text-xs font-mono whitespace-pre-wrap border border-border-default\">\n {output}\n </pre>\n <p className=\"text-[10px] text-text-tertiary mt-1 flex items-center gap-1\">\n {copiedCmd ? (\n <>\n <Check className=\"size-3 text-status-success-text\" />{' '}\n {tr('vibecontrols.agentManager.bridle.copiedToClipboard', 'Copied to clipboard')}\n </>\n ) : (\n tr('vibecontrols.agentManager.bridle.commandCopied', 'Command copied to clipboard')\n )}\n </p>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -57,7 +57,7 @@ var p = [t, ...n], m = ({ open: t, agentId: n, onClose: m }) => {
|
|
|
57
57
|
children: /* @__PURE__ */ d(l, { className: "size-4 text-text-secondary" })
|
|
58
58
|
})]
|
|
59
59
|
}), n ? /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ f("div", {
|
|
60
|
-
className: "mb-4 flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-3 py-1.5 focus-within:border-
|
|
60
|
+
className: "mb-4 flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-3 py-1.5 focus-within:border-[var(--color-accent-soft)]",
|
|
61
61
|
children: [
|
|
62
62
|
/* @__PURE__ */ d(c, { className: "size-3.5 text-text-tertiary shrink-0" }),
|
|
63
63
|
/* @__PURE__ */ d("input", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginInstallModal.js","names":[],"sources":["../../../src/components/agent-manager/PluginInstallModal.tsx"],"sourcesContent":["/**\n * PluginInstallModal — install / remove agent plugins from the Agent\n * Manager chat-pane header on /vibecontrols/ai.\n *\n * Lets operators add or remove a provider plugin (claude / codex /\n * gemini / opencode) at any time without leaving the conversation.\n * Wraps the same `PluginHarnessPicker` the AI page uses; the modal is\n * gated on agent connectivity inside the picker itself.\n */\n\nimport { useEffect, useMemo, useState, type FC } from 'react';\nimport { Search, X } from 'lucide-react';\nimport { PluginHarnessPicker } from '@/components/ai/PluginHarnessPicker';\nimport { AI_CORE_PACKAGE, AI_PROVIDER_PACKAGES, getPluginMeta } from '@/constants/pluginCatalog';\nimport { useTr } from '../../shared/hooks/useTr';\n\n// Orchestrator + every supported provider so users can install/remove\n// any of them from the chat window — claude/codex/gemini/opencode plus\n// the AI core itself in case the operator needs to reinstall it.\nconst INSTALLABLE_PACKAGES: readonly string[] = [AI_CORE_PACKAGE, ...AI_PROVIDER_PACKAGES];\n\nexport interface PluginInstallModalProps {\n open: boolean;\n agentId: string | null;\n onClose: () => void;\n}\n\nexport const PluginInstallModal: FC<PluginInstallModalProps> = ({ open, agentId, onClose }) => {\n const tr = useTr();\n const [search, setSearch] = useState('');\n\n // Esc-to-close so the modal feels native.\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [open, onClose]);\n\n // Reset search whenever the modal closes so a fresh open starts clean.\n useEffect(() => {\n if (!open) setSearch('');\n }, [open]);\n\n // Substring filter — case-insensitive, matches displayName,\n // description, OR raw package name. Empty query → full list.\n const filteredPackages = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return INSTALLABLE_PACKAGES;\n return INSTALLABLE_PACKAGES.filter((pkg) => {\n const meta = getPluginMeta(pkg);\n const haystack = [pkg, meta.displayName, meta.description].join(' ').toLowerCase();\n return haystack.includes(q);\n });\n }, [search]);\n\n if (!open) return null;\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"agent-manager-plugin-install-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center\"\n data-testid=\"agent-manager-plugin-install-modal\"\n >\n <button\n type=\"button\"\n aria-label={tr('vibecontrols.agentManager.installModal.dismiss', 'Dismiss')}\n className=\"absolute inset-0 bg-bg-overlay/60 backdrop-blur-sm\"\n onClick={onClose}\n />\n <div className=\"relative z-10 w-full max-w-3xl max-h-[85vh] overflow-y-auto rounded-lg border border-border-default bg-bg-surface p-6 shadow-xl\">\n <div className=\"flex items-start justify-between gap-4 mb-4\">\n <div>\n <h2\n id=\"agent-manager-plugin-install-title\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n {tr('vibecontrols.agentManager.installModal.title', 'Manage AI providers')}\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.installModal.description',\n 'Install or remove provider plugins (Claude, Codex, Gemini, OpenCode) on the connected agent. Changes take effect immediately for new messages.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-md p-1 hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.agentManager.installModal.close', 'Close')}\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n\n {agentId ? (\n <>\n {/* Inline search filter — keeps the modal scannable as the\n supported-provider list grows. Searches displayName,\n description, and the raw package name. */}\n <div className=\"mb-4 flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-3 py-1.5 focus-within:border-
|
|
1
|
+
{"version":3,"file":"PluginInstallModal.js","names":[],"sources":["../../../src/components/agent-manager/PluginInstallModal.tsx"],"sourcesContent":["/**\n * PluginInstallModal — install / remove agent plugins from the Agent\n * Manager chat-pane header on /vibecontrols/ai.\n *\n * Lets operators add or remove a provider plugin (claude / codex /\n * gemini / opencode) at any time without leaving the conversation.\n * Wraps the same `PluginHarnessPicker` the AI page uses; the modal is\n * gated on agent connectivity inside the picker itself.\n */\n\nimport { useEffect, useMemo, useState, type FC } from 'react';\nimport { Search, X } from 'lucide-react';\nimport { PluginHarnessPicker } from '@/components/ai/PluginHarnessPicker';\nimport { AI_CORE_PACKAGE, AI_PROVIDER_PACKAGES, getPluginMeta } from '@/constants/pluginCatalog';\nimport { useTr } from '../../shared/hooks/useTr';\n\n// Orchestrator + every supported provider so users can install/remove\n// any of them from the chat window — claude/codex/gemini/opencode plus\n// the AI core itself in case the operator needs to reinstall it.\nconst INSTALLABLE_PACKAGES: readonly string[] = [AI_CORE_PACKAGE, ...AI_PROVIDER_PACKAGES];\n\nexport interface PluginInstallModalProps {\n open: boolean;\n agentId: string | null;\n onClose: () => void;\n}\n\nexport const PluginInstallModal: FC<PluginInstallModalProps> = ({ open, agentId, onClose }) => {\n const tr = useTr();\n const [search, setSearch] = useState('');\n\n // Esc-to-close so the modal feels native.\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [open, onClose]);\n\n // Reset search whenever the modal closes so a fresh open starts clean.\n useEffect(() => {\n if (!open) setSearch('');\n }, [open]);\n\n // Substring filter — case-insensitive, matches displayName,\n // description, OR raw package name. Empty query → full list.\n const filteredPackages = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return INSTALLABLE_PACKAGES;\n return INSTALLABLE_PACKAGES.filter((pkg) => {\n const meta = getPluginMeta(pkg);\n const haystack = [pkg, meta.displayName, meta.description].join(' ').toLowerCase();\n return haystack.includes(q);\n });\n }, [search]);\n\n if (!open) return null;\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"agent-manager-plugin-install-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center\"\n data-testid=\"agent-manager-plugin-install-modal\"\n >\n <button\n type=\"button\"\n aria-label={tr('vibecontrols.agentManager.installModal.dismiss', 'Dismiss')}\n className=\"absolute inset-0 bg-bg-overlay/60 backdrop-blur-sm\"\n onClick={onClose}\n />\n <div className=\"relative z-10 w-full max-w-3xl max-h-[85vh] overflow-y-auto rounded-lg border border-border-default bg-bg-surface p-6 shadow-xl\">\n <div className=\"flex items-start justify-between gap-4 mb-4\">\n <div>\n <h2\n id=\"agent-manager-plugin-install-title\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n {tr('vibecontrols.agentManager.installModal.title', 'Manage AI providers')}\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.installModal.description',\n 'Install or remove provider plugins (Claude, Codex, Gemini, OpenCode) on the connected agent. Changes take effect immediately for new messages.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-md p-1 hover:bg-bg-sunken transition-colors\"\n aria-label={tr('vibecontrols.agentManager.installModal.close', 'Close')}\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n\n {agentId ? (\n <>\n {/* Inline search filter — keeps the modal scannable as the\n supported-provider list grows. Searches displayName,\n description, and the raw package name. */}\n <div className=\"mb-4 flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-3 py-1.5 focus-within:border-[var(--color-accent-soft)]\">\n <Search className=\"size-3.5 text-text-tertiary shrink-0\" />\n <input\n type=\"text\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={tr(\n 'vibecontrols.agentManager.installModal.searchPlaceholder',\n 'Search providers (Claude, Codex, Gemini, OpenCode…)'\n )}\n aria-label={tr(\n 'vibecontrols.agentManager.installModal.searchAria',\n 'Filter AI providers'\n )}\n data-testid=\"agent-manager-plugin-install-search\"\n className=\"flex-1 bg-transparent text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n />\n {search && (\n <button\n type=\"button\"\n onClick={() => setSearch('')}\n className=\"rounded p-0.5 text-text-tertiary hover:bg-bg-elevated\"\n aria-label={tr(\n 'vibecontrols.agentManager.installModal.clearSearch',\n 'Clear search'\n )}\n >\n <X className=\"size-3\" />\n </button>\n )}\n </div>\n\n {filteredPackages.length === 0 ? (\n <div className=\"rounded-md border border-border-default bg-bg-sunken px-3 py-6 text-center text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.installModal.noMatches',\n 'No providers match your search.'\n )}\n </div>\n ) : (\n <PluginHarnessPicker\n agentId={agentId}\n packages={filteredPackages}\n title={tr(\n 'vibecontrols.agentManager.installModal.providersHeading',\n 'Available providers'\n )}\n description={tr(\n 'vibecontrols.agentManager.installModal.providersHint',\n 'Click Install to add a provider, or Remove to uninstall one.'\n )}\n />\n )}\n </>\n ) : (\n <div className=\"rounded-md border border-border-default bg-bg-sunken px-3 py-4 text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.installModal.noAgent',\n 'Connect an agent in Targets & Agents before installing AI providers.'\n )}\n </div>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAmBA,IAAM,IAA0C,CAAC,GAAiB,GAAG,EAAqB,EAQ7E,KAAmD,EAAE,SAAM,YAAS,iBAAc;CAC7F,IAAM,IAAK,GAAO,EACZ,CAAC,GAAQ,KAAa,EAAS,GAAG;AAaxC,CAVA,QAAgB;AACd,MAAI,CAAC,EAAM;EACX,IAAM,KAAS,MAAqB;AAClC,GAAI,EAAE,QAAQ,YAAU,GAAS;;AAGnC,SADA,OAAO,iBAAiB,WAAW,EAAM,QAC5B,OAAO,oBAAoB,WAAW,EAAM;IACxD,CAAC,GAAM,EAAQ,CAAC,EAGnB,QAAgB;AACd,EAAK,KAAM,EAAU,GAAG;IACvB,CAAC,EAAK,CAAC;CAIV,IAAM,IAAmB,QAAc;EACrC,IAAM,IAAI,EAAO,MAAM,CAAC,aAAa;AAErC,SADK,IACE,EAAqB,QAAQ,MAAQ;GAC1C,IAAM,IAAO,EAAc,EAAI;AAE/B,UADiB;IAAC;IAAK,EAAK;IAAa,EAAK;IAAY,CAAC,KAAK,IAAI,CAAC,aAAa,CAClE,SAAS,EAAE;IAC3B,GALa;IAMd,CAAC,EAAO,CAAC;AAIZ,QAFK,IAGH,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,mBAAgB;EAChB,WAAU;EACV,eAAY;YALd,CAOE,kBAAC,UAAD;GACE,MAAK;GACL,cAAY,EAAG,kDAAkD,UAAU;GAC3E,WAAU;GACV,SAAS;GACT,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KACE,IAAG;KACH,WAAU;eAET,EAAG,gDAAgD,sBAAsB;KACvE,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,sDACA,iJACD;KACC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;KACV,cAAY,EAAG,gDAAgD,QAAQ;eAEvE,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;KACrC,CAAA,CACL;OAEL,IACC,kBAAA,GAAA,EAAA,UAAA,CAIE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAQ,WAAU,wCAAyC,CAAA;KAC3D,kBAAC,SAAD;MACE,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;MAC1C,aAAa,EACX,4DACA,sDACD;MACD,cAAY,EACV,qDACA,sBACD;MACD,eAAY;MACZ,WAAU;MACV,CAAA;KACD,KACC,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAU,GAAG;MAC5B,WAAU;MACV,cAAY,EACV,sDACA,eACD;gBAED,kBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;MACjB,CAAA;KAEP;OAEL,EAAiB,WAAW,IAC3B,kBAAC,OAAD;IAAK,WAAU;cACZ,EACC,oDACA,kCACD;IACG,CAAA,GAEN,kBAAC,GAAD;IACW;IACT,UAAU;IACV,OAAO,EACL,2DACA,sBACD;IACD,aAAa,EACX,wDACA,+DACD;IACD,CAAA,CAEH,EAAA,CAAA,GAEH,kBAAC,OAAD;IAAK,WAAU;cACZ,EACC,kDACA,uEACD;IACG,CAAA,CAEJ;KACF;MA9GU"}
|
|
@@ -114,7 +114,7 @@ function y({ agentId: d }) {
|
|
|
114
114
|
})]
|
|
115
115
|
}),
|
|
116
116
|
A && /* @__PURE__ */ p("div", {
|
|
117
|
-
className: "px-4 py-2 bg-status-error-bg
|
|
117
|
+
className: "px-4 py-2 bg-status-error-bg-subtle border-b border-status-error-border text-sm text-status-error-text",
|
|
118
118
|
children: A
|
|
119
119
|
}),
|
|
120
120
|
w && /* @__PURE__ */ m("div", {
|
|
@@ -186,7 +186,7 @@ function y({ agentId: d }) {
|
|
|
186
186
|
children: y.map((e) => {
|
|
187
187
|
let n = e.name === b, r = t(e);
|
|
188
188
|
return /* @__PURE__ */ m("div", {
|
|
189
|
-
className: `p-4 flex items-center justify-between gap-4 ${n ? "bg-
|
|
189
|
+
className: `p-4 flex items-center justify-between gap-4 ${n ? "bg-[var(--color-accent-soft)]" : ""}`,
|
|
190
190
|
children: [/* @__PURE__ */ m("div", {
|
|
191
191
|
className: "flex items-center gap-3 min-w-0",
|
|
192
192
|
children: [/* @__PURE__ */ p("span", {
|
|
@@ -202,11 +202,11 @@ function y({ agentId: d }) {
|
|
|
202
202
|
children: e.name
|
|
203
203
|
}),
|
|
204
204
|
e.isDefault && /* @__PURE__ */ m("span", {
|
|
205
|
-
className: "inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-
|
|
205
|
+
className: "inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-[var(--color-accent-soft)] text-action-primary-bg rounded",
|
|
206
206
|
children: [/* @__PURE__ */ p(c, { className: "size-3" }), "Default"]
|
|
207
207
|
}),
|
|
208
208
|
n && /* @__PURE__ */ p("span", {
|
|
209
|
-
className: "px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-status-success-bg
|
|
209
|
+
className: "px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-status-success-bg-subtle text-status-success-text rounded",
|
|
210
210
|
children: "Active"
|
|
211
211
|
})
|
|
212
212
|
]
|
|
@@ -242,7 +242,7 @@ function y({ agentId: d }) {
|
|
|
242
242
|
disabled: V || e.isDefault,
|
|
243
243
|
title: e.isDefault ? "Cannot delete the default profile" : "Delete",
|
|
244
244
|
onClick: () => void z(e),
|
|
245
|
-
className: "inline-flex items-center gap-1 px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg
|
|
245
|
+
className: "inline-flex items-center gap-1 px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg-subtle border border-status-error-border rounded disabled:opacity-50",
|
|
246
246
|
children: [/* @__PURE__ */ p(l, { className: "size-3" }), "Delete"]
|
|
247
247
|
})
|
|
248
248
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfilesPanel.js","names":[],"sources":["../../../src/components/agent-manager/ProfilesPanel.tsx"],"sourcesContent":["/**\n * ProfilesPanel — Phase 5B (multi-profile-per-daemon).\n *\n * Mounted on AgentDetailsPage as the \"Profiles\" tab. Lists every\n * `AgentProfile` row for the current agent and exposes full CRUDL:\n * - List with status indicators\n * - Create (autofilled name \"default\" but editable)\n * - Delete\n * - Set-default\n *\n * Apollo `gql` documents are inlined so the component compiles cleanly\n * before Phase 5A (svc) publishes the AgentProfile schema to Hive.\n */\n\nimport { useCallback, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { CheckCircle2, Loader2, Plus, Star, Trash2, X } from 'lucide-react';\nimport { Card } from '@/components/shared';\nimport {\n useActiveAgentProfile,\n isUsableProfile,\n type AgentProfile,\n} from '@/hooks/useActiveAgentProfile';\n\ninterface ProfilesPanelProps {\n agentId: string;\n}\n\ninterface CreateAgentProfileInput {\n agentId: string;\n name: string;\n port?: number | null;\n}\n\ninterface CreateProfileData {\n createVibecontrolsAgentProfile: Pick<AgentProfile, 'id' | 'name' | 'isDefault' | 'status'>;\n}\n\ninterface CreateProfileVars {\n input: CreateAgentProfileInput;\n}\n\ninterface DeleteProfileData {\n deleteVibecontrolsAgentProfile: boolean;\n}\n\ninterface DeleteProfileVars {\n id: string;\n}\n\ninterface SetDefaultProfileData {\n setDefaultVibecontrolsAgentProfile: Pick<AgentProfile, 'id' | 'isDefault'>;\n}\n\ninterface SetDefaultProfileVars {\n id: string;\n}\n\nconst CREATE_PROFILE_MUTATION = gql`\n mutation CreateVibecontrolsAgentProfile($input: CreateVibecontrolsAgentProfileInput!) {\n createVibecontrolsAgentProfile(input: $input) {\n id\n name\n isDefault\n status\n }\n }\n`;\n\nconst DELETE_PROFILE_MUTATION = gql`\n mutation DeleteVibecontrolsAgentProfile($id: ID!) {\n deleteVibecontrolsAgentProfile(id: $id)\n }\n`;\n\nconst SET_DEFAULT_PROFILE_MUTATION = gql`\n mutation SetDefaultVibecontrolsAgentProfile($id: ID!) {\n setDefaultVibecontrolsAgentProfile(id: $id) {\n id\n isDefault\n }\n }\n`;\n\nfunction statusDot(status: string | null): string {\n const s = (status ?? '').toLowerCase();\n if (s === 'running' || s === 'active' || s === 'healthy') return 'bg-status-success-text';\n if (s === 'stopped' || s === 'inactive') return 'bg-text-placeholder';\n if (s === 'error' || s === 'failed') return 'bg-status-error-text';\n return 'bg-status-warning-text';\n}\n\nexport function ProfilesPanel({ agentId }: ProfilesPanelProps) {\n const { profiles, active, setActive, loading, refetch } = useActiveAgentProfile(agentId);\n\n const [showCreate, setShowCreate] = useState(false);\n const [newName, setNewName] = useState('default');\n const [newPort, setNewPort] = useState<string>('');\n const [actionError, setActionError] = useState<string | null>(null);\n\n const [createProfile, { loading: creating }] = useMutation<CreateProfileData, CreateProfileVars>(\n CREATE_PROFILE_MUTATION\n );\n\n const [deleteProfile, { loading: deleting }] = useMutation<DeleteProfileData, DeleteProfileVars>(\n DELETE_PROFILE_MUTATION\n );\n\n const [setDefaultProfile, { loading: settingDefault }] = useMutation<\n SetDefaultProfileData,\n SetDefaultProfileVars\n >(SET_DEFAULT_PROFILE_MUTATION);\n\n const handleCreate = useCallback(async () => {\n setActionError(null);\n const trimmedName = newName.trim();\n if (!trimmedName) {\n setActionError('Profile name is required');\n return;\n }\n const portNum = newPort.trim() ? Number(newPort.trim()) : null;\n if (newPort.trim() && (Number.isNaN(portNum) || portNum === null || portNum <= 0)) {\n setActionError('Port must be a positive number');\n return;\n }\n try {\n await createProfile({\n variables: {\n input: { agentId, name: trimmedName, port: portNum },\n },\n });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to create profile');\n return;\n }\n setShowCreate(false);\n setNewName('default');\n setNewPort('');\n // The mutation already succeeded — refresh the list off the same observable\n // the panel renders from so the new profile shows without a manual\n // \"Refresh\". refetch() is non-throwing, so a refresh hiccup can never\n // surface as a \"Failed to create\" error; the list re-syncs on the next action.\n await refetch();\n }, [agentId, createProfile, newName, newPort, refetch]);\n\n const handleDelete = useCallback(\n async (profile: AgentProfile) => {\n setActionError(null);\n if (!window.confirm(`Delete profile \"${profile.name}\"? This cannot be undone.`)) {\n return;\n }\n try {\n await deleteProfile({ variables: { id: profile.id } });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to delete profile');\n return;\n }\n // Refresh after a confirmed delete; refetch() is non-throwing so a\n // network hiccup can't masquerade as a failed delete (see handleCreate).\n await refetch();\n },\n [deleteProfile, refetch]\n );\n\n const handleSetDefault = useCallback(\n async (profile: AgentProfile) => {\n setActionError(null);\n try {\n await setDefaultProfile({ variables: { id: profile.id } });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to set default profile');\n return;\n }\n // Refresh after a confirmed change; refetch() is non-throwing so a\n // network hiccup can't masquerade as a failed update (see handleCreate).\n await refetch();\n },\n [setDefaultProfile, refetch]\n );\n\n const busy = creating || deleting || settingDefault;\n\n return (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <div>\n <h3 className=\"font-semibold text-text-primary\">Profiles</h3>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Each profile runs an isolated CLI on this agent. Active:{' '}\n <code className=\"font-mono text-text-primary\">{active}</code>\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n // Fire-and-forget from a click handler: refetch() is non-throwing,\n // so ignoring the returned promise can't leak an unhandled rejection.\n onClick={() => void refetch()}\n className=\"px-2 py-1 text-xs text-text-secondary hover:text-text-primary\"\n >\n Refresh\n </button>\n <button\n type=\"button\"\n onClick={() => setShowCreate((v) => !v)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90\"\n >\n <Plus className=\"size-4\" />\n New profile\n </button>\n </div>\n </div>\n\n {actionError && (\n <div className=\"px-4 py-2 bg-status-error-bg/30 border-b border-status-error-text/20 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {showCreate && (\n <div className=\"p-4 border-b border-border-default bg-bg-sunken/40 space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor=\"profilespanel-2\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Profile name\n </label>\n <input\n id=\"profilespanel-2\"\n aria-label=\"default\"\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder=\"default\"\n className=\"w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n </div>\n <div>\n <label\n htmlFor=\"profilespanel-1\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Port (optional)\n </label>\n <input\n id=\"profilespanel-1\"\n aria-label=\"auto\"\n type=\"number\"\n value={newPort}\n onChange={(e) => setNewPort(e.target.value)}\n placeholder=\"auto\"\n className=\"w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={creating}\n onClick={() => void handleCreate()}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90 disabled:opacity-50\"\n >\n {creating ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"size-3.5\" />\n )}\n Create\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setShowCreate(false);\n setActionError(null);\n }}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary\"\n >\n <X className=\"size-3.5\" />\n Cancel\n </button>\n </div>\n </div>\n )}\n\n {loading && profiles.length === 0 ? (\n <div className=\"p-6 text-center text-text-secondary\">\n <Loader2 className=\"size-5 mx-auto mb-2 animate-spin\" />\n Loading profiles…\n </div>\n ) : profiles.length === 0 ? (\n <div className=\"p-6 text-center text-text-secondary\">\n <p>No profiles configured yet.</p>\n <p className=\"text-xs mt-1\">\n The agent will fall back to the implicit <code className=\"font-mono\">default</code>{' '}\n profile until you create one.\n </p>\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {profiles.map((profile) => {\n const isActive = profile.name === active;\n // A never-started (awaiting-config) profile can't be the active\n // one — selecting it would just bounce back to a usable profile.\n // Disable \"Use\" and explain why instead of silently reverting.\n const usable = isUsableProfile(profile);\n return (\n <div\n key={profile.id}\n className={`p-4 flex items-center justify-between gap-4 ${\n isActive ? 'bg-action-primary-bg/5' : ''\n }`}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span\n className={`size-2 rounded-full flex-shrink-0 ${statusDot(profile.status)}`}\n aria-hidden\n />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <code className=\"font-mono font-medium text-text-primary truncate\">\n {profile.name}\n </code>\n {profile.isDefault && (\n <span className=\"inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-action-primary-bg/10 text-action-primary-bg rounded\">\n <Star className=\"size-3\" />\n Default\n </span>\n )}\n {isActive && (\n <span className=\"px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-status-success-bg/40 text-status-success-text rounded\">\n Active\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5 truncate\">\n {profile.status ?? 'unknown'}\n {profile.port ? ` · port ${profile.port}` : ''}\n {profile.tunnelUrl ? ` · ${profile.tunnelUrl}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {!isActive && (\n <button\n type=\"button\"\n disabled={!usable}\n title={\n usable\n ? 'Make this the active profile'\n : \"This profile hasn't been started yet (awaiting config), so it can't be the active profile.\"\n }\n onClick={() => setActive(profile.name)}\n className=\"px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n Use\n </button>\n )}\n {!profile.isDefault && (\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void handleSetDefault(profile)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded disabled:opacity-50\"\n >\n <Star className=\"size-3\" />\n Set default\n </button>\n )}\n <button\n type=\"button\"\n disabled={busy || profile.isDefault}\n title={profile.isDefault ? 'Cannot delete the default profile' : 'Delete'}\n onClick={() => void handleDelete(profile)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg/30 border border-status-error-text/20 rounded disabled:opacity-50\"\n >\n <Trash2 className=\"size-3\" />\n Delete\n </button>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProfilesPanel.js","names":[],"sources":["../../../src/components/agent-manager/ProfilesPanel.tsx"],"sourcesContent":["/**\n * ProfilesPanel — Phase 5B (multi-profile-per-daemon).\n *\n * Mounted on AgentDetailsPage as the \"Profiles\" tab. Lists every\n * `AgentProfile` row for the current agent and exposes full CRUDL:\n * - List with status indicators\n * - Create (autofilled name \"default\" but editable)\n * - Delete\n * - Set-default\n *\n * Apollo `gql` documents are inlined so the component compiles cleanly\n * before Phase 5A (svc) publishes the AgentProfile schema to Hive.\n */\n\nimport { useCallback, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { CheckCircle2, Loader2, Plus, Star, Trash2, X } from 'lucide-react';\nimport { Card } from '@/components/shared';\nimport {\n useActiveAgentProfile,\n isUsableProfile,\n type AgentProfile,\n} from '@/hooks/useActiveAgentProfile';\n\ninterface ProfilesPanelProps {\n agentId: string;\n}\n\ninterface CreateAgentProfileInput {\n agentId: string;\n name: string;\n port?: number | null;\n}\n\ninterface CreateProfileData {\n createVibecontrolsAgentProfile: Pick<AgentProfile, 'id' | 'name' | 'isDefault' | 'status'>;\n}\n\ninterface CreateProfileVars {\n input: CreateAgentProfileInput;\n}\n\ninterface DeleteProfileData {\n deleteVibecontrolsAgentProfile: boolean;\n}\n\ninterface DeleteProfileVars {\n id: string;\n}\n\ninterface SetDefaultProfileData {\n setDefaultVibecontrolsAgentProfile: Pick<AgentProfile, 'id' | 'isDefault'>;\n}\n\ninterface SetDefaultProfileVars {\n id: string;\n}\n\nconst CREATE_PROFILE_MUTATION = gql`\n mutation CreateVibecontrolsAgentProfile($input: CreateVibecontrolsAgentProfileInput!) {\n createVibecontrolsAgentProfile(input: $input) {\n id\n name\n isDefault\n status\n }\n }\n`;\n\nconst DELETE_PROFILE_MUTATION = gql`\n mutation DeleteVibecontrolsAgentProfile($id: ID!) {\n deleteVibecontrolsAgentProfile(id: $id)\n }\n`;\n\nconst SET_DEFAULT_PROFILE_MUTATION = gql`\n mutation SetDefaultVibecontrolsAgentProfile($id: ID!) {\n setDefaultVibecontrolsAgentProfile(id: $id) {\n id\n isDefault\n }\n }\n`;\n\nfunction statusDot(status: string | null): string {\n const s = (status ?? '').toLowerCase();\n if (s === 'running' || s === 'active' || s === 'healthy') return 'bg-status-success-text';\n if (s === 'stopped' || s === 'inactive') return 'bg-text-placeholder';\n if (s === 'error' || s === 'failed') return 'bg-status-error-text';\n return 'bg-status-warning-text';\n}\n\nexport function ProfilesPanel({ agentId }: ProfilesPanelProps) {\n const { profiles, active, setActive, loading, refetch } = useActiveAgentProfile(agentId);\n\n const [showCreate, setShowCreate] = useState(false);\n const [newName, setNewName] = useState('default');\n const [newPort, setNewPort] = useState<string>('');\n const [actionError, setActionError] = useState<string | null>(null);\n\n const [createProfile, { loading: creating }] = useMutation<CreateProfileData, CreateProfileVars>(\n CREATE_PROFILE_MUTATION\n );\n\n const [deleteProfile, { loading: deleting }] = useMutation<DeleteProfileData, DeleteProfileVars>(\n DELETE_PROFILE_MUTATION\n );\n\n const [setDefaultProfile, { loading: settingDefault }] = useMutation<\n SetDefaultProfileData,\n SetDefaultProfileVars\n >(SET_DEFAULT_PROFILE_MUTATION);\n\n const handleCreate = useCallback(async () => {\n setActionError(null);\n const trimmedName = newName.trim();\n if (!trimmedName) {\n setActionError('Profile name is required');\n return;\n }\n const portNum = newPort.trim() ? Number(newPort.trim()) : null;\n if (newPort.trim() && (Number.isNaN(portNum) || portNum === null || portNum <= 0)) {\n setActionError('Port must be a positive number');\n return;\n }\n try {\n await createProfile({\n variables: {\n input: { agentId, name: trimmedName, port: portNum },\n },\n });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to create profile');\n return;\n }\n setShowCreate(false);\n setNewName('default');\n setNewPort('');\n // The mutation already succeeded — refresh the list off the same observable\n // the panel renders from so the new profile shows without a manual\n // \"Refresh\". refetch() is non-throwing, so a refresh hiccup can never\n // surface as a \"Failed to create\" error; the list re-syncs on the next action.\n await refetch();\n }, [agentId, createProfile, newName, newPort, refetch]);\n\n const handleDelete = useCallback(\n async (profile: AgentProfile) => {\n setActionError(null);\n if (!window.confirm(`Delete profile \"${profile.name}\"? This cannot be undone.`)) {\n return;\n }\n try {\n await deleteProfile({ variables: { id: profile.id } });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to delete profile');\n return;\n }\n // Refresh after a confirmed delete; refetch() is non-throwing so a\n // network hiccup can't masquerade as a failed delete (see handleCreate).\n await refetch();\n },\n [deleteProfile, refetch]\n );\n\n const handleSetDefault = useCallback(\n async (profile: AgentProfile) => {\n setActionError(null);\n try {\n await setDefaultProfile({ variables: { id: profile.id } });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to set default profile');\n return;\n }\n // Refresh after a confirmed change; refetch() is non-throwing so a\n // network hiccup can't masquerade as a failed update (see handleCreate).\n await refetch();\n },\n [setDefaultProfile, refetch]\n );\n\n const busy = creating || deleting || settingDefault;\n\n return (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <div>\n <h3 className=\"font-semibold text-text-primary\">Profiles</h3>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Each profile runs an isolated CLI on this agent. Active:{' '}\n <code className=\"font-mono text-text-primary\">{active}</code>\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n // Fire-and-forget from a click handler: refetch() is non-throwing,\n // so ignoring the returned promise can't leak an unhandled rejection.\n onClick={() => void refetch()}\n className=\"px-2 py-1 text-xs text-text-secondary hover:text-text-primary\"\n >\n Refresh\n </button>\n <button\n type=\"button\"\n onClick={() => setShowCreate((v) => !v)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90\"\n >\n <Plus className=\"size-4\" />\n New profile\n </button>\n </div>\n </div>\n\n {actionError && (\n <div className=\"px-4 py-2 bg-status-error-bg-subtle border-b border-status-error-border text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {showCreate && (\n <div className=\"p-4 border-b border-border-default bg-bg-sunken/40 space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor=\"profilespanel-2\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Profile name\n </label>\n <input\n id=\"profilespanel-2\"\n aria-label=\"default\"\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder=\"default\"\n className=\"w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n </div>\n <div>\n <label\n htmlFor=\"profilespanel-1\"\n className=\"block text-xs font-medium text-text-secondary mb-1\"\n >\n Port (optional)\n </label>\n <input\n id=\"profilespanel-1\"\n aria-label=\"auto\"\n type=\"number\"\n value={newPort}\n onChange={(e) => setNewPort(e.target.value)}\n placeholder=\"auto\"\n className=\"w-full px-3 py-1.5 text-sm bg-bg-surface border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg font-mono\"\n />\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={creating}\n onClick={() => void handleCreate()}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:opacity-90 disabled:opacity-50\"\n >\n {creating ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"size-3.5\" />\n )}\n Create\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setShowCreate(false);\n setActionError(null);\n }}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary\"\n >\n <X className=\"size-3.5\" />\n Cancel\n </button>\n </div>\n </div>\n )}\n\n {loading && profiles.length === 0 ? (\n <div className=\"p-6 text-center text-text-secondary\">\n <Loader2 className=\"size-5 mx-auto mb-2 animate-spin\" />\n Loading profiles…\n </div>\n ) : profiles.length === 0 ? (\n <div className=\"p-6 text-center text-text-secondary\">\n <p>No profiles configured yet.</p>\n <p className=\"text-xs mt-1\">\n The agent will fall back to the implicit <code className=\"font-mono\">default</code>{' '}\n profile until you create one.\n </p>\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {profiles.map((profile) => {\n const isActive = profile.name === active;\n // A never-started (awaiting-config) profile can't be the active\n // one — selecting it would just bounce back to a usable profile.\n // Disable \"Use\" and explain why instead of silently reverting.\n const usable = isUsableProfile(profile);\n return (\n <div\n key={profile.id}\n className={`p-4 flex items-center justify-between gap-4 ${\n isActive ? 'bg-[var(--color-accent-soft)]' : ''\n }`}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span\n className={`size-2 rounded-full flex-shrink-0 ${statusDot(profile.status)}`}\n aria-hidden\n />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <code className=\"font-mono font-medium text-text-primary truncate\">\n {profile.name}\n </code>\n {profile.isDefault && (\n <span className=\"inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-[var(--color-accent-soft)] text-action-primary-bg rounded\">\n <Star className=\"size-3\" />\n Default\n </span>\n )}\n {isActive && (\n <span className=\"px-1.5 py-0.5 text-[10px] uppercase tracking-wide bg-status-success-bg-subtle text-status-success-text rounded\">\n Active\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5 truncate\">\n {profile.status ?? 'unknown'}\n {profile.port ? ` · port ${profile.port}` : ''}\n {profile.tunnelUrl ? ` · ${profile.tunnelUrl}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {!isActive && (\n <button\n type=\"button\"\n disabled={!usable}\n title={\n usable\n ? 'Make this the active profile'\n : \"This profile hasn't been started yet (awaiting config), so it can't be the active profile.\"\n }\n onClick={() => setActive(profile.name)}\n className=\"px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n Use\n </button>\n )}\n {!profile.isDefault && (\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void handleSetDefault(profile)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs text-text-secondary hover:text-text-primary border border-border-default rounded disabled:opacity-50\"\n >\n <Star className=\"size-3\" />\n Set default\n </button>\n )}\n <button\n type=\"button\"\n disabled={busy || profile.isDefault}\n title={profile.isDefault ? 'Cannot delete the default profile' : 'Delete'}\n onClick={() => void handleDelete(profile)}\n className=\"inline-flex items-center gap-1 px-2 py-1 text-xs text-status-error-text hover:bg-status-error-bg-subtle border border-status-error-border rounded disabled:opacity-50\"\n >\n <Trash2 className=\"size-3\" />\n Delete\n </button>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
@@ -125,7 +125,7 @@ function p({ value: s, mode: c, onChange: l }) {
|
|
|
125
125
|
return /* @__PURE__ */ d("button", {
|
|
126
126
|
type: "button",
|
|
127
127
|
onClick: () => D(e.value),
|
|
128
|
-
className: `w-full flex items-start gap-2 px-3 py-2 text-left hover:bg-bg-elevated transition-colors ${e.value === s ? "text-action-primary-
|
|
128
|
+
className: `w-full flex items-start gap-2 px-3 py-2 text-left hover:bg-bg-elevated transition-colors ${e.value === s ? "text-action-primary-bg bg-[var(--color-accent-soft)]" : "text-text-primary"}`,
|
|
129
129
|
children: [/* @__PURE__ */ u(t, { className: "size-4 mt-0.5 shrink-0" }), /* @__PURE__ */ d("span", {
|
|
130
130
|
className: "flex flex-col",
|
|
131
131
|
children: [/* @__PURE__ */ u("span", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutonomySelector.js","names":[],"sources":["../../../../src/components/agent-manager/controls/AutonomySelector.tsx"],"sourcesContent":["/**\n * Autonomy (permission mode) selector.\n *\n * Provider-agnostic control for CLI-mode sessions: plan / acceptEdits /\n * fullAuto. Disabled in SDK mode (chat-only). Selecting fullAuto requires an\n * explicit confirmation, since it lets the agent run commands and edit files\n * without asking for approval.\n */\n\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport { Eye, Pencil, Zap, ChevronDown, AlertTriangle } from 'lucide-react';\nimport type { PermissionMode, ProviderMode } from '@/types/agentManager';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface AutonomySelectorProps {\n value: PermissionMode;\n mode: ProviderMode;\n onChange: (mode: PermissionMode) => void;\n}\n\nconst OPTIONS: Array<{\n value: PermissionMode;\n labelKey: string;\n labelFallback: string;\n descKey: string;\n descFallback: string;\n icon: typeof Eye;\n}> = [\n {\n value: 'plan',\n labelKey: 'vibecontrols.agentManager.controls.autonomy.plan',\n labelFallback: 'Plan',\n descKey: 'vibecontrols.agentManager.controls.autonomy.planDesc',\n descFallback: 'Read-only — plan first, no edits or commands.',\n icon: Eye,\n },\n {\n value: 'acceptEdits',\n labelKey: 'vibecontrols.agentManager.controls.autonomy.acceptEdits',\n labelFallback: 'Accept edits',\n descKey: 'vibecontrols.agentManager.controls.autonomy.acceptEditsDesc',\n descFallback: 'Auto-apply file edits; ask before risky commands.',\n icon: Pencil,\n },\n {\n value: 'fullAuto',\n labelKey: 'vibecontrols.agentManager.controls.autonomy.fullAuto',\n labelFallback: 'Full Auto',\n descKey: 'vibecontrols.agentManager.controls.autonomy.fullAutoDesc',\n descFallback: 'Run commands and edit files without asking.',\n icon: Zap,\n },\n];\n\nexport function AutonomySelector({ value, mode, onChange }: AutonomySelectorProps) {\n const tr = useTr();\n const [open, setOpen] = useState(false);\n const [confirmFullAuto, setConfirmFullAuto] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const [pos, setPos] = useState({ bottom: 0, left: 0, width: 0 });\n\n const disabled = mode === 'sdk';\n const current = OPTIONS.find((o) => o.value === value) ?? OPTIONS[1];\n const CurrentIcon = current.icon;\n\n const handleClickOutside = useCallback((e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n }, []);\n\n useEffect(() => {\n if (open) {\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [open, handleClickOutside]);\n\n // Escape closes the dropdown — important for a security-sensitive control\n // where a user may reflexively bail out of the fullAuto confirm.\n useEffect(() => {\n if (!open) return;\n const handleKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') setOpen(false);\n };\n document.addEventListener('keydown', handleKey);\n return () => document.removeEventListener('keydown', handleKey);\n }, [open]);\n\n useEffect(() => {\n if (!open) setConfirmFullAuto(false);\n }, [open]);\n\n const handleToggle = () => {\n if (disabled) return;\n if (!open && buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n setPos({\n bottom: window.innerHeight - rect.top + 4,\n left: rect.left,\n width: Math.max(rect.width, 240),\n });\n }\n setOpen((prev) => !prev);\n };\n\n const select = (next: PermissionMode) => {\n // fullAuto requires explicit confirmation (it runs commands/edits unattended).\n if (next === 'fullAuto') {\n setConfirmFullAuto(true);\n return;\n }\n onChange(next);\n setOpen(false);\n };\n\n return (\n <div ref={containerRef} className=\"relative\">\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={handleToggle}\n disabled={disabled}\n className=\"flex items-center gap-1.5 px-2 py-1 text-xs font-medium rounded border border-border-default bg-bg-surface text-text-secondary hover:text-text-primary hover:border-border-strong transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title={\n disabled\n ? tr(\n 'vibecontrols.agentManager.controls.autonomy.sdkDisabled',\n 'Autonomy applies in CLI mode (SDK is chat-only)'\n )\n : tr(current.labelKey, current.labelFallback)\n }\n >\n <CurrentIcon className=\"size-3.5\" />\n <span>{tr(current.labelKey, current.labelFallback)}</span>\n <ChevronDown className=\"size-3\" />\n </button>\n\n {open && !disabled && (\n <div\n className=\"fixed rounded-lg border border-border-default bg-bg-surface shadow-lg z-[100]\"\n style={{ bottom: pos.bottom, left: pos.left, minWidth: pos.width }}\n >\n {confirmFullAuto ? (\n <div className=\"p-3 space-y-2\" style={{ maxWidth: 280 }}>\n <div className=\"flex items-center gap-1.5 text-status-warning-text text-xs font-semibold\">\n <AlertTriangle className=\"size-3.5\" />\n {tr(\n 'vibecontrols.agentManager.controls.autonomy.confirmTitle',\n 'Enable Full Auto?'\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.controls.autonomy.confirmBody',\n 'Full Auto lets the agent run commands and edit files without asking for approval.'\n )}\n </p>\n <div className=\"flex items-center justify-end gap-2 pt-1\">\n <button\n type=\"button\"\n onClick={() => setConfirmFullAuto(false)}\n className=\"px-2 py-1 text-xs font-medium rounded border border-border-default text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr('vibecontrols.agentManager.controls.autonomy.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n onChange('fullAuto');\n setConfirmFullAuto(false);\n setOpen(false);\n }}\n className=\"px-2 py-1 text-xs font-medium rounded bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.agentManager.controls.autonomy.enable', 'Enable')}\n </button>\n </div>\n </div>\n ) : (\n <div className=\"py-1\">\n {OPTIONS.map((option) => {\n const Icon = option.icon;\n return (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => select(option.value)}\n className={`w-full flex items-start gap-2 px-3 py-2 text-left hover:bg-bg-elevated transition-colors ${\n option.value === value\n ? 'text-action-primary-text bg-action-primary-bg/10'\n : 'text-text-primary'\n }`}\n >\n <Icon className=\"size-4 mt-0.5 shrink-0\" />\n <span className=\"flex flex-col\">\n <span className=\"text-sm\">{tr(option.labelKey, option.labelFallback)}</span>\n <span className=\"text-[11px] text-text-tertiary\">\n {tr(option.descKey, option.descFallback)}\n </span>\n </span>\n </button>\n );\n })}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AutonomySelector.js","names":[],"sources":["../../../../src/components/agent-manager/controls/AutonomySelector.tsx"],"sourcesContent":["/**\n * Autonomy (permission mode) selector.\n *\n * Provider-agnostic control for CLI-mode sessions: plan / acceptEdits /\n * fullAuto. Disabled in SDK mode (chat-only). Selecting fullAuto requires an\n * explicit confirmation, since it lets the agent run commands and edit files\n * without asking for approval.\n */\n\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport { Eye, Pencil, Zap, ChevronDown, AlertTriangle } from 'lucide-react';\nimport type { PermissionMode, ProviderMode } from '@/types/agentManager';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface AutonomySelectorProps {\n value: PermissionMode;\n mode: ProviderMode;\n onChange: (mode: PermissionMode) => void;\n}\n\nconst OPTIONS: Array<{\n value: PermissionMode;\n labelKey: string;\n labelFallback: string;\n descKey: string;\n descFallback: string;\n icon: typeof Eye;\n}> = [\n {\n value: 'plan',\n labelKey: 'vibecontrols.agentManager.controls.autonomy.plan',\n labelFallback: 'Plan',\n descKey: 'vibecontrols.agentManager.controls.autonomy.planDesc',\n descFallback: 'Read-only — plan first, no edits or commands.',\n icon: Eye,\n },\n {\n value: 'acceptEdits',\n labelKey: 'vibecontrols.agentManager.controls.autonomy.acceptEdits',\n labelFallback: 'Accept edits',\n descKey: 'vibecontrols.agentManager.controls.autonomy.acceptEditsDesc',\n descFallback: 'Auto-apply file edits; ask before risky commands.',\n icon: Pencil,\n },\n {\n value: 'fullAuto',\n labelKey: 'vibecontrols.agentManager.controls.autonomy.fullAuto',\n labelFallback: 'Full Auto',\n descKey: 'vibecontrols.agentManager.controls.autonomy.fullAutoDesc',\n descFallback: 'Run commands and edit files without asking.',\n icon: Zap,\n },\n];\n\nexport function AutonomySelector({ value, mode, onChange }: AutonomySelectorProps) {\n const tr = useTr();\n const [open, setOpen] = useState(false);\n const [confirmFullAuto, setConfirmFullAuto] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const [pos, setPos] = useState({ bottom: 0, left: 0, width: 0 });\n\n const disabled = mode === 'sdk';\n const current = OPTIONS.find((o) => o.value === value) ?? OPTIONS[1];\n const CurrentIcon = current.icon;\n\n const handleClickOutside = useCallback((e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n }, []);\n\n useEffect(() => {\n if (open) {\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [open, handleClickOutside]);\n\n // Escape closes the dropdown — important for a security-sensitive control\n // where a user may reflexively bail out of the fullAuto confirm.\n useEffect(() => {\n if (!open) return;\n const handleKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') setOpen(false);\n };\n document.addEventListener('keydown', handleKey);\n return () => document.removeEventListener('keydown', handleKey);\n }, [open]);\n\n useEffect(() => {\n if (!open) setConfirmFullAuto(false);\n }, [open]);\n\n const handleToggle = () => {\n if (disabled) return;\n if (!open && buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n setPos({\n bottom: window.innerHeight - rect.top + 4,\n left: rect.left,\n width: Math.max(rect.width, 240),\n });\n }\n setOpen((prev) => !prev);\n };\n\n const select = (next: PermissionMode) => {\n // fullAuto requires explicit confirmation (it runs commands/edits unattended).\n if (next === 'fullAuto') {\n setConfirmFullAuto(true);\n return;\n }\n onChange(next);\n setOpen(false);\n };\n\n return (\n <div ref={containerRef} className=\"relative\">\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={handleToggle}\n disabled={disabled}\n className=\"flex items-center gap-1.5 px-2 py-1 text-xs font-medium rounded border border-border-default bg-bg-surface text-text-secondary hover:text-text-primary hover:border-border-strong transition-colors disabled:cursor-not-allowed disabled:opacity-40\"\n title={\n disabled\n ? tr(\n 'vibecontrols.agentManager.controls.autonomy.sdkDisabled',\n 'Autonomy applies in CLI mode (SDK is chat-only)'\n )\n : tr(current.labelKey, current.labelFallback)\n }\n >\n <CurrentIcon className=\"size-3.5\" />\n <span>{tr(current.labelKey, current.labelFallback)}</span>\n <ChevronDown className=\"size-3\" />\n </button>\n\n {open && !disabled && (\n <div\n className=\"fixed rounded-lg border border-border-default bg-bg-surface shadow-lg z-[100]\"\n style={{ bottom: pos.bottom, left: pos.left, minWidth: pos.width }}\n >\n {confirmFullAuto ? (\n <div className=\"p-3 space-y-2\" style={{ maxWidth: 280 }}>\n <div className=\"flex items-center gap-1.5 text-status-warning-text text-xs font-semibold\">\n <AlertTriangle className=\"size-3.5\" />\n {tr(\n 'vibecontrols.agentManager.controls.autonomy.confirmTitle',\n 'Enable Full Auto?'\n )}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.agentManager.controls.autonomy.confirmBody',\n 'Full Auto lets the agent run commands and edit files without asking for approval.'\n )}\n </p>\n <div className=\"flex items-center justify-end gap-2 pt-1\">\n <button\n type=\"button\"\n onClick={() => setConfirmFullAuto(false)}\n className=\"px-2 py-1 text-xs font-medium rounded border border-border-default text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr('vibecontrols.agentManager.controls.autonomy.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n onChange('fullAuto');\n setConfirmFullAuto(false);\n setOpen(false);\n }}\n className=\"px-2 py-1 text-xs font-medium rounded bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('vibecontrols.agentManager.controls.autonomy.enable', 'Enable')}\n </button>\n </div>\n </div>\n ) : (\n <div className=\"py-1\">\n {OPTIONS.map((option) => {\n const Icon = option.icon;\n return (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => select(option.value)}\n className={`w-full flex items-start gap-2 px-3 py-2 text-left hover:bg-bg-elevated transition-colors ${\n option.value === value\n ? 'text-action-primary-bg bg-[var(--color-accent-soft)]'\n : 'text-text-primary'\n }`}\n >\n <Icon className=\"size-4 mt-0.5 shrink-0\" />\n <span className=\"flex flex-col\">\n <span className=\"text-sm\">{tr(option.labelKey, option.labelFallback)}</span>\n <span className=\"text-[11px] text-text-tertiary\">\n {tr(option.descKey, option.descFallback)}\n </span>\n </span>\n </button>\n );\n })}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAoBA,IAAM,IAOD;CACH;EACE,OAAO;EACP,UAAU;EACV,eAAe;EACf,SAAS;EACT,cAAc;EACd,MAAM;EACP;CACD;EACE,OAAO;EACP,UAAU;EACV,eAAe;EACf,SAAS;EACT,cAAc;EACd,MAAM;EACP;CACD;EACE,OAAO;EACP,UAAU;EACV,eAAe;EACf,SAAS;EACT,cAAc;EACd,MAAM;EACP;CACF;AAED,SAAgB,EAAiB,EAAE,UAAO,SAAM,eAAmC;CACjF,IAAM,IAAK,GAAO,EACZ,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,IAAe,EAAuB,KAAK,EAC3C,IAAY,EAA0B,KAAK,EAC3C,CAAC,GAAK,KAAU,EAAS;EAAE,QAAQ;EAAG,MAAM;EAAG,OAAO;EAAG,CAAC,EAE1D,IAAW,MAAS,OACpB,IAAU,EAAQ,MAAM,MAAM,EAAE,UAAU,EAAM,IAAI,EAAQ,IAC5D,IAAc,EAAQ,MAEtB,IAAqB,GAAa,MAAkB;AACxD,EAAI,EAAa,WAAW,CAAC,EAAa,QAAQ,SAAS,EAAE,OAAe,IAC1E,EAAQ,GAAM;IAEf,EAAE,CAAC;AAoBN,CAlBA,QAAgB;AACd,MAAI,EAEF,QADA,SAAS,iBAAiB,aAAa,EAAmB,QAC7C,SAAS,oBAAoB,aAAa,EAAmB;IAE3E,CAAC,GAAM,EAAmB,CAAC,EAI9B,QAAgB;AACd,MAAI,CAAC,EAAM;EACX,IAAM,KAAa,MAAqB;AACtC,GAAI,EAAE,QAAQ,YAAU,EAAQ,GAAM;;AAGxC,SADA,SAAS,iBAAiB,WAAW,EAAU,QAClC,SAAS,oBAAoB,WAAW,EAAU;IAC9D,CAAC,EAAK,CAAC,EAEV,QAAgB;AACd,EAAK,KAAM,EAAmB,GAAM;IACnC,CAAC,EAAK,CAAC;CAEV,IAAM,UAAqB;AACrB,UACJ;OAAI,CAAC,KAAQ,EAAU,SAAS;IAC9B,IAAM,IAAO,EAAU,QAAQ,uBAAuB;AACtD,MAAO;KACL,QAAQ,OAAO,cAAc,EAAK,MAAM;KACxC,MAAM,EAAK;KACX,OAAO,KAAK,IAAI,EAAK,OAAO,IAAI;KACjC,CAAC;;AAEJ,MAAS,MAAS,CAAC,EAAK;;IAGpB,KAAU,MAAyB;AAEvC,MAAI,MAAS,YAAY;AACvB,KAAmB,GAAK;AACxB;;AAGF,EADA,EAAS,EAAK,EACd,EAAQ,GAAM;;AAGhB,QACE,kBAAC,OAAD;EAAK,KAAK;EAAc,WAAU;YAAlC,CACE,kBAAC,UAAD;GACE,KAAK;GACL,MAAK;GACL,SAAS;GACC;GACV,WAAU;GACV,OACE,IACI,EACE,2DACA,kDACD,GACD,EAAG,EAAQ,UAAU,EAAQ,cAAc;aAZnD;IAeE,kBAAC,GAAD,EAAa,WAAU,YAAa,CAAA;IACpC,kBAAC,QAAD,EAAA,UAAO,EAAG,EAAQ,UAAU,EAAQ,cAAc,EAAQ,CAAA;IAC1D,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA;IAC3B;MAER,KAAQ,CAAC,KACR,kBAAC,OAAD;GACE,WAAU;GACV,OAAO;IAAE,QAAQ,EAAI;IAAQ,MAAM,EAAI;IAAM,UAAU,EAAI;IAAO;aAEjE,IACC,kBAAC,OAAD;IAAK,WAAU;IAAgB,OAAO,EAAE,UAAU,KAAK;cAAvD;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAe,WAAU,YAAa,CAAA,EACrC,EACC,4DACA,oBACD,CACG;;KACN,kBAAC,KAAD;MAAG,WAAU;gBACV,EACC,2DACA,oFACD;MACC,CAAA;KACJ,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAmB,GAAM;OACxC,WAAU;iBAET,EAAG,sDAAsD,SAAS;OAC5D,CAAA,EACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe;AAGb,QAFA,EAAS,WAAW,EACpB,EAAmB,GAAM,EACzB,EAAQ,GAAM;;OAEhB,WAAU;iBAET,EAAG,sDAAsD,SAAS;OAC5D,CAAA,CACL;;KACF;QAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,MAAW;KACvB,IAAM,IAAO,EAAO;AACpB,YACE,kBAAC,UAAD;MAEE,MAAK;MACL,eAAe,EAAO,EAAO,MAAM;MACnC,WAAW,4FACT,EAAO,UAAU,IACb,yDACA;gBAPR,CAUE,kBAAC,GAAD,EAAM,WAAU,0BAA2B,CAAA,EAC3C,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAW,EAAG,EAAO,UAAU,EAAO,cAAc;QAAQ,CAAA,EAC5E,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,EAAO,SAAS,EAAO,aAAa;QACnC,CAAA,CACF;SACA;QAhBF,EAAO,MAgBL;MAEX;IACE,CAAA;GAEJ,CAAA,CAEJ"}
|
|
@@ -188,7 +188,7 @@ function A({ sdk: i, mode: a, providers: s = [], onSdkChange: c, onModeChange: l
|
|
|
188
188
|
let t = k(n, e.value, a);
|
|
189
189
|
c(e.value), t !== a && l(t), d(!1);
|
|
190
190
|
},
|
|
191
|
-
className: `w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-bg-elevated transition-colors ${e.value === i ? "text-action-primary-
|
|
191
|
+
className: `w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-bg-elevated transition-colors ${e.value === i ? "text-action-primary-bg bg-[var(--color-accent-soft)]" : "text-text-primary"}`,
|
|
192
192
|
children: [
|
|
193
193
|
/* @__PURE__ */ b(t, { className: "size-4" }),
|
|
194
194
|
/* @__PURE__ */ b("span", { children: e.label }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SdkSelector.js","names":[],"sources":["../../../../src/components/agent-manager/controls/SdkSelector.tsx"],"sourcesContent":["/**\n * SDK + mode selector dropdown.\n *\n * Shows the active SDK with an icon. The dropdown lists all available SDKs\n * and includes a mode toggle (SDK / CLI) at the bottom.\n */\n\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport {\n Brain,\n Code,\n Sparkles,\n Terminal,\n Bird,\n Zap,\n Github,\n Hammer,\n Cpu,\n PenTool,\n Map as MapIcon,\n ChevronDown,\n MousePointerClick,\n Network,\n Wand2,\n CloudCog,\n} from 'lucide-react';\nimport type { AgentSdkType, ProviderMode } from '@/types/agentManager';\nimport type { AIProvider } from '@/services/aiApi';\n\ninterface SdkSelectorProps {\n sdk: AgentSdkType;\n mode: ProviderMode;\n providers?: AIProvider[];\n onSdkChange: (sdk: AgentSdkType) => void;\n onModeChange: (mode: ProviderMode) => void;\n}\n\nconst SDK_OPTIONS: Array<{ value: AgentSdkType; label: string; icon: typeof Brain }> = [\n { value: 'claude', label: 'Claude', icon: Brain },\n { value: 'codex', label: 'Codex', icon: Code },\n { value: 'gemini', label: 'Gemini', icon: Sparkles },\n { value: 'opencode', label: 'OpenCode', icon: Terminal },\n { value: 'cursor', label: 'Cursor', icon: MousePointerClick },\n { value: 'openrouter', label: 'OpenRouter', icon: Network },\n { value: 'minimax', label: 'Minimax', icon: Wand2 },\n { value: 'ollama', label: 'Ollama', icon: CloudCog },\n { value: 'openai-compat', label: 'OpenAI-compatible', icon: Brain },\n { value: 'goose', label: 'Goose', icon: Bird },\n { value: 'amp', label: 'Amp', icon: Zap },\n { value: 'copilot', label: 'Copilot', icon: Github },\n { value: 'crush', label: 'Crush', icon: Hammer },\n { value: 'pi', label: 'Pi', icon: Cpu },\n { value: 'aider', label: 'Aider', icon: PenTool },\n { value: 'plandex', label: 'Plandex', icon: MapIcon },\n];\n\n// Fallback when the agent-side `provider.supportedModes` isn't available\n// (offline / fresh install / older meta-plugin). The runtime path through\n// `providerModes()` below prefers the live value when present so adding\n// a new provider here is only a fallback affordance.\nconst STATIC_MODE_SUPPORT: Record<AgentSdkType, ProviderMode[]> = {\n claude: ['sdk', 'cli'],\n codex: ['sdk', 'cli'],\n gemini: ['sdk', 'cli'],\n opencode: ['sdk', 'cli'],\n cursor: ['sdk', 'cli'],\n openrouter: ['sdk'],\n minimax: ['sdk', 'cli'],\n ollama: ['sdk', 'cli'],\n 'openai-compat': ['sdk'],\n goose: ['cli'],\n amp: ['cli'],\n copilot: ['cli'],\n crush: ['cli'],\n pi: ['cli'],\n aider: ['cli'],\n plandex: ['cli'],\n};\n\nconst ALL_MODES: ProviderMode[] = ['sdk', 'cli'];\nconst SDK_VALUES = new Set(SDK_OPTIONS.map((option) => option.value));\n\nfunction isProviderMode(mode: unknown): mode is ProviderMode {\n return mode === 'sdk' || mode === 'cli';\n}\n\nfunction normalizeProviderSdk(provider: AIProvider): AgentSdkType | null {\n const value = (provider.name || provider.pluginName).replace(/^ai-/, '');\n return SDK_VALUES.has(value as AgentSdkType) ? (value as AgentSdkType) : null;\n}\n\nfunction providerModes(provider: AIProvider | undefined, sdk: AgentSdkType): ProviderMode[] {\n const modes = provider?.supportedModes?.filter(isProviderMode);\n return modes && modes.length > 0 ? modes : STATIC_MODE_SUPPORT[sdk];\n}\n\nfunction nextSupportedMode(\n provider: AIProvider | undefined,\n sdk: AgentSdkType,\n currentMode: ProviderMode\n): ProviderMode {\n const supported = providerModes(provider, sdk);\n if (supported.includes(currentMode)) return currentMode;\n if (provider?.defaultMode && supported.includes(provider.defaultMode))\n return provider.defaultMode;\n if (provider?.currentMode && supported.includes(provider.currentMode))\n return provider.currentMode;\n return supported[0] ?? 'cli';\n}\n\nexport function SdkSelector({\n sdk,\n mode,\n providers = [],\n onSdkChange,\n onModeChange,\n}: SdkSelectorProps) {\n const [open, setOpen] = useState(false);\n const [search, setSearch] = useState('');\n const containerRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const [pos, setPos] = useState({ bottom: 0, left: 0, width: 0 });\n\n const providerBySdk = new Map<AgentSdkType, AIProvider>();\n for (const provider of providers) {\n const providerSdk = normalizeProviderSdk(provider);\n if (providerSdk) providerBySdk.set(providerSdk, provider);\n }\n\n const options = (\n providerBySdk.size > 0\n ? SDK_OPTIONS.filter((option) => providerBySdk.has(option.value))\n : SDK_OPTIONS\n ).map((option) => ({\n ...option,\n label: providerBySdk.get(option.value)?.displayName ?? option.label,\n }));\n\n const current = options.find((o) => o.value === sdk) ?? SDK_OPTIONS[0];\n const CurrentIcon = current.icon;\n const currentProvider = providerBySdk.get(sdk);\n const currentModes = providerModes(currentProvider, sdk);\n\n const handleClickOutside = useCallback((e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n }, []);\n\n useEffect(() => {\n if (open) {\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [open, handleClickOutside]);\n\n useEffect(() => {\n if (!open) setSearch('');\n }, [open]);\n\n const filtered = options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()));\n\n const handleToggle = () => {\n if (!open && buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n setPos({\n bottom: window.innerHeight - rect.top + 4,\n left: rect.left,\n width: Math.max(rect.width, 192),\n });\n }\n setOpen((prev) => !prev);\n };\n\n return (\n <div ref={containerRef} className=\"relative\">\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={handleToggle}\n className=\"flex items-center gap-1.5 px-2 py-1 text-xs font-medium rounded border border-border-default bg-bg-surface text-text-secondary hover:text-text-primary hover:border-border-strong transition-colors\"\n title={`${current.label} (${mode.toUpperCase()})`}\n >\n <CurrentIcon className=\"size-3.5\" />\n <span>{current.label}</span>\n <ChevronDown className=\"size-3\" />\n </button>\n\n {open && (\n <div\n className=\"fixed rounded-lg border border-border-default bg-bg-surface shadow-lg z-[100]\"\n style={{ bottom: pos.bottom, left: pos.left, minWidth: pos.width }}\n >\n {/* Search */}\n <input\n aria-label=\"Search...\"\n type=\"text\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder=\"Search...\"\n className=\"w-full px-3 py-1.5 text-xs border-b border-border-default bg-transparent text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n />\n {/* SDK list */}\n <div className=\"py-1 max-h-48 overflow-y-auto\">\n {filtered.map((option) => {\n const Icon = option.icon;\n const optionProvider = providerBySdk.get(option.value);\n const modes = providerModes(optionProvider, option.value);\n return (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => {\n const nextMode = nextSupportedMode(optionProvider, option.value, mode);\n onSdkChange(option.value);\n if (nextMode !== mode) onModeChange(nextMode);\n setOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-bg-elevated transition-colors ${\n option.value === sdk\n ? 'text-action-primary-text bg-action-primary-bg/10'\n : 'text-text-primary'\n }`}\n >\n <Icon className=\"size-4\" />\n <span>{option.label}</span>\n <span className=\"ml-auto flex items-center gap-1\">\n {ALL_MODES.map((optionMode) => (\n <span\n key={optionMode}\n className={`rounded px-1 py-0.5 text-[10px] font-semibold uppercase ${\n modes.includes(optionMode)\n ? 'bg-bg-sunken text-text-secondary'\n : 'border border-border-default text-text-tertiary opacity-50'\n }`}\n >\n {optionMode}\n </span>\n ))}\n </span>\n </button>\n );\n })}\n </div>\n\n {/* Mode toggle */}\n <div className=\"border-t border-border-default px-3 py-2\">\n <div className=\"flex items-center rounded-md bg-bg-sunken p-0.5\">\n <button\n type=\"button\"\n onClick={() => currentModes.includes('sdk') && onModeChange('sdk')}\n disabled={!currentModes.includes('sdk')}\n className={`flex-1 px-2 py-1 text-xs font-medium rounded transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${\n mode === 'sdk'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-tertiary hover:text-text-secondary'\n }`}\n title={\n currentModes.includes('sdk')\n ? 'Use SDK mode'\n : `${current.label} does not support SDK mode`\n }\n >\n SDK\n </button>\n <button\n type=\"button\"\n onClick={() => currentModes.includes('cli') && onModeChange('cli')}\n disabled={!currentModes.includes('cli')}\n className={`flex-1 px-2 py-1 text-xs font-medium rounded transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${\n mode === 'cli'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-tertiary hover:text-text-secondary'\n }`}\n title={\n currentModes.includes('cli')\n ? 'Use CLI mode'\n : `${current.label} does not support CLI mode`\n }\n >\n CLI\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SdkSelector.js","names":[],"sources":["../../../../src/components/agent-manager/controls/SdkSelector.tsx"],"sourcesContent":["/**\n * SDK + mode selector dropdown.\n *\n * Shows the active SDK with an icon. The dropdown lists all available SDKs\n * and includes a mode toggle (SDK / CLI) at the bottom.\n */\n\nimport { useState, useRef, useEffect, useCallback } from 'react';\nimport {\n Brain,\n Code,\n Sparkles,\n Terminal,\n Bird,\n Zap,\n Github,\n Hammer,\n Cpu,\n PenTool,\n Map as MapIcon,\n ChevronDown,\n MousePointerClick,\n Network,\n Wand2,\n CloudCog,\n} from 'lucide-react';\nimport type { AgentSdkType, ProviderMode } from '@/types/agentManager';\nimport type { AIProvider } from '@/services/aiApi';\n\ninterface SdkSelectorProps {\n sdk: AgentSdkType;\n mode: ProviderMode;\n providers?: AIProvider[];\n onSdkChange: (sdk: AgentSdkType) => void;\n onModeChange: (mode: ProviderMode) => void;\n}\n\nconst SDK_OPTIONS: Array<{ value: AgentSdkType; label: string; icon: typeof Brain }> = [\n { value: 'claude', label: 'Claude', icon: Brain },\n { value: 'codex', label: 'Codex', icon: Code },\n { value: 'gemini', label: 'Gemini', icon: Sparkles },\n { value: 'opencode', label: 'OpenCode', icon: Terminal },\n { value: 'cursor', label: 'Cursor', icon: MousePointerClick },\n { value: 'openrouter', label: 'OpenRouter', icon: Network },\n { value: 'minimax', label: 'Minimax', icon: Wand2 },\n { value: 'ollama', label: 'Ollama', icon: CloudCog },\n { value: 'openai-compat', label: 'OpenAI-compatible', icon: Brain },\n { value: 'goose', label: 'Goose', icon: Bird },\n { value: 'amp', label: 'Amp', icon: Zap },\n { value: 'copilot', label: 'Copilot', icon: Github },\n { value: 'crush', label: 'Crush', icon: Hammer },\n { value: 'pi', label: 'Pi', icon: Cpu },\n { value: 'aider', label: 'Aider', icon: PenTool },\n { value: 'plandex', label: 'Plandex', icon: MapIcon },\n];\n\n// Fallback when the agent-side `provider.supportedModes` isn't available\n// (offline / fresh install / older meta-plugin). The runtime path through\n// `providerModes()` below prefers the live value when present so adding\n// a new provider here is only a fallback affordance.\nconst STATIC_MODE_SUPPORT: Record<AgentSdkType, ProviderMode[]> = {\n claude: ['sdk', 'cli'],\n codex: ['sdk', 'cli'],\n gemini: ['sdk', 'cli'],\n opencode: ['sdk', 'cli'],\n cursor: ['sdk', 'cli'],\n openrouter: ['sdk'],\n minimax: ['sdk', 'cli'],\n ollama: ['sdk', 'cli'],\n 'openai-compat': ['sdk'],\n goose: ['cli'],\n amp: ['cli'],\n copilot: ['cli'],\n crush: ['cli'],\n pi: ['cli'],\n aider: ['cli'],\n plandex: ['cli'],\n};\n\nconst ALL_MODES: ProviderMode[] = ['sdk', 'cli'];\nconst SDK_VALUES = new Set(SDK_OPTIONS.map((option) => option.value));\n\nfunction isProviderMode(mode: unknown): mode is ProviderMode {\n return mode === 'sdk' || mode === 'cli';\n}\n\nfunction normalizeProviderSdk(provider: AIProvider): AgentSdkType | null {\n const value = (provider.name || provider.pluginName).replace(/^ai-/, '');\n return SDK_VALUES.has(value as AgentSdkType) ? (value as AgentSdkType) : null;\n}\n\nfunction providerModes(provider: AIProvider | undefined, sdk: AgentSdkType): ProviderMode[] {\n const modes = provider?.supportedModes?.filter(isProviderMode);\n return modes && modes.length > 0 ? modes : STATIC_MODE_SUPPORT[sdk];\n}\n\nfunction nextSupportedMode(\n provider: AIProvider | undefined,\n sdk: AgentSdkType,\n currentMode: ProviderMode\n): ProviderMode {\n const supported = providerModes(provider, sdk);\n if (supported.includes(currentMode)) return currentMode;\n if (provider?.defaultMode && supported.includes(provider.defaultMode))\n return provider.defaultMode;\n if (provider?.currentMode && supported.includes(provider.currentMode))\n return provider.currentMode;\n return supported[0] ?? 'cli';\n}\n\nexport function SdkSelector({\n sdk,\n mode,\n providers = [],\n onSdkChange,\n onModeChange,\n}: SdkSelectorProps) {\n const [open, setOpen] = useState(false);\n const [search, setSearch] = useState('');\n const containerRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const [pos, setPos] = useState({ bottom: 0, left: 0, width: 0 });\n\n const providerBySdk = new Map<AgentSdkType, AIProvider>();\n for (const provider of providers) {\n const providerSdk = normalizeProviderSdk(provider);\n if (providerSdk) providerBySdk.set(providerSdk, provider);\n }\n\n const options = (\n providerBySdk.size > 0\n ? SDK_OPTIONS.filter((option) => providerBySdk.has(option.value))\n : SDK_OPTIONS\n ).map((option) => ({\n ...option,\n label: providerBySdk.get(option.value)?.displayName ?? option.label,\n }));\n\n const current = options.find((o) => o.value === sdk) ?? SDK_OPTIONS[0];\n const CurrentIcon = current.icon;\n const currentProvider = providerBySdk.get(sdk);\n const currentModes = providerModes(currentProvider, sdk);\n\n const handleClickOutside = useCallback((e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n }, []);\n\n useEffect(() => {\n if (open) {\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [open, handleClickOutside]);\n\n useEffect(() => {\n if (!open) setSearch('');\n }, [open]);\n\n const filtered = options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()));\n\n const handleToggle = () => {\n if (!open && buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n setPos({\n bottom: window.innerHeight - rect.top + 4,\n left: rect.left,\n width: Math.max(rect.width, 192),\n });\n }\n setOpen((prev) => !prev);\n };\n\n return (\n <div ref={containerRef} className=\"relative\">\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={handleToggle}\n className=\"flex items-center gap-1.5 px-2 py-1 text-xs font-medium rounded border border-border-default bg-bg-surface text-text-secondary hover:text-text-primary hover:border-border-strong transition-colors\"\n title={`${current.label} (${mode.toUpperCase()})`}\n >\n <CurrentIcon className=\"size-3.5\" />\n <span>{current.label}</span>\n <ChevronDown className=\"size-3\" />\n </button>\n\n {open && (\n <div\n className=\"fixed rounded-lg border border-border-default bg-bg-surface shadow-lg z-[100]\"\n style={{ bottom: pos.bottom, left: pos.left, minWidth: pos.width }}\n >\n {/* Search */}\n <input\n aria-label=\"Search...\"\n type=\"text\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder=\"Search...\"\n className=\"w-full px-3 py-1.5 text-xs border-b border-border-default bg-transparent text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n />\n {/* SDK list */}\n <div className=\"py-1 max-h-48 overflow-y-auto\">\n {filtered.map((option) => {\n const Icon = option.icon;\n const optionProvider = providerBySdk.get(option.value);\n const modes = providerModes(optionProvider, option.value);\n return (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => {\n const nextMode = nextSupportedMode(optionProvider, option.value, mode);\n onSdkChange(option.value);\n if (nextMode !== mode) onModeChange(nextMode);\n setOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-bg-elevated transition-colors ${\n option.value === sdk\n ? 'text-action-primary-bg bg-[var(--color-accent-soft)]'\n : 'text-text-primary'\n }`}\n >\n <Icon className=\"size-4\" />\n <span>{option.label}</span>\n <span className=\"ml-auto flex items-center gap-1\">\n {ALL_MODES.map((optionMode) => (\n <span\n key={optionMode}\n className={`rounded px-1 py-0.5 text-[10px] font-semibold uppercase ${\n modes.includes(optionMode)\n ? 'bg-bg-sunken text-text-secondary'\n : 'border border-border-default text-text-tertiary opacity-50'\n }`}\n >\n {optionMode}\n </span>\n ))}\n </span>\n </button>\n );\n })}\n </div>\n\n {/* Mode toggle */}\n <div className=\"border-t border-border-default px-3 py-2\">\n <div className=\"flex items-center rounded-md bg-bg-sunken p-0.5\">\n <button\n type=\"button\"\n onClick={() => currentModes.includes('sdk') && onModeChange('sdk')}\n disabled={!currentModes.includes('sdk')}\n className={`flex-1 px-2 py-1 text-xs font-medium rounded transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${\n mode === 'sdk'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-tertiary hover:text-text-secondary'\n }`}\n title={\n currentModes.includes('sdk')\n ? 'Use SDK mode'\n : `${current.label} does not support SDK mode`\n }\n >\n SDK\n </button>\n <button\n type=\"button\"\n onClick={() => currentModes.includes('cli') && onModeChange('cli')}\n disabled={!currentModes.includes('cli')}\n className={`flex-1 px-2 py-1 text-xs font-medium rounded transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${\n mode === 'cli'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-tertiary hover:text-text-secondary'\n }`}\n title={\n currentModes.includes('cli')\n ? 'Use CLI mode'\n : `${current.label} does not support CLI mode`\n }\n >\n CLI\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ function r({ enabled: r, onToggle: i, isListening: a = !1 }) {
|
|
|
7
7
|
return /* @__PURE__ */ n("button", {
|
|
8
8
|
type: "button",
|
|
9
9
|
onClick: i,
|
|
10
|
-
className: `p-1.5 rounded transition-colors ${a ? "bg-status-error-bg text-status-error-text animate-pulse" : r ? "bg-
|
|
10
|
+
className: `p-1.5 rounded transition-colors ${a ? "bg-status-error-bg-subtle text-status-error-text animate-pulse" : r ? "bg-[var(--color-accent-soft)] text-action-primary-bg hover:bg-[var(--color-accent-soft)]" : "text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated"}`,
|
|
11
11
|
title: a ? o("vibecontrols.agentManager.controls.listening", "Listening...") : r ? o("vibecontrols.agentManager.controls.disableVoice", "Disable voice mode") : o("vibecontrols.agentManager.controls.enableVoice", "Enable voice mode"),
|
|
12
12
|
children: /* @__PURE__ */ n(t, { className: "size-4" })
|
|
13
13
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceToggle.js","names":[],"sources":["../../../../src/components/agent-manager/controls/VoiceToggle.tsx"],"sourcesContent":["/**\n * Voice mode toggle button.\n *\n * Mic icon that toggles voice input. Turns red when actively listening.\n */\n\nimport { Mic } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface VoiceToggleProps {\n enabled: boolean;\n onToggle: () => void;\n isListening?: boolean;\n}\n\nexport function VoiceToggle({ enabled, onToggle, isListening = false }: VoiceToggleProps) {\n const tr = useTr();\n\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n className={`p-1.5 rounded transition-colors ${\n isListening\n ? 'bg-status-error-bg text-status-error-text animate-pulse'\n : enabled\n ? 'bg-
|
|
1
|
+
{"version":3,"file":"VoiceToggle.js","names":[],"sources":["../../../../src/components/agent-manager/controls/VoiceToggle.tsx"],"sourcesContent":["/**\n * Voice mode toggle button.\n *\n * Mic icon that toggles voice input. Turns red when actively listening.\n */\n\nimport { Mic } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface VoiceToggleProps {\n enabled: boolean;\n onToggle: () => void;\n isListening?: boolean;\n}\n\nexport function VoiceToggle({ enabled, onToggle, isListening = false }: VoiceToggleProps) {\n const tr = useTr();\n\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n className={`p-1.5 rounded transition-colors ${\n isListening\n ? 'bg-status-error-bg-subtle text-status-error-text animate-pulse'\n : enabled\n ? 'bg-[var(--color-accent-soft)] text-action-primary-bg hover:bg-[var(--color-accent-soft)]'\n : 'text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated'\n }`}\n title={\n isListening\n ? tr('vibecontrols.agentManager.controls.listening', 'Listening...')\n : enabled\n ? tr('vibecontrols.agentManager.controls.disableVoice', 'Disable voice mode')\n : tr('vibecontrols.agentManager.controls.enableVoice', 'Enable voice mode')\n }\n >\n <Mic className=\"size-4\" />\n </button>\n );\n}\n"],"mappings":";;;;AAeA,SAAgB,EAAY,EAAE,YAAS,aAAU,iBAAc,MAA2B;CACxF,IAAM,IAAK,GAAO;AAElB,QACE,kBAAC,UAAD;EACE,MAAK;EACL,SAAS;EACT,WAAW,mCACT,IACI,mEACA,IACE,6FACA;EAER,OACE,IACI,EAAG,gDAAgD,eAAe,GAClE,IACE,EAAG,mDAAmD,qBAAqB,GAC3E,EAAG,kDAAkD,oBAAoB;YAGjF,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA;EACnB,CAAA"}
|
|
@@ -117,7 +117,7 @@ function g({ sessionId: g, agent: _ }) {
|
|
|
117
117
|
className: "text-xs text-text-tertiary",
|
|
118
118
|
children: "Status:"
|
|
119
119
|
}), /* @__PURE__ */ f("span", {
|
|
120
|
-
className: `px-2 py-0.5 text-xs font-medium rounded-full ${y.status === "active" ? "bg-status-success-bg
|
|
120
|
+
className: `px-2 py-0.5 text-xs font-medium rounded-full ${y.status === "active" ? "bg-status-success-bg-subtle text-status-success-text" : y.status === "error" ? "bg-status-error-bg-subtle text-status-error-text" : y.status === "terminated" ? "bg-bg-sunken text-text-tertiary" : "bg-status-warning-bg-subtle text-status-warning-text"}`,
|
|
121
121
|
children: y.status
|
|
122
122
|
})]
|
|
123
123
|
}),
|
|
@@ -138,7 +138,7 @@ function g({ sessionId: g, agent: _ }) {
|
|
|
138
138
|
onChange: (e) => z("model", e.target.value),
|
|
139
139
|
placeholder: v("vibecontrols.agentManager.config.modelPlaceholder", "Model ID"),
|
|
140
140
|
disabled: H,
|
|
141
|
-
className: "w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-
|
|
141
|
+
className: "w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-surface text-text-primary disabled:opacity-50"
|
|
142
142
|
})] }),
|
|
143
143
|
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
144
144
|
className: "block text-xs text-text-secondary mb-1",
|
|
@@ -150,7 +150,7 @@ function g({ sessionId: g, agent: _ }) {
|
|
|
150
150
|
placeholder: "Instructions for the AI...",
|
|
151
151
|
rows: 2,
|
|
152
152
|
disabled: H,
|
|
153
|
-
className: "w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-
|
|
153
|
+
className: "w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-surface text-text-primary resize-none disabled:opacity-50"
|
|
154
154
|
})] }),
|
|
155
155
|
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
156
156
|
className: "block text-xs text-text-secondary mb-1",
|
|
@@ -162,7 +162,7 @@ function g({ sessionId: g, agent: _ }) {
|
|
|
162
162
|
onChange: (e) => z("maxTokens", e.target.value ? Number(e.target.value) : null),
|
|
163
163
|
placeholder: "4096",
|
|
164
164
|
disabled: H,
|
|
165
|
-
className: "w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-
|
|
165
|
+
className: "w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-surface text-text-primary disabled:opacity-50"
|
|
166
166
|
})] }),
|
|
167
167
|
/* @__PURE__ */ p("div", { children: [
|
|
168
168
|
/* @__PURE__ */ f("label", {
|
|
@@ -201,7 +201,7 @@ function g({ sessionId: g, agent: _ }) {
|
|
|
201
201
|
S(e.target.value), M(null), L(e.target.value);
|
|
202
202
|
},
|
|
203
203
|
disabled: H,
|
|
204
|
-
className: "w-full h-48 rounded-lg border border-border-default bg-bg-
|
|
204
|
+
className: "w-full h-48 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-xs font-mono text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors resize-y disabled:opacity-50",
|
|
205
205
|
spellCheck: !1
|
|
206
206
|
}),
|
|
207
207
|
j && /* @__PURE__ */ f("p", {
|
|
@@ -231,7 +231,7 @@ function g({ sessionId: g, agent: _ }) {
|
|
|
231
231
|
type: "button",
|
|
232
232
|
onClick: () => P(!0),
|
|
233
233
|
disabled: H,
|
|
234
|
-
className: "flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-status-error-bg
|
|
234
|
+
className: "flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-status-error-bg-subtle text-status-error-text hover:bg-bg-sunken disabled:opacity-40 disabled:cursor-not-allowed transition-colors",
|
|
235
235
|
children: [/* @__PURE__ */ f(d, { className: "size-3.5" }), v("vibecontrols.agentManager.config.terminate", "Terminate")]
|
|
236
236
|
})
|
|
237
237
|
]
|