···
const PORT = Number(Bun.env.PORT || 8787);
20
-
const ROOT = new URL("../", import.meta.url).pathname;
21
-
const PUBLIC_DIR = `${ROOT}public`;
23
-
function notFound() {
24
-
return new Response("Not found", { status: 404 });
27
-
async function serveStatic(pathname: string) {
28
-
const filePath = PUBLIC_DIR + (pathname === "/" ? "/index.html" : pathname);
30
-
const file = Bun.file(filePath);
31
-
if (!(await file.exists())) return null;
32
-
return new Response(file);
function json(data: unknown, init: ResponseInit = {}) {
return new Response(JSON.stringify(data), {
···
122
+
const indexHtml = `
126
+
<meta charset="utf-8" />
127
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
128
+
<title>Anthropic Auth</title>
140
+
background: #0f0f10;
145
+
align-items: center;
146
+
justify-content: center;
149
+
background: #1a1a1b;
150
+
border: 1px solid #2b2b2c;
151
+
border-radius: 14px;
164
+
background: linear-gradient(135deg, #ff6b35, #ff8e53);
167
+
border-radius: 10px;
168
+
padding: 12px 16px;
171
+
text-decoration: none;
172
+
display: inline-block;
178
+
border: 1px solid #2b2b2c;
179
+
border-radius: 10px;
194
+
<script type="module" crossorigin src="../anthropic-api-key/index-9f070n0a.js"></script></head>
197
+
<h1>Anthropic Authentication</h1>
199
+
Start the OAuth flow, authorize in the new tab, then paste the
200
+
returned token here.
209
+
style="display: none"
210
+
>Open Anthropic Authorization</a
215
+
<label for="code">Authorization code</label>
218
+
placeholder="Paste the exact code shown by Anthropic (not a URL). If it includes a #, keep the part after it too."
223
+
<button id="complete">Complete Authentication</button>
226
+
<div id="status" class="status"></div>
231
+
const statusEl = document.getElementById("status");
233
+
function setStatus(msg, ok) {
234
+
statusEl.textContent = msg;
235
+
statusEl.style.color = ok ? "#34a853" : "#ea4335";
239
+
setStatus("Preparing authorization...", true);
240
+
const res = await fetch("/api/auth/start", { method: "POST" });
242
+
setStatus("Failed to prepare auth", false);
245
+
const data = await res.json();
246
+
verifier = data.verifier;
247
+
const a = document.getElementById("authlink");
248
+
a.href = data.authUrl;
249
+
a.style.display = "inline-block";
251
+
'Ready. Click "Open Authorization" to continue.',
256
+
const completeBtn = document.getElementById("complete");
258
+
.getElementById("complete")
259
+
.addEventListener("click", async () => {
260
+
if (completeBtn.disabled) return;
261
+
completeBtn.disabled = true;
262
+
const code = document.getElementById("code").value.trim();
263
+
if (!code || !verifier) {
265
+
"Missing code or verifier. Click Start first.",
268
+
completeBtn.disabled = false;
271
+
const res = await fetch("/api/auth/complete", {
273
+
headers: { "content-type": "application/json" },
274
+
body: JSON.stringify({ code, verifier }),
277
+
setStatus("Code exchange failed", false);
278
+
completeBtn.disabled = false;
281
+
setStatus("Authenticated! Fetching token...", true);
282
+
const t = await fetch("/api/token");
284
+
setStatus("Could not fetch token", false);
285
+
completeBtn.disabled = false;
288
+
const tok = await t.json();
290
+
"Access token acquired (expires " +
291
+
new Date(tok.expiresAt * 1000).toLocaleString() +
// Only start the server and open the browser if we didn't bootstrap from disk
{ accessToken: string; refreshToken: string; expiresAt: number }
development: { console: false },
150
-
const url = new URL(req.url);
152
-
if (url.pathname.startsWith("/api/")) {
153
-
if (url.pathname === "/api/ping")
154
-
return json({ ok: true, ts: Date.now() });
156
-
if (url.pathname === "/api/auth/start" && req.method === "POST") {
316
+
"/api/auth/start": {
317
+
POST: async () => {
const { verifier, challenge } = await pkcePair();
const authUrl = authorizeUrl(verifier, challenge);
return json({ authUrl, verifier });
162
-
if (url.pathname === "/api/auth/complete" && req.method === "POST") {
324
+
"/api/auth/complete": {
325
+
POST: async (req) => {
const body = (await req.json().catch(() => ({}))) as {
···
Bun.write(Bun.stdout, `${entry.accessToken}\n`);
setTimeout(() => process.exit(0), 100);
return json({ ok: true });
186
-
if (url.pathname === "/api/token" && req.method === "GET") {
350
+
// Get current token (refresh if needed)
let entry = memory.get("tokens");
const disk = await loadFromDisk();
···
accessToken: entry.accessToken,
expiresAt: entry.expiresAt,
381
+
// Wildcard route for all other /api/ routes
383
+
new Response(indexHtml, {
384
+
headers: { "content-type": "text/html; charset=utf-8" },
217
-
const staticResp = await serveStatic(url.pathname);
218
-
if (staticResp) return staticResp;
388
+
// Fallback for all other routes: serve static or 404
390
+
return new Response(
391
+
"something went wrong and your request fell through",