import type { ProviderId, ProviderStatus } from '../types.js'; import { maskSecret } from '../llm/provider.js'; import type { SearchProviderId } from '../tools/web/types.js'; /** * Logical namespace for a stored secret. Search-provider keys live in the * same keyring service as LLM keys but under separate accounts so the two * keyspaces never collide. */ export type SecretNamespace = 'llm' | 'search'; /** Where a resolved secret value originated. Mirrors `ProviderStatus.source`. */ export type SecretSource = ProviderStatus['source']; export declare function getFallbackKeysPath(): string; /** * Compose the keychain account name used for a `(namespace, id)` pair. * Exposed so tests and callers can inspect the exact account string. */ export declare function secretAccount(namespace: SecretNamespace, id: string): string; export declare function searchProviderEnvVar(id: SearchProviderId): string | undefined; /** * Read a secret out of the OS keychain (preferred) or the restricted-permission * plaintext fallback file. Returns `{ source: 'missing' }` when neither * backend has a value. * * Legacy LLM entries that still live under the bare `` account * name (no namespace prefix) are migrated lazily into `llm:` on * first read so older installs keep working without manual intervention. */ export declare function getSecret(namespace: SecretNamespace, id: string): Promise<{ value?: string; source: SecretSource; }>; export declare function setSecret(namespace: SecretNamespace, id: string, value: string): Promise<'keychain' | 'fallback'>; /** * Best-effort delete: removes the secret from both the keychain and the * fallback file. Never throws on keychain errors so unset always cleans * up the on-disk fallback even when the OS keystore is unreachable. */ export declare function unsetSecret(namespace: SecretNamespace, id: string): Promise; /** One stored API key slot for an LLM provider. */ export interface ProviderKeySlot { readonly id: string; readonly value: string; readonly createdAt: number; readonly disabled?: boolean | undefined; } /** Resolved multi-key view for a provider (storage or env). */ export interface ProviderKeysResult { readonly keys: ProviderKeySlot[]; /** Sticky index used as the next rotation start (clamped). */ readonly activeIndex: number; readonly source: ProviderStatus['source']; } declare const MAX_PROVIDER_KEYS = 10; /** * Parse a stored secret string into slots. Legacy plain API keys become a * single-slot list. Invalid JSON that looks like a key is treated as legacy. */ export declare function parseProviderKeysPayload(raw: string | undefined): { keys: ProviderKeySlot[]; activeIndex: number; }; export declare function serializeProviderKeysPayload(keys: readonly ProviderKeySlot[], activeIndex: number): string; export declare function envValue(provider: ProviderId): string | undefined; /** * Resolve all API keys for a provider. * * Precedence: * 1. Stored multi/single key under `llm:` (keychain / fallback) * 2. Env var as a single synthetic slot when nothing is stored * * `ollama` returns the host URL as a single local "key" for listing only. */ export declare function getProviderKeys(provider: ProviderId): Promise; /** * Resolve an LLM provider's secret using the precedence: * * 1. OS keychain account `llm:` (with lazy migration of the * legacy bare `` account) — i.e. a key the user explicitly * stored via `clai set ` always wins. * 2. Restricted-permission plaintext fallback file (`~/.clai/keys.json`) * 3. Provider env var (e.g. `OPENAI_API_KEY`) — used only when nothing has * been explicitly stored, so a stale ambient export can never override * a key the user deliberately set with `clai set`. * * Returns the **active** (sticky) key when multiple are configured. * * `ollama` is special-cased: it has no API key, only a base URL drawn from * `OLLAMA_HOST` or the user-config `ollamaHost`. */ export declare function getProviderSecret(provider: ProviderId): Promise<{ value?: string; source: ProviderStatus['source']; }>; /** * Replace the full key list for a provider (Keys editor Save). * Empty list clears storage (same as unset). */ export declare function setProviderKeys(provider: ProviderId, values: readonly string[], activeIndex?: number, disabledValues?: readonly string[]): Promise<'keychain' | 'fallback'>; /** * Append one API key (CLI `/set provider key`). Dedupes exact matches. * Creates a multi-key envelope, migrating a legacy single string if needed. */ export declare function appendProviderKey(provider: ProviderId, secret: string): Promise<'keychain' | 'fallback'>; /** * Compat: append a key (multi-key era). Prefer `appendProviderKey` or * `setProviderKeys` at new call sites. */ export declare function setProviderSecret(provider: ProviderId, secret: string): Promise<'keychain' | 'fallback'>; export declare function unsetProviderSecret(provider: ProviderId): Promise; /** * Remember which key last succeeded so the next request starts there. * No-op for env-only / ollama / missing storage. */ export declare function markProviderKeySuccess(provider: ProviderId, index: number): Promise; export declare function setProviderKeyDisabled(provider: ProviderId, value: string, disabled: boolean): Promise; export { MAX_PROVIDER_KEYS }; /** * Resolve every API key for a keyed search provider. * * This intentionally mirrors LLM key semantics: stored multi/single keys * win over an ambient environment variable, and an environment value is only * a synthetic single-key fallback when nothing has been stored. DuckDuckGo * is keyless and therefore never exposes stored key slots. */ export declare function getSearchProviderKeys(id: SearchProviderId): Promise; /** Resolve the sticky active key for compatibility with single-key callers. */ export declare function getSearchProviderKey(id: SearchProviderId): Promise<{ value?: string; source: SecretSource; }>; /** Replace all stored keys for a search provider (the shared keys editor). */ export declare function setSearchProviderKeys(id: SearchProviderId, values: readonly string[], activeIndex?: number, disabledValues?: readonly string[]): Promise<'keychain' | 'fallback'>; /** Append a search API key, preserving the existing sticky active key. */ export declare function appendSearchProviderKey(id: SearchProviderId, secret: string): Promise<'keychain' | 'fallback'>; export declare function unsetSearchProviderSecret(id: SearchProviderId): Promise; export declare function setSearchProviderKeyDisabled(id: SearchProviderId, value: string, disabled: boolean): Promise; /** Persist the key that last completed a search successfully as the sticky key. */ export declare function markSearchProviderKeySuccess(id: SearchProviderId, index: number): Promise; export type KeychainStatus = { available: true; } | { available: false; reason: 'module-missing' | 'runtime-error'; detail?: string; }; /** * Probes the OS keychain by performing a harmless read against a marker * service. Used by `clai doctor` so users can tell at a glance whether * secrets land in the OS store or the restricted-permission plaintext * fallback file at ~/.clai/keys.json. */ export declare function probeKeychain(): Promise; export declare function listProviderStatuses(activeProvider: ProviderId): Promise; export { maskSecret };