From 4c8fd516649b480a13b3955588b61e4aa11cae17 Mon Sep 17 00:00:00 2001 From: GodsBoy Date: Tue, 17 Mar 2026 17:35:46 +0200 Subject: [PATCH] fix(install): inject execPath for nvm detection in service env builder --- src/daemon/service-env.test.ts | 40 +++++++++++++++++++++++++++------- src/daemon/service-env.ts | 11 +++++++--- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/daemon/service-env.test.ts b/src/daemon/service-env.test.ts index 4e12400d261..60fcc4d4fb1 100644 --- a/src/daemon/service-env.test.ts +++ b/src/daemon/service-env.test.ts @@ -458,16 +458,18 @@ describe("buildNodeServiceEnvironment", () => { }); describe("shared Node TLS env defaults", () => { + // Pass an explicit non-nvm execPath so tests are deterministic regardless of + // whether the test runner itself runs under nvm. const builders = [ { name: "gateway service env", build: (env: Record, platform?: NodeJS.Platform) => - buildServiceEnvironment({ env, port: 18789, platform }), + buildServiceEnvironment({ env, port: 18789, platform, execPath: "/usr/bin/node" }), }, { name: "node service env", build: (env: Record, platform?: NodeJS.Platform) => - buildNodeServiceEnvironment({ env, platform }), + buildNodeServiceEnvironment({ env, platform, execPath: "/usr/bin/node" }), }, ] as const; @@ -536,16 +538,25 @@ describe("resolveLinuxSystemCaBundle", () => { }); describe("shared Node TLS env — Linux nvm detection", () => { + const nvmExecPath = "/home/user/.nvm/versions/node/v22.22.0/bin/node"; + const nonNvmExecPath = "/usr/bin/node"; + const builders = [ { name: "gateway service env", - build: (env: Record, platform?: NodeJS.Platform) => - buildServiceEnvironment({ env, port: 18789, platform }), + build: ( + env: Record, + platform?: NodeJS.Platform, + execPath?: string, + ) => buildServiceEnvironment({ env, port: 18789, platform, execPath }), }, { name: "node service env", - build: (env: Record, platform?: NodeJS.Platform) => - buildNodeServiceEnvironment({ env, platform }), + build: ( + env: Record, + platform?: NodeJS.Platform, + execPath?: string, + ) => buildNodeServiceEnvironment({ env, platform, execPath }), }, ] as const; @@ -555,7 +566,19 @@ describe("shared Node TLS env — Linux nvm detection", () => { it.each(builders)( "$name defaults NODE_EXTRA_CA_CERTS on Linux when NVM_DIR is set", ({ build }) => { - const env = build({ HOME: "/home/user", NVM_DIR: "/home/user/.nvm" }, "linux"); + const env = build( + { HOME: "/home/user", NVM_DIR: "/home/user/.nvm" }, + "linux", + nonNvmExecPath, + ); + expect(env.NODE_EXTRA_CA_CERTS).toBe(expectedCaBundle); + }, + ); + + it.each(builders)( + "$name defaults NODE_EXTRA_CA_CERTS on Linux when execPath is under nvm", + ({ build }) => { + const env = build({ HOME: "/home/user" }, "linux", nvmExecPath); expect(env.NODE_EXTRA_CA_CERTS).toBe(expectedCaBundle); }, ); @@ -563,7 +586,7 @@ describe("shared Node TLS env — Linux nvm detection", () => { it.each(builders)( "$name does not default NODE_EXTRA_CA_CERTS on Linux without nvm", ({ build }) => { - const env = build({ HOME: "/home/user" }, "linux"); + const env = build({ HOME: "/home/user" }, "linux", nonNvmExecPath); expect(env.NODE_EXTRA_CA_CERTS).toBeUndefined(); }, ); @@ -578,6 +601,7 @@ describe("shared Node TLS env — Linux nvm detection", () => { NODE_EXTRA_CA_CERTS: "/custom/ca-bundle.crt", }, "linux", + nvmExecPath, ); expect(env.NODE_EXTRA_CA_CERTS).toBe("/custom/ca-bundle.crt"); }, diff --git a/src/daemon/service-env.ts b/src/daemon/service-env.ts index d9f0331ac4d..98e2d4960ea 100644 --- a/src/daemon/service-env.ts +++ b/src/daemon/service-env.ts @@ -291,10 +291,12 @@ export function buildServiceEnvironment(params: { launchdLabel?: string; platform?: NodeJS.Platform; extraPathDirs?: string[]; + /** Override process.execPath for nvm detection (testing). */ + execPath?: string; }): Record { const { env, port, launchdLabel, extraPathDirs } = params; const platform = params.platform ?? process.platform; - const sharedEnv = resolveSharedServiceEnvironmentFields(env, platform, extraPathDirs); + const sharedEnv = resolveSharedServiceEnvironmentFields(env, platform, extraPathDirs, params.execPath); const profile = env.OPENCLAW_PROFILE; const resolvedLaunchdLabel = launchdLabel || (platform === "darwin" ? resolveGatewayLaunchAgentLabel(profile) : undefined); @@ -316,10 +318,12 @@ export function buildNodeServiceEnvironment(params: { env: Record; platform?: NodeJS.Platform; extraPathDirs?: string[]; + /** Override process.execPath for nvm detection (testing). */ + execPath?: string; }): Record { const { env, extraPathDirs } = params; const platform = params.platform ?? process.platform; - const sharedEnv = resolveSharedServiceEnvironmentFields(env, platform, extraPathDirs); + const sharedEnv = resolveSharedServiceEnvironmentFields(env, platform, extraPathDirs, params.execPath); const gatewayToken = env.OPENCLAW_GATEWAY_TOKEN?.trim() || env.CLAWDBOT_GATEWAY_TOKEN?.trim() || undefined; return { @@ -359,6 +363,7 @@ function resolveSharedServiceEnvironmentFields( env: Record, platform: NodeJS.Platform, extraPathDirs: string[] | undefined, + execPath?: string, ): SharedServiceEnvironmentFields { const stateDir = env.OPENCLAW_STATE_DIR; const configPath = env.OPENCLAW_CONFIG_PATH; @@ -374,7 +379,7 @@ function resolveSharedServiceEnvironmentFields( env.NODE_EXTRA_CA_CERTS ?? (platform === "darwin" ? "/etc/ssl/cert.pem" - : platform === "linux" && isNvmNode(env) + : platform === "linux" && isNvmNode(env, execPath ?? process.execPath) ? resolveLinuxSystemCaBundle() : undefined); const nodeUseSystemCa = env.NODE_USE_SYSTEM_CA ?? (platform === "darwin" ? "1" : undefined);