import { useState } from "react"; interface ProvisionResult { space_url: string; space_repo: string; rotation_secret: string; username: string; oauth_token: string; existing?: boolean; } interface Provider { id: string; label: string; envKey: string; keyUrl: string; hint: string; placeholder: string; } const PROVIDERS: Provider[] = [ { id: "groq", label: "Groq", envKey: "GROQ_API_KEY", keyUrl: "https://console.groq.com/keys", hint: "Fast inference, very generous free tier.", placeholder: "gsk_…", }, { id: "nvidia", label: "NVIDIA AI", envKey: "NVIDIA_API_KEY", keyUrl: "https://build.nvidia.com", hint: "Free credits for Llama, Mixtral, and more.", placeholder: "nvapi-…", }, { id: "google", label: "Google AI Studio", envKey: "GOOGLE_API_KEY", keyUrl: "https://aistudio.google.com/apikey", hint: "Gemini Flash & Pro — free tier.", placeholder: "AIza…", }, { id: "openrouter", label: "OpenRouter", envKey: "OPENROUTER_API_KEY", keyUrl: "https://openrouter.ai/keys", hint: "Access 100+ models; many have free quotas.", placeholder: "sk-or-…", }, { id: "anthropic", label: "Anthropic (Claude) — paid", envKey: "ANTHROPIC_API_KEY", keyUrl: "https://console.anthropic.com/settings/keys", hint: "Optional. Unlocks the full Claude agent tier (paid key).", placeholder: "sk-ant-…", }, ]; export function ProviderKeyWizard({ provision, onDone, }: { provision: ProvisionResult; onDone: () => void; }) { const [values, setValues] = useState>({}); const [statuses, setStatuses] = useState>({}); const [errors, setErrors] = useState>({}); const setKey = (id: string, val: string) => setValues((v) => ({ ...v, [id]: val })); async function saveKey(provider: Provider) { const val = (values[provider.id] || "").trim(); if (!val) return; setStatuses((s) => ({ ...s, [provider.id]: "saving" })); setErrors((e) => ({ ...e, [provider.id]: "" })); try { const res = await fetch("/oauth/set-provider-key", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ oauth_token: provision.oauth_token, repo: provision.space_repo, key_name: provider.envKey, key_value: val, }), }); if (!res.ok) { const j = await res.json().catch(() => ({})); throw new Error((j as { error?: string }).error || `HTTP ${res.status}`); } setStatuses((s) => ({ ...s, [provider.id]: "ok" })); } catch (e) { setStatuses((s) => ({ ...s, [provider.id]: "err" })); setErrors((er) => ({ ...er, [provider.id]: (e as Error).message })); } } const anyOk = Object.values(statuses).some((s) => s === "ok"); // Returning users already configured their keys on a previous visit — // never block them behind a step they've completed before. const canContinue = anyOk || !!provision.existing; const field = "flex-1 bg-surface border border-border rounded-xl px-3 py-2 text-[13px] outline-none focus:border-accent min-w-0"; return (

{provision.existing ? `Welcome back, ${provision.username}` : "Add a provider key"}

{provision.existing ? ( <> Your Space at {provision.space_url}{" "} has been re-linked with a fresh secret (it restarts briefly). Your existing provider keys are untouched — add more below, or continue. ) : ( <> Your Space is provisioned at{" "} {provision.space_url}. Add at least one free API key so the judges have models to call. )}

{PROVIDERS.map((p) => { const st = statuses[p.id] ?? "idle"; return (
{p.label} {p.hint} {st === "ok" && saved ✓}
setKey(p.id, e.target.value)} autoCapitalize="none" autoCorrect="off" disabled={st === "ok"} /> Get key
{st === "err" && errors[p.id] && (

{errors[p.id]}

)}
); })}
{!canContinue && (

Add at least one key above to continue.

)}
); }