From 7ac7b39efff19d2c5c36344e51b747fa9394ac58 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 8 Mar 2026 00:27:41 +0000 Subject: [PATCH] refactor(daemon): extract gateway token drift helper --- .../daemon-cli/gateway-token-drift.test.ts | 23 +++++++++++++++++++ src/cli/daemon-cli/gateway-token-drift.ts | 16 +++++++++++++ src/cli/daemon-cli/lifecycle-core.ts | 15 +++--------- 3 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 src/cli/daemon-cli/gateway-token-drift.test.ts create mode 100644 src/cli/daemon-cli/gateway-token-drift.ts diff --git a/src/cli/daemon-cli/gateway-token-drift.test.ts b/src/cli/daemon-cli/gateway-token-drift.test.ts new file mode 100644 index 000000000..58f4b706e --- /dev/null +++ b/src/cli/daemon-cli/gateway-token-drift.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from "vitest"; +import type { OpenClawConfig } from "../../config/config.js"; +import { resolveGatewayTokenForDriftCheck } from "./gateway-token-drift.js"; + +describe("resolveGatewayTokenForDriftCheck", () => { + it("prefers persisted config token over shell env", () => { + const token = resolveGatewayTokenForDriftCheck({ + cfg: { + gateway: { + mode: "local", + auth: { + token: "config-token", + }, + }, + } as OpenClawConfig, + env: { + OPENCLAW_GATEWAY_TOKEN: "env-token", + } as NodeJS.ProcessEnv, + }); + + expect(token).toBe("config-token"); + }); +}); diff --git a/src/cli/daemon-cli/gateway-token-drift.ts b/src/cli/daemon-cli/gateway-token-drift.ts new file mode 100644 index 000000000..e4421b32a --- /dev/null +++ b/src/cli/daemon-cli/gateway-token-drift.ts @@ -0,0 +1,16 @@ +import type { OpenClawConfig } from "../../config/config.js"; +import { resolveGatewayCredentialsFromConfig } from "../../gateway/credentials.js"; + +export function resolveGatewayTokenForDriftCheck(params: { + cfg: OpenClawConfig; + env?: NodeJS.ProcessEnv; +}) { + return resolveGatewayCredentialsFromConfig({ + cfg: params.cfg, + env: params.env, + modeOverride: "local", + // Drift checks should compare the persisted gateway token against the + // service token, not let an exported shell env mask config drift. + localTokenPrecedence: "config-first", + }).token; +} diff --git a/src/cli/daemon-cli/lifecycle-core.ts b/src/cli/daemon-cli/lifecycle-core.ts index 5e6daf188..db0b2182e 100644 --- a/src/cli/daemon-cli/lifecycle-core.ts +++ b/src/cli/daemon-cli/lifecycle-core.ts @@ -5,12 +5,10 @@ import { checkTokenDrift } from "../../daemon/service-audit.js"; import type { GatewayService } from "../../daemon/service.js"; import { renderSystemdUnavailableHints } from "../../daemon/systemd-hints.js"; import { isSystemdUserServiceAvailable } from "../../daemon/systemd.js"; -import { - isGatewaySecretRefUnavailableError, - resolveGatewayCredentialsFromConfig, -} from "../../gateway/credentials.js"; +import { isGatewaySecretRefUnavailableError } from "../../gateway/credentials.js"; import { isWSL } from "../../infra/wsl.js"; import { defaultRuntime } from "../../runtime.js"; +import { resolveGatewayTokenForDriftCheck } from "./gateway-token-drift.js"; import { buildDaemonServiceSnapshot, createNullWriter, @@ -284,14 +282,7 @@ export async function runServiceRestart(params: { const command = await params.service.readCommand(process.env); const serviceToken = command?.environment?.OPENCLAW_GATEWAY_TOKEN; const cfg = loadConfig(); - const configToken = resolveGatewayCredentialsFromConfig({ - cfg, - env: process.env, - modeOverride: "local", - // Drift checks should compare the persisted gateway token against the - // service token, not let an exported shell env mask config drift. - localTokenPrecedence: "config-first", - }).token; + const configToken = resolveGatewayTokenForDriftCheck({ cfg, env: process.env }); const driftIssue = checkTokenDrift({ serviceToken, configToken }); if (driftIssue) { const warning = driftIssue.detail