Skip to content

Commit 8670f2c

Browse files
authored
fix(openai-codex): bootstrap proxy on oauth refresh (#53078)
Verified: - pnpm install --frozen-lockfile - pnpm exec vitest run extensions/openai/openai-codex-provider.runtime.test.ts extensions/openai/openai-provider.test.ts
1 parent cdd797f commit 8670f2c

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
3030
- Telegram/auto-reply: preserve same-chat inbound debounce order without stranding stale busy-session followups, and keep same-key overflow turns ordered when tracked debounce keys are saturated. (#52998) Thanks @osolmaz.
3131
- ClawHub/macOS: read the local ClawHub login from the macOS Application Support path and still honor XDG config on macOS, so skill browsing uses the logged-in token on both default and XDG-style setups. Fixes #52949. Thanks @scoootscooob.
3232
- Discord/commands: return an explicit unauthorized reply for privileged native slash commands instead of falling through to Discord's misleading generic completion when auth gates reject the sender. Fixes #53041. Thanks @scoootscooob.
33+
- Models/OpenAI Codex OAuth: bootstrap the env-configured HTTP/HTTPS proxy dispatcher on the stored-credential refresh path before token renewal runs, so expired Codex OAuth profiles can refresh successfully in proxy-required environments instead of locking users out after the first token expiry.
3334

3435
## 2026.3.22
3536

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { beforeEach, describe, expect, it, vi } from "vitest";
2+
3+
const mocks = vi.hoisted(() => ({
4+
ensureGlobalUndiciEnvProxyDispatcher: vi.fn(),
5+
getOAuthApiKey: vi.fn(),
6+
}));
7+
8+
vi.mock("openclaw/plugin-sdk/infra-runtime", () => ({
9+
ensureGlobalUndiciEnvProxyDispatcher: mocks.ensureGlobalUndiciEnvProxyDispatcher,
10+
}));
11+
12+
vi.mock("@mariozechner/pi-ai/oauth", () => ({
13+
getOAuthApiKey: mocks.getOAuthApiKey,
14+
}));
15+
16+
import { getOAuthApiKey } from "./openai-codex-provider.runtime.js";
17+
18+
describe("openai-codex-provider.runtime", () => {
19+
beforeEach(() => {
20+
vi.clearAllMocks();
21+
});
22+
23+
it("bootstraps the env proxy dispatcher before refreshing oauth credentials", async () => {
24+
const refreshed = {
25+
newCredentials: {
26+
access: "next-access",
27+
refresh: "next-refresh",
28+
expires: Date.now() + 60_000,
29+
},
30+
};
31+
mocks.getOAuthApiKey.mockResolvedValue(refreshed);
32+
33+
await expect(
34+
getOAuthApiKey("openai-codex", {
35+
"openai-codex": {
36+
provider: "openai-codex",
37+
type: "oauth",
38+
access: "access-token",
39+
refresh: "refresh-token",
40+
expires: Date.now(),
41+
},
42+
}),
43+
).resolves.toBe(refreshed);
44+
45+
expect(mocks.ensureGlobalUndiciEnvProxyDispatcher).toHaveBeenCalledOnce();
46+
expect(mocks.getOAuthApiKey).toHaveBeenCalledOnce();
47+
expect(mocks.ensureGlobalUndiciEnvProxyDispatcher.mock.invocationCallOrder[0]).toBeLessThan(
48+
mocks.getOAuthApiKey.mock.invocationCallOrder[0],
49+
);
50+
});
51+
});
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
1-
export { getOAuthApiKey } from "@mariozechner/pi-ai/oauth";
1+
import { getOAuthApiKey as getOAuthApiKeyFromPi } from "@mariozechner/pi-ai/oauth";
2+
import { ensureGlobalUndiciEnvProxyDispatcher } from "openclaw/plugin-sdk/infra-runtime";
3+
4+
export async function getOAuthApiKey(
5+
...args: Parameters<typeof getOAuthApiKeyFromPi>
6+
): Promise<Awaited<ReturnType<typeof getOAuthApiKeyFromPi>>> {
7+
ensureGlobalUndiciEnvProxyDispatcher();
8+
return await getOAuthApiKeyFromPi(...args);
9+
}

0 commit comments

Comments
 (0)