mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 01:41:40 +00:00
- invalidate cached Codex CLI credentials when auth.json changes within the TTL window - skip external CLI sync when the stored Codex OAuth credential is newer - cover both behaviors with focused regression tests Refs #53466 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
140 lines
3.5 KiB
TypeScript
140 lines
3.5 KiB
TypeScript
import {
|
|
readCodexCliCredentialsCached,
|
|
readQwenCliCredentialsCached,
|
|
readMiniMaxCliCredentialsCached,
|
|
} from "../cli-credentials.js";
|
|
import {
|
|
EXTERNAL_CLI_SYNC_TTL_MS,
|
|
QWEN_CLI_PROFILE_ID,
|
|
MINIMAX_CLI_PROFILE_ID,
|
|
log,
|
|
} from "./constants.js";
|
|
import type { AuthProfileStore, OAuthCredential } from "./types.js";
|
|
|
|
const OPENAI_CODEX_DEFAULT_PROFILE_ID = "openai-codex:default";
|
|
|
|
type ExternalCliSyncOptions = {
|
|
log?: boolean;
|
|
};
|
|
|
|
function shallowEqualOAuthCredentials(a: OAuthCredential | undefined, b: OAuthCredential): boolean {
|
|
if (!a) {
|
|
return false;
|
|
}
|
|
if (a.type !== "oauth") {
|
|
return false;
|
|
}
|
|
return (
|
|
a.provider === b.provider &&
|
|
a.access === b.access &&
|
|
a.refresh === b.refresh &&
|
|
a.expires === b.expires &&
|
|
a.email === b.email &&
|
|
a.enterpriseUrl === b.enterpriseUrl &&
|
|
a.projectId === b.projectId &&
|
|
a.accountId === b.accountId
|
|
);
|
|
}
|
|
|
|
function hasNewerStoredOAuthCredential(
|
|
existing: OAuthCredential | undefined,
|
|
incoming: OAuthCredential,
|
|
): boolean {
|
|
return Boolean(
|
|
existing &&
|
|
existing.provider === incoming.provider &&
|
|
Number.isFinite(existing.expires) &&
|
|
(!Number.isFinite(incoming.expires) || existing.expires > incoming.expires),
|
|
);
|
|
}
|
|
|
|
/** Sync external CLI credentials into the store for a given provider. */
|
|
function syncExternalCliCredentialsForProvider(
|
|
store: AuthProfileStore,
|
|
profileId: string,
|
|
provider: string,
|
|
readCredentials: () => OAuthCredential | null,
|
|
options: ExternalCliSyncOptions,
|
|
): boolean {
|
|
const existing = store.profiles[profileId];
|
|
const creds = readCredentials();
|
|
if (!creds) {
|
|
return false;
|
|
}
|
|
|
|
const existingOAuth = existing?.type === "oauth" ? existing : undefined;
|
|
if (shallowEqualOAuthCredentials(existingOAuth, creds)) {
|
|
return false;
|
|
}
|
|
if (hasNewerStoredOAuthCredential(existingOAuth, creds)) {
|
|
if (options.log !== false) {
|
|
log.debug(`kept newer stored ${provider} credentials over external cli sync`, {
|
|
profileId,
|
|
storedExpires: new Date(existingOAuth!.expires).toISOString(),
|
|
externalExpires: Number.isFinite(creds.expires)
|
|
? new Date(creds.expires).toISOString()
|
|
: null,
|
|
});
|
|
}
|
|
return false;
|
|
}
|
|
|
|
store.profiles[profileId] = creds;
|
|
if (options.log !== false) {
|
|
log.info(`synced ${provider} credentials from external cli`, {
|
|
profileId,
|
|
expires: new Date(creds.expires).toISOString(),
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sync OAuth credentials from external CLI tools (Qwen Code CLI, MiniMax CLI, Codex CLI)
|
|
* into the store.
|
|
*
|
|
* Returns true if any credentials were updated.
|
|
*/
|
|
export function syncExternalCliCredentials(
|
|
store: AuthProfileStore,
|
|
options: ExternalCliSyncOptions = {},
|
|
): boolean {
|
|
let mutated = false;
|
|
|
|
if (
|
|
syncExternalCliCredentialsForProvider(
|
|
store,
|
|
QWEN_CLI_PROFILE_ID,
|
|
"qwen-portal",
|
|
() => readQwenCliCredentialsCached({ ttlMs: EXTERNAL_CLI_SYNC_TTL_MS }),
|
|
options,
|
|
)
|
|
) {
|
|
mutated = true;
|
|
}
|
|
if (
|
|
syncExternalCliCredentialsForProvider(
|
|
store,
|
|
MINIMAX_CLI_PROFILE_ID,
|
|
"minimax-portal",
|
|
() => readMiniMaxCliCredentialsCached({ ttlMs: EXTERNAL_CLI_SYNC_TTL_MS }),
|
|
options,
|
|
)
|
|
) {
|
|
mutated = true;
|
|
}
|
|
if (
|
|
syncExternalCliCredentialsForProvider(
|
|
store,
|
|
OPENAI_CODEX_DEFAULT_PROFILE_ID,
|
|
"openai-codex",
|
|
() => readCodexCliCredentialsCached({ ttlMs: EXTERNAL_CLI_SYNC_TTL_MS }),
|
|
options,
|
|
)
|
|
) {
|
|
mutated = true;
|
|
}
|
|
|
|
return mutated;
|
|
}
|