feat(FN-2347): add experimental OpenClaw runtime plugin scaffold

- Add fusion-plugin-openclaw-runtime workspace package with manifest, runtime metadata, and deferred placeholder factory
- Add unit tests for OpenClaw plugin behavior and PluginRunner runtime discovery compatibility
- Document OpenClaw runtime installation and runtimeHint usage in README, getting-started, and settings reference docs
- Include built dist artifacts for the new plugin and update workspace/lockfile entries
This commit is contained in:
Fusion
2026-04-23 14:03:35 -07:00
committed by gsxdsm
parent 79d00604ae
commit ce8cf6337a
21 changed files with 693 additions and 9 deletions

View File

@@ -998,6 +998,101 @@ describe("PluginRunner", () => {
});
});
describe("OpenClaw runtime compatibility", () => {
it("should resolve openclaw runtime when registered", () => {
const openclawRuntime = {
metadata: {
runtimeId: "openclaw",
name: "OpenClaw Runtime",
description: "Experimental OpenClaw runtime integration for Fusion tasks (execution deferred)",
version: "0.1.0",
},
factory: vi.fn().mockReturnValue({}),
};
mockPluginLoader.getPluginRuntimes.mockReturnValue([
{ pluginId: "fusion-plugin-openclaw-runtime", runtime: openclawRuntime as any },
]);
const result = pluginRunner.getRuntimeById("openclaw");
expect(result).toBeDefined();
expect(result?.pluginId).toBe("fusion-plugin-openclaw-runtime");
expect(result?.runtime.metadata.runtimeId).toBe("openclaw");
expect(result?.runtime.metadata.name).toBe("OpenClaw Runtime");
expect(result?.runtime.metadata.description).toBe(
"Experimental OpenClaw runtime integration for Fusion tasks (execution deferred)",
);
expect(result?.runtime.metadata.version).toBe("0.1.0");
});
it("should expose openclaw runtime metadata correctly", () => {
const openclawRuntime = {
metadata: {
runtimeId: "openclaw",
name: "OpenClaw Runtime",
description: "Experimental OpenClaw runtime integration for Fusion tasks (execution deferred)",
version: "0.1.0",
},
factory: vi.fn().mockReturnValue({}),
};
mockPluginLoader.getPluginRuntimes.mockReturnValue([
{ pluginId: "fusion-plugin-openclaw-runtime", runtime: openclawRuntime as any },
]);
const result = pluginRunner.getRuntimeById("openclaw");
expect(result).toBeDefined();
expect(result?.pluginId).toBe("fusion-plugin-openclaw-runtime");
expect(result?.runtime.metadata).toEqual({
runtimeId: "openclaw",
name: "OpenClaw Runtime",
description: "Experimental OpenClaw runtime integration for Fusion tasks (execution deferred)",
version: "0.1.0",
});
});
it("should return deferred placeholder object when openclaw factory is invoked", async () => {
const openclawPlaceholderRuntime = {
runtimeId: "openclaw",
version: "0.1.0",
status: "deferred",
message:
"OpenClaw runtime execution is currently deferred. This runtime is registered for discovery and configuration only.",
execute: vi.fn().mockRejectedValue(
new Error(
"OpenClaw runtime is not implemented yet. Runtime discovery and configuration are supported, but execution is deferred.",
),
),
};
const openclawRuntime = {
metadata: {
runtimeId: "openclaw",
name: "OpenClaw Runtime",
description: "Experimental OpenClaw runtime integration for Fusion tasks (execution deferred)",
version: "0.1.0",
},
factory: vi.fn().mockReturnValue(openclawPlaceholderRuntime),
};
mockPluginLoader.getPluginRuntimes.mockReturnValue([
{ pluginId: "fusion-plugin-openclaw-runtime", runtime: openclawRuntime as any },
]);
const result = pluginRunner.getRuntimeById("openclaw");
expect(result).toBeDefined();
const context = { pluginId: "fusion-plugin-openclaw-runtime" };
const runtime = await result!.runtime.factory(context as any) as typeof openclawPlaceholderRuntime;
expect(openclawRuntime.factory).toHaveBeenCalledWith(context);
expect(runtime).toBe(openclawPlaceholderRuntime);
expect(runtime).toMatchObject({
runtimeId: "openclaw",
version: "0.1.0",
status: "deferred",
});
expect(runtime.message).toContain("discovery and configuration only");
expect(runtime.execute).toBeTypeOf("function");
});
});
describe("getLoader() / getStore()", () => {
it("should return the plugin loader", () => {
const loader = pluginRunner.getLoader();