/** * Cross-platform OAuth token refresh for Claude Code credentials. * * Storage backends: * macOS — system Keychain via /usr/bin/security (no prompt — pre-authorised) * Linux — ~/.claude/.credentials.json * * The credential store is dependency-injectable for testing. Production code * uses createPlatformCredentialStore() which picks the right backend * automatically. * * Concurrent calls to refreshOAuthToken() are deduplicated: if a refresh is * already in flight, subsequent callers wait for the same promise rather than * issuing a second network request and racing on the write. */ /** * Map a `claudeConfigDir` to the keychain service name claude-code uses * for that directory. * * Default `~/.claude` uses the bare service name `Claude Code-credentials`. * Any other directory uses `Claude Code-credentials-` — * matching claude-code's own convention so we can read OAuth tokens for * additional Meridian profiles without prompting the user. */ export declare function configDirToKeychainService(claudeConfigDir: string): string; /** Map `claudeConfigDir` to the file-based credentials path. */ export declare function configDirToCredentialsFile(claudeConfigDir: string): string; interface OAuthCredentials { accessToken: string; refreshToken: string; expiresAt: number; scopes?: string[]; subscriptionType?: string; rateLimitTier?: string; } interface CredentialsFile { claudeAiOauth: OAuthCredentials; [key: string]: unknown; } export interface CredentialStore { read(): Promise; write(credentials: CredentialsFile): Promise; } /** * Serialize a credentials object to the on-disk / Keychain format Claude Code * expects. * * MUST be compact (no whitespace) — Claude Code's credential parser cannot * read pretty-printed JSON and treats the user as logged out when it * encounters one. See issue #452. * * Exported so the regression test can pin the output format directly. */ export declare function serializeCredentials(credentials: CredentialsFile): string; /** * Returns the appropriate credential store for the current platform. * * If `claudeConfigDir` is provided, returns a profile-specific store that * reads from the matching keychain entry (macOS) or `/.credentials.json` * (Linux). Default behaviour (no opts) is unchanged — reads from the * standard `~/.claude` location. */ export declare function createPlatformCredentialStore(opts?: { claudeConfigDir?: string; }): CredentialStore; /** Look up the appropriate file path for a profile (Linux convention even on macOS for inspection). */ export declare function credentialsFilePathForProfile(claudeConfigDir?: string): string; /** * Refresh the Claude Code OAuth access token. * * Reads the stored refresh token, exchanges it for a new access token via * Anthropic's OAuth endpoint, and writes the updated credentials back. * * Returns true on success, false on any failure. Concurrent calls share one * in-flight request so only one network round-trip is made. * * @param store Override the credential store (for testing). */ export declare function refreshOAuthToken(store?: CredentialStore): Promise; /** * Refresh the access token if it is within `bufferMs` of expiry. * * Cheap to call before every SDK request: when the token isn't due yet this * is just one credential-store read. When it is due, the underlying * `refreshOAuthToken()` call is in-flight-deduplicated so concurrent callers * share one network round-trip. * * Returns true when the token is fresh after the call (already valid OR * successfully refreshed), false on any failure (no credentials, no * expiresAt, refresh request failed). False is non-fatal — the caller * proceeds with whatever token is on disk and falls back to the reactive * refresh-on-401 path if Anthropic rejects it. */ export declare function ensureFreshToken(store?: CredentialStore, bufferMs?: number): Promise; /** * Start a self-rescheduling timer that refreshes the access token shortly * before each expiry — regardless of incoming traffic. * * Idempotent: a second call while one is already running is a no-op. Safe to * call from any code path; returns synchronously and schedules in the * background. * * Why traffic-independent matters: without this, an idle proxy never fires * either the proactive (`ensureFreshToken`) or reactive (401-retry) refresh * path. Anthropic's OAuth refresh tokens appear to be invalidated server-side * after sitting unused for an extended period (observed 2026-05-03: two NAS * instances idle past expiry both got `400 invalid_grant` on a manual refresh * attempt; only fix was OAuth-flow re-login). Running a refresh every ~8h * keeps the refresh chain warm. * * On `refreshOAuthToken()` failure (network, transient API error, refresh * token rejected) we retry every `failureRetryMs` — gives operators a window * to `claude login` and have the new tokens picked up automatically on the * next tick. */ export declare function startBackgroundRefresh(store?: CredentialStore, bufferMs?: number, failureRetryMs?: number): void; /** Stop the background scheduler. Idempotent. */ export declare function stopBackgroundRefresh(): void; /** For testing only. */ export declare function isBackgroundRefreshActive(): boolean; /** Reset in-flight state — for testing only. */ export declare function resetInflightRefresh(): void; export {}; //# sourceMappingURL=tokenRefresh.d.ts.map