@burdenoff/microfe-vibecontrols 2026.526.4 → 2026.526.6
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.
|
@@ -8,13 +8,17 @@ import { AlertCircle as p, CheckCircle2 as m, Loader2 as h, PlayCircle as g, Ref
|
|
|
8
8
|
import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
9
9
|
//#region src/components/ai/AIPlanPanel.tsx
|
|
10
10
|
function w({ agent: r, agentApiKey: i, tunnelUrl: a }) {
|
|
11
|
+
let { t: o } = f(), s = (e, t) => {
|
|
12
|
+
let n = o(e);
|
|
13
|
+
return n === e ? t : n;
|
|
14
|
+
};
|
|
11
15
|
return /* @__PURE__ */ S(n, {
|
|
12
16
|
agentId: r.id,
|
|
13
17
|
required: [e, t],
|
|
14
18
|
mode: "all",
|
|
15
|
-
eyebrow: "plan.pluginRequiredEyebrow",
|
|
16
|
-
title: "plan.installPlanProviderTitle",
|
|
17
|
-
description: "plan.installPlanProviderDesc",
|
|
19
|
+
eyebrow: s("plan.pluginRequiredEyebrow", "Plan plugin required"),
|
|
20
|
+
title: s("plan.installPlanProviderTitle", "Install the plan provider"),
|
|
21
|
+
description: s("plan.installPlanProviderDesc", "Plan mode routes plan sessions through a plan provider plugin on your agent. Install the orchestrator and a provider (Plannotator) below to continue."),
|
|
18
22
|
children: /* @__PURE__ */ S(T, {
|
|
19
23
|
agent: r,
|
|
20
24
|
agentApiKey: i,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIPlanPanel.js","names":[],"sources":["../../../src/components/ai/AIPlanPanel.tsx"],"sourcesContent":["/**\n * AIPlanPanel — the body of /ai-tools/plan.\n *\n * Five UI states gated in order:\n * 1. installing-plugin → vibe-plugin-plan + vibe-plugin-plan-plannotator\n * not installed on the agent. Renders the PluginGate install card.\n * 2. installing-cli → plugins installed but plannotator binary\n * missing on disk. Renders an Install CLI CTA.\n * 3. wizard → CLI installed but no AI-agent hook configured\n * yet. Renders the Claude/OpenCode/Codex/Pi/Gemini wizard.\n * 4. ready → all prereqs satisfied. Renders the tunnel\n * iframe pointing at ${tunnelUrl}/plan/, with postMessage auth\n * handoff on iframe load (mirrors PluginUIPanel.tsx).\n * 5. error → unrecoverable load failure for the status\n * query. Renders an error banner with retry.\n *\n * Auth handoff (matches PluginUIPanel.tsx:194–210):\n * onLoad → iframe.contentWindow.postMessage({type:'vibe-auth', apiKey},\n * new URL(tunnelUrl).origin)\n *\n * Surface contracts:\n * GET /api/plan-plannotator/status → PlannotatorStatus\n * POST /api/plan-plannotator/install → install binary\n * GET /api/plan-plannotator/agents/supported → AgentDetectionResult[]\n * POST /api/plan-plannotator/agents/:agent/configure-hook\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n AlertCircle,\n CheckCircle2,\n Loader2,\n PlayCircle,\n RefreshCcw,\n Settings,\n Wifi,\n WifiOff,\n} from 'lucide-react';\n\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PluginGate } from '@/components/shared';\nimport { PLAN_META_PACKAGE, PLAN_PLANNOTATOR_PACKAGE } from '@/constants/pluginCatalog';\nimport {\n configureAgentHook,\n getPlannotatorStatus,\n installPlannotatorBinary,\n listSupportedAgents,\n type AgentDetectionResult,\n type PlannotatorStatus,\n} from '@/services/planApi';\nimport type { AgentRef } from '@/services/aiApi';\n\ninterface AIPlanPanelProps {\n agent: AgentRef;\n agentApiKey: string | null;\n tunnelUrl: string | null;\n}\n\ntype ConnectionStatus = 'connecting' | 'connected' | 'error';\n\nexport function AIPlanPanel({ agent, agentApiKey, tunnelUrl }: AIPlanPanelProps) {\n return (\n <PluginGate\n agentId={agent.id}\n required={[PLAN_META_PACKAGE, PLAN_PLANNOTATOR_PACKAGE]}\n mode=\"all\"\n eyebrow=\"plan.pluginRequiredEyebrow\"\n title=\"plan.installPlanProviderTitle\"\n description=\"plan.installPlanProviderDesc\"\n >\n <PlanPanelInner agent={agent} agentApiKey={agentApiKey} tunnelUrl={tunnelUrl} />\n </PluginGate>\n );\n}\n\nfunction PlanPanelInner({ agent, agentApiKey, tunnelUrl }: AIPlanPanelProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [status, setStatus] = useState<PlannotatorStatus | null>(null);\n const [statusError, setStatusError] = useState<string | null>(null);\n const [statusLoading, setStatusLoading] = useState(true);\n const [agents, setAgents] = useState<AgentDetectionResult[]>([]);\n const [installing, setInstalling] = useState(false);\n const [configuring, setConfiguring] = useState<string | null>(null);\n const [actionMessage, setActionMessage] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const refresh = useCallback(async () => {\n setStatusLoading(true);\n setStatusError(null);\n try {\n const [statusResult, agentsResult] = await Promise.all([\n getPlannotatorStatus(agent),\n listSupportedAgents(agent).catch(() => ({ agents: [] })),\n ]);\n setStatus(statusResult);\n setAgents(agentsResult.agents ?? []);\n } catch (err) {\n setStatusError(err instanceof Error ? err.message : 'Failed to load plan status');\n } finally {\n setStatusLoading(false);\n }\n }, [agent]);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const handleInstallCli = useCallback(async () => {\n setInstalling(true);\n setActionError(null);\n setActionMessage(null);\n try {\n const result = await installPlannotatorBinary(agent);\n if (result.ok) {\n setActionMessage(tr('plan.cliInstalled', 'Plannotator CLI installed.'));\n await refresh();\n } else {\n setActionError(result.error ?? 'Install failed');\n }\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Install failed');\n } finally {\n setInstalling(false);\n }\n }, [agent, refresh, tr]);\n\n const handleConfigureHook = useCallback(\n async (agentKind: AgentDetectionResult['agent']) => {\n setConfiguring(agentKind);\n setActionError(null);\n setActionMessage(null);\n try {\n const result = await configureAgentHook(agent, agentKind);\n if (result.ok) {\n setActionMessage(tr('plan.hookConfigured', `Hook configured for ${agentKind}.`));\n await refresh();\n } else {\n setActionError(result.error ?? 'Hook configure failed');\n }\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Hook configure failed');\n } finally {\n setConfiguring(null);\n }\n },\n [agent, refresh, tr]\n );\n\n // Derived render state — guarded ordering means we always render the\n // most prerequisite-bound CTA first.\n const view = useMemo<'loading' | 'status-error' | 'install-cli' | 'wizard' | 'ready'>(() => {\n if (statusLoading) return 'loading';\n if (statusError) return 'status-error';\n if (!status?.installed) return 'install-cli';\n const anyConfigured = agents.some((a) => a.hookConfigured);\n if (!anyConfigured) return 'wizard';\n return 'ready';\n }, [status, statusError, statusLoading, agents]);\n\n if (view === 'loading') {\n return (\n <div className=\"flex items-center gap-2 p-6 text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{tr('plan.loadingStatus', 'Loading plan status…')}</span>\n </div>\n );\n }\n\n if (view === 'status-error') {\n return (\n <div className=\"rounded-md border border-status-error-border bg-status-error-bg p-4 text-sm text-status-error-text\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle className=\"h-4 w-4 mt-0.5 shrink-0\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">\n {tr('plan.statusError', 'Could not load plan status from the agent.')}\n </p>\n <p className=\"text-xs opacity-80 mt-1\">{statusError}</p>\n <button\n onClick={() => void refresh()}\n className=\"mt-3 inline-flex items-center gap-1 text-xs font-medium hover:underline\"\n >\n <RefreshCcw className=\"h-3 w-3\" /> {tr('plan.retry', 'Retry')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {actionMessage && (\n <div\n role=\"status\"\n className=\"rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text\"\n >\n {actionMessage}\n </div>\n )}\n {actionError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text\"\n >\n {actionError}\n </div>\n )}\n\n {view === 'install-cli' && (\n <InstallCliCard installing={installing} onInstall={handleInstallCli} tr={tr} />\n )}\n\n {view === 'wizard' && (\n <WizardCard\n agents={agents}\n configuring={configuring}\n onConfigure={handleConfigureHook}\n tr={tr}\n />\n )}\n\n {view === 'ready' && (\n <PlanReadyPanel\n agent={agent}\n agents={agents}\n agentApiKey={agentApiKey}\n tunnelUrl={tunnelUrl}\n onReconfigure={() => setActionMessage(null)}\n tr={tr}\n />\n )}\n </div>\n );\n}\n\ninterface InstallCliCardProps {\n installing: boolean;\n onInstall: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction InstallCliCard({ installing, onInstall, tr }: InstallCliCardProps) {\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex items-start gap-3\">\n <PlayCircle className=\"h-6 w-6 text-action-primary-bg shrink-0\" />\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('plan.installCliTitle', 'Install the plannotator CLI')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'plan.installCliDesc',\n 'Plan mode runs a local plannotator binary on the agent and proxies its UI back here. The plugin installs the binary into ~/.local/bin/plannotator (sha256-pinned). Roughly 30 seconds.'\n )}\n </p>\n <button\n onClick={onInstall}\n disabled={installing}\n className=\"mt-4 inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50\"\n >\n {installing ? (\n <>\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n {tr('plan.installing', 'Installing…')}\n </>\n ) : (\n tr('plan.installCliCta', 'Install plannotator CLI')\n )}\n </button>\n </div>\n </div>\n </div>\n );\n}\n\ninterface WizardCardProps {\n agents: AgentDetectionResult[];\n configuring: string | null;\n onConfigure: (agentKind: AgentDetectionResult['agent']) => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction WizardCard({ agents, configuring, onConfigure, tr }: WizardCardProps) {\n const installed = agents.filter((a) => a.cliInstalled);\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex items-start gap-3\">\n <Settings className=\"h-6 w-6 text-action-primary-bg shrink-0\" />\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('plan.wizardTitle', 'Connect an AI coding agent')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'plan.wizardDesc',\n 'Configure a plannotator hook for at least one installed AI coding agent. The hook forwards plan content from the agent to plannotator on every plan-finalisation step.'\n )}\n </p>\n {installed.length === 0 && (\n <p className=\"mt-4 text-sm text-status-warning-text\">\n {tr(\n 'plan.wizardNoAgents',\n 'No AI coding agents detected in this sandbox. Install one (Claude Code, OpenCode, Codex, Pi, or Gemini) and refresh.'\n )}\n </p>\n )}\n <ul className=\"mt-4 space-y-2\">\n {agents.map((entry) => (\n <li\n key={entry.agent}\n className=\"flex items-start gap-3 p-3 border border-border-default rounded-md bg-bg-surface\"\n >\n {entry.hookConfigured ? (\n <CheckCircle2 className=\"h-5 w-5 text-status-success-text shrink-0 mt-0.5\" />\n ) : (\n <div className=\"h-5 w-5 rounded-full border-2 border-border-default shrink-0 mt-0.5\" />\n )}\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">{entry.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">{entry.instructions}</p>\n {!entry.cliInstalled && (\n <p className=\"text-xs text-status-warning-text mt-1\">\n {tr('plan.agentCliMissing', 'CLI not detected on this sandbox.')}\n </p>\n )}\n </div>\n <button\n disabled={\n !entry.cliInstalled || entry.hookConfigured || configuring === entry.agent\n }\n onClick={() => onConfigure(entry.agent)}\n className=\"px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50\"\n >\n {entry.hookConfigured\n ? tr('plan.hookConfiguredLabel', 'Configured')\n : configuring === entry.agent\n ? tr('plan.configuring', 'Configuring…')\n : tr('plan.configureHook', 'Configure hook')}\n </button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n </div>\n );\n}\n\ninterface PlanReadyPanelProps {\n agent: AgentRef;\n agents: AgentDetectionResult[];\n agentApiKey: string | null;\n tunnelUrl: string | null;\n onReconfigure: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction PlanReadyPanel({\n agent,\n agents,\n agentApiKey,\n tunnelUrl,\n onReconfigure,\n tr,\n}: PlanReadyPanelProps) {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const [connection, setConnection] = useState<ConnectionStatus>('connecting');\n\n const iframeSrc = useMemo(() => {\n if (!tunnelUrl) return null;\n const base = tunnelUrl.replace(/\\/$/, '');\n return new URL(`${base}/plan/`).toString();\n }, [tunnelUrl]);\n\n const sendAuth = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe?.contentWindow || !agentApiKey || !tunnelUrl) return;\n try {\n const targetOrigin = new URL(tunnelUrl).origin;\n iframe.contentWindow.postMessage({ type: 'vibe-auth', apiKey: agentApiKey }, targetOrigin);\n } catch {\n // Origin parsing can throw on malformed tunnelUrl — ignored.\n }\n }, [agentApiKey, tunnelUrl]);\n\n const handleLoad = useCallback(() => {\n setConnection('connected');\n sendAuth();\n }, [sendAuth]);\n\n const handleError = useCallback(() => {\n setConnection('error');\n }, []);\n\n const handleRetry = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe) return;\n setConnection('connecting');\n const src = iframe.src;\n iframe.src = 'about:blank';\n requestAnimationFrame(() => {\n iframe.src = src;\n });\n }, []);\n\n // Used elsewhere for analytics / breadcrumbs.\n void agent;\n\n if (!tunnelUrl || !agentApiKey) {\n return (\n <div className=\"rounded-lg border border-status-warning-border bg-status-warning-bg p-4 text-sm text-status-warning-text\">\n {tr(\n 'plan.tunnelMissing',\n 'Agent tunnel URL or API key is missing. Bring the agent online and refresh.'\n )}\n </div>\n );\n }\n\n const configuredAgents = agents.filter((a) => a.hookConfigured);\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center gap-1\">\n {connection === 'connected' && <Wifi className=\"h-3.5 w-3.5 text-status-success-text\" />}\n {connection === 'connecting' && (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin text-status-warning-text\" />\n )}\n {connection === 'error' && <WifiOff className=\"h-3.5 w-3.5 text-status-error-text\" />}\n {tr(`plan.connection.${connection}`, connection)}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>{tr('plan.hookCount', `${configuredAgents.length} hook(s) configured`)}</span>\n <button\n onClick={onReconfigure}\n className=\"ml-auto inline-flex items-center gap-1 hover:text-text-primary\"\n >\n <Settings className=\"h-3.5 w-3.5\" />\n {tr('plan.reopenWizard', 'Manage hooks')}\n </button>\n </div>\n\n <div className=\"border border-border-default rounded-lg overflow-hidden bg-bg-sunken relative h-[calc(100vh-320px)] min-h-[500px]\">\n {iframeSrc && (\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n title={tr('plan.iframeTitle', 'Plannotator plan workspace')}\n className=\"w-full h-full border-0\"\n allow=\"clipboard-read; clipboard-write\"\n sandbox=\"allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation-by-user-activation\"\n onLoad={handleLoad}\n onError={handleError}\n data-testid=\"plan-iframe\"\n />\n )}\n {connection === 'connecting' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg-sunken/80 pointer-events-none\">\n <Loader2 className=\"h-6 w-6 animate-spin text-action-primary-bg\" />\n </div>\n )}\n {connection === 'error' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg-sunken p-6 text-center\">\n <div>\n <AlertCircle className=\"h-6 w-6 text-status-error-text mx-auto\" />\n <p className=\"text-sm font-medium text-text-primary mt-2\">\n {tr('plan.iframeErrorTitle', 'Could not load plannotator.')}\n </p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'plan.iframeErrorDesc',\n 'The agent reverse proxy returned an error. Check the agent status and try again.'\n )}\n </p>\n <button\n onClick={handleRetry}\n className=\"mt-3 inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-sm text-action-primary-text hover:bg-action-primary-bgHover\"\n >\n <RefreshCcw className=\"h-3.5 w-3.5\" /> {tr('plan.retry', 'Retry')}\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AIPlanPanel.js","names":[],"sources":["../../../src/components/ai/AIPlanPanel.tsx"],"sourcesContent":["/**\n * AIPlanPanel — the body of /ai-tools/plan.\n *\n * Five UI states gated in order:\n * 1. installing-plugin → vibe-plugin-plan + vibe-plugin-plan-plannotator\n * not installed on the agent. Renders the PluginGate install card.\n * 2. installing-cli → plugins installed but plannotator binary\n * missing on disk. Renders an Install CLI CTA.\n * 3. wizard → CLI installed but no AI-agent hook configured\n * yet. Renders the Claude/OpenCode/Codex/Pi/Gemini wizard.\n * 4. ready → all prereqs satisfied. Renders the tunnel\n * iframe pointing at ${tunnelUrl}/plan/, with postMessage auth\n * handoff on iframe load (mirrors PluginUIPanel.tsx).\n * 5. error → unrecoverable load failure for the status\n * query. Renders an error banner with retry.\n *\n * Auth handoff (matches PluginUIPanel.tsx:194–210):\n * onLoad → iframe.contentWindow.postMessage({type:'vibe-auth', apiKey},\n * new URL(tunnelUrl).origin)\n *\n * Surface contracts:\n * GET /api/plan-plannotator/status → PlannotatorStatus\n * POST /api/plan-plannotator/install → install binary\n * GET /api/plan-plannotator/agents/supported → AgentDetectionResult[]\n * POST /api/plan-plannotator/agents/:agent/configure-hook\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n AlertCircle,\n CheckCircle2,\n Loader2,\n PlayCircle,\n RefreshCcw,\n Settings,\n Wifi,\n WifiOff,\n} from 'lucide-react';\n\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PluginGate } from '@/components/shared';\nimport { PLAN_META_PACKAGE, PLAN_PLANNOTATOR_PACKAGE } from '@/constants/pluginCatalog';\nimport {\n configureAgentHook,\n getPlannotatorStatus,\n installPlannotatorBinary,\n listSupportedAgents,\n type AgentDetectionResult,\n type PlannotatorStatus,\n} from '@/services/planApi';\nimport type { AgentRef } from '@/services/aiApi';\n\ninterface AIPlanPanelProps {\n agent: AgentRef;\n agentApiKey: string | null;\n tunnelUrl: string | null;\n}\n\ntype ConnectionStatus = 'connecting' | 'connected' | 'error';\n\nexport function AIPlanPanel({ agent, agentApiKey, tunnelUrl }: AIPlanPanelProps) {\n const { t } = useI18n();\n // PluginGate renders eyebrow/title/description verbatim (no i18n lookup of\n // its own), so resolve them here with English fallbacks until plan.* keys exist.\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n return (\n <PluginGate\n agentId={agent.id}\n required={[PLAN_META_PACKAGE, PLAN_PLANNOTATOR_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('plan.pluginRequiredEyebrow', 'Plan plugin required')}\n title={tr('plan.installPlanProviderTitle', 'Install the plan provider')}\n description={tr(\n 'plan.installPlanProviderDesc',\n 'Plan mode routes plan sessions through a plan provider plugin on your agent. Install the orchestrator and a provider (Plannotator) below to continue.'\n )}\n >\n <PlanPanelInner agent={agent} agentApiKey={agentApiKey} tunnelUrl={tunnelUrl} />\n </PluginGate>\n );\n}\n\nfunction PlanPanelInner({ agent, agentApiKey, tunnelUrl }: AIPlanPanelProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [status, setStatus] = useState<PlannotatorStatus | null>(null);\n const [statusError, setStatusError] = useState<string | null>(null);\n const [statusLoading, setStatusLoading] = useState(true);\n const [agents, setAgents] = useState<AgentDetectionResult[]>([]);\n const [installing, setInstalling] = useState(false);\n const [configuring, setConfiguring] = useState<string | null>(null);\n const [actionMessage, setActionMessage] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const refresh = useCallback(async () => {\n setStatusLoading(true);\n setStatusError(null);\n try {\n const [statusResult, agentsResult] = await Promise.all([\n getPlannotatorStatus(agent),\n listSupportedAgents(agent).catch(() => ({ agents: [] })),\n ]);\n setStatus(statusResult);\n setAgents(agentsResult.agents ?? []);\n } catch (err) {\n setStatusError(err instanceof Error ? err.message : 'Failed to load plan status');\n } finally {\n setStatusLoading(false);\n }\n }, [agent]);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const handleInstallCli = useCallback(async () => {\n setInstalling(true);\n setActionError(null);\n setActionMessage(null);\n try {\n const result = await installPlannotatorBinary(agent);\n if (result.ok) {\n setActionMessage(tr('plan.cliInstalled', 'Plannotator CLI installed.'));\n await refresh();\n } else {\n setActionError(result.error ?? 'Install failed');\n }\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Install failed');\n } finally {\n setInstalling(false);\n }\n }, [agent, refresh, tr]);\n\n const handleConfigureHook = useCallback(\n async (agentKind: AgentDetectionResult['agent']) => {\n setConfiguring(agentKind);\n setActionError(null);\n setActionMessage(null);\n try {\n const result = await configureAgentHook(agent, agentKind);\n if (result.ok) {\n setActionMessage(tr('plan.hookConfigured', `Hook configured for ${agentKind}.`));\n await refresh();\n } else {\n setActionError(result.error ?? 'Hook configure failed');\n }\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Hook configure failed');\n } finally {\n setConfiguring(null);\n }\n },\n [agent, refresh, tr]\n );\n\n // Derived render state — guarded ordering means we always render the\n // most prerequisite-bound CTA first.\n const view = useMemo<'loading' | 'status-error' | 'install-cli' | 'wizard' | 'ready'>(() => {\n if (statusLoading) return 'loading';\n if (statusError) return 'status-error';\n if (!status?.installed) return 'install-cli';\n const anyConfigured = agents.some((a) => a.hookConfigured);\n if (!anyConfigured) return 'wizard';\n return 'ready';\n }, [status, statusError, statusLoading, agents]);\n\n if (view === 'loading') {\n return (\n <div className=\"flex items-center gap-2 p-6 text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{tr('plan.loadingStatus', 'Loading plan status…')}</span>\n </div>\n );\n }\n\n if (view === 'status-error') {\n return (\n <div className=\"rounded-md border border-status-error-border bg-status-error-bg p-4 text-sm text-status-error-text\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle className=\"h-4 w-4 mt-0.5 shrink-0\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">\n {tr('plan.statusError', 'Could not load plan status from the agent.')}\n </p>\n <p className=\"text-xs opacity-80 mt-1\">{statusError}</p>\n <button\n onClick={() => void refresh()}\n className=\"mt-3 inline-flex items-center gap-1 text-xs font-medium hover:underline\"\n >\n <RefreshCcw className=\"h-3 w-3\" /> {tr('plan.retry', 'Retry')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {actionMessage && (\n <div\n role=\"status\"\n className=\"rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text\"\n >\n {actionMessage}\n </div>\n )}\n {actionError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text\"\n >\n {actionError}\n </div>\n )}\n\n {view === 'install-cli' && (\n <InstallCliCard installing={installing} onInstall={handleInstallCli} tr={tr} />\n )}\n\n {view === 'wizard' && (\n <WizardCard\n agents={agents}\n configuring={configuring}\n onConfigure={handleConfigureHook}\n tr={tr}\n />\n )}\n\n {view === 'ready' && (\n <PlanReadyPanel\n agent={agent}\n agents={agents}\n agentApiKey={agentApiKey}\n tunnelUrl={tunnelUrl}\n onReconfigure={() => setActionMessage(null)}\n tr={tr}\n />\n )}\n </div>\n );\n}\n\ninterface InstallCliCardProps {\n installing: boolean;\n onInstall: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction InstallCliCard({ installing, onInstall, tr }: InstallCliCardProps) {\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex items-start gap-3\">\n <PlayCircle className=\"h-6 w-6 text-action-primary-bg shrink-0\" />\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('plan.installCliTitle', 'Install the plannotator CLI')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'plan.installCliDesc',\n 'Plan mode runs a local plannotator binary on the agent and proxies its UI back here. The plugin installs the binary into ~/.local/bin/plannotator (sha256-pinned). Roughly 30 seconds.'\n )}\n </p>\n <button\n onClick={onInstall}\n disabled={installing}\n className=\"mt-4 inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50\"\n >\n {installing ? (\n <>\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n {tr('plan.installing', 'Installing…')}\n </>\n ) : (\n tr('plan.installCliCta', 'Install plannotator CLI')\n )}\n </button>\n </div>\n </div>\n </div>\n );\n}\n\ninterface WizardCardProps {\n agents: AgentDetectionResult[];\n configuring: string | null;\n onConfigure: (agentKind: AgentDetectionResult['agent']) => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction WizardCard({ agents, configuring, onConfigure, tr }: WizardCardProps) {\n const installed = agents.filter((a) => a.cliInstalled);\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex items-start gap-3\">\n <Settings className=\"h-6 w-6 text-action-primary-bg shrink-0\" />\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('plan.wizardTitle', 'Connect an AI coding agent')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'plan.wizardDesc',\n 'Configure a plannotator hook for at least one installed AI coding agent. The hook forwards plan content from the agent to plannotator on every plan-finalisation step.'\n )}\n </p>\n {installed.length === 0 && (\n <p className=\"mt-4 text-sm text-status-warning-text\">\n {tr(\n 'plan.wizardNoAgents',\n 'No AI coding agents detected in this sandbox. Install one (Claude Code, OpenCode, Codex, Pi, or Gemini) and refresh.'\n )}\n </p>\n )}\n <ul className=\"mt-4 space-y-2\">\n {agents.map((entry) => (\n <li\n key={entry.agent}\n className=\"flex items-start gap-3 p-3 border border-border-default rounded-md bg-bg-surface\"\n >\n {entry.hookConfigured ? (\n <CheckCircle2 className=\"h-5 w-5 text-status-success-text shrink-0 mt-0.5\" />\n ) : (\n <div className=\"h-5 w-5 rounded-full border-2 border-border-default shrink-0 mt-0.5\" />\n )}\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">{entry.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">{entry.instructions}</p>\n {!entry.cliInstalled && (\n <p className=\"text-xs text-status-warning-text mt-1\">\n {tr('plan.agentCliMissing', 'CLI not detected on this sandbox.')}\n </p>\n )}\n </div>\n <button\n disabled={\n !entry.cliInstalled || entry.hookConfigured || configuring === entry.agent\n }\n onClick={() => onConfigure(entry.agent)}\n className=\"px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50\"\n >\n {entry.hookConfigured\n ? tr('plan.hookConfiguredLabel', 'Configured')\n : configuring === entry.agent\n ? tr('plan.configuring', 'Configuring…')\n : tr('plan.configureHook', 'Configure hook')}\n </button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n </div>\n );\n}\n\ninterface PlanReadyPanelProps {\n agent: AgentRef;\n agents: AgentDetectionResult[];\n agentApiKey: string | null;\n tunnelUrl: string | null;\n onReconfigure: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction PlanReadyPanel({\n agent,\n agents,\n agentApiKey,\n tunnelUrl,\n onReconfigure,\n tr,\n}: PlanReadyPanelProps) {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const [connection, setConnection] = useState<ConnectionStatus>('connecting');\n\n const iframeSrc = useMemo(() => {\n if (!tunnelUrl) return null;\n const base = tunnelUrl.replace(/\\/$/, '');\n return new URL(`${base}/plan/`).toString();\n }, [tunnelUrl]);\n\n const sendAuth = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe?.contentWindow || !agentApiKey || !tunnelUrl) return;\n try {\n const targetOrigin = new URL(tunnelUrl).origin;\n iframe.contentWindow.postMessage({ type: 'vibe-auth', apiKey: agentApiKey }, targetOrigin);\n } catch {\n // Origin parsing can throw on malformed tunnelUrl — ignored.\n }\n }, [agentApiKey, tunnelUrl]);\n\n const handleLoad = useCallback(() => {\n setConnection('connected');\n sendAuth();\n }, [sendAuth]);\n\n const handleError = useCallback(() => {\n setConnection('error');\n }, []);\n\n const handleRetry = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe) return;\n setConnection('connecting');\n const src = iframe.src;\n iframe.src = 'about:blank';\n requestAnimationFrame(() => {\n iframe.src = src;\n });\n }, []);\n\n // Used elsewhere for analytics / breadcrumbs.\n void agent;\n\n if (!tunnelUrl || !agentApiKey) {\n return (\n <div className=\"rounded-lg border border-status-warning-border bg-status-warning-bg p-4 text-sm text-status-warning-text\">\n {tr(\n 'plan.tunnelMissing',\n 'Agent tunnel URL or API key is missing. Bring the agent online and refresh.'\n )}\n </div>\n );\n }\n\n const configuredAgents = agents.filter((a) => a.hookConfigured);\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center gap-1\">\n {connection === 'connected' && <Wifi className=\"h-3.5 w-3.5 text-status-success-text\" />}\n {connection === 'connecting' && (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin text-status-warning-text\" />\n )}\n {connection === 'error' && <WifiOff className=\"h-3.5 w-3.5 text-status-error-text\" />}\n {tr(`plan.connection.${connection}`, connection)}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>{tr('plan.hookCount', `${configuredAgents.length} hook(s) configured`)}</span>\n <button\n onClick={onReconfigure}\n className=\"ml-auto inline-flex items-center gap-1 hover:text-text-primary\"\n >\n <Settings className=\"h-3.5 w-3.5\" />\n {tr('plan.reopenWizard', 'Manage hooks')}\n </button>\n </div>\n\n <div className=\"border border-border-default rounded-lg overflow-hidden bg-bg-sunken relative h-[calc(100vh-320px)] min-h-[500px]\">\n {iframeSrc && (\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n title={tr('plan.iframeTitle', 'Plannotator plan workspace')}\n className=\"w-full h-full border-0\"\n allow=\"clipboard-read; clipboard-write\"\n sandbox=\"allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation-by-user-activation\"\n onLoad={handleLoad}\n onError={handleError}\n data-testid=\"plan-iframe\"\n />\n )}\n {connection === 'connecting' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg-sunken/80 pointer-events-none\">\n <Loader2 className=\"h-6 w-6 animate-spin text-action-primary-bg\" />\n </div>\n )}\n {connection === 'error' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg-sunken p-6 text-center\">\n <div>\n <AlertCircle className=\"h-6 w-6 text-status-error-text mx-auto\" />\n <p className=\"text-sm font-medium text-text-primary mt-2\">\n {tr('plan.iframeErrorTitle', 'Could not load plannotator.')}\n </p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'plan.iframeErrorDesc',\n 'The agent reverse proxy returned an error. Check the agent status and try again.'\n )}\n </p>\n <button\n onClick={handleRetry}\n className=\"mt-3 inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-sm text-action-primary-text hover:bg-action-primary-bgHover\"\n >\n <RefreshCcw className=\"h-3.5 w-3.5\" /> {tr('plan.retry', 'Retry')}\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
2
|
import { agentAiGet as t, agentAiMutate as n } from "../../services/aiApi.js";
|
|
3
3
|
import { validateAliasName as ee } from "./aliasReservedNames.js";
|
|
4
|
-
import { useCallback as
|
|
5
|
-
import { Check as
|
|
4
|
+
import { useCallback as te, useEffect as ne, useState as r } from "react";
|
|
5
|
+
import { Check as re, Loader2 as i, PackageOpen as ie, Pencil as a, Play as o, Plus as s, Terminal as ae, Trash2 as c, X as l } from "lucide-react";
|
|
6
6
|
import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/aliases/AliasManager.tsx
|
|
8
|
+
var p = /(?:^|[\s"'(:\[])404(?=$|[\s"',.):;\]])/, m = /(?:^|[\s"'(:\[])not_found(?=$|[\s"',.):;\]])/;
|
|
8
9
|
function oe(e) {
|
|
9
10
|
if (!e) return !1;
|
|
10
11
|
let t = e.toLowerCase();
|
|
11
|
-
return
|
|
12
|
+
return !!(m.test(t) || p.test(t) || t.includes("not installed on the agent"));
|
|
12
13
|
}
|
|
13
|
-
var
|
|
14
|
-
function se(e, t =
|
|
14
|
+
var h = 80;
|
|
15
|
+
function se(e, t = h) {
|
|
15
16
|
return e ? e.length > t ? `${e.slice(0, t)}…` : e : "";
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
-
let m = e(), h = `/api/profiles/${encodeURIComponent(p.profile ?? "default")}/aliases`, [g, ce] =
|
|
18
|
+
function g({ agent: p }) {
|
|
19
|
+
let m = e(), h = `/api/profiles/${encodeURIComponent(p.profile ?? "default")}/agent/aliases`, [g, ce] = r([]), [le, _] = r(!0), [v, y] = r(null), [ue, b] = r(!1), [x, S] = r(""), [C, w] = r(""), [T, E] = r(""), [D, O] = r(!1), [k, A] = r(null), [de, j] = r(null), [M, N] = r(""), [P, F] = r(""), [I, L] = r(!1), [R, z] = r(null), [B, V] = r(""), [H, U] = r(null), [W, G] = r(!1), [K, q] = r(null), [J, Y] = r(null), [X, Z] = r(!1), Q = te(async () => {
|
|
19
20
|
_(!0), y(null), b(!1);
|
|
20
21
|
try {
|
|
21
22
|
let e = await t(p.id, h);
|
|
22
|
-
ce(Array.isArray(e) ? e : e.items ?? []);
|
|
23
|
+
ce(Array.isArray(e) ? e : e.aliases ?? e.items ?? []);
|
|
23
24
|
} catch (e) {
|
|
24
25
|
let t = e instanceof Error ? e.message : String(e);
|
|
25
26
|
oe(t) ? b(!0) : y(t);
|
|
@@ -27,7 +28,7 @@ function m({ agent: p }) {
|
|
|
27
28
|
_(!1);
|
|
28
29
|
}
|
|
29
30
|
}, [p.id, h]);
|
|
30
|
-
|
|
31
|
+
ne(() => {
|
|
31
32
|
Q();
|
|
32
33
|
}, [Q]);
|
|
33
34
|
let fe = async () => {
|
|
@@ -96,14 +97,14 @@ function m({ agent: p }) {
|
|
|
96
97
|
return ue ? /* @__PURE__ */ f("div", {
|
|
97
98
|
className: "rounded-lg border border-border-default bg-bg-surface p-8 text-center space-y-3",
|
|
98
99
|
children: [
|
|
99
|
-
/* @__PURE__ */ d(
|
|
100
|
+
/* @__PURE__ */ d(ie, { className: "w-8 h-8 mx-auto text-text-muted" }),
|
|
100
101
|
/* @__PURE__ */ d("h3", {
|
|
101
102
|
className: "text-base font-medium text-text-primary",
|
|
102
|
-
children: m("aliases.
|
|
103
|
+
children: m("aliases.unavailableTitle", "Aliases not available on this agent")
|
|
103
104
|
}),
|
|
104
105
|
/* @__PURE__ */ d("p", {
|
|
105
106
|
className: "text-sm text-text-secondary max-w-md mx-auto",
|
|
106
|
-
children: m("aliases.
|
|
107
|
+
children: m("aliases.unavailableBody", "This agent version doesn't support shell aliases. Upgrade the agent to the latest @vibecontrols/agent release to enable per-agent shell aliases.")
|
|
107
108
|
}),
|
|
108
109
|
/* @__PURE__ */ d("button", {
|
|
109
110
|
type: "button",
|
|
@@ -155,7 +156,7 @@ function m({ agent: p }) {
|
|
|
155
156
|
onClick: fe,
|
|
156
157
|
disabled: D || !x.trim() || !C.trim(),
|
|
157
158
|
className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50",
|
|
158
|
-
children: [D ? /* @__PURE__ */ d(
|
|
159
|
+
children: [D ? /* @__PURE__ */ d(i, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ d(s, { className: "w-4 h-4" }), m("aliases.create", "Create")]
|
|
159
160
|
})
|
|
160
161
|
})
|
|
161
162
|
]
|
|
@@ -164,7 +165,7 @@ function m({ agent: p }) {
|
|
|
164
165
|
className: "rounded-lg border border-border-default overflow-hidden",
|
|
165
166
|
children: le ? /* @__PURE__ */ d("div", {
|
|
166
167
|
className: "p-8 text-center text-text-secondary",
|
|
167
|
-
children: /* @__PURE__ */ d(
|
|
168
|
+
children: /* @__PURE__ */ d(i, { className: "w-5 h-5 animate-spin mx-auto" })
|
|
168
169
|
}) : v ? /* @__PURE__ */ d("div", {
|
|
169
170
|
className: "p-4 text-sm text-status-error-text",
|
|
170
171
|
children: v
|
|
@@ -243,7 +244,7 @@ function m({ agent: p }) {
|
|
|
243
244
|
disabled: I,
|
|
244
245
|
className: "p-1.5 rounded hover:bg-bg-sunken text-status-success-text",
|
|
245
246
|
title: m("aliases.save", "Save"),
|
|
246
|
-
children: I ? /* @__PURE__ */ d(
|
|
247
|
+
children: I ? /* @__PURE__ */ d(i, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ d(re, { className: "w-4 h-4" })
|
|
247
248
|
}), /* @__PURE__ */ d("button", {
|
|
248
249
|
onClick: $,
|
|
249
250
|
className: "p-1.5 rounded hover:bg-bg-sunken text-text-secondary",
|
|
@@ -262,7 +263,7 @@ function m({ agent: p }) {
|
|
|
262
263
|
onClick: () => pe(e),
|
|
263
264
|
className: "p-1.5 rounded hover:bg-bg-sunken text-text-secondary",
|
|
264
265
|
title: m("aliases.edit", "Edit"),
|
|
265
|
-
children: /* @__PURE__ */ d(
|
|
266
|
+
children: /* @__PURE__ */ d(a, { className: "w-4 h-4" })
|
|
266
267
|
}),
|
|
267
268
|
/* @__PURE__ */ d("button", {
|
|
268
269
|
onClick: () => Y(e),
|
|
@@ -360,7 +361,7 @@ function m({ agent: p }) {
|
|
|
360
361
|
onClick: () => void ge(),
|
|
361
362
|
disabled: W,
|
|
362
363
|
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50",
|
|
363
|
-
children: [W ? /* @__PURE__ */ d(
|
|
364
|
+
children: [W ? /* @__PURE__ */ d(i, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ d(o, { className: "w-4 h-4" }), m("aliases.run", "Run")]
|
|
364
365
|
})]
|
|
365
366
|
})
|
|
366
367
|
]
|
|
@@ -396,7 +397,7 @@ function m({ agent: p }) {
|
|
|
396
397
|
onClick: () => void he(),
|
|
397
398
|
disabled: X,
|
|
398
399
|
className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-status-error-text bg-status-error-bg rounded-md hover:opacity-80 disabled:opacity-50",
|
|
399
|
-
children: [X ? /* @__PURE__ */ d(
|
|
400
|
+
children: [X ? /* @__PURE__ */ d(i, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ d(c, { className: "w-4 h-4" }), m("aliases.delete", "Delete")]
|
|
400
401
|
})]
|
|
401
402
|
})
|
|
402
403
|
]
|
|
@@ -406,6 +407,6 @@ function m({ agent: p }) {
|
|
|
406
407
|
});
|
|
407
408
|
}
|
|
408
409
|
//#endregion
|
|
409
|
-
export {
|
|
410
|
+
export { g as AliasManager };
|
|
410
411
|
|
|
411
412
|
//# sourceMappingURL=AliasManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AliasManager.js","names":[],"sources":["../../../src/components/aliases/AliasManager.tsx"],"sourcesContent":["/**\n * AliasManager — per-agent CRUDL for shell aliases exposed by\n * vibecontrols-agent under `/api/aliases/*`.\n *\n * Pairs with the agent's Phase A4 ship.\n *\n * Endpoints (proxied through wspace-vibecontrols-svc → agent):\n * GET /api/aliases → { items: Alias[] }\n * GET /api/aliases/:name → Alias\n * POST /api/aliases → Alias\n * PUT /api/aliases/:name → Alias\n * DELETE /api/aliases/:name\n * POST /api/aliases/:name/execute → { exit_code, stdout, stderr }\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { Plus, Trash2, Play, Pencil, Check, X, Loader2, Terminal, PackageOpen } from 'lucide-react';\nimport { agentAiGet, agentAiMutate } from '@/services/aiApi';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { validateAliasName } from './aliasReservedNames';\n\n/**\n * Detect the signatures that mean \"the aliases plugin isn't installed on\n * this agent.\" The agent answers 404 with body `NOT_FOUND` when no plugin\n * is mounted at `/api/profiles/<profile>/aliases`; the svc proxy\n * sanitizes that to the \"feature is not installed\" string (see aiApi\n * `sanitizeError`). We also catch the underlying error text directly so\n * we don't depend on `sanitizeError` keeping that exact phrasing.\n *\n * Reviewer follow-up: deliberately NOT matching Apollo's default\n * `Unexpected error.` mask here. It would catch today's\n * `WorkspaceRequiredError`-from-`agentAiGet` symptom AND legitimate\n * future errors (auth failures, timeouts, etc.) once the plugin ships,\n * masking them as \"plugin not installed\" — a worse UX than the honest\n * raw error. The right cure for the workspace-context surface lives at\n * the svc resolver (translate to a typed GraphQL error code the FE can\n * pivot on) and the aiApi sanitizer; tracked in BOFF-2613.\n */\nfunction isAliasesPluginUnavailable(msg: string): boolean {\n if (!msg) return false;\n const m = msg.toLowerCase();\n return m.includes('not installed on the agent') || m.includes('not_found') || m.includes(' 404');\n}\n\ninterface Alias {\n name: string;\n command: string;\n description?: string | null;\n updatedAt?: string | null;\n createdAt?: string | null;\n}\n\ninterface AliasListResponse {\n items?: Alias[];\n}\n\ninterface AliasExecuteResult {\n exit_code: number;\n stdout: string;\n stderr: string;\n}\n\ninterface AliasManagerProps {\n agent: { id: string; profile?: string };\n}\n\nconst TRUNCATE = 80;\n\nfunction truncate(str: string, n = TRUNCATE): string {\n if (!str) return '';\n return str.length > n ? `${str.slice(0, n)}…` : str;\n}\n\nexport function AliasManager({ agent }: AliasManagerProps) {\n const tr = useTr();\n // Canonical agent URL hierarchy: agent → profile → plugins/data.\n // The aliases plugin lives under `/api/profiles/<profile>/aliases/*`.\n // No client-side rewrites — every consumer constructs the canonical\n // path inline so the URL the agent sees is the URL the FE wrote.\n const profile = encodeURIComponent(agent.profile ?? 'default');\n const aliasesBase = `/api/profiles/${profile}/aliases`;\n const [aliases, setAliases] = useState<Alias[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n // Distinguish \"the agent doesn't have this plugin installed\" from a generic\n // error so we can show a clean empty-state with an actionable hint instead\n // of a scary stack-trace fragment. [BOFF-2613]\n const [pluginUnavailable, setPluginUnavailable] = useState(false);\n\n // Add-new form\n const [newName, setNewName] = useState('');\n const [newCommand, setNewCommand] = useState('');\n const [newDescription, setNewDescription] = useState('');\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n\n // Edit row\n const [editingName, setEditingName] = useState<string | null>(null);\n const [editCommand, setEditCommand] = useState('');\n const [editDescription, setEditDescription] = useState('');\n const [savingEdit, setSavingEdit] = useState(false);\n\n // Run modal\n const [runTarget, setRunTarget] = useState<Alias | null>(null);\n const [runArgs, setRunArgs] = useState('');\n const [runResult, setRunResult] = useState<AliasExecuteResult | null>(null);\n const [runRunning, setRunRunning] = useState(false);\n const [runError, setRunError] = useState<string | null>(null);\n\n // Delete confirm\n const [deleteTarget, setDeleteTarget] = useState<Alias | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n const refresh = useCallback(async () => {\n setLoading(true);\n setError(null);\n setPluginUnavailable(false);\n try {\n const data = await agentAiGet<AliasListResponse | Alias[]>(agent.id, aliasesBase);\n const items = Array.isArray(data) ? data : (data.items ?? []);\n setAliases(items);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (isAliasesPluginUnavailable(msg)) {\n // Plugin not installed on this agent (or upstream context-required\n // error that we treat the same way for this tab) — show empty-state\n // instead of a scary error banner. [BOFF-2613]\n setPluginUnavailable(true);\n } else {\n setError(msg);\n }\n } finally {\n setLoading(false);\n }\n }, [agent.id, aliasesBase]);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n // ── Create ────────────────────────────────────────────────────────\n const handleCreate = async () => {\n setCreateError(null);\n const nameErr = validateAliasName(newName.trim());\n if (nameErr) {\n setCreateError(nameErr);\n return;\n }\n if (!newCommand.trim()) {\n setCreateError(tr('aliases.commandRequired', 'Command is required'));\n return;\n }\n setCreating(true);\n try {\n await agentAiMutate(agent.id, aliasesBase, 'POST', {\n name: newName.trim(),\n command: newCommand,\n description: newDescription || undefined,\n });\n setNewName('');\n setNewCommand('');\n setNewDescription('');\n await refresh();\n } catch (e) {\n setCreateError(e instanceof Error ? e.message : String(e));\n } finally {\n setCreating(false);\n }\n };\n\n // ── Edit ──────────────────────────────────────────────────────────\n const startEdit = (alias: Alias) => {\n setEditingName(alias.name);\n setEditCommand(alias.command);\n setEditDescription(alias.description ?? '');\n };\n\n const cancelEdit = () => {\n setEditingName(null);\n setEditCommand('');\n setEditDescription('');\n };\n\n const saveEdit = async (alias: Alias) => {\n setSavingEdit(true);\n try {\n await agentAiMutate(agent.id, `${aliasesBase}/${encodeURIComponent(alias.name)}`, 'PUT', {\n command: editCommand,\n description: editDescription || undefined,\n });\n cancelEdit();\n await refresh();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setSavingEdit(false);\n }\n };\n\n // ── Delete ────────────────────────────────────────────────────────\n const confirmDelete = async () => {\n if (!deleteTarget) return;\n setDeleting(true);\n try {\n await agentAiMutate(\n agent.id,\n `${aliasesBase}/${encodeURIComponent(deleteTarget.name)}`,\n 'DELETE'\n );\n setDeleteTarget(null);\n await refresh();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setDeleting(false);\n }\n };\n\n // ── Run ───────────────────────────────────────────────────────────\n const runAlias = async () => {\n if (!runTarget) return;\n setRunRunning(true);\n setRunError(null);\n setRunResult(null);\n try {\n const args = runArgs.split(/\\s+/).filter(Boolean);\n const result = await agentAiMutate<AliasExecuteResult>(\n agent.id,\n `${aliasesBase}/${encodeURIComponent(runTarget.name)}/execute`,\n 'POST',\n { args }\n );\n setRunResult(result);\n } catch (e) {\n setRunError(e instanceof Error ? e.message : String(e));\n } finally {\n setRunRunning(false);\n }\n };\n\n if (pluginUnavailable) {\n // The aliases plugin isn't installed on this agent. Show a clean,\n // actionable empty-state instead of the form + the raw error string.\n // [BOFF-2613]\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-8 text-center space-y-3\">\n <PackageOpen className=\"w-8 h-8 mx-auto text-text-muted\" />\n <h3 className=\"text-base font-medium text-text-primary\">\n {tr('aliases.pluginMissingTitle', 'Aliases plugin not installed')}\n </h3>\n <p className=\"text-sm text-text-secondary max-w-md mx-auto\">\n {tr(\n 'aliases.pluginMissingBody',\n \"This agent doesn't have the aliases plugin installed yet. Install @vibecontrols/vibe-plugin-aliases from the Plugins tab to enable per-agent shell aliases.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={refresh}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-text-secondary border border-border-default rounded-md hover:bg-bg-sunken transition-colors\"\n >\n {tr('aliases.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Add new alias form */}\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4 space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary flex items-center gap-2\">\n <Plus className=\"w-4 h-4\" />\n {tr('aliases.createTitle', 'New alias')}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <input\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder={tr('aliases.namePlaceholder', 'Name (a-z, 0-9, _, -)')}\n className=\"px-3 py-2 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 <input\n type=\"text\"\n value={newDescription}\n onChange={(e) => setNewDescription(e.target.value)}\n placeholder={tr('aliases.descriptionPlaceholder', 'Description (optional)')}\n className=\"px-3 py-2 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\"\n />\n </div>\n <textarea\n value={newCommand}\n onChange={(e) => setNewCommand(e.target.value)}\n placeholder={tr('aliases.commandPlaceholder', 'Command (e.g. git status)')}\n rows={3}\n className=\"w-full px-3 py-2 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 {createError && <div className=\"text-xs text-status-error-text\">{createError}</div>}\n <div className=\"flex justify-end\">\n <button\n onClick={handleCreate}\n disabled={creating || !newName.trim() || !newCommand.trim()}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {creating ? <Loader2 className=\"w-4 h-4 animate-spin\" /> : <Plus className=\"w-4 h-4\" />}\n {tr('aliases.create', 'Create')}\n </button>\n </div>\n </div>\n\n {/* List */}\n <div className=\"rounded-lg border border-border-default overflow-hidden\">\n {loading ? (\n <div className=\"p-8 text-center text-text-secondary\">\n <Loader2 className=\"w-5 h-5 animate-spin mx-auto\" />\n </div>\n ) : error ? (\n <div className=\"p-4 text-sm text-status-error-text\">{error}</div>\n ) : aliases.length === 0 ? (\n <div className=\"p-8 text-center text-sm text-text-secondary\">\n {tr('aliases.empty', 'No aliases yet. Create one above.')}\n </div>\n ) : (\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-sunken text-text-secondary\">\n <tr>\n <th className=\"text-left px-3 py-2 font-medium\">{tr('aliases.colName', 'Name')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colCommand', 'Command')}\n </th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colDescription', 'Description')}\n </th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colUpdatedAt', 'Updated')}\n </th>\n <th className=\"text-right px-3 py-2 font-medium\">\n {tr('aliases.colActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {aliases.map((alias) => {\n const isEditing = editingName === alias.name;\n return (\n <tr key={alias.name} className=\"border-t border-border-default\">\n <td className=\"px-3 py-2 font-mono text-text-primary align-top\">\n {alias.name}\n </td>\n <td className=\"px-3 py-2 align-top\">\n {isEditing ? (\n <textarea\n value={editCommand}\n onChange={(e) => setEditCommand(e.target.value)}\n rows={2}\n className=\"w-full px-2 py-1 text-xs bg-bg-surface border border-border-default rounded text-text-primary font-mono\"\n />\n ) : (\n <code className=\"text-xs text-text-primary\" title={alias.command}>\n {truncate(alias.command)}\n </code>\n )}\n </td>\n <td className=\"px-3 py-2 align-top\">\n {isEditing ? (\n <input\n value={editDescription}\n onChange={(e) => setEditDescription(e.target.value)}\n className=\"w-full px-2 py-1 text-xs bg-bg-surface border border-border-default rounded text-text-primary\"\n />\n ) : (\n <span className=\"text-xs text-text-secondary\">\n {alias.description ?? '—'}\n </span>\n )}\n </td>\n <td className=\"px-3 py-2 text-xs text-text-secondary align-top\">\n {alias.updatedAt ? new Date(alias.updatedAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2 align-top\">\n <div className=\"flex items-center justify-end gap-1\">\n {isEditing ? (\n <>\n <button\n onClick={() => void saveEdit(alias)}\n disabled={savingEdit}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-status-success-text\"\n title={tr('aliases.save', 'Save')}\n >\n {savingEdit ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Check className=\"w-4 h-4\" />\n )}\n </button>\n <button\n onClick={cancelEdit}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-text-secondary\"\n title={tr('common.cancel', 'Cancel')}\n >\n <X className=\"w-4 h-4\" />\n </button>\n </>\n ) : (\n <>\n <button\n onClick={() => {\n setRunTarget(alias);\n setRunArgs('');\n setRunResult(null);\n setRunError(null);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-action-primary-bg\"\n title={tr('aliases.run', 'Run')}\n >\n <Play className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => startEdit(alias)}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-text-secondary\"\n title={tr('aliases.edit', 'Edit')}\n >\n <Pencil className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => setDeleteTarget(alias)}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-status-error-text\"\n title={tr('aliases.delete', 'Delete')}\n >\n <Trash2 className=\"w-4 h-4\" />\n </button>\n </>\n )}\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n )}\n </div>\n\n {/* Run modal */}\n {runTarget && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-2xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] flex flex-col\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"w-5 h-5\" />\n {tr('aliases.runTitle', 'Run alias')}: <code>{runTarget.name}</code>\n </h2>\n <button onClick={() => setRunTarget(null)} className=\"p-1 rounded hover:bg-bg-sunken\">\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto p-6 space-y-3\">\n <div className=\"text-xs text-text-secondary font-mono break-all\">\n $ {runTarget.command}\n </div>\n <input\n type=\"text\"\n value={runArgs}\n onChange={(e) => setRunArgs(e.target.value)}\n placeholder={tr('aliases.argsPlaceholder', 'Args (space-separated, optional)')}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary font-mono\"\n />\n {runError && <div className=\"text-sm text-status-error-text\">{runError}</div>}\n {runResult && (\n <div className=\"space-y-2\">\n <div className=\"text-xs text-text-secondary\">\n {tr('aliases.exitCode', 'Exit code')}:{' '}\n <span\n className={\n runResult.exit_code === 0\n ? 'text-status-success-text font-mono'\n : 'text-status-error-text font-mono'\n }\n >\n {runResult.exit_code}\n </span>\n </div>\n {runResult.stdout && (\n <div>\n <div className=\"text-xs text-text-secondary mb-1\">stdout</div>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded font-mono whitespace-pre-wrap break-all max-h-64 overflow-y-auto text-text-primary\">\n {runResult.stdout}\n </pre>\n </div>\n )}\n {runResult.stderr && (\n <div>\n <div className=\"text-xs text-text-secondary mb-1\">stderr</div>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded font-mono whitespace-pre-wrap break-all max-h-64 overflow-y-auto text-status-error-text\">\n {runResult.stderr}\n </pre>\n </div>\n )}\n </div>\n )}\n </div>\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken\">\n <button\n onClick={() => setRunTarget(null)}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated\"\n >\n {tr('common.close', 'Close')}\n </button>\n <button\n onClick={() => void runAlias()}\n disabled={runRunning}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {runRunning ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Play className=\"w-4 h-4\" />\n )}\n {tr('aliases.run', 'Run')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Delete confirm */}\n {deleteTarget && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-md bg-bg-surface rounded-lg shadow-xl p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('aliases.deleteTitle', 'Delete alias?')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('aliases.deleteConfirm', 'This permanently removes the alias from this profile.')}{' '}\n <code className=\"font-mono text-text-primary\">{deleteTarget.name}</code>\n </p>\n <div className=\"flex justify-end gap-3\">\n <button\n onClick={() => setDeleteTarget(null)}\n className=\"px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-elevated\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={() => void confirmDelete()}\n disabled={deleting}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-status-error-text bg-status-error-bg rounded-md hover:opacity-80 disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Trash2 className=\"w-4 h-4\" />\n )}\n {tr('aliases.delete', 'Delete')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AliasManager.js","names":[],"sources":["../../../src/components/aliases/AliasManager.tsx"],"sourcesContent":["/**\n * AliasManager — per-agent CRUDL for shell aliases exposed by\n * vibecontrols-agent under `/api/agent/aliases/*` (mounted by the core\n * `agent` plugin's route module, not a standalone aliases plugin).\n *\n * Endpoints (proxied through wspace-vibecontrols-svc → agent):\n * GET /api/agent/aliases → { aliases: Alias[] }\n * GET /api/agent/aliases/:name → Alias\n * POST /api/agent/aliases → Alias\n * PUT /api/agent/aliases/:name → Alias\n * DELETE /api/agent/aliases/:name\n * POST /api/agent/aliases/:name/execute → { exit_code, stdout, stderr, duration_ms }\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { Plus, Trash2, Play, Pencil, Check, X, Loader2, Terminal, PackageOpen } from 'lucide-react';\nimport { agentAiGet, agentAiMutate } from '@/services/aiApi';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { validateAliasName } from './aliasReservedNames';\n\n// Anchor 404 by surrounding punctuation, not `\\b404\\b` — `\\b` matches\n// `v1.2.404`. Same shape used for `not_found` below so we don't match\n// inside `user_not_found` / `config_not_found` etc.\n// Escape `[` for `u`/`v` Unicode-mode forward-compat.\n// eslint-disable-next-line no-useless-escape -- forward-compat for Unicode regex mode\nconst HTTP_404_PATTERN = /(?:^|[\\s\"'(:\\[])404(?=$|[\\s\"',.):;\\]])/;\n// eslint-disable-next-line no-useless-escape -- forward-compat for Unicode regex mode\nconst NOT_FOUND_TOKEN_PATTERN = /(?:^|[\\s\"'(:\\[])not_found(?=$|[\\s\"',.):;\\]])/;\nfunction isAliasesRouteUnavailable(msg: string): boolean {\n if (!msg) return false;\n const m = msg.toLowerCase();\n if (NOT_FOUND_TOKEN_PATTERN.test(m)) return true;\n if (HTTP_404_PATTERN.test(m)) return true;\n if (m.includes('not installed on the agent')) return true;\n return false;\n}\n\ninterface Alias {\n name: string;\n command: string;\n description?: string | null;\n updatedAt?: string | null;\n createdAt?: string | null;\n}\n\n// The agent's `GET /api/agent/aliases` returns `{ aliases: Alias[] }`.\n// We also tolerate `{ items: Alias[] }` and a bare array for forward-\n// compat with any future plugin shipment that returns the older shape.\ninterface AliasListResponse {\n aliases?: Alias[];\n items?: Alias[];\n}\n\ninterface AliasExecuteResult {\n exit_code: number;\n stdout: string;\n stderr: string;\n /**\n * Wall-clock milliseconds the child took to exit. Matches the\n * agent's `RunCapturedResult.duration_ms`. Not surfaced in the run\n * modal yet — declared so the type matches the documented response\n * shape and so a future UI can show it without a second TS pass.\n */\n duration_ms?: number;\n}\n\ninterface AliasManagerProps {\n agent: { id: string; profile?: string };\n}\n\nconst TRUNCATE = 80;\n\nfunction truncate(str: string, n = TRUNCATE): string {\n if (!str) return '';\n return str.length > n ? `${str.slice(0, n)}…` : str;\n}\n\nexport function AliasManager({ agent }: AliasManagerProps) {\n const tr = useTr();\n // Canonical agent URL hierarchy: agent → profile → plugin → routes.\n // Alias CRUDL is owned by the core `agent` plugin, mounted at\n // `/api/profiles/<profile>/agent/aliases/*`. No client-side rewrites —\n // every consumer constructs the canonical path inline so the URL the\n // agent sees is the URL the FE wrote.\n const profile = encodeURIComponent(agent.profile ?? 'default');\n const aliasesBase = `/api/profiles/${profile}/agent/aliases`;\n const [aliases, setAliases] = useState<Alias[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n // Distinguish \"this agent build doesn't have the alias routes\" from a\n // generic error so we can show a clean empty-state with an actionable\n // hint instead of a scary stack-trace fragment. [BOFF-2613/2616]\n const [routeUnavailable, setRouteUnavailable] = useState(false);\n\n // Add-new form\n const [newName, setNewName] = useState('');\n const [newCommand, setNewCommand] = useState('');\n const [newDescription, setNewDescription] = useState('');\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n\n // Edit row\n const [editingName, setEditingName] = useState<string | null>(null);\n const [editCommand, setEditCommand] = useState('');\n const [editDescription, setEditDescription] = useState('');\n const [savingEdit, setSavingEdit] = useState(false);\n\n // Run modal\n const [runTarget, setRunTarget] = useState<Alias | null>(null);\n const [runArgs, setRunArgs] = useState('');\n const [runResult, setRunResult] = useState<AliasExecuteResult | null>(null);\n const [runRunning, setRunRunning] = useState(false);\n const [runError, setRunError] = useState<string | null>(null);\n\n // Delete confirm\n const [deleteTarget, setDeleteTarget] = useState<Alias | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n const refresh = useCallback(async () => {\n setLoading(true);\n setError(null);\n setRouteUnavailable(false);\n try {\n const data = await agentAiGet<AliasListResponse | Alias[]>(agent.id, aliasesBase);\n const items = Array.isArray(data) ? data : (data.aliases ?? data.items ?? []);\n setAliases(items);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (isAliasesRouteUnavailable(msg)) {\n // Alias routes missing on this agent (or upstream context-required\n // error that we treat the same way for this tab) — show empty-state\n // instead of a scary error banner. [BOFF-2613/2616]\n setRouteUnavailable(true);\n } else {\n setError(msg);\n }\n } finally {\n setLoading(false);\n }\n }, [agent.id, aliasesBase]);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n // ── Create ────────────────────────────────────────────────────────\n const handleCreate = async () => {\n setCreateError(null);\n const nameErr = validateAliasName(newName.trim());\n if (nameErr) {\n setCreateError(nameErr);\n return;\n }\n if (!newCommand.trim()) {\n setCreateError(tr('aliases.commandRequired', 'Command is required'));\n return;\n }\n setCreating(true);\n try {\n await agentAiMutate(agent.id, aliasesBase, 'POST', {\n name: newName.trim(),\n command: newCommand,\n description: newDescription || undefined,\n });\n setNewName('');\n setNewCommand('');\n setNewDescription('');\n await refresh();\n } catch (e) {\n setCreateError(e instanceof Error ? e.message : String(e));\n } finally {\n setCreating(false);\n }\n };\n\n // ── Edit ──────────────────────────────────────────────────────────\n const startEdit = (alias: Alias) => {\n setEditingName(alias.name);\n setEditCommand(alias.command);\n setEditDescription(alias.description ?? '');\n };\n\n const cancelEdit = () => {\n setEditingName(null);\n setEditCommand('');\n setEditDescription('');\n };\n\n const saveEdit = async (alias: Alias) => {\n setSavingEdit(true);\n try {\n await agentAiMutate(agent.id, `${aliasesBase}/${encodeURIComponent(alias.name)}`, 'PUT', {\n command: editCommand,\n description: editDescription || undefined,\n });\n cancelEdit();\n await refresh();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setSavingEdit(false);\n }\n };\n\n // ── Delete ────────────────────────────────────────────────────────\n const confirmDelete = async () => {\n if (!deleteTarget) return;\n setDeleting(true);\n try {\n await agentAiMutate(\n agent.id,\n `${aliasesBase}/${encodeURIComponent(deleteTarget.name)}`,\n 'DELETE'\n );\n setDeleteTarget(null);\n await refresh();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setDeleting(false);\n }\n };\n\n // ── Run ───────────────────────────────────────────────────────────\n const runAlias = async () => {\n if (!runTarget) return;\n setRunRunning(true);\n setRunError(null);\n setRunResult(null);\n try {\n const args = runArgs.split(/\\s+/).filter(Boolean);\n const result = await agentAiMutate<AliasExecuteResult>(\n agent.id,\n `${aliasesBase}/${encodeURIComponent(runTarget.name)}/execute`,\n 'POST',\n { args }\n );\n setRunResult(result);\n } catch (e) {\n setRunError(e instanceof Error ? e.message : String(e));\n } finally {\n setRunRunning(false);\n }\n };\n\n if (routeUnavailable) {\n // Alias routes not present on this agent build. Show a clean,\n // actionable empty-state instead of the form + the raw error string.\n // [BOFF-2613/2616]\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-8 text-center space-y-3\">\n <PackageOpen className=\"w-8 h-8 mx-auto text-text-muted\" />\n <h3 className=\"text-base font-medium text-text-primary\">\n {tr('aliases.unavailableTitle', 'Aliases not available on this agent')}\n </h3>\n <p className=\"text-sm text-text-secondary max-w-md mx-auto\">\n {tr(\n 'aliases.unavailableBody',\n \"This agent version doesn't support shell aliases. Upgrade the agent to the latest @vibecontrols/agent release to enable per-agent shell aliases.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={refresh}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-text-secondary border border-border-default rounded-md hover:bg-bg-sunken transition-colors\"\n >\n {tr('aliases.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Add new alias form */}\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4 space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary flex items-center gap-2\">\n <Plus className=\"w-4 h-4\" />\n {tr('aliases.createTitle', 'New alias')}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <input\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder={tr('aliases.namePlaceholder', 'Name (a-z, 0-9, _, -)')}\n className=\"px-3 py-2 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 <input\n type=\"text\"\n value={newDescription}\n onChange={(e) => setNewDescription(e.target.value)}\n placeholder={tr('aliases.descriptionPlaceholder', 'Description (optional)')}\n className=\"px-3 py-2 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\"\n />\n </div>\n <textarea\n value={newCommand}\n onChange={(e) => setNewCommand(e.target.value)}\n placeholder={tr('aliases.commandPlaceholder', 'Command (e.g. git status)')}\n rows={3}\n className=\"w-full px-3 py-2 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 {createError && <div className=\"text-xs text-status-error-text\">{createError}</div>}\n <div className=\"flex justify-end\">\n <button\n onClick={handleCreate}\n disabled={creating || !newName.trim() || !newCommand.trim()}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {creating ? <Loader2 className=\"w-4 h-4 animate-spin\" /> : <Plus className=\"w-4 h-4\" />}\n {tr('aliases.create', 'Create')}\n </button>\n </div>\n </div>\n\n {/* List */}\n <div className=\"rounded-lg border border-border-default overflow-hidden\">\n {loading ? (\n <div className=\"p-8 text-center text-text-secondary\">\n <Loader2 className=\"w-5 h-5 animate-spin mx-auto\" />\n </div>\n ) : error ? (\n <div className=\"p-4 text-sm text-status-error-text\">{error}</div>\n ) : aliases.length === 0 ? (\n <div className=\"p-8 text-center text-sm text-text-secondary\">\n {tr('aliases.empty', 'No aliases yet. Create one above.')}\n </div>\n ) : (\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-sunken text-text-secondary\">\n <tr>\n <th className=\"text-left px-3 py-2 font-medium\">{tr('aliases.colName', 'Name')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colCommand', 'Command')}\n </th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colDescription', 'Description')}\n </th>\n <th className=\"text-left px-3 py-2 font-medium\">\n {tr('aliases.colUpdatedAt', 'Updated')}\n </th>\n <th className=\"text-right px-3 py-2 font-medium\">\n {tr('aliases.colActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {aliases.map((alias) => {\n const isEditing = editingName === alias.name;\n return (\n <tr key={alias.name} className=\"border-t border-border-default\">\n <td className=\"px-3 py-2 font-mono text-text-primary align-top\">\n {alias.name}\n </td>\n <td className=\"px-3 py-2 align-top\">\n {isEditing ? (\n <textarea\n value={editCommand}\n onChange={(e) => setEditCommand(e.target.value)}\n rows={2}\n className=\"w-full px-2 py-1 text-xs bg-bg-surface border border-border-default rounded text-text-primary font-mono\"\n />\n ) : (\n <code className=\"text-xs text-text-primary\" title={alias.command}>\n {truncate(alias.command)}\n </code>\n )}\n </td>\n <td className=\"px-3 py-2 align-top\">\n {isEditing ? (\n <input\n value={editDescription}\n onChange={(e) => setEditDescription(e.target.value)}\n className=\"w-full px-2 py-1 text-xs bg-bg-surface border border-border-default rounded text-text-primary\"\n />\n ) : (\n <span className=\"text-xs text-text-secondary\">\n {alias.description ?? '—'}\n </span>\n )}\n </td>\n <td className=\"px-3 py-2 text-xs text-text-secondary align-top\">\n {alias.updatedAt ? new Date(alias.updatedAt).toLocaleString() : '—'}\n </td>\n <td className=\"px-3 py-2 align-top\">\n <div className=\"flex items-center justify-end gap-1\">\n {isEditing ? (\n <>\n <button\n onClick={() => void saveEdit(alias)}\n disabled={savingEdit}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-status-success-text\"\n title={tr('aliases.save', 'Save')}\n >\n {savingEdit ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Check className=\"w-4 h-4\" />\n )}\n </button>\n <button\n onClick={cancelEdit}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-text-secondary\"\n title={tr('common.cancel', 'Cancel')}\n >\n <X className=\"w-4 h-4\" />\n </button>\n </>\n ) : (\n <>\n <button\n onClick={() => {\n setRunTarget(alias);\n setRunArgs('');\n setRunResult(null);\n setRunError(null);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-action-primary-bg\"\n title={tr('aliases.run', 'Run')}\n >\n <Play className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => startEdit(alias)}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-text-secondary\"\n title={tr('aliases.edit', 'Edit')}\n >\n <Pencil className=\"w-4 h-4\" />\n </button>\n <button\n onClick={() => setDeleteTarget(alias)}\n className=\"p-1.5 rounded hover:bg-bg-sunken text-status-error-text\"\n title={tr('aliases.delete', 'Delete')}\n >\n <Trash2 className=\"w-4 h-4\" />\n </button>\n </>\n )}\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n )}\n </div>\n\n {/* Run modal */}\n {runTarget && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-2xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] flex flex-col\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"w-5 h-5\" />\n {tr('aliases.runTitle', 'Run alias')}: <code>{runTarget.name}</code>\n </h2>\n <button onClick={() => setRunTarget(null)} className=\"p-1 rounded hover:bg-bg-sunken\">\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto p-6 space-y-3\">\n <div className=\"text-xs text-text-secondary font-mono break-all\">\n $ {runTarget.command}\n </div>\n <input\n type=\"text\"\n value={runArgs}\n onChange={(e) => setRunArgs(e.target.value)}\n placeholder={tr('aliases.argsPlaceholder', 'Args (space-separated, optional)')}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary font-mono\"\n />\n {runError && <div className=\"text-sm text-status-error-text\">{runError}</div>}\n {runResult && (\n <div className=\"space-y-2\">\n <div className=\"text-xs text-text-secondary\">\n {tr('aliases.exitCode', 'Exit code')}:{' '}\n <span\n className={\n runResult.exit_code === 0\n ? 'text-status-success-text font-mono'\n : 'text-status-error-text font-mono'\n }\n >\n {runResult.exit_code}\n </span>\n </div>\n {runResult.stdout && (\n <div>\n <div className=\"text-xs text-text-secondary mb-1\">stdout</div>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded font-mono whitespace-pre-wrap break-all max-h-64 overflow-y-auto text-text-primary\">\n {runResult.stdout}\n </pre>\n </div>\n )}\n {runResult.stderr && (\n <div>\n <div className=\"text-xs text-text-secondary mb-1\">stderr</div>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded font-mono whitespace-pre-wrap break-all max-h-64 overflow-y-auto text-status-error-text\">\n {runResult.stderr}\n </pre>\n </div>\n )}\n </div>\n )}\n </div>\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken\">\n <button\n onClick={() => setRunTarget(null)}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated\"\n >\n {tr('common.close', 'Close')}\n </button>\n <button\n onClick={() => void runAlias()}\n disabled={runRunning}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {runRunning ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Play className=\"w-4 h-4\" />\n )}\n {tr('aliases.run', 'Run')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Delete confirm */}\n {deleteTarget && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-md bg-bg-surface rounded-lg shadow-xl p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('aliases.deleteTitle', 'Delete alias?')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('aliases.deleteConfirm', 'This permanently removes the alias from this profile.')}{' '}\n <code className=\"font-mono text-text-primary\">{deleteTarget.name}</code>\n </p>\n <div className=\"flex justify-end gap-3\">\n <button\n onClick={() => setDeleteTarget(null)}\n className=\"px-3 py-1.5 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-elevated\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={() => void confirmDelete()}\n disabled={deleting}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-status-error-text bg-status-error-bg rounded-md hover:opacity-80 disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Trash2 className=\"w-4 h-4\" />\n )}\n {tr('aliases.delete', 'Delete')}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|