scripts/claude-oauth-login.ts

#!/usr/bin/env bun /**

  • One-time script to obtain Claude OAuth tokens for clanky.
  • Run: bun scripts/claude-oauth-login.ts
    1. Opens the authorize URL in your browser
    1. You log in / authorize
    1. Paste the code back here
    1. Tokens are saved to data/claude-oauth-tokens.json */

import { buildAuthorizeUrl, exchangeCodeForTokens } from "../src/llm/claudeOAuth.ts"; import { createInterface } from "node:readline";

const { url, verifier } = buildAuthorizeUrl();

console.log(" --- Claude OAuth Login --- "); console.log("1. Open this URL in your browser: "); console.log( ${url}); console.log("2. Log in with your Claude Pro/Max account and authorize. "); console.log("3. You'll see an authorization code. Copy it and paste it below. ");

// Try to open the URL automatically try { const proc = Bun.spawn(["open", url], { stdout: "ignore", stderr: "ignore" }); await proc.exited; } catch { // ignore - user can open manually }

const rl = createInterface({ input: process.stdin, output: process.stdout }); const code = await new Promise((resolve) => { rl.question("Authorization code: ", (answer) => { rl.close(); resolve(answer.trim()); }); });

if (!code) { console.error(" No code provided. Aborting."); process.exit(1); }

try { const tokens = await exchangeCodeForTokens(code, verifier); console.log(" Tokens saved to data/claude-oauth-tokens.json"); console.log(Refresh token: ${tokens.refreshToken.slice(0, 12)}...); console.log(Access token expires: ${new Date(tokens.expiresAt).toISOString()}); console.log(" You can now use provider: claude-oauth in your settings."); console.log("Or set DEFAULT_PROVIDER=claude-oauth in your .env "); } catch (error) { console.error(" Failed to exchange code:", (error as Error).message); process.exit(1); }