diff --git a/scripts/build-all.mjs b/scripts/build-all.mjs index 6d7b7fa0419..4b00df63f54 100644 --- a/scripts/build-all.mjs +++ b/scripts/build-all.mjs @@ -173,6 +173,16 @@ export const BUILD_ALL_PROFILE_STEP_ENV = { OPENCLAW_RUN_NODE_SKIP_DTS_BUILD: "1", }, }, + gatewayWatch: { + "runtime-postbuild": { + OPENCLAW_RUNTIME_POSTBUILD_STATIC_ASSETS: "0", + }, + }, + cliStartup: { + "runtime-postbuild": { + OPENCLAW_RUNTIME_POSTBUILD_STATIC_ASSETS: "0", + }, + }, }; export function buildAllUsage() { diff --git a/scripts/runtime-postbuild.mjs b/scripts/runtime-postbuild.mjs index 7a43f923b65..7a977b38af8 100644 --- a/scripts/runtime-postbuild.mjs +++ b/scripts/runtime-postbuild.mjs @@ -453,6 +453,11 @@ export function writeLegacyCliExitCompatChunks(params = {}) { } } +function shouldCopyStaticExtensionAssets(params) { + const env = params.env ?? process.env; + return env.OPENCLAW_RUNTIME_POSTBUILD_STATIC_ASSETS !== "0"; +} + export function runRuntimePostBuild(params = {}) { const timingsEnabled = params.timings ?? process.env.OPENCLAW_RUNTIME_POSTBUILD_TIMINGS !== "0"; const runPhase = (label, action) => { @@ -471,6 +476,9 @@ export function runRuntimePostBuild(params = {}) { runPhase("official channel catalog", () => writeOfficialChannelCatalog(params)); runPhase("bundled plugin runtime overlay", () => stageBundledPluginRuntime(params)); runPhase("static extension assets", () => { + if (!shouldCopyStaticExtensionAssets(params)) { + return; + } const staticAssetParams = { rootDir: ROOT, ...params, diff --git a/test/scripts/build-all.test.ts b/test/scripts/build-all.test.ts index e9e9d81dc06..b63e9f7a0ba 100644 --- a/test/scripts/build-all.test.ts +++ b/test/scripts/build-all.test.ts @@ -180,9 +180,7 @@ describe("resolveBuildAllSteps", () => { help: true, profile: "cliStartup", }); - expect(() => parseBuildAllArgs(["cliStartup", "--bogus"])).toThrow( - "unknown argument: --bogus", - ); + expect(() => parseBuildAllArgs(["cliStartup", "--bogus"])).toThrow("unknown argument: --bogus"); expect(() => parseBuildAllArgs(["wat"])).toThrow("Unknown build profile: wat"); }); @@ -279,6 +277,28 @@ describe("resolveBuildAllSteps", () => { ]); }); + it("skips generated static plugin assets for minimal backend-only profiles", () => { + for (const profile of ["gatewayWatch", "cliStartup"]) { + const runtimePostbuild = resolveBuildAllSteps(profile).find( + (step) => step.label === "runtime-postbuild", + ); + if (!runtimePostbuild) { + throw new Error(`Missing ${profile} runtime-postbuild step`); + } + + expect(BUILD_ALL_PROFILE_STEP_ENV[profile]["runtime-postbuild"]).toEqual({ + OPENCLAW_RUNTIME_POSTBUILD_STATIC_ASSETS: "0", + }); + expect( + resolveBuildAllStep(runtimePostbuild, { + env: { OPENCLAW_RUNTIME_POSTBUILD_STATIC_ASSETS: "1" }, + }).options.env, + ).toMatchObject({ + OPENCLAW_RUNTIME_POSTBUILD_STATIC_ASSETS: "0", + }); + } + }); + it("writes the runtime postbuild stamp after the build stamp", () => { const labels = resolveBuildAllSteps("full").map((step) => step.label); expect(labels).toContain("runtime-postbuild"); diff --git a/test/scripts/runtime-postbuild.test.ts b/test/scripts/runtime-postbuild.test.ts index 6257f026878..143a0420972 100644 --- a/test/scripts/runtime-postbuild.test.ts +++ b/test/scripts/runtime-postbuild.test.ts @@ -61,9 +61,7 @@ describe("runtime postbuild static assets", () => { "dist/extensions/diffs-language-pack/assets/viewer-runtime.js", "dist/extensions/diffs/assets/viewer-runtime.js", ]); - expect(payload.sources).toContain( - "extensions/diffs-language-pack/assets/viewer-runtime.js", - ); + expect(payload.sources).toContain("extensions/diffs-language-pack/assets/viewer-runtime.js"); expect(payload.sources).toContain("extensions/diffs/assets/viewer-runtime.js"); }); @@ -208,6 +206,45 @@ describe("runtime postbuild static assets", () => { await expect(fs.readFile(runtimeAsset, "utf8")).resolves.toBe("console.log('viewer');\n"); }); + it("can skip static asset copies for minimal runtime builds", async () => { + const rootDir = createTempDir("openclaw-runtime-postbuild-"); + const warn = vi.fn(); + const output = "assets/viewer-runtime.js"; + + await fs.mkdir(path.join(rootDir, "src", "plugin-sdk"), { recursive: true }); + await fs.writeFile( + path.join(rootDir, "src", "plugin-sdk", "root-alias.cjs"), + "module.exports = {};\n", + "utf8", + ); + await fs.mkdir(path.join(rootDir, "extensions", "diffs"), { recursive: true }); + await fs.writeFile( + path.join(rootDir, "extensions", "diffs", "package.json"), + JSON.stringify({ + name: "@openclaw/diffs", + openclaw: { + extensions: ["./index.ts"], + build: { + staticAssets: [{ source: `./${output}`, output }], + }, + }, + }), + "utf8", + ); + + runRuntimePostBuild({ + cwd: rootDir, + repoRoot: rootDir, + rootDir, + env: { OPENCLAW_RUNTIME_POSTBUILD_STATIC_ASSETS: "0" }, + timings: false, + warn, + }); + + expect(warn).not.toHaveBeenCalled(); + await expectPathMissing(path.join(rootDir, "dist", "extensions", "diffs", output)); + }); + it("skips runtime overlay asset copies when the runtime extension root is absent", async () => { const rootDir = createTempDir("openclaw-runtime-postbuild-"); await fs.mkdir(path.join(rootDir, "extensions", "demo", "assets"), { recursive: true });