mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-24 06:59:48 +00:00
fix(channels): narrow bundled channel loader diagnostics
Narrow bundled channel wrong-loader diagnostics, preserve ordinary missing-register debug details, and add focused loader coverage. Verification: - pnpm test src/plugins/loader.test.ts src/plugins/loader.cli-metadata.test.ts -- --reporter=verbose - env -u OPENCLAW_TESTBOX -u OPENCLAW_TESTBOX_REMOTE_RUN pnpm check:changed CI notes: - Real behavior proof passed with maintainer proof: override label. - check-additional-boundaries-d is red from existing extension test imports outside this PR: extensions/telegram/src/bot.create-telegram-bot.test.ts and extensions/whatsapp/src/login.coverage.test.ts. Co-authored-by: Chinar Amrutkar <chinar.amrutkar@gmail.com>
This commit is contained in:
@@ -61,6 +61,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Plugins/update: include beta-channel fallback details in plugin update outcomes when `@beta` is unavailable and OpenClaw uses the recorded default/latest plugin spec, making mixed beta/latest plugin cohorts visible in update summaries. Fixes #80689. Thanks @BKF-Gitty.
|
||||
- Control UI/performance: scope Nodes polling to the active Nodes tab, debounce stale session-list reconciliation, and bound chat-side session refreshes so long-running dashboards avoid background reload churn. Thanks @BunsDev.
|
||||
- Plugins/channels: explain bundled channel entry files that reach the legacy plugin loader as setup-runtime loader mismatches instead of generic missing-register failures. Thanks @chinar-amrutkar.
|
||||
- Bonjour/Gateway: treat active ciao probing and fresh name-conflict renames as in-progress so the mDNS watchdog waits for probe settlement before retrying, preventing rapid re-advertise loops on Windows, WSL, and other multicast-hostile hosts. (#74778) Refs #74242. Thanks @fuller-stack-dev.
|
||||
- Providers/MiniMax: send a minimal Anthropic-compatible user fallback when message conversion filters a turn to an empty payload, so MiniMax M2.7 no longer returns `chat content is empty` after tool-heavy sessions. Fixes #74589. Thanks @neeravmakwana and @DerekEXS.
|
||||
- Tools/media: preserve implicit allow-all semantics from `tools.alsoAllow`-only policies when preconstructing built-in media generation and PDF tools, so configured media tools become live without forcing `tools.allow: ["*", ...]`. Fixes #77841. Thanks @trialanderrorstudios.
|
||||
|
||||
@@ -26,6 +26,58 @@ afterAll(() => {
|
||||
});
|
||||
|
||||
describe("plugin loader CLI metadata", () => {
|
||||
it.each([
|
||||
{
|
||||
id: "wrong-cli-channel-entry",
|
||||
kind: "bundled-channel-entry",
|
||||
error: "bundled channel entry requires setup-runtime loader",
|
||||
},
|
||||
{
|
||||
id: "wrong-cli-channel-setup-entry",
|
||||
kind: "bundled-channel-setup-entry",
|
||||
error: "bundled channel setup entry requires setup-runtime loader",
|
||||
},
|
||||
])(
|
||||
"reports $kind loaded through CLI metadata legacy plugin path",
|
||||
async ({ id, kind, error }) => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
id,
|
||||
filename: `${id}.cjs`,
|
||||
body: `module.exports = { id: ${JSON.stringify(id)}, kind: ${JSON.stringify(kind)} };`,
|
||||
});
|
||||
const errors: string[] = [];
|
||||
|
||||
const registry = await loadOpenClawPluginCliRegistry({
|
||||
cache: false,
|
||||
logger: {
|
||||
info: () => {},
|
||||
warn: () => {},
|
||||
error: (msg: string) => errors.push(msg),
|
||||
debug: () => {},
|
||||
},
|
||||
config: {
|
||||
plugins: {
|
||||
load: { paths: [plugin.file] },
|
||||
allow: [id],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const loaded = registry.plugins.find((entry) => entry.id === id);
|
||||
expect(loaded?.status).toBe("error");
|
||||
expect(loaded?.error).toBe(error);
|
||||
expect(
|
||||
registry.diagnostics.some(
|
||||
(diag) => diag.level === "error" && diag.pluginId === id && diag.message === error,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(errors).toEqual([
|
||||
`[plugins] ${id} ${error}; ensure plugin is loaded via bundled channel discovery, not legacy plugin loader`,
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
it("suppresses trust warning logs during CLI metadata loads", async () => {
|
||||
useNoBundledPlugins();
|
||||
const stateDir = makeTempDir();
|
||||
|
||||
@@ -3994,6 +3994,45 @@ module.exports = { id: "throws-after-import", register() {} };`,
|
||||
expect(loaded?.error).toContain("export.default:object keys=default");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
id: "wrong-channel-entry",
|
||||
kind: "bundled-channel-entry",
|
||||
error: "bundled channel entry requires setup-runtime loader",
|
||||
},
|
||||
{
|
||||
id: "wrong-channel-setup-entry",
|
||||
kind: "bundled-channel-setup-entry",
|
||||
error: "bundled channel setup entry requires setup-runtime loader",
|
||||
},
|
||||
])("reports $kind loaded through the legacy plugin loader", ({ id, kind, error }) => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
id,
|
||||
filename: `${id}.cjs`,
|
||||
body: `module.exports = { id: ${JSON.stringify(id)}, kind: ${JSON.stringify(kind)} };`,
|
||||
});
|
||||
const errors: string[] = [];
|
||||
|
||||
const registry = loadRegistryFromSinglePlugin({
|
||||
plugin,
|
||||
pluginConfig: {
|
||||
allow: [id],
|
||||
},
|
||||
options: {
|
||||
logger: createErrorLogger(errors),
|
||||
},
|
||||
});
|
||||
|
||||
const loaded = registry.plugins.find((entry) => entry.id === id);
|
||||
expect(loaded?.status).toBe("error");
|
||||
expect(loaded?.error).toBe(error);
|
||||
expectRegistryErrorDiagnostic({ registry, pluginId: id, message: error });
|
||||
expect(errors).toEqual([
|
||||
`[plugins] ${id} ${error}; ensure plugin is loaded via bundled channel discovery, not legacy plugin loader`,
|
||||
]);
|
||||
});
|
||||
|
||||
it("handles single-plugin channel, context engine, and cli validation", () => {
|
||||
useNoBundledPlugins();
|
||||
const scenarios = [
|
||||
|
||||
@@ -1439,6 +1439,20 @@ function resolvePluginModuleExport(moduleExport: unknown): {
|
||||
return {};
|
||||
}
|
||||
|
||||
function kindIncludes(kind: unknown, target: string): boolean {
|
||||
return kind === target || (Array.isArray(kind) && kind.includes(target));
|
||||
}
|
||||
|
||||
function formatBundledChannelWrongLoaderError(kind: unknown): string | null {
|
||||
if (kindIncludes(kind, "bundled-channel-setup-entry")) {
|
||||
return "bundled channel setup entry requires setup-runtime loader";
|
||||
}
|
||||
if (kindIncludes(kind, "bundled-channel-entry")) {
|
||||
return "bundled channel entry requires setup-runtime loader";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function pushDiagnostics(diagnostics: PluginDiagnostic[], append: PluginDiagnostic[]) {
|
||||
diagnostics.push(...append);
|
||||
}
|
||||
@@ -2340,8 +2354,16 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
}
|
||||
|
||||
if (typeof register !== "function") {
|
||||
logger.error(`[plugins] ${record.id} missing register/activate export`);
|
||||
pushPluginLoadError(formatMissingPluginRegisterError(mod, env));
|
||||
const bundledChannelWrongLoaderError = formatBundledChannelWrongLoaderError(record.kind);
|
||||
if (bundledChannelWrongLoaderError) {
|
||||
logger.error(
|
||||
`[plugins] ${record.id} ${bundledChannelWrongLoaderError}; ensure plugin is loaded via bundled channel discovery, not legacy plugin loader`,
|
||||
);
|
||||
pushPluginLoadError(bundledChannelWrongLoaderError);
|
||||
} else {
|
||||
logger.error(`[plugins] ${record.id} missing register/activate export`);
|
||||
pushPluginLoadError(formatMissingPluginRegisterError(mod, env));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2793,8 +2815,16 @@ export async function loadOpenClawPluginCliRegistry(
|
||||
}
|
||||
|
||||
if (typeof register !== "function") {
|
||||
logger.error(`[plugins] ${record.id} missing register/activate export`);
|
||||
pushPluginLoadError(formatMissingPluginRegisterError(mod, env));
|
||||
const bundledChannelWrongLoaderError = formatBundledChannelWrongLoaderError(record.kind);
|
||||
if (bundledChannelWrongLoaderError) {
|
||||
logger.error(
|
||||
`[plugins] ${record.id} ${bundledChannelWrongLoaderError}; ensure plugin is loaded via bundled channel discovery, not legacy plugin loader`,
|
||||
);
|
||||
pushPluginLoadError(bundledChannelWrongLoaderError);
|
||||
} else {
|
||||
logger.error(`[plugins] ${record.id} missing register/activate export`);
|
||||
pushPluginLoadError(formatMissingPluginRegisterError(mod, env));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user