get your claude code tokens here
1import { chmodSync, existsSync } from "node:fs";
2import fs from "node:fs/promises";
3import path from "node:path";
4
5const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
6
7const HOME = process.env.HOME || process.env.USERPROFILE || ".";
8const CACHE_DIR = path.join(HOME, ".config/crush/anthropic");
9const BEARER_FILE = path.join(CACHE_DIR, "bearer_token");
10const REFRESH_FILE = path.join(CACHE_DIR, "refresh_token");
11const EXPIRES_FILE = path.join(CACHE_DIR, "bearer_token.expires");
12
13export type TokenEntry = {
14 accessToken: string;
15 refreshToken: string;
16 expiresAt: number;
17};
18
19export async function ensureDir() {
20 await fs.mkdir(CACHE_DIR, { recursive: true });
21}
22
23export async function writeSecret(filePath: string, data: string) {
24 await fs.writeFile(filePath, data, { encoding: "utf8", mode: 0o600 });
25 chmodSync(filePath, 0o600);
26}
27
28export async function readText(filePath: string) {
29 if (!existsSync(filePath)) return undefined;
30 return await fs.readFile(filePath, "utf8");
31}
32
33export async function loadFromDisk(): Promise<TokenEntry | undefined> {
34 const [bearer, refresh, expires] = await Promise.all([
35 readText(BEARER_FILE),
36 readText(REFRESH_FILE),
37 readText(EXPIRES_FILE),
38 ]);
39 if (!bearer || !refresh || !expires) return undefined;
40 const exp = Number.parseInt(expires, 10) || 0;
41 return {
42 accessToken: bearer.trim(),
43 refreshToken: refresh.trim(),
44 expiresAt: exp,
45 };
46}
47
48export async function saveToDisk(entry: TokenEntry) {
49 await ensureDir();
50 await writeSecret(BEARER_FILE, `${entry.accessToken}\n`);
51 await writeSecret(REFRESH_FILE, `${entry.refreshToken}\n`);
52 await writeSecret(EXPIRES_FILE, `${String(entry.expiresAt)}\n`);
53}
54
55export async function exchangeRefreshToken(refreshToken: string) {
56 const res = await fetch("https://console.anthropic.com/v1/oauth/token", {
57 method: "POST",
58 headers: {
59 "content-type": "application/json",
60 "user-agent": "anthropic",
61 },
62 body: JSON.stringify({
63 grant_type: "refresh_token",
64 refresh_token: refreshToken,
65 client_id: CLIENT_ID,
66 }),
67 });
68 if (!res.ok) throw new Error(`refresh failed: ${res.status}`);
69 return (await res.json()) as {
70 access_token: string;
71 refresh_token?: string;
72 expires_in: number;
73 };
74}
75
76/**
77 * Attempts to load a valid token from disk, refresh if needed, and print it to stdout.
78 * Returns true if a valid token was found and printed, false otherwise.
79 */
80export async function bootstrapFromDisk(): Promise<boolean> {
81 const entry = await loadFromDisk();
82 if (!entry) return false;
83 const now = Math.floor(Date.now() / 1000);
84 if (now < entry.expiresAt - 60) {
85 process.stdout.write(`${entry.accessToken}\n`);
86 setTimeout(() => process.exit(0), 50);
87 return true;
88 }
89 try {
90 const refreshed = await exchangeRefreshToken(entry.refreshToken);
91 entry.accessToken = refreshed.access_token;
92 entry.expiresAt = Math.floor(Date.now() / 1000) + refreshed.expires_in;
93 if (refreshed.refresh_token) entry.refreshToken = refreshed.refresh_token;
94 await saveToDisk(entry);
95 process.stdout.write(`${entry.accessToken}\n`);
96 setTimeout(() => process.exit(0), 50);
97 return true;
98 } catch {
99 return false;
100 }
101}