()
| 91 | // Returns the new API key on a successful exchange, null if there was no |
| 92 | // callback in progress, or throws on failure. |
| 93 | export async function handleCallback() { |
| 94 | const params = new URLSearchParams(location.search); |
| 95 | const code = params.get("code"); |
| 96 | if (!code) return null; |
| 97 | |
| 98 | const verifier = sessionStorage.getItem(VERIFIER_KEY); |
| 99 | sessionStorage.removeItem(VERIFIER_KEY); |
| 100 | |
| 101 | // Always clean the URL, even on failure, so a stale ?code= doesn't haunt |
| 102 | // future loads. |
| 103 | history.replaceState({}, "", callbackURL()); |
| 104 | |
| 105 | if (!verifier) { |
| 106 | throw new Error("PKCE verifier missing — please click Sign in again."); |
| 107 | } |
| 108 | |
| 109 | const r = await fetch("https://openrouter.ai/api/v1/auth/keys", { |
| 110 | method: "POST", |
| 111 | headers: { "Content-Type": "application/json" }, |
| 112 | body: JSON.stringify({ |
| 113 | code, |
| 114 | code_verifier: verifier, |
| 115 | code_challenge_method: "S256", |
| 116 | }), |
| 117 | }); |
| 118 | if (!r.ok) { |
| 119 | const body = await r.text().catch(() => ""); |
| 120 | throw new Error(`OpenRouter token exchange failed: ${r.status} ${body}`); |
| 121 | } |
| 122 | const { key } = await r.json(); |
| 123 | if (!key) throw new Error("OpenRouter token exchange returned no key"); |
| 124 | localStorage.setItem(STORAGE_KEY, key); |
| 125 | return key; |
| 126 | } |
| 127 | |
| 128 | // getKey returns the stored API key, or null if the user is not signed in. |
| 129 | export function getKey() { |
nothing calls this directly
no test coverage detected