fix(plugins): set channel runtime during discovery

This commit is contained in:
Gustavo Madeira Santana
2026-04-24 22:47:29 -04:00
parent cba60755b6
commit 39382bbf05
4 changed files with 90 additions and 4 deletions

View File

@@ -487,11 +487,11 @@ export function defineBundledChannelEntry<TPlugin = ChannelPlugin>({
profile("bundled-register:registerChannel", () =>
api.registerChannel({ plugin: channelPlugin as ChannelPlugin }),
);
profile("bundled-register:setChannelRuntime", () => setChannelRuntime?.(api.runtime));
if (api.registrationMode === "discovery") {
profile("bundled-register:registerCliMetadata", () => registerCliMetadata?.(api));
return;
}
profile("bundled-register:setChannelRuntime", () => setChannelRuntime?.(api.runtime));
if (api.registrationMode !== "full") {
return;
}

View File

@@ -504,11 +504,11 @@ export function defineChannelPluginEntry<TPlugin>({
return;
}
api.registerChannel({ plugin: plugin as ChannelPlugin });
setRuntime?.(api.runtime);
if (api.registrationMode === "discovery") {
registerCliMetadata?.(api);
return;
}
setRuntime?.(api.runtime);
if (api.registrationMode !== "full") {
return;
}

View File

@@ -1,6 +1,11 @@
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { afterAll, afterEach, describe, expect, it } from "vitest";
import {
defineBundledChannelEntry,
type OpenClawPluginApi,
} from "../plugin-sdk/channel-entry-contract.js";
import { loadOpenClawPluginCliRegistry, loadOpenClawPlugins } from "./loader.js";
import {
cleanupPluginLoaderFixturesForTest,
@@ -650,12 +655,93 @@ module.exports = {
expect(fs.readFileSync(modeMarker, "utf-8")).toBe("discovery");
expect(fs.existsSync(fullMarker)).toBe(false);
expect(fs.existsSync(runtimeMarker)).toBe(false);
expect(fs.existsSync(runtimeMarker)).toBe(true);
expect(registry.cliRegistrars.flatMap((entry) => entry.commands)).toContain(
"discovery-cli-metadata-channel",
);
});
it("sets bundled channel runtime before discovery CLI metadata registration", () => {
const pluginDir = makeTempDir();
const runtimeMarker = path.join(pluginDir, "runtime-set.txt");
const channelPluginPath = path.join(pluginDir, "channel.cjs");
const runtimePath = path.join(pluginDir, "runtime.cjs");
fs.writeFileSync(
channelPluginPath,
`exports.plugin = {
id: "bundled-discovery-cli",
meta: {
id: "bundled-discovery-cli",
label: "Bundled Discovery CLI",
selectionLabel: "Bundled Discovery CLI",
docsPath: "/channels/bundled-discovery-cli",
blurb: "bundled discovery cli",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" }),
},
outbound: { deliveryMode: "direct" },
};`,
"utf-8",
);
fs.writeFileSync(
runtimePath,
`exports.setRuntime = () => {
require("node:fs").writeFileSync(${JSON.stringify(runtimeMarker)}, "loaded", "utf-8");
};`,
"utf-8",
);
const commands: string[] = [];
const channels: string[] = [];
const entry = defineBundledChannelEntry({
id: "bundled-discovery-cli",
name: "Bundled Discovery CLI",
description: "bundled discovery cli",
importMetaUrl: pathToFileURL(path.join(pluginDir, "index.cjs")).href,
plugin: {
specifier: "./channel.cjs",
exportName: "plugin",
},
runtime: {
specifier: "./runtime.cjs",
exportName: "setRuntime",
},
registerCliMetadata(api) {
api.registerCli(() => {}, {
descriptors: [
{
name: "bundled-discovery-cli",
description: "Bundled discovery CLI metadata",
hasSubcommands: true,
},
],
});
},
registerFull() {
throw new Error("full registration should not run during discovery");
},
});
entry.register({
registrationMode: "discovery",
runtime: {} as OpenClawPluginApi["runtime"],
registerChannel: (registration) => {
const plugin = "plugin" in registration ? registration.plugin : registration;
channels.push(plugin.id);
},
registerCli: (_register, options) => {
commands.push(...(options?.descriptors ?? []).map((descriptor) => descriptor.name));
},
} as OpenClawPluginApi);
expect(channels).toEqual(["bundled-discovery-cli"]);
expect(fs.existsSync(runtimeMarker)).toBe(true);
expect(commands).toEqual(["bundled-discovery-cli"]);
});
it("sanitizes plugin CLI descriptor descriptions and rejects unsafe command names", async () => {
useNoBundledPlugins();
const unsafeDescription =

View File

@@ -56,11 +56,11 @@ export function inlineChannelPluginEntryFactorySource(): string {
return;
}
api.registerChannel({ plugin: options.plugin });
options.setRuntime?.(api.runtime);
if (api.registrationMode === "discovery") {
options.registerCliMetadata?.(api);
return;
}
options.setRuntime?.(api.runtime);
if (api.registrationMode !== "full") {
return;
}