feat(FN-3071): add droid runtime plugin with event bridge, process manageme

This merge introduces a droid runtime plugin system with event bridge and process management (FN-3228), stabilizes plugin workspace verification gates and suppresses placeholder plugin shells in onboarding/settings flows (FN-3071), tokenizes danger/error styling in task detail components (FN-3350),

Fusion-Task-Id: FN-3071
This commit is contained in:
Fusion
2026-05-04 04:24:55 -07:00
committed by gsxdsm
parent bfd92456e9
commit 813bdad2e1
14 changed files with 343 additions and 17 deletions

View File

@@ -197,6 +197,29 @@ describe("GET /plugins", () => {
});
});
it("includes the Droid plugin in installed plugin listings", async () => {
(pluginStore.listPlugins as ReturnType<typeof vi.fn>).mockResolvedValueOnce([
{
...FAKE_PLUGIN,
id: "fusion-plugin-droid-runtime",
name: "Droid Runtime Plugin",
version: "0.1.0",
},
]);
const res = await GET(buildApp(), "/api/plugins");
expect(res.status).toBe(200);
expect(pluginStore.listPlugins).toHaveBeenCalledWith({});
expect(res.body).toEqual([
expect.objectContaining({
id: "fusion-plugin-droid-runtime",
name: "Droid Runtime Plugin",
version: "0.1.0",
}),
]);
});
it("filters plugins by enabled status", async () => {
const res = await GET(buildApp(), "/api/plugins?enabled=true");

View File

@@ -92,6 +92,7 @@ function createMockPluginLoader(overrides: Partial<PluginLoader> = {}): PluginLo
getPluginTools: vi.fn().mockReturnValue([]),
getPluginRoutes: vi.fn().mockReturnValue([]),
getPluginUiSlots: vi.fn().mockReturnValue([]),
getPluginRuntimes: vi.fn().mockReturnValue([]),
getPluginDashboardViews: vi.fn().mockReturnValue([]),
loadAllPlugins: vi.fn().mockResolvedValue({ loaded: 0, errors: 0 }),
stopAllPlugins: vi.fn().mockResolvedValue(undefined),
@@ -846,4 +847,111 @@ describe("GET /api/plugins/ui-slots", () => {
expect(res.status).toBe(200);
expect(res.body).toEqual([]);
});
it("surfaces Droid plugin slot registrations through standard loader aggregation", async () => {
const droidSlots = [
{
pluginId: "fusion-plugin-droid-runtime",
slot: {
slotId: "settings-provider-card",
label: "Droid CLI Provider",
componentPath: "./components/settings-provider-card.js",
order: 5,
},
},
{
pluginId: "fusion-plugin-droid-runtime",
slot: {
slotId: "onboarding-provider-card",
label: "Droid CLI Provider",
componentPath: "./components/onboarding-provider-card.js",
placement: "after-default",
},
},
];
(pluginLoader.getPluginUiSlots as ReturnType<typeof vi.fn>).mockReturnValue(droidSlots);
const res = await performGet(buildApp(), "/api/plugins/ui-slots");
expect(res.status).toBe(200);
expect(pluginLoader.getPluginUiSlots).toHaveBeenCalledTimes(1);
expect(res.body).toEqual([
{
pluginId: "fusion-plugin-droid-runtime",
slot: {
slotId: "settings-provider-card",
surface: "settings-provider-card",
label: "Droid CLI Provider",
componentPath: "./components/settings-provider-card.js",
order: 5,
},
},
{
pluginId: "fusion-plugin-droid-runtime",
slot: {
slotId: "onboarding-provider-card",
surface: "onboarding-provider-card",
label: "Droid CLI Provider",
componentPath: "./components/onboarding-provider-card.js",
placement: "after-default",
order: null,
},
},
]);
});
});
describe("GET /api/plugins/runtimes", () => {
let pluginStore: PluginStore;
let pluginLoader: PluginLoader;
let store: TaskStore;
beforeEach(() => {
vi.clearAllMocks();
pluginStore = createMockPluginStore();
pluginLoader = createMockPluginLoader();
store = createMockTaskStore({
getPluginStore: vi.fn().mockReturnValue(pluginStore),
});
});
function buildApp() {
const app = express();
app.use(express.json());
app.use("/api", createApiRoutes(store, { pluginStore, pluginLoader }));
return app;
}
it("includes Droid runtime metadata from plugin loader aggregation", async () => {
(pluginLoader.getPluginRuntimes as ReturnType<typeof vi.fn>).mockReturnValue([
{
pluginId: "fusion-plugin-droid-runtime",
runtime: {
metadata: {
runtimeId: "droid",
name: "Droid Runtime",
description: "Drives the Droid CLI for Fusion agents",
version: "0.1.0",
},
factory: vi.fn(),
},
},
]);
const res = await performGet(buildApp(), "/api/plugins/runtimes");
expect(res.status).toBe(200);
expect(pluginLoader.getPluginRuntimes).toHaveBeenCalledTimes(1);
expect(res.body).toEqual(
expect.arrayContaining([
expect.objectContaining({
pluginId: "fusion-plugin-droid-runtime",
runtimeId: "droid",
name: "Droid Runtime",
description: "Drives the Droid CLI for Fusion agents",
version: "0.1.0",
}),
]),
);
});
});