|
| 1 | +import { readFile } from "node:fs/promises" |
| 2 | +import { join } from "node:path" |
| 3 | +import { homedir } from "node:os" |
| 4 | + |
| 5 | +export interface OpenCodeAuthEntry { |
| 6 | + type: "oauth" | "api" | "wellknown" |
| 7 | + access?: string | undefined |
| 8 | + refresh?: string | undefined |
| 9 | + expires?: number | undefined |
| 10 | + accountId?: string | undefined |
| 11 | + key?: string | undefined |
| 12 | + token?: string | undefined |
| 13 | + enterpriseUrl?: string | undefined |
| 14 | +} |
| 15 | + |
| 16 | +export interface OpenCodeAuthReaderOptions { |
| 17 | + readFileFn?: ((path: string, encoding: BufferEncoding) => Promise<string>) | undefined |
| 18 | + env?: NodeJS.ProcessEnv | undefined |
| 19 | + platform?: string | undefined |
| 20 | +} |
| 21 | + |
| 22 | +export function resolveOpenCodeDataDir( |
| 23 | + env?: NodeJS.ProcessEnv, |
| 24 | + platform?: string |
| 25 | +): string { |
| 26 | + const e = env ?? process.env |
| 27 | + const p = platform ?? process.platform |
| 28 | + |
| 29 | + if (e.XDG_DATA_HOME) { |
| 30 | + return join(e.XDG_DATA_HOME, "opencode") |
| 31 | + } |
| 32 | + |
| 33 | + if (p === "win32" && e.LOCALAPPDATA) { |
| 34 | + return join(e.LOCALAPPDATA, "opencode") |
| 35 | + } |
| 36 | + |
| 37 | + return join(homedir(), ".local", "share", "opencode") |
| 38 | +} |
| 39 | + |
| 40 | +export async function readOpenCodeAuth( |
| 41 | + providerKey: string, |
| 42 | + options?: OpenCodeAuthReaderOptions |
| 43 | +): Promise<OpenCodeAuthEntry | null> { |
| 44 | + const read = options?.readFileFn ?? readFile |
| 45 | + const dataDir = resolveOpenCodeDataDir(options?.env, options?.platform) |
| 46 | + const authJsonPath = join(dataDir, "auth.json") |
| 47 | + |
| 48 | + let content: string |
| 49 | + try { |
| 50 | + content = await read(authJsonPath, "utf-8") |
| 51 | + } catch { |
| 52 | + return null |
| 53 | + } |
| 54 | + |
| 55 | + let parsed: unknown |
| 56 | + try { |
| 57 | + parsed = JSON.parse(content) |
| 58 | + } catch { |
| 59 | + return null |
| 60 | + } |
| 61 | + |
| 62 | + if (typeof parsed !== "object" || parsed === null) { |
| 63 | + return null |
| 64 | + } |
| 65 | + |
| 66 | + const entry = (parsed as Record<string, unknown>)[providerKey] |
| 67 | + if (typeof entry !== "object" || entry === null) { |
| 68 | + return null |
| 69 | + } |
| 70 | + |
| 71 | + const data = entry as Record<string, unknown> |
| 72 | + const entryType = data.type |
| 73 | + if (entryType !== "oauth" && entryType !== "api" && entryType !== "wellknown") { |
| 74 | + return null |
| 75 | + } |
| 76 | + |
| 77 | + return { |
| 78 | + type: entryType, |
| 79 | + access: typeof data.access === "string" ? data.access : undefined, |
| 80 | + refresh: typeof data.refresh === "string" ? data.refresh : undefined, |
| 81 | + expires: typeof data.expires === "number" ? data.expires : undefined, |
| 82 | + accountId: typeof data.accountId === "string" ? data.accountId : undefined, |
| 83 | + key: typeof data.key === "string" ? data.key : undefined, |
| 84 | + token: typeof data.token === "string" ? data.token : undefined, |
| 85 | + enterpriseUrl: typeof data.enterpriseUrl === "string" ? data.enterpriseUrl : undefined, |
| 86 | + } |
| 87 | +} |
0 commit comments