feat(FN-3565): add plugin management CLI, loader, runner, and dashboard rou

This merge adds a complete plugin management system to Fusion: a new `fn plugin` CLI command for installing/removing plugins, a plugin loader in core, a plugin runner in engine, and dashboard routes for plugin management UI, along with a plugin management guide in docs. It also documents task evalua

Fusion-Task-Id: FN-3565
This commit is contained in:
Fusion
2026-05-06 04:15:13 -07:00
committed by gsxdsm
parent 27973a8345
commit 8f812e2f89
15 changed files with 994 additions and 7 deletions

View File

@@ -25,6 +25,7 @@ import type {
PluginPromptSurface,
PluginSetupManifest,
PluginSetupHooks,
PluginSetupCheckResult,
} from "@fusion/core";
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
import { Type } from "@mariozechner/pi-ai";
@@ -371,6 +372,55 @@ export class PluginRunner {
return this.cachedSetupInfo.setups;
}
async checkPluginSetup(pluginId: string): Promise<PluginSetupCheckResult> {
try {
return await this.withTimeout(
this.options.pluginLoader.checkPluginSetup(pluginId),
this.hookTimeoutMs,
`Setup check for plugin ${pluginId} timed out`,
);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
this.log.warn(`Setup check failed for plugin ${pluginId}: ${message}`);
return { status: "error", error: message };
}
}
async installPluginSetup(pluginId: string): Promise<{ success: boolean; error?: string }> {
try {
await this.options.pluginLoader.installPluginSetup(pluginId);
this.invalidateSetupCache();
return { success: true };
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
this.log.warn(`Setup install failed for plugin ${pluginId}: ${message}`);
return { success: false, error: message };
}
}
async uninstallPluginSetup(pluginId: string): Promise<{ success: boolean; error?: string }> {
try {
await this.options.pluginLoader.uninstallPluginSetup(pluginId);
this.invalidateSetupCache();
return { success: true };
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
this.log.warn(`Setup uninstall failed for plugin ${pluginId}: ${message}`);
return { success: false, error: message };
}
}
async getSetupStatuses(): Promise<Array<{ pluginId: string; manifest: PluginSetupManifest; status?: PluginSetupCheckResult }>> {
const setupInfo = this.getPluginSetupInfo();
return Promise.all(
setupInfo.map(async ({ pluginId, manifest }) => ({
pluginId,
manifest,
status: await this.checkPluginSetup(pluginId),
})),
);
}
getPromptContributionsForSurface(surface: PluginPromptSurface): Array<{
pluginId: string;
contribution: PluginPromptContribution;