Fix Compound Engineering plugin install failing from Settings
Installing the Compound Engineering plugin from Settings → Built-in Plugins failed with "Plugin manifest not found": the dashboard's bundled-plugin fallback set (BUNDLED_PLUGIN_IDS in routes.ts) was missing fusion-plugin-compound-engineering, so when the relative ./plugins/... path missed the server cwd, the bundled lookup was never attempted. fusion-plugin-cli-printing-press had the same gap. - Add both ids to the dashboard's BUNDLED_PLUGIN_IDS fallback set. - Stage fusion-plugin-compound-engineering into dist/plugins via bundlePluginEntry in the CLI tsup config so packaged installs can resolve the bundled copy (every other bundled plugin already had a staging block). - Add route tests that actually exercise the bundled fallback (the existing ones let cwd resolution succeed, so the fallback path was untested). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,3 +6,4 @@ Fix the workflow graph editor opening invisibly and bundle the Compound Engineer
|
||||
|
||||
- The "Graph editor" button now actually shows the editor: its overlay was rendered without the `open` class, leaving it `display: none`, so opening it looked like the workflow steps view was just dismissed.
|
||||
- `fusion-plugin-compound-engineering` and `fusion-plugin-roadmap` are now listed in the dashboard's built-in plugins, so they appear under Settings → Built-in Plugins (they were implemented and registered but missing from the list).
|
||||
- Installing Compound Engineering (and CLI Printing Press) from Settings → Built-in Plugins no longer fails with "Plugin manifest not found": both ids are now in the dashboard's bundled-plugin fallback set, and the Compound Engineering plugin is staged into `dist/plugins/` so packaged installs can resolve it.
|
||||
|
||||
@@ -41,6 +41,8 @@ const reportsPluginSrc = join(__dirname, "..", "..", "plugins", "fusion-plugin-r
|
||||
const reportsPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-reports");
|
||||
const cliPrintingPressPluginSrc = join(__dirname, "..", "..", "plugins", "fusion-plugin-cli-printing-press");
|
||||
const cliPrintingPressPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-cli-printing-press");
|
||||
const compoundEngineeringPluginSrc = join(__dirname, "..", "..", "plugins", "fusion-plugin-compound-engineering");
|
||||
const compoundEngineeringPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-compound-engineering");
|
||||
const dashboardClientStub = `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -241,6 +243,12 @@ const cliBuildConfig = {
|
||||
destDir: roadmapPluginDest,
|
||||
});
|
||||
|
||||
await bundlePluginEntry({
|
||||
pluginId: "fusion-plugin-compound-engineering",
|
||||
srcDir: compoundEngineeringPluginSrc,
|
||||
destDir: compoundEngineeringPluginDest,
|
||||
});
|
||||
|
||||
if (existsSync(reportsPluginDest)) {
|
||||
rmSync(reportsPluginDest, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -549,6 +549,74 @@ describe("POST /api/plugins mode:install — bundled plugin path fallback", () =
|
||||
);
|
||||
});
|
||||
|
||||
it("installs bundled compound engineering plugin when relative path misses cwd", async () => {
|
||||
const bundledManifest = {
|
||||
...VALID_MANIFEST,
|
||||
id: "fusion-plugin-compound-engineering",
|
||||
name: "Compound Engineering",
|
||||
};
|
||||
// Only the staged bundled copy under dist/plugins exists — the
|
||||
// cwd-relative path must miss so the bundled fallback is exercised.
|
||||
mockExistsSync.mockImplementation((p: string) => p.includes("dist/plugins/fusion-plugin-compound-engineering/manifest.json"));
|
||||
mockAccess.mockImplementation((p: string) => {
|
||||
if (p.includes("dist/plugins/fusion-plugin-compound-engineering")) return Promise.resolve();
|
||||
return Promise.reject(new Error("not found"));
|
||||
});
|
||||
mockReadFile.mockResolvedValue(JSON.stringify(bundledManifest));
|
||||
(pluginStore.registerPlugin as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
...INSTALLED_PLUGIN,
|
||||
id: "fusion-plugin-compound-engineering",
|
||||
name: "Compound Engineering",
|
||||
});
|
||||
|
||||
const res = await REQUEST(buildApp(), "POST", "/api/plugins", {
|
||||
mode: "install",
|
||||
path: "./plugins/fusion-plugin-compound-engineering",
|
||||
});
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
expect(pluginStore.registerPlugin).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
manifest: expect.objectContaining({ id: "fusion-plugin-compound-engineering" }),
|
||||
path: expect.stringContaining("fusion-plugin-compound-engineering"),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("installs bundled cli printing press plugin when relative path misses cwd", async () => {
|
||||
const bundledManifest = {
|
||||
...VALID_MANIFEST,
|
||||
id: "fusion-plugin-cli-printing-press",
|
||||
name: "CLI Printing Press",
|
||||
};
|
||||
// Only the staged bundled copy under dist/plugins exists — the
|
||||
// cwd-relative path must miss so the bundled fallback is exercised.
|
||||
mockExistsSync.mockImplementation((p: string) => p.includes("dist/plugins/fusion-plugin-cli-printing-press/manifest.json"));
|
||||
mockAccess.mockImplementation((p: string) => {
|
||||
if (p.includes("dist/plugins/fusion-plugin-cli-printing-press")) return Promise.resolve();
|
||||
return Promise.reject(new Error("not found"));
|
||||
});
|
||||
mockReadFile.mockResolvedValue(JSON.stringify(bundledManifest));
|
||||
(pluginStore.registerPlugin as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
...INSTALLED_PLUGIN,
|
||||
id: "fusion-plugin-cli-printing-press",
|
||||
name: "CLI Printing Press",
|
||||
});
|
||||
|
||||
const res = await REQUEST(buildApp(), "POST", "/api/plugins", {
|
||||
mode: "install",
|
||||
path: "./plugins/fusion-plugin-cli-printing-press",
|
||||
});
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
expect(pluginStore.registerPlugin).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
manifest: expect.objectContaining({ id: "fusion-plugin-cli-printing-press" }),
|
||||
path: expect.stringContaining("fusion-plugin-cli-printing-press"),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns 404 with helpful message when local and bundled paths are unresolved", async () => {
|
||||
mockExistsSync.mockReturnValue(false);
|
||||
mockAccess.mockRejectedValue(new Error("not found"));
|
||||
|
||||
@@ -97,6 +97,8 @@ const BUNDLED_PLUGIN_IDS = new Set([
|
||||
"fusion-plugin-openclaw-runtime",
|
||||
"fusion-plugin-paperclip-runtime",
|
||||
"fusion-plugin-cursor-runtime",
|
||||
"fusion-plugin-cli-printing-press",
|
||||
"fusion-plugin-compound-engineering",
|
||||
]);
|
||||
|
||||
function extractBundledPluginId(pathInput: string): string | null {
|
||||
|
||||
Reference in New Issue
Block a user