diff --git a/CHANGELOG.md b/CHANGELOG.md index d420e218ee9..6a38f1e68ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -361,6 +361,7 @@ Docs: https://docs.openclaw.ai - Agents/bootstrap: dedupe hook-injected bootstrap context files by workspace-relative path and store normalized resolved paths so duplicate relative and absolute hook paths no longer depend on the process cwd. (#59344; fixes #59319; related #56721, #56725, and #57587) Thanks @koen666. - Agents/bootstrap: refresh cached workspace bootstrap snapshots on long-lived main-session turns when `AGENTS.md`, `SOUL.md`, `MEMORY.md`, or `TOOLS.md` change on disk, while preserving unchanged snapshot identity through the workspace file cache. (#64871; related #43901, #26497, #28594, #30896) Thanks @aimqwest and @mikejuyoon. - macOS Gateway: detect installed-but-unloaded LaunchAgent split-brain states during status, doctor, and restart, and re-bootstrap launchd supervision before falling back to unmanaged listener restarts. Fixes #67335, #53475, and #71060; refs #58890, #60885, and #70801. Thanks @ze1tgeist88, @dafacto, and @vishutdhar. +- Gateway/restart: keep local restart-health probes on configured local daemon auth without falling back to remote gateway credentials. (#57374, #59439) Thanks @zssggle-rgb and @roytong9. - Plugins/install: treat mirrored core logger dependencies as staged bundled runtime deps so packaged Gateway starts do not crash when the external plugin-runtime-deps root is missing `tslog`. Fixes #72228; supersedes #72493. Thanks @deepujain. - Build/plugins: preserve active bundled runtime-dependency staging temp directories owned by live build processes so overlapping postbuild runs no longer delete each other's staged deps mid-prune. Supersedes #72220. Thanks @VACInc. - Plugins/install: hide bundled runtime-dependency npm child windows on Windows across Gateway startup, postinstall, and packaged staging paths so Telegram/Anthropic dependency repair no longer flashes shell windows. Fixes #72315. Thanks @athuljayaram and @joshfeng. diff --git a/src/gateway/probe-auth.test.ts b/src/gateway/probe-auth.test.ts index 217fb4cc092..a56c0837315 100644 --- a/src/gateway/probe-auth.test.ts +++ b/src/gateway/probe-auth.test.ts @@ -77,6 +77,30 @@ describe("resolveGatewayProbeAuthSafe", () => { } as OpenClawConfig); }); + it("does not fall through to remote credentials for local probes", () => { + const result = resolveGatewayProbeAuthSafe({ + cfg: { + gateway: { + mode: "local", + remote: { + url: "wss://gateway.example", + token: "remote-token", + password: "remote-password", // pragma: allowlist secret + }, + }, + } as OpenClawConfig, + mode: "local", + env: {} as NodeJS.ProcessEnv, + }); + + expect(result).toEqual({ + auth: { + token: undefined, + password: undefined, + }, + }); + }); + it("ignores unresolved local token SecretRef in remote mode when remote-only auth is requested", () => { const result = resolveGatewayProbeAuthSafe({ cfg: { @@ -171,6 +195,35 @@ describe("resolveGatewayProbeAuthSafeWithSecretInputs", () => { }); }); + it("returns empty auth without warning for gateway.remote SecretRefs in local probes", async () => { + const result = await resolveGatewayProbeAuthSafeWithSecretInputs({ + cfg: { + gateway: { + mode: "local", + remote: { + url: "wss://gateway.example", + token: { source: "env", provider: "default", id: "REMOTE_GATEWAY_TOKEN" }, + }, + }, + secrets: { + providers: { + default: { source: "env" }, + }, + }, + } as OpenClawConfig, + mode: "local", + env: { + REMOTE_GATEWAY_TOKEN: "remote-token", + } as NodeJS.ProcessEnv, + }); + + expect(result.warning).toBeUndefined(); + expect(result.auth).toEqual({ + token: undefined, + password: undefined, + }); + }); + it("returns warning and empty auth when SecretRef cannot be resolved via async path", async () => { const result = await resolveGatewayProbeAuthSafeWithSecretInputs({ cfg: { diff --git a/src/gateway/probe-auth.ts b/src/gateway/probe-auth.ts index 2b61f8e84aa..d984319342c 100644 --- a/src/gateway/probe-auth.ts +++ b/src/gateway/probe-auth.ts @@ -15,9 +15,10 @@ function buildGatewayProbeCredentialPolicy(params: { env?: NodeJS.ProcessEnv; explicitAuth?: ExplicitGatewayAuth; }) { + const cfg = resolveGatewayProbeCredentialConfig(params); return { - config: params.cfg, - cfg: params.cfg, + config: cfg, + cfg, env: params.env, explicitAuth: params.explicitAuth, modeOverride: params.mode, @@ -26,6 +27,31 @@ function buildGatewayProbeCredentialPolicy(params: { }; } +function resolveGatewayProbeCredentialConfig(params: { + cfg: OpenClawConfig; + mode: "local" | "remote"; +}): OpenClawConfig { + if (params.mode !== "local") { + return params.cfg; + } + + const remote = params.cfg.gateway?.remote; + if (!remote || (remote.token === undefined && remote.password === undefined)) { + return params.cfg; + } + + const remoteWithoutAuth = { ...remote }; + delete remoteWithoutAuth.token; + delete remoteWithoutAuth.password; + return { + ...params.cfg, + gateway: { + ...params.cfg.gateway, + remote: remoteWithoutAuth, + }, + }; +} + function resolveExplicitProbeAuth(explicitAuth?: ExplicitGatewayAuth): { token?: string; password?: string;