@burdenoff/microfe-vibecontrols 2026.623.2 → 2026.623.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/actions/ActionTypeBadge.js +1 -1
- package/dist/components/actions/ActionTypeBadge.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 +1 -1
- package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerInputArea.js +1 -1
- package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
- package/dist/components/agent-manager/BridleConfigPanel.js +2 -2
- package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/ProfilesPanel.js +1 -1
- 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/SessionLogsPanel.js +2 -2
- package/dist/components/agent-manager/detail/SessionLogsPanel.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 +1 -1
- package/dist/components/ai/AIOverviewTab.js.map +1 -1
- package/dist/components/ai/AIPromptsTab.js +1 -1
- package/dist/components/ai/AIPromptsTab.js.map +1 -1
- package/dist/components/ai/AIQueueTab.js +1 -1
- package/dist/components/ai/AIQueueTab.js.map +1 -1
- package/dist/components/ai/AISessionsTab.js +1 -1
- package/dist/components/ai/AISessionsTab.js.map +1 -1
- package/dist/components/audit/AuditLogTable.js +1 -1
- package/dist/components/audit/AuditLogTable.js.map +1 -1
- package/dist/components/docs/DocsPageTree.js +1 -1
- package/dist/components/docs/DocsPageTree.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/notes/NoteCard.js +1 -1
- package/dist/components/notes/NoteCard.js.map +1 -1
- package/dist/components/notes/NoteEditor.js +1 -1
- 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/shared/AgentSetupBanner.js +3 -3
- package/dist/components/shared/AgentSetupBanner.js.map +1 -1
- package/dist/components/templates/TemplateCard.js +1 -1
- package/dist/components/templates/TemplateCard.js.map +1 -1
- package/dist/components/vibecalendar/types.js +1 -1
- package/dist/components/vibecalendar/types.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonForm.js +1 -1
- package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckExecutionLog.js +2 -2
- package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckForm.js +1 -1
- package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/pages/AgentsPage.js +2 -2
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +1 -1
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +1 -1
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/OverviewPage.js +10 -10
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginsCatalogPage.js +1 -1
- package/dist/pages/PluginsCatalogPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +3 -3
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/SharedSessionPage.js +2 -2
- package/dist/pages/SharedSessionPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +2 -2
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +2 -2
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/dist/pages/VibesPage.js +2 -2
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/WebhooksPage.js +2 -2
- package/dist/pages/WebhooksPage.js.map +1 -1
- package/dist/pages/ai/AISessionDetailPage.js +2 -2
- package/dist/pages/ai/AISessionDetailPage.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-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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"}
|
|
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-text-primary',\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-text-primary'\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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"}
|
|
@@ -202,7 +202,7 @@ 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-[var(--color-accent-soft)] text-
|
|
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-text-primary rounded",
|
|
206
206
|
children: [/* @__PURE__ */ p(c, { className: "size-3" }), "Default"]
|
|
207
207
|
}),
|
|
208
208
|
n && /* @__PURE__ */ p("span", {
|
|
@@ -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-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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"}
|
|
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-text-primary 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-
|
|
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-text-primary 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-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":"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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-text-primary 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":"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"}
|
|
@@ -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-
|
|
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-text-primary 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-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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"}
|
|
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-text-primary 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-subtle text-status-error-text animate-pulse" : r ? "bg-[var(--color-accent-soft)] text-
|
|
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-text-primary 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-subtle text-status-error-text animate-pulse'\n : enabled\n ? 'bg-[var(--color-accent-soft)] text-
|
|
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-text-primary 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,wFACA;EAER,OACE,IACI,EAAG,gDAAgD,eAAe,GAClE,IACE,EAAG,mDAAmD,qBAAqB,GAC3E,EAAG,kDAAkD,oBAAoB;YAGjF,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA;EACnB,CAAA"}
|
|
@@ -16,7 +16,7 @@ var f = [
|
|
|
16
16
|
"error",
|
|
17
17
|
"metadata"
|
|
18
18
|
], p = {
|
|
19
|
-
input: "bg-[var(--color-accent-soft)] text-
|
|
19
|
+
input: "bg-[var(--color-accent-soft)] text-text-primary",
|
|
20
20
|
output: "bg-status-success-bg-subtle text-status-success-text",
|
|
21
21
|
thinking: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
22
22
|
event: "bg-bg-elevated text-text-secondary",
|
|
@@ -62,7 +62,7 @@ function m({ sessionId: m, agent: h }) {
|
|
|
62
62
|
/* @__PURE__ */ u("button", {
|
|
63
63
|
type: "button",
|
|
64
64
|
onClick: () => E((e) => !e),
|
|
65
|
-
className: `p-1 rounded transition-colors ${T ? "text-
|
|
65
|
+
className: `p-1 rounded transition-colors ${T ? "text-text-primary bg-[var(--color-accent-soft)]" : "text-text-tertiary hover:text-text-secondary"}`,
|
|
66
66
|
title: T ? g("vibecontrols.agentManager.logs.disableAutoRefresh", "Disable auto-refresh") : g("vibecontrols.agentManager.logs.enableAutoRefresh", "Enable auto-refresh"),
|
|
67
67
|
children: /* @__PURE__ */ u(l, { className: `size-3.5 ${T ? "animate-spin" : ""}` })
|
|
68
68
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionLogsPanel.js","names":[],"sources":["../../../../src/components/agent-manager/detail/SessionLogsPanel.tsx"],"sourcesContent":["/**\n * Session logs viewer panel.\n *\n * Fetches and displays logs for a session with type filtering\n * and optional auto-refresh.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { RefreshCw } from 'lucide-react';\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { fetchSessionLogs, type AISessionLog, type AgentRef } from '@/services/aiApi';\nimport { isSessionNotFoundError } from '@/utils/aiSessionNotFound';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface SessionLogsPanelProps {\n sessionId: string;\n agent: AgentRef;\n}\n\nconst LOG_TYPES = ['input', 'output', 'thinking', 'event', 'error', 'metadata'] as const;\n\nconst TYPE_COLORS: Record<string, string> = {\n input: 'bg-[var(--color-accent-soft)] text-
|
|
1
|
+
{"version":3,"file":"SessionLogsPanel.js","names":[],"sources":["../../../../src/components/agent-manager/detail/SessionLogsPanel.tsx"],"sourcesContent":["/**\n * Session logs viewer panel.\n *\n * Fetches and displays logs for a session with type filtering\n * and optional auto-refresh.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { RefreshCw } from 'lucide-react';\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { fetchSessionLogs, type AISessionLog, type AgentRef } from '@/services/aiApi';\nimport { isSessionNotFoundError } from '@/utils/aiSessionNotFound';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface SessionLogsPanelProps {\n sessionId: string;\n agent: AgentRef;\n}\n\nconst LOG_TYPES = ['input', 'output', 'thinking', 'event', 'error', 'metadata'] as const;\n\nconst TYPE_COLORS: Record<string, string> = {\n input: 'bg-[var(--color-accent-soft)] text-text-primary',\n output: 'bg-status-success-bg-subtle text-status-success-text',\n thinking: 'bg-status-warning-bg-subtle text-status-warning-text',\n event: 'bg-bg-elevated text-text-secondary',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n metadata: 'bg-bg-sunken text-text-tertiary',\n};\n\nexport function SessionLogsPanel({ sessionId, agent }: SessionLogsPanelProps) {\n const tr = useTr();\n const [logs, setLogs] = useState<AISessionLog[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [activeTypes, setActiveTypes] = useState<Set<string>>(new Set(LOG_TYPES));\n const [autoRefresh, setAutoRefresh] = useState(false);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const loadLogs = useCallback(async () => {\n try {\n const data = await fetchSessionLogs(agent, sessionId, { limit: 100 });\n setLogs(data);\n setError(null);\n } catch (err) {\n // Until the user sends the first message the agent has no\n // record of this session, so /api/ai/sessions/:id/logs 404s.\n // Render that as an empty list with a hint, not as an error.\n if (isSessionNotFoundError(err)) {\n setLogs([]);\n setError(null);\n } else {\n setError(err instanceof Error ? err.message : 'Failed to load logs');\n }\n } finally {\n setLoading(false);\n }\n }, [agent, sessionId]);\n\n useEffect(() => {\n setLoading(true);\n loadLogs();\n }, [loadLogs]);\n\n useEffect(() => {\n if (autoRefresh) {\n intervalRef.current = setInterval(loadLogs, 5000);\n }\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [autoRefresh, loadLogs]);\n\n const toggleType = useCallback((type: string) => {\n setActiveTypes((prev) => {\n const next = new Set(prev);\n if (next.has(type)) {\n next.delete(type);\n } else {\n next.add(type);\n }\n return next;\n });\n }, []);\n\n const filteredLogs = logs.filter((log) => activeTypes.has(log.type));\n\n if (loading) return <LoadingState message=\"Loading logs...\" />;\n if (error) return <ErrorState message={error} onRetry={loadLogs} />;\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Filter bar */}\n <div className=\"flex items-center gap-1.5 p-2 border-b border-border-default flex-wrap\">\n {LOG_TYPES.map((type) => (\n <button\n key={type}\n type=\"button\"\n onClick={() => toggleType(type)}\n className={`px-2 py-0.5 text-[10px] font-medium rounded-full transition-colors ${\n activeTypes.has(type)\n ? TYPE_COLORS[type]\n : 'bg-bg-sunken text-text-tertiary opacity-50'\n }`}\n >\n {type}\n </button>\n ))}\n <div className=\"flex-1\" />\n <button\n type=\"button\"\n onClick={() => setAutoRefresh((prev) => !prev)}\n className={`p-1 rounded transition-colors ${\n autoRefresh\n ? 'text-text-primary bg-[var(--color-accent-soft)]'\n : 'text-text-tertiary hover:text-text-secondary'\n }`}\n title={\n autoRefresh\n ? tr('vibecontrols.agentManager.logs.disableAutoRefresh', 'Disable auto-refresh')\n : tr('vibecontrols.agentManager.logs.enableAutoRefresh', 'Enable auto-refresh')\n }\n >\n <RefreshCw className={`size-3.5 ${autoRefresh ? 'animate-spin' : ''}`} />\n </button>\n </div>\n\n {/* Logs list */}\n <div className=\"flex-1 overflow-y-auto\">\n {filteredLogs.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-text-tertiary\">\n {logs.length === 0\n ? tr(\n 'vibecontrols.agentManager.logs.empty',\n 'Logs will appear here after you send the first message.'\n )\n : tr(\n 'vibecontrols.agentManager.logs.noLogsMatch',\n 'No logs match the selected filters'\n )}\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {filteredLogs.map((log) => (\n <div key={log.id} className=\"px-3 py-2\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span\n className={`px-1.5 py-0.5 text-[10px] font-medium rounded ${TYPE_COLORS[log.type] ?? 'bg-bg-sunken text-text-tertiary'}`}\n >\n {log.type}\n </span>\n <span className=\"text-[10px] text-text-tertiary\">\n {new Date(log.timestamp).toLocaleTimeString()}\n </span>\n {log.tokenCount != null && (\n <span className=\"text-[10px] text-text-tertiary ml-auto\">\n {log.tokenCount} tokens\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary break-words line-clamp-3\">\n {log.message || log.content}\n </p>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -219,7 +219,7 @@ function w({ agent: w }) {
|
|
|
219
219
|
className: "font-semibold text-text-primary truncate",
|
|
220
220
|
children: e.name
|
|
221
221
|
}), /* @__PURE__ */ b("span", {
|
|
222
|
-
className: "px-1.5 py-0.5 text-xs bg-[var(--color-accent-soft)] text-
|
|
222
|
+
className: "px-1.5 py-0.5 text-xs bg-[var(--color-accent-soft)] text-text-primary rounded",
|
|
223
223
|
children: e.type
|
|
224
224
|
})]
|
|
225
225
|
})
|