/** * NVIDIA login flow. * * NVIDIA provides OpenAI-compatible models via https://integrate.api.nvidia.com/v1. * * This is not OAuth - it's a simple API key flow: * 1. Open browser to NVIDIA NGC catalog * 2. User copies their API key * 3. User pastes the API key into the CLI */ import { validateOpenAICompatibleApiKey } from "./api-key-validation"; import type { OAuthController } from "./types"; const AUTH_URL = "https://org.ngc.nvidia.com/setup/personal-keys"; const API_BASE_URL = "https://integrate.api.nvidia.com/v1"; const VALIDATION_MODEL = "nvidia/llama-3.1-nemotron-70b-instruct"; const PROVIDER_ID = "nvidia"; /** * Login to NVIDIA. * * Opens browser to NVIDIA dashboard, prompts user to paste their API key. * Returns the API key directly (not OAuthCredentials - this isn't OAuth). */ export async function loginNvidia(options: OAuthController): Promise { if (!options.onPrompt) { throw new Error("NVIDIA login requires onPrompt callback"); } options.onAuth?.({ url: AUTH_URL, instructions: "Copy your API key from NVIDIA NGC Personal Keys", }); const apiKey = await options.onPrompt({ message: "Paste your NVIDIA API key", placeholder: "nvapi-...", }); if (options.signal?.aborted) { throw new Error("Login cancelled"); } const trimmed = apiKey.trim(); if (!trimmed) { throw new Error("API key is required"); } options.onProgress?.("Validating API key (optional)..."); try { await validateOpenAICompatibleApiKey({ provider: PROVIDER_ID, apiKey: trimmed, baseUrl: API_BASE_URL, model: VALIDATION_MODEL, signal: options.signal, }); } catch (error) { const message = error instanceof Error ? error.message : String(error); const statusMatch = message.match(/\((\d{3})\)/); const statusCode = statusMatch?.[1]; if (statusCode === "401" || statusCode === "403") { throw error; } options.onProgress?.("Skipping NVIDIA validation endpoint; continuing with provided API key."); } return trimmed; }