import type { Api, Model } from "@earendil-works/pi-ai"; import type { ModelRuntime } from "./model-runtime.ts"; import type { AuthStatus, ProviderConfigInput } from "./provider-composer.ts"; export type { ProviderConfigInput } from "./provider-composer.ts"; export type ResolvedRequestAuth = | { ok: true; apiKey?: string; headers?: Record; env?: Record; } | { ok: false; error: string }; export { clearApiKeyCache } from "./provider-composer.ts"; /** * Synchronous compatibility facade exposed to extensions. * Coding-agent internals use ModelRuntime directly. */ export class ModelRegistry { private readonly runtime: ModelRuntime; constructor(runtime: ModelRuntime) { this.runtime = runtime; } /** Reload models.json asynchronously. Await before making synchronous registry reads. */ refresh(): Promise { return this.runtime.reloadConfig(); } getError(): string | undefined { return this.runtime.getError(); } getAll(): Model[] { return [...this.runtime.getModels()]; } getAvailable(): Model[] { return [...this.runtime.getAvailableSnapshot()]; } find(provider: string, modelId: string): Model | undefined { return this.runtime.getModel(provider, modelId); } hasConfiguredAuth(model: Model): boolean { return this.runtime.hasConfiguredAuth(model.provider); } async getApiKeyAndHeaders(model: Model): Promise { try { const resolution = await this.runtime.getAuth(model); if (!resolution) { const compatibility = this.runtime.getCompatibilityRequestConfig(model); if (compatibility.authHeader) { return { ok: false, error: `No API key found for "${model.provider}"` }; } const headers = compatibility.headers ? Object.fromEntries( Object.entries(compatibility.headers).filter( (entry): entry is [string, string] => entry[1] !== null, ), ) : undefined; return { ok: true, headers }; } const headers = resolution.auth.headers ? Object.fromEntries( Object.entries(resolution.auth.headers).filter( (entry): entry is [string, string] => entry[1] !== null, ), ) : undefined; return { ok: true, apiKey: resolution.auth.apiKey, headers, env: resolution.env }; } catch (error) { const cause = error instanceof Error ? error.cause : undefined; const message = cause instanceof Error ? cause.message : error instanceof Error ? error.message : String(error); return { ok: false, error: message === "authHeader requires a resolved API key" ? `No API key found for "${model.provider}"` : message, }; } } getProviderAuthStatus(provider: string): AuthStatus { return this.runtime.getProviderAuthStatus(provider); } getProviderDisplayName(provider: string): string { return this.runtime.getProvider(provider)?.name ?? provider; } async getApiKeyForProvider(provider: string): Promise { try { return (await this.runtime.getAuth(provider))?.auth.apiKey; } catch { return undefined; } } isUsingOAuth(model: Model): boolean { return this.runtime.isUsingOAuth(model.provider); } registerProvider(providerName: string, config: ProviderConfigInput): void { this.runtime.registerProvider(providerName, config); } unregisterProvider(providerName: string): void { this.runtime.unregisterProvider(providerName); } getRegisteredProviderConfig(providerName: string): ProviderConfigInput | undefined { return this.runtime.getRegisteredProviderConfig(providerName); } getRegisteredProviderIds(): readonly string[] { return this.runtime.getRegisteredProviderIds(); } }