diff --git a/CHANGELOG.md b/CHANGELOG.md index b47fd4a3104..f90f939abd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Docs: https://docs.openclaw.ai - TUI/chat: skip full provider model normalization during context-window warmup while preserving provider-owned context metadata, avoiding cold-start stalls with large model registries. Thanks @547895019. - Memory Wiki: accept relative Markdown links that include the `.md` suffix during broken-wikilink validation, avoiding false positives for native render-mode links. Thanks @Kenneth8128. +- OpenAI Codex: show the device-pairing code in the interactive SSH/headless prompt while keeping the short-lived code out of persistent runtime logs. Fixes #74212. Thanks @da22le123. - Plugins/CLI: cache plugin CLI registration entries per command program so completion state generation does not repeat the full plugin sweep in one invocation. Thanks @ScientificProgrammer. - Plugins: reuse gateway-bindable plugin loader cache entries for later default-mode loads without serving default-built registries to gateway-bound requests, reducing repeated plugin registration during dispatch. Refs #61756. Thanks @DmitryPogodaev. - Gateway/secrets: include the caught error message in `secrets.reload` and `secrets.resolve` warning logs while keeping RPC errors generic, so operators can diagnose reload and permission failures. Thanks @davidangularme. diff --git a/extensions/openai/openai-codex-provider.test.ts b/extensions/openai/openai-codex-provider.test.ts index 92c1e7d9bfc..c74085b83e6 100644 --- a/extensions/openai/openai-codex-provider.test.ts +++ b/extensions/openai/openai-codex-provider.test.ts @@ -234,7 +234,7 @@ describe("openai codex provider", () => { expect(result?.profiles[0]?.credential).not.toHaveProperty("idToken"); }); - it("does not log the device pairing code in remote mode", async () => { + async function runRemoteDeviceCodeAuthFlow() { const provider = buildOpenAICodexProviderPlugin(); const deviceCodeMethod = provider.auth?.find((method) => method.id === "device-code"); const note = vi.fn(async () => {}); @@ -273,17 +273,28 @@ describe("openai codex provider", () => { }), ).resolves.toBeDefined(); - const logOutput = runtime.log.mock.calls.flat().join("\n"); - expect(logOutput).toContain("https://auth.openai.com/codex/device"); - expect(logOutput).not.toContain("CODE-12345"); + return { note, runtime }; + } + + it("surfaces the device pairing code via the prompter note in remote (SSH) mode (#74212)", async () => { + const { note } = await runRemoteDeviceCodeAuthFlow(); + expect(note).toHaveBeenCalledWith( - expect.stringContaining("Code: [shown on the local device only]"), - "OpenAI Codex device code", - ); - expect(note).not.toHaveBeenCalledWith( expect.stringContaining("Code: CODE-12345"), "OpenAI Codex device code", ); + expect(note).not.toHaveBeenCalledWith( + expect.stringContaining("Code: [shown on the local device only]"), + "OpenAI Codex device code", + ); + }); + + it("does not write the device pairing code to the runtime log in remote mode", async () => { + const { runtime } = await runRemoteDeviceCodeAuthFlow(); + + const logOutput = runtime.log.mock.calls.flat().join("\n"); + expect(logOutput).toContain("https://auth.openai.com/codex/device"); + expect(logOutput).not.toContain("CODE-12345"); }); it("owns native reasoning output mode for Codex responses", () => { diff --git a/extensions/openai/openai-codex-provider.ts b/extensions/openai/openai-codex-provider.ts index 6662fd87a3b..462e5dbc2b7 100644 --- a/extensions/openai/openai-codex-provider.ts +++ b/extensions/openai/openai-codex-provider.ts @@ -378,16 +378,15 @@ async function runOpenAICodexDeviceCode(ctx: ProviderAuthContext) { onProgress: (message) => spin.update(message), onVerification: async ({ verificationUrl, userCode, expiresInMs }) => { const expiresInMinutes = Math.max(1, Math.round(expiresInMs / 60_000)); - const codeLine = ctx.isRemote - ? "Code: [shown on the local device only]" - : `Code: ${userCode}`; + // The prompter note is the user-facing TTY surface, so remote/headless + // users need the code there; keep the persistent runtime log URL-only. await ctx.prompter.note( [ ctx.isRemote ? "Open this URL in your LOCAL browser and enter the code below." : "Open this URL in your browser and enter the code below.", `URL: ${verificationUrl}`, - codeLine, + `Code: ${userCode}`, `Code expires in ${expiresInMinutes} minutes. Never share it.`, ].join("\n"), "OpenAI Codex device code",