import { useMemo, useState } from 'react'; import { ExternalLink, Search } from 'lucide-react'; import { api } from '@/api/client'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { ACP_PROVIDER_CATALOG, buildAcpProviderConfigPatch, type AcpCatalogEntry, } from '@/data/acp-provider-catalog'; interface Props { open: boolean; onOpenChange: (open: boolean) => void; /** Fired after a successful add so the parent can refetch the snapshot. */ onAdded: (id: string) => void; } /** * v2.3 Phase 5 (design.md §7.3). Search the curated ACP catalog and register a * provider: PATCH /api/providers/config with its custom-ACP override, then * refresh that one provider. Adding only edits config — it does NOT install the * binary, so the provider shows "Not installed" until the CLI is on PATH. */ export function AddProviderModal({ open, onOpenChange, onAdded }: Props) { const [query, setQuery] = useState(''); const [busyId, setBusyId] = useState(null); const [error, setError] = useState(null); const filtered = useMemo(() => { const q = query.trim().toLowerCase(); if (!q) return ACP_PROVIDER_CATALOG; return ACP_PROVIDER_CATALOG.filter( (e) => e.id.toLowerCase().includes(q) || e.label.toLowerCase().includes(q) || e.description.toLowerCase().includes(q), ); }, [query]); async function add(entry: AcpCatalogEntry): Promise { setBusyId(entry.id); setError(null); try { await api.coder.patchProvidersConfig(buildAcpProviderConfigPatch(entry)); await api.coder.refreshProviders([entry.id]); onAdded(entry.id); onOpenChange(false); } catch (err) { // 422 from PATCH (invalid override) surfaces here as ApiError.message. setError(err instanceof Error ? err.message : 'failed to add provider'); } finally { setBusyId(null); } } return ( Add ACP provider Registers the provider in your coder config. It is not installed — install the CLI yourself; until it's on PATH it shows as “Not installed”.
setQuery(e.target.value)} placeholder="Search providers…" className="pl-7" />
{filtered.length === 0 && (
No matching providers.
)} {filtered.map((e) => (
{e.label}
{e.description}
$ {e.command.join(' ')}
Install {e.label} {e.installCmd && ( {e.installCmd} )}
))}
{error &&
{error}
}
); }