diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index 4bae4812fdf..13b063810b9 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -1037,7 +1037,8 @@ describe("loadOpenClawPlugins", () => { }, }); - expect(registry.plugins.find((entry) => entry.id === "discord")?.status).toBe("loaded"); + const record = registry.plugins.find((entry) => entry.id === "discord"); + expect(record?.status, record?.error).toBe("loaded"); }); it("registers standalone text transforms", () => { useNoBundledPlugins(); @@ -6591,7 +6592,7 @@ module.exports = { }), ); const record = registry.plugins.find((entry) => entry.id === "legacy-root-import"); - expect(record?.status).toBe("loaded"); + expect(record?.status, record?.error).toBe("loaded"); }); it("supports legacy plugins subscribing to diagnostic events from the root sdk", async () => { @@ -6639,7 +6640,7 @@ module.exports = { const record = registry.plugins.find( (entry) => entry.id === "legacy-root-diagnostic-listener", ); - expect(record?.status).toBe("loaded"); + expect(record?.status, record?.error).toBe("loaded"); emitDiagnosticEvent({ type: "model.usage", diff --git a/src/plugins/native-module-require.test.ts b/src/plugins/native-module-require.test.ts index 6566a930aa0..67e97a1c7e3 100644 --- a/src/plugins/native-module-require.test.ts +++ b/src/plugins/native-module-require.test.ts @@ -64,6 +64,19 @@ describe("tryNativeRequireJavaScriptModule", () => { ); }); + it("declines missing dependency errors when source-transform fallback is available", () => { + const dir = makeTempDir(); + const modulePath = path.join(dir, "plugin.cjs"); + fs.writeFileSync(modulePath, 'require("openclaw/plugin-sdk");\n', "utf8"); + + expect( + tryNativeRequireJavaScriptModule(modulePath, { + allowWindows: true, + fallbackOnMissingDependency: true, + }), + ).toEqual({ ok: false }); + }); + it("propagates real module evaluation errors instead of falling back", () => { const dir = makeTempDir(); const modulePath = path.join(dir, "plugin.cjs"); diff --git a/src/plugins/native-module-require.ts b/src/plugins/native-module-require.ts index a89d7beb723..e70a1079722 100644 --- a/src/plugins/native-module-require.ts +++ b/src/plugins/native-module-require.ts @@ -33,7 +33,7 @@ function isSourceTransformFallbackError(error: unknown, modulePath: string): boo export function tryNativeRequireJavaScriptModule( modulePath: string, - options: { allowWindows?: boolean } = {}, + options: { allowWindows?: boolean; fallbackOnMissingDependency?: boolean } = {}, ): { ok: true; moduleExport: unknown } | { ok: false } { if (process.platform === "win32" && options.allowWindows !== true) { return { ok: false }; @@ -44,7 +44,15 @@ export function tryNativeRequireJavaScriptModule( try { return { ok: true, moduleExport: nodeRequire(modulePath) }; } catch (error) { - if (!isSourceTransformFallbackError(error, modulePath)) { + const code = + error && typeof error === "object" ? (error as { code?: unknown }).code : undefined; + if ( + !isSourceTransformFallbackError(error, modulePath) && + !( + options.fallbackOnMissingDependency === true && + (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") + ) + ) { throw error; } return { ok: false }; diff --git a/src/plugins/plugin-module-loader-cache.test.ts b/src/plugins/plugin-module-loader-cache.test.ts index 4b29754583a..8004266bf3e 100644 --- a/src/plugins/plugin-module-loader-cache.test.ts +++ b/src/plugins/plugin-module-loader-cache.test.ts @@ -406,6 +406,7 @@ describe("getCachedPluginModuleLoader", () => { // allowWindows must be passed so the native fast path works on Windows too. expect(nativeStub).toHaveBeenCalledWith("/repo/dist/extensions/demo/api.js", { allowWindows: true, + fallbackOnMissingDependency: true, }); expect(getPluginModuleLoaderStats()).toMatchObject({ calls: 1, diff --git a/src/plugins/plugin-module-loader-cache.ts b/src/plugins/plugin-module-loader-cache.ts index 076dda986bc..d7bbc38bdcf 100644 --- a/src/plugins/plugin-module-loader-cache.ts +++ b/src/plugins/plugin-module-loader-cache.ts @@ -282,7 +282,10 @@ function createPluginModuleLoader(params: { ...rest, ); } - const native = tryNativeRequireJavaScriptModule(target, { allowWindows: true }); + const native = tryNativeRequireJavaScriptModule(target, { + allowWindows: true, + fallbackOnMissingDependency: true, + }); if (native.ok) { pluginModuleLoaderStats.nativeHits += 1; return native.moduleExport; diff --git a/src/plugins/plugin-sdk-dist-alias.ts b/src/plugins/plugin-sdk-dist-alias.ts index e1f98c8aa74..58cddb1b31e 100644 --- a/src/plugins/plugin-sdk-dist-alias.ts +++ b/src/plugins/plugin-sdk-dist-alias.ts @@ -8,12 +8,7 @@ function writeRuntimeJsonFile(targetPath: string, value: unknown): void { function writeRuntimeModuleWrapper(sourcePath: string, targetPath: string): void { const relative = `./${path.relative(path.dirname(targetPath), sourcePath).split(path.sep).join("/")}`; - const content = [ - `export * from ${JSON.stringify(relative)};`, - `import * as moduleExports from ${JSON.stringify(relative)};`, - `export default moduleExports.default ?? moduleExports;`, - "", - ].join("\n"); + const content = [`export * from ${JSON.stringify(relative)};`, ""].join("\n"); try { if (fs.readFileSync(targetPath, "utf8") === content) { return;