/** * `claude-code` credential seeder — imports the Claude Code OAuth token * (`~/.claude/.credentials.json`) into the unified credential pool as an * `anthropic` entry. * * E-CONFIG-AUTH-UNIFY E2a §5.2 T-E2-3. Architectural mirror of Hermes * Agent's `_seed_from_singletons` dispatch (`agent/credential_pool.py`). * * ## Consent gate * * Reading the Claude Code credential file requires the operator to have * explicitly opted in via `auth.claudeCodeConsentGiven = true` in the * global config. This mirrors Hermes Agent's PR #4210 fix: without the * gate, auxiliary fallback chains could silently route requests through * a user's Claude Code OAuth token without consent. The default value is * `false`, so the seeder is a no-op on fresh installs. * * The gate is checked BEFORE any filesystem access: an unconsented seeder * never calls `readFileSync` on the credentials path. `isConsentEstablished` * reads the global config through `getConfigValue('auth.claudeCodeConsentGiven')` * so CLI/env overrides on the consent flag are respected uniformly. * * ## What the seeder emits * * One `SeederCredentialEntry` per call (or zero) with: * * - `provider: 'anthropic'` * - `label: 'claude-code'` * - `authType: 'oauth'` * - `source: 'claude-code'` * - `accessToken`, `refreshToken`, `expiresAt` extracted from * `claudeAiOauth` via {@link parseClaudeCodeCredentials}. * * Zero-result paths (consent off, file missing, file unreadable, JSON * malformed, token expired) all return `{ entries: [] }` without * throwing. Parsing is delegated to `parseClaudeCodeCredentials` in * `@cleocode/contracts` so the JSON shape lives in exactly one place. * * @module llm/credential-seeders/claude-code-seeder * @task T9410 * @epic E-CONFIG-AUTH-UNIFY (E2a) */ import type { CredentialSeeder, SeederResult, SeederSourceId } from './index.js'; /** * Concrete `CredentialSeeder` for the `claude-code` source. * * Constructor-injectable filesystem hooks (`readCredentialFile`) keep tests * pure — they exercise the consent gate, expiry filter, and missing-file * path without touching the real `~/.claude/.credentials.json` and without * fs mocks at the module level. The default `readCredentialFile` * implementation calls `readFileSync` on `~/.claude/.credentials.json`. * * @task T9410 */ export declare class ClaudeCodeSeeder implements CredentialSeeder { /** @inheritdoc */ readonly sourceId: SeederSourceId; /** @inheritdoc */ readonly provider = "anthropic"; private readonly readCredentialFile; private readonly readConsentFlag; /** * Construct a seeder. * * @param opts - Optional dependency-injection seams. `readCredentialFile` * MUST return `null` when the file is missing or unreadable (it MUST * NOT throw). `readConsentFlag` returns the resolved consent boolean * from the config cascade. */ constructor(opts?: { readCredentialFile?: () => string | null; readConsentFlag?: () => Promise; }); /** * Resolve the consent flag from the canonical config cascade. * * Returns `true` only when `auth.claudeCodeConsentGiven` is explicitly * the boolean `true`. Any other value — `undefined`, `null`, `false`, * or a stray string — yields `false` so the gate fails closed. * * @param _provider - Provider id (ignored — this seeder is anthropic-only; * the parameter is accepted to satisfy the `CredentialSeeder` contract). */ isConsentEstablished(_provider: string): Promise; /** * Read `~/.claude/.credentials.json` and emit a single pool entry. * * Short-circuits to `{ entries: [] }` when the consent gate is closed, * the file is absent, the JSON is malformed, the `claudeAiOauth` block * is missing, or the token has expired (per `parseClaudeCodeCredentials`). */ seed(): Promise; } /** * Factory for the singleton `ClaudeCodeSeeder` registered into * `BUILTIN_SEEDERS` from `index.ts`. * * Kept as a factory (not a module-level `const`) so the registry-side * import remains acyclic and tests can construct fresh instances with * injected dependencies. * * @task T9410 */ export declare function createClaudeCodeSeeder(): ClaudeCodeSeeder; //# sourceMappingURL=claude-code-seeder.d.ts.map