From 6fec1ec2d66a56b9810e4597c33f8fc373347db6 Mon Sep 17 00:00:00 2001 From: Richard Poelderl Date: Sun, 12 Apr 2026 13:59:36 +0200 Subject: [PATCH] fix(update): Suppress Corepack prompts during update preflight (#61456) Merged via squash. Prepared head SHA: da1b791ce6ed0aee245ebc47b72dd64ceeb4a1d1 Co-authored-by: p6l-richard <18185649+p6l-richard@users.noreply.github.com> Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com> Reviewed-by: @osolmaz --- CHANGELOG.md | 1 + src/infra/update-global.test.ts | 15 +++++++++++++++ src/infra/update-global.ts | 19 +++++++++++++++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f61d985116..718c62ab496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -693,6 +693,7 @@ Docs: https://docs.openclaw.ai - Gateway/OpenAI HTTP: restore default operator scopes for bearer-authenticated requests that omit `x-openclaw-scopes`, so headless `/v1/chat/completions` and session-history callers work again after the recent method-scope hardening. (#57596) Thanks @openperf. - Gateway/attachments: offload large inbound images without leaking `media://` markers into text-only runs, preserve mixed attachment order for model input/transcripts, and fail closed when model image capability cannot be resolved. (#55513) Thanks @Syysean. - Telegram/outbound chunking: use static markdown chunking when Telegram runtime state is unavailable so long outbound Telegram messages still split correctly after cold starts. (#57816) Thanks @ForestDengHK. +- Update/Corepack: disable interactive Corepack download prompts during update preflight install unless `COREPACK_ENABLE_DOWNLOAD_PROMPT` is already explicitly set, so `openclaw update` can fetch the repo-pinned pnpm version non-interactively. (#61456) Thanks @p6l-richard. ## 2026.4.2 diff --git a/src/infra/update-global.test.ts b/src/infra/update-global.test.ts index 92a83b09de6..92c86205e7d 100644 --- a/src/infra/update-global.test.ts +++ b/src/infra/update-global.test.ts @@ -11,6 +11,7 @@ import { cleanupGlobalRenameDirs, detectGlobalInstallManagerByPresence, detectGlobalInstallManagerForRoot, + createGlobalInstallEnv, globalInstallArgs, globalInstallFallbackArgs, isExplicitPackageInstallSpec, @@ -89,6 +90,20 @@ describe("update global helpers", () => { ).toBe("https://example.com/openclaw-main.tgz"); }); + it("defaults corepack download prompts off for global install env", async () => { + await expect(createGlobalInstallEnv({})).resolves.toMatchObject({ + COREPACK_ENABLE_DOWNLOAD_PROMPT: "0", + }); + + await expect( + createGlobalInstallEnv({ + COREPACK_ENABLE_DOWNLOAD_PROMPT: "1", + }), + ).resolves.toMatchObject({ + COREPACK_ENABLE_DOWNLOAD_PROMPT: "1", + }); + }); + it("classifies main and raw install specs separately from registry selectors", () => { expect(isMainPackageTarget("main")).toBe(true); expect(isMainPackageTarget(" MAIN ")).toBe(true); diff --git a/src/infra/update-global.ts b/src/infra/update-global.ts index ce2da669ab1..dd6debeee39 100644 --- a/src/infra/update-global.ts +++ b/src/infra/update-global.ts @@ -29,6 +29,7 @@ const PRIMARY_PACKAGE_NAME = "openclaw"; const ALL_PACKAGE_NAMES = [PRIMARY_PACKAGE_NAME] as const; const GLOBAL_RENAME_PREFIX = "."; export const OPENCLAW_MAIN_PACKAGE_SPEC = "github:openclaw/openclaw#main"; +const COREPACK_ENABLE_DOWNLOAD_PROMPT_DEFAULT = "0"; const NPM_GLOBAL_INSTALL_QUIET_FLAGS = ["--no-fund", "--no-audit", "--loglevel=error"] as const; const NPM_GLOBAL_INSTALL_OMIT_OPTIONAL_FLAGS = [ "--omit=optional", @@ -140,6 +141,13 @@ function applyWindowsPackageInstallEnv(env: Record) { env.NODE_LLAMA_CPP_SKIP_DOWNLOAD = "1"; } +function applyCorepackDownloadPromptEnv(env: Record) { + const current = env.COREPACK_ENABLE_DOWNLOAD_PROMPT?.trim(); + if (!current) { + env.COREPACK_ENABLE_DOWNLOAD_PROMPT = COREPACK_ENABLE_DOWNLOAD_PROMPT_DEFAULT; + } +} + export function resolveGlobalInstallSpec(params: { packageName: string; tag: string; @@ -165,16 +173,23 @@ export async function createGlobalInstallEnv( env?: NodeJS.ProcessEnv, ): Promise { const pathPrepend = await resolvePortableGitPathPrepend(env); - if (pathPrepend.length === 0 && process.platform !== "win32") { + const sourceEnv = env ?? process.env; + const hasCorepackDownloadPromptSetting = Boolean( + sourceEnv.COREPACK_ENABLE_DOWNLOAD_PROMPT?.trim(), + ); + const requiresMergedEnv = + pathPrepend.length > 0 || process.platform === "win32" || !hasCorepackDownloadPromptSetting; + if (!requiresMergedEnv) { return env; } const merged = Object.fromEntries( - Object.entries(env ?? process.env) + Object.entries(sourceEnv) .filter(([, value]) => value != null) .map(([key, value]) => [key, String(value)]), ) as Record; applyPathPrepend(merged, pathPrepend); applyWindowsPackageInstallEnv(merged); + applyCorepackDownloadPromptEnv(merged); return merged; }