mirror of https://github.com/garrytan/gstack.git
fix(model-benchmark): recognize macOS Keychain auth in the claude adapter (#1890)
The default macOS Claude Code install stores OAuth under the Keychain generic-password service "Claude Code-credentials" and never writes ~/.claude/.credentials.json, so available()'s file-or-env sniff reported "No Claude auth found" while claude -p worked fine. On darwin the sniff now also probes the Keychain entry — metadata only (no -w, the secret is never read), 5s timeout, any security(1) failure degrades to not-found. Verified live on this machine (subscription install, no creds file, Keychain entry present). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4d15254229
commit
cd5433add1
|
|
@ -1,6 +1,6 @@
|
|||
import type { ProviderAdapter, RunOpts, RunResult, AvailabilityCheck } from './types';
|
||||
import { estimateCostUsd } from '../pricing';
|
||||
import { execFileSync } from 'child_process';
|
||||
import { execFileSync, spawnSync } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
|
|
@ -25,11 +25,30 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|||
if (!resolved) {
|
||||
return { ok: false, reason: 'claude CLI not found on PATH. Install from https://claude.ai/download or npm i -g @anthropic-ai/claude-code (or set GSTACK_CLAUDE_BIN)' };
|
||||
}
|
||||
// Auth sniff: ~/.claude/.credentials.json OR ANTHROPIC_API_KEY
|
||||
// Auth sniff: ~/.claude/.credentials.json OR ANTHROPIC_API_KEY OR (macOS)
|
||||
// the Keychain entry subscription installs use instead of the creds file.
|
||||
// #1890: the default macOS install stores OAuth under the generic-password
|
||||
// service "Claude Code-credentials" and never writes .credentials.json,
|
||||
// so the file-or-env sniff reported "No Claude auth found" while
|
||||
// `claude -p` worked fine. Metadata probe only (no -w — never reads the
|
||||
// secret), and any failure of `security` itself falls through to the
|
||||
// not-found reason rather than throwing.
|
||||
const credsPath = path.join(os.homedir(), '.claude', '.credentials.json');
|
||||
const hasCreds = fs.existsSync(credsPath);
|
||||
const hasKey = !!process.env.ANTHROPIC_API_KEY;
|
||||
if (!hasCreds && !hasKey) {
|
||||
let hasKeychain = false;
|
||||
if (!hasCreds && !hasKey && process.platform === 'darwin') {
|
||||
try {
|
||||
const probe = spawnSync('security', ['find-generic-password', '-s', 'Claude Code-credentials'], {
|
||||
stdio: 'ignore',
|
||||
timeout: 5000,
|
||||
});
|
||||
hasKeychain = probe.status === 0;
|
||||
} catch {
|
||||
hasKeychain = false;
|
||||
}
|
||||
}
|
||||
if (!hasCreds && !hasKey && !hasKeychain) {
|
||||
return { ok: false, reason: 'No Claude auth found. Log in via `claude` interactive session, or export ANTHROPIC_API_KEY.' };
|
||||
}
|
||||
return { ok: true };
|
||||
|
|
|
|||
Loading…
Reference in New Issue