From 47f3c410d64e14c8be8c65f248f19ca89931505d Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Thu, 14 May 2026 14:50:05 -0500 Subject: [PATCH] fix(npm): avoid pnpm optional postinstall hangs --- npm/deepseek-tui/scripts/install.js | 19 ++++++++++++++++ npm/deepseek-tui/test/postinstall.test.js | 27 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/npm/deepseek-tui/scripts/install.js b/npm/deepseek-tui/scripts/install.js index 92e2f876..71cb4039 100644 --- a/npm/deepseek-tui/scripts/install.js +++ b/npm/deepseek-tui/scripts/install.js @@ -103,6 +103,18 @@ function isInstallContext(context) { return context === "install"; } +function isPnpmUserAgent(env = process.env) { + return String(env.npm_config_user_agent || "").toLowerCase().includes("pnpm/"); +} + +function shouldSkipOptionalPostinstall( + context, + argv = process.argv.slice(2), + env = process.env, +) { + return isInstallContext(context) && isOptionalInstall(argv, env) && isPnpmUserAgent(env); +} + // Optional install only relaxes npm postinstall behavior. Runtime downloads // keep the normal retry/timeout budget so first-run recovery stays resilient. function defaultTimeoutMs(context = "runtime", env = process.env) { @@ -1089,6 +1101,12 @@ async function run(options = {}) { if (process.env.DEEPSEEK_TUI_DISABLE_INSTALL === "1" || process.env.DEEPSEEK_DISABLE_INSTALL === "1") { return; } + if (shouldSkipOptionalPostinstall(context)) { + logInfo( + "pnpm optional postinstall detected; skipping install-time download. The binary will be checked on first run.", + ); + return; + } const version = resolvePackageVersion(); const repo = resolveRepo(); const paths = binaryPaths(); @@ -1129,6 +1147,7 @@ module.exports = { isOptionalInstall, adoptExistingBinaryIfValid, shouldIgnoreInstallFailure, + shouldSkipOptionalPostinstall, defaultTimeoutMs, defaultStallMs, ensureBinary, diff --git a/npm/deepseek-tui/test/postinstall.test.js b/npm/deepseek-tui/test/postinstall.test.js index 10e1ac7e..f609b366 100644 --- a/npm/deepseek-tui/test/postinstall.test.js +++ b/npm/deepseek-tui/test/postinstall.test.js @@ -24,6 +24,33 @@ test("optional mode only changes install-time defaults", () => { assert.equal(_internal.defaultStallMs("runtime", { DEEPSEEK_TUI_OPTIONAL_INSTALL: "1" }), 30_000); }); +test("pnpm optional postinstall skips install-time download", () => { + assert.equal( + _internal.shouldSkipOptionalPostinstall("install", ["--optional"], { + npm_config_user_agent: "pnpm/10.11.0 npm/? node/v22.15.0 win32 x64", + }), + true, + ); + assert.equal( + _internal.shouldSkipOptionalPostinstall("runtime", ["--optional"], { + npm_config_user_agent: "pnpm/10.11.0 npm/? node/v22.15.0 win32 x64", + }), + false, + ); + assert.equal( + _internal.shouldSkipOptionalPostinstall("install", [], { + npm_config_user_agent: "pnpm/10.11.0 npm/? node/v22.15.0 win32 x64", + }), + false, + ); + assert.equal( + _internal.shouldSkipOptionalPostinstall("install", ["--optional"], { + npm_config_user_agent: "npm/11.3.0 node/v22.15.0 win32 x64", + }), + false, + ); +}); + test("optional install only swallows retryable download failures", () => { const socketHangUp = new Error("socket hang up"); assert.equal(