feat(dashboard): add Plugins page with enable/disable, auth status, install/remove

- New PluginsPage.tsx: full plugin management UI (list, enable/disable,
  install from git, remove, git pull updates, provider picker)
- Backend: dashboard_set_agent_plugin_enabled now also toggles the
  plugin's toolset in platform_toolsets so enabling actually makes
  tools visible in agent sessions
- Backend: /api/dashboard/plugins/hub returns auth_required + auth_command
  per plugin (checks tool registry check_fn)
- Frontend: auth_required shown as Badge + CommandBlock with copy-able
  auth command
- Fix: Select overflow in providers card (min-w-0 grid cells, removed
  truncate/overflow-hidden that clipped dropdown)
- Refactor: _install_plugin_core extracted for non-interactive reuse,
  PluginOperationError for structured error handling
- i18n: en/zh/types updated with all new plugin page strings
This commit is contained in:
Austin Pickett 2026-04-30 17:41:10 -04:00
parent e5dad4ac57
commit e2a4905606
10 changed files with 1521 additions and 189 deletions

View file

@ -259,6 +259,46 @@ export const api = {
rescanPlugins: () =>
fetchJSON<{ ok: boolean; count: number }>("/api/dashboard/plugins/rescan"),
getPluginsHub: () => fetchJSON<PluginsHubResponse>("/api/dashboard/plugins/hub"),
installAgentPlugin: (body: AgentPluginInstallRequest) =>
fetchJSON<AgentPluginInstallResponse>("/api/dashboard/agent-plugins/install", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...body }),
}),
enableAgentPlugin: (name: string) =>
fetchJSON<{ ok: boolean; name: string; unchanged?: boolean }>(
`/api/dashboard/agent-plugins/${encodeURIComponent(name)}/enable`,
{ method: "POST" },
),
disableAgentPlugin: (name: string) =>
fetchJSON<{ ok: boolean; name: string; unchanged?: boolean }>(
`/api/dashboard/agent-plugins/${encodeURIComponent(name)}/disable`,
{ method: "POST" },
),
updateAgentPlugin: (name: string) =>
fetchJSON<AgentPluginUpdateResponse>(
`/api/dashboard/agent-plugins/${encodeURIComponent(name)}/update`,
{ method: "POST" },
),
removeAgentPlugin: (name: string) =>
fetchJSON<{ ok: boolean; name: string }>(
`/api/dashboard/agent-plugins/${encodeURIComponent(name)}`,
{ method: "DELETE" },
),
savePluginProviders: (body: PluginProvidersPutRequest) =>
fetchJSON<{ ok: boolean }>("/api/dashboard/plugin-providers", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
}),
// Dashboard themes
getThemes: () =>
fetchJSON<DashboardThemesResponse>("/api/dashboard/themes"),
@ -668,8 +708,66 @@ export interface PluginManifestResponse {
override?: string;
hidden?: boolean;
};
slots?: string[];
entry: string;
css?: string | null;
has_api: boolean;
source: string;
}
export interface HubAgentPluginRow {
name: string;
version: string;
description: string;
source: string;
runtime_status: "disabled" | "enabled" | "inactive";
has_dashboard_manifest: boolean;
dashboard_manifest: PluginManifestResponse | null;
path: string;
can_remove: boolean;
can_update_git: boolean;
auth_required: boolean;
auth_command: string;
}
export interface PluginsHubProviders {
memory_provider: string;
memory_options: Array<{ name: string; description: string }>;
context_engine: string;
context_options: Array<{ name: string; description: string }>;
}
export interface PluginsHubResponse {
plugins: HubAgentPluginRow[];
orphan_dashboard_plugins: PluginManifestResponse[];
providers: PluginsHubProviders;
}
export interface AgentPluginInstallRequest {
identifier: string;
force?: boolean;
enable?: boolean;
}
export interface AgentPluginInstallResponse {
ok: boolean;
plugin_name?: string;
warnings?: string[];
missing_env?: string[];
after_install_path?: string | null;
enabled?: boolean;
error?: string;
}
export interface AgentPluginUpdateResponse {
ok: boolean;
name?: string;
output?: string;
unchanged?: boolean;
error?: string;
}
export interface PluginProvidersPutRequest {
memory_provider?: string;
context_engine?: string;
}