Fix Codex CLI auth profile sync (#45353)

Merged via squash.

Prepared head SHA: e5432ec4e1
Co-authored-by: Gugu-sugar <201366873+Gugu-sugar@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06
This commit is contained in:
Gugu-sugar
2026-03-15 07:36:09 +08:00
committed by GitHub
parent b202ac2ad1
commit c1a0196826
4 changed files with 88 additions and 4 deletions

View File

@@ -0,0 +1,54 @@
import { describe, expect, it, vi } from "vitest";
import type { AuthProfileStore } from "./auth-profiles/types.js";
const mocks = vi.hoisted(() => ({
readCodexCliCredentialsCached: vi.fn(),
readQwenCliCredentialsCached: vi.fn(() => null),
readMiniMaxCliCredentialsCached: vi.fn(() => null),
}));
vi.mock("./cli-credentials.js", () => ({
readCodexCliCredentialsCached: mocks.readCodexCliCredentialsCached,
readQwenCliCredentialsCached: mocks.readQwenCliCredentialsCached,
readMiniMaxCliCredentialsCached: mocks.readMiniMaxCliCredentialsCached,
}));
const { syncExternalCliCredentials } = await import("./auth-profiles/external-cli-sync.js");
const { CODEX_CLI_PROFILE_ID } = await import("./auth-profiles/constants.js");
const OPENAI_CODEX_DEFAULT_PROFILE_ID = "openai-codex:default";
describe("syncExternalCliCredentials", () => {
it("syncs Codex CLI credentials into the supported default auth profile", () => {
const expires = Date.now() + 60_000;
mocks.readCodexCliCredentialsCached.mockReturnValue({
type: "oauth",
provider: "openai-codex",
access: "access-token",
refresh: "refresh-token",
expires,
accountId: "acct_123",
});
const store: AuthProfileStore = {
version: 1,
profiles: {},
};
const mutated = syncExternalCliCredentials(store);
expect(mutated).toBe(true);
expect(mocks.readCodexCliCredentialsCached).toHaveBeenCalledWith(
expect.objectContaining({ ttlMs: expect.any(Number) }),
);
expect(store.profiles[OPENAI_CODEX_DEFAULT_PROFILE_ID]).toMatchObject({
type: "oauth",
provider: "openai-codex",
access: "access-token",
refresh: "refresh-token",
expires,
accountId: "acct_123",
});
expect(store.profiles[CODEX_CLI_PROFILE_ID]).toBeUndefined();
});
});

View File

@@ -1,4 +1,5 @@
import {
readCodexCliCredentialsCached,
readQwenCliCredentialsCached,
readMiniMaxCliCredentialsCached,
} from "../cli-credentials.js";
@@ -11,6 +12,8 @@ import {
} from "./constants.js";
import type { AuthProfileCredential, AuthProfileStore, OAuthCredential } from "./types.js";
const OPENAI_CODEX_DEFAULT_PROFILE_ID = "openai-codex:default";
function shallowEqualOAuthCredentials(a: OAuthCredential | undefined, b: OAuthCredential): boolean {
if (!a) {
return false;
@@ -37,7 +40,11 @@ function isExternalProfileFresh(cred: AuthProfileCredential | undefined, now: nu
if (cred.type !== "oauth" && cred.type !== "token") {
return false;
}
if (cred.provider !== "qwen-portal" && cred.provider !== "minimax-portal") {
if (
cred.provider !== "qwen-portal" &&
cred.provider !== "minimax-portal" &&
cred.provider !== "openai-codex"
) {
return false;
}
if (typeof cred.expires !== "number") {
@@ -82,7 +89,8 @@ function syncExternalCliCredentialsForProvider(
}
/**
* Sync OAuth credentials from external CLI tools (Qwen Code CLI, MiniMax CLI) into the store.
* Sync OAuth credentials from external CLI tools (Qwen Code CLI, MiniMax CLI, Codex CLI)
* into the store.
*
* Returns true if any credentials were updated.
*/
@@ -130,6 +138,17 @@ export function syncExternalCliCredentials(store: AuthProfileStore): boolean {
) {
mutated = true;
}
if (
syncExternalCliCredentialsForProvider(
store,
OPENAI_CODEX_DEFAULT_PROFILE_ID,
"openai-codex",
() => readCodexCliCredentialsCached({ ttlMs: EXTERNAL_CLI_SYNC_TTL_MS }),
now,
)
) {
mutated = true;
}
return mutated;
}