@open-vibe-lab/open-sub-auth 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/cli/{claude-3WKImhqM.mjs → claude-Bu4Vb3Sa.mjs} +18 -4
  2. package/dist/cli/{claude-3WKImhqM.mjs.map → claude-Bu4Vb3Sa.mjs.map} +1 -1
  3. package/dist/cli/{registry-Cp-_Ipc6.mjs → errors-BQeS4Q64.mjs} +11 -37
  4. package/dist/cli/errors-BQeS4Q64.mjs.map +1 -0
  5. package/dist/cli/export-CpcD-XlM.mjs +11 -0
  6. package/dist/cli/export-CpcD-XlM.mjs.map +1 -0
  7. package/dist/cli/import-BZDRd0Nj.mjs +40 -0
  8. package/dist/cli/import-BZDRd0Nj.mjs.map +1 -0
  9. package/dist/cli/index.mjs +61 -12
  10. package/dist/cli/index.mjs.map +1 -1
  11. package/dist/cli/{login-_HdTW5J_.mjs → login-DfG9kqBN.mjs} +7 -6
  12. package/dist/cli/login-DfG9kqBN.mjs.map +1 -0
  13. package/dist/cli/{logout-CZm-tSVH.mjs → logout-BdnhQ3aw.mjs} +7 -6
  14. package/dist/cli/logout-BdnhQ3aw.mjs.map +1 -0
  15. package/dist/cli/manager-C-RjavF0.mjs +129 -0
  16. package/dist/cli/manager-C-RjavF0.mjs.map +1 -0
  17. package/dist/cli/{oauth-pkce-Bi02-h23.mjs → oauth-pkce-CCKyG5wP.mjs} +4 -10
  18. package/dist/cli/{oauth-pkce-Bi02-h23.mjs.map → oauth-pkce-CCKyG5wP.mjs.map} +1 -1
  19. package/dist/cli/{openai-codex-DJi_Q6Zm.mjs → openai-codex-CKKY1Gu_.mjs} +3 -3
  20. package/dist/cli/{openai-codex-DJi_Q6Zm.mjs.map → openai-codex-CKKY1Gu_.mjs.map} +1 -1
  21. package/dist/cli/registry-Dv4TWuqv.mjs +39 -0
  22. package/dist/cli/registry-Dv4TWuqv.mjs.map +1 -0
  23. package/dist/cli/{status-C-vkcjVM.mjs → status-CvktIWmx.mjs} +6 -5
  24. package/dist/cli/status-CvktIWmx.mjs.map +1 -0
  25. package/dist/cli/{manager-CKGbp7Yz.mjs → store-57VEqlSz.mjs} +16 -132
  26. package/dist/cli/store-57VEqlSz.mjs.map +1 -0
  27. package/dist/cli/{token-DF_-h4Rb.mjs → token-Ce_FmqGM.mjs} +7 -6
  28. package/dist/cli/token-Ce_FmqGM.mjs.map +1 -0
  29. package/dist/index.cjs +271 -15
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +67 -4
  32. package/dist/index.d.mts +67 -4
  33. package/dist/index.mjs +265 -16
  34. package/dist/index.mjs.map +1 -1
  35. package/package.json +4 -4
  36. package/dist/cli/login-_HdTW5J_.mjs.map +0 -1
  37. package/dist/cli/logout-CZm-tSVH.mjs.map +0 -1
  38. package/dist/cli/manager-CKGbp7Yz.mjs.map +0 -1
  39. package/dist/cli/registry-Cp-_Ipc6.mjs.map +0 -1
  40. package/dist/cli/status-C-vkcjVM.mjs.map +0 -1
  41. package/dist/cli/token-DF_-h4Rb.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["homedir"],"sources":["../src/errors.ts","../src/providers/registry.ts","../src/token/manager.ts","../src/storage/file-store.ts","../src/storage/keychain-store.ts","../src/storage/store.ts","../src/core/browser.ts","../src/core/callback-server.ts","../src/core/crypto.ts","../src/core/manual-code-input.ts","../src/core/oauth-pkce.ts","../src/providers/claude.ts","../src/token/jwt.ts","../src/providers/openai-codex.ts"],"sourcesContent":["export class OpenSubAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"OpenSubAuthError\";\n }\n}\n\nexport class AuthenticationError extends OpenSubAuthError {\n constructor(\n message: string,\n public readonly provider?: string,\n ) {\n super(message);\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class TokenExpiredError extends OpenSubAuthError {\n constructor(public readonly provider: string) {\n super(\n `Access token for \"${provider}\" has expired and no refresh token is available. Please login again.`,\n );\n this.name = \"TokenExpiredError\";\n }\n}\n\nexport class TokenRefreshError extends OpenSubAuthError {\n constructor(\n public readonly provider: string,\n public readonly cause_: unknown,\n ) {\n super(\n `Failed to refresh token for \"${provider}\": ${cause_ instanceof Error ? cause_.message : String(cause_)}`,\n );\n this.name = \"TokenRefreshError\";\n }\n}\n\nexport class ProviderNotFoundError extends OpenSubAuthError {\n constructor(public readonly providerName: string) {\n super(\n `Provider \"${providerName}\" is not registered. Available providers can be listed with listProviders().`,\n );\n this.name = \"ProviderNotFoundError\";\n }\n}\n\nexport class NoCredentialError extends OpenSubAuthError {\n constructor(\n public readonly provider: string,\n public readonly accountId?: string,\n ) {\n const msg = accountId\n ? `No stored credential for \"${provider}\" account \"${accountId}\". Please login first.`\n : `No stored credential for \"${provider}\". Please login first.`;\n super(msg);\n this.name = \"NoCredentialError\";\n }\n}\n\nexport class OAuthCallbackError extends OpenSubAuthError {\n constructor(message: string) {\n super(message);\n this.name = \"OAuthCallbackError\";\n }\n}\n\nexport class OAuthTimeoutError extends OpenSubAuthError {\n constructor(public readonly timeoutMs: number) {\n super(`OAuth login timed out after ${Math.round(timeoutMs / 1000)} seconds. Please try again.`);\n this.name = \"OAuthTimeoutError\";\n }\n}\n\nexport class StateMismatchError extends OpenSubAuthError {\n constructor() {\n super(\"OAuth state parameter mismatch — possible CSRF attack. Please try again.\");\n this.name = \"StateMismatchError\";\n }\n}\n","import { ProviderNotFoundError } from \"@/errors.ts\";\nimport type { Provider } from \"@/types.ts\";\n\nconst providers = new Map<string, () => Provider>();\n\n/** Register a provider factory */\nexport function registerProvider(name: string, factory: () => Provider): void {\n providers.set(name, factory);\n}\n\n/** Get a provider instance by name */\nexport function getProvider(name: string): Provider {\n const factory = providers.get(name);\n if (!factory) {\n throw new ProviderNotFoundError(name);\n }\n return factory();\n}\n\n/** List all registered provider names */\nexport function listProviders(): string[] {\n return Array.from(providers.keys());\n}\n","import { NoCredentialError, TokenExpiredError, TokenRefreshError } from \"@/errors.ts\";\nimport { getProvider } from \"@/providers/registry.ts\";\nimport type {\n AuthHeaders,\n CredentialStatus,\n LoginOptions,\n StoredCredential,\n TokenStore,\n} from \"@/types.ts\";\n\n/** Buffer time before expiry to trigger refresh (5 minutes) */\nconst REFRESH_BUFFER_MS = 5 * 60 * 1000;\n\n/** Central token lifecycle manager */\nexport class TokenManager {\n private readonly store: TokenStore;\n /** Per-provider+account mutex to prevent concurrent refreshes */\n private readonly refreshLocks = new Map<string, Promise<void>>();\n\n constructor(store: TokenStore) {\n this.store = store;\n }\n\n /** Run the interactive login flow for a provider, store the tokens */\n async login(providerName: string, options?: LoginOptions): Promise<StoredCredential> {\n const provider = getProvider(providerName);\n const tokenSet = await provider.login(options);\n\n const accountId = provider.getAccountId(tokenSet);\n const accountLabel = provider.getAccountLabel?.(tokenSet);\n const now = Date.now();\n\n const credential: StoredCredential = {\n tokenSet,\n metadata: {\n provider: providerName,\n accountId,\n accountLabel,\n createdAt: now,\n lastRefreshedAt: now,\n },\n };\n\n await this.store.set(providerName, accountId, credential);\n return credential;\n }\n\n /** Get a valid access token, refreshing if needed */\n async getToken(providerName: string, accountId?: string): Promise<string> {\n const credential = await this.resolveCredential(providerName, accountId);\n const { tokenSet } = credential;\n\n if (this.isTokenValid(tokenSet.expiresAt)) {\n return tokenSet.accessToken;\n }\n\n // Token expired or near-expiry — try to refresh\n if (!tokenSet.refreshToken) {\n throw new TokenExpiredError(providerName);\n }\n\n await this.refreshWithLock(providerName, credential);\n\n // Re-read from store after refresh\n const refreshed = await this.store.get(providerName, credential.metadata.accountId);\n if (!refreshed) {\n throw new TokenExpiredError(providerName);\n }\n return refreshed.tokenSet.accessToken;\n }\n\n /** Get authenticated headers for API calls */\n async getAuthHeaders(providerName: string, accountId?: string): Promise<AuthHeaders> {\n const token = await this.getToken(providerName, accountId);\n const provider = getProvider(providerName);\n return provider.getAuthHeaders(token);\n }\n\n /** Remove stored tokens for a provider */\n async logout(providerName: string, accountId?: string): Promise<void> {\n if (accountId) {\n await this.store.delete(providerName, accountId);\n } else {\n const credentials = await this.store.list(providerName);\n for (const cred of credentials) {\n await this.store.delete(providerName, cred.metadata.accountId);\n }\n }\n }\n\n /** Get status of all stored credentials */\n async status(): Promise<CredentialStatus[]> {\n const credentials = await this.store.list();\n return credentials.map((cred) => {\n let displayName = cred.metadata.provider;\n try {\n const provider = getProvider(cred.metadata.provider);\n displayName = provider.config.displayName;\n } catch {\n // Unknown provider, use raw name\n }\n\n return {\n provider: cred.metadata.provider,\n displayName,\n accountId: cred.metadata.accountId,\n accountLabel: cred.metadata.accountLabel,\n isExpired: !this.isTokenValid(cred.tokenSet.expiresAt),\n expiresAt: cred.tokenSet.expiresAt,\n hasRefreshToken: cred.tokenSet.refreshToken !== null,\n };\n });\n }\n\n private isTokenValid(expiresAt: number): boolean {\n return Date.now() + REFRESH_BUFFER_MS < expiresAt;\n }\n\n private async resolveCredential(\n providerName: string,\n accountId?: string,\n ): Promise<StoredCredential> {\n if (accountId) {\n const credential = await this.store.get(providerName, accountId);\n if (!credential) {\n throw new NoCredentialError(providerName, accountId);\n }\n return credential;\n }\n\n // No account specified — find the most recently refreshed one\n const credentials = await this.store.list(providerName);\n if (credentials.length === 0) {\n throw new NoCredentialError(providerName);\n }\n\n return credentials.sort((a, b) => b.metadata.lastRefreshedAt - a.metadata.lastRefreshedAt)[0]!;\n }\n\n private async refreshWithLock(providerName: string, credential: StoredCredential): Promise<void> {\n const lockKey = `${providerName}::${credential.metadata.accountId}`;\n\n // If a refresh is already in progress for this account, wait for it\n const existingLock = this.refreshLocks.get(lockKey);\n if (existingLock) {\n await existingLock;\n return;\n }\n\n const refreshPromise = this.doRefresh(providerName, credential);\n this.refreshLocks.set(lockKey, refreshPromise);\n\n try {\n await refreshPromise;\n } finally {\n this.refreshLocks.delete(lockKey);\n }\n }\n\n private async doRefresh(providerName: string, credential: StoredCredential): Promise<void> {\n const provider = getProvider(providerName);\n const { refreshToken } = credential.tokenSet;\n\n if (!refreshToken) {\n throw new TokenExpiredError(providerName);\n }\n\n let newTokenSet;\n try {\n newTokenSet = await provider.refresh(refreshToken);\n } catch (err) {\n throw new TokenRefreshError(providerName, err);\n }\n\n // Preserve the refresh token if the new response doesn't include one\n if (!newTokenSet.refreshToken && refreshToken) {\n newTokenSet.refreshToken = refreshToken;\n }\n\n const updatedCredential: StoredCredential = {\n tokenSet: newTokenSet,\n metadata: {\n ...credential.metadata,\n lastRefreshedAt: Date.now(),\n },\n };\n\n await this.store.set(providerName, credential.metadata.accountId, updatedCredential);\n }\n}\n","import { createCipheriv, createDecipheriv, pbkdf2Sync, randomBytes } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { hostname, userInfo } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { StoredCredential, TokenStore } from \"@/types.ts\";\n\nconst ALGORITHM = \"aes-256-gcm\";\nconst KEY_LENGTH = 32;\nconst IV_LENGTH = 12;\nconst SALT_LENGTH = 16;\nconst PBKDF2_ITERATIONS = 100_000;\nconst AUTH_TAG_LENGTH = 16;\n\ninterface EncryptedEntry {\n data: string; // base64 encoded ciphertext + auth tag\n iv: string; // base64\n salt: string; // base64\n}\n\ninterface FileStoreData {\n version: 1;\n credentials: Record<string, EncryptedEntry>;\n}\n\n/** Token store backed by an encrypted JSON file */\nexport class FileStore implements TokenStore {\n private readonly filePath: string;\n\n constructor(filePath?: string) {\n this.filePath =\n filePath ??\n join(process.env.OPEN_SUB_AUTH_HOME ?? join(homedir(), \".open-sub-auth\"), \"credentials.json\");\n }\n\n async get(provider: string, accountId: string): Promise<StoredCredential | null> {\n const data = this.readFile();\n const key = `${provider}::${accountId}`;\n const entry = data.credentials[key];\n if (!entry) return null;\n\n try {\n const decrypted = this.decrypt(entry);\n return JSON.parse(decrypted) as StoredCredential;\n } catch {\n return null;\n }\n }\n\n async set(provider: string, accountId: string, credential: StoredCredential): Promise<void> {\n const data = this.readFile();\n const key = `${provider}::${accountId}`;\n data.credentials[key] = this.encrypt(JSON.stringify(credential));\n this.writeFile(data);\n }\n\n async delete(provider: string, accountId: string): Promise<void> {\n const data = this.readFile();\n const key = `${provider}::${accountId}`;\n delete data.credentials[key];\n this.writeFile(data);\n }\n\n async list(provider?: string): Promise<StoredCredential[]> {\n const data = this.readFile();\n const results: StoredCredential[] = [];\n\n for (const [key, entry] of Object.entries(data.credentials)) {\n const [keyProvider] = key.split(\"::\");\n if (provider && keyProvider !== provider) continue;\n\n try {\n const decrypted = this.decrypt(entry);\n results.push(JSON.parse(decrypted) as StoredCredential);\n } catch {\n // Skip corrupted entries\n }\n }\n\n return results;\n }\n\n private deriveKey(salt: Buffer): Buffer {\n const machineId = `${hostname()}:${userInfo().username}`;\n return pbkdf2Sync(machineId, salt, PBKDF2_ITERATIONS, KEY_LENGTH, \"sha512\");\n }\n\n private encrypt(plaintext: string): EncryptedEntry {\n const salt = randomBytes(SALT_LENGTH);\n const key = this.deriveKey(salt);\n const iv = randomBytes(IV_LENGTH);\n\n const cipher = createCipheriv(ALGORITHM, key, iv, {\n authTagLength: AUTH_TAG_LENGTH,\n });\n const encrypted = Buffer.concat([\n cipher.update(plaintext, \"utf8\"),\n cipher.final(),\n cipher.getAuthTag(),\n ]);\n\n return {\n data: encrypted.toString(\"base64\"),\n iv: iv.toString(\"base64\"),\n salt: salt.toString(\"base64\"),\n };\n }\n\n private decrypt(entry: EncryptedEntry): string {\n const salt = Buffer.from(entry.salt, \"base64\");\n const key = this.deriveKey(salt);\n const iv = Buffer.from(entry.iv, \"base64\");\n const raw = Buffer.from(entry.data, \"base64\");\n\n const authTag = raw.subarray(raw.length - AUTH_TAG_LENGTH);\n const ciphertext = raw.subarray(0, raw.length - AUTH_TAG_LENGTH);\n\n const decipher = createDecipheriv(ALGORITHM, key, iv, {\n authTagLength: AUTH_TAG_LENGTH,\n });\n decipher.setAuthTag(authTag);\n\n return decipher.update(ciphertext) + decipher.final(\"utf8\");\n }\n\n private readFile(): FileStoreData {\n try {\n const content = readFileSync(this.filePath, \"utf8\");\n return JSON.parse(content) as FileStoreData;\n } catch {\n return { version: 1, credentials: {} };\n }\n }\n\n private writeFile(data: FileStoreData): void {\n mkdirSync(dirname(this.filePath), { recursive: true, mode: 0o700 });\n writeFileSync(this.filePath, JSON.stringify(data, null, 2), { mode: 0o600 });\n }\n}\n\nfunction homedir(): string {\n return process.env.HOME ?? process.env.USERPROFILE ?? \"/tmp\";\n}\n","import type { StoredCredential, TokenStore } from \"@/types.ts\";\n\nconst SERVICE_NAME = \"open-sub-auth\";\nconst INDEX_ACCOUNT = \"__index__\";\n\n/** Token store backed by the OS keychain via cross-keychain */\nexport class KeychainStore implements TokenStore {\n private keychain: typeof import(\"cross-keychain\") | null = null;\n\n private async getKeychain() {\n if (!this.keychain) {\n this.keychain = await import(\"cross-keychain\");\n }\n return this.keychain;\n }\n\n private makeKey(provider: string, accountId: string): string {\n return `${provider}__${accountId}`;\n }\n\n async get(provider: string, accountId: string): Promise<StoredCredential | null> {\n const kc = await this.getKeychain();\n try {\n const value = await kc.getPassword(SERVICE_NAME, this.makeKey(provider, accountId));\n if (!value) return null;\n return JSON.parse(value) as StoredCredential;\n } catch {\n return null;\n }\n }\n\n async set(provider: string, accountId: string, credential: StoredCredential): Promise<void> {\n const kc = await this.getKeychain();\n const key = this.makeKey(provider, accountId);\n const value = JSON.stringify(credential);\n\n try {\n await kc.deletePassword(SERVICE_NAME, key);\n } catch {\n // Ignore if it doesn't exist\n }\n await kc.setPassword(SERVICE_NAME, key, value);\n await this.addToIndex(key);\n }\n\n async delete(provider: string, accountId: string): Promise<void> {\n const kc = await this.getKeychain();\n const key = this.makeKey(provider, accountId);\n try {\n await kc.deletePassword(SERVICE_NAME, key);\n } catch {\n // Ignore if it doesn't exist\n }\n await this.removeFromIndex(key);\n }\n\n async list(provider?: string): Promise<StoredCredential[]> {\n const index = await this.getIndex();\n const results: StoredCredential[] = [];\n\n for (const key of index) {\n const [keyProvider, keyAccountId] = key.split(\"__\");\n if (provider && keyProvider !== provider) continue;\n if (!keyProvider || !keyAccountId) continue;\n\n const credential = await this.get(keyProvider, keyAccountId);\n if (credential) {\n results.push(credential);\n }\n }\n\n return results;\n }\n\n private async getIndex(): Promise<string[]> {\n const kc = await this.getKeychain();\n try {\n const value = await kc.getPassword(SERVICE_NAME, INDEX_ACCOUNT);\n if (!value) return [];\n return JSON.parse(value) as string[];\n } catch {\n return [];\n }\n }\n\n private async setIndex(index: string[]): Promise<void> {\n const kc = await this.getKeychain();\n try {\n await kc.deletePassword(SERVICE_NAME, INDEX_ACCOUNT);\n } catch {\n // Ignore\n }\n await kc.setPassword(SERVICE_NAME, INDEX_ACCOUNT, JSON.stringify(index));\n }\n\n private async addToIndex(key: string): Promise<void> {\n const index = await this.getIndex();\n if (!index.includes(key)) {\n index.push(key);\n await this.setIndex(index);\n }\n }\n\n private async removeFromIndex(key: string): Promise<void> {\n const index = await this.getIndex();\n const filtered = index.filter((k) => k !== key);\n await this.setIndex(filtered);\n }\n}\n","import type { TokenStore } from \"@/types.ts\";\nimport { FileStore } from \"@/storage/file-store.ts\";\nimport { KeychainStore } from \"@/storage/keychain-store.ts\";\n\n/**\n * Create a token store, preferring the OS keychain with encrypted file fallback.\n * @param preferKeychain - If true (default), try keychain first\n */\nexport async function createTokenStore(preferKeychain = true): Promise<TokenStore> {\n if (preferKeychain) {\n try {\n const store = new KeychainStore();\n // Probe if keychain is accessible\n await store.get(\"__probe__\", \"__probe__\");\n return store;\n } catch {\n // Keychain not available, fall through to file store\n }\n }\n return new FileStore();\n}\n\nexport { FileStore, KeychainStore };\n","import { execFile } from \"node:child_process\";\nimport { platform } from \"node:os\";\n\n/** Open a URL in the user's default browser. Does not throw on failure. */\nexport function openBrowser(url: string): void {\n const os = platform();\n try {\n if (os === \"darwin\") {\n execFile(\"open\", [url]);\n } else if (os === \"win32\") {\n execFile(\"cmd\", [\"/c\", \"start\", \"\", url]);\n } else {\n execFile(\"xdg-open\", [url]);\n }\n } catch {\n // Silently ignore — caller should provide fallback instructions\n }\n}\n","import { createServer, type Server } from \"node:http\";\nimport { OAuthCallbackError, OAuthTimeoutError } from \"@/errors.ts\";\nimport type { AuthorizationResult } from \"@/types.ts\";\n\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>Authorization Successful</title>\n<style>body{font-family:system-ui,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#f8f9fa}\n.card{text-align:center;padding:2rem;border-radius:12px;background:white;box-shadow:0 2px 8px rgba(0,0,0,0.1)}\nh1{color:#22c55e;font-size:1.5rem}p{color:#666}</style></head>\n<body><div class=\"card\"><h1>Authorization Successful</h1><p>You can close this window and return to the terminal.</p></div></body></html>`;\n\nconst ERROR_HTML = `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>Authorization Failed</title>\n<style>body{font-family:system-ui,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#f8f9fa}\n.card{text-align:center;padding:2rem;border-radius:12px;background:white;box-shadow:0 2px 8px rgba(0,0,0,0.1)}\nh1{color:#ef4444;font-size:1.5rem}p{color:#666}</style></head>\n<body><div class=\"card\"><h1>Authorization Failed</h1><p>Something went wrong. Please try again.</p></div></body></html>`;\n\nexport interface CallbackServerOptions {\n /** Port to listen on (0 = random available port) */\n port?: number;\n /** Expected state parameter for CSRF validation */\n expectedState: string;\n /** Timeout in milliseconds (default: 120000) */\n timeout?: number;\n /** Path to listen on (default: \"/callback\") */\n path?: string;\n}\n\n/** Start a local HTTP server to receive the OAuth callback redirect */\nexport function startCallbackServer(\n options: CallbackServerOptions,\n): Promise<{ result: Promise<AuthorizationResult>; port: number; close: () => void }> {\n const { expectedState, timeout = 120_000, path = \"/callback\" } = options;\n const port = options.port ?? 0;\n\n return new Promise((resolveSetup, _rejectSetup) => {\n let server: Server;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const resultPromise = new Promise<AuthorizationResult>((resolveResult, rejectResult) => {\n server = createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://localhost`);\n\n if (url.pathname !== path) {\n res.writeHead(404);\n res.end(\"Not found\");\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n\n if (error) {\n const errorDesc = url.searchParams.get(\"error_description\") ?? error;\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(ERROR_HTML);\n cleanup();\n rejectResult(new OAuthCallbackError(`OAuth error: ${errorDesc}`));\n return;\n }\n\n if (!code) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(ERROR_HTML);\n cleanup();\n rejectResult(new OAuthCallbackError(\"No authorization code in callback\"));\n return;\n }\n\n if (state && state !== expectedState) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(ERROR_HTML);\n cleanup();\n rejectResult(\n new OAuthCallbackError(\"State mismatch — possible CSRF attack. Please try again.\"),\n );\n return;\n }\n\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(SUCCESS_HTML);\n cleanup();\n resolveResult({ code, state: state ?? expectedState });\n });\n\n const cleanup = () => {\n if (timeoutId) clearTimeout(timeoutId);\n server.close();\n };\n\n timeoutId = setTimeout(() => {\n cleanup();\n rejectResult(new OAuthTimeoutError(timeout));\n }, timeout);\n\n server.on(\"error\", (err) => {\n cleanup();\n rejectResult(new OAuthCallbackError(`Callback server error: ${err.message}`));\n });\n\n server.listen(port, \"127.0.0.1\", () => {\n const addr = server.address();\n const actualPort = typeof addr === \"object\" && addr ? addr.port : port;\n resolveSetup({\n result: resultPromise,\n port: actualPort,\n close: cleanup,\n });\n });\n });\n });\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport type { PKCEParams } from \"@/types.ts\";\n\n/** Generate a cryptographically random PKCE code verifier (43-128 chars, base64url) */\nexport function generateCodeVerifier(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\n/** Generate a PKCE code challenge from a verifier using S256 method */\nexport function generateCodeChallenge(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\n/** Generate both PKCE code verifier and challenge */\nexport function generatePKCE(): PKCEParams {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n return { codeVerifier, codeChallenge };\n}\n\n/** Generate a random state parameter for CSRF protection */\nexport function generateState(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n","import { createInterface } from \"node:readline\";\nimport { OAuthCallbackError, StateMismatchError } from \"@/errors.ts\";\nimport type { AuthorizationResult } from \"@/types.ts\";\n\n/**\n * Parse a \"code#state\" string (Anthropic's manual paste format).\n * Also supports a plain authorization code (state validated separately).\n */\nexport function parseCodeAndState(input: string, expectedState: string): AuthorizationResult {\n const trimmed = input.trim();\n if (!trimmed) {\n throw new OAuthCallbackError(\"Empty authorization code input\");\n }\n\n const hashIndex = trimmed.indexOf(\"#\");\n if (hashIndex !== -1) {\n const code = trimmed.slice(0, hashIndex);\n const state = trimmed.slice(hashIndex + 1);\n if (!code) {\n throw new OAuthCallbackError(\"Empty authorization code in code#state input\");\n }\n if (state !== expectedState) {\n throw new StateMismatchError();\n }\n return { code, state };\n }\n\n // No \"#\" found — assume just the code was provided\n return { code: trimmed, state: expectedState };\n}\n\n/** Prompt the user to paste the authorization code from their browser */\nexport function promptForCode(expectedState: string): Promise<AuthorizationResult> {\n return new Promise((resolve, reject) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr, // Use stderr so stdout stays clean for piping\n });\n\n rl.question(\"Paste the authorization code (or code#state) from your browser: \", (answer) => {\n rl.close();\n try {\n resolve(parseCodeAndState(answer, expectedState));\n } catch (err) {\n reject(err);\n }\n });\n });\n}\n","import { OAuthCallbackError } from \"@/errors.ts\";\nimport type { AuthorizationResult, LoginOptions, ProviderConfig, TokenSet } from \"@/types.ts\";\nimport { openBrowser } from \"@/core/browser.ts\";\nimport { startCallbackServer } from \"@/core/callback-server.ts\";\nimport { generatePKCE, generateState } from \"@/core/crypto.ts\";\nimport { promptForCode } from \"@/core/manual-code-input.ts\";\n\n/** Build the full OAuth authorization URL with PKCE and state params */\nexport function buildAuthorizationUrl(\n config: ProviderConfig,\n codeChallenge: string,\n state: string,\n redirectUri: string,\n): string {\n if (!config.authorizationEndpoint) {\n throw new OAuthCallbackError(`Provider \"${config.name}\" has no authorization endpoint`);\n }\n\n const url = new URL(config.authorizationEndpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", config.clientId);\n url.searchParams.set(\"redirect_uri\", redirectUri);\n url.searchParams.set(\"code_challenge\", codeChallenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"state\", state);\n\n if (config.scopes?.length) {\n url.searchParams.set(\"scope\", config.scopes.join(\" \"));\n }\n\n return url.toString();\n}\n\n/** Exchange an authorization code for tokens */\nexport async function exchangeCode(\n config: ProviderConfig,\n code: string,\n codeVerifier: string,\n redirectUri: string,\n state?: string,\n): Promise<TokenSet> {\n const useJson = config.tokenBodyFormat === \"json\";\n\n const params: Record<string, string> = {\n grant_type: \"authorization_code\",\n code,\n code_verifier: codeVerifier,\n client_id: config.clientId,\n redirect_uri: redirectUri,\n };\n if (state !== undefined) {\n params.state = state;\n }\n\n const response = await fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": useJson ? \"application/json\" : \"application/x-www-form-urlencoded\" },\n body: useJson ? JSON.stringify(params) : new URLSearchParams(params).toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new OAuthCallbackError(`Token exchange failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n return parseTokenResponse(data);\n}\n\n/** Refresh an access token using a refresh token */\nexport async function refreshAccessToken(\n config: ProviderConfig,\n refreshToken: string,\n): Promise<TokenSet> {\n const useJson = config.tokenBodyFormat === \"json\";\n\n const params: Record<string, string> = {\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n client_id: config.clientId,\n };\n\n const response = await fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": useJson ? \"application/json\" : \"application/x-www-form-urlencoded\" },\n body: useJson ? JSON.stringify(params) : new URLSearchParams(params).toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new OAuthCallbackError(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n return parseTokenResponse(data);\n}\n\nexport interface PKCEFlowOptions {\n config: ProviderConfig;\n loginOptions?: LoginOptions;\n /**\n * Manual mode redirect URI (used when manual=true).\n * If not provided, manual mode is not available.\n */\n manualRedirectUri?: string;\n}\n\n/** Execute the full PKCE flow: generate params, open browser, wait for callback, exchange code */\nexport async function executePKCEFlow(options: PKCEFlowOptions): Promise<TokenSet> {\n const { config, loginOptions } = options;\n const { codeVerifier, codeChallenge } = generatePKCE();\n // Some providers (e.g. Claude) require state === verifier for their token exchange\n const state = config.stateIsVerifier ? codeVerifier : generateState();\n const manual = loginOptions?.manual ?? false;\n\n let authResult: AuthorizationResult;\n let redirectUri: string;\n\n if (manual && options.manualRedirectUri) {\n // Manual mode: redirect to provider's callback page, user copies code\n redirectUri = options.manualRedirectUri;\n const authUrl = buildAuthorizationUrl(config, codeChallenge, state, redirectUri);\n\n if (loginOptions?.onOpenBrowser) {\n loginOptions.onOpenBrowser(authUrl);\n } else {\n openBrowser(authUrl);\n }\n\n process.stderr.write(\n `\\nOpen this URL in your browser if it didn't open automatically:\\n${authUrl}\\n\\n`,\n );\n authResult = await promptForCode(state);\n } else {\n // Automatic mode: local callback server\n const port = loginOptions?.port ?? 0;\n const timeout = loginOptions?.timeout ?? 120_000;\n\n const server = await startCallbackServer({\n port,\n expectedState: state,\n timeout,\n });\n\n redirectUri = `http://127.0.0.1:${server.port}/callback`;\n const authUrl = buildAuthorizationUrl(config, codeChallenge, state, redirectUri);\n\n if (loginOptions?.onOpenBrowser) {\n loginOptions.onOpenBrowser(authUrl);\n } else {\n openBrowser(authUrl);\n }\n\n process.stderr.write(\n `\\nOpen this URL in your browser if it didn't open automatically:\\n${authUrl}\\n\\nWaiting for authorization...\\n`,\n );\n\n try {\n authResult = await server.result;\n } catch (err) {\n server.close();\n throw err;\n }\n }\n\n // Pass state to exchange when provider uses JSON body format (e.g. Claude)\n const exchangeState = config.tokenBodyFormat === \"json\" ? authResult.state : undefined;\n return exchangeCode(config, authResult.code, codeVerifier, redirectUri, exchangeState);\n}\n\nfunction parseTokenResponse(data: Record<string, unknown>): TokenSet {\n const accessToken = data.access_token as string | undefined;\n if (!accessToken) {\n throw new OAuthCallbackError(\"No access_token in token response\");\n }\n\n const expiresIn = (data.expires_in as number | undefined) ?? 3600;\n const expiresAt = Date.now() + expiresIn * 1000;\n\n return {\n accessToken,\n refreshToken: (data.refresh_token as string | undefined) ?? null,\n expiresAt,\n idToken: data.id_token as string | undefined,\n tokenType: ((data.token_type as string | undefined) ?? \"bearer\").toLowerCase() as\n | \"bearer\"\n | \"api-key\",\n scopes: data.scope ? (data.scope as string).split(\" \") : undefined,\n raw: data,\n };\n}\n","import { createHash } from \"node:crypto\";\nimport { executePKCEFlow, refreshAccessToken } from \"@/core/oauth-pkce.ts\";\nimport type { AuthHeaders, LoginOptions, Provider, ProviderConfig, TokenSet } from \"@/types.ts\";\nimport { registerProvider } from \"@/providers/registry.ts\";\n\nconst CLAUDE_CONFIG: ProviderConfig = {\n name: \"claude\",\n displayName: \"Claude Pro/Max\",\n authorizationEndpoint: \"https://claude.ai/oauth/authorize\",\n tokenEndpoint: \"https://console.anthropic.com/v1/oauth/token\",\n clientId: \"9d1c250a-e61b-44d9-88ed-5944d1962f5e\",\n scopes: [\"org:create_api_key\", \"user:profile\", \"user:inference\"],\n grantType: \"authorization_code\",\n // Claude only supports console.anthropic.com redirect URI (no localhost),\n // and its token endpoint requires JSON body with state field\n tokenBodyFormat: \"json\",\n stateIsVerifier: true,\n};\n\n/** Redirect URI used in manual/headless mode (user copies code from browser) */\nconst MANUAL_REDIRECT_URI = \"https://console.anthropic.com/oauth/code/callback\";\n\nexport class ClaudeProvider implements Provider {\n readonly config = CLAUDE_CONFIG;\n\n async login(options?: LoginOptions): Promise<TokenSet> {\n // Claude only allows console.anthropic.com/oauth/code/callback as redirect URI.\n // Local callback server is not supported, so always use manual (code-paste) mode.\n return executePKCEFlow({\n config: this.config,\n loginOptions: { ...options, manual: true },\n manualRedirectUri: MANUAL_REDIRECT_URI,\n });\n }\n\n async refresh(refreshToken: string): Promise<TokenSet> {\n return refreshAccessToken(this.config, refreshToken);\n }\n\n getAuthHeaders(accessToken: string): AuthHeaders {\n return {\n authorization: `Bearer ${accessToken}`,\n \"anthropic-version\": \"2023-06-01\",\n \"anthropic-beta\": \"oauth-2025-04-20\",\n \"content-type\": \"application/json\",\n };\n }\n\n getAccountId(tokenSet: TokenSet): string {\n // Claude doesn't provide a profile endpoint, so derive ID from refresh token hash\n const source = tokenSet.refreshToken ?? tokenSet.accessToken;\n return createHash(\"sha256\").update(source).digest(\"hex\").slice(0, 16);\n }\n\n getAccountLabel(_tokenSet: TokenSet): string | undefined {\n // Claude tokens don't carry user identity info\n return undefined;\n }\n}\n\n// Auto-register\nregisterProvider(\"claude\", () => new ClaudeProvider());\n","/** Decoded JWT claims (no signature verification) */\nexport interface JWTClaims {\n sub?: string;\n email?: string;\n name?: string;\n iss?: string;\n aud?: string | string[];\n exp?: number;\n iat?: number;\n [key: string]: unknown;\n}\n\n/**\n * Decode a JWT token's payload without verifying the signature.\n * Used to extract user info from id_tokens (e.g., OpenAI).\n */\nexport function decodeJWT(token: string): JWTClaims {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format: expected 3 parts\");\n }\n\n const payload = parts[1];\n if (!payload) {\n throw new Error(\"Invalid JWT: empty payload\");\n }\n\n // Add padding if needed for base64url decoding\n const padded = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const decoded = Buffer.from(padded, \"base64\").toString(\"utf8\");\n\n return JSON.parse(decoded) as JWTClaims;\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { executePKCEFlow, refreshAccessToken } from \"@/core/oauth-pkce.ts\";\nimport { decodeJWT } from \"@/token/jwt.ts\";\nimport type { AuthHeaders, LoginOptions, Provider, ProviderConfig, TokenSet } from \"@/types.ts\";\nimport { registerProvider } from \"@/providers/registry.ts\";\n\nconst OPENAI_CODEX_CONFIG: ProviderConfig = {\n name: \"openai-codex\",\n displayName: \"OpenAI ChatGPT Plus/Pro\",\n authorizationEndpoint: \"https://auth.openai.com/oauth/authorize\",\n tokenEndpoint: \"https://auth.openai.com/oauth/token\",\n clientId: \"app_EMoamEEZ73f0CkXaXp7hrann\",\n redirectUri: \"http://localhost:1455/auth/callback\",\n scopes: [\"openid\", \"profile\", \"email\", \"offline_access\"],\n grantType: \"authorization_code\",\n};\n\nexport class OpenAICodexProvider implements Provider {\n readonly config = OPENAI_CODEX_CONFIG;\n\n async login(options?: LoginOptions): Promise<TokenSet> {\n return executePKCEFlow({\n config: this.config,\n loginOptions: {\n ...options,\n port: options?.port ?? 1455,\n },\n });\n }\n\n async refresh(refreshToken: string): Promise<TokenSet> {\n return refreshAccessToken(this.config, refreshToken);\n }\n\n getAuthHeaders(accessToken: string): AuthHeaders {\n return {\n authorization: `Bearer ${accessToken}`,\n \"content-type\": \"application/json\",\n };\n }\n\n getAccountId(tokenSet: TokenSet): string {\n if (tokenSet.idToken) {\n try {\n const claims = decodeJWT(tokenSet.idToken);\n if (claims.sub) return claims.sub;\n } catch {\n // Fall through\n }\n }\n return createHash(\"sha256\").update(tokenSet.accessToken).digest(\"hex\").slice(0, 16);\n }\n\n getAccountLabel(tokenSet: TokenSet): string | undefined {\n if (tokenSet.idToken) {\n try {\n const claims = decodeJWT(tokenSet.idToken);\n if (claims.email) return claims.email as string;\n } catch {\n // Fall through\n }\n }\n return undefined;\n }\n}\n\n/**\n * Import tokens from an existing Codex CLI installation.\n * Reads from ~/.codex/auth.json or $CODEX_HOME/auth.json.\n */\nexport function importFromCodexCli(): TokenSet | null {\n const paths = [\n process.env.CODEX_HOME ? join(process.env.CODEX_HOME, \"auth.json\") : null,\n join(homedir(), \".codex\", \"auth.json\"),\n ].filter(Boolean) as string[];\n\n for (const filePath of paths) {\n if (!existsSync(filePath)) continue;\n\n try {\n const content = readFileSync(filePath, \"utf8\");\n const data = JSON.parse(content) as {\n tokens?: {\n access_token?: string;\n refresh_token?: string;\n id_token?: string;\n };\n };\n\n if (!data.tokens?.access_token) continue;\n\n return {\n accessToken: data.tokens.access_token,\n refreshToken: data.tokens.refresh_token ?? null,\n expiresAt: Date.now() + 3600_000, // Assume 1 hour, will refresh as needed\n idToken: data.tokens.id_token,\n tokenType: \"bearer\",\n };\n } catch {\n continue;\n }\n }\n\n return null;\n}\n\n// Auto-register\nregisterProvider(\"openai-codex\", () => new OpenAICodexProvider());\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["homedir","EnvHttpProxyAgent"],"sources":["../src/errors.ts","../src/providers/registry.ts","../src/token/manager.ts","../src/storage/file-store.ts","../src/storage/keychain-store.ts","../src/storage/store.ts","../src/core/browser.ts","../src/core/callback-server.ts","../src/core/crypto.ts","../src/core/manual-code-input.ts","../src/core/oauth-pkce.ts","../src/providers/claude.ts","../src/token/jwt.ts","../src/providers/openai-codex.ts","../src/core/oauth-device.ts","../src/providers/github-copilot.ts","../src/core/proxy.ts"],"sourcesContent":["export class OpenSubAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"OpenSubAuthError\";\n }\n}\n\nexport class AuthenticationError extends OpenSubAuthError {\n constructor(\n message: string,\n public readonly provider?: string,\n ) {\n super(message);\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class TokenExpiredError extends OpenSubAuthError {\n constructor(public readonly provider: string) {\n super(\n `Access token for \"${provider}\" has expired and no refresh token is available. Please login again.`,\n );\n this.name = \"TokenExpiredError\";\n }\n}\n\nexport class TokenRefreshError extends OpenSubAuthError {\n constructor(\n public readonly provider: string,\n public readonly cause_: unknown,\n ) {\n // Use only the error class name, not .message — cause may contain token values\n // from upstream HTTP responses that should not be propagated to callers.\n const causeType = cause_ instanceof Error ? cause_.constructor.name : typeof cause_;\n super(`Failed to refresh token for \"${provider}\" (${causeType}). Check logs for details.`);\n this.name = \"TokenRefreshError\";\n }\n}\n\nexport class ProviderNotFoundError extends OpenSubAuthError {\n constructor(public readonly providerName: string) {\n super(\n `Provider \"${providerName}\" is not registered. Available providers can be listed with listProviders().`,\n );\n this.name = \"ProviderNotFoundError\";\n }\n}\n\nexport class NoCredentialError extends OpenSubAuthError {\n constructor(\n public readonly provider: string,\n public readonly accountId?: string,\n ) {\n const msg = accountId\n ? `No stored credential for \"${provider}\" account \"${accountId}\". Please login first.`\n : `No stored credential for \"${provider}\". Please login first.`;\n super(msg);\n this.name = \"NoCredentialError\";\n }\n}\n\nexport class OAuthCallbackError extends OpenSubAuthError {\n constructor(message: string) {\n super(message);\n this.name = \"OAuthCallbackError\";\n }\n}\n\nexport class OAuthTimeoutError extends OpenSubAuthError {\n constructor(public readonly timeoutMs: number) {\n super(`OAuth login timed out after ${Math.round(timeoutMs / 1000)} seconds. Please try again.`);\n this.name = \"OAuthTimeoutError\";\n }\n}\n\nexport class StateMismatchError extends OpenSubAuthError {\n constructor() {\n super(\"OAuth state parameter mismatch — possible CSRF attack. Please try again.\");\n this.name = \"StateMismatchError\";\n }\n}\n","import { ProviderNotFoundError } from \"@/errors.ts\";\nimport type { Provider } from \"@/types.ts\";\n\nconst providers = new Map<string, () => Provider>();\n\n/** Register a provider factory */\nexport function registerProvider(name: string, factory: () => Provider): void {\n providers.set(name, factory);\n}\n\n/** Get a provider instance by name */\nexport function getProvider(name: string): Provider {\n const factory = providers.get(name);\n if (!factory) {\n throw new ProviderNotFoundError(name);\n }\n return factory();\n}\n\n/** List all registered provider names */\nexport function listProviders(): string[] {\n return Array.from(providers.keys());\n}\n","import { NoCredentialError, TokenExpiredError, TokenRefreshError } from \"@/errors.ts\";\nimport { getProvider } from \"@/providers/registry.ts\";\nimport type {\n AuthHeaders,\n CredentialStatus,\n LoginOptions,\n StoredCredential,\n TokenStore,\n} from \"@/types.ts\";\n\n/** Buffer time before expiry to trigger refresh (5 minutes) */\nconst REFRESH_BUFFER_MS = 5 * 60 * 1000;\n\n/** Central token lifecycle manager */\nexport class TokenManager {\n private readonly store: TokenStore;\n /** Per-provider+account mutex to prevent concurrent refreshes */\n private readonly refreshLocks = new Map<string, Promise<void>>();\n\n constructor(store: TokenStore) {\n this.store = store;\n }\n\n /** Run the interactive login flow for a provider, store the tokens */\n async login(providerName: string, options?: LoginOptions): Promise<StoredCredential> {\n const provider = getProvider(providerName);\n const tokenSet = await provider.login(options);\n\n const accountId = provider.getAccountId(tokenSet);\n const accountLabel = provider.getAccountLabel?.(tokenSet);\n const now = Date.now();\n\n const credential: StoredCredential = {\n tokenSet,\n metadata: {\n provider: providerName,\n accountId,\n accountLabel,\n createdAt: now,\n lastRefreshedAt: now,\n },\n };\n\n await this.store.set(providerName, accountId, credential);\n return credential;\n }\n\n /** Get a valid access token, refreshing if needed */\n async getToken(providerName: string, accountId?: string): Promise<string> {\n const credential = await this.resolveCredential(providerName, accountId);\n const { tokenSet } = credential;\n\n if (this.isTokenValid(tokenSet.expiresAt)) {\n return tokenSet.accessToken;\n }\n\n // Token expired or near-expiry — try to refresh\n if (!tokenSet.refreshToken) {\n throw new TokenExpiredError(providerName);\n }\n\n await this.refreshWithLock(providerName, credential);\n\n // Re-read from store after refresh\n const refreshed = await this.store.get(providerName, credential.metadata.accountId);\n if (!refreshed) {\n throw new TokenExpiredError(providerName);\n }\n return refreshed.tokenSet.accessToken;\n }\n\n /** Get authenticated headers for API calls */\n async getAuthHeaders(providerName: string, accountId?: string): Promise<AuthHeaders> {\n const token = await this.getToken(providerName, accountId);\n const provider = getProvider(providerName);\n return provider.getAuthHeaders(token);\n }\n\n /** Remove stored tokens for a provider */\n async logout(providerName: string, accountId?: string): Promise<void> {\n if (accountId) {\n await this.store.delete(providerName, accountId);\n } else {\n const credentials = await this.store.list(providerName);\n for (const cred of credentials) {\n await this.store.delete(providerName, cred.metadata.accountId);\n }\n }\n }\n\n /** Get status of all stored credentials */\n async status(): Promise<CredentialStatus[]> {\n const credentials = await this.store.list();\n return credentials.map((cred) => {\n let displayName = cred.metadata.provider;\n try {\n const provider = getProvider(cred.metadata.provider);\n displayName = provider.config.displayName;\n } catch {\n // Unknown provider, use raw name\n }\n\n return {\n provider: cred.metadata.provider,\n displayName,\n accountId: cred.metadata.accountId,\n accountLabel: cred.metadata.accountLabel,\n isExpired: !this.isTokenValid(cred.tokenSet.expiresAt),\n expiresAt: cred.tokenSet.expiresAt,\n hasRefreshToken: cred.tokenSet.refreshToken !== null,\n };\n });\n }\n\n private isTokenValid(expiresAt: number): boolean {\n return Date.now() + REFRESH_BUFFER_MS < expiresAt;\n }\n\n private async resolveCredential(\n providerName: string,\n accountId?: string,\n ): Promise<StoredCredential> {\n if (accountId) {\n const credential = await this.store.get(providerName, accountId);\n if (!credential) {\n throw new NoCredentialError(providerName, accountId);\n }\n return credential;\n }\n\n // No account specified — find the most recently refreshed one\n const credentials = await this.store.list(providerName);\n if (credentials.length === 0) {\n throw new NoCredentialError(providerName);\n }\n\n return credentials.sort((a, b) => b.metadata.lastRefreshedAt - a.metadata.lastRefreshedAt)[0]!;\n }\n\n private async refreshWithLock(providerName: string, credential: StoredCredential): Promise<void> {\n const lockKey = `${providerName}::${credential.metadata.accountId}`;\n\n // If a refresh is already in progress for this account, wait for it\n const existingLock = this.refreshLocks.get(lockKey);\n if (existingLock) {\n await existingLock;\n return;\n }\n\n const refreshPromise = this.doRefresh(providerName, credential);\n this.refreshLocks.set(lockKey, refreshPromise);\n\n try {\n await refreshPromise;\n } finally {\n this.refreshLocks.delete(lockKey);\n }\n }\n\n private async doRefresh(providerName: string, credential: StoredCredential): Promise<void> {\n const provider = getProvider(providerName);\n const { refreshToken } = credential.tokenSet;\n\n if (!refreshToken) {\n throw new TokenExpiredError(providerName);\n }\n\n let newTokenSet;\n try {\n newTokenSet = await provider.refresh(refreshToken);\n } catch (err) {\n throw new TokenRefreshError(providerName, err);\n }\n\n // Preserve the refresh token if the new response doesn't include one\n if (!newTokenSet.refreshToken && refreshToken) {\n newTokenSet.refreshToken = refreshToken;\n }\n\n const updatedCredential: StoredCredential = {\n tokenSet: newTokenSet,\n metadata: {\n ...credential.metadata,\n lastRefreshedAt: Date.now(),\n },\n };\n\n await this.store.set(providerName, credential.metadata.accountId, updatedCredential);\n }\n}\n","import { createCipheriv, createDecipheriv, pbkdf2Sync, randomBytes } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { hostname, userInfo } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { StoredCredential, TokenStore } from \"@/types.ts\";\n\nconst ALGORITHM = \"aes-256-gcm\";\nconst KEY_LENGTH = 32;\nconst IV_LENGTH = 12;\nconst SALT_LENGTH = 16;\nconst PBKDF2_ITERATIONS = 100_000;\nconst AUTH_TAG_LENGTH = 16;\n\ninterface EncryptedEntry {\n data: string; // base64 encoded ciphertext + auth tag\n iv: string; // base64\n salt: string; // base64\n}\n\ninterface FileStoreData {\n version: 1;\n credentials: Record<string, EncryptedEntry>;\n}\n\n/** Token store backed by an encrypted JSON file */\nexport class FileStore implements TokenStore {\n private readonly filePath: string;\n\n constructor(filePath?: string) {\n this.filePath =\n filePath ??\n join(process.env.OPEN_SUB_AUTH_HOME ?? join(homedir(), \".open-sub-auth\"), \"credentials.json\");\n }\n\n async get(provider: string, accountId: string): Promise<StoredCredential | null> {\n const data = this.readFile();\n const key = `${provider}::${accountId}`;\n const entry = data.credentials[key];\n if (!entry) return null;\n\n try {\n const decrypted = this.decrypt(entry);\n return JSON.parse(decrypted) as StoredCredential;\n } catch {\n return null;\n }\n }\n\n async set(provider: string, accountId: string, credential: StoredCredential): Promise<void> {\n const data = this.readFile();\n const key = `${provider}::${accountId}`;\n data.credentials[key] = this.encrypt(JSON.stringify(credential));\n this.writeFile(data);\n }\n\n async delete(provider: string, accountId: string): Promise<void> {\n const data = this.readFile();\n const key = `${provider}::${accountId}`;\n delete data.credentials[key];\n this.writeFile(data);\n }\n\n async list(provider?: string): Promise<StoredCredential[]> {\n const data = this.readFile();\n const results: StoredCredential[] = [];\n\n for (const [key, entry] of Object.entries(data.credentials)) {\n const [keyProvider] = key.split(\"::\");\n if (provider && keyProvider !== provider) continue;\n\n try {\n const decrypted = this.decrypt(entry);\n results.push(JSON.parse(decrypted) as StoredCredential);\n } catch {\n // Skip corrupted entries\n }\n }\n\n return results;\n }\n\n private deriveKey(salt: Buffer): Buffer {\n const machineId = `${hostname()}:${userInfo().username}`;\n return pbkdf2Sync(machineId, salt, PBKDF2_ITERATIONS, KEY_LENGTH, \"sha512\");\n }\n\n private encrypt(plaintext: string): EncryptedEntry {\n const salt = randomBytes(SALT_LENGTH);\n const key = this.deriveKey(salt);\n const iv = randomBytes(IV_LENGTH);\n\n const cipher = createCipheriv(ALGORITHM, key, iv, {\n authTagLength: AUTH_TAG_LENGTH,\n });\n const encrypted = Buffer.concat([\n cipher.update(plaintext, \"utf8\"),\n cipher.final(),\n cipher.getAuthTag(),\n ]);\n\n return {\n data: encrypted.toString(\"base64\"),\n iv: iv.toString(\"base64\"),\n salt: salt.toString(\"base64\"),\n };\n }\n\n private decrypt(entry: EncryptedEntry): string {\n const salt = Buffer.from(entry.salt, \"base64\");\n const key = this.deriveKey(salt);\n const iv = Buffer.from(entry.iv, \"base64\");\n const raw = Buffer.from(entry.data, \"base64\");\n\n const authTag = raw.subarray(raw.length - AUTH_TAG_LENGTH);\n const ciphertext = raw.subarray(0, raw.length - AUTH_TAG_LENGTH);\n\n const decipher = createDecipheriv(ALGORITHM, key, iv, {\n authTagLength: AUTH_TAG_LENGTH,\n });\n decipher.setAuthTag(authTag);\n\n return decipher.update(ciphertext) + decipher.final(\"utf8\");\n }\n\n private readFile(): FileStoreData {\n try {\n const content = readFileSync(this.filePath, \"utf8\");\n return JSON.parse(content) as FileStoreData;\n } catch {\n return { version: 1, credentials: {} };\n }\n }\n\n private writeFile(data: FileStoreData): void {\n mkdirSync(dirname(this.filePath), { recursive: true, mode: 0o700 });\n writeFileSync(this.filePath, JSON.stringify(data, null, 2), { mode: 0o600 });\n }\n}\n\nfunction homedir(): string {\n return process.env.HOME ?? process.env.USERPROFILE ?? \"/tmp\";\n}\n","import type { StoredCredential, TokenStore } from \"@/types.ts\";\n\nconst SERVICE_NAME = \"open-sub-auth\";\nconst INDEX_ACCOUNT = \"__index__\";\n\n/** Token store backed by the OS keychain via cross-keychain */\nexport class KeychainStore implements TokenStore {\n private keychain: typeof import(\"cross-keychain\") | null = null;\n\n private async getKeychain() {\n if (!this.keychain) {\n this.keychain = await import(\"cross-keychain\");\n }\n return this.keychain;\n }\n\n private makeKey(provider: string, accountId: string): string {\n return `${provider}__${accountId}`;\n }\n\n async get(provider: string, accountId: string): Promise<StoredCredential | null> {\n const kc = await this.getKeychain();\n try {\n const value = await kc.getPassword(SERVICE_NAME, this.makeKey(provider, accountId));\n if (!value) return null;\n return JSON.parse(value) as StoredCredential;\n } catch {\n return null;\n }\n }\n\n async set(provider: string, accountId: string, credential: StoredCredential): Promise<void> {\n const kc = await this.getKeychain();\n const key = this.makeKey(provider, accountId);\n const value = JSON.stringify(credential);\n\n try {\n await kc.deletePassword(SERVICE_NAME, key);\n } catch {\n // Ignore if it doesn't exist\n }\n await kc.setPassword(SERVICE_NAME, key, value);\n await this.addToIndex(key);\n }\n\n async delete(provider: string, accountId: string): Promise<void> {\n const kc = await this.getKeychain();\n const key = this.makeKey(provider, accountId);\n try {\n await kc.deletePassword(SERVICE_NAME, key);\n } catch {\n // Ignore if it doesn't exist\n }\n await this.removeFromIndex(key);\n }\n\n async list(provider?: string): Promise<StoredCredential[]> {\n const index = await this.getIndex();\n const results: StoredCredential[] = [];\n\n for (const key of index) {\n const [keyProvider, keyAccountId] = key.split(\"__\");\n if (provider && keyProvider !== provider) continue;\n if (!keyProvider || !keyAccountId) continue;\n\n const credential = await this.get(keyProvider, keyAccountId);\n if (credential) {\n results.push(credential);\n }\n }\n\n return results;\n }\n\n private async getIndex(): Promise<string[]> {\n const kc = await this.getKeychain();\n try {\n const value = await kc.getPassword(SERVICE_NAME, INDEX_ACCOUNT);\n if (!value) return [];\n return JSON.parse(value) as string[];\n } catch {\n return [];\n }\n }\n\n private async setIndex(index: string[]): Promise<void> {\n const kc = await this.getKeychain();\n try {\n await kc.deletePassword(SERVICE_NAME, INDEX_ACCOUNT);\n } catch {\n // Ignore\n }\n await kc.setPassword(SERVICE_NAME, INDEX_ACCOUNT, JSON.stringify(index));\n }\n\n private async addToIndex(key: string): Promise<void> {\n const index = await this.getIndex();\n if (!index.includes(key)) {\n index.push(key);\n await this.setIndex(index);\n }\n }\n\n private async removeFromIndex(key: string): Promise<void> {\n const index = await this.getIndex();\n const filtered = index.filter((k) => k !== key);\n await this.setIndex(filtered);\n }\n}\n","import { AuthenticationError } from \"@/errors.ts\";\nimport type { TokenStore } from \"@/types.ts\";\nimport { FileStore } from \"@/storage/file-store.ts\";\nimport { KeychainStore } from \"@/storage/keychain-store.ts\";\n\nexport type StoreType = \"auto\" | \"keychain\" | \"file\";\n\n/**\n * Create a token store.\n *\n * @param storeType\n * - `\"auto\"` (default): try OS keychain first, fall back to encrypted file store\n * - `\"keychain\"`: force OS keychain; throws AuthenticationError if unavailable\n * - `\"file\"`: always use the encrypted file store (~/.open-sub-auth/credentials.json)\n */\nexport async function createTokenStore(storeType: StoreType = \"auto\"): Promise<TokenStore> {\n if (storeType === \"file\") {\n return new FileStore();\n }\n\n try {\n const store = new KeychainStore();\n // Probe if keychain is accessible\n await store.get(\"__probe__\", \"__probe__\");\n return store;\n } catch (err) {\n if (storeType === \"keychain\") {\n throw new AuthenticationError(\n `OS keychain is not available on this system: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n // \"auto\" mode: fall back silently\n return new FileStore();\n }\n}\n\nexport { FileStore, KeychainStore };\n","import { execFile } from \"node:child_process\";\nimport { platform } from \"node:os\";\n\n/** Open a URL in the user's default browser. Does not throw on failure. */\nexport function openBrowser(url: string): void {\n const os = platform();\n try {\n if (os === \"darwin\") {\n execFile(\"open\", [url]);\n } else if (os === \"win32\") {\n execFile(\"cmd\", [\"/c\", \"start\", \"\", url]);\n } else {\n execFile(\"xdg-open\", [url]);\n }\n } catch {\n // Silently ignore — caller should provide fallback instructions\n }\n}\n","import { createServer, type Server } from \"node:http\";\nimport { OAuthCallbackError, OAuthTimeoutError } from \"@/errors.ts\";\nimport type { AuthorizationResult } from \"@/types.ts\";\n\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>Authorization Successful</title>\n<style>body{font-family:system-ui,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#f8f9fa}\n.card{text-align:center;padding:2rem;border-radius:12px;background:white;box-shadow:0 2px 8px rgba(0,0,0,0.1)}\nh1{color:#22c55e;font-size:1.5rem}p{color:#666}</style></head>\n<body><div class=\"card\"><h1>Authorization Successful</h1><p>You can close this window and return to the terminal.</p></div></body></html>`;\n\nconst ERROR_HTML = `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>Authorization Failed</title>\n<style>body{font-family:system-ui,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#f8f9fa}\n.card{text-align:center;padding:2rem;border-radius:12px;background:white;box-shadow:0 2px 8px rgba(0,0,0,0.1)}\nh1{color:#ef4444;font-size:1.5rem}p{color:#666}</style></head>\n<body><div class=\"card\"><h1>Authorization Failed</h1><p>Something went wrong. Please try again.</p></div></body></html>`;\n\nexport interface CallbackServerOptions {\n /** Port to listen on (0 = random available port) */\n port?: number;\n /** Expected state parameter for CSRF validation */\n expectedState: string;\n /** Timeout in milliseconds (default: 120000) */\n timeout?: number;\n /** Path to listen on (default: \"/callback\") */\n path?: string;\n}\n\n/** Start a local HTTP server to receive the OAuth callback redirect */\nexport function startCallbackServer(\n options: CallbackServerOptions,\n): Promise<{ result: Promise<AuthorizationResult>; port: number; close: () => void }> {\n const { expectedState, timeout = 120_000, path = \"/callback\" } = options;\n const port = options.port ?? 0;\n\n return new Promise((resolveSetup, _rejectSetup) => {\n let server: Server;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const resultPromise = new Promise<AuthorizationResult>((resolveResult, rejectResult) => {\n server = createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://localhost`);\n\n if (url.pathname !== path) {\n res.writeHead(404);\n res.end(\"Not found\");\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n\n if (error) {\n const errorDesc = url.searchParams.get(\"error_description\") ?? error;\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(ERROR_HTML);\n cleanup();\n rejectResult(new OAuthCallbackError(`OAuth error: ${errorDesc}`));\n return;\n }\n\n if (!code) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(ERROR_HTML);\n cleanup();\n rejectResult(new OAuthCallbackError(\"No authorization code in callback\"));\n return;\n }\n\n if (state && state !== expectedState) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(ERROR_HTML);\n cleanup();\n rejectResult(\n new OAuthCallbackError(\"State mismatch — possible CSRF attack. Please try again.\"),\n );\n return;\n }\n\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(SUCCESS_HTML);\n cleanup();\n resolveResult({ code, state: state ?? expectedState });\n });\n\n const cleanup = () => {\n if (timeoutId) clearTimeout(timeoutId);\n server.close();\n };\n\n timeoutId = setTimeout(() => {\n cleanup();\n rejectResult(new OAuthTimeoutError(timeout));\n }, timeout);\n\n server.on(\"error\", (err) => {\n cleanup();\n rejectResult(new OAuthCallbackError(`Callback server error: ${err.message}`));\n });\n\n server.listen(port, \"127.0.0.1\", () => {\n const addr = server.address();\n const actualPort = typeof addr === \"object\" && addr ? addr.port : port;\n resolveSetup({\n result: resultPromise,\n port: actualPort,\n close: cleanup,\n });\n });\n });\n });\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport type { PKCEParams } from \"@/types.ts\";\n\n/** Generate a cryptographically random PKCE code verifier (43-128 chars, base64url) */\nexport function generateCodeVerifier(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\n/** Generate a PKCE code challenge from a verifier using S256 method */\nexport function generateCodeChallenge(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\n/** Generate both PKCE code verifier and challenge */\nexport function generatePKCE(): PKCEParams {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n return { codeVerifier, codeChallenge };\n}\n\n/** Generate a random state parameter for CSRF protection */\nexport function generateState(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n","import { createInterface } from \"node:readline\";\nimport { OAuthCallbackError, StateMismatchError } from \"@/errors.ts\";\nimport type { AuthorizationResult } from \"@/types.ts\";\n\n/**\n * Parse a \"code#state\" string (Anthropic's manual paste format).\n * Also supports a plain authorization code (state validated separately).\n */\nexport function parseCodeAndState(input: string, expectedState: string): AuthorizationResult {\n const trimmed = input.trim();\n if (!trimmed) {\n throw new OAuthCallbackError(\"Empty authorization code input\");\n }\n\n const hashIndex = trimmed.indexOf(\"#\");\n if (hashIndex !== -1) {\n const code = trimmed.slice(0, hashIndex);\n const state = trimmed.slice(hashIndex + 1);\n if (!code) {\n throw new OAuthCallbackError(\"Empty authorization code in code#state input\");\n }\n if (state !== expectedState) {\n throw new StateMismatchError();\n }\n return { code, state };\n }\n\n // No \"#\" found — assume just the code was provided\n return { code: trimmed, state: expectedState };\n}\n\n/** Prompt the user to paste the authorization code from their browser */\nexport function promptForCode(expectedState: string): Promise<AuthorizationResult> {\n return new Promise((resolve, reject) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr, // Use stderr so stdout stays clean for piping\n });\n\n rl.question(\"Paste the authorization code (or code#state) from your browser: \", (answer) => {\n rl.close();\n try {\n resolve(parseCodeAndState(answer, expectedState));\n } catch (err) {\n reject(err);\n }\n });\n });\n}\n","import { OAuthCallbackError } from \"@/errors.ts\";\nimport type { AuthorizationResult, LoginOptions, ProviderConfig, TokenSet } from \"@/types.ts\";\nimport { openBrowser } from \"@/core/browser.ts\";\nimport { startCallbackServer } from \"@/core/callback-server.ts\";\nimport { generatePKCE, generateState } from \"@/core/crypto.ts\";\nimport { promptForCode } from \"@/core/manual-code-input.ts\";\n\n/** Build the full OAuth authorization URL with PKCE and state params */\nexport function buildAuthorizationUrl(\n config: ProviderConfig,\n codeChallenge: string,\n state: string,\n redirectUri: string,\n): string {\n if (!config.authorizationEndpoint) {\n throw new OAuthCallbackError(`Provider \"${config.name}\" has no authorization endpoint`);\n }\n\n const url = new URL(config.authorizationEndpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", config.clientId);\n url.searchParams.set(\"redirect_uri\", redirectUri);\n url.searchParams.set(\"code_challenge\", codeChallenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"state\", state);\n\n if (config.scopes?.length) {\n url.searchParams.set(\"scope\", config.scopes.join(\" \"));\n }\n\n return url.toString();\n}\n\n/** Exchange an authorization code for tokens */\nexport async function exchangeCode(\n config: ProviderConfig,\n code: string,\n codeVerifier: string,\n redirectUri: string,\n state?: string,\n): Promise<TokenSet> {\n const useJson = config.tokenBodyFormat === \"json\";\n\n const params: Record<string, string> = {\n grant_type: \"authorization_code\",\n code,\n code_verifier: codeVerifier,\n client_id: config.clientId,\n redirect_uri: redirectUri,\n };\n if (state !== undefined) {\n params.state = state;\n }\n\n const response = await fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": useJson ? \"application/json\" : \"application/x-www-form-urlencoded\" },\n body: useJson ? JSON.stringify(params) : new URLSearchParams(params).toString(),\n });\n\n if (!response.ok) {\n // Do not include the raw response body — it may contain sensitive OAuth debug info.\n // Consumers that need details should inspect the HTTP response directly.\n throw new OAuthCallbackError(`Token exchange failed (HTTP ${response.status})`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n return parseTokenResponse(data);\n}\n\n/** Refresh an access token using a refresh token */\nexport async function refreshAccessToken(\n config: ProviderConfig,\n refreshToken: string,\n): Promise<TokenSet> {\n const useJson = config.tokenBodyFormat === \"json\";\n\n const params: Record<string, string> = {\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n client_id: config.clientId,\n };\n\n const response = await fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": useJson ? \"application/json\" : \"application/x-www-form-urlencoded\" },\n body: useJson ? JSON.stringify(params) : new URLSearchParams(params).toString(),\n });\n\n if (!response.ok) {\n throw new OAuthCallbackError(`Token refresh failed (HTTP ${response.status})`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n return parseTokenResponse(data);\n}\n\nexport interface PKCEFlowOptions {\n config: ProviderConfig;\n loginOptions?: LoginOptions;\n /**\n * Manual mode redirect URI (used when manual=true).\n * If not provided, manual mode is not available.\n */\n manualRedirectUri?: string;\n}\n\n/** Execute the full PKCE flow: generate params, open browser, wait for callback, exchange code */\nexport async function executePKCEFlow(options: PKCEFlowOptions): Promise<TokenSet> {\n const { config, loginOptions } = options;\n const { codeVerifier, codeChallenge } = generatePKCE();\n // Some providers (e.g. Claude) require state === verifier for their token exchange\n const state = config.stateIsVerifier ? codeVerifier : generateState();\n const manual = loginOptions?.manual ?? false;\n\n let authResult: AuthorizationResult;\n let redirectUri: string;\n\n if (manual && options.manualRedirectUri) {\n // Manual mode: redirect to provider's callback page, user copies code\n redirectUri = options.manualRedirectUri;\n const authUrl = buildAuthorizationUrl(config, codeChallenge, state, redirectUri);\n\n if (loginOptions?.onOpenBrowser) {\n loginOptions.onOpenBrowser(authUrl);\n } else {\n openBrowser(authUrl);\n }\n\n process.stderr.write(\n `\\nOpen this URL in your browser if it didn't open automatically:\\n${authUrl}\\n\\n`,\n );\n authResult = await promptForCode(state);\n } else {\n // Automatic mode: local callback server\n const port = loginOptions?.port ?? 0;\n const timeout = loginOptions?.timeout ?? 120_000;\n\n const server = await startCallbackServer({\n port,\n expectedState: state,\n timeout,\n });\n\n redirectUri = `http://127.0.0.1:${server.port}/callback`;\n const authUrl = buildAuthorizationUrl(config, codeChallenge, state, redirectUri);\n\n if (loginOptions?.onOpenBrowser) {\n loginOptions.onOpenBrowser(authUrl);\n } else {\n openBrowser(authUrl);\n }\n\n process.stderr.write(\n `\\nOpen this URL in your browser if it didn't open automatically:\\n${authUrl}\\n\\nWaiting for authorization...\\n`,\n );\n\n try {\n authResult = await server.result;\n } catch (err) {\n server.close();\n throw err;\n }\n }\n\n // Pass state to exchange when provider uses JSON body format (e.g. Claude)\n const exchangeState = config.tokenBodyFormat === \"json\" ? authResult.state : undefined;\n return exchangeCode(config, authResult.code, codeVerifier, redirectUri, exchangeState);\n}\n\nfunction parseTokenResponse(data: Record<string, unknown>): TokenSet {\n const accessToken = data.access_token as string | undefined;\n if (!accessToken) {\n throw new OAuthCallbackError(\"No access_token in token response\");\n }\n\n const expiresIn = (data.expires_in as number | undefined) ?? 3600;\n const expiresAt = Date.now() + expiresIn * 1000;\n\n return {\n accessToken,\n refreshToken: (data.refresh_token as string | undefined) ?? null,\n expiresAt,\n idToken: data.id_token as string | undefined,\n tokenType: ((data.token_type as string | undefined) ?? \"bearer\").toLowerCase() as\n | \"bearer\"\n | \"api-key\",\n scopes: data.scope ? (data.scope as string).split(\" \") : undefined,\n raw: data,\n };\n}\n","import { createHash } from \"node:crypto\";\nimport { executePKCEFlow, refreshAccessToken } from \"@/core/oauth-pkce.ts\";\nimport type { AuthHeaders, LoginOptions, Provider, ProviderConfig, TokenSet } from \"@/types.ts\";\nimport { registerProvider } from \"@/providers/registry.ts\";\n\nconst CLAUDE_CONFIG: ProviderConfig = {\n name: \"claude\",\n displayName: \"Claude Pro/Max\",\n authorizationEndpoint: \"https://claude.ai/oauth/authorize\",\n tokenEndpoint: \"https://console.anthropic.com/v1/oauth/token\",\n clientId: \"9d1c250a-e61b-44d9-88ed-5944d1962f5e\",\n scopes: [\"org:create_api_key\", \"user:profile\", \"user:inference\"],\n grantType: \"authorization_code\",\n // Claude only supports console.anthropic.com redirect URI (no localhost),\n // and its token endpoint requires JSON body with state field\n tokenBodyFormat: \"json\",\n stateIsVerifier: true,\n};\n\n/** Redirect URI used in manual/headless mode (user copies code from browser) */\nconst MANUAL_REDIRECT_URI = \"https://console.anthropic.com/oauth/code/callback\";\n\nexport class ClaudeProvider implements Provider {\n readonly config = CLAUDE_CONFIG;\n\n async login(options?: LoginOptions): Promise<TokenSet> {\n // Claude only allows console.anthropic.com/oauth/code/callback as redirect URI.\n // Local callback server is not supported, so always use manual (code-paste) mode.\n return executePKCEFlow({\n config: this.config,\n loginOptions: { ...options, manual: true },\n manualRedirectUri: MANUAL_REDIRECT_URI,\n });\n }\n\n async refresh(refreshToken: string): Promise<TokenSet> {\n return refreshAccessToken(this.config, refreshToken);\n }\n\n getAuthHeaders(accessToken: string): AuthHeaders {\n return {\n authorization: `Bearer ${accessToken}`,\n \"anthropic-version\": \"2023-06-01\",\n \"anthropic-beta\": \"oauth-2025-04-20\",\n \"content-type\": \"application/json\",\n };\n }\n\n getAccountId(tokenSet: TokenSet): string {\n // Claude doesn't provide a profile endpoint, so derive ID from refresh token hash\n const source = tokenSet.refreshToken ?? tokenSet.accessToken;\n return createHash(\"sha256\").update(source).digest(\"hex\").slice(0, 16);\n }\n\n getAccountLabel(_tokenSet: TokenSet): string | undefined {\n // Claude tokens don't carry user identity info\n return undefined;\n }\n}\n\n/**\n * Import a Claude OAuth token from the CLAUDE_CODE_OAUTH_TOKEN environment variable.\n * This token is used directly as the access token for API calls.\n */\nexport function importClaudeTokenFromEnv(): TokenSet | null {\n const token = process.env.CLAUDE_CODE_OAUTH_TOKEN;\n if (!token) return null;\n return {\n accessToken: token,\n refreshToken: null,\n expiresAt: Date.now() + 3600_000, // Assume 1 hour; will refresh as needed\n tokenType: \"bearer\",\n };\n}\n\n// Auto-register\nregisterProvider(\"claude\", () => new ClaudeProvider());\n","/** Decoded JWT claims (no signature verification) */\nexport interface JWTClaims {\n sub?: string;\n email?: string;\n name?: string;\n iss?: string;\n aud?: string | string[];\n exp?: number;\n iat?: number;\n [key: string]: unknown;\n}\n\n/**\n * Decode a JWT token's payload without verifying the signature.\n * Used to extract user info from id_tokens (e.g., OpenAI).\n */\nexport function decodeJWT(token: string): JWTClaims {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid JWT format: expected 3 parts\");\n }\n\n const payload = parts[1];\n if (!payload) {\n throw new Error(\"Invalid JWT: empty payload\");\n }\n\n // Add padding if needed for base64url decoding\n const padded = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const decoded = Buffer.from(padded, \"base64\").toString(\"utf8\");\n\n return JSON.parse(decoded) as JWTClaims;\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { executePKCEFlow, refreshAccessToken } from \"@/core/oauth-pkce.ts\";\nimport { decodeJWT } from \"@/token/jwt.ts\";\nimport type { AuthHeaders, LoginOptions, Provider, ProviderConfig, TokenSet } from \"@/types.ts\";\nimport { registerProvider } from \"@/providers/registry.ts\";\n\nconst OPENAI_CODEX_CONFIG: ProviderConfig = {\n name: \"openai-codex\",\n displayName: \"OpenAI ChatGPT Plus/Pro\",\n authorizationEndpoint: \"https://auth.openai.com/oauth/authorize\",\n tokenEndpoint: \"https://auth.openai.com/oauth/token\",\n clientId: \"app_EMoamEEZ73f0CkXaXp7hrann\",\n redirectUri: \"http://localhost:1455/auth/callback\",\n scopes: [\"openid\", \"profile\", \"email\", \"offline_access\"],\n grantType: \"authorization_code\",\n};\n\nexport class OpenAICodexProvider implements Provider {\n readonly config = OPENAI_CODEX_CONFIG;\n\n async login(options?: LoginOptions): Promise<TokenSet> {\n return executePKCEFlow({\n config: this.config,\n loginOptions: {\n ...options,\n port: options?.port ?? 1455,\n },\n });\n }\n\n async refresh(refreshToken: string): Promise<TokenSet> {\n return refreshAccessToken(this.config, refreshToken);\n }\n\n getAuthHeaders(accessToken: string): AuthHeaders {\n return {\n authorization: `Bearer ${accessToken}`,\n \"content-type\": \"application/json\",\n };\n }\n\n getAccountId(tokenSet: TokenSet): string {\n if (tokenSet.idToken) {\n try {\n const claims = decodeJWT(tokenSet.idToken);\n if (claims.sub) return claims.sub;\n } catch {\n // Fall through\n }\n }\n return createHash(\"sha256\").update(tokenSet.accessToken).digest(\"hex\").slice(0, 16);\n }\n\n getAccountLabel(tokenSet: TokenSet): string | undefined {\n if (tokenSet.idToken) {\n try {\n const claims = decodeJWT(tokenSet.idToken);\n if (claims.email) return claims.email as string;\n } catch {\n // Fall through\n }\n }\n return undefined;\n }\n}\n\n/**\n * Import tokens from an existing Codex CLI installation.\n * Reads from ~/.codex/auth.json or $CODEX_HOME/auth.json.\n */\nexport function importFromCodexCli(): TokenSet | null {\n const paths = [\n process.env.CODEX_HOME ? join(process.env.CODEX_HOME, \"auth.json\") : null,\n join(homedir(), \".codex\", \"auth.json\"),\n ].filter(Boolean) as string[];\n\n for (const filePath of paths) {\n if (!existsSync(filePath)) continue;\n\n try {\n const content = readFileSync(filePath, \"utf8\");\n const data = JSON.parse(content) as {\n tokens?: {\n access_token?: string;\n refresh_token?: string;\n id_token?: string;\n };\n };\n\n if (!data.tokens?.access_token) continue;\n\n return {\n accessToken: data.tokens.access_token,\n refreshToken: data.tokens.refresh_token ?? null,\n expiresAt: Date.now() + 3600_000, // Assume 1 hour, will refresh as needed\n idToken: data.tokens.id_token,\n tokenType: \"bearer\",\n };\n } catch {\n continue;\n }\n }\n\n return null;\n}\n\n// Auto-register\nregisterProvider(\"openai-codex\", () => new OpenAICodexProvider());\n","import { OAuthCallbackError, OAuthTimeoutError } from \"@/errors.ts\";\nimport { openBrowser } from \"@/core/browser.ts\";\nimport type { DeviceCodeInfo, LoginOptions, ProviderConfig, TokenSet } from \"@/types.ts\";\n\nexport interface DeviceCodeFlowOptions {\n config: ProviderConfig;\n loginOptions?: LoginOptions;\n}\n\n/** Request a device code from the provider's device code endpoint */\nexport async function requestDeviceCode(config: ProviderConfig): Promise<{\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete?: string;\n expiresIn: number;\n interval: number;\n}> {\n if (!config.deviceCodeEndpoint) {\n throw new OAuthCallbackError(`Provider \"${config.name}\" has no device code endpoint`);\n }\n\n const params: Record<string, string> = {\n client_id: config.clientId,\n };\n if (config.scopes?.length) {\n params.scope = config.scopes.join(\" \");\n }\n\n const useJson = config.tokenBodyFormat === \"json\";\n\n const response = await fetch(config.deviceCodeEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": useJson ? \"application/json\" : \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: useJson ? JSON.stringify(params) : new URLSearchParams(params).toString(),\n });\n\n if (!response.ok) {\n throw new OAuthCallbackError(`Device code request failed (HTTP ${response.status})`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n\n const deviceCode = data.device_code as string | undefined;\n const userCode = data.user_code as string | undefined;\n const verificationUri = (data.verification_uri ?? data.verification_url) as string | undefined;\n\n if (!deviceCode || !userCode || !verificationUri) {\n throw new OAuthCallbackError(\"Invalid device code response: missing required fields\");\n }\n\n return {\n deviceCode,\n userCode,\n verificationUri,\n verificationUriComplete: (data.verification_uri_complete ?? data.verification_url_complete) as\n | string\n | undefined,\n expiresIn: (data.expires_in as number | undefined) ?? 900,\n interval: (data.interval as number | undefined) ?? 5,\n };\n}\n\n/**\n * Poll the token endpoint until authorization is granted, denied, or times out.\n * Handles authorization_pending / slow_down / expired_token error codes per RFC 8628.\n */\nexport async function pollForToken(\n config: ProviderConfig,\n deviceCode: string,\n interval: number,\n expiresIn: number,\n): Promise<TokenSet> {\n const params: Record<string, string> = {\n client_id: config.clientId,\n device_code: deviceCode,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n };\n\n const useJson = config.tokenBodyFormat === \"json\";\n const deadline = Date.now() + expiresIn * 1000;\n let pollIntervalMs = interval * 1000;\n\n while (Date.now() < deadline) {\n await sleep(pollIntervalMs);\n\n const response = await fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": useJson ? \"application/json\" : \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: useJson ? JSON.stringify(params) : new URLSearchParams(params).toString(),\n });\n\n // GitHub returns 200 even on \"pending\" errors, parse JSON regardless of status\n const data = (await response.json()) as Record<string, unknown>;\n\n if (data.access_token) {\n return parseDeviceTokenResponse(data);\n }\n\n const error = data.error as string | undefined;\n\n if (error === \"authorization_pending\") {\n continue;\n }\n if (error === \"slow_down\") {\n // Per RFC 8628: increase interval by 5 seconds and continue\n pollIntervalMs += 5000;\n continue;\n }\n if (error === \"expired_token\") {\n throw new OAuthTimeoutError(expiresIn * 1000);\n }\n if (error === \"access_denied\") {\n throw new OAuthCallbackError(\"User denied device authorization\");\n }\n\n throw new OAuthCallbackError(`Device code token polling failed: ${error ?? \"unknown error\"}`);\n }\n\n throw new OAuthTimeoutError(expiresIn * 1000);\n}\n\n/** Execute the full device code flow: request code, display to user, poll for token */\nexport async function executeDeviceCodeFlow(options: DeviceCodeFlowOptions): Promise<TokenSet> {\n const { config, loginOptions } = options;\n\n const deviceCode = await requestDeviceCode(config);\n\n const displayInfo: DeviceCodeInfo = {\n userCode: deviceCode.userCode,\n verificationUri: deviceCode.verificationUri,\n expiresIn: deviceCode.expiresIn,\n interval: deviceCode.interval,\n };\n\n if (loginOptions?.onDeviceCode) {\n loginOptions.onDeviceCode(displayInfo);\n } else {\n // Open browser to the verification page and print instructions to stderr\n openBrowser(deviceCode.verificationUriComplete ?? deviceCode.verificationUri);\n process.stderr.write(\n `\\nVisit: ${deviceCode.verificationUri}\\nEnter code: ${deviceCode.userCode}\\n\\nWaiting for authorization...\\n`,\n );\n }\n\n return pollForToken(config, deviceCode.deviceCode, deviceCode.interval, deviceCode.expiresIn);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Parse device code token response.\n * Note: GitHub uses comma-separated scopes (unlike space-separated in PKCE flow).\n */\nfunction parseDeviceTokenResponse(data: Record<string, unknown>): TokenSet {\n const accessToken = data.access_token as string | undefined;\n if (!accessToken) {\n throw new OAuthCallbackError(\"No access_token in device code token response\");\n }\n\n // GitHub OAuth App tokens may not include expires_in (they don't expire by default)\n const expiresIn = data.expires_in as number | undefined;\n const expiresAt = expiresIn ? Date.now() + expiresIn * 1000 : Date.now() + 8 * 3600 * 1000;\n\n // GitHub uses comma-separated scopes in device code responses\n const rawScope = data.scope as string | undefined;\n const scopes = rawScope ? rawScope.split(/[, ]+/).filter(Boolean) : undefined;\n\n return {\n accessToken,\n refreshToken: (data.refresh_token as string | undefined) ?? null,\n expiresAt,\n tokenType: ((data.token_type as string | undefined) ?? \"bearer\").toLowerCase() as\n | \"bearer\"\n | \"api-key\",\n scopes,\n raw: data,\n };\n}\n","import { createHash } from \"node:crypto\";\nimport { OAuthCallbackError } from \"@/errors.ts\";\nimport { executeDeviceCodeFlow } from \"@/core/oauth-device.ts\";\nimport { registerProvider } from \"@/providers/registry.ts\";\nimport type { AuthHeaders, LoginOptions, Provider, ProviderConfig, TokenSet } from \"@/types.ts\";\n\nconst GITHUB_COPILOT_CONFIG: ProviderConfig = {\n name: \"github-copilot\",\n displayName: \"GitHub Copilot\",\n tokenEndpoint: \"https://github.com/login/oauth/access_token\",\n deviceCodeEndpoint: \"https://github.com/login/device/code\",\n // Public client ID used by several Copilot-compatible tools\n clientId: \"Iv1.b507a08c87ecfe98\",\n scopes: [\"gist\"],\n grantType: \"device_code\",\n};\n\n/** Copilot session token endpoint (internal GitHub API) */\nconst COPILOT_SESSION_ENDPOINT = \"https://api.github.com/copilot_internal/v2/token\";\n\n/** Fetch a short-lived Copilot session token using a GitHub OAuth access token */\nasync function fetchCopilotSessionToken(\n githubToken: string,\n): Promise<{ token: string; expiresAt: number }> {\n const response = await fetch(COPILOT_SESSION_ENDPOINT, {\n headers: {\n Authorization: `Bearer ${githubToken}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw new OAuthCallbackError(`Copilot session token request failed (HTTP ${response.status})`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n const token = data.token as string | undefined;\n\n if (!token) {\n throw new OAuthCallbackError(\"No token in Copilot session token response\");\n }\n\n // expires_at is a Unix timestamp in seconds\n const expiresAt =\n typeof data.expires_at === \"number\" ? data.expires_at * 1000 : Date.now() + 30 * 60 * 1000; // 30 minutes default\n\n return { token, expiresAt };\n}\n\n/** Fetch the authenticated GitHub user info for account labelling */\nasync function fetchGitHubUser(\n githubToken: string,\n): Promise<{ login: string; name?: string; email?: string }> {\n try {\n const response = await fetch(\"https://api.github.com/user\", {\n headers: { Authorization: `Bearer ${githubToken}` },\n });\n if (!response.ok) return { login: \"unknown\" };\n return (await response.json()) as { login: string; name?: string; email?: string };\n } catch {\n return { login: \"unknown\" };\n }\n}\n\nexport class GitHubCopilotProvider implements Provider {\n readonly config = GITHUB_COPILOT_CONFIG;\n\n async login(options?: LoginOptions): Promise<TokenSet> {\n // Step 1: Device Code Flow → GitHub OAuth access token\n const githubTokenSet = await executeDeviceCodeFlow({\n config: this.config,\n loginOptions: options,\n });\n\n const githubToken = githubTokenSet.accessToken;\n\n // Step 2: Exchange for a short-lived Copilot session token\n const session = await fetchCopilotSessionToken(githubToken);\n\n // Step 3: Fetch user info for account identity (best-effort)\n const user = await fetchGitHubUser(githubToken);\n\n return {\n // The access token exposed to callers is the Copilot session token (~30 min)\n accessToken: session.token,\n // The refresh token is the GitHub OAuth token, used to get new session tokens\n refreshToken: githubToken,\n expiresAt: session.expiresAt,\n tokenType: \"bearer\",\n scopes: githubTokenSet.scopes,\n raw: {\n githubToken,\n githubRefreshToken: githubTokenSet.refreshToken,\n login: user.login,\n name: user.name,\n email: user.email,\n },\n };\n }\n\n /** Refresh by exchanging the stored GitHub OAuth token for a new Copilot session token */\n async refresh(refreshToken: string): Promise<TokenSet> {\n // refreshToken here is the GitHub OAuth access token (set during login)\n const session = await fetchCopilotSessionToken(refreshToken);\n\n return {\n accessToken: session.token,\n refreshToken, // Preserve the GitHub OAuth token for the next refresh\n expiresAt: session.expiresAt,\n tokenType: \"bearer\",\n };\n }\n\n getAuthHeaders(accessToken: string): AuthHeaders {\n return {\n authorization: `Bearer ${accessToken}`,\n \"content-type\": \"application/json\",\n };\n }\n\n getAccountId(tokenSet: TokenSet): string {\n // Prefer GitHub login name (set during login in raw)\n if (\n tokenSet.raw?.login &&\n typeof tokenSet.raw.login === \"string\" &&\n tokenSet.raw.login !== \"unknown\"\n ) {\n return tokenSet.raw.login;\n }\n // Fall back to hash of the GitHub OAuth token (refreshToken)\n const source = tokenSet.refreshToken ?? tokenSet.accessToken;\n return createHash(\"sha256\").update(source).digest(\"hex\").slice(0, 16);\n }\n\n getAccountLabel(tokenSet: TokenSet): string | undefined {\n if (tokenSet.raw?.name && typeof tokenSet.raw.name === \"string\") {\n return tokenSet.raw.name;\n }\n if (\n tokenSet.raw?.login &&\n typeof tokenSet.raw.login === \"string\" &&\n tokenSet.raw.login !== \"unknown\"\n ) {\n return tokenSet.raw.login;\n }\n return undefined;\n }\n}\n\n/**\n * Import a GitHub OAuth token from the COPILOT_GITHUB_TOKEN environment variable.\n * The GitHub OAuth token becomes the refresh token; the expired access token forces an\n * immediate Copilot session token fetch on the first call to getToken().\n */\nexport function importCopilotTokenFromEnv(): TokenSet | null {\n const token = process.env.COPILOT_GITHUB_TOKEN;\n if (!token) return null;\n return {\n accessToken: \"\", // Expired — getToken() will call refresh() to obtain a session token\n refreshToken: token, // GitHub OAuth token used to obtain Copilot session tokens\n expiresAt: 0,\n tokenType: \"bearer\",\n };\n}\n\n// Auto-register\nregisterProvider(\"github-copilot\", () => new GitHubCopilotProvider());\n","import { EnvHttpProxyAgent, setGlobalDispatcher } from \"node:undici\";\n\n/**\n * Initialize HTTP proxy support for all outgoing fetch() calls.\n *\n * Reads HTTPS_PROXY / HTTP_PROXY / NO_PROXY environment variables automatically.\n * If a proxyUrl is provided, it overrides those env vars before installing the dispatcher.\n *\n * Call this once at startup — CLI entry point or library init — before any network requests.\n *\n * @param proxyUrl - Optional explicit proxy URL (e.g. \"http://proxy.corp:8080\").\n * Sets both HTTPS_PROXY and HTTP_PROXY env vars when provided.\n */\nexport function initProxy(proxyUrl?: string): void {\n if (proxyUrl) {\n process.env.HTTPS_PROXY = proxyUrl;\n process.env.HTTP_PROXY = proxyUrl;\n }\n setGlobalDispatcher(new EnvHttpProxyAgent());\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -207,11 +207,16 @@ declare class KeychainStore implements TokenStore {
207
207
  }
208
208
  //#endregion
209
209
  //#region src/storage/store.d.ts
210
+ type StoreType = "auto" | "keychain" | "file";
210
211
  /**
211
- * Create a token store, preferring the OS keychain with encrypted file fallback.
212
- * @param preferKeychain - If true (default), try keychain first
212
+ * Create a token store.
213
+ *
214
+ * @param storeType
215
+ * - `"auto"` (default): try OS keychain first, fall back to encrypted file store
216
+ * - `"keychain"`: force OS keychain; throws AuthenticationError if unavailable
217
+ * - `"file"`: always use the encrypted file store (~/.open-sub-auth/credentials.json)
213
218
  */
214
- declare function createTokenStore(preferKeychain?: boolean): Promise<TokenStore>;
219
+ declare function createTokenStore(storeType?: StoreType): Promise<TokenStore>;
215
220
  //#endregion
216
221
  //#region src/providers/registry.d.ts
217
222
  /** Register a provider factory */
@@ -230,6 +235,11 @@ declare class ClaudeProvider implements Provider {
230
235
  getAccountId(tokenSet: TokenSet): string;
231
236
  getAccountLabel(_tokenSet: TokenSet): string | undefined;
232
237
  }
238
+ /**
239
+ * Import a Claude OAuth token from the CLAUDE_CODE_OAUTH_TOKEN environment variable.
240
+ * This token is used directly as the access token for API calls.
241
+ */
242
+ declare function importClaudeTokenFromEnv(): TokenSet | null;
233
243
  //#endregion
234
244
  //#region src/providers/openai-codex.d.ts
235
245
  declare class OpenAICodexProvider implements Provider {
@@ -246,6 +256,23 @@ declare class OpenAICodexProvider implements Provider {
246
256
  */
247
257
  declare function importFromCodexCli(): TokenSet | null;
248
258
  //#endregion
259
+ //#region src/providers/github-copilot.d.ts
260
+ declare class GitHubCopilotProvider implements Provider {
261
+ readonly config: ProviderConfig;
262
+ login(options?: LoginOptions): Promise<TokenSet>;
263
+ /** Refresh by exchanging the stored GitHub OAuth token for a new Copilot session token */
264
+ refresh(refreshToken: string): Promise<TokenSet>;
265
+ getAuthHeaders(accessToken: string): AuthHeaders;
266
+ getAccountId(tokenSet: TokenSet): string;
267
+ getAccountLabel(tokenSet: TokenSet): string | undefined;
268
+ }
269
+ /**
270
+ * Import a GitHub OAuth token from the COPILOT_GITHUB_TOKEN environment variable.
271
+ * The GitHub OAuth token becomes the refresh token; the expired access token forces an
272
+ * immediate Copilot session token fetch on the first call to getToken().
273
+ */
274
+ declare function importCopilotTokenFromEnv(): TokenSet | null;
275
+ //#endregion
249
276
  //#region src/core/crypto.d.ts
250
277
  /** Generate a cryptographically random PKCE code verifier (43-128 chars, base64url) */
251
278
  declare function generateCodeVerifier(): string;
@@ -275,6 +302,28 @@ interface PKCEFlowOptions {
275
302
  /** Execute the full PKCE flow: generate params, open browser, wait for callback, exchange code */
276
303
  declare function executePKCEFlow(options: PKCEFlowOptions): Promise<TokenSet>;
277
304
  //#endregion
305
+ //#region src/core/oauth-device.d.ts
306
+ interface DeviceCodeFlowOptions {
307
+ config: ProviderConfig;
308
+ loginOptions?: LoginOptions;
309
+ }
310
+ /** Request a device code from the provider's device code endpoint */
311
+ declare function requestDeviceCode(config: ProviderConfig): Promise<{
312
+ deviceCode: string;
313
+ userCode: string;
314
+ verificationUri: string;
315
+ verificationUriComplete?: string;
316
+ expiresIn: number;
317
+ interval: number;
318
+ }>;
319
+ /**
320
+ * Poll the token endpoint until authorization is granted, denied, or times out.
321
+ * Handles authorization_pending / slow_down / expired_token error codes per RFC 8628.
322
+ */
323
+ declare function pollForToken(config: ProviderConfig, deviceCode: string, interval: number, expiresIn: number): Promise<TokenSet>;
324
+ /** Execute the full device code flow: request code, display to user, poll for token */
325
+ declare function executeDeviceCodeFlow(options: DeviceCodeFlowOptions): Promise<TokenSet>;
326
+ //#endregion
278
327
  //#region src/core/callback-server.d.ts
279
328
  interface CallbackServerOptions {
280
329
  /** Port to listen on (0 = random available port) */
@@ -306,6 +355,20 @@ declare function promptForCode(expectedState: string): Promise<AuthorizationResu
306
355
  /** Open a URL in the user's default browser. Does not throw on failure. */
307
356
  declare function openBrowser(url: string): void;
308
357
  //#endregion
358
+ //#region src/core/proxy.d.ts
359
+ /**
360
+ * Initialize HTTP proxy support for all outgoing fetch() calls.
361
+ *
362
+ * Reads HTTPS_PROXY / HTTP_PROXY / NO_PROXY environment variables automatically.
363
+ * If a proxyUrl is provided, it overrides those env vars before installing the dispatcher.
364
+ *
365
+ * Call this once at startup — CLI entry point or library init — before any network requests.
366
+ *
367
+ * @param proxyUrl - Optional explicit proxy URL (e.g. "http://proxy.corp:8080").
368
+ * Sets both HTTPS_PROXY and HTTP_PROXY env vars when provided.
369
+ */
370
+ declare function initProxy(proxyUrl?: string): void;
371
+ //#endregion
309
372
  //#region src/token/jwt.d.ts
310
373
  /** Decoded JWT claims (no signature verification) */
311
374
  interface JWTClaims {
@@ -324,5 +387,5 @@ interface JWTClaims {
324
387
  */
325
388
  declare function decodeJWT(token: string): JWTClaims;
326
389
  //#endregion
327
- export { type AuthHeaders, AuthenticationError, type AuthorizationResult, ClaudeProvider, type CredentialStatus, type DeviceCodeInfo, FileStore, KeychainStore, type LoginOptions, NoCredentialError, OAuthCallbackError, OAuthTimeoutError, OpenAICodexProvider, OpenSubAuthError, type PKCEParams, type Provider, type ProviderConfig, ProviderNotFoundError, StateMismatchError, type StoredCredential, TokenExpiredError, TokenManager, type TokenMetadata, TokenRefreshError, type TokenSet, type TokenStore, buildAuthorizationUrl, createTokenStore, decodeJWT, exchangeCode, executePKCEFlow, generateCodeChallenge, generateCodeVerifier, generatePKCE, generateState, getProvider, importFromCodexCli, listProviders, openBrowser, parseCodeAndState, promptForCode, refreshAccessToken, registerProvider, startCallbackServer };
390
+ export { type AuthHeaders, AuthenticationError, type AuthorizationResult, ClaudeProvider, type CredentialStatus, type DeviceCodeInfo, FileStore, GitHubCopilotProvider, KeychainStore, type LoginOptions, NoCredentialError, OAuthCallbackError, OAuthTimeoutError, OpenAICodexProvider, OpenSubAuthError, type PKCEParams, type Provider, type ProviderConfig, ProviderNotFoundError, StateMismatchError, type StoreType, type StoredCredential, TokenExpiredError, TokenManager, type TokenMetadata, TokenRefreshError, type TokenSet, type TokenStore, buildAuthorizationUrl, createTokenStore, decodeJWT, exchangeCode, executeDeviceCodeFlow, executePKCEFlow, generateCodeChallenge, generateCodeVerifier, generatePKCE, generateState, getProvider, importClaudeTokenFromEnv, importCopilotTokenFromEnv, importFromCodexCli, initProxy, listProviders, openBrowser, parseCodeAndState, pollForToken, promptForCode, refreshAccessToken, registerProvider, requestDeviceCode, startCallbackServer };
328
391
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.mts CHANGED
@@ -207,11 +207,16 @@ declare class KeychainStore implements TokenStore {
207
207
  }
208
208
  //#endregion
209
209
  //#region src/storage/store.d.ts
210
+ type StoreType = "auto" | "keychain" | "file";
210
211
  /**
211
- * Create a token store, preferring the OS keychain with encrypted file fallback.
212
- * @param preferKeychain - If true (default), try keychain first
212
+ * Create a token store.
213
+ *
214
+ * @param storeType
215
+ * - `"auto"` (default): try OS keychain first, fall back to encrypted file store
216
+ * - `"keychain"`: force OS keychain; throws AuthenticationError if unavailable
217
+ * - `"file"`: always use the encrypted file store (~/.open-sub-auth/credentials.json)
213
218
  */
214
- declare function createTokenStore(preferKeychain?: boolean): Promise<TokenStore>;
219
+ declare function createTokenStore(storeType?: StoreType): Promise<TokenStore>;
215
220
  //#endregion
216
221
  //#region src/providers/registry.d.ts
217
222
  /** Register a provider factory */
@@ -230,6 +235,11 @@ declare class ClaudeProvider implements Provider {
230
235
  getAccountId(tokenSet: TokenSet): string;
231
236
  getAccountLabel(_tokenSet: TokenSet): string | undefined;
232
237
  }
238
+ /**
239
+ * Import a Claude OAuth token from the CLAUDE_CODE_OAUTH_TOKEN environment variable.
240
+ * This token is used directly as the access token for API calls.
241
+ */
242
+ declare function importClaudeTokenFromEnv(): TokenSet | null;
233
243
  //#endregion
234
244
  //#region src/providers/openai-codex.d.ts
235
245
  declare class OpenAICodexProvider implements Provider {
@@ -246,6 +256,23 @@ declare class OpenAICodexProvider implements Provider {
246
256
  */
247
257
  declare function importFromCodexCli(): TokenSet | null;
248
258
  //#endregion
259
+ //#region src/providers/github-copilot.d.ts
260
+ declare class GitHubCopilotProvider implements Provider {
261
+ readonly config: ProviderConfig;
262
+ login(options?: LoginOptions): Promise<TokenSet>;
263
+ /** Refresh by exchanging the stored GitHub OAuth token for a new Copilot session token */
264
+ refresh(refreshToken: string): Promise<TokenSet>;
265
+ getAuthHeaders(accessToken: string): AuthHeaders;
266
+ getAccountId(tokenSet: TokenSet): string;
267
+ getAccountLabel(tokenSet: TokenSet): string | undefined;
268
+ }
269
+ /**
270
+ * Import a GitHub OAuth token from the COPILOT_GITHUB_TOKEN environment variable.
271
+ * The GitHub OAuth token becomes the refresh token; the expired access token forces an
272
+ * immediate Copilot session token fetch on the first call to getToken().
273
+ */
274
+ declare function importCopilotTokenFromEnv(): TokenSet | null;
275
+ //#endregion
249
276
  //#region src/core/crypto.d.ts
250
277
  /** Generate a cryptographically random PKCE code verifier (43-128 chars, base64url) */
251
278
  declare function generateCodeVerifier(): string;
@@ -275,6 +302,28 @@ interface PKCEFlowOptions {
275
302
  /** Execute the full PKCE flow: generate params, open browser, wait for callback, exchange code */
276
303
  declare function executePKCEFlow(options: PKCEFlowOptions): Promise<TokenSet>;
277
304
  //#endregion
305
+ //#region src/core/oauth-device.d.ts
306
+ interface DeviceCodeFlowOptions {
307
+ config: ProviderConfig;
308
+ loginOptions?: LoginOptions;
309
+ }
310
+ /** Request a device code from the provider's device code endpoint */
311
+ declare function requestDeviceCode(config: ProviderConfig): Promise<{
312
+ deviceCode: string;
313
+ userCode: string;
314
+ verificationUri: string;
315
+ verificationUriComplete?: string;
316
+ expiresIn: number;
317
+ interval: number;
318
+ }>;
319
+ /**
320
+ * Poll the token endpoint until authorization is granted, denied, or times out.
321
+ * Handles authorization_pending / slow_down / expired_token error codes per RFC 8628.
322
+ */
323
+ declare function pollForToken(config: ProviderConfig, deviceCode: string, interval: number, expiresIn: number): Promise<TokenSet>;
324
+ /** Execute the full device code flow: request code, display to user, poll for token */
325
+ declare function executeDeviceCodeFlow(options: DeviceCodeFlowOptions): Promise<TokenSet>;
326
+ //#endregion
278
327
  //#region src/core/callback-server.d.ts
279
328
  interface CallbackServerOptions {
280
329
  /** Port to listen on (0 = random available port) */
@@ -306,6 +355,20 @@ declare function promptForCode(expectedState: string): Promise<AuthorizationResu
306
355
  /** Open a URL in the user's default browser. Does not throw on failure. */
307
356
  declare function openBrowser(url: string): void;
308
357
  //#endregion
358
+ //#region src/core/proxy.d.ts
359
+ /**
360
+ * Initialize HTTP proxy support for all outgoing fetch() calls.
361
+ *
362
+ * Reads HTTPS_PROXY / HTTP_PROXY / NO_PROXY environment variables automatically.
363
+ * If a proxyUrl is provided, it overrides those env vars before installing the dispatcher.
364
+ *
365
+ * Call this once at startup — CLI entry point or library init — before any network requests.
366
+ *
367
+ * @param proxyUrl - Optional explicit proxy URL (e.g. "http://proxy.corp:8080").
368
+ * Sets both HTTPS_PROXY and HTTP_PROXY env vars when provided.
369
+ */
370
+ declare function initProxy(proxyUrl?: string): void;
371
+ //#endregion
309
372
  //#region src/token/jwt.d.ts
310
373
  /** Decoded JWT claims (no signature verification) */
311
374
  interface JWTClaims {
@@ -324,5 +387,5 @@ interface JWTClaims {
324
387
  */
325
388
  declare function decodeJWT(token: string): JWTClaims;
326
389
  //#endregion
327
- export { type AuthHeaders, AuthenticationError, type AuthorizationResult, ClaudeProvider, type CredentialStatus, type DeviceCodeInfo, FileStore, KeychainStore, type LoginOptions, NoCredentialError, OAuthCallbackError, OAuthTimeoutError, OpenAICodexProvider, OpenSubAuthError, type PKCEParams, type Provider, type ProviderConfig, ProviderNotFoundError, StateMismatchError, type StoredCredential, TokenExpiredError, TokenManager, type TokenMetadata, TokenRefreshError, type TokenSet, type TokenStore, buildAuthorizationUrl, createTokenStore, decodeJWT, exchangeCode, executePKCEFlow, generateCodeChallenge, generateCodeVerifier, generatePKCE, generateState, getProvider, importFromCodexCli, listProviders, openBrowser, parseCodeAndState, promptForCode, refreshAccessToken, registerProvider, startCallbackServer };
390
+ export { type AuthHeaders, AuthenticationError, type AuthorizationResult, ClaudeProvider, type CredentialStatus, type DeviceCodeInfo, FileStore, GitHubCopilotProvider, KeychainStore, type LoginOptions, NoCredentialError, OAuthCallbackError, OAuthTimeoutError, OpenAICodexProvider, OpenSubAuthError, type PKCEParams, type Provider, type ProviderConfig, ProviderNotFoundError, StateMismatchError, type StoreType, type StoredCredential, TokenExpiredError, TokenManager, type TokenMetadata, TokenRefreshError, type TokenSet, type TokenStore, buildAuthorizationUrl, createTokenStore, decodeJWT, exchangeCode, executeDeviceCodeFlow, executePKCEFlow, generateCodeChallenge, generateCodeVerifier, generatePKCE, generateState, getProvider, importClaudeTokenFromEnv, importCopilotTokenFromEnv, importFromCodexCli, initProxy, listProviders, openBrowser, parseCodeAndState, pollForToken, promptForCode, refreshAccessToken, registerProvider, requestDeviceCode, startCallbackServer };
328
391
  //# sourceMappingURL=index.d.mts.map