import type { OAuthCredentials, OAuthLoginCallbacks } from "@earendil-works/pi-ai"; import type { ProviderConfig } from "@earendil-works/pi-coding-agent"; import type { OllamaCloudCredentials, OllamaProviderModel } from "./models.js"; import { getOllamaCloudRuntimeConfig, OLLAMA_CLOUD_API_KEY_ENV, OLLAMA_CLOUD_AUTH_DOCS_URL } from "./config.js"; import { enrichOllamaCloudCredentials } from "./models.js"; const STATIC_CREDENTIAL_TTL_MS = 365 * 24 * 60 * 60 * 1000; export async function loginOllamaCloud(callbacks: OAuthLoginCallbacks): Promise { const config = getOllamaCloudRuntimeConfig(); callbacks.onAuth({ instructions: "Create an Ollama API key, then paste it back into pi. Ollama documents API keys for third-party cloud access; pi uses that flow for Ollama Cloud login.", url: config.keysUrl, }); callbacks.onProgress?.("Waiting for Ollama Cloud API key..."); const envApiKey = getEnvApiKey(); const promptMessage = envApiKey ? `Paste your Ollama API key (leave blank to use ${OLLAMA_CLOUD_API_KEY_ENV} from the environment):` : `Paste your Ollama API key (see ${OLLAMA_CLOUD_AUTH_DOCS_URL}):`; const input = (await callbacks.onPrompt({ message: promptMessage })).trim(); const apiKey = input || envApiKey; if (!apiKey) { throw new Error( `No Ollama API key provided. Set ${OLLAMA_CLOUD_API_KEY_ENV} or paste a key from ${config.keysUrl}.`, ); } callbacks.onProgress?.("Validating Ollama Cloud API key and discovering models..."); return enrichOllamaCloudCredentials(createStaticCredential(apiKey), { signal: callbacks.signal, }); } export async function refreshOllamaCloudCredential( credentials: OAuthCredentials, options: { preserveModels?: boolean } = {}, ): Promise { return enrichOllamaCloudCredentials(createStaticCredential(credentials.access), { previous: options.preserveModels === false ? undefined : (credentials as OllamaCloudCredentials), }); } export async function refreshOllamaCloudCredentialModels( credentials: OllamaCloudCredentials, ): Promise { return enrichOllamaCloudCredentials(createStaticCredential(credentials.access), { previous: credentials }); } export type CloudModelsGetter = () => OllamaProviderModel[]; export function createOllamaCloudOAuthProvider( _getActiveCloudModels: CloudModelsGetter, ): NonNullable { return { getApiKey(credentials) { return credentials.access; }, async login(callbacks) { return loginOllamaCloud(callbacks); }, name: "Ollama Cloud", async refreshToken(credentials) { return refreshOllamaCloudCredential(credentials); }, }; } function createStaticCredential(apiKey: string): OAuthCredentials { return { access: apiKey, expires: Date.now() + STATIC_CREDENTIAL_TTL_MS, refresh: apiKey, }; } function getEnvApiKey(): string | undefined { const value = process.env[OLLAMA_CLOUD_API_KEY_ENV]; return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined; }