FN-6071: add plugin registry browsing and install actions

Add a curated plugin registry surface to the dashboard plugin manager.

- expose GET /api/plugins/registry with search, category filtering, and installed-state annotations from the dashboard registry manifest
- add registry fetch/install support to the dashboard plugin manager UI, including search, install/manage actions, and related tests
- document plugin registry entries and registry installability in plugin authoring docs and shared concepts

Files changed:
 CONCEPTS.md                                        |   6 +
 docs/PLUGIN_AUTHORING.md                           |  23 ++
 packages/dashboard/app/api/legacy.ts               |  36 +++
 .../dashboard/app/components/PluginManager.css     | 185 ++++++++++++++-
 .../dashboard/app/components/PluginManager.tsx     | 162 ++++++++++++-
 .../PluginManager.install-browse.test.tsx          |   5 +-
 .../__tests__/PluginManager.registry.test.tsx      | 260 +++++++++++++++++++++
 .../components/__tests__/PluginManager.test.tsx    |   3 +-
 .../__tests__/PluginManager.toggle.test.tsx        |   5 +-
 .../src/__tests__/routes-plugin-registry.test.ts   | 156 +++++++++++++
 packages/dashboard/src/plugin-routes.ts            | 115 +++++++++
 packages/dashboard/src/registry-manifest.json      | 143 ++++++++++++
 12 files changed, 1091 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-6071

Fusion-Task-Lineage: 72c7bdfd-5832-4323-b675-63d0680167d8
This commit is contained in:
gsxdsm
2026-06-09 04:27:37 -07:00
parent f51e29bb29
commit f8200adb71
12 changed files with 1091 additions and 8 deletions

View File

@@ -20,6 +20,7 @@ import type {
WorkflowStepResult,
PluginInstallation,
PluginSetupCheckResult,
PluginState,
PluginUiSlotDefinition,
PluginUiContributionDefinition,
PluginDashboardViewDefinition,
@@ -9073,11 +9074,46 @@ export function resetAgentBudget(agentId: string, projectId?: string): Promise<v
// ── Plugin Management ────────────────────────────────────────────────────────
export interface RegistryPluginEntry {
id: string;
name: string;
description: string;
version: string;
author: string;
category: "runtime" | "integration";
npmPackage?: string;
path?: string;
homepage?: string;
tags?: string[];
installed: boolean;
state?: PluginState;
installedVersion?: string;
canInstall: boolean;
}
/** Fetch all installed plugins */
export async function fetchPlugins(projectId?: string): Promise<PluginInstallation[]> {
return api<PluginInstallation[]>(withProjectId("/plugins", projectId));
}
/** Fetch curated registry plugins with installed-state metadata */
export async function fetchPluginRegistry(
query?: string,
category?: string,
projectId?: string,
): Promise<RegistryPluginEntry[]> {
const params = new URLSearchParams();
if (query?.trim()) {
params.set("q", query.trim());
}
if (category?.trim()) {
params.set("category", category.trim());
}
const suffix = params.size > 0 ? `?${params.toString()}` : "";
const response = await api<{ plugins: RegistryPluginEntry[] }>(withProjectId(`/plugins/registry${suffix}`, projectId));
return response.plugins;
}
/** Fetch a single plugin by ID */
export async function fetchPluginDetail(id: string, projectId?: string): Promise<PluginInstallation> {
return api<PluginInstallation>(withProjectId(`/plugins/${encodeURIComponent(id)}`, projectId));