mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 19:00:45 +00:00
plugins: isolate setup-entry loader failures
This commit is contained in:
@@ -3256,6 +3256,75 @@ module.exports = {
|
||||
expect(registry.channels).toHaveLength(expectedChannels);
|
||||
});
|
||||
|
||||
it("isolates loadSetupPlugin errors as per-plugin diagnostics instead of crashing registry load", () => {
|
||||
useNoBundledPlugins();
|
||||
const pluginDir = makeTempDir();
|
||||
|
||||
// Plugin whose setup-entry uses the bundled contract but loadSetupPlugin() throws
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "@openclaw/setup-entry-throws-test",
|
||||
openclaw: {
|
||||
extensions: ["./index.cjs"],
|
||||
setupEntry: "./setup-entry.cjs",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "openclaw.plugin.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
id: "setup-entry-throws-test",
|
||||
configSchema: EMPTY_PLUGIN_SCHEMA,
|
||||
channels: ["setup-entry-throws-test"],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
// index.cjs: full entry (should NOT be reached if setup-entry is used)
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "index.cjs"),
|
||||
`module.exports = { id: "setup-entry-throws-test", register() {} };`,
|
||||
"utf-8",
|
||||
);
|
||||
// setup-entry.cjs: bundled contract whose loadSetupPlugin throws
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "setup-entry.cjs"),
|
||||
`module.exports = {
|
||||
kind: "bundled-channel-setup-entry",
|
||||
loadSetupPlugin: () => { throw new Error("boom: setup plugin missing"); },
|
||||
};`,
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const registry = loadOpenClawPlugins({
|
||||
cache: false,
|
||||
config: {
|
||||
plugins: {
|
||||
load: { paths: [pluginDir] },
|
||||
allow: ["setup-entry-throws-test"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// The registry load should NOT crash; the error should be recorded as a
|
||||
// per-plugin diagnostic rather than aborting the whole load.
|
||||
expect(registry.diagnostics.length).toBeGreaterThanOrEqual(1);
|
||||
const diagnostic = registry.diagnostics.find(
|
||||
(d) => d.pluginId === "setup-entry-throws-test" && d.level === "error",
|
||||
);
|
||||
expect(diagnostic).toBeDefined();
|
||||
expect(diagnostic!.message).toContain("failed to load setup entry");
|
||||
});
|
||||
|
||||
it("prefers setupEntry for configured channel loads during startup when opted in", () => {
|
||||
expect(
|
||||
__testing.shouldLoadChannelPluginInSetupRuntime({
|
||||
|
||||
@@ -646,6 +646,7 @@ function resolvePluginModuleExport(moduleExport: unknown): {
|
||||
|
||||
function resolveSetupChannelRegistration(moduleExport: unknown): {
|
||||
plugin?: ChannelPlugin;
|
||||
loadError?: unknown;
|
||||
} {
|
||||
const resolved = unwrapDefaultModuleExport(moduleExport);
|
||||
if (!resolved || typeof resolved !== "object") {
|
||||
@@ -659,11 +660,15 @@ function resolveSetupChannelRegistration(moduleExport: unknown): {
|
||||
setupEntryRecord.kind === "bundled-channel-setup-entry" &&
|
||||
typeof setupEntryRecord.loadSetupPlugin === "function"
|
||||
) {
|
||||
const loadedPlugin = setupEntryRecord.loadSetupPlugin();
|
||||
if (loadedPlugin && typeof loadedPlugin === "object") {
|
||||
return {
|
||||
plugin: loadedPlugin as ChannelPlugin,
|
||||
};
|
||||
try {
|
||||
const loadedPlugin = setupEntryRecord.loadSetupPlugin();
|
||||
if (loadedPlugin && typeof loadedPlugin === "object") {
|
||||
return {
|
||||
plugin: loadedPlugin as ChannelPlugin,
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
return { loadError: err };
|
||||
}
|
||||
}
|
||||
const setup = resolved as {
|
||||
@@ -1650,6 +1655,21 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
manifestRecord.setupSource
|
||||
) {
|
||||
const setupRegistration = resolveSetupChannelRegistration(mod);
|
||||
if (setupRegistration.loadError) {
|
||||
recordPluginError({
|
||||
logger,
|
||||
registry,
|
||||
record,
|
||||
seenIds,
|
||||
pluginId,
|
||||
origin: candidate.origin,
|
||||
phase: "load",
|
||||
error: setupRegistration.loadError,
|
||||
logPrefix: `[plugins] ${record.id} failed to load setup entry from ${record.source}: `,
|
||||
diagnosticMessagePrefix: "failed to load setup entry: ",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (setupRegistration.plugin) {
|
||||
if (setupRegistration.plugin.id && setupRegistration.plugin.id !== record.id) {
|
||||
pushPluginLoadError(
|
||||
|
||||
Reference in New Issue
Block a user