/** * Provider settings adapter for the TUI `/model` provider-management panel: * the same-process equivalent of the web host's Models page join * (`packages/client/ui-settings-models`), reading the advisory `ctx.llm` * registry, the redacted `ctx.settings` descriptors, and the value-free * `ctx.credentials` facts directly. Secrets never cross this module: settings * are read with `redactSecrets: true`, credentials are only ever described * (never resolved), and every message is single-line without embedding key * data. * * @module @deepseek-ai/dsh-tui/provider-settings */ import type { Context } from '@deepseek-ai/cordis'; /** * The conventional credential reference for a provider route: `_API_KEY` * with the route uppercased and every non-alphanumeric run collapsed to one * underscore — the exact derivation the official Models page uses * (`deriveKeyRef` in `ui-settings-models`), so a key saved here is found there. * @param provider - provider route id (e.g. `pi-ai`, `minimax-cn`). * @returns the derived reference name (e.g. `PI_AI_API_KEY`). */ export declare function deriveCredentialRef(provider: string): string; /** Value-free facts about one credential reference — never the value. */ export interface ProviderCredentialFacts { /** Whether the reference currently resolves to a stored value. */ readonly configured: boolean; /** Source layer supplying the value; absent while unconfigured. */ readonly source?: string; /** Whether a write through this panel would currently succeed. */ readonly writable: boolean; } /** * One row's credential state: value-free facts once the reference was * described, a bounded error when that describe failed (the row itself is * never dropped), or `undefined` when the row names no reference to describe — * an unmanaged active provider, a dormant route, or a profile authenticating * through the provider's own path. */ export type ProviderCredentialView = ({ readonly kind: 'facts'; } & ProviderCredentialFacts) | { readonly kind: 'error'; readonly message: string; }; /** One explicit model enabled for a provider profile. */ export interface ProviderModelSettings { readonly id: string; readonly name?: string; readonly contextWindow?: number; readonly maxTokens?: number; /** * Remaining entry fields the editor does not model (`reasoningEfforts`, * `compat`, `input`, …), carried verbatim so a save preserves them. * Populated by {@link loadProviderSettings}; never contains the four * modelled keys. */ readonly extras?: Readonly>; } /** The small, portable subset of a provider profile the terminal edits. */ export interface ProviderConfiguration { readonly baseURL?: string; /** * Wire protocol the stored profile names (e.g. `openai-responses`), when it * names one. Load-only: the editor never writes it, but endpoint discovery * passes it so the listing speaks the same protocol as real requests. */ readonly api?: string; readonly models: readonly ProviderModelSettings[]; } /** One model an endpoint reported about itself (mirrors `LlmDiscoveredModel`). */ export interface DiscoveredModelView { /** Model id the endpoint accepts. */ readonly id: string; /** Human-readable name when the endpoint supplies one. */ readonly name?: string; /** Context window when disclosed; adoption still owes it if absent. */ readonly contextWindow?: number; /** Output cap when disclosed. */ readonly maxTokens?: number; } /** * The seven canonical reasoning levels a reasoningEfforts key may name - * pi-ai's THINKING_LEVELS. A pi-ai upgrade that adds or removes one fails * upstream's own drift gate; this mirror exists so the terminal editor can * validate drafts without importing the pi-ai package. */ export declare const REASONING_EFFORT_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"]; /** * One stored reasoningEfforts declaration: a display-level to wire-value map * (null sends no reasoning parameter), an explicit false disabling the * picker, or undefined leaving the entry to inherit. */ export type ReasoningEffortsValue = Record | false | undefined; /** Whether a raw extras value is a declared efforts dict (non-empty, non-false). */ export declare function isDeclaredReasoningEfforts(value: unknown): value is Record; /** * Parse the setup page's compact efforts draft into a storable declaration. * Grammar: empty = clear back to inherit; the single token "false" = disable * the picker; otherwise space-separated level:wire pairs where level is one * of REASONING_EFFORT_LEVELS and wire is any non-empty string or the literal * "null" (send no parameter). */ export declare function parseReasoningEffortsDraft(draft: string): { readonly ok: true; readonly value: ReasoningEffortsValue; } | { readonly ok: false; readonly error: string; }; /** Serialize a stored declaration back to the compact draft form (stored key order preserved). */ export declare function serializeReasoningEfforts(value: unknown): string; /** * One provider row in the TUI provider-management panel: the configurable * directory entry joined with its settings profile and credential facts. * Every mutation below addresses this row, and the caller passes the row back * after re-loading so the revision/ref facts are current. */ export interface ProviderTargetView { /** Provider route id (`GenerateOptions.provider`). */ readonly provider: string; /** Human-readable provider name. */ readonly displayName: string; /** Whether an adapter currently serves this route. */ readonly active: boolean; /** User-settings namespace whose section configures this provider; '' when unmanaged. */ readonly settingsNs: string; /** Path from that section's root to this provider's profile; [] when the whole section is the profile. */ readonly settingsPath: readonly string[]; /** Revision of the owning settings section at load (0 when no namespace resolved). */ readonly settingsRevision: number; /** Whether the resolved profile exists (the whole section, or at `settingsPath`). */ readonly configured: boolean; /** Whether only the user settings layer carries the profile, so removal restores the base. */ readonly removable: boolean; /** The credential reference the resolved profile names, when one does. */ readonly credentialRef?: string; /** The conventional reference a save uses for a dormant or ref-less profile. */ readonly suggestedRef: string; /** Credential facts, a bounded describe error, or undefined when there is no ref to describe. */ readonly credential: ProviderCredentialView | undefined; /** Endpoint and explicit model overrides visible to the provider editor. */ readonly configuration: ProviderConfiguration; /** The owning adapter reports this route as hand-declared (absent when it draws no distinction). */ readonly declared?: boolean; /** * Configuration diagnostic the adapter reported for this route (catalog or * profile damage): the row stays listed and repairable instead of the whole * provider vanishing; absent when the route reads clean. */ readonly diagnostic?: string; } /** The resolved provider/settings/credential join. */ export interface ProviderSettingsDirectory { /** Provider rows: configurable-directory order first, active-unmanaged rows after. */ readonly rows: readonly ProviderTargetView[]; /** Whether the settings provider accepts writes (mirrors the web page's flag). */ readonly writable: boolean; /** Non-fatal join failures (settings/directory reads), for a degradation notice. */ readonly failures: readonly string[]; } /** Subscribe to the same provider-directory invalidations as the official Web Models page. */ export declare function subscribeProviderSettings(ctx: Context, listener: () => void): () => void; /** A single-line, bounded error from the provider-management adapter. */ export declare class ProviderSettingsError extends Error { constructor(message: string); } /** * Join the configurable-provider directory, the redacted settings * namespaces, and the referenced credentials into panel rows, web-parity: * - directory entries merge with `listProviders()` to mark each live or * dormant, and routes registered without a directory declaration appear as * read-only/unmanaged rows (no settings address); * - a whole-section entry is configured whenever its namespace resolves; * a path-addressed one only when the profile resolves there; * - a row is removable when the user layer alone carries its profile; * - only refs named by resolved profiles are described, and a per-ref failure * degrades to that row's bounded error instead of losing it. * Absent `settings`/`credentials` services are tolerated the same way. * @param ctx - context carrying the `llm` service (settings/credentials optional). * @returns the resolved directory; empty rows when `llm` is unavailable. */ export declare function loadProviderSettings(ctx: Context): Promise; /** * Store a provider API key, web-parity: validate with `normalizeApiKey` * (single-line, actionable errors that never echo the key), materialize the * profile/`apiKeyEnv` through `settings.mutate` first when the resolved * profile names no reference (dormant route or ref-less profile), then store * under the trusted named ref or the derived conventional ref. An existing * whole-section DeepSeek whose resolved profile already names * `DEEPSEEK_API_KEY` needs no settings mutation. Env-supplied read-only keys * are refused before any service call. * @param ctx - context carrying `settings` (when materializing) and `credentials`. * @param target - the joined row to write through. * @param rawKey - the key exactly as typed; surrounding whitespace is trimmed. * @throws {@link ProviderSettingsError} with a single-line, key-free message. */ export declare function saveProviderCredential(ctx: Context, target: ProviderTargetView, rawKey: string): Promise; /** Save the endpoint and an explicit model allow-list without rebuilding the profile. */ export declare function saveProviderConfiguration(ctx: Context, target: ProviderTargetView, configuration: ProviderConfiguration): Promise; /** * Switch one provider route to its subscription (plan sign-in) channel: the * profile keeps neither a key reference nor an explicit model list, so the * official catalog endpoint serves requests and the stored OAuth record — not * a settings-named key override — authenticates them. Enabling a route that * has no profile yet creates the empty profile that registers it. * @param ctx - context carrying the `settings` service. * @param target - provider row whose settings namespace owns the profile. * @throws when the route is unmanaged or settings are read-only. */ export declare function enableProviderSubscription(ctx: Context, target: ProviderTargetView): Promise; /** * Interrogate a provider endpoint for the models it really serves, through * the model-discovery capability the provider's settings namespace * registered — the same pipe the official Web Models page uses. The request * is a draft: a typed key forces direct endpoint interrogation (gateway * truth), while an empty key lets the harness resolve the route's stored * credential; with neither baseURL nor route the adapter answers from its * own knowledge. * @param ctx - context carrying the `llm` service (optional discovery). * @param target - provider row whose settings namespace serves the draft. * @param request - typed key and/or endpoint override for this one probe. * @param signal - caller cancellation (panel navigation aborts the probe). * @returns the advertised models in endpoint order, deduplicated. */ export declare function discoverProviderModels(ctx: Context, target: ProviderTargetView, request: { readonly apiKey?: string; readonly baseURL?: string; }, signal?: AbortSignal): Promise; /** * Remove the currently named credential without touching the provider * profile. Only the resolved profile's own reference is unset; a dormant or * ref-less row (nothing to remove), an already-absent key, and an * env-supplied read-only key are rejected safely before any service call. * @param ctx - context carrying the `credentials` service. * @param target - the joined row whose named credential to unset. * @throws {@link ProviderSettingsError} with a single-line, key-free message. */ export declare function unsetProviderCredential(ctx: Context, target: ProviderTargetView): Promise; /** * Remove a user-added provider profile, web-parity: only `removable` rows may * be removed; a page-managed credential — the derived ref, configured and * writable — is unset first (so a second-step failure leaves the row visible * and the operation retryable), then `settings.mutate` unsets * `target.settingsPath`. Both steps are idempotent. A hand-named credential * ref may be shared elsewhere and is left alone. * @param ctx - context carrying `credentials` and `settings`. * @param target - the joined row to remove. * @throws {@link ProviderSettingsError} with a single-line, key-free message. */ export declare function removeProviderSettings(ctx: Context, target: ProviderTargetView): Promise;