agent-relay-server 0.121.5 → 0.122.0
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/docs/openapi.json +1 -1
- package/package.json +5 -4
- package/public/assets/{MessageBubble-CLqxCG9b.js → MessageBubble-DRdUD-kW.js} +2 -2
- package/public/assets/{MessageBubble-CLqxCG9b.js.map → MessageBubble-DRdUD-kW.js.map} +1 -1
- package/public/assets/{PlanGraphPanel-BkkToFEb.js → PlanGraphPanel-siKMPCln.js} +2 -2
- package/public/assets/{PlanGraphPanel-BkkToFEb.js.map → PlanGraphPanel-siKMPCln.js.map} +1 -1
- package/public/assets/{activity-D-ocGQGs.js → activity-D3MNcSaV.js} +2 -2
- package/public/assets/{activity-D-ocGQGs.js.map → activity-D3MNcSaV.js.map} +1 -1
- package/public/assets/{agent-profiles-CRPgYTyb.js → agent-profiles-CKzttq6G.js} +2 -2
- package/public/assets/{agent-profiles-CRPgYTyb.js.map → agent-profiles-CKzttq6G.js.map} +1 -1
- package/public/assets/{agent-type-icon-CeEoMouY.js → agent-type-icon-0ZJRP1Vy.js} +2 -2
- package/public/assets/{agent-type-icon-CeEoMouY.js.map → agent-type-icon-0ZJRP1Vy.js.map} +1 -1
- package/public/assets/{agents-Chw5E_2q.js → agents-CuVj_rfG.js} +2 -2
- package/public/assets/{agents-Chw5E_2q.js.map → agents-CuVj_rfG.js.map} +1 -1
- package/public/assets/{analytics-BmNDG0hR.js → analytics-iob6C27d.js} +2 -2
- package/public/assets/{analytics-BmNDG0hR.js.map → analytics-iob6C27d.js.map} +1 -1
- package/public/assets/{automation-D7g90kTv.js → automation-B2ixxs4q.js} +2 -2
- package/public/assets/{automation-D7g90kTv.js.map → automation-B2ixxs4q.js.map} +1 -1
- package/public/assets/{branch-state-badge-Bv7DhLBZ.js → branch-state-badge-CafBJWKo.js} +2 -2
- package/public/assets/{branch-state-badge-Bv7DhLBZ.js.map → branch-state-badge-CafBJWKo.js.map} +1 -1
- package/public/assets/{channels-CLwPeEPi.js → channels-C-WsbL2H.js} +2 -2
- package/public/assets/{channels-CLwPeEPi.js.map → channels-C-WsbL2H.js.map} +1 -1
- package/public/assets/{chat-Cgj7VKzc.js → chat-DeoVTVob.js} +2 -2
- package/public/assets/{chat-Cgj7VKzc.js.map → chat-DeoVTVob.js.map} +1 -1
- package/public/assets/{connectors-Br6fiTny.js → connectors-C2Ac03LJ.js} +2 -2
- package/public/assets/{connectors-Br6fiTny.js.map → connectors-C2Ac03LJ.js.map} +1 -1
- package/public/assets/display-CJzSoTMq.js +3 -0
- package/public/assets/display-CJzSoTMq.js.map +1 -0
- package/public/assets/{formatted-body-CmkuD23M.js → formatted-body-35XBOY5L.js} +3 -3
- package/public/assets/{formatted-body-CmkuD23M.js.map → formatted-body-35XBOY5L.js.map} +1 -1
- package/public/assets/{formatted-body-impl-DbdFmbwW.js → formatted-body-impl-RmDxI5Ux.js} +2 -2
- package/public/assets/{formatted-body-impl-DbdFmbwW.js.map → formatted-body-impl-RmDxI5Ux.js.map} +1 -1
- package/public/assets/index-D0xMQQb_.js +25 -0
- package/public/assets/{index-D9OogaB5.js.map → index-D0xMQQb_.js.map} +1 -1
- package/public/assets/{insights-qvaPqWCV.js → insights-B643SFvm.js} +2 -2
- package/public/assets/{insights-qvaPqWCV.js.map → insights-B643SFvm.js.map} +1 -1
- package/public/assets/{integrations-DYEGSqq_.js → integrations-hFIHeCF4.js} +2 -2
- package/public/assets/{integrations-DYEGSqq_.js.map → integrations-hFIHeCF4.js.map} +1 -1
- package/public/assets/{maintenance-C0HIEB-J.js → maintenance-CURvdp_Z.js} +2 -2
- package/public/assets/{maintenance-C0HIEB-J.js.map → maintenance-CURvdp_Z.js.map} +1 -1
- package/public/assets/{managed-agents-DdS7aI38.js → managed-agents-DrZtdlSn.js} +2 -2
- package/public/assets/{managed-agents-DdS7aI38.js.map → managed-agents-DrZtdlSn.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-BnXMH1z1.js → markdown-preview-impl-CVDO--ek.js} +2 -2
- package/public/assets/{markdown-preview-impl-BnXMH1z1.js.map → markdown-preview-impl-CVDO--ek.js.map} +1 -1
- package/public/assets/{memory-CSwx7bz8.js → memory-Bm7dt_Qf.js} +2 -2
- package/public/assets/{memory-CSwx7bz8.js.map → memory-Bm7dt_Qf.js.map} +1 -1
- package/public/assets/{messages-Be9CmvDv.js → messages-DTq4i2KZ.js} +2 -2
- package/public/assets/{messages-Be9CmvDv.js.map → messages-DTq4i2KZ.js.map} +1 -1
- package/public/assets/{orchestrators-BgtvvHvo.js → orchestrators-Dra318Ap.js} +2 -2
- package/public/assets/{orchestrators-BgtvvHvo.js.map → orchestrators-Dra318Ap.js.map} +1 -1
- package/public/assets/{overview-BvhbPWtF.js → overview-yr1Vmk64.js} +2 -2
- package/public/assets/{overview-BvhbPWtF.js.map → overview-yr1Vmk64.js.map} +1 -1
- package/public/assets/{pairs-CpXSOMx0.js → pairs-mnlKDBGp.js} +2 -2
- package/public/assets/{pairs-CpXSOMx0.js.map → pairs-mnlKDBGp.js.map} +1 -1
- package/public/assets/projects-Dzdf-gkK.js +2 -0
- package/public/assets/{projects-DdPNPFJI.js.map → projects-Dzdf-gkK.js.map} +1 -1
- package/public/assets/prompt-settings-ByL8SNCi.js +2 -0
- package/public/assets/{prompt-settings-BpZse7-X.js.map → prompt-settings-ByL8SNCi.js.map} +1 -1
- package/public/assets/{registry-pb1gTZEg.js → registry-CXWyOtpr.js} +2 -2
- package/public/assets/{registry-pb1gTZEg.js.map → registry-CXWyOtpr.js.map} +1 -1
- package/public/assets/{security-BgcX1n9D.js → security-0SBsc_4W.js} +2 -2
- package/public/assets/{security-BgcX1n9D.js.map → security-0SBsc_4W.js.map} +1 -1
- package/public/assets/{select-DZPVpKPv.js → select-Bw5z2KJ_.js} +2 -2
- package/public/assets/{select-DZPVpKPv.js.map → select-Bw5z2KJ_.js.map} +1 -1
- package/public/assets/{settings-hd5kzdRB.js → settings-78Cyq_tA.js} +4 -4
- package/public/assets/{settings-hd5kzdRB.js.map → settings-78Cyq_tA.js.map} +1 -1
- package/public/assets/{store-DKTLubBT.js → store-BPrVuKiv.js} +4 -4
- package/public/assets/store-BPrVuKiv.js.map +1 -0
- package/public/assets/{tasks-C5d2LU5E.js → tasks-Cg2MMeri.js} +2 -2
- package/public/assets/{tasks-C5d2LU5E.js.map → tasks-Cg2MMeri.js.map} +1 -1
- package/public/assets/teams-DWv6UwFV.js +2 -0
- package/public/assets/{teams-B8f2pGJe.js.map → teams-DWv6UwFV.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-CxAscH0U.js → terminal-viewer-impl-DBLDjbIg.js} +2 -2
- package/public/assets/{terminal-viewer-impl-CxAscH0U.js.map → terminal-viewer-impl-DBLDjbIg.js.map} +1 -1
- package/public/assets/{types-uVOXgvMT.js → types-BeJUSfDj.js} +2 -2
- package/public/assets/types-BeJUSfDj.js.map +1 -0
- package/public/assets/{user-avatar-Dw6mzWhv.js → user-avatar-BsOLyVJj.js} +2 -2
- package/public/assets/{user-avatar-Dw6mzWhv.js.map → user-avatar-BsOLyVJj.js.map} +1 -1
- package/public/assets/{work-queue-9imc7aNK.js → work-queue-DmwvnF6F.js} +2 -2
- package/public/assets/{work-queue-9imc7aNK.js.map → work-queue-DmwvnF6F.js.map} +1 -1
- package/public/assets/{workspaces-CLYc3yF3.js → workspaces-dIBivJN8.js} +2 -2
- package/public/assets/{workspaces-CLYc3yF3.js.map → workspaces-dIBivJN8.js.map} +1 -1
- package/public/index.html +6 -6
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/plugins/claude/monitors/relay-monitor.provisioned.mjs +44 -938
- package/runner/src/adapter.ts +53 -18
- package/runner/src/config.ts +1 -4
- package/src/agent-card-projection.ts +5 -1
- package/src/agent-command-activity.ts +39 -0
- package/src/automations/reconcile.ts +2 -2
- package/src/cli/workspace.ts +3 -1
- package/src/config.ts +10 -1
- package/src/db/agents.ts +118 -37
- package/src/db/message-reads.ts +50 -0
- package/src/db/messages.ts +48 -9
- package/src/db/migrations.ts +54 -0
- package/src/db/provider-quota-burn.ts +96 -67
- package/src/db/provider-quotas.ts +29 -7
- package/src/db/scheduled-timers.ts +21 -0
- package/src/db/schema.ts +1 -0
- package/src/db/tasks.ts +33 -3
- package/src/db/work-evidence.ts +10 -7
- package/src/event-loop-lag.ts +15 -0
- package/src/gate-resolver.ts +24 -1
- package/src/maintenance/jobs.ts +23 -6
- package/src/managed-policy.ts +5 -12
- package/src/mcp-plan-tools.ts +13 -1
- package/src/project-instructions.ts +36 -2
- package/src/quota-advisory.ts +6 -4
- package/src/quota-band-transition.ts +23 -14
- package/src/routes/agent-sessions.ts +39 -7
- package/src/routes/agents.ts +3 -2
- package/src/routes/tasks.ts +16 -4
- package/src/routes/workspaces.ts +35 -10
- package/src/services/plan-graph.ts +27 -9
- package/src/services/register-agent.ts +12 -12
- package/src/services/send-message.ts +43 -15
- package/src/services/spawn-agent.ts +3 -0
- package/src/spawn-command.ts +8 -0
- package/src/spawn-targets.ts +19 -34
- package/src/sse.ts +25 -3
- package/src/workspace-merge.ts +25 -5
- package/src/workspace-orphans.ts +13 -6
- package/public/assets/display-exQDWum3.js +0 -3
- package/public/assets/display-exQDWum3.js.map +0 -1
- package/public/assets/index-D9OogaB5.js +0 -25
- package/public/assets/projects-DdPNPFJI.js +0 -2
- package/public/assets/prompt-settings-BpZse7-X.js +0 -2
- package/public/assets/store-DKTLubBT.js.map +0 -1
- package/public/assets/teams-B8f2pGJe.js +0 -2
- package/public/assets/types-uVOXgvMT.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projects-DdPNPFJI.js","names":[],"sources":["../../dashboard/src/components/views/projects.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { errMessage } from 'agent-relay-sdk/types'\nimport { api } from '@/lib/api'\nimport { useRelayStore } from '@/store'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { CopyButton } from '@/components/shared/copy-button'\nimport { timeAgo } from '@/lib/display'\nimport { Boxes, FileText, FolderGit2, Layers, RefreshCw } from 'lucide-react'\nimport type { Project, ProjectEntryKind, ProjectRoot, ResolvedProjectEntry } from '@/types'\n\n// Projects (#488) — the human's window into the durable Projects scope (epic #403/#405).\n// Read-only: writes go through MCP relay_project_*. Pick a project, see its roots, its\n// entries grouped by kind, what it inherits from umbrella projects, and any ingested\n// CLAUDE.md. Templated on the insights/memory view conventions (zinc/Tailwind, lazy-loaded).\n\ninterface ProjectDetail {\n project: Project\n roots: ProjectRoot[]\n}\n\n// Display order + labels for the entry kinds. Rules first (they're the binding ones),\n// then decisions, facts, and project-native memory.\nconst KIND_ORDER: ProjectEntryKind[] = ['rule', 'decision', 'fact', 'memory']\nconst KIND_LABELS: Record<ProjectEntryKind, string> = {\n rule: 'Rules',\n decision: 'Decisions',\n fact: 'Facts',\n memory: 'Memory',\n}\n\nfunction sourceClass(source: ResolvedProjectEntry['source']): string {\n if (source === 'ingested') return 'bg-blue-500/10 text-blue-400 border-blue-500/30'\n if (source === 'promoted') return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30'\n return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/30'\n}\n\nconst BODY_CLAMP = 320\n\nfunction EntryRow({ entry, ownerName, now }: { entry: ResolvedProjectEntry; ownerName?: string; now: number }) {\n const [expanded, setExpanded] = useState(false)\n const body = entry.body ?? ''\n const isLong = body.length > BODY_CLAMP\n const shown = expanded || !isLong ? body : body.slice(0, BODY_CLAMP).trimEnd() + '…'\n\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium\">{entry.title}</span>\n <Badge variant=\"outline\" className={`text-[10px] ${sourceClass(entry.source)}`}>{entry.source}</Badge>\n {entry.inherited && (\n <Badge variant=\"outline\" className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-[10px] text-amber-400\">\n <Layers className=\"h-3 w-3\" />\n inherited{ownerName ? ` · ${ownerName}` : ''}\n </Badge>\n )}\n <span className=\"ml-auto text-[10px] text-muted-foreground\">{timeAgo(now, entry.updatedAt)}</span>\n </div>\n {body && (\n <div className=\"mt-2\">\n <pre className=\"whitespace-pre-wrap break-words font-mono text-xs text-muted-foreground\">{shown}</pre>\n {isLong && (\n <button\n type=\"button\"\n className=\"mt-1 text-[11px] font-medium text-primary hover:underline\"\n onClick={() => setExpanded((v) => !v)}\n >\n {expanded ? 'Show less' : 'Show more'}\n </button>\n )}\n </div>\n )}\n {entry.tags.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {entry.tags.map((tag) => (\n <Badge key={tag} variant=\"secondary\" className=\"h-4 px-1 text-[9px]\">{tag}</Badge>\n ))}\n </div>\n )}\n </div>\n )\n}\n\nexport function ProjectsView() {\n const projectFocusId = useRelayStore((s) => s.projectFocusId)\n const consumeProjectFocus = useRelayStore((s) => s.consumeProjectFocus)\n\n const [projects, setProjects] = useState<Project[]>([])\n const [selectedId, setSelectedId] = useState<string>('')\n const [includeArchived, setIncludeArchived] = useState(false)\n const [detail, setDetail] = useState<ProjectDetail | null>(null)\n const [entries, setEntries] = useState<ResolvedProjectEntry[]>([])\n const [loadingList, setLoadingList] = useState(true)\n const [loadingDetail, setLoadingDetail] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [now, setNow] = useState(() => Date.now())\n\n async function refreshList() {\n setError(null)\n try {\n const res = await api<{ projects: Project[] }>('GET', `/projects${includeArchived ? '?includeArchived=true' : ''}`)\n setProjects(res.projects)\n setNow(Date.now())\n // Selection precedence: a pending deep-link focus → keep current if still present → first.\n const focus = projectFocusId\n const has = (ref: string) => res.projects.some((p) => p.id === ref || p.slug === ref)\n if (focus && has(focus)) { setSelectedId(focus); consumeProjectFocus() }\n else setSelectedId((cur) => (cur && has(cur) ? cur : res.projects[0]?.id ?? ''))\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setLoadingList(false)\n }\n }\n\n // Mount + archived-toggle: sync the project list from the relay (external data source).\n useEffect(() => {\n void refreshList()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [includeArchived])\n\n // A deep-link focus that arrives while this view is already mounted: select it.\n useEffect(() => {\n if (projectFocusId && projects.some((p) => p.id === projectFocusId || p.slug === projectFocusId)) {\n setSelectedId(projectFocusId)\n consumeProjectFocus()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [projectFocusId])\n\n // Selected project changed: pull its roots + resolved entries (own + inherited).\n useEffect(() => {\n if (!selectedId) { setDetail(null); setEntries([]); return }\n let cancelled = false\n setLoadingDetail(true)\n Promise.all([\n api<ProjectDetail>('GET', `/projects/${encodeURIComponent(selectedId)}`),\n api<{ entries: ResolvedProjectEntry[] }>('GET', `/projects/${encodeURIComponent(selectedId)}/entries?includeInherited=true`),\n ])\n .then(([d, e]) => {\n if (cancelled) return\n setDetail(d)\n setEntries(e.entries)\n setNow(Date.now())\n })\n .catch((e) => { if (!cancelled) setError(errMessage(e)) })\n .finally(() => { if (!cancelled) setLoadingDetail(false) })\n return () => { cancelled = true }\n }, [selectedId])\n\n // projectId → display name, so an inherited entry can name the umbrella it came from.\n const nameById = useMemo(() => {\n const map = new Map<string, string>()\n for (const p of projects) map.set(p.id, p.title || p.slug)\n return map\n }, [projects])\n\n const ingested = useMemo(() => entries.filter((e) => e.source === 'ingested'), [entries])\n\n // Group the non-ingested entries by kind; ingested CLAUDE.md gets its own card above,\n // so it's excluded here to avoid listing the same entry twice.\n const byKind = useMemo(() => {\n const groups = new Map<ProjectEntryKind, ResolvedProjectEntry[]>()\n for (const entry of entries) {\n if (entry.source === 'ingested') continue\n const list = groups.get(entry.kind) ?? []\n list.push(entry)\n groups.set(entry.kind, list)\n }\n return groups\n }, [entries])\n const ownCount = entries.filter((e) => !e.inherited).length\n const inheritedCount = entries.length - ownCount\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Boxes className=\"size-5 text-primary\" />\n <h1 className=\"text-lg font-semibold\">Projects</h1>\n <span className=\"text-xs text-muted-foreground\">durable knowledge scope · epic #403</span>\n\n <select\n className=\"ml-auto h-8 rounded-md border border-border bg-card px-2 text-sm text-foreground\"\n value={selectedId}\n onChange={(e) => setSelectedId(e.target.value)}\n disabled={projects.length === 0}\n >\n {projects.length === 0 && <option value=\"\">No projects</option>}\n {projects.map((p) => (\n <option key={p.id} value={p.id}>\n {(p.title || p.slug)}{p.status === 'archived' ? ' (archived)' : ''}\n </option>\n ))}\n </select>\n\n <label className=\"flex items-center gap-1.5 text-xs text-muted-foreground select-none\">\n <input type=\"checkbox\" className=\"rounded\" checked={includeArchived} onChange={(e) => setIncludeArchived(e.target.checked)} />\n Archived\n </label>\n\n <Button variant=\"outline\" size=\"sm\" onClick={() => void refreshList()}>\n <RefreshCw className=\"size-4\" /> Refresh\n </Button>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error}</div>\n )}\n\n {!loadingList && projects.length === 0 && (\n <Card>\n <CardContent className=\"py-10 text-center text-sm text-muted-foreground\">\n No projects yet. Projects are created through the relay <code className=\"font-mono text-xs\">relay_project_*</code> MCP tools\n (or bootstrapped from a root's CLAUDE.md).\n </CardContent>\n </Card>\n )}\n\n {detail && (\n <>\n {/* Project header */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center gap-2\">\n <span>{detail.project.title || detail.project.slug}</span>\n <Badge variant=\"outline\" className=\"font-mono text-[10px]\">{detail.project.slug}</Badge>\n <Badge variant={detail.project.status === 'active' ? 'secondary' : 'outline'} className=\"text-[10px]\">\n {detail.project.status}\n </Badge>\n <span className=\"ml-auto text-xs font-normal text-muted-foreground\">\n {ownCount} own · {inheritedCount} inherited · updated {timeAgo(now, detail.project.updatedAt)}\n </span>\n </CardTitle>\n </CardHeader>\n {(detail.project.description || detail.project.issueRepo) && (\n <CardContent className=\"space-y-2 pt-0 text-sm text-muted-foreground\">\n {detail.project.description && <div>{detail.project.description}</div>}\n {detail.project.issueRepo && (\n <a\n href={`https://github.com/${detail.project.issueRepo}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1.5 text-primary hover:underline\"\n >\n <FolderGit2 className=\"size-3.5\" />\n <span className=\"font-mono text-xs\">{detail.project.issueRepo}</span>\n </a>\n )}\n </CardContent>\n )}\n </Card>\n\n {/* Roots */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <FolderGit2 className=\"size-4 text-muted-foreground\" /> Roots\n <span className=\"text-xs font-normal text-muted-foreground\">({detail.roots.length})</span>\n </CardTitle>\n </CardHeader>\n <CardContent>\n {detail.roots.length === 0 ? (\n <div className=\"text-sm text-muted-foreground\">No roots bound. A project resolves to agents by the repo paths registered here.</div>\n ) : (\n <div className=\"space-y-1.5\">\n {detail.roots.map((root) => (\n <div key={root.rootPath} className=\"flex items-center gap-2 rounded border border-border bg-card/60 px-2 py-1\">\n <span className=\"flex-1 truncate font-mono text-xs text-muted-foreground\" title={root.rootPath}>{root.rootPath}</span>\n <CopyButton value={root.rootPath} label=\"Copy root path\" size=\"icon\" className=\"h-6 w-6\" iconClassName=\"h-3 w-3\" />\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Ingested CLAUDE.md (called out separately — it's the bootstrapped host context) */}\n {ingested.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <FileText className=\"size-4 text-blue-400\" /> Ingested CLAUDE.md\n <span className=\"text-xs font-normal text-muted-foreground\">({ingested.length})</span>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {ingested.map((entry) => (\n <EntryRow key={entry.id} entry={entry} ownerName={entry.inherited ? nameById.get(entry.projectId) : undefined} now={now} />\n ))}\n </CardContent>\n </Card>\n )}\n\n {/* Entries grouped by kind */}\n {loadingDetail && entries.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">Loading entries…</div>\n ) : entries.length === 0 ? (\n <Card>\n <CardContent className=\"py-8 text-center text-sm text-muted-foreground\">\n No entries yet. Agents write durable rules, decisions, facts, and memory here via the relay MCP tools.\n </CardContent>\n </Card>\n ) : (\n KIND_ORDER.filter((kind) => byKind.has(kind)).map((kind) => {\n const list = byKind.get(kind)!\n return (\n <Card key={kind}>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n {KIND_LABELS[kind]}\n <span className=\"text-xs font-normal text-muted-foreground\">({list.length})</span>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {list.map((entry) => (\n <EntryRow key={entry.id} entry={entry} ownerName={entry.inherited ? nameById.get(entry.projectId) : undefined} now={now} />\n ))}\n </CardContent>\n </Card>\n )\n })\n )}\n </>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"projects-Dzdf-gkK.js","names":[],"sources":["../../dashboard/src/components/views/projects.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { errMessage } from 'agent-relay-sdk/types'\nimport { api } from '@/lib/api'\nimport { useRelayStore } from '@/store'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { CopyButton } from '@/components/shared/copy-button'\nimport { timeAgo } from '@/lib/display'\nimport { Boxes, FileText, FolderGit2, Layers, RefreshCw } from 'lucide-react'\nimport type { Project, ProjectEntryKind, ProjectRoot, ResolvedProjectEntry } from '@/types'\n\n// Projects (#488) — the human's window into the durable Projects scope (epic #403/#405).\n// Read-only: writes go through MCP relay_project_*. Pick a project, see its roots, its\n// entries grouped by kind, what it inherits from umbrella projects, and any ingested\n// CLAUDE.md. Templated on the insights/memory view conventions (zinc/Tailwind, lazy-loaded).\n\ninterface ProjectDetail {\n project: Project\n roots: ProjectRoot[]\n}\n\n// Display order + labels for the entry kinds. Rules first (they're the binding ones),\n// then decisions, facts, and project-native memory.\nconst KIND_ORDER: ProjectEntryKind[] = ['rule', 'decision', 'fact', 'memory']\nconst KIND_LABELS: Record<ProjectEntryKind, string> = {\n rule: 'Rules',\n decision: 'Decisions',\n fact: 'Facts',\n memory: 'Memory',\n}\n\nfunction sourceClass(source: ResolvedProjectEntry['source']): string {\n if (source === 'ingested') return 'bg-blue-500/10 text-blue-400 border-blue-500/30'\n if (source === 'promoted') return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30'\n return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/30'\n}\n\nconst BODY_CLAMP = 320\n\nfunction EntryRow({ entry, ownerName, now }: { entry: ResolvedProjectEntry; ownerName?: string; now: number }) {\n const [expanded, setExpanded] = useState(false)\n const body = entry.body ?? ''\n const isLong = body.length > BODY_CLAMP\n const shown = expanded || !isLong ? body : body.slice(0, BODY_CLAMP).trimEnd() + '…'\n\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium\">{entry.title}</span>\n <Badge variant=\"outline\" className={`text-[10px] ${sourceClass(entry.source)}`}>{entry.source}</Badge>\n {entry.inherited && (\n <Badge variant=\"outline\" className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-[10px] text-amber-400\">\n <Layers className=\"h-3 w-3\" />\n inherited{ownerName ? ` · ${ownerName}` : ''}\n </Badge>\n )}\n <span className=\"ml-auto text-[10px] text-muted-foreground\">{timeAgo(now, entry.updatedAt)}</span>\n </div>\n {body && (\n <div className=\"mt-2\">\n <pre className=\"whitespace-pre-wrap break-words font-mono text-xs text-muted-foreground\">{shown}</pre>\n {isLong && (\n <button\n type=\"button\"\n className=\"mt-1 text-[11px] font-medium text-primary hover:underline\"\n onClick={() => setExpanded((v) => !v)}\n >\n {expanded ? 'Show less' : 'Show more'}\n </button>\n )}\n </div>\n )}\n {entry.tags.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {entry.tags.map((tag) => (\n <Badge key={tag} variant=\"secondary\" className=\"h-4 px-1 text-[9px]\">{tag}</Badge>\n ))}\n </div>\n )}\n </div>\n )\n}\n\nexport function ProjectsView() {\n const projectFocusId = useRelayStore((s) => s.projectFocusId)\n const consumeProjectFocus = useRelayStore((s) => s.consumeProjectFocus)\n\n const [projects, setProjects] = useState<Project[]>([])\n const [selectedId, setSelectedId] = useState<string>('')\n const [includeArchived, setIncludeArchived] = useState(false)\n const [detail, setDetail] = useState<ProjectDetail | null>(null)\n const [entries, setEntries] = useState<ResolvedProjectEntry[]>([])\n const [loadingList, setLoadingList] = useState(true)\n const [loadingDetail, setLoadingDetail] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [now, setNow] = useState(() => Date.now())\n\n async function refreshList() {\n setError(null)\n try {\n const res = await api<{ projects: Project[] }>('GET', `/projects${includeArchived ? '?includeArchived=true' : ''}`)\n setProjects(res.projects)\n setNow(Date.now())\n // Selection precedence: a pending deep-link focus → keep current if still present → first.\n const focus = projectFocusId\n const has = (ref: string) => res.projects.some((p) => p.id === ref || p.slug === ref)\n if (focus && has(focus)) { setSelectedId(focus); consumeProjectFocus() }\n else setSelectedId((cur) => (cur && has(cur) ? cur : res.projects[0]?.id ?? ''))\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setLoadingList(false)\n }\n }\n\n // Mount + archived-toggle: sync the project list from the relay (external data source).\n useEffect(() => {\n void refreshList()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [includeArchived])\n\n // A deep-link focus that arrives while this view is already mounted: select it.\n useEffect(() => {\n if (projectFocusId && projects.some((p) => p.id === projectFocusId || p.slug === projectFocusId)) {\n setSelectedId(projectFocusId)\n consumeProjectFocus()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [projectFocusId])\n\n // Selected project changed: pull its roots + resolved entries (own + inherited).\n useEffect(() => {\n if (!selectedId) { setDetail(null); setEntries([]); return }\n let cancelled = false\n setLoadingDetail(true)\n Promise.all([\n api<ProjectDetail>('GET', `/projects/${encodeURIComponent(selectedId)}`),\n api<{ entries: ResolvedProjectEntry[] }>('GET', `/projects/${encodeURIComponent(selectedId)}/entries?includeInherited=true`),\n ])\n .then(([d, e]) => {\n if (cancelled) return\n setDetail(d)\n setEntries(e.entries)\n setNow(Date.now())\n })\n .catch((e) => { if (!cancelled) setError(errMessage(e)) })\n .finally(() => { if (!cancelled) setLoadingDetail(false) })\n return () => { cancelled = true }\n }, [selectedId])\n\n // projectId → display name, so an inherited entry can name the umbrella it came from.\n const nameById = useMemo(() => {\n const map = new Map<string, string>()\n for (const p of projects) map.set(p.id, p.title || p.slug)\n return map\n }, [projects])\n\n const ingested = useMemo(() => entries.filter((e) => e.source === 'ingested'), [entries])\n\n // Group the non-ingested entries by kind; ingested CLAUDE.md gets its own card above,\n // so it's excluded here to avoid listing the same entry twice.\n const byKind = useMemo(() => {\n const groups = new Map<ProjectEntryKind, ResolvedProjectEntry[]>()\n for (const entry of entries) {\n if (entry.source === 'ingested') continue\n const list = groups.get(entry.kind) ?? []\n list.push(entry)\n groups.set(entry.kind, list)\n }\n return groups\n }, [entries])\n const ownCount = entries.filter((e) => !e.inherited).length\n const inheritedCount = entries.length - ownCount\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Boxes className=\"size-5 text-primary\" />\n <h1 className=\"text-lg font-semibold\">Projects</h1>\n <span className=\"text-xs text-muted-foreground\">durable knowledge scope · epic #403</span>\n\n <select\n className=\"ml-auto h-8 rounded-md border border-border bg-card px-2 text-sm text-foreground\"\n value={selectedId}\n onChange={(e) => setSelectedId(e.target.value)}\n disabled={projects.length === 0}\n >\n {projects.length === 0 && <option value=\"\">No projects</option>}\n {projects.map((p) => (\n <option key={p.id} value={p.id}>\n {(p.title || p.slug)}{p.status === 'archived' ? ' (archived)' : ''}\n </option>\n ))}\n </select>\n\n <label className=\"flex items-center gap-1.5 text-xs text-muted-foreground select-none\">\n <input type=\"checkbox\" className=\"rounded\" checked={includeArchived} onChange={(e) => setIncludeArchived(e.target.checked)} />\n Archived\n </label>\n\n <Button variant=\"outline\" size=\"sm\" onClick={() => void refreshList()}>\n <RefreshCw className=\"size-4\" /> Refresh\n </Button>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error}</div>\n )}\n\n {!loadingList && projects.length === 0 && (\n <Card>\n <CardContent className=\"py-10 text-center text-sm text-muted-foreground\">\n No projects yet. Projects are created through the relay <code className=\"font-mono text-xs\">relay_project_*</code> MCP tools\n (or bootstrapped from a root's CLAUDE.md).\n </CardContent>\n </Card>\n )}\n\n {detail && (\n <>\n {/* Project header */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center gap-2\">\n <span>{detail.project.title || detail.project.slug}</span>\n <Badge variant=\"outline\" className=\"font-mono text-[10px]\">{detail.project.slug}</Badge>\n <Badge variant={detail.project.status === 'active' ? 'secondary' : 'outline'} className=\"text-[10px]\">\n {detail.project.status}\n </Badge>\n <span className=\"ml-auto text-xs font-normal text-muted-foreground\">\n {ownCount} own · {inheritedCount} inherited · updated {timeAgo(now, detail.project.updatedAt)}\n </span>\n </CardTitle>\n </CardHeader>\n {(detail.project.description || detail.project.issueRepo) && (\n <CardContent className=\"space-y-2 pt-0 text-sm text-muted-foreground\">\n {detail.project.description && <div>{detail.project.description}</div>}\n {detail.project.issueRepo && (\n <a\n href={`https://github.com/${detail.project.issueRepo}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1.5 text-primary hover:underline\"\n >\n <FolderGit2 className=\"size-3.5\" />\n <span className=\"font-mono text-xs\">{detail.project.issueRepo}</span>\n </a>\n )}\n </CardContent>\n )}\n </Card>\n\n {/* Roots */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <FolderGit2 className=\"size-4 text-muted-foreground\" /> Roots\n <span className=\"text-xs font-normal text-muted-foreground\">({detail.roots.length})</span>\n </CardTitle>\n </CardHeader>\n <CardContent>\n {detail.roots.length === 0 ? (\n <div className=\"text-sm text-muted-foreground\">No roots bound. A project resolves to agents by the repo paths registered here.</div>\n ) : (\n <div className=\"space-y-1.5\">\n {detail.roots.map((root) => (\n <div key={root.rootPath} className=\"flex items-center gap-2 rounded border border-border bg-card/60 px-2 py-1\">\n <span className=\"flex-1 truncate font-mono text-xs text-muted-foreground\" title={root.rootPath}>{root.rootPath}</span>\n <CopyButton value={root.rootPath} label=\"Copy root path\" size=\"icon\" className=\"h-6 w-6\" iconClassName=\"h-3 w-3\" />\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Ingested CLAUDE.md (called out separately — it's the bootstrapped host context) */}\n {ingested.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <FileText className=\"size-4 text-blue-400\" /> Ingested CLAUDE.md\n <span className=\"text-xs font-normal text-muted-foreground\">({ingested.length})</span>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {ingested.map((entry) => (\n <EntryRow key={entry.id} entry={entry} ownerName={entry.inherited ? nameById.get(entry.projectId) : undefined} now={now} />\n ))}\n </CardContent>\n </Card>\n )}\n\n {/* Entries grouped by kind */}\n {loadingDetail && entries.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">Loading entries…</div>\n ) : entries.length === 0 ? (\n <Card>\n <CardContent className=\"py-8 text-center text-sm text-muted-foreground\">\n No entries yet. Agents write durable rules, decisions, facts, and memory here via the relay MCP tools.\n </CardContent>\n </Card>\n ) : (\n KIND_ORDER.filter((kind) => byKind.has(kind)).map((kind) => {\n const list = byKind.get(kind)!\n return (\n <Card key={kind}>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n {KIND_LABELS[kind]}\n <span className=\"text-xs font-normal text-muted-foreground\">({list.length})</span>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {list.map((entry) => (\n <EntryRow key={entry.id} entry={entry} ownerName={entry.inherited ? nameById.get(entry.projectId) : undefined} now={now} />\n ))}\n </CardContent>\n </Card>\n )\n })\n )}\n </>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{Fn as t,L as n,P as r,Ut as i,bn as a,er as o,hr as s,j as c,nr as l,rr as u}from"./lucide-react-DuhlF2Sj.js";import{d}from"./types-BeJUSfDj.js";import{K as f}from"./display-CJzSoTMq.js";import{t as p}from"./badge-DCIT3Irj.js";import{t as m}from"./button-CzdTkRIf.js";import{W as h}from"./index-D0xMQQb_.js";import{a as g,i as _,o as v,r as y,s as ee,t as b}from"./card-C3BGuVC6.js";var x=e(s(),1),S=l(),C=`all`;function w(e){return e.split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}function T(e){return Array.isArray(e)?[...e].sort((e,t)=>{let n=e.category.localeCompare(t.category);if(n!==0)return n;let r=e.title.localeCompare(t.title);return r===0?e.slug.localeCompare(t.slug):r}):[]}function E(){let[e,s]=(0,x.useState)([]),[l,E]=(0,x.useState)(!0),[D,O]=(0,x.useState)(``),[k,A]=(0,x.useState)(``),[j,M]=(0,x.useState)(``),[N,P]=(0,x.useState)(C),[F,I]=(0,x.useState)(``),[L,R]=(0,x.useState)({}),[z,B]=(0,x.useState)(``),[V,H]=(0,x.useState)(()=>Date.now());async function U(){E(!0),O(``);try{s(T((await u(`GET`,`/settings/prompts`)).templates)),H(Date.now())}catch(e){O(d(e))}finally{E(!1)}}(0,x.useEffect)(()=>{U()},[]);let W=(0,x.useMemo)(()=>{let t=new Map;for(let n of e)t.set(n.category,(t.get(n.category)??0)+1);return[...t.entries()].sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>({key:e,count:t,label:w(e)}))},[e]),G=N===C||W.some(e=>e.key===N)?N:C,K=(0,x.useMemo)(()=>G===C?e:e.filter(e=>e.category===G),[G,e]),q=K.some(e=>e.slug===F)?F:K[0]?.slug??``,J=K.find(e=>e.slug===q)??null,Y=J?L[J.slug]??J.template:``,X=J?Y!==J.template:!1,Z=e.filter(e=>e.template!==e.defaultTemplate).length,Q=J?z===J.slug:!1;function te(e){I(e),A(``),M(``)}function $(e){s(t=>T(t.map(t=>t.slug===e.slug?e:t))),R(t=>({...t,[e.slug]:e.template})),H(Date.now())}async function ne(){if(!(!J||!X)){B(J.slug),A(``),M(``);try{let e=await u(`PUT`,`/settings/prompts/${encodeURIComponent(J.slug)}`,{template:Y});$(e),M(`Saved ${e.title}.`)}catch(e){A(d(e))}finally{B(``)}}}async function re(){if(!(!J||J.template===J.defaultTemplate)){B(J.slug),A(``),M(``);try{let e=await u(`POST`,`/settings/prompts/${encodeURIComponent(J.slug)}/reset`);$(e),M(`Reset ${e.title} to the built-in default.`)}catch(e){A(d(e))}finally{B(``)}}}function ie(){J&&(R(e=>({...e,[J.slug]:J.template})),A(``),M(`Unsaved edits discarded.`))}return(0,S.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-3`,children:[(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Prompts`}),(0,S.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`View and edit the externalized prompt templates used across Relay lifecycle, routing, workspace, and automation flows.`})]}),(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,S.jsxs)(p,{variant:`outline`,className:`gap-1`,children:[(0,S.jsx)(i,{className:`size-3`}),e.length,` template`,e.length===1?``:`s`]}),(0,S.jsxs)(p,{variant:`outline`,className:o(`gap-1`,Z>0&&`border-amber-500/40 bg-amber-500/10 text-amber-300`),children:[Z,` modified`]}),(0,S.jsxs)(m,{variant:`outline`,size:`sm`,onClick:()=>void U(),disabled:l,children:[(0,S.jsx)(n,{className:o(`size-4`,l&&`animate-spin`)}),`Refresh`]})]})]}),D&&(0,S.jsxs)(`div`,{className:`flex items-start gap-2 rounded-lg border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive`,children:[(0,S.jsx)(a,{className:`mt-0.5 size-4`}),(0,S.jsx)(`span`,{children:D})]}),l&&e.length===0?(0,S.jsx)(b,{size:`sm`,children:(0,S.jsx)(y,{className:`text-sm text-muted-foreground`,children:`Loading prompt templates…`})}):e.length===0?(0,S.jsx)(b,{children:(0,S.jsx)(y,{className:`py-10 text-center text-sm text-muted-foreground`,children:`No prompt templates are available yet.`})}):(0,S.jsxs)(`div`,{className:`grid gap-4 xl:grid-cols-[220px_minmax(0,0.9fr)_minmax(0,1.1fr)]`,children:[(0,S.jsxs)(`nav`,{className:`space-y-1 xl:sticky xl:top-4 xl:self-start`,children:[(0,S.jsxs)(`button`,{type:`button`,className:o(`w-full rounded-md border px-3 py-2 text-left text-sm transition-colors`,G===C?`border-primary/50 bg-primary/10 text-foreground`:`border-transparent hover:border-border hover:bg-accent`),onClick:()=>P(C),children:[(0,S.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(`span`,{className:`font-medium`,children:`All`}),(0,S.jsx)(p,{variant:`outline`,className:`ml-auto h-4 px-1 text-[10px]`,children:e.length})]}),(0,S.jsx)(`span`,{className:`block truncate text-xs text-muted-foreground`,children:`Every externalized prompt template`})]}),W.map(e=>(0,S.jsxs)(`button`,{type:`button`,className:o(`w-full rounded-md border px-3 py-2 text-left text-sm transition-colors`,G===e.key?`border-primary/50 bg-primary/10 text-foreground`:`border-transparent hover:border-border hover:bg-accent`),onClick:()=>P(e.key),children:[(0,S.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(`span`,{className:`font-medium`,children:e.label}),(0,S.jsx)(p,{variant:`outline`,className:`ml-auto h-4 px-1 text-[10px]`,children:e.count})]}),(0,S.jsx)(`span`,{className:`block truncate text-xs font-mono text-muted-foreground`,children:e.key})]},e.key))]}),(0,S.jsxs)(`div`,{className:`min-w-0 space-y-3`,children:[(0,S.jsx)(`div`,{className:`flex items-center justify-between gap-2 px-1`,children:(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`h2`,{className:`text-sm font-semibold`,children:G===C?`All templates`:`${w(G)} templates`}),(0,S.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[K.length,` template`,K.length===1?``:`s`,` in this view`]})]})}),K.map(e=>{let t=e.slug===q,n=e.template!==e.defaultTemplate;return(0,S.jsx)(`button`,{type:`button`,className:o(`w-full rounded-xl border p-4 text-left transition-colors`,t?`border-primary/50 bg-primary/10 ring-1 ring-primary/20`:`border-border bg-card hover:border-primary/30 hover:bg-accent`),onClick:()=>te(e.slug),children:(0,S.jsx)(`div`,{className:`flex flex-wrap items-start gap-2`,children:(0,S.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,S.jsx)(`span`,{className:`font-medium text-foreground`,children:e.title}),n&&(0,S.jsx)(p,{className:`bg-amber-500/15 text-amber-300 hover:bg-amber-500/15`,children:`Modified`}),(0,S.jsx)(p,{variant:`outline`,className:`text-[10px]`,children:w(e.category)})]}),(0,S.jsx)(`div`,{className:`mt-1 font-mono text-[11px] text-muted-foreground`,children:e.slug}),e.description&&(0,S.jsx)(`p`,{className:`mt-2 line-clamp-2 text-sm text-muted-foreground`,children:e.description}),(0,S.jsxs)(`div`,{className:`mt-3 flex flex-wrap items-center gap-3 text-[11px] text-muted-foreground`,children:[(0,S.jsxs)(`span`,{children:[e.variables.length,` variable`,e.variables.length===1?``:`s`]}),(0,S.jsxs)(`span`,{children:[`Updated `,f(V,e.updatedAt)]})]})]})})},e.slug)})]}),(0,S.jsx)(`div`,{className:`min-w-0`,children:J?(0,S.jsxs)(b,{children:[(0,S.jsxs)(v,{className:`border-b border-border`,children:[(0,S.jsxs)(ee,{className:`flex flex-wrap items-center gap-2`,children:[(0,S.jsx)(`span`,{children:J.title}),J.template!==J.defaultTemplate&&(0,S.jsx)(p,{className:`bg-amber-500/15 text-amber-300 hover:bg-amber-500/15`,children:`Customized`}),X&&(0,S.jsx)(p,{variant:`outline`,children:`Unsaved`})]}),(0,S.jsxs)(_,{className:`space-y-1`,children:[(0,S.jsx)(`div`,{className:`font-mono text-[11px]`,children:J.slug}),J.description&&(0,S.jsx)(`div`,{children:J.description})]})]}),(0,S.jsxs)(y,{className:`space-y-4 pt-4`,children:[k&&(0,S.jsxs)(`div`,{className:`flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-2 text-xs text-destructive`,children:[(0,S.jsx)(a,{className:`mt-0.5 size-3.5`}),(0,S.jsx)(`span`,{children:k})]}),j&&(0,S.jsx)(`div`,{className:`rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-2 text-xs text-emerald-300`,children:j}),(0,S.jsxs)(`div`,{className:`space-y-2`,children:[(0,S.jsxs)(`div`,{className:`flex items-center gap-2 text-xs font-medium text-muted-foreground`,children:[(0,S.jsx)(t,{className:`size-3.5`}),`Available variables`]}),J.variables.length>0?(0,S.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:J.variables.map(e=>(0,S.jsx)(p,{variant:`secondary`,className:`font-mono text-[11px]`,children:`{{${e}}}`},e))}):(0,S.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`This template has no placeholder variables.`})]}),J.template!==J.defaultTemplate&&(0,S.jsx)(`div`,{className:`rounded-lg border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs text-amber-200`,children:`This template differs from the built-in default. Reset will restore the original text.`}),(0,S.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,S.jsx)(`label`,{htmlFor:`prompt-template-editor`,className:`text-xs font-medium text-muted-foreground`,children:`Template text`}),(0,S.jsx)(h,{id:`prompt-template-editor`,value:Y,onChange:e=>{let t=e.target.value;R(e=>({...e,[J.slug]:t})),k&&A(``),j&&M(``)},rows:18,spellCheck:!1,className:`min-h-[360px] resize-y font-mono text-xs leading-5`})]})]}),(0,S.jsxs)(g,{className:`flex flex-wrap items-center justify-between gap-2`,children:[(0,S.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:X?`Editing local changes.`:`Updated ${f(V,J.updatedAt)}.`}),(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[J.template!==J.defaultTemplate&&(0,S.jsxs)(m,{variant:`outline`,size:`sm`,onClick:()=>void re(),disabled:Q,children:[(0,S.jsx)(r,{className:`size-4`}),`Reset to default`]}),(0,S.jsx)(m,{variant:`outline`,size:`sm`,onClick:ie,disabled:!X||Q,children:`Cancel`}),(0,S.jsxs)(m,{size:`sm`,onClick:()=>void ne(),disabled:!X||Q,children:[(0,S.jsx)(c,{className:`size-4`}),`Save`]})]})]})]}):(0,S.jsx)(b,{size:`sm`,children:(0,S.jsx)(y,{className:`text-sm text-muted-foreground`,children:`Select a prompt template to edit it.`})})})]})]})}export{E as PromptSettingsView};
|
|
2
|
+
//# sourceMappingURL=prompt-settings-ByL8SNCi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-settings-BpZse7-X.js","names":[],"sources":["../../dashboard/src/components/views/prompt-settings.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { api } from '@/lib/api'\nimport { cn } from '@/lib/utils'\nimport { timeAgo } from '@/lib/display'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Textarea } from '@/components/ui/textarea'\nimport { AlertCircle, Braces, FileText, RefreshCw, RotateCcw, Save } from 'lucide-react'\nimport { errMessage } from 'agent-relay-sdk/types'\n\ninterface PromptTemplate {\n slug: string\n category: string\n title: string\n description?: string\n template: string\n defaultTemplate: string\n variables: string[]\n updatedAt: number\n createdAt: number\n}\n\nconst ALL_CATEGORY = 'all'\n\n\nfunction titleCaseCategory(category: string): string {\n return category\n .split(/[^a-zA-Z0-9]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(' ')\n}\n\nfunction sortTemplates(items: unknown): PromptTemplate[] {\n if (!Array.isArray(items)) return []\n return [...(items as PromptTemplate[])].sort((a, b) => {\n const category = a.category.localeCompare(b.category)\n if (category !== 0) return category\n const title = a.title.localeCompare(b.title)\n if (title !== 0) return title\n return a.slug.localeCompare(b.slug)\n })\n}\n\nexport function PromptSettingsView() {\n const [templates, setTemplates] = useState<PromptTemplate[]>([])\n const [loading, setLoading] = useState(true)\n const [loadError, setLoadError] = useState('')\n const [actionError, setActionError] = useState('')\n const [notice, setNotice] = useState('')\n const [activeCategory, setActiveCategory] = useState(ALL_CATEGORY)\n const [selectedSlug, setSelectedSlug] = useState('')\n const [drafts, setDrafts] = useState<Record<string, string>>({})\n const [savingSlug, setSavingSlug] = useState('')\n const [now, setNow] = useState(() => Date.now())\n\n async function reload() {\n setLoading(true)\n setLoadError('')\n try {\n const result = await api<{ templates: PromptTemplate[] }>('GET', '/settings/prompts')\n setTemplates(sortTemplates(result.templates))\n setNow(Date.now())\n } catch (error) {\n setLoadError(errMessage(error))\n } finally {\n setLoading(false)\n }\n }\n\n useEffect(() => {\n void reload()\n }, [])\n\n const categories = useMemo(() => {\n const counts = new Map<string, number>()\n for (const template of templates) counts.set(template.category, (counts.get(template.category) ?? 0) + 1)\n return [...counts.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, count]) => ({ key, count, label: titleCaseCategory(key) }))\n }, [templates])\n\n const effectiveCategory = activeCategory === ALL_CATEGORY || categories.some((item) => item.key === activeCategory)\n ? activeCategory\n : ALL_CATEGORY\n\n const visibleTemplates = useMemo(() => {\n if (effectiveCategory === ALL_CATEGORY) return templates\n return templates.filter((template) => template.category === effectiveCategory)\n }, [effectiveCategory, templates])\n\n const resolvedSelectedSlug = visibleTemplates.some((template) => template.slug === selectedSlug)\n ? selectedSlug\n : (visibleTemplates[0]?.slug ?? '')\n\n const selectedTemplate = visibleTemplates.find((template) => template.slug === resolvedSelectedSlug) ?? null\n const draft = selectedTemplate ? (drafts[selectedTemplate.slug] ?? selectedTemplate.template) : ''\n const hasUnsavedChanges = selectedTemplate ? draft !== selectedTemplate.template : false\n const modifiedCount = templates.filter((template) => template.template !== template.defaultTemplate).length\n const saving = selectedTemplate ? savingSlug === selectedTemplate.slug : false\n\n function selectTemplate(slug: string) {\n setSelectedSlug(slug)\n setActionError('')\n setNotice('')\n }\n\n function replaceTemplate(updated: PromptTemplate) {\n setTemplates((current) => sortTemplates(current.map((template) => template.slug === updated.slug ? updated : template)))\n setDrafts((current) => ({ ...current, [updated.slug]: updated.template }))\n setNow(Date.now())\n }\n\n async function saveTemplate() {\n if (!selectedTemplate || !hasUnsavedChanges) return\n setSavingSlug(selectedTemplate.slug)\n setActionError('')\n setNotice('')\n try {\n const updated = await api<PromptTemplate>('PUT', `/settings/prompts/${encodeURIComponent(selectedTemplate.slug)}`, {\n template: draft,\n })\n replaceTemplate(updated)\n setNotice(`Saved ${updated.title}.`)\n } catch (error) {\n setActionError(errMessage(error))\n } finally {\n setSavingSlug('')\n }\n }\n\n async function resetTemplate() {\n if (!selectedTemplate || selectedTemplate.template === selectedTemplate.defaultTemplate) return\n setSavingSlug(selectedTemplate.slug)\n setActionError('')\n setNotice('')\n try {\n const updated = await api<PromptTemplate>('POST', `/settings/prompts/${encodeURIComponent(selectedTemplate.slug)}/reset`)\n replaceTemplate(updated)\n setNotice(`Reset ${updated.title} to the built-in default.`)\n } catch (error) {\n setActionError(errMessage(error))\n } finally {\n setSavingSlug('')\n }\n }\n\n function cancelDraft() {\n if (!selectedTemplate) return\n setDrafts((current) => ({ ...current, [selectedTemplate.slug]: selectedTemplate.template }))\n setActionError('')\n setNotice('Unsaved edits discarded.')\n }\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h1 className=\"text-lg font-semibold\">Prompts</h1>\n <p className=\"text-sm text-muted-foreground\">View and edit the externalized prompt templates used across Relay lifecycle, routing, workspace, and automation flows.</p>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className=\"gap-1\">\n <FileText className=\"size-3\" />\n {templates.length} template{templates.length === 1 ? '' : 's'}\n </Badge>\n <Badge variant=\"outline\" className={cn('gap-1', modifiedCount > 0 && 'border-amber-500/40 bg-amber-500/10 text-amber-300')}>\n {modifiedCount} modified\n </Badge>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void reload()} disabled={loading}>\n <RefreshCw className={cn('size-4', loading && 'animate-spin')} />\n Refresh\n </Button>\n </div>\n </div>\n\n {loadError && (\n <div className=\"flex items-start gap-2 rounded-lg border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">\n <AlertCircle className=\"mt-0.5 size-4\" />\n <span>{loadError}</span>\n </div>\n )}\n\n {loading && templates.length === 0 ? (\n <Card size=\"sm\">\n <CardContent className=\"text-sm text-muted-foreground\">Loading prompt templates…</CardContent>\n </Card>\n ) : templates.length === 0 ? (\n <Card>\n <CardContent className=\"py-10 text-center text-sm text-muted-foreground\">\n No prompt templates are available yet.\n </CardContent>\n </Card>\n ) : (\n <div className=\"grid gap-4 xl:grid-cols-[220px_minmax(0,0.9fr)_minmax(0,1.1fr)]\">\n <nav className=\"space-y-1 xl:sticky xl:top-4 xl:self-start\">\n <button\n type=\"button\"\n className={cn(\n 'w-full rounded-md border px-3 py-2 text-left text-sm transition-colors',\n effectiveCategory === ALL_CATEGORY ? 'border-primary/50 bg-primary/10 text-foreground' : 'border-transparent hover:border-border hover:bg-accent',\n )}\n onClick={() => setActiveCategory(ALL_CATEGORY)}\n >\n <span className=\"flex items-center gap-2\">\n <span className=\"font-medium\">All</span>\n <Badge variant=\"outline\" className=\"ml-auto h-4 px-1 text-[10px]\">{templates.length}</Badge>\n </span>\n <span className=\"block truncate text-xs text-muted-foreground\">Every externalized prompt template</span>\n </button>\n\n {categories.map((category) => (\n <button\n key={category.key}\n type=\"button\"\n className={cn(\n 'w-full rounded-md border px-3 py-2 text-left text-sm transition-colors',\n effectiveCategory === category.key ? 'border-primary/50 bg-primary/10 text-foreground' : 'border-transparent hover:border-border hover:bg-accent',\n )}\n onClick={() => setActiveCategory(category.key)}\n >\n <span className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{category.label}</span>\n <Badge variant=\"outline\" className=\"ml-auto h-4 px-1 text-[10px]\">{category.count}</Badge>\n </span>\n <span className=\"block truncate text-xs font-mono text-muted-foreground\">{category.key}</span>\n </button>\n ))}\n </nav>\n\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <div>\n <h2 className=\"text-sm font-semibold\">\n {effectiveCategory === ALL_CATEGORY ? 'All templates' : `${titleCaseCategory(effectiveCategory)} templates`}\n </h2>\n <p className=\"text-xs text-muted-foreground\">{visibleTemplates.length} template{visibleTemplates.length === 1 ? '' : 's'} in this view</p>\n </div>\n </div>\n\n {visibleTemplates.map((template) => {\n const selected = template.slug === resolvedSelectedSlug\n const modified = template.template !== template.defaultTemplate\n return (\n <button\n key={template.slug}\n type=\"button\"\n className={cn(\n 'w-full rounded-xl border p-4 text-left transition-colors',\n selected ? 'border-primary/50 bg-primary/10 ring-1 ring-primary/20' : 'border-border bg-card hover:border-primary/30 hover:bg-accent',\n )}\n onClick={() => selectTemplate(template.slug)}\n >\n <div className=\"flex flex-wrap items-start gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-foreground\">{template.title}</span>\n {modified && (\n <Badge className=\"bg-amber-500/15 text-amber-300 hover:bg-amber-500/15\">Modified</Badge>\n )}\n <Badge variant=\"outline\" className=\"text-[10px]\">{titleCaseCategory(template.category)}</Badge>\n </div>\n <div className=\"mt-1 font-mono text-[11px] text-muted-foreground\">{template.slug}</div>\n {template.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-muted-foreground\">{template.description}</p>\n )}\n <div className=\"mt-3 flex flex-wrap items-center gap-3 text-[11px] text-muted-foreground\">\n <span>{template.variables.length} variable{template.variables.length === 1 ? '' : 's'}</span>\n <span>Updated {timeAgo(now, template.updatedAt)}</span>\n </div>\n </div>\n </div>\n </button>\n )\n })}\n </div>\n\n <div className=\"min-w-0\">\n {selectedTemplate ? (\n <Card>\n <CardHeader className=\"border-b border-border\">\n <CardTitle className=\"flex flex-wrap items-center gap-2\">\n <span>{selectedTemplate.title}</span>\n {selectedTemplate.template !== selectedTemplate.defaultTemplate && (\n <Badge className=\"bg-amber-500/15 text-amber-300 hover:bg-amber-500/15\">Customized</Badge>\n )}\n {hasUnsavedChanges && <Badge variant=\"outline\">Unsaved</Badge>}\n </CardTitle>\n <CardDescription className=\"space-y-1\">\n <div className=\"font-mono text-[11px]\">{selectedTemplate.slug}</div>\n {selectedTemplate.description && <div>{selectedTemplate.description}</div>}\n </CardDescription>\n </CardHeader>\n\n <CardContent className=\"space-y-4 pt-4\">\n {actionError && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-2 text-xs text-destructive\">\n <AlertCircle className=\"mt-0.5 size-3.5\" />\n <span>{actionError}</span>\n </div>\n )}\n\n {notice && (\n <div className=\"rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-2 text-xs text-emerald-300\">\n {notice}\n </div>\n )}\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-muted-foreground\">\n <Braces className=\"size-3.5\" />\n Available variables\n </div>\n {selectedTemplate.variables.length > 0 ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {selectedTemplate.variables.map((variable) => (\n <Badge key={variable} variant=\"secondary\" className=\"font-mono text-[11px]\">\n {`{{${variable}}}`}\n </Badge>\n ))}\n </div>\n ) : (\n <div className=\"text-xs text-muted-foreground\">This template has no placeholder variables.</div>\n )}\n </div>\n\n {selectedTemplate.template !== selectedTemplate.defaultTemplate && (\n <div className=\"rounded-lg border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs text-amber-200\">\n This template differs from the built-in default. Reset will restore the original text.\n </div>\n )}\n\n <div className=\"space-y-1.5\">\n <label htmlFor=\"prompt-template-editor\" className=\"text-xs font-medium text-muted-foreground\">\n Template text\n </label>\n <Textarea\n id=\"prompt-template-editor\"\n value={draft}\n onChange={(event) => {\n const next = event.target.value\n setDrafts((current) => ({ ...current, [selectedTemplate.slug]: next }))\n if (actionError) setActionError('')\n if (notice) setNotice('')\n }}\n rows={18}\n spellCheck={false}\n className=\"min-h-[360px] resize-y font-mono text-xs leading-5\"\n />\n </div>\n </CardContent>\n\n <CardFooter className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"text-xs text-muted-foreground\">\n {hasUnsavedChanges ? 'Editing local changes.' : `Updated ${timeAgo(now, selectedTemplate.updatedAt)}.`}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {selectedTemplate.template !== selectedTemplate.defaultTemplate && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => void resetTemplate()} disabled={saving}>\n <RotateCcw className=\"size-4\" />\n Reset to default\n </Button>\n )}\n <Button variant=\"outline\" size=\"sm\" onClick={cancelDraft} disabled={!hasUnsavedChanges || saving}>\n Cancel\n </Button>\n <Button size=\"sm\" onClick={() => void saveTemplate()} disabled={!hasUnsavedChanges || saving}>\n <Save className=\"size-4\" />\n Save\n </Button>\n </div>\n </CardFooter>\n </Card>\n ) : (\n <Card size=\"sm\">\n <CardContent className=\"text-sm text-muted-foreground\">\n Select a prompt template to edit it.\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"prompt-settings-ByL8SNCi.js","names":[],"sources":["../../dashboard/src/components/views/prompt-settings.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { api } from '@/lib/api'\nimport { cn } from '@/lib/utils'\nimport { timeAgo } from '@/lib/display'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Textarea } from '@/components/ui/textarea'\nimport { AlertCircle, Braces, FileText, RefreshCw, RotateCcw, Save } from 'lucide-react'\nimport { errMessage } from 'agent-relay-sdk/types'\n\ninterface PromptTemplate {\n slug: string\n category: string\n title: string\n description?: string\n template: string\n defaultTemplate: string\n variables: string[]\n updatedAt: number\n createdAt: number\n}\n\nconst ALL_CATEGORY = 'all'\n\n\nfunction titleCaseCategory(category: string): string {\n return category\n .split(/[^a-zA-Z0-9]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(' ')\n}\n\nfunction sortTemplates(items: unknown): PromptTemplate[] {\n if (!Array.isArray(items)) return []\n return [...(items as PromptTemplate[])].sort((a, b) => {\n const category = a.category.localeCompare(b.category)\n if (category !== 0) return category\n const title = a.title.localeCompare(b.title)\n if (title !== 0) return title\n return a.slug.localeCompare(b.slug)\n })\n}\n\nexport function PromptSettingsView() {\n const [templates, setTemplates] = useState<PromptTemplate[]>([])\n const [loading, setLoading] = useState(true)\n const [loadError, setLoadError] = useState('')\n const [actionError, setActionError] = useState('')\n const [notice, setNotice] = useState('')\n const [activeCategory, setActiveCategory] = useState(ALL_CATEGORY)\n const [selectedSlug, setSelectedSlug] = useState('')\n const [drafts, setDrafts] = useState<Record<string, string>>({})\n const [savingSlug, setSavingSlug] = useState('')\n const [now, setNow] = useState(() => Date.now())\n\n async function reload() {\n setLoading(true)\n setLoadError('')\n try {\n const result = await api<{ templates: PromptTemplate[] }>('GET', '/settings/prompts')\n setTemplates(sortTemplates(result.templates))\n setNow(Date.now())\n } catch (error) {\n setLoadError(errMessage(error))\n } finally {\n setLoading(false)\n }\n }\n\n useEffect(() => {\n void reload()\n }, [])\n\n const categories = useMemo(() => {\n const counts = new Map<string, number>()\n for (const template of templates) counts.set(template.category, (counts.get(template.category) ?? 0) + 1)\n return [...counts.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, count]) => ({ key, count, label: titleCaseCategory(key) }))\n }, [templates])\n\n const effectiveCategory = activeCategory === ALL_CATEGORY || categories.some((item) => item.key === activeCategory)\n ? activeCategory\n : ALL_CATEGORY\n\n const visibleTemplates = useMemo(() => {\n if (effectiveCategory === ALL_CATEGORY) return templates\n return templates.filter((template) => template.category === effectiveCategory)\n }, [effectiveCategory, templates])\n\n const resolvedSelectedSlug = visibleTemplates.some((template) => template.slug === selectedSlug)\n ? selectedSlug\n : (visibleTemplates[0]?.slug ?? '')\n\n const selectedTemplate = visibleTemplates.find((template) => template.slug === resolvedSelectedSlug) ?? null\n const draft = selectedTemplate ? (drafts[selectedTemplate.slug] ?? selectedTemplate.template) : ''\n const hasUnsavedChanges = selectedTemplate ? draft !== selectedTemplate.template : false\n const modifiedCount = templates.filter((template) => template.template !== template.defaultTemplate).length\n const saving = selectedTemplate ? savingSlug === selectedTemplate.slug : false\n\n function selectTemplate(slug: string) {\n setSelectedSlug(slug)\n setActionError('')\n setNotice('')\n }\n\n function replaceTemplate(updated: PromptTemplate) {\n setTemplates((current) => sortTemplates(current.map((template) => template.slug === updated.slug ? updated : template)))\n setDrafts((current) => ({ ...current, [updated.slug]: updated.template }))\n setNow(Date.now())\n }\n\n async function saveTemplate() {\n if (!selectedTemplate || !hasUnsavedChanges) return\n setSavingSlug(selectedTemplate.slug)\n setActionError('')\n setNotice('')\n try {\n const updated = await api<PromptTemplate>('PUT', `/settings/prompts/${encodeURIComponent(selectedTemplate.slug)}`, {\n template: draft,\n })\n replaceTemplate(updated)\n setNotice(`Saved ${updated.title}.`)\n } catch (error) {\n setActionError(errMessage(error))\n } finally {\n setSavingSlug('')\n }\n }\n\n async function resetTemplate() {\n if (!selectedTemplate || selectedTemplate.template === selectedTemplate.defaultTemplate) return\n setSavingSlug(selectedTemplate.slug)\n setActionError('')\n setNotice('')\n try {\n const updated = await api<PromptTemplate>('POST', `/settings/prompts/${encodeURIComponent(selectedTemplate.slug)}/reset`)\n replaceTemplate(updated)\n setNotice(`Reset ${updated.title} to the built-in default.`)\n } catch (error) {\n setActionError(errMessage(error))\n } finally {\n setSavingSlug('')\n }\n }\n\n function cancelDraft() {\n if (!selectedTemplate) return\n setDrafts((current) => ({ ...current, [selectedTemplate.slug]: selectedTemplate.template }))\n setActionError('')\n setNotice('Unsaved edits discarded.')\n }\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h1 className=\"text-lg font-semibold\">Prompts</h1>\n <p className=\"text-sm text-muted-foreground\">View and edit the externalized prompt templates used across Relay lifecycle, routing, workspace, and automation flows.</p>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className=\"gap-1\">\n <FileText className=\"size-3\" />\n {templates.length} template{templates.length === 1 ? '' : 's'}\n </Badge>\n <Badge variant=\"outline\" className={cn('gap-1', modifiedCount > 0 && 'border-amber-500/40 bg-amber-500/10 text-amber-300')}>\n {modifiedCount} modified\n </Badge>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void reload()} disabled={loading}>\n <RefreshCw className={cn('size-4', loading && 'animate-spin')} />\n Refresh\n </Button>\n </div>\n </div>\n\n {loadError && (\n <div className=\"flex items-start gap-2 rounded-lg border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">\n <AlertCircle className=\"mt-0.5 size-4\" />\n <span>{loadError}</span>\n </div>\n )}\n\n {loading && templates.length === 0 ? (\n <Card size=\"sm\">\n <CardContent className=\"text-sm text-muted-foreground\">Loading prompt templates…</CardContent>\n </Card>\n ) : templates.length === 0 ? (\n <Card>\n <CardContent className=\"py-10 text-center text-sm text-muted-foreground\">\n No prompt templates are available yet.\n </CardContent>\n </Card>\n ) : (\n <div className=\"grid gap-4 xl:grid-cols-[220px_minmax(0,0.9fr)_minmax(0,1.1fr)]\">\n <nav className=\"space-y-1 xl:sticky xl:top-4 xl:self-start\">\n <button\n type=\"button\"\n className={cn(\n 'w-full rounded-md border px-3 py-2 text-left text-sm transition-colors',\n effectiveCategory === ALL_CATEGORY ? 'border-primary/50 bg-primary/10 text-foreground' : 'border-transparent hover:border-border hover:bg-accent',\n )}\n onClick={() => setActiveCategory(ALL_CATEGORY)}\n >\n <span className=\"flex items-center gap-2\">\n <span className=\"font-medium\">All</span>\n <Badge variant=\"outline\" className=\"ml-auto h-4 px-1 text-[10px]\">{templates.length}</Badge>\n </span>\n <span className=\"block truncate text-xs text-muted-foreground\">Every externalized prompt template</span>\n </button>\n\n {categories.map((category) => (\n <button\n key={category.key}\n type=\"button\"\n className={cn(\n 'w-full rounded-md border px-3 py-2 text-left text-sm transition-colors',\n effectiveCategory === category.key ? 'border-primary/50 bg-primary/10 text-foreground' : 'border-transparent hover:border-border hover:bg-accent',\n )}\n onClick={() => setActiveCategory(category.key)}\n >\n <span className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{category.label}</span>\n <Badge variant=\"outline\" className=\"ml-auto h-4 px-1 text-[10px]\">{category.count}</Badge>\n </span>\n <span className=\"block truncate text-xs font-mono text-muted-foreground\">{category.key}</span>\n </button>\n ))}\n </nav>\n\n <div className=\"min-w-0 space-y-3\">\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <div>\n <h2 className=\"text-sm font-semibold\">\n {effectiveCategory === ALL_CATEGORY ? 'All templates' : `${titleCaseCategory(effectiveCategory)} templates`}\n </h2>\n <p className=\"text-xs text-muted-foreground\">{visibleTemplates.length} template{visibleTemplates.length === 1 ? '' : 's'} in this view</p>\n </div>\n </div>\n\n {visibleTemplates.map((template) => {\n const selected = template.slug === resolvedSelectedSlug\n const modified = template.template !== template.defaultTemplate\n return (\n <button\n key={template.slug}\n type=\"button\"\n className={cn(\n 'w-full rounded-xl border p-4 text-left transition-colors',\n selected ? 'border-primary/50 bg-primary/10 ring-1 ring-primary/20' : 'border-border bg-card hover:border-primary/30 hover:bg-accent',\n )}\n onClick={() => selectTemplate(template.slug)}\n >\n <div className=\"flex flex-wrap items-start gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-foreground\">{template.title}</span>\n {modified && (\n <Badge className=\"bg-amber-500/15 text-amber-300 hover:bg-amber-500/15\">Modified</Badge>\n )}\n <Badge variant=\"outline\" className=\"text-[10px]\">{titleCaseCategory(template.category)}</Badge>\n </div>\n <div className=\"mt-1 font-mono text-[11px] text-muted-foreground\">{template.slug}</div>\n {template.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-muted-foreground\">{template.description}</p>\n )}\n <div className=\"mt-3 flex flex-wrap items-center gap-3 text-[11px] text-muted-foreground\">\n <span>{template.variables.length} variable{template.variables.length === 1 ? '' : 's'}</span>\n <span>Updated {timeAgo(now, template.updatedAt)}</span>\n </div>\n </div>\n </div>\n </button>\n )\n })}\n </div>\n\n <div className=\"min-w-0\">\n {selectedTemplate ? (\n <Card>\n <CardHeader className=\"border-b border-border\">\n <CardTitle className=\"flex flex-wrap items-center gap-2\">\n <span>{selectedTemplate.title}</span>\n {selectedTemplate.template !== selectedTemplate.defaultTemplate && (\n <Badge className=\"bg-amber-500/15 text-amber-300 hover:bg-amber-500/15\">Customized</Badge>\n )}\n {hasUnsavedChanges && <Badge variant=\"outline\">Unsaved</Badge>}\n </CardTitle>\n <CardDescription className=\"space-y-1\">\n <div className=\"font-mono text-[11px]\">{selectedTemplate.slug}</div>\n {selectedTemplate.description && <div>{selectedTemplate.description}</div>}\n </CardDescription>\n </CardHeader>\n\n <CardContent className=\"space-y-4 pt-4\">\n {actionError && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-2 text-xs text-destructive\">\n <AlertCircle className=\"mt-0.5 size-3.5\" />\n <span>{actionError}</span>\n </div>\n )}\n\n {notice && (\n <div className=\"rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-2 text-xs text-emerald-300\">\n {notice}\n </div>\n )}\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-muted-foreground\">\n <Braces className=\"size-3.5\" />\n Available variables\n </div>\n {selectedTemplate.variables.length > 0 ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {selectedTemplate.variables.map((variable) => (\n <Badge key={variable} variant=\"secondary\" className=\"font-mono text-[11px]\">\n {`{{${variable}}}`}\n </Badge>\n ))}\n </div>\n ) : (\n <div className=\"text-xs text-muted-foreground\">This template has no placeholder variables.</div>\n )}\n </div>\n\n {selectedTemplate.template !== selectedTemplate.defaultTemplate && (\n <div className=\"rounded-lg border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs text-amber-200\">\n This template differs from the built-in default. Reset will restore the original text.\n </div>\n )}\n\n <div className=\"space-y-1.5\">\n <label htmlFor=\"prompt-template-editor\" className=\"text-xs font-medium text-muted-foreground\">\n Template text\n </label>\n <Textarea\n id=\"prompt-template-editor\"\n value={draft}\n onChange={(event) => {\n const next = event.target.value\n setDrafts((current) => ({ ...current, [selectedTemplate.slug]: next }))\n if (actionError) setActionError('')\n if (notice) setNotice('')\n }}\n rows={18}\n spellCheck={false}\n className=\"min-h-[360px] resize-y font-mono text-xs leading-5\"\n />\n </div>\n </CardContent>\n\n <CardFooter className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"text-xs text-muted-foreground\">\n {hasUnsavedChanges ? 'Editing local changes.' : `Updated ${timeAgo(now, selectedTemplate.updatedAt)}.`}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {selectedTemplate.template !== selectedTemplate.defaultTemplate && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => void resetTemplate()} disabled={saving}>\n <RotateCcw className=\"size-4\" />\n Reset to default\n </Button>\n )}\n <Button variant=\"outline\" size=\"sm\" onClick={cancelDraft} disabled={!hasUnsavedChanges || saving}>\n Cancel\n </Button>\n <Button size=\"sm\" onClick={() => void saveTemplate()} disabled={!hasUnsavedChanges || saving}>\n <Save className=\"size-4\" />\n Save\n </Button>\n </div>\n </CardFooter>\n </Card>\n ) : (\n <Card size=\"sm\">\n <CardContent className=\"text-sm text-muted-foreground\">\n Select a prompt template to edit it.\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{L as t,U as n,V as r,X as i,_n as a,bn as o,cn as s,en as c,hr as l,nr as u,rr as d}from"./lucide-react-DuhlF2Sj.js";import{m as f,p,u as m}from"./types-uVOXgvMT.js";import{t as h}from"./badge-DCIT3Irj.js";import{t as g}from"./button-CzdTkRIf.js";import{t as _}from"./input-B6Q0NWCS.js";import{U as v,yt as y}from"./index-D9OogaB5.js";import{o as b,r as x,s as S,t as C}from"./card-C3BGuVC6.js";var w=e(l(),1),T=u(),E=[{value:`all`,label:`All`},{value:`skill`,label:`Skills`},{value:`plugin`,label:`Plugins`},{value:`mcp`,label:`MCP`}],D=[`any`,...p()];function O(e){return e===`skill`?`bg-blue-500/10 text-blue-400 border-blue-500/30`:e===`plugin`?`bg-violet-500/10 text-violet-400 border-violet-500/30`:`bg-emerald-500/10 text-emerald-400 border-emerald-500/30`}function k(e){return e===`any`?`bg-zinc-500/10 text-zinc-400 border-zinc-500/30`:f(e)}function A(e){return e===`approved`?`bg-emerald-500/10 text-emerald-400 border-emerald-500/30`:e===`rejected`?`bg-red-500/10 text-red-400 border-red-500/30`:`bg-yellow-500/10 text-yellow-400 border-yellow-500/30`}function j({status:e}){return e===`valid`?(0,T.jsx)(a,{className:`h-3.5 w-3.5 text-emerald-400`}):e===`invalid`?(0,T.jsx)(o,{className:`h-3.5 w-3.5 text-red-400`}):(0,T.jsx)(s,{className:`h-3.5 w-3.5 text-zinc-400`})}var M={kind:`skill`,slug:``,title:``},N={githubUrl:``,ref:`main`,subpath:``,provider:`any`,versionConstraint:``},P={command:``,args:``,url:``,approvalStatus:`pending`,versionConstraint:``};function ee({variant:e,onToggle:t,onUpdate:r,onValidate:i,busy:a}){let o=a===e.id;return(0,T.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-3 space-y-2`,children:[(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,T.jsx)(h,{variant:`outline`,className:`text-[10px] ${k(e.provider)}`,children:e.provider}),(0,T.jsx)(`span`,{className:`text-xs font-medium font-mono`,children:e.name}),e.approvalStatus&&(0,T.jsx)(h,{variant:`outline`,className:`text-[10px] ${A(e.approvalStatus)}`,children:e.approvalStatus}),(0,T.jsxs)(`span`,{className:`text-[10px] text-muted-foreground flex items-center gap-1 ml-auto`,children:[(0,T.jsx)(j,{status:e.validationStatus}),e.validationStatus??`unvalidated`]}),(0,T.jsx)(y,{size:`sm`,checked:e.enabled,disabled:o,onCheckedChange:n=>t(e.id,n)})]}),(e.sourceUrl||e.sourceRef)&&(0,T.jsxs)(`div`,{className:`text-[10px] text-muted-foreground font-mono truncate`,title:e.sourceUrl,children:[e.sourceUrl,e.sourceRef?`@${e.resolvedRef??e.sourceRef}`:``,e.sourceSubpath?` · ${e.sourceSubpath}`:``]}),(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsxs)(g,{variant:`outline`,size:`sm`,className:`h-6 text-xs`,disabled:o||!e.sourceRef,onClick:()=>r(e.id),title:`Pull latest from source ref`,children:[(0,T.jsx)(c,{className:`h-3 w-3 mr-1`}),`Update ref`]}),(0,T.jsxs)(g,{variant:`outline`,size:`sm`,className:`h-6 text-xs`,disabled:o,onClick:()=>i(e.id),children:[(0,T.jsx)(n,{className:`h-3 w-3 mr-1`}),`Validate`]})]})]})}function F({form:e,onChange:t,onSubmit:n,onCancel:r,busy:i,error:a}){return(0,T.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-3 space-y-3`,children:[(0,T.jsx)(`div`,{className:`text-sm font-medium`,children:`New Capability`}),a&&(0,T.jsx)(`div`,{className:`text-xs text-destructive`,children:a}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Kind`}),(0,T.jsxs)(`select`,{className:`mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm`,value:e.kind,onChange:e=>t({kind:e.target.value}),children:[(0,T.jsx)(`option`,{value:`skill`,children:`Skill`}),(0,T.jsx)(`option`,{value:`plugin`,children:`Plugin`}),(0,T.jsx)(`option`,{value:`mcp`,children:`MCP Server`})]})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Slug`}),(0,T.jsx)(_,{value:e.slug,onChange:e=>t({slug:e.target.value}),placeholder:`my-capability`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Title (optional)`}),(0,T.jsx)(_,{value:e.title,onChange:e=>t({title:e.target.value}),placeholder:`My Capability`,className:`mt-1 text-sm`})]}),(0,T.jsxs)(`div`,{className:`flex gap-2`,children:[(0,T.jsx)(g,{size:`sm`,onClick:n,disabled:i||!e.slug.trim(),children:i?`Creating…`:`Create`}),(0,T.jsx)(g,{size:`sm`,variant:`outline`,onClick:r,children:`Cancel`})]})]})}function I({form:e,onChange:t,onSubmit:n,onCancel:r,busy:i,error:a}){return(0,T.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-3 space-y-3`,children:[(0,T.jsx)(`div`,{className:`text-sm font-medium`,children:`Import from GitHub`}),a&&(0,T.jsx)(`div`,{className:`text-xs text-destructive`,children:a}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`GitHub URL`}),(0,T.jsx)(_,{value:e.githubUrl,onChange:e=>t({githubUrl:e.target.value}),placeholder:`https://github.com/owner/repo`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-2 gap-2`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Ref`}),(0,T.jsx)(_,{value:e.ref,onChange:e=>t({ref:e.target.value}),placeholder:`main`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Subpath (optional)`}),(0,T.jsx)(_,{value:e.subpath,onChange:e=>t({subpath:e.target.value}),placeholder:`skills/my-skill`,className:`mt-1 text-sm font-mono`})]})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-2 gap-2`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Provider`}),(0,T.jsx)(`select`,{className:`mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm`,value:e.provider,onChange:e=>t({provider:e.target.value}),children:D.map(e=>(0,T.jsx)(`option`,{value:e,children:e},e))})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Version constraint (optional)`}),(0,T.jsx)(_,{value:e.versionConstraint,onChange:e=>t({versionConstraint:e.target.value}),placeholder:`>=1.0.0`,className:`mt-1 text-sm font-mono`})]})]}),(0,T.jsxs)(`div`,{className:`flex gap-2`,children:[(0,T.jsx)(g,{size:`sm`,onClick:n,disabled:i||!e.githubUrl.trim()||!e.ref.trim(),children:i?`Importing…`:`Import`}),(0,T.jsx)(g,{size:`sm`,variant:`outline`,onClick:r,children:`Cancel`})]})]})}function L({form:e,onChange:t,onSubmit:n,onCancel:r,busy:i,error:a}){return(0,T.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-3 space-y-3`,children:[(0,T.jsx)(`div`,{className:`text-sm font-medium`,children:`Add MCP Server Variant`}),a&&(0,T.jsx)(`div`,{className:`text-xs text-destructive`,children:a}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Command (for stdio type)`}),(0,T.jsx)(_,{value:e.command,onChange:e=>t({command:e.target.value}),placeholder:`npx -y @modelcontextprotocol/server-foo`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Args (space-separated, overrides command split)`}),(0,T.jsx)(_,{value:e.args,onChange:e=>t({args:e.target.value}),placeholder:`--port 3000`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`URL (for http/sse type)`}),(0,T.jsx)(_,{value:e.url,onChange:e=>t({url:e.target.value}),placeholder:`http://localhost:3000/mcp`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-2 gap-2`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Approval status`}),(0,T.jsxs)(`select`,{className:`mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm`,value:e.approvalStatus,onChange:e=>t({approvalStatus:e.target.value}),children:[(0,T.jsx)(`option`,{value:`pending`,children:`Pending`}),(0,T.jsx)(`option`,{value:`approved`,children:`Approved`})]})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Version constraint (optional)`}),(0,T.jsx)(_,{value:e.versionConstraint,onChange:e=>t({versionConstraint:e.target.value}),placeholder:`>=1.0.0`,className:`mt-1 text-sm font-mono`})]})]}),(0,T.jsxs)(`div`,{className:`flex gap-2`,children:[(0,T.jsx)(g,{size:`sm`,onClick:n,disabled:i||!e.command.trim()&&!e.url.trim(),children:i?`Adding…`:`Add MCP Variant`}),(0,T.jsx)(g,{size:`sm`,variant:`outline`,onClick:r,children:`Cancel`})]})]})}function R(){let[e,n]=(0,w.useState)([]),[a,o]=(0,w.useState)(`all`),[s,c]=(0,w.useState)(``),[l,u]=(0,w.useState)(!0),[f,p]=(0,w.useState)(null),[_,v]=(0,w.useState)(`none`),[y,D]=(0,w.useState)(!1),[k,A]=(0,w.useState)(null),[j,R]=(0,w.useState)(null),[z,B]=(0,w.useState)(M),[V,H]=(0,w.useState)(N),[U,W]=(0,w.useState)(P);async function G(){p(null);try{let e=await d(`GET`,`/provisioning/capabilities${a===`all`?``:`?kind=${a}`}`);n(e.capabilities),!s&&e.capabilities.length>0&&c(e.capabilities[0].capability.id)}catch(e){p(m(e))}finally{u(!1)}}(0,w.useEffect)(()=>{u(!0),G()},[a]);let K=e.find(e=>e.capability.id===s)??null;function q(e){v(e),A(null),D(!1),B(M),H(N),W(P)}async function J(){D(!0),A(null);try{await d(`POST`,`/provisioning/capabilities`,{kind:z.kind,slug:z.slug.trim(),title:z.title.trim()||void 0}),v(`none`),await G()}catch(e){A(m(e))}finally{D(!1)}}async function Y(){if(K){D(!0),A(null);try{await d(`POST`,`/provisioning/capabilities/${encodeURIComponent(K.capability.id)}/variants/import`,{githubUrl:V.githubUrl.trim(),ref:V.ref.trim(),subpath:V.subpath.trim()||void 0,provider:V.provider,versionConstraint:V.versionConstraint.trim()||void 0}),v(`none`),await G()}catch(e){A(m(e))}finally{D(!1)}}}async function X(){if(K){D(!0),A(null);try{let e={};if(U.url.trim())e.type=`http`,e.url=U.url.trim();else{e.type=`stdio`;let t=U.command.trim().split(/\s+/);e.command=t[0];let n=U.args.trim()?U.args.trim().split(/\s+/):t.slice(1);n.length>0&&(e.args=n)}await d(`POST`,`/provisioning/capabilities/${encodeURIComponent(K.capability.id)}/variants/mcp`,{server:e,approvalStatus:U.approvalStatus,versionConstraint:U.versionConstraint.trim()||void 0}),v(`none`),await G()}catch(e){A(m(e))}finally{D(!1)}}}async function Z(e,t){R(e);try{await d(`PATCH`,`/provisioning/variants/${encodeURIComponent(e)}`,{enabled:t}),n(n=>n.map(n=>({...n,variants:n.variants.map(n=>n.id===e?{...n,enabled:t}:n)})))}catch(e){p(m(e))}finally{R(null)}}async function Q(e){let t=K?.variants.find(t=>t.id===e);if(t?.sourceRef){R(e);try{await d(`POST`,`/provisioning/variants/${encodeURIComponent(e)}/update`,{ref:t.sourceRef,subpath:t.sourceSubpath}),await G()}catch(e){p(m(e))}finally{R(null)}}}async function te(e){R(e);try{await d(`POST`,`/provisioning/variants/${encodeURIComponent(e)}/validate`,{}),await G()}catch(e){p(m(e))}finally{R(null)}}let $=K?.capability;return(0,T.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,T.jsx)(i,{className:`size-5 text-primary`}),(0,T.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Registry`}),(0,T.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`provisioning capability registry · #561`}),(0,T.jsxs)(`div`,{className:`ml-auto flex items-center gap-2`,children:[E.map(e=>(0,T.jsx)(`button`,{type:`button`,onClick:()=>o(e.value),className:`rounded-md border px-2.5 py-1 text-xs transition-colors ${a===e.value?`border-primary/40 bg-primary/10 text-primary`:`border-border text-muted-foreground hover:bg-accent`}`,children:e.label},e.value)),(0,T.jsx)(g,{variant:`outline`,size:`sm`,onClick:()=>void G(),children:(0,T.jsx)(t,{className:`size-4`})})]})]}),f&&(0,T.jsx)(`div`,{className:`rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:f}),(0,T.jsxs)(`div`,{className:`grid grid-cols-1 gap-4 xl:grid-cols-[300px_1fr]`,children:[(0,T.jsxs)(`div`,{className:`space-y-2`,children:[(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsxs)(`span`,{className:`text-xs text-muted-foreground font-medium`,children:[e.length,` capabilities`]}),(0,T.jsxs)(g,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>q(`new-capability`),children:[(0,T.jsx)(r,{className:`h-3 w-3`}),`New`]})]}),_===`new-capability`&&(0,T.jsx)(F,{form:z,onChange:e=>B(t=>({...t,...e})),onSubmit:()=>void J(),onCancel:()=>v(`none`),busy:y,error:k}),l&&e.length===0&&(0,T.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`Loading…`}),!l&&e.length===0&&(0,T.jsx)(C,{children:(0,T.jsx)(x,{className:`py-10 text-center text-sm text-muted-foreground`,children:`No capabilities yet. Click New to create one.`})}),e.map(({capability:e,variants:t})=>(0,T.jsxs)(`button`,{type:`button`,onClick:()=>{c(e.id),v(`none`)},className:`w-full rounded-md border p-3 text-left transition-colors ${s===e.id?`border-primary/50 bg-primary/5`:`border-border bg-card/60 hover:bg-accent`}`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsx)(h,{variant:`outline`,className:`text-[10px] ${O(e.kind)}`,children:e.kind}),(0,T.jsx)(`span`,{className:`text-xs font-medium font-mono truncate flex-1`,children:e.slug}),(0,T.jsxs)(`span`,{className:`text-[10px] text-muted-foreground shrink-0`,children:[t.length,`v`]})]}),e.title&&(0,T.jsx)(`div`,{className:`mt-1 text-xs text-muted-foreground truncate`,children:e.title}),(0,T.jsxs)(`div`,{className:`mt-1 flex items-center gap-1.5`,children:[(0,T.jsx)(`span`,{className:`inline-block h-1.5 w-1.5 rounded-full ${e.status===`active`?`bg-emerald-500`:`bg-zinc-500`}`}),(0,T.jsx)(`span`,{className:`text-[10px] text-muted-foreground`,children:e.status})]})]},e.id))]}),$?(0,T.jsxs)(`div`,{className:`space-y-3`,children:[(0,T.jsxs)(C,{children:[(0,T.jsx)(b,{children:(0,T.jsxs)(S,{className:`flex flex-wrap items-center gap-2`,children:[(0,T.jsx)(h,{variant:`outline`,className:O($.kind),children:$.kind}),(0,T.jsx)(`span`,{className:`font-mono text-base`,children:$.slug}),$.title&&(0,T.jsxs)(`span`,{className:`text-sm font-normal text-muted-foreground`,children:[`· `,$.title]}),(0,T.jsx)(h,{variant:$.status===`active`?`secondary`:`outline`,className:`ml-auto text-[10px]`,children:$.status})]})}),$.description&&(0,T.jsx)(x,{className:`pt-0 text-sm text-muted-foreground`,children:$.description})]}),(0,T.jsxs)(C,{children:[(0,T.jsx)(b,{children:(0,T.jsxs)(S,{className:`flex items-center gap-2 text-base`,children:[`Variants`,(0,T.jsxs)(`span`,{className:`text-xs font-normal text-muted-foreground`,children:[`(`,K?.variants.length??0,`)`]}),(0,T.jsxs)(`div`,{className:`ml-auto flex items-center gap-2`,children:[(0,T.jsxs)(g,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>q(_===`import-github`?`none`:`import-github`),children:[(0,T.jsx)(r,{className:`h-3 w-3`}),`GitHub`]}),$.kind===`mcp`&&(0,T.jsxs)(g,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>q(_===`import-mcp`?`none`:`import-mcp`),children:[(0,T.jsx)(r,{className:`h-3 w-3`}),`MCP Server`]})]})]})}),(0,T.jsxs)(x,{className:`space-y-3`,children:[_===`import-github`&&(0,T.jsx)(I,{form:V,onChange:e=>H(t=>({...t,...e})),onSubmit:()=>void Y(),onCancel:()=>v(`none`),busy:y,error:k}),_===`import-mcp`&&(0,T.jsx)(L,{form:U,onChange:e=>W(t=>({...t,...e})),onSubmit:()=>void X(),onCancel:()=>v(`none`),busy:y,error:k}),K&&K.variants.length===0&&_===`none`&&(0,T.jsxs)(`div`,{className:`py-6 text-center text-sm text-muted-foreground`,children:[`No variants yet. Add one via GitHub import`,$.kind===`mcp`?` or MCP Server`:``,`.`]}),K?.variants.map(e=>(0,T.jsx)(ee,{variant:e,onToggle:(e,t)=>void Z(e,t),onUpdate:e=>void Q(e),onValidate:e=>void te(e),busy:j},e.id))]})]})]}):!l&&(0,T.jsx)(C,{children:(0,T.jsx)(x,{className:`py-16 text-center text-sm text-muted-foreground`,children:`Select a capability to view details and variants.`})})]})]})}export{R as RegistryView};
|
|
2
|
-
//# sourceMappingURL=registry-
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{L as t,U as n,V as r,X as i,_n as a,bn as o,cn as s,en as c,hr as l,nr as u,rr as d}from"./lucide-react-DuhlF2Sj.js";import{d as f,h as p,m}from"./types-BeJUSfDj.js";import{t as h}from"./badge-DCIT3Irj.js";import{t as g}from"./button-CzdTkRIf.js";import{t as _}from"./input-B6Q0NWCS.js";import{U as v,yt as y}from"./index-D0xMQQb_.js";import{o as b,r as x,s as S,t as C}from"./card-C3BGuVC6.js";var w=e(l(),1),T=u(),E=[{value:`all`,label:`All`},{value:`skill`,label:`Skills`},{value:`plugin`,label:`Plugins`},{value:`mcp`,label:`MCP`}],D=[`any`,...m()];function O(e){return e===`skill`?`bg-blue-500/10 text-blue-400 border-blue-500/30`:e===`plugin`?`bg-violet-500/10 text-violet-400 border-violet-500/30`:`bg-emerald-500/10 text-emerald-400 border-emerald-500/30`}function k(e){return e===`any`?`bg-zinc-500/10 text-zinc-400 border-zinc-500/30`:p(e)}function A(e){return e===`approved`?`bg-emerald-500/10 text-emerald-400 border-emerald-500/30`:e===`rejected`?`bg-red-500/10 text-red-400 border-red-500/30`:`bg-yellow-500/10 text-yellow-400 border-yellow-500/30`}function j({status:e}){return e===`valid`?(0,T.jsx)(a,{className:`h-3.5 w-3.5 text-emerald-400`}):e===`invalid`?(0,T.jsx)(o,{className:`h-3.5 w-3.5 text-red-400`}):(0,T.jsx)(s,{className:`h-3.5 w-3.5 text-zinc-400`})}var M={kind:`skill`,slug:``,title:``},N={githubUrl:``,ref:`main`,subpath:``,provider:`any`,versionConstraint:``},P={command:``,args:``,url:``,approvalStatus:`pending`,versionConstraint:``};function ee({variant:e,onToggle:t,onUpdate:r,onValidate:i,busy:a}){let o=a===e.id;return(0,T.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-3 space-y-2`,children:[(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,T.jsx)(h,{variant:`outline`,className:`text-[10px] ${k(e.provider)}`,children:e.provider}),(0,T.jsx)(`span`,{className:`text-xs font-medium font-mono`,children:e.name}),e.approvalStatus&&(0,T.jsx)(h,{variant:`outline`,className:`text-[10px] ${A(e.approvalStatus)}`,children:e.approvalStatus}),(0,T.jsxs)(`span`,{className:`text-[10px] text-muted-foreground flex items-center gap-1 ml-auto`,children:[(0,T.jsx)(j,{status:e.validationStatus}),e.validationStatus??`unvalidated`]}),(0,T.jsx)(y,{size:`sm`,checked:e.enabled,disabled:o,onCheckedChange:n=>t(e.id,n)})]}),(e.sourceUrl||e.sourceRef)&&(0,T.jsxs)(`div`,{className:`text-[10px] text-muted-foreground font-mono truncate`,title:e.sourceUrl,children:[e.sourceUrl,e.sourceRef?`@${e.resolvedRef??e.sourceRef}`:``,e.sourceSubpath?` · ${e.sourceSubpath}`:``]}),(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsxs)(g,{variant:`outline`,size:`sm`,className:`h-6 text-xs`,disabled:o||!e.sourceRef,onClick:()=>r(e.id),title:`Pull latest from source ref`,children:[(0,T.jsx)(c,{className:`h-3 w-3 mr-1`}),`Update ref`]}),(0,T.jsxs)(g,{variant:`outline`,size:`sm`,className:`h-6 text-xs`,disabled:o,onClick:()=>i(e.id),children:[(0,T.jsx)(n,{className:`h-3 w-3 mr-1`}),`Validate`]})]})]})}function F({form:e,onChange:t,onSubmit:n,onCancel:r,busy:i,error:a}){return(0,T.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-3 space-y-3`,children:[(0,T.jsx)(`div`,{className:`text-sm font-medium`,children:`New Capability`}),a&&(0,T.jsx)(`div`,{className:`text-xs text-destructive`,children:a}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Kind`}),(0,T.jsxs)(`select`,{className:`mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm`,value:e.kind,onChange:e=>t({kind:e.target.value}),children:[(0,T.jsx)(`option`,{value:`skill`,children:`Skill`}),(0,T.jsx)(`option`,{value:`plugin`,children:`Plugin`}),(0,T.jsx)(`option`,{value:`mcp`,children:`MCP Server`})]})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Slug`}),(0,T.jsx)(_,{value:e.slug,onChange:e=>t({slug:e.target.value}),placeholder:`my-capability`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Title (optional)`}),(0,T.jsx)(_,{value:e.title,onChange:e=>t({title:e.target.value}),placeholder:`My Capability`,className:`mt-1 text-sm`})]}),(0,T.jsxs)(`div`,{className:`flex gap-2`,children:[(0,T.jsx)(g,{size:`sm`,onClick:n,disabled:i||!e.slug.trim(),children:i?`Creating…`:`Create`}),(0,T.jsx)(g,{size:`sm`,variant:`outline`,onClick:r,children:`Cancel`})]})]})}function I({form:e,onChange:t,onSubmit:n,onCancel:r,busy:i,error:a}){return(0,T.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-3 space-y-3`,children:[(0,T.jsx)(`div`,{className:`text-sm font-medium`,children:`Import from GitHub`}),a&&(0,T.jsx)(`div`,{className:`text-xs text-destructive`,children:a}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`GitHub URL`}),(0,T.jsx)(_,{value:e.githubUrl,onChange:e=>t({githubUrl:e.target.value}),placeholder:`https://github.com/owner/repo`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-2 gap-2`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Ref`}),(0,T.jsx)(_,{value:e.ref,onChange:e=>t({ref:e.target.value}),placeholder:`main`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Subpath (optional)`}),(0,T.jsx)(_,{value:e.subpath,onChange:e=>t({subpath:e.target.value}),placeholder:`skills/my-skill`,className:`mt-1 text-sm font-mono`})]})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-2 gap-2`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Provider`}),(0,T.jsx)(`select`,{className:`mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm`,value:e.provider,onChange:e=>t({provider:e.target.value}),children:D.map(e=>(0,T.jsx)(`option`,{value:e,children:e},e))})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Version constraint (optional)`}),(0,T.jsx)(_,{value:e.versionConstraint,onChange:e=>t({versionConstraint:e.target.value}),placeholder:`>=1.0.0`,className:`mt-1 text-sm font-mono`})]})]}),(0,T.jsxs)(`div`,{className:`flex gap-2`,children:[(0,T.jsx)(g,{size:`sm`,onClick:n,disabled:i||!e.githubUrl.trim()||!e.ref.trim(),children:i?`Importing…`:`Import`}),(0,T.jsx)(g,{size:`sm`,variant:`outline`,onClick:r,children:`Cancel`})]})]})}function L({form:e,onChange:t,onSubmit:n,onCancel:r,busy:i,error:a}){return(0,T.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-3 space-y-3`,children:[(0,T.jsx)(`div`,{className:`text-sm font-medium`,children:`Add MCP Server Variant`}),a&&(0,T.jsx)(`div`,{className:`text-xs text-destructive`,children:a}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Command (for stdio type)`}),(0,T.jsx)(_,{value:e.command,onChange:e=>t({command:e.target.value}),placeholder:`npx -y @modelcontextprotocol/server-foo`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Args (space-separated, overrides command split)`}),(0,T.jsx)(_,{value:e.args,onChange:e=>t({args:e.target.value}),placeholder:`--port 3000`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`URL (for http/sse type)`}),(0,T.jsx)(_,{value:e.url,onChange:e=>t({url:e.target.value}),placeholder:`http://localhost:3000/mcp`,className:`mt-1 text-sm font-mono`})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-2 gap-2`,children:[(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Approval status`}),(0,T.jsxs)(`select`,{className:`mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm`,value:e.approvalStatus,onChange:e=>t({approvalStatus:e.target.value}),children:[(0,T.jsx)(`option`,{value:`pending`,children:`Pending`}),(0,T.jsx)(`option`,{value:`approved`,children:`Approved`})]})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(v,{className:`text-xs`,children:`Version constraint (optional)`}),(0,T.jsx)(_,{value:e.versionConstraint,onChange:e=>t({versionConstraint:e.target.value}),placeholder:`>=1.0.0`,className:`mt-1 text-sm font-mono`})]})]}),(0,T.jsxs)(`div`,{className:`flex gap-2`,children:[(0,T.jsx)(g,{size:`sm`,onClick:n,disabled:i||!e.command.trim()&&!e.url.trim(),children:i?`Adding…`:`Add MCP Variant`}),(0,T.jsx)(g,{size:`sm`,variant:`outline`,onClick:r,children:`Cancel`})]})]})}function R(){let[e,n]=(0,w.useState)([]),[a,o]=(0,w.useState)(`all`),[s,c]=(0,w.useState)(``),[l,u]=(0,w.useState)(!0),[p,m]=(0,w.useState)(null),[_,v]=(0,w.useState)(`none`),[y,D]=(0,w.useState)(!1),[k,A]=(0,w.useState)(null),[j,R]=(0,w.useState)(null),[z,B]=(0,w.useState)(M),[V,H]=(0,w.useState)(N),[U,W]=(0,w.useState)(P);async function G(){m(null);try{let e=await d(`GET`,`/provisioning/capabilities${a===`all`?``:`?kind=${a}`}`);n(e.capabilities),!s&&e.capabilities.length>0&&c(e.capabilities[0].capability.id)}catch(e){m(f(e))}finally{u(!1)}}(0,w.useEffect)(()=>{u(!0),G()},[a]);let K=e.find(e=>e.capability.id===s)??null;function q(e){v(e),A(null),D(!1),B(M),H(N),W(P)}async function J(){D(!0),A(null);try{await d(`POST`,`/provisioning/capabilities`,{kind:z.kind,slug:z.slug.trim(),title:z.title.trim()||void 0}),v(`none`),await G()}catch(e){A(f(e))}finally{D(!1)}}async function Y(){if(K){D(!0),A(null);try{await d(`POST`,`/provisioning/capabilities/${encodeURIComponent(K.capability.id)}/variants/import`,{githubUrl:V.githubUrl.trim(),ref:V.ref.trim(),subpath:V.subpath.trim()||void 0,provider:V.provider,versionConstraint:V.versionConstraint.trim()||void 0}),v(`none`),await G()}catch(e){A(f(e))}finally{D(!1)}}}async function X(){if(K){D(!0),A(null);try{let e={};if(U.url.trim())e.type=`http`,e.url=U.url.trim();else{e.type=`stdio`;let t=U.command.trim().split(/\s+/);e.command=t[0];let n=U.args.trim()?U.args.trim().split(/\s+/):t.slice(1);n.length>0&&(e.args=n)}await d(`POST`,`/provisioning/capabilities/${encodeURIComponent(K.capability.id)}/variants/mcp`,{server:e,approvalStatus:U.approvalStatus,versionConstraint:U.versionConstraint.trim()||void 0}),v(`none`),await G()}catch(e){A(f(e))}finally{D(!1)}}}async function Z(e,t){R(e);try{await d(`PATCH`,`/provisioning/variants/${encodeURIComponent(e)}`,{enabled:t}),n(n=>n.map(n=>({...n,variants:n.variants.map(n=>n.id===e?{...n,enabled:t}:n)})))}catch(e){m(f(e))}finally{R(null)}}async function Q(e){let t=K?.variants.find(t=>t.id===e);if(t?.sourceRef){R(e);try{await d(`POST`,`/provisioning/variants/${encodeURIComponent(e)}/update`,{ref:t.sourceRef,subpath:t.sourceSubpath}),await G()}catch(e){m(f(e))}finally{R(null)}}}async function te(e){R(e);try{await d(`POST`,`/provisioning/variants/${encodeURIComponent(e)}/validate`,{}),await G()}catch(e){m(f(e))}finally{R(null)}}let $=K?.capability;return(0,T.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,T.jsx)(i,{className:`size-5 text-primary`}),(0,T.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Registry`}),(0,T.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`provisioning capability registry · #561`}),(0,T.jsxs)(`div`,{className:`ml-auto flex items-center gap-2`,children:[E.map(e=>(0,T.jsx)(`button`,{type:`button`,onClick:()=>o(e.value),className:`rounded-md border px-2.5 py-1 text-xs transition-colors ${a===e.value?`border-primary/40 bg-primary/10 text-primary`:`border-border text-muted-foreground hover:bg-accent`}`,children:e.label},e.value)),(0,T.jsx)(g,{variant:`outline`,size:`sm`,onClick:()=>void G(),children:(0,T.jsx)(t,{className:`size-4`})})]})]}),p&&(0,T.jsx)(`div`,{className:`rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:p}),(0,T.jsxs)(`div`,{className:`grid grid-cols-1 gap-4 xl:grid-cols-[300px_1fr]`,children:[(0,T.jsxs)(`div`,{className:`space-y-2`,children:[(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsxs)(`span`,{className:`text-xs text-muted-foreground font-medium`,children:[e.length,` capabilities`]}),(0,T.jsxs)(g,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>q(`new-capability`),children:[(0,T.jsx)(r,{className:`h-3 w-3`}),`New`]})]}),_===`new-capability`&&(0,T.jsx)(F,{form:z,onChange:e=>B(t=>({...t,...e})),onSubmit:()=>void J(),onCancel:()=>v(`none`),busy:y,error:k}),l&&e.length===0&&(0,T.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`Loading…`}),!l&&e.length===0&&(0,T.jsx)(C,{children:(0,T.jsx)(x,{className:`py-10 text-center text-sm text-muted-foreground`,children:`No capabilities yet. Click New to create one.`})}),e.map(({capability:e,variants:t})=>(0,T.jsxs)(`button`,{type:`button`,onClick:()=>{c(e.id),v(`none`)},className:`w-full rounded-md border p-3 text-left transition-colors ${s===e.id?`border-primary/50 bg-primary/5`:`border-border bg-card/60 hover:bg-accent`}`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsx)(h,{variant:`outline`,className:`text-[10px] ${O(e.kind)}`,children:e.kind}),(0,T.jsx)(`span`,{className:`text-xs font-medium font-mono truncate flex-1`,children:e.slug}),(0,T.jsxs)(`span`,{className:`text-[10px] text-muted-foreground shrink-0`,children:[t.length,`v`]})]}),e.title&&(0,T.jsx)(`div`,{className:`mt-1 text-xs text-muted-foreground truncate`,children:e.title}),(0,T.jsxs)(`div`,{className:`mt-1 flex items-center gap-1.5`,children:[(0,T.jsx)(`span`,{className:`inline-block h-1.5 w-1.5 rounded-full ${e.status===`active`?`bg-emerald-500`:`bg-zinc-500`}`}),(0,T.jsx)(`span`,{className:`text-[10px] text-muted-foreground`,children:e.status})]})]},e.id))]}),$?(0,T.jsxs)(`div`,{className:`space-y-3`,children:[(0,T.jsxs)(C,{children:[(0,T.jsx)(b,{children:(0,T.jsxs)(S,{className:`flex flex-wrap items-center gap-2`,children:[(0,T.jsx)(h,{variant:`outline`,className:O($.kind),children:$.kind}),(0,T.jsx)(`span`,{className:`font-mono text-base`,children:$.slug}),$.title&&(0,T.jsxs)(`span`,{className:`text-sm font-normal text-muted-foreground`,children:[`· `,$.title]}),(0,T.jsx)(h,{variant:$.status===`active`?`secondary`:`outline`,className:`ml-auto text-[10px]`,children:$.status})]})}),$.description&&(0,T.jsx)(x,{className:`pt-0 text-sm text-muted-foreground`,children:$.description})]}),(0,T.jsxs)(C,{children:[(0,T.jsx)(b,{children:(0,T.jsxs)(S,{className:`flex items-center gap-2 text-base`,children:[`Variants`,(0,T.jsxs)(`span`,{className:`text-xs font-normal text-muted-foreground`,children:[`(`,K?.variants.length??0,`)`]}),(0,T.jsxs)(`div`,{className:`ml-auto flex items-center gap-2`,children:[(0,T.jsxs)(g,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>q(_===`import-github`?`none`:`import-github`),children:[(0,T.jsx)(r,{className:`h-3 w-3`}),`GitHub`]}),$.kind===`mcp`&&(0,T.jsxs)(g,{size:`sm`,variant:`outline`,className:`h-7 text-xs gap-1`,onClick:()=>q(_===`import-mcp`?`none`:`import-mcp`),children:[(0,T.jsx)(r,{className:`h-3 w-3`}),`MCP Server`]})]})]})}),(0,T.jsxs)(x,{className:`space-y-3`,children:[_===`import-github`&&(0,T.jsx)(I,{form:V,onChange:e=>H(t=>({...t,...e})),onSubmit:()=>void Y(),onCancel:()=>v(`none`),busy:y,error:k}),_===`import-mcp`&&(0,T.jsx)(L,{form:U,onChange:e=>W(t=>({...t,...e})),onSubmit:()=>void X(),onCancel:()=>v(`none`),busy:y,error:k}),K&&K.variants.length===0&&_===`none`&&(0,T.jsxs)(`div`,{className:`py-6 text-center text-sm text-muted-foreground`,children:[`No variants yet. Add one via GitHub import`,$.kind===`mcp`?` or MCP Server`:``,`.`]}),K?.variants.map(e=>(0,T.jsx)(ee,{variant:e,onToggle:(e,t)=>void Z(e,t),onUpdate:e=>void Q(e),onValidate:e=>void te(e),busy:j},e.id))]})]})]}):!l&&(0,T.jsx)(C,{children:(0,T.jsx)(x,{className:`py-16 text-center text-sm text-muted-foreground`,children:`Select a capability to view details and variants.`})})]})]})}export{R as RegistryView};
|
|
2
|
+
//# sourceMappingURL=registry-CXWyOtpr.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-pb1gTZEg.js","names":[],"sources":["../../dashboard/src/components/views/registry.tsx"],"sourcesContent":["import { useEffect, useState } from 'react'\nimport { errMessage } from 'agent-relay-sdk/types'\nimport { api } from '@/lib/api'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { Switch } from '@/components/ui/switch'\nimport { PackagePlus, RefreshCw, Plus, Trash2, CheckCircle, AlertCircle, Clock, Download, Play } from 'lucide-react'\nimport type { ProvisioningAssetKind, ProvisioningCapability, ProvisioningAsset, ProvisioningCapabilityWithVariants } from '@/types'\nimport { getKnownProviderIds, getProviderBadgeClass } from '@/lib/provider-display'\n\n// Registry view (#561) — browse and manage the provisioning capability registry.\n// Two-pane: left = capability list + filters, right = selected capability detail + variants.\n\ntype KindFilter = 'all' | ProvisioningAssetKind\n\nconst KIND_OPTIONS: Array<{ value: KindFilter; label: string }> = [\n { value: 'all', label: 'All' },\n { value: 'skill', label: 'Skills' },\n { value: 'plugin', label: 'Plugins' },\n { value: 'mcp', label: 'MCP' },\n]\n\nconst PROVIDER_OPTIONS = ['any', ...getKnownProviderIds()] as const\ntype Provider = string\n\nfunction kindBadgeClass(kind: ProvisioningAssetKind): string {\n if (kind === 'skill') return 'bg-blue-500/10 text-blue-400 border-blue-500/30'\n if (kind === 'plugin') return 'bg-violet-500/10 text-violet-400 border-violet-500/30'\n return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30'\n}\n\nfunction providerBadgeClass(provider: string): string {\n if (provider === 'any') return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/30'\n return getProviderBadgeClass(provider)\n}\n\nfunction approvalBadgeClass(status?: string): string {\n if (status === 'approved') return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30'\n if (status === 'rejected') return 'bg-red-500/10 text-red-400 border-red-500/30'\n return 'bg-yellow-500/10 text-yellow-400 border-yellow-500/30'\n}\n\nfunction ValidationIcon({ status }: { status?: string }) {\n if (status === 'valid') return <CheckCircle className=\"h-3.5 w-3.5 text-emerald-400\" />\n if (status === 'invalid') return <AlertCircle className=\"h-3.5 w-3.5 text-red-400\" />\n return <Clock className=\"h-3.5 w-3.5 text-zinc-400\" />\n}\n\n// Form state for creating a new capability\ninterface NewCapabilityForm {\n kind: ProvisioningAssetKind\n slug: string\n title: string\n}\n\n// Form state for GitHub import wizard\ninterface ImportGithubForm {\n githubUrl: string\n ref: string\n subpath: string\n provider: string\n versionConstraint: string\n}\n\n// Form state for MCP server variant\ninterface ImportMcpForm {\n command: string\n args: string\n url: string\n approvalStatus: string\n versionConstraint: string\n}\n\nconst DEFAULT_NEW_CAP: NewCapabilityForm = { kind: 'skill', slug: '', title: '' }\nconst DEFAULT_IMPORT_GH: ImportGithubForm = { githubUrl: '', ref: 'main', subpath: '', provider: 'any', versionConstraint: '' }\nconst DEFAULT_IMPORT_MCP: ImportMcpForm = { command: '', args: '', url: '', approvalStatus: 'pending', versionConstraint: '' }\n\ntype InlineWizard = 'none' | 'new-capability' | 'import-github' | 'import-mcp'\n\nfunction VariantRow({\n variant,\n onToggle,\n onUpdate,\n onValidate,\n busy,\n}: {\n variant: ProvisioningAsset\n onToggle: (id: string, enabled: boolean) => void\n onUpdate: (id: string) => void\n onValidate: (id: string) => void\n busy: string | null\n}) {\n const isBusy = busy === variant.id\n\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3 space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className={`text-[10px] ${providerBadgeClass(variant.provider)}`}>\n {variant.provider}\n </Badge>\n <span className=\"text-xs font-medium font-mono\">{variant.name}</span>\n {variant.approvalStatus && (\n <Badge variant=\"outline\" className={`text-[10px] ${approvalBadgeClass(variant.approvalStatus)}`}>\n {variant.approvalStatus}\n </Badge>\n )}\n <span className=\"text-[10px] text-muted-foreground flex items-center gap-1 ml-auto\">\n <ValidationIcon status={variant.validationStatus} />\n {variant.validationStatus ?? 'unvalidated'}\n </span>\n <Switch\n size=\"sm\"\n checked={variant.enabled}\n disabled={isBusy}\n onCheckedChange={(v) => onToggle(variant.id, v)}\n />\n </div>\n {(variant.sourceUrl || variant.sourceRef) && (\n <div className=\"text-[10px] text-muted-foreground font-mono truncate\" title={variant.sourceUrl}>\n {variant.sourceUrl}{variant.sourceRef ? `@${variant.resolvedRef ?? variant.sourceRef}` : ''}\n {variant.sourceSubpath ? ` · ${variant.sourceSubpath}` : ''}\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-6 text-xs\"\n disabled={isBusy || !variant.sourceRef}\n onClick={() => onUpdate(variant.id)}\n title=\"Pull latest from source ref\"\n >\n <Download className=\"h-3 w-3 mr-1\" />\n Update ref\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-6 text-xs\"\n disabled={isBusy}\n onClick={() => onValidate(variant.id)}\n >\n <Play className=\"h-3 w-3 mr-1\" />\n Validate\n </Button>\n </div>\n </div>\n )\n}\n\nfunction NewCapabilityForm({\n form,\n onChange,\n onSubmit,\n onCancel,\n busy,\n error,\n}: {\n form: NewCapabilityForm\n onChange: (patch: Partial<NewCapabilityForm>) => void\n onSubmit: () => void\n onCancel: () => void\n busy: boolean\n error: string | null\n}) {\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3 space-y-3\">\n <div className=\"text-sm font-medium\">New Capability</div>\n {error && <div className=\"text-xs text-destructive\">{error}</div>}\n <div>\n <Label className=\"text-xs\">Kind</Label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n value={form.kind}\n onChange={(e) => onChange({ kind: e.target.value as ProvisioningAssetKind })}\n >\n <option value=\"skill\">Skill</option>\n <option value=\"plugin\">Plugin</option>\n <option value=\"mcp\">MCP Server</option>\n </select>\n </div>\n <div>\n <Label className=\"text-xs\">Slug</Label>\n <Input\n value={form.slug}\n onChange={(e) => onChange({ slug: e.target.value })}\n placeholder=\"my-capability\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div>\n <Label className=\"text-xs\">Title (optional)</Label>\n <Input\n value={form.title}\n onChange={(e) => onChange({ title: e.target.value })}\n placeholder=\"My Capability\"\n className=\"mt-1 text-sm\"\n />\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={onSubmit} disabled={busy || !form.slug.trim()}>\n {busy ? 'Creating…' : 'Create'}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={onCancel}>Cancel</Button>\n </div>\n </div>\n )\n}\n\nfunction ImportGithubForm({\n form,\n onChange,\n onSubmit,\n onCancel,\n busy,\n error,\n}: {\n form: ImportGithubForm\n onChange: (patch: Partial<ImportGithubForm>) => void\n onSubmit: () => void\n onCancel: () => void\n busy: boolean\n error: string | null\n}) {\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3 space-y-3\">\n <div className=\"text-sm font-medium\">Import from GitHub</div>\n {error && <div className=\"text-xs text-destructive\">{error}</div>}\n <div>\n <Label className=\"text-xs\">GitHub URL</Label>\n <Input\n value={form.githubUrl}\n onChange={(e) => onChange({ githubUrl: e.target.value })}\n placeholder=\"https://github.com/owner/repo\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <Label className=\"text-xs\">Ref</Label>\n <Input\n value={form.ref}\n onChange={(e) => onChange({ ref: e.target.value })}\n placeholder=\"main\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div>\n <Label className=\"text-xs\">Subpath (optional)</Label>\n <Input\n value={form.subpath}\n onChange={(e) => onChange({ subpath: e.target.value })}\n placeholder=\"skills/my-skill\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <Label className=\"text-xs\">Provider</Label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n value={form.provider}\n onChange={(e) => onChange({ provider: e.target.value })}\n >\n {PROVIDER_OPTIONS.map((p) => <option key={p} value={p}>{p}</option>)}\n </select>\n </div>\n <div>\n <Label className=\"text-xs\">Version constraint (optional)</Label>\n <Input\n value={form.versionConstraint}\n onChange={(e) => onChange({ versionConstraint: e.target.value })}\n placeholder=\">=1.0.0\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={onSubmit} disabled={busy || !form.githubUrl.trim() || !form.ref.trim()}>\n {busy ? 'Importing…' : 'Import'}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={onCancel}>Cancel</Button>\n </div>\n </div>\n )\n}\n\nfunction ImportMcpForm({\n form,\n onChange,\n onSubmit,\n onCancel,\n busy,\n error,\n}: {\n form: ImportMcpForm\n onChange: (patch: Partial<ImportMcpForm>) => void\n onSubmit: () => void\n onCancel: () => void\n busy: boolean\n error: string | null\n}) {\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3 space-y-3\">\n <div className=\"text-sm font-medium\">Add MCP Server Variant</div>\n {error && <div className=\"text-xs text-destructive\">{error}</div>}\n <div>\n <Label className=\"text-xs\">Command (for stdio type)</Label>\n <Input\n value={form.command}\n onChange={(e) => onChange({ command: e.target.value })}\n placeholder=\"npx -y @modelcontextprotocol/server-foo\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div>\n <Label className=\"text-xs\">Args (space-separated, overrides command split)</Label>\n <Input\n value={form.args}\n onChange={(e) => onChange({ args: e.target.value })}\n placeholder=\"--port 3000\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div>\n <Label className=\"text-xs\">URL (for http/sse type)</Label>\n <Input\n value={form.url}\n onChange={(e) => onChange({ url: e.target.value })}\n placeholder=\"http://localhost:3000/mcp\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <Label className=\"text-xs\">Approval status</Label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n value={form.approvalStatus}\n onChange={(e) => onChange({ approvalStatus: e.target.value })}\n >\n <option value=\"pending\">Pending</option>\n <option value=\"approved\">Approved</option>\n </select>\n </div>\n <div>\n <Label className=\"text-xs\">Version constraint (optional)</Label>\n <Input\n value={form.versionConstraint}\n onChange={(e) => onChange({ versionConstraint: e.target.value })}\n placeholder=\">=1.0.0\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={onSubmit} disabled={busy || (!form.command.trim() && !form.url.trim())}>\n {busy ? 'Adding…' : 'Add MCP Variant'}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={onCancel}>Cancel</Button>\n </div>\n </div>\n )\n}\n\nexport function RegistryView() {\n const [items, setItems] = useState<ProvisioningCapabilityWithVariants[]>([])\n const [kindFilter, setKindFilter] = useState<KindFilter>('all')\n const [selectedId, setSelectedId] = useState<string>('')\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [wizard, setWizard] = useState<InlineWizard>('none')\n const [wizardBusy, setWizardBusy] = useState(false)\n const [wizardError, setWizardError] = useState<string | null>(null)\n const [variantBusy, setVariantBusy] = useState<string | null>(null)\n const [newCap, setNewCap] = useState<NewCapabilityForm>(DEFAULT_NEW_CAP)\n const [importGh, setImportGh] = useState<ImportGithubForm>(DEFAULT_IMPORT_GH)\n const [importMcp, setImportMcp] = useState<ImportMcpForm>(DEFAULT_IMPORT_MCP)\n\n async function load() {\n setError(null)\n try {\n const params = kindFilter !== 'all' ? `?kind=${kindFilter}` : ''\n const res = await api<{ capabilities: ProvisioningCapabilityWithVariants[] }>('GET', `/provisioning/capabilities${params}`)\n setItems(res.capabilities)\n if (!selectedId && res.capabilities.length > 0) {\n setSelectedId(res.capabilities[0]!.capability.id)\n }\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setLoading(false)\n }\n }\n\n // Initial load + kind filter changes\n useEffect(() => {\n setLoading(true)\n void load()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [kindFilter])\n\n const selected = items.find((i) => i.capability.id === selectedId) ?? null\n\n function openWizard(w: InlineWizard) {\n setWizard(w)\n setWizardError(null)\n setWizardBusy(false)\n setNewCap(DEFAULT_NEW_CAP)\n setImportGh(DEFAULT_IMPORT_GH)\n setImportMcp(DEFAULT_IMPORT_MCP)\n }\n\n async function createCapability() {\n setWizardBusy(true)\n setWizardError(null)\n try {\n await api<ProvisioningCapability>('POST', '/provisioning/capabilities', {\n kind: newCap.kind,\n slug: newCap.slug.trim(),\n title: newCap.title.trim() || undefined,\n })\n setWizard('none')\n await load()\n } catch (e) {\n setWizardError(errMessage(e))\n } finally {\n setWizardBusy(false)\n }\n }\n\n async function importFromGithub() {\n if (!selected) return\n setWizardBusy(true)\n setWizardError(null)\n try {\n await api('POST', `/provisioning/capabilities/${encodeURIComponent(selected.capability.id)}/variants/import`, {\n githubUrl: importGh.githubUrl.trim(),\n ref: importGh.ref.trim(),\n subpath: importGh.subpath.trim() || undefined,\n provider: importGh.provider,\n versionConstraint: importGh.versionConstraint.trim() || undefined,\n })\n setWizard('none')\n await load()\n } catch (e) {\n setWizardError(errMessage(e))\n } finally {\n setWizardBusy(false)\n }\n }\n\n async function importMcpServer() {\n if (!selected) return\n setWizardBusy(true)\n setWizardError(null)\n try {\n // Build server spec: command/args or url\n const server: Record<string, unknown> = {}\n if (importMcp.url.trim()) {\n server.type = 'http'\n server.url = importMcp.url.trim()\n } else {\n server.type = 'stdio'\n const parts = importMcp.command.trim().split(/\\s+/)\n server.command = parts[0]\n const extraArgs = importMcp.args.trim() ? importMcp.args.trim().split(/\\s+/) : parts.slice(1)\n if (extraArgs.length > 0) server.args = extraArgs\n }\n await api('POST', `/provisioning/capabilities/${encodeURIComponent(selected.capability.id)}/variants/mcp`, {\n server,\n approvalStatus: importMcp.approvalStatus,\n versionConstraint: importMcp.versionConstraint.trim() || undefined,\n })\n setWizard('none')\n await load()\n } catch (e) {\n setWizardError(errMessage(e))\n } finally {\n setWizardBusy(false)\n }\n }\n\n async function toggleVariant(id: string, enabled: boolean) {\n setVariantBusy(id)\n try {\n await api('PATCH', `/provisioning/variants/${encodeURIComponent(id)}`, { enabled })\n setItems((prev) =>\n prev.map((item) => ({\n ...item,\n variants: item.variants.map((v) => (v.id === id ? { ...v, enabled } : v)),\n }))\n )\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setVariantBusy(null)\n }\n }\n\n async function updateVariantRef(id: string) {\n const variant = selected?.variants.find((v) => v.id === id)\n if (!variant?.sourceRef) return\n setVariantBusy(id)\n try {\n await api('POST', `/provisioning/variants/${encodeURIComponent(id)}/update`, {\n ref: variant.sourceRef,\n subpath: variant.sourceSubpath,\n })\n await load()\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setVariantBusy(null)\n }\n }\n\n async function validateVariant(id: string) {\n setVariantBusy(id)\n try {\n await api('POST', `/provisioning/variants/${encodeURIComponent(id)}/validate`, {})\n await load()\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setVariantBusy(null)\n }\n }\n\n const cap = selected?.capability\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <PackagePlus className=\"size-5 text-primary\" />\n <h1 className=\"text-lg font-semibold\">Registry</h1>\n <span className=\"text-xs text-muted-foreground\">provisioning capability registry · #561</span>\n <div className=\"ml-auto flex items-center gap-2\">\n {KIND_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => setKindFilter(opt.value)}\n className={`rounded-md border px-2.5 py-1 text-xs transition-colors ${kindFilter === opt.value ? 'border-primary/40 bg-primary/10 text-primary' : 'border-border text-muted-foreground hover:bg-accent'}`}\n >\n {opt.label}\n </button>\n ))}\n <Button variant=\"outline\" size=\"sm\" onClick={() => void load()}>\n <RefreshCw className=\"size-4\" />\n </Button>\n </div>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error}</div>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 xl:grid-cols-[300px_1fr]\">\n {/* Left pane: capability list */}\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-muted-foreground font-medium\">{items.length} capabilities</span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => openWizard('new-capability')}\n >\n <Plus className=\"h-3 w-3\" />\n New\n </Button>\n </div>\n\n {wizard === 'new-capability' && (\n <NewCapabilityForm\n form={newCap}\n onChange={(p) => setNewCap((f) => ({ ...f, ...p }))}\n onSubmit={() => void createCapability()}\n onCancel={() => setWizard('none')}\n busy={wizardBusy}\n error={wizardError}\n />\n )}\n\n {loading && items.length === 0 && (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">Loading…</div>\n )}\n\n {!loading && items.length === 0 && (\n <Card>\n <CardContent className=\"py-10 text-center text-sm text-muted-foreground\">\n No capabilities yet. Click New to create one.\n </CardContent>\n </Card>\n )}\n\n {items.map(({ capability, variants }) => (\n <button\n key={capability.id}\n type=\"button\"\n onClick={() => { setSelectedId(capability.id); setWizard('none') }}\n className={`w-full rounded-md border p-3 text-left transition-colors ${selectedId === capability.id ? 'border-primary/50 bg-primary/5' : 'border-border bg-card/60 hover:bg-accent'}`}\n >\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"outline\" className={`text-[10px] ${kindBadgeClass(capability.kind)}`}>\n {capability.kind}\n </Badge>\n <span className=\"text-xs font-medium font-mono truncate flex-1\">{capability.slug}</span>\n <span className=\"text-[10px] text-muted-foreground shrink-0\">{variants.length}v</span>\n </div>\n {capability.title && (\n <div className=\"mt-1 text-xs text-muted-foreground truncate\">{capability.title}</div>\n )}\n <div className=\"mt-1 flex items-center gap-1.5\">\n <span className={`inline-block h-1.5 w-1.5 rounded-full ${capability.status === 'active' ? 'bg-emerald-500' : 'bg-zinc-500'}`} />\n <span className=\"text-[10px] text-muted-foreground\">{capability.status}</span>\n </div>\n </button>\n ))}\n </div>\n\n {/* Right pane: selected capability detail */}\n {cap ? (\n <div className=\"space-y-3\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className={kindBadgeClass(cap.kind)}>{cap.kind}</Badge>\n <span className=\"font-mono text-base\">{cap.slug}</span>\n {cap.title && <span className=\"text-sm font-normal text-muted-foreground\">· {cap.title}</span>}\n <Badge variant={cap.status === 'active' ? 'secondary' : 'outline'} className=\"ml-auto text-[10px]\">\n {cap.status}\n </Badge>\n </CardTitle>\n </CardHeader>\n {cap.description && (\n <CardContent className=\"pt-0 text-sm text-muted-foreground\">{cap.description}</CardContent>\n )}\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n Variants\n <span className=\"text-xs font-normal text-muted-foreground\">({selected?.variants.length ?? 0})</span>\n <div className=\"ml-auto flex items-center gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => openWizard(wizard === 'import-github' ? 'none' : 'import-github')}\n >\n <Plus className=\"h-3 w-3\" />\n GitHub\n </Button>\n {cap.kind === 'mcp' && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => openWizard(wizard === 'import-mcp' ? 'none' : 'import-mcp')}\n >\n <Plus className=\"h-3 w-3\" />\n MCP Server\n </Button>\n )}\n </div>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {wizard === 'import-github' && (\n <ImportGithubForm\n form={importGh}\n onChange={(p) => setImportGh((f) => ({ ...f, ...p }))}\n onSubmit={() => void importFromGithub()}\n onCancel={() => setWizard('none')}\n busy={wizardBusy}\n error={wizardError}\n />\n )}\n {wizard === 'import-mcp' && (\n <ImportMcpForm\n form={importMcp}\n onChange={(p) => setImportMcp((f) => ({ ...f, ...p }))}\n onSubmit={() => void importMcpServer()}\n onCancel={() => setWizard('none')}\n busy={wizardBusy}\n error={wizardError}\n />\n )}\n {selected && selected.variants.length === 0 && wizard === 'none' && (\n <div className=\"py-6 text-center text-sm text-muted-foreground\">\n No variants yet. Add one via GitHub import{cap.kind === 'mcp' ? ' or MCP Server' : ''}.\n </div>\n )}\n {selected?.variants.map((variant) => (\n <VariantRow\n key={variant.id}\n variant={variant}\n onToggle={(id, enabled) => void toggleVariant(id, enabled)}\n onUpdate={(id) => void updateVariantRef(id)}\n onValidate={(id) => void validateVariant(id)}\n busy={variantBusy}\n />\n ))}\n </CardContent>\n </Card>\n </div>\n ) : (\n !loading && (\n <Card>\n <CardContent className=\"py-16 text-center text-sm text-muted-foreground\">\n Select a capability to view details and variants.\n </CardContent>\n </Card>\n )\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registry-CXWyOtpr.js","names":[],"sources":["../../dashboard/src/components/views/registry.tsx"],"sourcesContent":["import { useEffect, useState } from 'react'\nimport { errMessage } from 'agent-relay-sdk/types'\nimport { api } from '@/lib/api'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { Switch } from '@/components/ui/switch'\nimport { PackagePlus, RefreshCw, Plus, Trash2, CheckCircle, AlertCircle, Clock, Download, Play } from 'lucide-react'\nimport type { ProvisioningAssetKind, ProvisioningCapability, ProvisioningAsset, ProvisioningCapabilityWithVariants } from '@/types'\nimport { getKnownProviderIds, getProviderBadgeClass } from '@/lib/provider-display'\n\n// Registry view (#561) — browse and manage the provisioning capability registry.\n// Two-pane: left = capability list + filters, right = selected capability detail + variants.\n\ntype KindFilter = 'all' | ProvisioningAssetKind\n\nconst KIND_OPTIONS: Array<{ value: KindFilter; label: string }> = [\n { value: 'all', label: 'All' },\n { value: 'skill', label: 'Skills' },\n { value: 'plugin', label: 'Plugins' },\n { value: 'mcp', label: 'MCP' },\n]\n\nconst PROVIDER_OPTIONS = ['any', ...getKnownProviderIds()] as const\ntype Provider = string\n\nfunction kindBadgeClass(kind: ProvisioningAssetKind): string {\n if (kind === 'skill') return 'bg-blue-500/10 text-blue-400 border-blue-500/30'\n if (kind === 'plugin') return 'bg-violet-500/10 text-violet-400 border-violet-500/30'\n return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30'\n}\n\nfunction providerBadgeClass(provider: string): string {\n if (provider === 'any') return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/30'\n return getProviderBadgeClass(provider)\n}\n\nfunction approvalBadgeClass(status?: string): string {\n if (status === 'approved') return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30'\n if (status === 'rejected') return 'bg-red-500/10 text-red-400 border-red-500/30'\n return 'bg-yellow-500/10 text-yellow-400 border-yellow-500/30'\n}\n\nfunction ValidationIcon({ status }: { status?: string }) {\n if (status === 'valid') return <CheckCircle className=\"h-3.5 w-3.5 text-emerald-400\" />\n if (status === 'invalid') return <AlertCircle className=\"h-3.5 w-3.5 text-red-400\" />\n return <Clock className=\"h-3.5 w-3.5 text-zinc-400\" />\n}\n\n// Form state for creating a new capability\ninterface NewCapabilityForm {\n kind: ProvisioningAssetKind\n slug: string\n title: string\n}\n\n// Form state for GitHub import wizard\ninterface ImportGithubForm {\n githubUrl: string\n ref: string\n subpath: string\n provider: string\n versionConstraint: string\n}\n\n// Form state for MCP server variant\ninterface ImportMcpForm {\n command: string\n args: string\n url: string\n approvalStatus: string\n versionConstraint: string\n}\n\nconst DEFAULT_NEW_CAP: NewCapabilityForm = { kind: 'skill', slug: '', title: '' }\nconst DEFAULT_IMPORT_GH: ImportGithubForm = { githubUrl: '', ref: 'main', subpath: '', provider: 'any', versionConstraint: '' }\nconst DEFAULT_IMPORT_MCP: ImportMcpForm = { command: '', args: '', url: '', approvalStatus: 'pending', versionConstraint: '' }\n\ntype InlineWizard = 'none' | 'new-capability' | 'import-github' | 'import-mcp'\n\nfunction VariantRow({\n variant,\n onToggle,\n onUpdate,\n onValidate,\n busy,\n}: {\n variant: ProvisioningAsset\n onToggle: (id: string, enabled: boolean) => void\n onUpdate: (id: string) => void\n onValidate: (id: string) => void\n busy: string | null\n}) {\n const isBusy = busy === variant.id\n\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3 space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className={`text-[10px] ${providerBadgeClass(variant.provider)}`}>\n {variant.provider}\n </Badge>\n <span className=\"text-xs font-medium font-mono\">{variant.name}</span>\n {variant.approvalStatus && (\n <Badge variant=\"outline\" className={`text-[10px] ${approvalBadgeClass(variant.approvalStatus)}`}>\n {variant.approvalStatus}\n </Badge>\n )}\n <span className=\"text-[10px] text-muted-foreground flex items-center gap-1 ml-auto\">\n <ValidationIcon status={variant.validationStatus} />\n {variant.validationStatus ?? 'unvalidated'}\n </span>\n <Switch\n size=\"sm\"\n checked={variant.enabled}\n disabled={isBusy}\n onCheckedChange={(v) => onToggle(variant.id, v)}\n />\n </div>\n {(variant.sourceUrl || variant.sourceRef) && (\n <div className=\"text-[10px] text-muted-foreground font-mono truncate\" title={variant.sourceUrl}>\n {variant.sourceUrl}{variant.sourceRef ? `@${variant.resolvedRef ?? variant.sourceRef}` : ''}\n {variant.sourceSubpath ? ` · ${variant.sourceSubpath}` : ''}\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-6 text-xs\"\n disabled={isBusy || !variant.sourceRef}\n onClick={() => onUpdate(variant.id)}\n title=\"Pull latest from source ref\"\n >\n <Download className=\"h-3 w-3 mr-1\" />\n Update ref\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-6 text-xs\"\n disabled={isBusy}\n onClick={() => onValidate(variant.id)}\n >\n <Play className=\"h-3 w-3 mr-1\" />\n Validate\n </Button>\n </div>\n </div>\n )\n}\n\nfunction NewCapabilityForm({\n form,\n onChange,\n onSubmit,\n onCancel,\n busy,\n error,\n}: {\n form: NewCapabilityForm\n onChange: (patch: Partial<NewCapabilityForm>) => void\n onSubmit: () => void\n onCancel: () => void\n busy: boolean\n error: string | null\n}) {\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3 space-y-3\">\n <div className=\"text-sm font-medium\">New Capability</div>\n {error && <div className=\"text-xs text-destructive\">{error}</div>}\n <div>\n <Label className=\"text-xs\">Kind</Label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n value={form.kind}\n onChange={(e) => onChange({ kind: e.target.value as ProvisioningAssetKind })}\n >\n <option value=\"skill\">Skill</option>\n <option value=\"plugin\">Plugin</option>\n <option value=\"mcp\">MCP Server</option>\n </select>\n </div>\n <div>\n <Label className=\"text-xs\">Slug</Label>\n <Input\n value={form.slug}\n onChange={(e) => onChange({ slug: e.target.value })}\n placeholder=\"my-capability\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div>\n <Label className=\"text-xs\">Title (optional)</Label>\n <Input\n value={form.title}\n onChange={(e) => onChange({ title: e.target.value })}\n placeholder=\"My Capability\"\n className=\"mt-1 text-sm\"\n />\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={onSubmit} disabled={busy || !form.slug.trim()}>\n {busy ? 'Creating…' : 'Create'}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={onCancel}>Cancel</Button>\n </div>\n </div>\n )\n}\n\nfunction ImportGithubForm({\n form,\n onChange,\n onSubmit,\n onCancel,\n busy,\n error,\n}: {\n form: ImportGithubForm\n onChange: (patch: Partial<ImportGithubForm>) => void\n onSubmit: () => void\n onCancel: () => void\n busy: boolean\n error: string | null\n}) {\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3 space-y-3\">\n <div className=\"text-sm font-medium\">Import from GitHub</div>\n {error && <div className=\"text-xs text-destructive\">{error}</div>}\n <div>\n <Label className=\"text-xs\">GitHub URL</Label>\n <Input\n value={form.githubUrl}\n onChange={(e) => onChange({ githubUrl: e.target.value })}\n placeholder=\"https://github.com/owner/repo\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <Label className=\"text-xs\">Ref</Label>\n <Input\n value={form.ref}\n onChange={(e) => onChange({ ref: e.target.value })}\n placeholder=\"main\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div>\n <Label className=\"text-xs\">Subpath (optional)</Label>\n <Input\n value={form.subpath}\n onChange={(e) => onChange({ subpath: e.target.value })}\n placeholder=\"skills/my-skill\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <Label className=\"text-xs\">Provider</Label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n value={form.provider}\n onChange={(e) => onChange({ provider: e.target.value })}\n >\n {PROVIDER_OPTIONS.map((p) => <option key={p} value={p}>{p}</option>)}\n </select>\n </div>\n <div>\n <Label className=\"text-xs\">Version constraint (optional)</Label>\n <Input\n value={form.versionConstraint}\n onChange={(e) => onChange({ versionConstraint: e.target.value })}\n placeholder=\">=1.0.0\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={onSubmit} disabled={busy || !form.githubUrl.trim() || !form.ref.trim()}>\n {busy ? 'Importing…' : 'Import'}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={onCancel}>Cancel</Button>\n </div>\n </div>\n )\n}\n\nfunction ImportMcpForm({\n form,\n onChange,\n onSubmit,\n onCancel,\n busy,\n error,\n}: {\n form: ImportMcpForm\n onChange: (patch: Partial<ImportMcpForm>) => void\n onSubmit: () => void\n onCancel: () => void\n busy: boolean\n error: string | null\n}) {\n return (\n <div className=\"rounded-md border border-border bg-card/60 p-3 space-y-3\">\n <div className=\"text-sm font-medium\">Add MCP Server Variant</div>\n {error && <div className=\"text-xs text-destructive\">{error}</div>}\n <div>\n <Label className=\"text-xs\">Command (for stdio type)</Label>\n <Input\n value={form.command}\n onChange={(e) => onChange({ command: e.target.value })}\n placeholder=\"npx -y @modelcontextprotocol/server-foo\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div>\n <Label className=\"text-xs\">Args (space-separated, overrides command split)</Label>\n <Input\n value={form.args}\n onChange={(e) => onChange({ args: e.target.value })}\n placeholder=\"--port 3000\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div>\n <Label className=\"text-xs\">URL (for http/sse type)</Label>\n <Input\n value={form.url}\n onChange={(e) => onChange({ url: e.target.value })}\n placeholder=\"http://localhost:3000/mcp\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <Label className=\"text-xs\">Approval status</Label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n value={form.approvalStatus}\n onChange={(e) => onChange({ approvalStatus: e.target.value })}\n >\n <option value=\"pending\">Pending</option>\n <option value=\"approved\">Approved</option>\n </select>\n </div>\n <div>\n <Label className=\"text-xs\">Version constraint (optional)</Label>\n <Input\n value={form.versionConstraint}\n onChange={(e) => onChange({ versionConstraint: e.target.value })}\n placeholder=\">=1.0.0\"\n className=\"mt-1 text-sm font-mono\"\n />\n </div>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={onSubmit} disabled={busy || (!form.command.trim() && !form.url.trim())}>\n {busy ? 'Adding…' : 'Add MCP Variant'}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={onCancel}>Cancel</Button>\n </div>\n </div>\n )\n}\n\nexport function RegistryView() {\n const [items, setItems] = useState<ProvisioningCapabilityWithVariants[]>([])\n const [kindFilter, setKindFilter] = useState<KindFilter>('all')\n const [selectedId, setSelectedId] = useState<string>('')\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [wizard, setWizard] = useState<InlineWizard>('none')\n const [wizardBusy, setWizardBusy] = useState(false)\n const [wizardError, setWizardError] = useState<string | null>(null)\n const [variantBusy, setVariantBusy] = useState<string | null>(null)\n const [newCap, setNewCap] = useState<NewCapabilityForm>(DEFAULT_NEW_CAP)\n const [importGh, setImportGh] = useState<ImportGithubForm>(DEFAULT_IMPORT_GH)\n const [importMcp, setImportMcp] = useState<ImportMcpForm>(DEFAULT_IMPORT_MCP)\n\n async function load() {\n setError(null)\n try {\n const params = kindFilter !== 'all' ? `?kind=${kindFilter}` : ''\n const res = await api<{ capabilities: ProvisioningCapabilityWithVariants[] }>('GET', `/provisioning/capabilities${params}`)\n setItems(res.capabilities)\n if (!selectedId && res.capabilities.length > 0) {\n setSelectedId(res.capabilities[0]!.capability.id)\n }\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setLoading(false)\n }\n }\n\n // Initial load + kind filter changes\n useEffect(() => {\n setLoading(true)\n void load()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [kindFilter])\n\n const selected = items.find((i) => i.capability.id === selectedId) ?? null\n\n function openWizard(w: InlineWizard) {\n setWizard(w)\n setWizardError(null)\n setWizardBusy(false)\n setNewCap(DEFAULT_NEW_CAP)\n setImportGh(DEFAULT_IMPORT_GH)\n setImportMcp(DEFAULT_IMPORT_MCP)\n }\n\n async function createCapability() {\n setWizardBusy(true)\n setWizardError(null)\n try {\n await api<ProvisioningCapability>('POST', '/provisioning/capabilities', {\n kind: newCap.kind,\n slug: newCap.slug.trim(),\n title: newCap.title.trim() || undefined,\n })\n setWizard('none')\n await load()\n } catch (e) {\n setWizardError(errMessage(e))\n } finally {\n setWizardBusy(false)\n }\n }\n\n async function importFromGithub() {\n if (!selected) return\n setWizardBusy(true)\n setWizardError(null)\n try {\n await api('POST', `/provisioning/capabilities/${encodeURIComponent(selected.capability.id)}/variants/import`, {\n githubUrl: importGh.githubUrl.trim(),\n ref: importGh.ref.trim(),\n subpath: importGh.subpath.trim() || undefined,\n provider: importGh.provider,\n versionConstraint: importGh.versionConstraint.trim() || undefined,\n })\n setWizard('none')\n await load()\n } catch (e) {\n setWizardError(errMessage(e))\n } finally {\n setWizardBusy(false)\n }\n }\n\n async function importMcpServer() {\n if (!selected) return\n setWizardBusy(true)\n setWizardError(null)\n try {\n // Build server spec: command/args or url\n const server: Record<string, unknown> = {}\n if (importMcp.url.trim()) {\n server.type = 'http'\n server.url = importMcp.url.trim()\n } else {\n server.type = 'stdio'\n const parts = importMcp.command.trim().split(/\\s+/)\n server.command = parts[0]\n const extraArgs = importMcp.args.trim() ? importMcp.args.trim().split(/\\s+/) : parts.slice(1)\n if (extraArgs.length > 0) server.args = extraArgs\n }\n await api('POST', `/provisioning/capabilities/${encodeURIComponent(selected.capability.id)}/variants/mcp`, {\n server,\n approvalStatus: importMcp.approvalStatus,\n versionConstraint: importMcp.versionConstraint.trim() || undefined,\n })\n setWizard('none')\n await load()\n } catch (e) {\n setWizardError(errMessage(e))\n } finally {\n setWizardBusy(false)\n }\n }\n\n async function toggleVariant(id: string, enabled: boolean) {\n setVariantBusy(id)\n try {\n await api('PATCH', `/provisioning/variants/${encodeURIComponent(id)}`, { enabled })\n setItems((prev) =>\n prev.map((item) => ({\n ...item,\n variants: item.variants.map((v) => (v.id === id ? { ...v, enabled } : v)),\n }))\n )\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setVariantBusy(null)\n }\n }\n\n async function updateVariantRef(id: string) {\n const variant = selected?.variants.find((v) => v.id === id)\n if (!variant?.sourceRef) return\n setVariantBusy(id)\n try {\n await api('POST', `/provisioning/variants/${encodeURIComponent(id)}/update`, {\n ref: variant.sourceRef,\n subpath: variant.sourceSubpath,\n })\n await load()\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setVariantBusy(null)\n }\n }\n\n async function validateVariant(id: string) {\n setVariantBusy(id)\n try {\n await api('POST', `/provisioning/variants/${encodeURIComponent(id)}/validate`, {})\n await load()\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setVariantBusy(null)\n }\n }\n\n const cap = selected?.capability\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <PackagePlus className=\"size-5 text-primary\" />\n <h1 className=\"text-lg font-semibold\">Registry</h1>\n <span className=\"text-xs text-muted-foreground\">provisioning capability registry · #561</span>\n <div className=\"ml-auto flex items-center gap-2\">\n {KIND_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => setKindFilter(opt.value)}\n className={`rounded-md border px-2.5 py-1 text-xs transition-colors ${kindFilter === opt.value ? 'border-primary/40 bg-primary/10 text-primary' : 'border-border text-muted-foreground hover:bg-accent'}`}\n >\n {opt.label}\n </button>\n ))}\n <Button variant=\"outline\" size=\"sm\" onClick={() => void load()}>\n <RefreshCw className=\"size-4\" />\n </Button>\n </div>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error}</div>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 xl:grid-cols-[300px_1fr]\">\n {/* Left pane: capability list */}\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-muted-foreground font-medium\">{items.length} capabilities</span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => openWizard('new-capability')}\n >\n <Plus className=\"h-3 w-3\" />\n New\n </Button>\n </div>\n\n {wizard === 'new-capability' && (\n <NewCapabilityForm\n form={newCap}\n onChange={(p) => setNewCap((f) => ({ ...f, ...p }))}\n onSubmit={() => void createCapability()}\n onCancel={() => setWizard('none')}\n busy={wizardBusy}\n error={wizardError}\n />\n )}\n\n {loading && items.length === 0 && (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">Loading…</div>\n )}\n\n {!loading && items.length === 0 && (\n <Card>\n <CardContent className=\"py-10 text-center text-sm text-muted-foreground\">\n No capabilities yet. Click New to create one.\n </CardContent>\n </Card>\n )}\n\n {items.map(({ capability, variants }) => (\n <button\n key={capability.id}\n type=\"button\"\n onClick={() => { setSelectedId(capability.id); setWizard('none') }}\n className={`w-full rounded-md border p-3 text-left transition-colors ${selectedId === capability.id ? 'border-primary/50 bg-primary/5' : 'border-border bg-card/60 hover:bg-accent'}`}\n >\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"outline\" className={`text-[10px] ${kindBadgeClass(capability.kind)}`}>\n {capability.kind}\n </Badge>\n <span className=\"text-xs font-medium font-mono truncate flex-1\">{capability.slug}</span>\n <span className=\"text-[10px] text-muted-foreground shrink-0\">{variants.length}v</span>\n </div>\n {capability.title && (\n <div className=\"mt-1 text-xs text-muted-foreground truncate\">{capability.title}</div>\n )}\n <div className=\"mt-1 flex items-center gap-1.5\">\n <span className={`inline-block h-1.5 w-1.5 rounded-full ${capability.status === 'active' ? 'bg-emerald-500' : 'bg-zinc-500'}`} />\n <span className=\"text-[10px] text-muted-foreground\">{capability.status}</span>\n </div>\n </button>\n ))}\n </div>\n\n {/* Right pane: selected capability detail */}\n {cap ? (\n <div className=\"space-y-3\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className={kindBadgeClass(cap.kind)}>{cap.kind}</Badge>\n <span className=\"font-mono text-base\">{cap.slug}</span>\n {cap.title && <span className=\"text-sm font-normal text-muted-foreground\">· {cap.title}</span>}\n <Badge variant={cap.status === 'active' ? 'secondary' : 'outline'} className=\"ml-auto text-[10px]\">\n {cap.status}\n </Badge>\n </CardTitle>\n </CardHeader>\n {cap.description && (\n <CardContent className=\"pt-0 text-sm text-muted-foreground\">{cap.description}</CardContent>\n )}\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n Variants\n <span className=\"text-xs font-normal text-muted-foreground\">({selected?.variants.length ?? 0})</span>\n <div className=\"ml-auto flex items-center gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => openWizard(wizard === 'import-github' ? 'none' : 'import-github')}\n >\n <Plus className=\"h-3 w-3\" />\n GitHub\n </Button>\n {cap.kind === 'mcp' && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 text-xs gap-1\"\n onClick={() => openWizard(wizard === 'import-mcp' ? 'none' : 'import-mcp')}\n >\n <Plus className=\"h-3 w-3\" />\n MCP Server\n </Button>\n )}\n </div>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {wizard === 'import-github' && (\n <ImportGithubForm\n form={importGh}\n onChange={(p) => setImportGh((f) => ({ ...f, ...p }))}\n onSubmit={() => void importFromGithub()}\n onCancel={() => setWizard('none')}\n busy={wizardBusy}\n error={wizardError}\n />\n )}\n {wizard === 'import-mcp' && (\n <ImportMcpForm\n form={importMcp}\n onChange={(p) => setImportMcp((f) => ({ ...f, ...p }))}\n onSubmit={() => void importMcpServer()}\n onCancel={() => setWizard('none')}\n busy={wizardBusy}\n error={wizardError}\n />\n )}\n {selected && selected.variants.length === 0 && wizard === 'none' && (\n <div className=\"py-6 text-center text-sm text-muted-foreground\">\n No variants yet. Add one via GitHub import{cap.kind === 'mcp' ? ' or MCP Server' : ''}.\n </div>\n )}\n {selected?.variants.map((variant) => (\n <VariantRow\n key={variant.id}\n variant={variant}\n onToggle={(id, enabled) => void toggleVariant(id, enabled)}\n onUpdate={(id) => void updateVariantRef(id)}\n onValidate={(id) => void validateVariant(id)}\n busy={variantBusy}\n />\n ))}\n </CardContent>\n </Card>\n </div>\n ) : (\n !loading && (\n <Card>\n <CardContent className=\"py-16 text-center text-sm text-muted-foreground\">\n Select a capability to view details and variants.\n </CardContent>\n </Card>\n )\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"+cAkBM,EAA4D,CAChE,CAAE,MAAO,MAAO,MAAO,MAAO,CAC9B,CAAE,MAAO,QAAS,MAAO,SAAU,CACnC,CAAE,MAAO,SAAU,MAAO,UAAW,CACrC,CAAE,MAAO,MAAO,MAAO,MAAO,CAC/B,CAEK,EAAmB,CAAC,MAAO,GAAG,GAAqB,CAAC,CAG1D,SAAS,EAAe,EAAqC,CAG3D,OAFI,IAAS,QAAgB,kDACzB,IAAS,SAAiB,wDACvB,2DAGT,SAAS,EAAmB,EAA0B,CAEpD,OADI,IAAa,MAAc,kDACxB,EAAsB,EAAS,CAGxC,SAAS,EAAmB,EAAyB,CAGnD,OAFI,IAAW,WAAmB,2DAC9B,IAAW,WAAmB,+CAC3B,wDAGT,SAAS,EAAe,CAAE,UAA+B,CAGvD,OAFI,IAAW,SAAgB,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,+BAAiC,CAAA,CACnF,IAAW,WAAkB,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,2BAA6B,CAAA,EAC9E,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,4BAA8B,CAAA,CA4BxD,IAAM,EAAqC,CAAE,KAAM,QAAS,KAAM,GAAI,MAAO,GAAI,CAC3E,EAAsC,CAAE,UAAW,GAAI,IAAK,OAAQ,QAAS,GAAI,SAAU,MAAO,kBAAmB,GAAI,CACzH,EAAoC,CAAE,QAAS,GAAI,KAAM,GAAI,IAAK,GAAI,eAAgB,UAAW,kBAAmB,GAAI,CAI9H,SAAS,GAAW,CAClB,UACA,WACA,WACA,aACA,QAOC,CACD,IAAM,EAAS,IAAS,EAAQ,GAEhC,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oEAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,eAAe,EAAmB,EAAQ,SAAS,YACpF,EAAQ,SACH,CAAA,EACR,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,yCAAiC,EAAQ,KAAY,CAAA,CACpE,EAAQ,iBACP,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,eAAe,EAAmB,EAAQ,eAAe,YAC1F,EAAQ,eACH,CAAA,EAEV,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,6EAAhB,EACE,EAAA,EAAA,KAAC,EAAD,CAAgB,OAAQ,EAAQ,iBAAoB,CAAA,CACnD,EAAQ,kBAAoB,cACxB,IACP,EAAA,EAAA,KAAC,EAAD,CACE,KAAK,KACL,QAAS,EAAQ,QACjB,SAAU,EACV,gBAAkB,GAAM,EAAS,EAAQ,GAAI,EAAE,CAC/C,CAAA,CACE,IACJ,EAAQ,WAAa,EAAQ,aAC7B,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uDAAuD,MAAO,EAAQ,mBAArF,CACG,EAAQ,UAAW,EAAQ,UAAY,IAAI,EAAQ,aAAe,EAAQ,YAAc,GACxF,EAAQ,cAAgB,MAAM,EAAQ,gBAAkB,GACrD,IAER,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,MAAC,EAAD,CACE,QAAQ,UACR,KAAK,KACL,UAAU,cACV,SAAU,GAAU,CAAC,EAAQ,UAC7B,YAAe,EAAS,EAAQ,GAAG,CACnC,MAAM,uCANR,EAQE,EAAA,EAAA,KAAC,EAAD,CAAU,UAAU,eAAiB,CAAA,CAAA,aAE9B,IACT,EAAA,EAAA,MAAC,EAAD,CACE,QAAQ,UACR,KAAK,KACL,UAAU,cACV,SAAU,EACV,YAAe,EAAW,EAAQ,GAAG,UALvC,EAOE,EAAA,EAAA,KAAC,EAAD,CAAM,UAAU,eAAiB,CAAA,CAAA,WAE1B,GACL,GACF,GAIV,SAAS,EAAkB,CACzB,OACA,WACA,WACA,WACA,OACA,SAQC,CACD,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oEAAf,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,+BAAsB,iBAAoB,CAAA,CACxD,IAAS,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,oCAA4B,EAAY,CAAA,EACjE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,OAAY,CAAA,EACvC,EAAA,EAAA,MAAC,SAAD,CACE,UAAU,+EACV,MAAO,EAAK,KACZ,SAAW,GAAM,EAAS,CAAE,KAAM,EAAE,OAAO,MAAgC,CAAC,UAH9E,EAKE,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,iBAAQ,QAAc,CAAA,EACpC,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,kBAAS,SAAe,CAAA,EACtC,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,eAAM,aAAmB,CAAA,CAChC,GACL,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,OAAY,CAAA,EACvC,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,KACZ,SAAW,GAAM,EAAS,CAAE,KAAM,EAAE,OAAO,MAAO,CAAC,CACnD,YAAY,gBACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,mBAAwB,CAAA,EACnD,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,MACZ,SAAW,GAAM,EAAS,CAAE,MAAO,EAAE,OAAO,MAAO,CAAC,CACpD,YAAY,gBACZ,UAAU,eACV,CAAA,CACE,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,sBAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,KAAK,QAAS,EAAU,SAAU,GAAQ,CAAC,EAAK,KAAK,MAAM,UACrE,EAAO,YAAc,SACf,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,KAAK,QAAQ,UAAU,QAAS,WAAU,SAAe,CAAA,CAClE,GACF,GAIV,SAAS,EAAiB,CACxB,OACA,WACA,WACA,WACA,OACA,SAQC,CACD,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oEAAf,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,+BAAsB,qBAAwB,CAAA,CAC5D,IAAS,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,oCAA4B,EAAY,CAAA,EACjE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,aAAkB,CAAA,EAC7C,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,UACZ,SAAW,GAAM,EAAS,CAAE,UAAW,EAAE,OAAO,MAAO,CAAC,CACxD,YAAY,gCACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kCAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,MAAW,CAAA,EACtC,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,IACZ,SAAW,GAAM,EAAS,CAAE,IAAK,EAAE,OAAO,MAAO,CAAC,CAClD,YAAY,OACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,qBAA0B,CAAA,EACrD,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,QACZ,SAAW,GAAM,EAAS,CAAE,QAAS,EAAE,OAAO,MAAO,CAAC,CACtD,YAAY,kBACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,CACF,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kCAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,WAAgB,CAAA,EAC3C,EAAA,EAAA,KAAC,SAAD,CACE,UAAU,+EACV,MAAO,EAAK,SACZ,SAAW,GAAM,EAAS,CAAE,SAAU,EAAE,OAAO,MAAO,CAAC,UAEtD,EAAiB,IAAK,IAAM,EAAA,EAAA,KAAC,SAAD,CAAgB,MAAO,WAAI,EAAW,CAAzB,EAAyB,CAAC,CAC7D,CAAA,CACL,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,gCAAqC,CAAA,EAChE,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,kBACZ,SAAW,GAAM,EAAS,CAAE,kBAAmB,EAAE,OAAO,MAAO,CAAC,CAChE,YAAY,UACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,CACF,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,sBAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,KAAK,QAAS,EAAU,SAAU,GAAQ,CAAC,EAAK,UAAU,MAAM,EAAI,CAAC,EAAK,IAAI,MAAM,UAC9F,EAAO,aAAe,SAChB,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,KAAK,QAAQ,UAAU,QAAS,WAAU,SAAe,CAAA,CAClE,GACF,GAIV,SAAS,EAAc,CACrB,OACA,WACA,WACA,WACA,OACA,SAQC,CACD,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oEAAf,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,+BAAsB,yBAA4B,CAAA,CAChE,IAAS,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,oCAA4B,EAAY,CAAA,EACjE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,2BAAgC,CAAA,EAC3D,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,QACZ,SAAW,GAAM,EAAS,CAAE,QAAS,EAAE,OAAO,MAAO,CAAC,CACtD,YAAY,0CACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,kDAAuD,CAAA,EAClF,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,KACZ,SAAW,GAAM,EAAS,CAAE,KAAM,EAAE,OAAO,MAAO,CAAC,CACnD,YAAY,cACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,0BAA+B,CAAA,EAC1D,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,IACZ,SAAW,GAAM,EAAS,CAAE,IAAK,EAAE,OAAO,MAAO,CAAC,CAClD,YAAY,4BACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kCAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,kBAAuB,CAAA,EAClD,EAAA,EAAA,MAAC,SAAD,CACE,UAAU,+EACV,MAAO,EAAK,eACZ,SAAW,GAAM,EAAS,CAAE,eAAgB,EAAE,OAAO,MAAO,CAAC,UAH/D,EAKE,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,mBAAU,UAAgB,CAAA,EACxC,EAAA,EAAA,KAAC,SAAD,CAAQ,MAAM,oBAAW,WAAiB,CAAA,CACnC,GACL,CAAA,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,mBAAU,gCAAqC,CAAA,EAChE,EAAA,EAAA,KAAC,EAAD,CACE,MAAO,EAAK,kBACZ,SAAW,GAAM,EAAS,CAAE,kBAAmB,EAAE,OAAO,MAAO,CAAC,CAChE,YAAY,UACZ,UAAU,yBACV,CAAA,CACE,CAAA,CAAA,CACF,IACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,sBAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,KAAK,QAAS,EAAU,SAAU,GAAS,CAAC,EAAK,QAAQ,MAAM,EAAI,CAAC,EAAK,IAAI,MAAM,UAC7F,EAAO,UAAY,kBACb,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,KAAK,QAAQ,UAAU,QAAS,WAAU,SAAe,CAAA,CAClE,GACF,GAIV,SAAgB,GAAe,CAC7B,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,UAA2D,EAAE,CAAC,CACtE,CAAC,EAAY,IAAA,EAAA,EAAA,UAAsC,MAAM,CACzD,CAAC,EAAY,IAAA,EAAA,EAAA,UAAkC,GAAG,CAClD,CAAC,EAAS,IAAA,EAAA,EAAA,UAAuB,GAAK,CACtC,CAAC,EAAO,IAAA,EAAA,EAAA,UAAoC,KAAK,CACjD,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAoC,OAAO,CACpD,CAAC,EAAY,IAAA,EAAA,EAAA,UAA0B,GAAM,CAC7C,CAAC,EAAa,IAAA,EAAA,EAAA,UAA0C,KAAK,CAC7D,CAAC,EAAa,IAAA,EAAA,EAAA,UAA0C,KAAK,CAC7D,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAyC,EAAgB,CAClE,CAAC,EAAU,IAAA,EAAA,EAAA,UAA0C,EAAkB,CACvE,CAAC,EAAW,IAAA,EAAA,EAAA,UAAwC,EAAmB,CAE7E,eAAe,GAAO,CACpB,EAAS,KAAK,CACd,GAAI,CAEF,IAAM,EAAM,MAAM,EAA4D,MAAO,6BADtE,IAAe,MAAgC,GAAxB,SAAS,MAC4E,CAC3H,EAAS,EAAI,aAAa,CACtB,CAAC,GAAc,EAAI,aAAa,OAAS,GAC3C,EAAc,EAAI,aAAa,GAAI,WAAW,GAAG,OAE5C,EAAG,CACV,EAAS,EAAW,EAAE,CAAC,QACf,CACR,EAAW,GAAM,GAKrB,EAAA,EAAA,eAAgB,CACd,EAAW,GAAK,CAChB,GAAW,EAEV,CAAC,EAAW,CAAC,CAEhB,IAAM,EAAW,EAAM,KAAM,GAAM,EAAE,WAAW,KAAO,EAAW,EAAI,KAEtE,SAAS,EAAW,EAAiB,CACnC,EAAU,EAAE,CACZ,EAAe,KAAK,CACpB,EAAc,GAAM,CACpB,EAAU,EAAgB,CAC1B,EAAY,EAAkB,CAC9B,EAAa,EAAmB,CAGlC,eAAe,GAAmB,CAChC,EAAc,GAAK,CACnB,EAAe,KAAK,CACpB,GAAI,CACF,MAAM,EAA4B,OAAQ,6BAA8B,CACtE,KAAM,EAAO,KACb,KAAM,EAAO,KAAK,MAAM,CACxB,MAAO,EAAO,MAAM,MAAM,EAAI,IAAA,GAC/B,CAAC,CACF,EAAU,OAAO,CACjB,MAAM,GAAM,OACL,EAAG,CACV,EAAe,EAAW,EAAE,CAAC,QACrB,CACR,EAAc,GAAM,EAIxB,eAAe,GAAmB,CAC3B,KAEL,CADA,EAAc,GAAK,CACnB,EAAe,KAAK,CACpB,GAAI,CACF,MAAM,EAAI,OAAQ,8BAA8B,mBAAmB,EAAS,WAAW,GAAG,CAAC,kBAAmB,CAC5G,UAAW,EAAS,UAAU,MAAM,CACpC,IAAK,EAAS,IAAI,MAAM,CACxB,QAAS,EAAS,QAAQ,MAAM,EAAI,IAAA,GACpC,SAAU,EAAS,SACnB,kBAAmB,EAAS,kBAAkB,MAAM,EAAI,IAAA,GACzD,CAAC,CACF,EAAU,OAAO,CACjB,MAAM,GAAM,OACL,EAAG,CACV,EAAe,EAAW,EAAE,CAAC,QACrB,CACR,EAAc,GAAM,GAIxB,eAAe,GAAkB,CAC1B,KAEL,CADA,EAAc,GAAK,CACnB,EAAe,KAAK,CACpB,GAAI,CAEF,IAAM,EAAkC,EAAE,CAC1C,GAAI,EAAU,IAAI,MAAM,CACtB,EAAO,KAAO,OACd,EAAO,IAAM,EAAU,IAAI,MAAM,KAC5B,CACL,EAAO,KAAO,QACd,IAAM,EAAQ,EAAU,QAAQ,MAAM,CAAC,MAAM,MAAM,CACnD,EAAO,QAAU,EAAM,GACvB,IAAM,EAAY,EAAU,KAAK,MAAM,CAAG,EAAU,KAAK,MAAM,CAAC,MAAM,MAAM,CAAG,EAAM,MAAM,EAAE,CACzF,EAAU,OAAS,IAAG,EAAO,KAAO,GAE1C,MAAM,EAAI,OAAQ,8BAA8B,mBAAmB,EAAS,WAAW,GAAG,CAAC,eAAgB,CACzG,SACA,eAAgB,EAAU,eAC1B,kBAAmB,EAAU,kBAAkB,MAAM,EAAI,IAAA,GAC1D,CAAC,CACF,EAAU,OAAO,CACjB,MAAM,GAAM,OACL,EAAG,CACV,EAAe,EAAW,EAAE,CAAC,QACrB,CACR,EAAc,GAAM,GAIxB,eAAe,EAAc,EAAY,EAAkB,CACzD,EAAe,EAAG,CAClB,GAAI,CACF,MAAM,EAAI,QAAS,0BAA0B,mBAAmB,EAAG,GAAI,CAAE,UAAS,CAAC,CACnF,EAAU,GACR,EAAK,IAAK,IAAU,CAClB,GAAG,EACH,SAAU,EAAK,SAAS,IAAK,GAAO,EAAE,KAAO,EAAK,CAAE,GAAG,EAAG,UAAS,CAAG,EAAG,CAC1E,EAAE,CACJ,OACM,EAAG,CACV,EAAS,EAAW,EAAE,CAAC,QACf,CACR,EAAe,KAAK,EAIxB,eAAe,EAAiB,EAAY,CAC1C,IAAM,EAAU,GAAU,SAAS,KAAM,GAAM,EAAE,KAAO,EAAG,CACtD,MAAS,UACd,GAAe,EAAG,CAClB,GAAI,CACF,MAAM,EAAI,OAAQ,0BAA0B,mBAAmB,EAAG,CAAC,SAAU,CAC3E,IAAK,EAAQ,UACb,QAAS,EAAQ,cAClB,CAAC,CACF,MAAM,GAAM,OACL,EAAG,CACV,EAAS,EAAW,EAAE,CAAC,QACf,CACR,EAAe,KAAK,GAIxB,eAAe,GAAgB,EAAY,CACzC,EAAe,EAAG,CAClB,GAAI,CACF,MAAM,EAAI,OAAQ,0BAA0B,mBAAmB,EAAG,CAAC,WAAY,EAAE,CAAC,CAClF,MAAM,GAAM,OACL,EAAG,CACV,EAAS,EAAW,EAAE,CAAC,QACf,CACR,EAAe,KAAK,EAIxB,IAAM,EAAM,GAAU,WAEtB,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,yBAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,sBAAwB,CAAA,EAC/C,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,iCAAwB,WAAa,CAAA,EACnD,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,yCAAgC,0CAA8C,CAAA,EAC9F,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2CAAf,CACG,EAAa,IAAK,IACjB,EAAA,EAAA,KAAC,SAAD,CAEE,KAAK,SACL,YAAe,EAAc,EAAI,MAAM,CACvC,UAAW,2DAA2D,IAAe,EAAI,MAAQ,+CAAiD,iEAEjJ,EAAI,MACE,CANF,EAAI,MAMF,CACT,EACF,EAAA,EAAA,KAAC,EAAD,CAAQ,QAAQ,UAAU,KAAK,KAAK,YAAe,KAAK,GAAM,WAC5D,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,SAAW,CAAA,CACzB,CAAA,CACL,GACF,GAEL,IACC,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,wGAAgG,EAAY,CAAA,EAG7H,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2DAAf,EAEE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qBAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,EACE,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,qDAAhB,CAA6D,EAAM,OAAO,gBAAoB,IAC9F,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,KACL,QAAQ,UACR,UAAU,oBACV,YAAe,EAAW,iBAAiB,UAJ7C,EAME,EAAA,EAAA,KAAC,EAAD,CAAM,UAAU,UAAY,CAAA,CAAA,MAErB,GACL,GAEL,IAAW,mBACV,EAAA,EAAA,KAAC,EAAD,CACE,KAAM,EACN,SAAW,GAAM,EAAW,IAAO,CAAE,GAAG,EAAG,GAAG,EAAG,EAAE,CACnD,aAAgB,KAAK,GAAkB,CACvC,aAAgB,EAAU,OAAO,CACjC,KAAM,EACN,MAAO,EACP,CAAA,CAGH,GAAW,EAAM,SAAW,IAC3B,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,0DAAiD,WAAc,CAAA,CAG/E,CAAC,GAAW,EAAM,SAAW,IAC5B,EAAA,EAAA,KAAC,EAAD,CAAA,UACE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,2DAAkD,gDAE3D,CAAA,CACT,CAAA,CAGR,EAAM,KAAK,CAAE,aAAY,eACxB,EAAA,EAAA,MAAC,SAAD,CAEE,KAAK,SACL,YAAe,CAAE,EAAc,EAAW,GAAG,CAAE,EAAU,OAAO,EAChE,UAAW,4DAA4D,IAAe,EAAW,GAAK,iCAAmC,sDAJ3I,EAME,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,eAAe,EAAe,EAAW,KAAK,YAC/E,EAAW,KACN,CAAA,EACR,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,yDAAiD,EAAW,KAAY,CAAA,EACxF,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,sDAAhB,CAA8D,EAAS,OAAO,IAAQ,GAClF,GACL,EAAW,QACV,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,uDAA+C,EAAW,MAAY,CAAA,EAEvF,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0CAAf,EACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAW,yCAAyC,EAAW,SAAW,SAAW,iBAAmB,gBAAmB,CAAA,EACjI,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,6CAAqC,EAAW,OAAc,CAAA,CAC1E,GACC,EAnBF,EAAW,GAmBT,CACT,CACE,GAGL,GACC,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,qBAAf,EACE,EAAA,EAAA,MAAC,EAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAA,UACE,EAAA,EAAA,MAAC,EAAD,CAAW,UAAU,6CAArB,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,QAAQ,UAAU,UAAW,EAAe,EAAI,KAAK,UAAG,EAAI,KAAa,CAAA,EAChF,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,+BAAuB,EAAI,KAAY,CAAA,CACtD,EAAI,QAAS,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,qDAAhB,CAA4D,KAAG,EAAI,MAAa,IAC9F,EAAA,EAAA,KAAC,EAAD,CAAO,QAAS,EAAI,SAAW,SAAW,YAAc,UAAW,UAAU,+BAC1E,EAAI,OACC,CAAA,CACE,GACD,CAAA,CACZ,EAAI,cACH,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,8CAAsC,EAAI,YAA0B,CAAA,CAExF,CAAA,CAAA,EAEP,EAAA,EAAA,MAAC,EAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAA,UACE,EAAA,EAAA,MAAC,EAAD,CAAW,UAAU,6CAArB,CAAyD,YAEvD,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,qDAAhB,CAA4D,IAAE,GAAU,SAAS,QAAU,EAAE,IAAQ,IACrG,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2CAAf,EACE,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,KACL,QAAQ,UACR,UAAU,oBACV,YAAe,EAAW,IAAW,gBAAkB,OAAS,gBAAgB,UAJlF,EAME,EAAA,EAAA,KAAC,EAAD,CAAM,UAAU,UAAY,CAAA,CAAA,SAErB,GACR,EAAI,OAAS,QACZ,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,KACL,QAAQ,UACR,UAAU,oBACV,YAAe,EAAW,IAAW,aAAe,OAAS,aAAa,UAJ5E,EAME,EAAA,EAAA,KAAC,EAAD,CAAM,UAAU,UAAY,CAAA,CAAA,aAErB,GAEP,GACI,GACD,CAAA,EACb,EAAA,EAAA,MAAC,EAAD,CAAa,UAAU,qBAAvB,CACG,IAAW,kBACV,EAAA,EAAA,KAAC,EAAD,CACE,KAAM,EACN,SAAW,GAAM,EAAa,IAAO,CAAE,GAAG,EAAG,GAAG,EAAG,EAAE,CACrD,aAAgB,KAAK,GAAkB,CACvC,aAAgB,EAAU,OAAO,CACjC,KAAM,EACN,MAAO,EACP,CAAA,CAEH,IAAW,eACV,EAAA,EAAA,KAAC,EAAD,CACE,KAAM,EACN,SAAW,GAAM,EAAc,IAAO,CAAE,GAAG,EAAG,GAAG,EAAG,EAAE,CACtD,aAAgB,KAAK,GAAiB,CACtC,aAAgB,EAAU,OAAO,CACjC,KAAM,EACN,MAAO,EACP,CAAA,CAEH,GAAY,EAAS,SAAS,SAAW,GAAK,IAAW,SACxD,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0DAAf,CAAgE,6CACnB,EAAI,OAAS,MAAQ,iBAAmB,GAAG,IAClF,GAEP,GAAU,SAAS,IAAK,IACvB,EAAA,EAAA,KAAC,GAAD,CAEW,UACT,UAAW,EAAI,IAAY,KAAK,EAAc,EAAI,EAAQ,CAC1D,SAAW,GAAO,KAAK,EAAiB,EAAG,CAC3C,WAAa,GAAO,KAAK,GAAgB,EAAG,CAC5C,KAAM,EACN,CANK,EAAQ,GAMb,CACF,CACU,GACT,CAAA,CAAA,CACH,GAEN,CAAC,IACC,EAAA,EAAA,KAAC,EAAD,CAAA,UACE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,2DAAkD,oDAE3D,CAAA,CACT,CAAA,CAGP,GACF"}
|