feat(adapters): external adapter plugin system with dynamic UI parser

- Plugin loader: install/reload/remove/reinstall external adapters
  from npm packages or local directories
- Plugin store persisted at ~/.paperclip/adapter-plugins.json
- Self-healing UI parser resolution with version caching
- UI: Adapter Manager page, dynamic loader, display registry
  with humanized names for unknown adapter types
- Dev watch: exclude adapter-plugins dir from tsx watcher
  to prevent mid-request server restarts during reinstall
- All consumer fallbacks use getAdapterLabel() for consistent display
- AdapterTypeDropdown uses controlled open state for proper close behavior
- Remove hermes-local from built-in UI (externalized to plugin)
- Add docs for external adapters and UI parser contract
This commit is contained in:
HenkDz 2026-03-31 20:21:13 +01:00
parent f8452a4520
commit 14d59da316
72 changed files with 4102 additions and 585 deletions

View file

@ -1,5 +1,5 @@
import { useQuery } from "@tanstack/react-query";
import { Clock3, FlaskConical, Puzzle, Settings, SlidersHorizontal } from "lucide-react";
import { Clock3, Cpu, FlaskConical, Puzzle, Settings, SlidersHorizontal } from "lucide-react";
import { NavLink } from "@/lib/router";
import { pluginsApi } from "@/api/plugins";
import { queryKeys } from "@/lib/queryKeys";
@ -26,6 +26,7 @@ export function InstanceSidebar() {
<SidebarNavItem to="/instance/settings/heartbeats" label="Heartbeats" icon={Clock3} end />
<SidebarNavItem to="/instance/settings/experimental" label="Experimental" icon={FlaskConical} />
<SidebarNavItem to="/instance/settings/plugins" label="Plugins" icon={Puzzle} />
<SidebarNavItem to="/instance/settings/adapters" label="Adapters" icon={Cpu} />
{(plugins ?? []).length > 0 ? (
<div className="ml-4 mt-1 flex flex-col gap-0.5 border-l border-border/70 pl-3">
{(plugins ?? []).map((plugin) => (