mirror of
https://github.com/musistudio/claude-code-router.git
synced 2026-07-09 17:18:24 +00:00
Read claude credential from KeyChain
This commit is contained in:
parent
499d25d200
commit
a65b8bb6f9
1 changed files with 38 additions and 0 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { execFileSync } from "node:child_process";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type {
|
||||
|
|
@ -9,6 +10,7 @@ import type {
|
|||
import {
|
||||
bearerAuthPlugin,
|
||||
findOauthTokenSet,
|
||||
isRecord,
|
||||
missingCandidate,
|
||||
providerInternalNamePlaceholder,
|
||||
providerPayload,
|
||||
|
|
@ -18,6 +20,8 @@ import {
|
|||
type OAuthTokenSet
|
||||
} from "@ccr/core/agents/local-providers/shared";
|
||||
|
||||
const claudeCodeKeychainService = "Claude Code-credentials";
|
||||
|
||||
const claudeDefaultModels = ["claude-sonnet-4-20250514"];
|
||||
|
||||
const percentLimitMapping = (id: string, label: string, path: string, window: string) => ({
|
||||
|
|
@ -148,6 +152,19 @@ function readClaudeCodeOauth(): OAuthTokenSet | undefined {
|
|||
sourceFile
|
||||
};
|
||||
}
|
||||
|
||||
const keychainRecord = readClaudeCodeKeychainRecord();
|
||||
if (keychainRecord) {
|
||||
const credential = findOauthTokenSet(keychainRecord);
|
||||
if (credential) {
|
||||
return {
|
||||
accessToken: credential.accessToken,
|
||||
refreshToken: credential.refreshToken,
|
||||
sourceFile: `keychain:${claudeCodeKeychainService}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
@ -158,3 +175,24 @@ function claudeCredentialFiles(): string[] {
|
|||
path.join(os.homedir(), ".config", "claude", "credentials.json")
|
||||
]);
|
||||
}
|
||||
|
||||
// Newer macOS builds of the Claude Code CLI store credentials in the
|
||||
// Keychain instead of ~/.claude/.credentials.json. Reading it triggers the
|
||||
// standard macOS keychain access prompt (Allow / Always Allow); the user
|
||||
// declining or the item not existing both surface as a non-zero exit here.
|
||||
function readClaudeCodeKeychainRecord(): Record<string, unknown> | undefined {
|
||||
if (process.platform !== "darwin") {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const output = execFileSync(
|
||||
"security",
|
||||
["find-generic-password", "-s", claudeCodeKeychainService, "-w"],
|
||||
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
||||
);
|
||||
const parsed = JSON.parse(output.trim()) as unknown;
|
||||
return isRecord(parsed) ? parsed : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue