diff --git a/src/agents/bash-tools.exec-gateway-approval.e2e.test.ts b/src/agents/bash-tools.exec-gateway-approval.e2e.test.ts index 936814714b3c..8f5eb948e2e2 100644 --- a/src/agents/bash-tools.exec-gateway-approval.e2e.test.ts +++ b/src/agents/bash-tools.exec-gateway-approval.e2e.test.ts @@ -17,6 +17,7 @@ import { disconnectGatewayClient, getFreeGatewayPort, } from "../gateway/test-helpers.e2e.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "../gateway/test-helpers.env.js"; import { captureEnv, setTestEnvValue } from "../test-utils/env.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; import { withTimeout } from "../utils/with-timeout.js"; @@ -25,10 +26,10 @@ import type { ExecApprovalFollowupOutcome } from "./bash-tools.exec-types.js"; const TEST_ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_TOKEN", - "OPENCLAW_GATEWAY_PORT", "OPENCLAW_SKIP_CHANNELS", "OPENCLAW_SKIP_GMAIL_WATCHER", "OPENCLAW_SKIP_CRON", diff --git a/src/gateway/gateway-acp-bind.live.test.ts b/src/gateway/gateway-acp-bind.live.test.ts index e08d151c508e..73712879bf12 100644 --- a/src/gateway/gateway-acp-bind.live.test.ts +++ b/src/gateway/gateway-acp-bind.live.test.ts @@ -47,7 +47,7 @@ const DEFAULT_LIVE_PARENT_MODEL = "openai/gpt-5.4"; type LiveAcpAgent = "claude" | "codex" | "droid" | "gemini" | "opencode"; function snapshotAcpBindLiveEnv(): LiveEnvSnapshot { - return snapshotLiveEnv(["CODEX_HOME", "OPENCLAW_GATEWAY_PORT"]); + return snapshotLiveEnv(["CODEX_HOME"]); } function resolveLiveTimeoutMs(raw: string | undefined, fallback: number): number { diff --git a/src/gateway/gateway-acp-spawn-defaults.live.test.ts b/src/gateway/gateway-acp-spawn-defaults.live.test.ts index 4f2ae4e6e09c..8f88d733b3e0 100644 --- a/src/gateway/gateway-acp-spawn-defaults.live.test.ts +++ b/src/gateway/gateway-acp-spawn-defaults.live.test.ts @@ -41,7 +41,7 @@ const LIVE_TIMEOUT_MS = resolvePositiveInteger( ); function snapshotAcpSpawnDefaultsLiveEnv(): LiveEnvSnapshot { - return snapshotLiveEnv(["CODEX_HOME", "OPENCLAW_GATEWAY_PORT"]); + return snapshotLiveEnv(["CODEX_HOME"]); } function resolvePositiveInteger(raw: string | undefined, fallback: number): number { diff --git a/src/gateway/gateway-cli-backend.live-helpers.test.ts b/src/gateway/gateway-cli-backend.live-helpers.test.ts index 3c71f0fd14bb..c3320cc3cc0e 100644 --- a/src/gateway/gateway-cli-backend.live-helpers.test.ts +++ b/src/gateway/gateway-cli-backend.live-helpers.test.ts @@ -3,6 +3,8 @@ */ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { testing as cliBackendsTesting } from "../agents/cli-backends.test-support.js"; +import { captureEnv } from "../test-utils/env.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "./test-helpers.env.js"; vi.mock("./client-start-readiness.js", () => ({ startGatewayClientWhenEventLoopReady: async (client: { start: () => void }) => { @@ -12,6 +14,7 @@ vi.mock("./client-start-readiness.js", () => ({ })); describe("gateway cli backend live helpers", () => { + const gatewayStartupEnv = captureEnv([...GATEWAY_STARTUP_MUTATED_ENV_KEYS]); let liveHelpers: typeof import("./gateway-cli-backend.live-helpers.js"); beforeAll(async () => { @@ -24,6 +27,7 @@ describe("gateway cli backend live helpers", () => { afterEach(() => { vi.useRealTimers(); + gatewayStartupEnv.restore(); cliBackendsTesting.resetDepsForTest(); delete process.env.OPENCLAW_SKIP_CHANNELS; delete process.env.OPENCLAW_SKIP_PROVIDERS; @@ -53,9 +57,15 @@ describe("gateway cli backend live helpers", () => { process.env.OPENCLAW_TEST_MINIMAL_GATEWAY = "old-minimal"; process.env.ANTHROPIC_API_KEY = "old-anthropic"; process.env.ANTHROPIC_API_KEY_OLD = "old-anthropic-old"; + process.env.PATH = "old-path"; + process.env.OPENCLAW_GATEWAY_PORT = "old-port"; + process.env.OPENCLAW_PATH_BOOTSTRAPPED = "old-bootstrap"; const snapshot = snapshotCliBackendLiveEnv(); applyCliBackendLiveEnv(new Set()); + process.env.PATH = "gateway-path"; + process.env.OPENCLAW_GATEWAY_PORT = "gateway-port"; + process.env.OPENCLAW_PATH_BOOTSTRAPPED = "1"; expect(process.env.OPENCLAW_SKIP_CHANNELS).toBe("1"); expect(process.env.OPENCLAW_SKIP_PROVIDERS).toBe("1"); @@ -80,6 +90,9 @@ describe("gateway cli backend live helpers", () => { expect(process.env.OPENCLAW_TEST_MINIMAL_GATEWAY).toBe("old-minimal"); expect(process.env.ANTHROPIC_API_KEY).toBe("old-anthropic"); expect(process.env.ANTHROPIC_API_KEY_OLD).toBe("old-anthropic-old"); + expect(process.env.PATH).toBe("old-path"); + expect(process.env.OPENCLAW_GATEWAY_PORT).toBe("old-port"); + expect(process.env.OPENCLAW_PATH_BOOTSTRAPPED).toBe("old-bootstrap"); }); it("defaults the model switch probe to Claude Sonnet -> Opus", async () => { diff --git a/src/gateway/gateway-cli-backend.live-helpers.ts b/src/gateway/gateway-cli-backend.live-helpers.ts index 5f5e4a76c8ad..86d0c10ab2fe 100644 --- a/src/gateway/gateway-cli-backend.live-helpers.ts +++ b/src/gateway/gateway-cli-backend.live-helpers.ts @@ -26,6 +26,7 @@ import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-cha import { sleep } from "../utils/sleep.js"; import { startGatewayClientWhenEventLoopReady } from "./client-start-readiness.js"; import { GatewayClient, type GatewayClientOptions } from "./client.js"; +import { restoreLiveEnv, snapshotLiveEnv, type LiveEnvSnapshot } from "./live-env-test-helpers.js"; // Aggregate docker live runs can contend on startup enough that the gateway // websocket handshake needs a wider budget than the single-provider reruns. @@ -49,21 +50,7 @@ export type CliBackendLiveModelSelection = { agentRuntime: { id: string }; }; -export type CliBackendLiveEnvSnapshot = { - configPath?: string; - stateDir?: string; - token?: string; - skipChannels?: string; - skipProviders?: string; - skipGmail?: string; - skipCron?: string; - skipCanvas?: string; - skipBrowserControl?: string; - bundledPluginsDir?: string; - minimalGateway?: string; - anthropicApiKey?: string; - anthropicApiKeyOld?: string; -}; +export type CliBackendLiveEnvSnapshot = LiveEnvSnapshot; export const CLI_BACKEND_LIVE_PROVIDER_SKIP_ENV = "OPENCLAW_LIVE_CLI_BACKEND_ALLOW_PROVIDER_SKIP"; export const CLI_BACKEND_LIVE_ADVISORY_ENV = "OPENCLAW_LIVE_CLI_BACKEND_ADVISORY"; @@ -550,21 +537,13 @@ function isRetryableGatewayConnectError(error: Error): boolean { } export function snapshotCliBackendLiveEnv(): CliBackendLiveEnvSnapshot { - return { - configPath: process.env.OPENCLAW_CONFIG_PATH, - stateDir: process.env.OPENCLAW_STATE_DIR, - token: process.env.OPENCLAW_GATEWAY_TOKEN, - skipChannels: process.env.OPENCLAW_SKIP_CHANNELS, - skipProviders: process.env.OPENCLAW_SKIP_PROVIDERS, - skipGmail: process.env.OPENCLAW_SKIP_GMAIL_WATCHER, - skipCron: process.env.OPENCLAW_SKIP_CRON, - skipCanvas: process.env.OPENCLAW_SKIP_CANVAS_HOST, - skipBrowserControl: process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER, - bundledPluginsDir: process.env.OPENCLAW_BUNDLED_PLUGINS_DIR, - minimalGateway: process.env.OPENCLAW_TEST_MINIMAL_GATEWAY, - anthropicApiKey: process.env.ANTHROPIC_API_KEY, - anthropicApiKeyOld: process.env.ANTHROPIC_API_KEY_OLD, - }; + return snapshotLiveEnv([ + "OPENCLAW_SKIP_PROVIDERS", + "OPENCLAW_BUNDLED_PLUGINS_DIR", + "OPENCLAW_TEST_MINIMAL_GATEWAY", + "ANTHROPIC_API_KEY", + "ANTHROPIC_API_KEY_OLD", + ]); } export function applyCliBackendLiveEnv(preservedEnv: ReadonlySet): void { @@ -584,27 +563,7 @@ export function applyCliBackendLiveEnv(preservedEnv: ReadonlySet): void } export function restoreCliBackendLiveEnv(snapshot: CliBackendLiveEnvSnapshot): void { - restoreEnvVar("OPENCLAW_CONFIG_PATH", snapshot.configPath); - restoreEnvVar("OPENCLAW_STATE_DIR", snapshot.stateDir); - restoreEnvVar("OPENCLAW_GATEWAY_TOKEN", snapshot.token); - restoreEnvVar("OPENCLAW_SKIP_CHANNELS", snapshot.skipChannels); - restoreEnvVar("OPENCLAW_SKIP_PROVIDERS", snapshot.skipProviders); - restoreEnvVar("OPENCLAW_SKIP_GMAIL_WATCHER", snapshot.skipGmail); - restoreEnvVar("OPENCLAW_SKIP_CRON", snapshot.skipCron); - restoreEnvVar("OPENCLAW_SKIP_CANVAS_HOST", snapshot.skipCanvas); - restoreEnvVar("OPENCLAW_SKIP_BROWSER_CONTROL_SERVER", snapshot.skipBrowserControl); - restoreEnvVar("OPENCLAW_BUNDLED_PLUGINS_DIR", snapshot.bundledPluginsDir); - restoreEnvVar("OPENCLAW_TEST_MINIMAL_GATEWAY", snapshot.minimalGateway); - restoreEnvVar("ANTHROPIC_API_KEY", snapshot.anthropicApiKey); - restoreEnvVar("ANTHROPIC_API_KEY_OLD", snapshot.anthropicApiKeyOld); -} - -function restoreEnvVar(name: string, value: string | undefined): void { - if (value === undefined) { - delete process.env[name]; - return; - } - process.env[name] = value; + restoreLiveEnv(snapshot); } export async function ensurePairedTestGatewayClientIdentity(params?: { diff --git a/src/gateway/gateway-models.profiles.live.test.ts b/src/gateway/gateway-models.profiles.live.test.ts index a8e29ba2937b..967a9306a316 100644 --- a/src/gateway/gateway-models.profiles.live.test.ts +++ b/src/gateway/gateway-models.profiles.live.test.ts @@ -80,6 +80,8 @@ import { deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js"; import { getFreePort, isPortFree } from "../test-utils/ports.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; import { GatewayClient } from "./client.js"; +import { restoreLiveEnv, snapshotLiveEnv } from "./live-env-test-helpers.js"; +import type { GatewayServer } from "./server-public.js"; type ProviderThinkingModelCompat = { thinkingFormat?: string; @@ -92,7 +94,6 @@ import { shouldRetryExecReadProbe, shouldRetryToolReadProbe, } from "./live-tool-probe.test-helpers.js"; -import { startGatewayServer } from "./server.impl.js"; import { readSessionMessagesAsync } from "./session-transcript-readers.js"; import { loadSessionEntry } from "./session-utils.js"; @@ -4570,25 +4571,19 @@ async function runGatewayModelSuite(params: GatewayModelSuiteParams) { ); } const [ultraUpstreamBaseUrl] = [...ultraUpstreamBaseUrls]; - const previous = { - configPath: process.env.OPENCLAW_CONFIG_PATH, - token: process.env.OPENCLAW_GATEWAY_TOKEN, - skipChannels: process.env.OPENCLAW_SKIP_CHANNELS, - skipGmail: process.env.OPENCLAW_SKIP_GMAIL_WATCHER, - skipCron: process.env.OPENCLAW_SKIP_CRON, - skipCanvas: process.env.OPENCLAW_SKIP_CANVAS_HOST, - disableBonjour: process.env.OPENCLAW_DISABLE_BONJOUR, - logLevel: process.env.OPENCLAW_LOG_LEVEL, - agentDir: process.env.OPENCLAW_AGENT_DIR, - stateDir: process.env.OPENCLAW_STATE_DIR, - }; + const previousEnv = snapshotLiveEnv([ + "OPENCLAW_DISABLE_BONJOUR", + "OPENCLAW_LOG_LEVEL", + "OPENCLAW_AGENT_DIR", + ]); + const { startGatewayServer } = await import("./server.impl.js"); let runtimeEnv: ReturnType | undefined; let cleanupTempStateDir: string | undefined; let cleanupTempAgentDir: string | undefined; let cleanupToolProbePath: string | undefined; let cleanupTempDir: string | undefined; let ultraWireCapture: OpenAIUltraWireCapture | undefined; - let server: Awaited> | undefined; + let server: GatewayServer | undefined; let client: GatewayClient | undefined; try { @@ -5481,16 +5476,7 @@ async function runGatewayModelSuite(params: GatewayModelSuiteParams) { if (runtimeEnv) { restoreProductionEnvForLiveRun(runtimeEnv); } - restoreOptionalEnv("OPENCLAW_CONFIG_PATH", previous.configPath); - restoreOptionalEnv("OPENCLAW_GATEWAY_TOKEN", previous.token); - restoreOptionalEnv("OPENCLAW_SKIP_CHANNELS", previous.skipChannels); - restoreOptionalEnv("OPENCLAW_SKIP_GMAIL_WATCHER", previous.skipGmail); - restoreOptionalEnv("OPENCLAW_SKIP_CRON", previous.skipCron); - restoreOptionalEnv("OPENCLAW_SKIP_CANVAS_HOST", previous.skipCanvas); - restoreOptionalEnv("OPENCLAW_DISABLE_BONJOUR", previous.disableBonjour); - restoreOptionalEnv("OPENCLAW_LOG_LEVEL", previous.logLevel); - restoreOptionalEnv("OPENCLAW_AGENT_DIR", previous.agentDir); - restoreOptionalEnv("OPENCLAW_STATE_DIR", previous.stateDir); + restoreLiveEnv(previousEnv); } } } @@ -5747,16 +5733,8 @@ describeLive("gateway live (dev agent, profile keys)", () => { } clearRuntimeConfigSnapshot(); const runtimeEnv = enterProductionEnvForLiveRun(); - const previous = { - configPath: process.env.OPENCLAW_CONFIG_PATH, - token: process.env.OPENCLAW_GATEWAY_TOKEN, - skipChannels: process.env.OPENCLAW_SKIP_CHANNELS, - skipGmail: process.env.OPENCLAW_SKIP_GMAIL_WATCHER, - skipCron: process.env.OPENCLAW_SKIP_CRON, - skipCanvas: process.env.OPENCLAW_SKIP_CANVAS_HOST, - agentDir: process.env.OPENCLAW_AGENT_DIR, - stateDir: process.env.OPENCLAW_STATE_DIR, - }; + const previousEnv = snapshotLiveEnv(["OPENCLAW_AGENT_DIR"]); + const { startGatewayServer } = await import("./server.impl.js"); process.env.OPENCLAW_SKIP_CHANNELS = "1"; process.env.OPENCLAW_SKIP_GMAIL_WATCHER = "1"; @@ -5766,7 +5744,7 @@ describeLive("gateway live (dev agent, profile keys)", () => { const token = `test-${randomUUID()}`; process.env.OPENCLAW_GATEWAY_TOKEN = token; - let server: Awaited> | undefined; + let server: GatewayServer | undefined; let client: GatewayClient | undefined; let toolProbePath: string | undefined; let tempDir: string | undefined; @@ -5972,14 +5950,7 @@ describeLive("gateway live (dev agent, profile keys)", () => { }); } - restoreOptionalEnv("OPENCLAW_CONFIG_PATH", previous.configPath); - restoreOptionalEnv("OPENCLAW_GATEWAY_TOKEN", previous.token); - restoreOptionalEnv("OPENCLAW_SKIP_CHANNELS", previous.skipChannels); - restoreOptionalEnv("OPENCLAW_SKIP_GMAIL_WATCHER", previous.skipGmail); - restoreOptionalEnv("OPENCLAW_SKIP_CRON", previous.skipCron); - restoreOptionalEnv("OPENCLAW_SKIP_CANVAS_HOST", previous.skipCanvas); - restoreOptionalEnv("OPENCLAW_AGENT_DIR", previous.agentDir); - restoreOptionalEnv("OPENCLAW_STATE_DIR", previous.stateDir); + restoreLiveEnv(previousEnv); } }, 180_000); }); diff --git a/src/gateway/gateway-wizard-cancel.e2e.test.ts b/src/gateway/gateway-wizard-cancel.e2e.test.ts index 29eb0f1e846d..b79b461553f3 100644 --- a/src/gateway/gateway-wizard-cancel.e2e.test.ts +++ b/src/gateway/gateway-wizard-cancel.e2e.test.ts @@ -15,10 +15,12 @@ import { disconnectGatewayClient, getFreeGatewayPort, } from "./test-helpers.e2e.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "./test-helpers.env.js"; const GATEWAY_E2E_TIMEOUT_MS = 90_000; const ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_TOKEN", diff --git a/src/gateway/gateway.test.ts b/src/gateway/gateway.test.ts index a38d49922c68..a2c01d97ef94 100644 --- a/src/gateway/gateway.test.ts +++ b/src/gateway/gateway.test.ts @@ -28,6 +28,7 @@ import { getFreeGatewayPort, startGatewayWithClient, } from "./test-helpers.e2e.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "./test-helpers.env.js"; import { installOpenAiResponsesMock } from "./test-helpers.openai-mock.js"; import { buildMockOpenAiResponsesProvider } from "./test-openai-responses-model.js"; @@ -36,6 +37,7 @@ const GATEWAY_E2E_TIMEOUT_MS = 90_000; let gatewayTestSeq = 0; const GATEWAY_TEST_ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_TOKEN", @@ -986,17 +988,7 @@ module.exports = { { timeout: GATEWAY_E2E_TIMEOUT_MS }, async () => { const envSnapshot = captureEnv([ - "HOME", - "OPENCLAW_STATE_DIR", - "OPENCLAW_CONFIG_PATH", - "OPENCLAW_GATEWAY_TOKEN", - "OPENCLAW_SKIP_CHANNELS", - "OPENCLAW_SKIP_GMAIL_WATCHER", - "OPENCLAW_SKIP_CRON", - "OPENCLAW_SKIP_CANVAS_HOST", - "OPENCLAW_SKIP_BROWSER_CONTROL_SERVER", - "OPENCLAW_SKIP_PROVIDERS", - "OPENCLAW_BUNDLED_PLUGINS_DIR", + ...GATEWAY_TEST_ENV_KEYS, "OPENCLAW_TEST_MINIMAL_GATEWAY", "DISCORD_BOT_TOKEN", ]); diff --git a/src/gateway/live-env-test-helpers.ts b/src/gateway/live-env-test-helpers.ts index 35b1b6b13c64..0ebfcf9aa2cc 100644 --- a/src/gateway/live-env-test-helpers.ts +++ b/src/gateway/live-env-test-helpers.ts @@ -2,8 +2,10 @@ * Environment snapshot helpers for live gateway tests. */ import { deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "./test-helpers.env.js"; const COMMON_LIVE_ENV_NAMES = [ + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_AGENT_RUNTIME", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_TOKEN", diff --git a/src/gateway/operator-approvals-client.e2e.test.ts b/src/gateway/operator-approvals-client.e2e.test.ts index a037dd8dbe94..18c7fbdd9346 100644 --- a/src/gateway/operator-approvals-client.e2e.test.ts +++ b/src/gateway/operator-approvals-client.e2e.test.ts @@ -24,15 +24,16 @@ import { disconnectGatewayClient, getFreeGatewayPort, } from "./test-helpers.e2e.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "./test-helpers.env.js"; const TEST_ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_URL", "OPENCLAW_GATEWAY_TOKEN", "OPENCLAW_GATEWAY_PASSWORD", - "OPENCLAW_GATEWAY_PORT", ]; type Cleanup = () => Promise | void; diff --git a/src/gateway/server-methods/plugin-approval-turn-source-routing.e2e.test.ts b/src/gateway/server-methods/plugin-approval-turn-source-routing.e2e.test.ts index ba04dc86ecfd..0e31b3945218 100644 --- a/src/gateway/server-methods/plugin-approval-turn-source-routing.e2e.test.ts +++ b/src/gateway/server-methods/plugin-approval-turn-source-routing.e2e.test.ts @@ -22,15 +22,16 @@ import { disconnectGatewayClient, getFreeGatewayPort, } from "../test-helpers.e2e.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "../test-helpers.env.js"; const TEST_ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_URL", "OPENCLAW_GATEWAY_TOKEN", "OPENCLAW_GATEWAY_PASSWORD", - "OPENCLAW_GATEWAY_PORT", ]; describe("plugin.approval.request delivery routing (real gateway)", () => { diff --git a/src/gateway/server-network-runtime.e2e.test.ts b/src/gateway/server-network-runtime.e2e.test.ts index 7ba40d930e16..6311bc9f761c 100644 --- a/src/gateway/server-network-runtime.e2e.test.ts +++ b/src/gateway/server-network-runtime.e2e.test.ts @@ -11,9 +11,11 @@ import { PROXY_ENV_KEYS } from "../infra/net/proxy-env.js"; import { captureEnv, deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js"; import { startGatewayServer } from "./server.js"; import { getFreeGatewayPort } from "./test-helpers.e2e.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "./test-helpers.env.js"; const NETWORK_GATEWAY_ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_TOKEN", diff --git a/src/gateway/server-start.ts b/src/gateway/server-start.ts index 6c489e10c9ad..4a6d87eb2bbe 100644 --- a/src/gateway/server-start.ts +++ b/src/gateway/server-start.ts @@ -30,8 +30,6 @@ export async function resetPreparedModelCatalogForTest(): Promise { await resetPreparedModelCatalogForTestLocal(); } -ensureOpenClawCliOnPath(); - const loadGatewayStartupEarlyModule = createLazyRuntimeModule( () => import("./server-startup-early.js"), ); @@ -108,6 +106,7 @@ export async function startGatewayServer( port = 18789, opts: GatewayServerOptions = {}, ): Promise { + ensureOpenClawCliOnPath(); let releasePostReadyWork: () => void = () => {}; const postReadyWorkBarrier = new Promise((resolve) => { releasePostReadyWork = resolve; diff --git a/src/gateway/server.control-ui-device-auth-migration.test.ts b/src/gateway/server.control-ui-device-auth-migration.test.ts index dce444298afc..97f10c37dab9 100644 --- a/src/gateway/server.control-ui-device-auth-migration.test.ts +++ b/src/gateway/server.control-ui-device-auth-migration.test.ts @@ -12,8 +12,8 @@ import { signDevicePayload, } from "../infra/device-identity.js"; import { buildDeviceAuthPayload } from "./device-auth.js"; +import { shouldRetainControlUiDeviceAuthMigrationSession } from "./server-public.js"; import { CONTROL_UI_CLIENT } from "./server.auth.test-helpers.js"; -import { shouldRetainControlUiDeviceAuthMigrationSession } from "./server.impl.js"; import { connectReq, createGatewaySuiteHarness, diff --git a/src/gateway/test-helpers.e2e.ts b/src/gateway/test-helpers.e2e.ts index 26384e2c0303..635a46c90190 100644 --- a/src/gateway/test-helpers.e2e.ts +++ b/src/gateway/test-helpers.e2e.ts @@ -15,6 +15,7 @@ import { signDevicePayload, } from "../infra/device-identity.js"; import { rawDataToString } from "../infra/ws.js"; +import { captureEnv } from "../test-utils/env.js"; import { getDeterministicFreePortBlock } from "../test-utils/ports.js"; import { GATEWAY_CLIENT_MODES, @@ -25,6 +26,7 @@ import { import { GatewayClient } from "./client.js"; import { buildDeviceAuthPayloadV3 } from "./device-auth.js"; import { startGatewayServer } from "./server.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "./test-helpers.env.js"; /** Reserve a deterministic free port block for Gateway E2E tests. */ export async function getFreeGatewayPort(): Promise { @@ -263,23 +265,47 @@ export async function startGatewayWithClient(params: { token: string; clientDisplayName?: string; }) { - await writeFile(params.configPath, `${JSON.stringify(params.cfg, null, 2)}\n`); - process.env.OPENCLAW_CONFIG_PATH = params.configPath; - clearRuntimeConfigSnapshot(); - clearConfigCache(); - clearSessionStoreCacheForTest(); + const gatewayStartupEnv = captureEnv([...GATEWAY_STARTUP_MUTATED_ENV_KEYS]); + let server: Awaited> | undefined; + try { + await writeFile(params.configPath, `${JSON.stringify(params.cfg, null, 2)}\n`); + process.env.OPENCLAW_CONFIG_PATH = params.configPath; + clearRuntimeConfigSnapshot(); + clearConfigCache(); + clearSessionStoreCacheForTest(); - const port = await getFreeGatewayPort(); - const server = await startGatewayServer(port, { - bind: "loopback", - auth: { mode: "token", token: params.token }, - controlUiEnabled: false, - }); - const client = await connectGatewayClient({ - url: `ws://127.0.0.1:${port}`, - token: params.token, - clientDisplayName: params.clientDisplayName, - }); + const port = await getFreeGatewayPort(); + const startedServer = await startGatewayServer(port, { + bind: "loopback", + auth: { mode: "token", token: params.token }, + controlUiEnabled: false, + }); + server = startedServer; + const client = await connectGatewayClient({ + url: `ws://127.0.0.1:${port}`, + token: params.token, + clientDisplayName: params.clientDisplayName, + }); - return { port, server, client }; + return { + port, + client, + server: { + close: async (...args: Parameters) => { + try { + await startedServer.close(...args); + } finally { + gatewayStartupEnv.restore(); + } + }, + }, + }; + } catch (error) { + try { + await server?.close({ reason: "gateway E2E client setup failed" }); + } finally { + gatewayStartupEnv.restore(); + } + throw error; + } } diff --git a/src/gateway/test-helpers.env.ts b/src/gateway/test-helpers.env.ts new file mode 100644 index 000000000000..fe71d15e7f68 --- /dev/null +++ b/src/gateway/test-helpers.env.ts @@ -0,0 +1,12 @@ +// Gateway startup rewrites these process-wide values. Manual in-process test +// owners must snapshot them so later files never inherit a closed server or stale PATH. +export const GATEWAY_STARTUP_MUTATED_ENV_KEYS = [ + "PATH", + "OPENCLAW_GATEWAY_PORT", + "OPENCLAW_PATH_BOOTSTRAPPED", +] as const; + +/** Captures values that in-process Gateway startup can mutate. */ +export function snapshotGatewayStartupEnv(): Record { + return Object.fromEntries(GATEWAY_STARTUP_MUTATED_ENV_KEYS.map((key) => [key, process.env[key]])); +} diff --git a/src/gateway/test-helpers.server-env.test.ts b/src/gateway/test-helpers.server-env.test.ts new file mode 100644 index 000000000000..dd172b7ef975 --- /dev/null +++ b/src/gateway/test-helpers.server-env.test.ts @@ -0,0 +1,68 @@ +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js"; +import { disconnectGatewayClient, startGatewayWithClient } from "./test-helpers.e2e.js"; +import { installGatewayTestHooks, withGatewayServer } from "./test-helpers.server.js"; + +const envBeforeSuite = { + PATH: process.env.PATH, + OPENCLAW_GATEWAY_PORT: process.env.OPENCLAW_GATEWAY_PORT, + OPENCLAW_PATH_BOOTSTRAPPED: process.env.OPENCLAW_PATH_BOOTSTRAPPED, +}; + +installGatewayTestHooks(); + +describe("Gateway test environment lifecycle", () => { + it("records the process-wide startup environment", async () => { + await withGatewayServer(async ({ port }) => { + expect(process.env.OPENCLAW_GATEWAY_PORT).toBe(String(port)); + expect(process.env.OPENCLAW_PATH_BOOTSTRAPPED).toBe("1"); + }); + }); + + it("restores startup-owned environment before the next test", () => { + expect({ + PATH: process.env.PATH, + OPENCLAW_GATEWAY_PORT: process.env.OPENCLAW_GATEWAY_PORT, + OPENCLAW_PATH_BOOTSTRAPPED: process.env.OPENCLAW_PATH_BOOTSTRAPPED, + }).toEqual(envBeforeSuite); + }); + + it("restores startup-owned environment when a direct E2E server closes", async () => { + const stateDir = process.env.OPENCLAW_STATE_DIR; + if (!stateDir) { + throw new Error("OPENCLAW_STATE_DIR is required"); + } + setTestEnvValue("PATH", process.env.PATH ?? ""); + deleteTestEnvValue("OPENCLAW_PATH_BOOTSTRAPPED"); + const envBeforeServer = { + PATH: process.env.PATH, + OPENCLAW_GATEWAY_PORT: process.env.OPENCLAW_GATEWAY_PORT, + OPENCLAW_PATH_BOOTSTRAPPED: process.env.OPENCLAW_PATH_BOOTSTRAPPED, + }; + const token = "test-gateway-token-1234567890"; + for (const attempt of ["first", "second"]) { + const started = await startGatewayWithClient({ + cfg: { gateway: { auth: { mode: "token", token } } }, + configPath: path.join(stateDir, "openclaw.json"), + token, + }); + + try { + expect(process.env.OPENCLAW_GATEWAY_PORT).toBe(String(started.port)); + expect(process.env.OPENCLAW_PATH_BOOTSTRAPPED).toBe("1"); + } finally { + await disconnectGatewayClient(started.client).catch(() => undefined); + await started.server.close({ + reason: `${attempt} direct E2E environment proof complete`, + }); + } + + expect({ + PATH: process.env.PATH, + OPENCLAW_GATEWAY_PORT: process.env.OPENCLAW_GATEWAY_PORT, + OPENCLAW_PATH_BOOTSTRAPPED: process.env.OPENCLAW_PATH_BOOTSTRAPPED, + }).toEqual(envBeforeServer); + } + }); +}); diff --git a/src/gateway/test-helpers.server.ts b/src/gateway/test-helpers.server.ts index 2e379d3e3908..b221386b9d73 100644 --- a/src/gateway/test-helpers.server.ts +++ b/src/gateway/test-helpers.server.ts @@ -60,6 +60,7 @@ import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-cha import { buildDeviceAuthPayloadV3 } from "./device-auth.js"; import type { GatewayServerOptions } from "./server.js"; import { invalidateSessionSharingSnapshot } from "./session-sharing.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "./test-helpers.env.js"; import { resetTestPluginRegistry } from "./test-helpers.plugin-registry.js"; import { agentCommand, @@ -80,6 +81,7 @@ const getServerModule = createLazyRuntimeModule(() => import("./server.js")); const GATEWAY_TEST_ENV_KEYS = [ "HOME", "USERPROFILE", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_AGENT_DIR", diff --git a/src/test-utils/openclaw-test-state.test.ts b/src/test-utils/openclaw-test-state.test.ts index 398b4a436f84..e52e8608ab2e 100644 --- a/src/test-utils/openclaw-test-state.test.ts +++ b/src/test-utils/openclaw-test-state.test.ts @@ -4,6 +4,10 @@ import os from "node:os"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { loadPersistedAuthProfileStore } from "../agents/auth-profiles/persisted.js"; +import { + GATEWAY_STARTUP_MUTATED_ENV_KEYS, + snapshotGatewayStartupEnv, +} from "../gateway/test-helpers.env.js"; import { closeOpenClawAgentDatabaseByPath, openOpenClawAgentDatabase, @@ -12,7 +16,7 @@ import { closeOpenClawStateDatabaseByPath, openOpenClawStateDatabase, } from "../state/openclaw-state-db.js"; -import { withEnvAsync } from "./env.js"; +import { setTestEnvValue, withEnvAsync } from "./env.js"; import { createOpenClawTestState, withOpenClawTestState } from "./openclaw-test-state.js"; async function expectPathMissing(targetPath: string): Promise { @@ -31,6 +35,7 @@ describe("openclaw test state", () => { const previousOpenClawHome = process.env.OPENCLAW_HOME; const previousStateDir = process.env.OPENCLAW_STATE_DIR; const previousConfigPath = process.env.OPENCLAW_CONFIG_PATH; + const previousGatewayStartupEnv = snapshotGatewayStartupEnv(); const state = await createOpenClawTestState({ label: "unit", @@ -49,6 +54,9 @@ describe("openclaw test state", () => { expect(process.env.HOME).toBe(state.home); expect(process.env.OPENCLAW_HOME).toBe(state.home); expect(JSON.parse(await fs.readFile(state.configPath, "utf8"))).toStrictEqual({}); + for (const key of GATEWAY_STARTUP_MUTATED_ENV_KEYS) { + setTestEnvValue(key, `mutated-${key}`); + } } finally { await state.cleanup(); } @@ -57,6 +65,7 @@ describe("openclaw test state", () => { expect(process.env.OPENCLAW_HOME).toBe(previousOpenClawHome); expect(process.env.OPENCLAW_STATE_DIR).toBe(previousStateDir); expect(process.env.OPENCLAW_CONFIG_PATH).toBe(previousConfigPath); + expect(snapshotGatewayStartupEnv()).toEqual(previousGatewayStartupEnv); await expectPathMissing(state.root); }); diff --git a/src/test-utils/openclaw-test-state.ts b/src/test-utils/openclaw-test-state.ts index aa08a4e1691b..7b3e775ac8f5 100644 --- a/src/test-utils/openclaw-test-state.ts +++ b/src/test-utils/openclaw-test-state.ts @@ -7,6 +7,7 @@ import { resolveAuthProfileDatabasePath } from "../agents/auth-profiles/sqlite.j import { saveAuthProfileStore } from "../agents/auth-profiles/store.js"; import type { AuthProfileStore } from "../agents/auth-profiles/types.js"; import * as configRuntime from "../config/config.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "../gateway/test-helpers.env.js"; import { isPathInside } from "../infra/path-guards.js"; import { closeOpenClawAgentDatabaseByPath, @@ -72,6 +73,7 @@ const ENV_KEYS = [ "USERPROFILE", "HOMEDRIVE", "HOMEPATH", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_HOME", "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", diff --git a/test/e2e/qa-lab/runtime/gateway-agent-artifact-apis.e2e.test.ts b/test/e2e/qa-lab/runtime/gateway-agent-artifact-apis.e2e.test.ts index a437ee853c62..7bfce2d477f7 100644 --- a/test/e2e/qa-lab/runtime/gateway-agent-artifact-apis.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-agent-artifact-apis.e2e.test.ts @@ -18,6 +18,7 @@ import { disconnectGatewayClient, getFreeGatewayPort, } from "../../../../src/gateway/test-helpers.e2e.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "../../../../src/gateway/test-helpers.env.js"; import { closeOpenClawAgentDatabasesForTest } from "../../../../src/state/openclaw-agent-db.js"; import { closeOpenClawStateDatabaseForTest } from "../../../../src/state/openclaw-state-db.js"; import { createTaskRecord, deleteTaskRecordById } from "../../../../src/tasks/task-registry.js"; @@ -120,11 +121,11 @@ vi.mock("../../../../src/gateway/server-request-context.js", async () => { const ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_TOKEN", "OPENCLAW_GATEWAY_PASSWORD", - "OPENCLAW_GATEWAY_PORT", "OPENCLAW_SKIP_CHANNELS", "OPENCLAW_SKIP_GMAIL_WATCHER", "OPENCLAW_SKIP_CRON", diff --git a/test/e2e/qa-lab/runtime/gateway-hosted-web.e2e.test.ts b/test/e2e/qa-lab/runtime/gateway-hosted-web.e2e.test.ts index f785cd22ffc9..ad8f38d36bab 100644 --- a/test/e2e/qa-lab/runtime/gateway-hosted-web.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-hosted-web.e2e.test.ts @@ -15,6 +15,7 @@ import { disconnectGatewayClient, getFreeGatewayPort, } from "../../../../src/gateway/test-helpers.e2e.js"; +import { snapshotGatewayStartupEnv } from "../../../../src/gateway/test-helpers.env.js"; import { registerPluginHttpRoute, withPluginHttpRouteRegistry, @@ -139,6 +140,7 @@ describe("Gateway hosted web surfaces", () => { await withEnvAsync( { + ...snapshotGatewayStartupEnv(), HOME: root, OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1", diff --git a/test/e2e/qa-lab/runtime/gateway-loopback-lan-access.test.ts b/test/e2e/qa-lab/runtime/gateway-loopback-lan-access.test.ts index a3b7c84ab3c3..9e78d4ad3808 100644 --- a/test/e2e/qa-lab/runtime/gateway-loopback-lan-access.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-loopback-lan-access.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import { snapshotGatewayStartupEnv } from "../../../../src/gateway/test-helpers.env.js"; import { assertGatewayLoopbackLanProof, parseGatewayLoopbackLanOptions, @@ -36,7 +37,9 @@ describe("Gateway loopback and LAN access producer", () => { }); it("proves real loopback isolation, LAN reachability, and shared-token authentication", async () => { + const gatewayStartupEnv = snapshotGatewayStartupEnv(); const proof = await runGatewayLoopbackLanProof(); + expect(snapshotGatewayStartupEnv()).toEqual(gatewayStartupEnv); expect(proof).toEqual({ loopback: { authenticatedHealthRpc: true, diff --git a/test/e2e/qa-lab/runtime/gateway-loopback-lan-access.ts b/test/e2e/qa-lab/runtime/gateway-loopback-lan-access.ts index 31230ef5d348..b336ab9c1e1c 100644 --- a/test/e2e/qa-lab/runtime/gateway-loopback-lan-access.ts +++ b/test/e2e/qa-lab/runtime/gateway-loopback-lan-access.ts @@ -13,6 +13,7 @@ import { clearSessionStoreCacheForTest } from "../../../../src/config/sessions/s import { pickPrimaryLanIPv4 } from "../../../../src/gateway/net.js"; import { startGatewayServer, type GatewayServer } from "../../../../src/gateway/server.js"; import { getFreeGatewayPort } from "../../../../src/gateway/test-helpers.e2e.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "../../../../src/gateway/test-helpers.env.js"; import { resetAgentEventsForTest } from "../../../../src/infra/agent-events.js"; import { rawDataToString } from "../../../../src/infra/ws.js"; import { captureEnv, deleteTestEnvValue, setTestEnvValue } from "../../../../src/test-utils/env.js"; @@ -27,6 +28,7 @@ const SCENARIO_ID = "gateway-loopback-lan-access"; const PROBE_TIMEOUT_MS = 10_000; const ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_GATEWAY_TOKEN", diff --git a/test/e2e/qa-lab/runtime/gateway-ssh-tunnels.test.ts b/test/e2e/qa-lab/runtime/gateway-ssh-tunnels.test.ts index adcb71d492f7..649f0b7eb4c6 100644 --- a/test/e2e/qa-lab/runtime/gateway-ssh-tunnels.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-ssh-tunnels.test.ts @@ -9,6 +9,7 @@ import os from "node:os"; import path from "node:path"; import { promisify } from "node:util"; import { afterEach, describe, expect, it } from "vitest"; +import { snapshotGatewayStartupEnv } from "../../../../src/gateway/test-helpers.env.js"; import { withEnvAsync } from "../../../../src/test-utils/env.js"; import { waitForFile } from "../../../helpers/process-wait.js"; import { useAutoCleanupTempDirTracker } from "../../../helpers/temp-dir.js"; @@ -171,11 +172,13 @@ describeOnTestbox("Gateway SSH tunnel QA producer", () => { it("proves real forwarding, cleanup, and operator diagnostics", async () => { const artifactBase = tempDirs.make("openclaw-gateway-ssh-evidence-"); + const gatewayStartupEnv = snapshotGatewayStartupEnv(); const evidence = await runGatewaySshTunnels({ artifactBase, repoRoot: process.cwd(), }); + expect(snapshotGatewayStartupEnv()).toEqual(gatewayStartupEnv); expect(evidence.entries).toHaveLength(1); expect(evidence.entries[0]?.result.status).toBe("pass"); const summary = JSON.parse( diff --git a/test/e2e/qa-lab/runtime/gateway-ssh-tunnels.ts b/test/e2e/qa-lab/runtime/gateway-ssh-tunnels.ts index d01b25c9b09c..2de25f208f26 100644 --- a/test/e2e/qa-lab/runtime/gateway-ssh-tunnels.ts +++ b/test/e2e/qa-lab/runtime/gateway-ssh-tunnels.ts @@ -13,6 +13,7 @@ import { import { gatewayStatusCommand } from "../../../../src/commands/gateway-status.js"; import { clearConfigCache, clearRuntimeConfigSnapshot } from "../../../../src/config/config.js"; import { startGatewayServer } from "../../../../src/gateway/server.js"; +import { snapshotGatewayStartupEnv } from "../../../../src/gateway/test-helpers.env.js"; import { formatErrorMessage } from "../../../../src/infra/errors.js"; import type { OutputRuntimeEnv } from "../../../../src/runtime.js"; import { withEnvAsync } from "../../../../src/test-utils/env.js"; @@ -581,6 +582,7 @@ export async function runGatewaySshTunnels( const result = await withEnvAsync( { + ...snapshotGatewayStartupEnv(), HOME: homeDir, OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1", diff --git a/test/e2e/qa-lab/runtime/gateway-tls-pinning.test.ts b/test/e2e/qa-lab/runtime/gateway-tls-pinning.test.ts index 597ce2dce0a9..d754ea323f5b 100644 --- a/test/e2e/qa-lab/runtime/gateway-tls-pinning.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-tls-pinning.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; +import { snapshotGatewayStartupEnv } from "../../../../src/gateway/test-helpers.env.js"; import { runGatewayTlsPinningProducer } from "./gateway-tls-pinning.js"; const tempDirs: string[] = []; @@ -14,12 +15,14 @@ describe("Gateway TLS pinning evidence", () => { it("proves the live listener fingerprint and public client pin policy", async () => { const artifactBase = await fs.mkdtemp(path.join(os.tmpdir(), "gateway-tls-pinning-evidence-")); tempDirs.push(artifactBase); + const gatewayStartupEnv = snapshotGatewayStartupEnv(); const evidence = await runGatewayTlsPinningProducer({ artifactBase, repoRoot: process.cwd(), }); + expect(snapshotGatewayStartupEnv()).toEqual(gatewayStartupEnv); expect(evidence.entries[0]?.result.status).toBe("pass"); const proof = JSON.parse( await fs.readFile(path.join(artifactBase, "gateway-tls-pinning-summary.json"), "utf8"), diff --git a/test/e2e/qa-lab/runtime/gateway-tls-pinning.ts b/test/e2e/qa-lab/runtime/gateway-tls-pinning.ts index 5b28b7ac73c5..2fe5247b9b5b 100644 --- a/test/e2e/qa-lab/runtime/gateway-tls-pinning.ts +++ b/test/e2e/qa-lab/runtime/gateway-tls-pinning.ts @@ -13,6 +13,7 @@ import { } from "../../../../extensions/qa-lab/api.js"; import { clearConfigCache, clearRuntimeConfigSnapshot } from "../../../../src/config/config.js"; import { startGatewayServer } from "../../../../src/gateway/server.js"; +import { GATEWAY_STARTUP_MUTATED_ENV_KEYS } from "../../../../src/gateway/test-helpers.env.js"; import { formatErrorMessage } from "../../../../src/infra/errors.js"; import { normalizeFingerprint } from "../../../../src/infra/tls/fingerprint.js"; import { loadGatewayTlsRuntime } from "../../../../src/infra/tls/gateway.js"; @@ -24,6 +25,7 @@ const DISCOVERY_PLUGIN_ID = "tls-discovery-proof"; const CONNECTION_TIMEOUT_MS = 15_000; const ENV_KEYS = [ "HOME", + ...GATEWAY_STARTUP_MUTATED_ENV_KEYS, "NODE_ENV", "OPENCLAW_CONFIG_PATH", "OPENCLAW_STATE_DIR",