From b49d499b458c379adc16309a92ec4f0bdb0ecd93 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Apr 2026 21:41:47 +0100 Subject: [PATCH] fix: stabilize native Windows onboarding --- CHANGELOG.md | 3 +++ src/entry.respawn.test.ts | 16 ++++++++++++++++ src/entry.respawn.ts | 6 ++++++ src/plugins/public-surface-loader.test.ts | 4 ++-- src/plugins/sdk-alias.test.ts | 10 +++++----- src/plugins/sdk-alias.ts | 2 +- 6 files changed, 33 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c57d458103e..bc1ef0bb8fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,9 @@ Docs: https://docs.openclaw.ai ### Fixes +- Windows/native: keep CLI startup and bundled provider plugin loading off + Windows ESM raw-path failure paths, fixing native onboarding/install smoke on + Node 24. Thanks @steipete. - Providers/Google: transcode Gemini TTS PCM to Opus for voice-note targets so WhatsApp and other native voice-note replies can play as voice messages. - Plugins/runtime deps: reuse existing external bundled-plugin stage roots when diff --git a/src/entry.respawn.test.ts b/src/entry.respawn.test.ts index b495319afc7..6c2b917d2d2 100644 --- a/src/entry.respawn.test.ts +++ b/src/entry.respawn.test.ts @@ -72,4 +72,20 @@ describe("buildCliRespawnPlan", () => { }), ).toBeNull(); }); + + it("does not respawn on Windows", () => { + expect( + buildCliRespawnPlan({ + argv: [ + "node", + "C:\\Users\\alice\\AppData\\Roaming\\npm\\node_modules\\openclaw\\openclaw.mjs", + "onboard", + ], + env: {}, + execArgv: [], + autoNodeExtraCaCerts: "/etc/ssl/certs/ca-certificates.crt", + platform: "win32", + }), + ).toBeNull(); + }); }); diff --git a/src/entry.respawn.ts b/src/entry.respawn.ts index 70eea3772f7..19ec362372c 100644 --- a/src/entry.respawn.ts +++ b/src/entry.respawn.ts @@ -28,17 +28,23 @@ export function buildCliRespawnPlan( execArgv?: string[]; execPath?: string; autoNodeExtraCaCerts?: string | undefined; + platform?: NodeJS.Platform; } = {}, ): { argv: string[]; env: NodeJS.ProcessEnv } | null { const argv = params.argv ?? process.argv; const env = params.env ?? process.env; const execArgv = params.execArgv ?? process.execArgv; const execPath = params.execPath ?? process.execPath; + const platform = params.platform ?? process.platform; if (shouldSkipRespawnForArgv(argv) || isTruthyEnvValue(env.OPENCLAW_NO_RESPAWN)) { return null; } + if (platform === "win32") { + return null; + } + const childEnv: NodeJS.ProcessEnv = { ...env }; const childExecArgv = [...execArgv]; let needsRespawn = false; diff --git a/src/plugins/public-surface-loader.test.ts b/src/plugins/public-surface-loader.test.ts index fe3587eb3e4..9feb5bb0529 100644 --- a/src/plugins/public-surface-loader.test.ts +++ b/src/plugins/public-surface-loader.test.ts @@ -28,7 +28,7 @@ afterEach(() => { }); describe("bundled plugin public surface loader", () => { - it("uses native Jiti import for Windows dist public artifact loads", async () => { + it("uses transpiled Jiti import for Windows dist public artifact loads", async () => { const createJiti = vi.fn(() => vi.fn(() => ({ marker: "windows-dist-ok" }))); vi.doMock("jiti", () => ({ createJiti, @@ -56,7 +56,7 @@ describe("bundled plugin public surface loader", () => { expect(createJiti).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ - tryNative: true, + tryNative: false, }), ); } finally { diff --git a/src/plugins/sdk-alias.test.ts b/src/plugins/sdk-alias.test.ts index 3f5ad2ee0cc..91fa35ed920 100644 --- a/src/plugins/sdk-alias.test.ts +++ b/src/plugins/sdk-alias.test.ts @@ -870,7 +870,7 @@ describe("plugin sdk alias helpers", () => { } }); - it("prefers native Jiti loads on Windows for built JavaScript entries", () => { + it("disables native Jiti loads on Windows for built JavaScript entries", () => { const originalPlatform = process.platform; Object.defineProperty(process, "platform", { configurable: true, @@ -878,9 +878,9 @@ describe("plugin sdk alias helpers", () => { }); try { - expect(shouldPreferNativeJiti("/repo/dist/plugins/runtime/index.js")).toBe(true); + expect(shouldPreferNativeJiti("/repo/dist/plugins/runtime/index.js")).toBe(false); expect(shouldPreferNativeJiti(`/repo/${bundledDistPluginFile("browser", "index.js")}`)).toBe( - true, + false, ); } finally { Object.defineProperty(process, "platform", { @@ -890,7 +890,7 @@ describe("plugin sdk alias helpers", () => { } }); - it("keeps plugin loader dist shortcuts native on Windows", () => { + it("keeps plugin loader dist shortcuts on transpiled Jiti on Windows", () => { const originalPlatform = process.platform; Object.defineProperty(process, "platform", { configurable: true, @@ -902,7 +902,7 @@ describe("plugin sdk alias helpers", () => { resolvePluginLoaderJitiTryNative(`/repo/${bundledDistPluginFile("browser", "index.js")}`, { preferBuiltDist: true, }), - ).toBe(true); + ).toBe(false); expect( resolvePluginLoaderJitiTryNative(`/repo/${bundledDistPluginFile("browser", "helper.ts")}`, { preferBuiltDist: true, diff --git a/src/plugins/sdk-alias.ts b/src/plugins/sdk-alias.ts index 26b5aba0d06..212537d55f1 100644 --- a/src/plugins/sdk-alias.ts +++ b/src/plugins/sdk-alias.ts @@ -695,7 +695,7 @@ export function buildPluginLoaderJitiOptions(aliasMap: Record) { function supportsNativeJitiRuntime(): boolean { const versions = process.versions as { bun?: string }; - return typeof versions.bun !== "string"; + return typeof versions.bun !== "string" && process.platform !== "win32"; } function isBundledPluginDistModulePath(modulePath: string): boolean {