diff --git a/src/cli/gateway-cli/run.ts b/src/cli/gateway-cli/run.ts index 6c0d2277f45..634a5b40a67 100644 --- a/src/cli/gateway-cli/run.ts +++ b/src/cli/gateway-cli/run.ts @@ -4,7 +4,7 @@ import path from "node:path"; import type { GatewayAuthMode } from "../../config/config.js"; import type { GatewayWsLogStyle } from "../../gateway/ws-logging.js"; import { - CONFIG_PATH, + getConfigPath, loadConfig, readConfigFileSnapshot, resolveStateDir, @@ -161,7 +161,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) { const tokenRaw = toOptionString(opts.token); const snapshot = await readConfigFileSnapshot().catch(() => null); - const configExists = snapshot?.exists ?? fs.existsSync(CONFIG_PATH); + const configExists = snapshot?.exists ?? fs.existsSync(getConfigPath()); const configAuditPath = path.join(resolveStateDir(process.env), "logs", "config-audit.jsonl"); const mode = cfg.gateway?.mode; if (!opts.allowUnconfigured && mode !== "local") { diff --git a/src/config/paths.ts b/src/config/paths.ts index 18f8ab92663..fe0fe867124 100644 --- a/src/config/paths.ts +++ b/src/config/paths.ts @@ -182,8 +182,21 @@ export function resolveConfigPath( return path.join(stateDir, CONFIG_FILENAME); } +/** + * @deprecated Use resolveConfigPathCandidate() instead. This constant is evaluated + * at module load time and does not respect OPENCLAW_HOME set after import. + * Kept for backwards compatibility but should be avoided in new code. + */ export const CONFIG_PATH = resolveConfigPathCandidate(); +/** + * Runtime-evaluated config path that respects OPENCLAW_HOME. + * Use this instead of CONFIG_PATH when OPENCLAW_HOME may be set dynamically. + */ +export function getConfigPath(env: NodeJS.ProcessEnv = process.env): string { + return resolveConfigPathCandidate(env, envHomedir(env)); +} + /** * Resolve default config path candidates across default locations. * Order: explicit config path → state-dir-derived paths → new default. @@ -258,6 +271,22 @@ export function resolveGatewayPort( cfg?: OpenClawConfig, env: NodeJS.ProcessEnv = process.env, ): number { + // When OPENCLAW_HOME is set (isolated instance), prefer config over inherited env vars. + // This prevents a parent gateway's OPENCLAW_GATEWAY_PORT from bleeding through. + const isIsolatedInstance = Boolean(env.OPENCLAW_HOME?.trim()); + + // Config port takes precedence for isolated instances + const configPort = cfg?.gateway?.port; + if ( + isIsolatedInstance && + typeof configPort === "number" && + Number.isFinite(configPort) && + configPort > 0 + ) { + return configPort; + } + + // Check env vars (for non-isolated or when config doesn't specify port) const envRaw = env.OPENCLAW_GATEWAY_PORT?.trim() || env.CLAWDBOT_GATEWAY_PORT?.trim(); if (envRaw) { const parsed = Number.parseInt(envRaw, 10); @@ -265,11 +294,11 @@ export function resolveGatewayPort( return parsed; } } - const configPort = cfg?.gateway?.port; - if (typeof configPort === "number" && Number.isFinite(configPort)) { - if (configPort > 0) { - return configPort; - } + + // Fall back to config for non-isolated instances + if (typeof configPort === "number" && Number.isFinite(configPort) && configPort > 0) { + return configPort; } + return DEFAULT_GATEWAY_PORT; }