Created
January 22, 2026 00:23
-
-
Save Melvynx/bfe45de5405c7f3be04bd90df20ed573 to your computer and use it in GitHub Desktop.
Use Claude models with local API keys (OAuth tokens from Claude Code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Claude Code AI - Use Claude Models with Local OAuth Tokens | |
| Utility scripts to access Claude API using Claude Code's OAuth credentials. No need for a separate API key! | |
| ## Why? | |
| When you authenticate with Claude Code (the official CLI), it stores OAuth tokens locally. These scripts let you reuse those tokens to make API calls to Claude models programmatically. | |
| ## Installation | |
| ```bash | |
| # Clone or copy the scripts | |
| bun add @ai-sdk/anthropic ai | |
| ``` | |
| ## Quick Start | |
| ### CLI Usage | |
| ```bash | |
| bun run cli.ts "What is the capital of France?" -m haiku | |
| bun run cli.ts "Explain quantum computing" -m sonnet | |
| bun run cli.ts "Write a poem" -m opus -s "You are a creative poet" | |
| ``` | |
| ### Programmatic Usage | |
| ```typescript | |
| import { generateTextCC } from "./claude"; | |
| const response = await generateTextCC({ | |
| prompt: "Your prompt here", | |
| model: "haiku", // "haiku" | "sonnet" | "opus" | |
| system: "Optional system prompt", | |
| }); | |
| console.log(response); | |
| ``` | |
| ### Get OAuth Token Directly | |
| ```typescript | |
| import { getClaudeCodeToken, getClaudeCodeTokenSafe } from "./helper/credentials"; | |
| const token = await getClaudeCodeToken(); // Throws on error | |
| const token = await getClaudeCodeTokenSafe(); // Returns null on error | |
| ``` | |
| ## Available Models | |
| | Alias | Model ID | | |
| |-------|----------| | |
| | `haiku` | `claude-haiku-4-5-20251001` | | |
| | `sonnet` | `claude-sonnet-4-5-20250929` | | |
| | `opus` | `claude-opus-4-5-20251101` | | |
| ## Where Are Credentials Stored? | |
| | Platform | Location | | |
| |----------|----------| | |
| | macOS | Keychain (`security find-generic-password -s "Claude Code-credentials" -w`) | | |
| | Linux | `~/.claude/.credentials.json` | | |
| | Windows | WSL required, uses Linux path | | |
| ## Core Files | |
| ### `claude.ts` - Main API | |
| ```typescript | |
| import { createAnthropic } from "@ai-sdk/anthropic"; | |
| import { generateText } from "ai"; | |
| import { getClaudeCodeToken } from "./helper/credentials"; | |
| export type Model = "haiku" | "sonnet" | "opus"; | |
| const MODEL_MAP: Record<Model, string> = { | |
| haiku: "claude-haiku-4-5-20251001", | |
| sonnet: "claude-sonnet-4-5-20250929", | |
| opus: "claude-opus-4-5-20251101", | |
| }; | |
| export async function generateTextCC({ | |
| prompt, | |
| system, | |
| model = "sonnet", | |
| }: { | |
| prompt: string; | |
| system?: string; | |
| model?: Model; | |
| }): Promise<string> { | |
| const token = await getClaudeCodeToken(); | |
| const anthropic = createAnthropic({ | |
| apiKey: "oauth-token", | |
| headers: { | |
| "anthropic-beta": "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14", | |
| "anthropic-dangerous-direct-browser-access": "true", | |
| "x-app": "cli", | |
| "User-Agent": "claude-cli/2.0.76 (external, cli)", | |
| }, | |
| fetch: (async (url: RequestInfo | URL, options?: RequestInit) => { | |
| const headers = new Headers(options?.headers); | |
| headers.delete("x-api-key"); | |
| headers.set("Authorization", `Bearer ${token}`); | |
| return fetch(url, { ...options, headers }); | |
| }) as typeof fetch, | |
| }); | |
| const result = await generateText({ | |
| model: anthropic(MODEL_MAP[model]), | |
| system, | |
| prompt, | |
| }); | |
| return result.text; | |
| } | |
| ``` | |
| ### `helper/credentials.ts` - Token Retrieval | |
| ```typescript | |
| import { existsSync } from "node:fs"; | |
| import { readFile } from "node:fs/promises"; | |
| import { homedir } from "node:os"; | |
| import { join } from "node:path"; | |
| import { $ } from "bun"; | |
| interface Credentials { | |
| claudeAiOauth: { | |
| accessToken: string; | |
| refreshToken: string; | |
| expiresAt: number; | |
| scopes: string[]; | |
| subscriptionType: string; | |
| }; | |
| } | |
| function getCredentialsFilePath(): string { | |
| return join(homedir(), ".claude", ".credentials.json"); | |
| } | |
| async function getMacOSKeychainCredentials(): Promise<string> { | |
| const result = await $`security find-generic-password -s "Claude Code-credentials" -w` | |
| .quiet() | |
| .text(); | |
| const creds: Credentials = JSON.parse(result.trim()); | |
| return creds.claudeAiOauth.accessToken; | |
| } | |
| async function getFileCredentials(): Promise<string> { | |
| const credentialsPath = getCredentialsFilePath(); | |
| if (!existsSync(credentialsPath)) { | |
| throw new Error(`Credentials file not found at ${credentialsPath}`); | |
| } | |
| const content = await readFile(credentialsPath, "utf-8"); | |
| const creds: Credentials = JSON.parse(content); | |
| return creds.claudeAiOauth.accessToken; | |
| } | |
| export async function getClaudeCodeToken(): Promise<string> { | |
| const platform = process.platform; | |
| if (platform === "darwin") { | |
| try { | |
| return await getMacOSKeychainCredentials(); | |
| } catch { | |
| return await getFileCredentials(); | |
| } | |
| } | |
| return await getFileCredentials(); | |
| } | |
| export async function getClaudeCodeTokenSafe(): Promise<string | null> { | |
| try { | |
| return await getClaudeCodeToken(); | |
| } catch { | |
| return null; | |
| } | |
| } | |
| ``` | |
| ### `cli.ts` - Command Line Interface | |
| ```typescript | |
| #!/usr/bin/env bun | |
| import { parseArgs } from "util"; | |
| import { generateTextCC, type Model } from "./claude"; | |
| const { values, positionals } = parseArgs({ | |
| args: Bun.argv.slice(2), | |
| options: { | |
| model: { type: "string", short: "m", default: "sonnet" }, | |
| system: { type: "string", short: "s" }, | |
| }, | |
| allowPositionals: true, | |
| }); | |
| const prompt = positionals.join(" "); | |
| if (!prompt) { | |
| console.error("Usage: bun run cli.ts <prompt> [-m opus|sonnet|haiku] [-s system_prompt]"); | |
| process.exit(1); | |
| } | |
| const model = values.model as Model; | |
| if (!["haiku", "sonnet", "opus"].includes(model)) { | |
| console.error(`Invalid model: ${model}. Use: opus, sonnet, or haiku`); | |
| process.exit(1); | |
| } | |
| const response = await generateTextCC({ prompt, model, system: values.system }); | |
| console.log(response); | |
| ``` | |
| ## Prerequisites | |
| 1. Install and authenticate [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) | |
| 2. Install [Bun](https://bun.sh) runtime | |
| 3. Install dependencies: `bun add @ai-sdk/anthropic ai` | |
| ## Notes | |
| - Tokens are automatically retrieved from the local credential store | |
| - On macOS, tokens are stored securely in Keychain | |
| - On Linux/Windows (WSL), tokens are stored in `~/.claude/.credentials.json` | |
| - The script handles cross-platform differences automatically |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment