mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
166 lines
4.8 KiB
TypeScript
166 lines
4.8 KiB
TypeScript
import crypto from "node:crypto";
|
|
import { loadAuthProfileStoreForRuntime } from "./auth-profiles/store.js";
|
|
import type { AuthProfileCredential, AuthProfileStore } from "./auth-profiles/types.js";
|
|
import {
|
|
readClaudeCliCredentialsCached,
|
|
readCodexCliCredentialsCached,
|
|
type ClaudeCliCredential,
|
|
type CodexCliCredential,
|
|
} from "./cli-credentials.js";
|
|
|
|
type CliAuthEpochDeps = {
|
|
readClaudeCliCredentialsCached: typeof readClaudeCliCredentialsCached;
|
|
readCodexCliCredentialsCached: typeof readCodexCliCredentialsCached;
|
|
loadAuthProfileStoreForRuntime: typeof loadAuthProfileStoreForRuntime;
|
|
};
|
|
|
|
const defaultCliAuthEpochDeps: CliAuthEpochDeps = {
|
|
readClaudeCliCredentialsCached,
|
|
readCodexCliCredentialsCached,
|
|
loadAuthProfileStoreForRuntime,
|
|
};
|
|
|
|
const cliAuthEpochDeps: CliAuthEpochDeps = { ...defaultCliAuthEpochDeps };
|
|
|
|
export function setCliAuthEpochTestDeps(overrides: Partial<CliAuthEpochDeps>): void {
|
|
Object.assign(cliAuthEpochDeps, overrides);
|
|
}
|
|
|
|
export function resetCliAuthEpochTestDeps(): void {
|
|
Object.assign(cliAuthEpochDeps, defaultCliAuthEpochDeps);
|
|
}
|
|
|
|
function hashCliAuthEpochPart(value: string): string {
|
|
return crypto.createHash("sha256").update(value).digest("hex");
|
|
}
|
|
|
|
function encodeUnknown(value: unknown): string {
|
|
return JSON.stringify(value ?? null);
|
|
}
|
|
|
|
function encodeClaudeCredential(credential: ClaudeCliCredential): string {
|
|
if (credential.type === "oauth") {
|
|
return JSON.stringify([
|
|
"oauth",
|
|
credential.provider,
|
|
credential.access,
|
|
credential.refresh,
|
|
credential.expires,
|
|
]);
|
|
}
|
|
return JSON.stringify(["token", credential.provider, credential.token, credential.expires]);
|
|
}
|
|
|
|
function encodeCodexCredential(credential: CodexCliCredential): string {
|
|
return JSON.stringify([
|
|
credential.type,
|
|
credential.provider,
|
|
credential.access,
|
|
credential.refresh,
|
|
credential.expires,
|
|
credential.accountId ?? null,
|
|
]);
|
|
}
|
|
|
|
function encodeAuthProfileCredential(credential: AuthProfileCredential): string {
|
|
switch (credential.type) {
|
|
case "api_key":
|
|
return JSON.stringify([
|
|
"api_key",
|
|
credential.provider,
|
|
credential.key ?? null,
|
|
encodeUnknown(credential.keyRef),
|
|
credential.email ?? null,
|
|
credential.displayName ?? null,
|
|
encodeUnknown(credential.metadata),
|
|
]);
|
|
case "token":
|
|
return JSON.stringify([
|
|
"token",
|
|
credential.provider,
|
|
credential.token ?? null,
|
|
encodeUnknown(credential.tokenRef),
|
|
credential.expires ?? null,
|
|
credential.email ?? null,
|
|
credential.displayName ?? null,
|
|
]);
|
|
case "oauth":
|
|
return JSON.stringify([
|
|
"oauth",
|
|
credential.provider,
|
|
credential.access,
|
|
credential.refresh,
|
|
credential.expires,
|
|
credential.clientId ?? null,
|
|
credential.email ?? null,
|
|
credential.displayName ?? null,
|
|
credential.enterpriseUrl ?? null,
|
|
credential.projectId ?? null,
|
|
credential.accountId ?? null,
|
|
credential.managedBy ?? null,
|
|
]);
|
|
}
|
|
}
|
|
|
|
function getLocalCliCredentialFingerprint(provider: string): string | undefined {
|
|
switch (provider) {
|
|
case "claude-cli": {
|
|
const credential = cliAuthEpochDeps.readClaudeCliCredentialsCached({
|
|
ttlMs: 5000,
|
|
allowKeychainPrompt: false,
|
|
});
|
|
return credential ? hashCliAuthEpochPart(encodeClaudeCredential(credential)) : undefined;
|
|
}
|
|
case "codex-cli": {
|
|
const credential = cliAuthEpochDeps.readCodexCliCredentialsCached({
|
|
ttlMs: 5000,
|
|
});
|
|
return credential ? hashCliAuthEpochPart(encodeCodexCredential(credential)) : undefined;
|
|
}
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function getAuthProfileCredential(
|
|
store: AuthProfileStore,
|
|
authProfileId: string | undefined,
|
|
): AuthProfileCredential | undefined {
|
|
if (!authProfileId) {
|
|
return undefined;
|
|
}
|
|
return store.profiles[authProfileId];
|
|
}
|
|
|
|
export async function resolveCliAuthEpoch(params: {
|
|
provider: string;
|
|
authProfileId?: string;
|
|
}): Promise<string | undefined> {
|
|
const provider = params.provider.trim();
|
|
const authProfileId = params.authProfileId?.trim() || undefined;
|
|
const parts: string[] = [];
|
|
|
|
const localFingerprint = getLocalCliCredentialFingerprint(provider);
|
|
if (localFingerprint) {
|
|
parts.push(`local:${provider}:${localFingerprint}`);
|
|
}
|
|
|
|
if (authProfileId) {
|
|
const store = cliAuthEpochDeps.loadAuthProfileStoreForRuntime(undefined, {
|
|
readOnly: true,
|
|
allowKeychainPrompt: false,
|
|
});
|
|
const credential = getAuthProfileCredential(store, authProfileId);
|
|
if (credential) {
|
|
parts.push(
|
|
`profile:${authProfileId}:${hashCliAuthEpochPart(encodeAuthProfileCredential(credential))}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (parts.length === 0) {
|
|
return undefined;
|
|
}
|
|
return hashCliAuthEpochPart(parts.join("\n"));
|
|
}
|