From f352caf07e5305aac10d2a2d271ee30afdc31d65 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 2 May 2026 23:54:57 +0100 Subject: [PATCH] fix: keep runtime model auth alias after build --- .../src/providers/image-generation.test.ts | 13 +++++---- .../qa-lab/src/providers/image-generation.ts | 2 +- .../shared/mock-provider-definition.ts | 5 ++-- scripts/runtime-postbuild.mjs | 29 +++++++++++++++++-- test/scripts/runtime-postbuild.test.ts | 22 ++++++++++++++ 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/extensions/qa-lab/src/providers/image-generation.test.ts b/extensions/qa-lab/src/providers/image-generation.test.ts index a775e26713a..04c2c8cf7f9 100644 --- a/extensions/qa-lab/src/providers/image-generation.test.ts +++ b/extensions/qa-lab/src/providers/image-generation.test.ts @@ -2,15 +2,16 @@ import { describe, expect, it } from "vitest"; import { buildQaImageGenerationConfigPatch } from "./image-generation.js"; describe("QA provider image generation config", () => { - it("uses the selected mock provider for mock-openai image generation", () => { + it("routes mock-openai image generation through the OpenAI image provider", () => { const patch = buildQaImageGenerationConfigPatch({ providerMode: "mock-openai", providerBaseUrl: "http://127.0.0.1:44080/v1", requiredPluginIds: ["qa-channel"], }); - expect(patch.plugins.allow).toEqual(["acpx", "memory-core", "qa-channel"]); - expect(patch.agents.defaults.imageGenerationModel.primary).toBe("mock-openai/gpt-image-1"); + expect(patch.plugins.allow).toEqual(["acpx", "memory-core", "openai", "qa-channel"]); + expect(patch.plugins.entries).toEqual({ openai: { enabled: true } }); + expect(patch.agents.defaults.imageGenerationModel.primary).toBe("openai/gpt-image-1"); expect(patch.models?.providers["mock-openai"]?.baseUrl).toBe("http://127.0.0.1:44080/v1"); }); @@ -30,14 +31,16 @@ describe("QA provider image generation config", () => { "qa-channel", ]); }); - it("uses the selected mock provider for AIMock image generation", () => { + it("routes AIMock image generation through the OpenAI image provider", () => { const patch = buildQaImageGenerationConfigPatch({ providerMode: "aimock", providerBaseUrl: "http://127.0.0.1:45080/v1", requiredPluginIds: [], }); - expect(patch.agents.defaults.imageGenerationModel.primary).toBe("aimock/gpt-image-1"); + expect(patch.plugins.allow).toEqual(["acpx", "memory-core", "openai"]); + expect(patch.plugins.entries).toEqual({ openai: { enabled: true } }); + expect(patch.agents.defaults.imageGenerationModel.primary).toBe("openai/gpt-image-1"); expect(patch.models?.providers.aimock?.baseUrl).toBe("http://127.0.0.1:45080/v1"); expect(patch.models?.providers["mock-openai"]).toBeUndefined(); }); diff --git a/extensions/qa-lab/src/providers/image-generation.ts b/extensions/qa-lab/src/providers/image-generation.ts index 5606db95e07..cd4c794a5a6 100644 --- a/extensions/qa-lab/src/providers/image-generation.ts +++ b/extensions/qa-lab/src/providers/image-generation.ts @@ -42,7 +42,7 @@ export function buildQaImageGenerationConfigPatch(input: QaImageGenerationPatchI providerBaseUrl: input.providerBaseUrl, }); })(); - const providerPluginIds = provider.usesModelProviderPlugins ? [imageProviderId] : []; + const providerPluginIds = imageProviderId ? [imageProviderId] : []; const enabledPluginIds = uniqueNonEmpty(providerPluginIds); return { diff --git a/extensions/qa-lab/src/providers/shared/mock-provider-definition.ts b/extensions/qa-lab/src/providers/shared/mock-provider-definition.ts index ae9cd2f2fd1..445181431d0 100644 --- a/extensions/qa-lab/src/providers/shared/mock-provider-definition.ts +++ b/extensions/qa-lab/src/providers/shared/mock-provider-definition.ts @@ -25,8 +25,9 @@ export function createMockQaProviderDefinition( serverLabel: params.serverLabel, }, defaultModel: (options) => mockModelRef(params.mode, options?.alternate), - defaultImageGenerationProviderIds: [], - defaultImageGenerationModel: () => `${params.mode}/gpt-image-1`, + defaultImageGenerationProviderIds: ["openai"], + defaultImageGenerationModel: ({ modelProviderIds }) => + modelProviderIds.includes("openai") ? "openai/gpt-image-1" : null, usesFastModeByDefault: () => false, resolveModelParams: () => ({ transport: "sse", diff --git a/scripts/runtime-postbuild.mjs b/scripts/runtime-postbuild.mjs index ec7a723a4a4..09e0a38523f 100644 --- a/scripts/runtime-postbuild.mjs +++ b/scripts/runtime-postbuild.mjs @@ -122,13 +122,38 @@ export function writeStableRootRuntimeAliases(params = {}) { candidatesByAlias.set(aliasFileName, candidates); } + const resolveAliasCandidate = (candidates) => { + if (candidates.length === 1) { + return candidates[0]; + } + const candidateSet = new Set(candidates); + const wrappers = candidates.filter((candidate) => { + const filePath = path.join(distDir, candidate); + let source; + try { + source = fsImpl.readFileSync(filePath, "utf8"); + } catch { + return false; + } + return candidates.some( + (target) => + target !== candidate && + candidateSet.has(target) && + source.includes(`"./${target}"`) && + !source.includes("\n//#region "), + ); + }); + return wrappers.length === 1 ? wrappers[0] : null; + }; + for (const [aliasFileName, candidates] of candidatesByAlias) { const aliasPath = path.join(distDir, aliasFileName); - if (candidates.length !== 1) { + const candidate = resolveAliasCandidate(candidates); + if (!candidate) { fsImpl.rmSync?.(aliasPath, { force: true }); continue; } - writeTextFileIfChanged(aliasPath, `export * from "./${candidates[0]}";\n`); + writeTextFileIfChanged(aliasPath, `export * from "./${candidate}";\n`); } } diff --git a/test/scripts/runtime-postbuild.test.ts b/test/scripts/runtime-postbuild.test.ts index f2c05df07a3..ea99ba07e4d 100644 --- a/test/scripts/runtime-postbuild.test.ts +++ b/test/scripts/runtime-postbuild.test.ts @@ -113,6 +113,28 @@ describe("runtime postbuild static assets", () => { await expect(fs.stat(path.join(distDir, "install.runtime.js"))).rejects.toThrow(); }); + it("keeps stable aliases when one colliding root runtime chunk re-exports the implementation", async () => { + const rootDir = createTempDir("openclaw-runtime-postbuild-"); + const distDir = path.join(rootDir, "dist"); + await fs.mkdir(distDir, { recursive: true }); + await fs.writeFile( + path.join(distDir, "runtime-model-auth.runtime-Impl123.js"), + "export const auth = true;\n", + "utf8", + ); + await fs.writeFile( + path.join(distDir, "runtime-model-auth.runtime-Wrap456.js"), + 'import { auth } from "./runtime-model-auth.runtime-Impl123.js";\nexport { auth };\n', + "utf8", + ); + + writeStableRootRuntimeAliases({ rootDir }); + + expect(await fs.readFile(path.join(distDir, "runtime-model-auth.runtime.js"), "utf8")).toBe( + 'export * from "./runtime-model-auth.runtime-Wrap456.js";\n', + ); + }); + it("rewrites root runtime imports to stable aliases", async () => { const rootDir = createTempDir("openclaw-runtime-postbuild-"); const distDir = path.join(rootDir, "dist");