diff --git a/scripts/postinstall-bundled-plugins.mjs b/scripts/postinstall-bundled-plugins.mjs index 3b7b51bbb14..4f542c8a3bc 100644 --- a/scripts/postinstall-bundled-plugins.mjs +++ b/scripts/postinstall-bundled-plugins.mjs @@ -34,7 +34,16 @@ const DEFAULT_PACKAGE_ROOT = join(__dirname, ".."); const DISABLE_POSTINSTALL_ENV = "OPENCLAW_DISABLE_BUNDLED_PLUGIN_POSTINSTALL"; const EAGER_BUNDLED_PLUGIN_DEPS_ENV = "OPENCLAW_EAGER_BUNDLED_PLUGIN_DEPS"; const DIST_INVENTORY_PATH = "dist/postinstall-inventory.json"; -const LEGACY_UPDATE_COMPAT_SIDECARS = []; +const LEGACY_UPDATE_COMPAT_SIDECARS = [ + { + path: "dist/extensions/qa-channel/runtime-api.js", + content: "export {};\n", + }, + { + path: "dist/extensions/qa-lab/runtime-api.js", + content: "export {};\n", + }, +]; const BAILEYS_MEDIA_FILE = join( "node_modules", "@whiskeysockets", diff --git a/src/infra/npm-update-compat-sidecars.ts b/src/infra/npm-update-compat-sidecars.ts index a378fc5270c..d3f63866c71 100644 --- a/src/infra/npm-update-compat-sidecars.ts +++ b/src/infra/npm-update-compat-sidecars.ts @@ -5,7 +5,18 @@ type NpmUpdateCompatSidecar = { content: string; }; -export const NPM_UPDATE_COMPAT_SIDECARS = [] as readonly NpmUpdateCompatSidecar[]; +const EMPTY_RUNTIME_SIDECAR = "export {};\n"; + +export const NPM_UPDATE_COMPAT_SIDECARS = [ + { + path: "dist/extensions/qa-channel/runtime-api.js", + content: EMPTY_RUNTIME_SIDECAR, + }, + { + path: `dist/extensions/${LEGACY_QA_LAB_DIR}/runtime-api.js`, + content: EMPTY_RUNTIME_SIDECAR, + }, +] as const satisfies readonly NpmUpdateCompatSidecar[]; export const NPM_UPDATE_COMPAT_SIDECAR_PATHS = new Set( NPM_UPDATE_COMPAT_SIDECARS.map((entry) => entry.path), diff --git a/test/scripts/postinstall-bundled-plugins.test.ts b/test/scripts/postinstall-bundled-plugins.test.ts index 4cf5dea3ba9..1a644e96fd0 100644 --- a/test/scripts/postinstall-bundled-plugins.test.ts +++ b/test/scripts/postinstall-bundled-plugins.test.ts @@ -291,8 +291,10 @@ describe("bundled plugin postinstall", () => { await expect(fs.stat(stalePackage)).rejects.toMatchObject({ code: "ENOENT" }); await expect(fs.stat(staleManifest)).rejects.toMatchObject({ code: "ENOENT" }); await expect( - fs.stat(path.join(packageRoot, "dist", "extensions", "qa-channel", "runtime-api.js")), - ).rejects.toMatchObject({ code: "ENOENT" }); + fs.readFile(path.join(packageRoot, "dist", "extensions", "qa-channel", "runtime-api.js"), { + encoding: "utf8", + }), + ).resolves.toBe("export {};\n"); await expect( fs.stat(path.join(packageRoot, "dist", "extensions", "qa-channel", "package.json")), ).rejects.toMatchObject({ code: "ENOENT" }); @@ -300,8 +302,10 @@ describe("bundled plugin postinstall", () => { fs.stat(path.join(packageRoot, "dist", "extensions", "qa-channel", "openclaw.plugin.json")), ).rejects.toMatchObject({ code: "ENOENT" }); await expect( - fs.stat(path.join(packageRoot, "dist", "extensions", "qa-lab", "runtime-api.js")), - ).rejects.toMatchObject({ code: "ENOENT" }); + fs.readFile(path.join(packageRoot, "dist", "extensions", "qa-lab", "runtime-api.js"), { + encoding: "utf8", + }), + ).resolves.toBe("export {};\n"); }); it("keeps packaged postinstall non-fatal when the dist inventory is missing", async () => {