fix(FN-668): add macOS keychain support for Claude CLI credentials
- Add readClaudeKeychainCredentials() to read from macOS keychain using security CLI - Fall back to keychain when legacy credential files don't exist - Support both subscriptionType and rateLimitTier for plan detection - Add comprehensive tests for keychain credential reading - Add changeset for patch release
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import * as https from "node:https";
|
||||
import * as child_process from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
/**
|
||||
* Pace information for weekly usage windows
|
||||
@@ -209,6 +211,23 @@ function decodeJwtPayload(token: string): any {
|
||||
|
||||
// ── Claude fetcher ─────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Read Claude credentials from macOS keychain.
|
||||
* Returns the parsed credentials object or null if not found/error.
|
||||
*/
|
||||
function readClaudeKeychainCredentials(): any | null {
|
||||
try {
|
||||
const result = child_process.execFileSync(
|
||||
"security",
|
||||
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
||||
{ encoding: "utf-8", timeout: 5000 }
|
||||
);
|
||||
return JSON.parse(result.trim());
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchClaudeUsage(): Promise<ProviderUsage> {
|
||||
const usage: ProviderUsage = {
|
||||
name: "Claude",
|
||||
@@ -231,6 +250,11 @@ async function fetchClaudeUsage(): Promise<ProviderUsage> {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Fallback to macOS keychain if file credentials not found
|
||||
if (!creds) {
|
||||
creds = readClaudeKeychainCredentials();
|
||||
}
|
||||
|
||||
const oauthCreds = creds?.claudeAiOauth || creds;
|
||||
if (!oauthCreds?.accessToken) {
|
||||
usage.error = "No Claude CLI credentials — run 'claude' to login";
|
||||
|
||||
Reference in New Issue
Block a user