feat(FN-3718): add droid runtime to tsup bundle with asset gate

Stages the droid runtime as a bundled plugin in the CLI's tsup configuration, gates the bundle bootstrap on droid asset availability, and adds tests asserting correct staging behavior in bundle output.

Fusion-Task-Id: FN-3718
This commit is contained in:
Fusion
2026-05-07 19:14:43 -07:00
committed by gsxdsm
parent a732ebb8c9
commit 9743dab1d9
5 changed files with 48 additions and 3 deletions

View File

@@ -0,0 +1,8 @@
---
"@runfusion/fusion": patch
---
Stage `fusion-plugin-droid-runtime` (including its `mcp-schema-server.cjs`
bridge asset) into the published CLI tarball, mirroring the
`fusion-plugin-openclaw-runtime` build pipeline. The droid runtime plugin
is now bundled and asserted by the bundle-output test suite.

View File

@@ -16,6 +16,7 @@ import {
bundlePath, bundlePath,
clientIndexPath, clientIndexPath,
dashboardClientStubMarker, dashboardClientStubMarker,
droidPluginMcpServerPath,
hasBuiltDashboardAssets, hasBuiltDashboardAssets,
openclawMcpSchemaServerPath, openclawMcpSchemaServerPath,
} from "./bundle-output-helpers"; } from "./bundle-output-helpers";
@@ -39,11 +40,21 @@ describe("hasBuiltDashboardAssets", () => {
expect(hasBuiltDashboardAssets()).toBe(false); expect(hasBuiltDashboardAssets()).toBe(false);
}); });
it("returns false when droid mcp-schema-server.cjs is missing", () => {
state.existingPaths.add(bundlePath);
state.existingPaths.add(clientIndexPath);
state.existingPaths.add(cursorPluginManifestPath);
state.existingPaths.add(openclawMcpSchemaServerPath);
expect(hasBuiltDashboardAssets()).toBe(false);
});
it("returns true when all required assets exist and dashboard stub marker is absent", () => { it("returns true when all required assets exist and dashboard stub marker is absent", () => {
state.existingPaths.add(bundlePath); state.existingPaths.add(bundlePath);
state.existingPaths.add(clientIndexPath); state.existingPaths.add(clientIndexPath);
state.existingPaths.add(cursorPluginManifestPath); state.existingPaths.add(cursorPluginManifestPath);
state.existingPaths.add(openclawMcpSchemaServerPath); state.existingPaths.add(openclawMcpSchemaServerPath);
state.existingPaths.add(droidPluginMcpServerPath);
expect(hasBuiltDashboardAssets()).toBe(true); expect(hasBuiltDashboardAssets()).toBe(true);
}); });
@@ -53,6 +64,7 @@ describe("hasBuiltDashboardAssets", () => {
state.existingPaths.add(clientIndexPath); state.existingPaths.add(clientIndexPath);
state.existingPaths.add(cursorPluginManifestPath); state.existingPaths.add(cursorPluginManifestPath);
state.existingPaths.add(openclawMcpSchemaServerPath); state.existingPaths.add(openclawMcpSchemaServerPath);
state.existingPaths.add(droidPluginMcpServerPath);
state.indexHtml = dashboardClientStubMarker; state.indexHtml = dashboardClientStubMarker;
expect(hasBuiltDashboardAssets()).toBe(false); expect(hasBuiltDashboardAssets()).toBe(false);

View File

@@ -14,6 +14,13 @@ export const openclawMcpSchemaServerPath = join(
"fusion-plugin-openclaw-runtime", "fusion-plugin-openclaw-runtime",
"mcp-schema-server.cjs", "mcp-schema-server.cjs",
); );
export const droidPluginMcpServerPath = join(
cliRoot,
"dist",
"plugins",
"fusion-plugin-droid-runtime",
"mcp-schema-server.cjs",
);
export const dashboardClientStubMarker = "Dashboard assets not built"; export const dashboardClientStubMarker = "Dashboard assets not built";
@@ -40,7 +47,8 @@ export function hasBuiltDashboardAssets(): boolean {
!existsSync(bundlePath) || !existsSync(bundlePath) ||
!existsSync(clientIndexPath) || !existsSync(clientIndexPath) ||
!existsSync(cursorPluginManifestPath) || !existsSync(cursorPluginManifestPath) ||
!existsSync(openclawMcpSchemaServerPath) !existsSync(openclawMcpSchemaServerPath) ||
!existsSync(droidPluginMcpServerPath)
) { ) {
return false; return false;
} }

View File

@@ -189,6 +189,17 @@ describe("CLI bundle output", () => {
expect(existsSync(join(stagedRoot, "mcp-schema-server.cjs"))).toBe(true); expect(existsSync(join(stagedRoot, "mcp-schema-server.cjs"))).toBe(true);
}); });
it("dist/plugins/fusion-plugin-droid-runtime/ is staged with required bridge assets", () => {
const stagedRoot = join(cliRoot, "dist", "plugins", "fusion-plugin-droid-runtime");
const manifestPath = join(stagedRoot, "manifest.json");
expect(existsSync(manifestPath)).toBe(true);
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")) as { id?: string };
expect(manifest.id).toBe("fusion-plugin-droid-runtime");
expect(existsSync(join(stagedRoot, "bundled.js"))).toBe(true);
expect(existsSync(join(stagedRoot, "mcp-schema-server.cjs"))).toBe(true);
});
it("dist/plugins/fusion-plugin-cursor-runtime/ is staged with a valid manifest", () => { it("dist/plugins/fusion-plugin-cursor-runtime/ is staged with a valid manifest", () => {
const stagedRoot = join(cliRoot, "dist", "plugins", "fusion-plugin-cursor-runtime"); const stagedRoot = join(cliRoot, "dist", "plugins", "fusion-plugin-cursor-runtime");
const manifestPath = join(stagedRoot, "manifest.json"); const manifestPath = join(stagedRoot, "manifest.json");

View File

@@ -13,8 +13,14 @@ const RUNTIME_PLUGIN_IDS = [
"fusion-plugin-openclaw-runtime", "fusion-plugin-openclaw-runtime",
"fusion-plugin-paperclip-runtime", "fusion-plugin-paperclip-runtime",
"fusion-plugin-cursor-runtime", "fusion-plugin-cursor-runtime",
"fusion-plugin-droid-runtime",
] as const; ] as const;
const RUNTIME_PLUGINS_WITH_MCP_SCHEMA_SERVER = new Set([
"fusion-plugin-openclaw-runtime",
"fusion-plugin-droid-runtime",
]);
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
const dashboardClientSrc = join(__dirname, "..", "dashboard", "dist", "client"); const dashboardClientSrc = join(__dirname, "..", "dashboard", "dist", "client");
const dashboardClientDest = join(__dirname, "dist", "client"); const dashboardClientDest = join(__dirname, "dist", "client");
@@ -214,11 +220,11 @@ export default defineConfig({
logLevel: "warning", logLevel: "warning",
}); });
if (pluginId === "fusion-plugin-openclaw-runtime") { if (RUNTIME_PLUGINS_WITH_MCP_SCHEMA_SERVER.has(pluginId)) {
const mcpServerAsset = join(pluginSrcDir, "src", "mcp-schema-server.cjs"); const mcpServerAsset = join(pluginSrcDir, "src", "mcp-schema-server.cjs");
if (!existsSync(mcpServerAsset)) { if (!existsSync(mcpServerAsset)) {
throw new Error( throw new Error(
`[tsup] Missing required openclaw bridge asset at ${mcpServerAsset}; expected committed source file mcp-schema-server.cjs.`, `[tsup] Missing required bridge asset for ${pluginId} at ${mcpServerAsset}; expected committed source file mcp-schema-server.cjs.`,
); );
} }
cpSync(mcpServerAsset, join(pluginDestDir, "mcp-schema-server.cjs")); cpSync(mcpServerAsset, join(pluginDestDir, "mcp-schema-server.cjs"));