@open-vibe-lab/open-sub-auth 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/cli/claude-3WKImhqM.mjs +56 -0
- package/dist/cli/claude-3WKImhqM.mjs.map +1 -0
- package/dist/cli/index.d.mts +2 -0
- package/dist/cli/index.mjs +90 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/cli/login-_HdTW5J_.mjs +33 -0
- package/dist/cli/login-_HdTW5J_.mjs.map +1 -0
- package/dist/cli/logout-CZm-tSVH.mjs +23 -0
- package/dist/cli/logout-CZm-tSVH.mjs.map +1 -0
- package/dist/cli/manager-CKGbp7Yz.mjs +331 -0
- package/dist/cli/manager-CKGbp7Yz.mjs.map +1 -0
- package/dist/cli/oauth-pkce-Bi02-h23.mjs +282 -0
- package/dist/cli/oauth-pkce-Bi02-h23.mjs.map +1 -0
- package/dist/cli/openai-codex-DJi_Q6Zm.mjs +102 -0
- package/dist/cli/openai-codex-DJi_Q6Zm.mjs.map +1 -0
- package/dist/cli/registry-Cp-_Ipc6.mjs +96 -0
- package/dist/cli/registry-Cp-_Ipc6.mjs.map +1 -0
- package/dist/cli/status-C-vkcjVM.mjs +47 -0
- package/dist/cli/status-C-vkcjVM.mjs.map +1 -0
- package/dist/cli/token-DF_-h4Rb.mjs +23 -0
- package/dist/cli/token-DF_-h4Rb.mjs.map +1 -0
- package/dist/cli/ui-CdGEuLwh.mjs +34 -0
- package/dist/cli/ui-CdGEuLwh.mjs.map +1 -0
- package/dist/index.cjs +859 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +328 -0
- package/dist/index.d.mts +328 -0
- package/dist/index.mjs +827 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +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":";;;;;;;;;AAAA,IAAa,mBAAb,cAAsC,MAAM;CAC1C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;AAIhB,IAAa,sBAAb,cAAyC,iBAAiB;CACxD,YACE,SACA,UACA;AACA,QAAM,QAAQ;AAFE,OAAA,WAAA;AAGhB,OAAK,OAAO;;;AAIhB,IAAa,oBAAb,cAAuC,iBAAiB;CACtD,YAAY,UAAkC;AAC5C,QACE,qBAAqB,SAAS,sEAC/B;AAHyB,OAAA,WAAA;AAI1B,OAAK,OAAO;;;AAIhB,IAAa,oBAAb,cAAuC,iBAAiB;CACtD,YACE,UACA,QACA;AACA,QACE,gCAAgC,SAAS,KAAK,kBAAkB,QAAQ,OAAO,UAAU,OAAO,OAAO,GACxG;AALe,OAAA,WAAA;AACA,OAAA,SAAA;AAKhB,OAAK,OAAO;;;AAIhB,IAAa,wBAAb,cAA2C,iBAAiB;CAC1D,YAAY,cAAsC;AAChD,QACE,aAAa,aAAa,8EAC3B;AAHyB,OAAA,eAAA;AAI1B,OAAK,OAAO;;;AAIhB,IAAa,oBAAb,cAAuC,iBAAiB;CACtD,YACE,UACA,WACA;EACA,MAAM,MAAM,YACR,6BAA6B,SAAS,aAAa,UAAU,0BAC7D,6BAA6B,SAAS;AAC1C,QAAM,IAAI;AANM,OAAA,WAAA;AACA,OAAA,YAAA;AAMhB,OAAK,OAAO;;;AAIhB,IAAa,qBAAb,cAAwC,iBAAiB;CACvD,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;AAIhB,IAAa,oBAAb,cAAuC,iBAAiB;CACtD,YAAY,WAAmC;AAC7C,QAAM,+BAA+B,KAAK,MAAM,YAAY,IAAK,CAAC,6BAA6B;AADrE,OAAA,YAAA;AAE1B,OAAK,OAAO;;;AAIhB,IAAa,qBAAb,cAAwC,iBAAiB;CACvD,cAAc;AACZ,QAAM,2EAA2E;AACjF,OAAK,OAAO;;;;;AC1EhB,MAAM,4BAAY,IAAI,KAA6B;;AAGnD,SAAgB,iBAAiB,MAAc,SAA+B;AAC5E,WAAU,IAAI,MAAM,QAAQ;;;AAI9B,SAAgB,YAAY,MAAwB;CAClD,MAAM,UAAU,UAAU,IAAI,KAAK;AACnC,KAAI,CAAC,QACH,OAAM,IAAI,sBAAsB,KAAK;AAEvC,QAAO,SAAS;;;AAIlB,SAAgB,gBAA0B;AACxC,QAAO,MAAM,KAAK,UAAU,MAAM,CAAC;;;;;ACVrC,MAAM,oBAAoB,MAAS;;AAGnC,IAAa,eAAb,MAA0B;CACxB;;CAEA,+BAAgC,IAAI,KAA4B;CAEhE,YAAY,OAAmB;AAC7B,OAAK,QAAQ;;;CAIf,MAAM,MAAM,cAAsB,SAAmD;EACnF,MAAM,WAAW,YAAY,aAAa;EAC1C,MAAM,WAAW,MAAM,SAAS,MAAM,QAAQ;EAE9C,MAAM,YAAY,SAAS,aAAa,SAAS;EACjD,MAAM,eAAe,SAAS,kBAAkB,SAAS;EACzD,MAAM,MAAM,KAAK,KAAK;EAEtB,MAAM,aAA+B;GACnC;GACA,UAAU;IACR,UAAU;IACV;IACA;IACA,WAAW;IACX,iBAAiB;IAClB;GACF;AAED,QAAM,KAAK,MAAM,IAAI,cAAc,WAAW,WAAW;AACzD,SAAO;;;CAIT,MAAM,SAAS,cAAsB,WAAqC;EACxE,MAAM,aAAa,MAAM,KAAK,kBAAkB,cAAc,UAAU;EACxE,MAAM,EAAE,aAAa;AAErB,MAAI,KAAK,aAAa,SAAS,UAAU,CACvC,QAAO,SAAS;AAIlB,MAAI,CAAC,SAAS,aACZ,OAAM,IAAI,kBAAkB,aAAa;AAG3C,QAAM,KAAK,gBAAgB,cAAc,WAAW;EAGpD,MAAM,YAAY,MAAM,KAAK,MAAM,IAAI,cAAc,WAAW,SAAS,UAAU;AACnF,MAAI,CAAC,UACH,OAAM,IAAI,kBAAkB,aAAa;AAE3C,SAAO,UAAU,SAAS;;;CAI5B,MAAM,eAAe,cAAsB,WAA0C;EACnF,MAAM,QAAQ,MAAM,KAAK,SAAS,cAAc,UAAU;AAE1D,SADiB,YAAY,aAAa,CAC1B,eAAe,MAAM;;;CAIvC,MAAM,OAAO,cAAsB,WAAmC;AACpE,MAAI,UACF,OAAM,KAAK,MAAM,OAAO,cAAc,UAAU;OAC3C;GACL,MAAM,cAAc,MAAM,KAAK,MAAM,KAAK,aAAa;AACvD,QAAK,MAAM,QAAQ,YACjB,OAAM,KAAK,MAAM,OAAO,cAAc,KAAK,SAAS,UAAU;;;;CAMpE,MAAM,SAAsC;AAE1C,UADoB,MAAM,KAAK,MAAM,MAAM,EACxB,KAAK,SAAS;GAC/B,IAAI,cAAc,KAAK,SAAS;AAChC,OAAI;AAEF,kBADiB,YAAY,KAAK,SAAS,SAAS,CAC7B,OAAO;WACxB;AAIR,UAAO;IACL,UAAU,KAAK,SAAS;IACxB;IACA,WAAW,KAAK,SAAS;IACzB,cAAc,KAAK,SAAS;IAC5B,WAAW,CAAC,KAAK,aAAa,KAAK,SAAS,UAAU;IACtD,WAAW,KAAK,SAAS;IACzB,iBAAiB,KAAK,SAAS,iBAAiB;IACjD;IACD;;CAGJ,aAAqB,WAA4B;AAC/C,SAAO,KAAK,KAAK,GAAG,oBAAoB;;CAG1C,MAAc,kBACZ,cACA,WAC2B;AAC3B,MAAI,WAAW;GACb,MAAM,aAAa,MAAM,KAAK,MAAM,IAAI,cAAc,UAAU;AAChE,OAAI,CAAC,WACH,OAAM,IAAI,kBAAkB,cAAc,UAAU;AAEtD,UAAO;;EAIT,MAAM,cAAc,MAAM,KAAK,MAAM,KAAK,aAAa;AACvD,MAAI,YAAY,WAAW,EACzB,OAAM,IAAI,kBAAkB,aAAa;AAG3C,SAAO,YAAY,MAAM,GAAG,MAAM,EAAE,SAAS,kBAAkB,EAAE,SAAS,gBAAgB,CAAC;;CAG7F,MAAc,gBAAgB,cAAsB,YAA6C;EAC/F,MAAM,UAAU,GAAG,aAAa,IAAI,WAAW,SAAS;EAGxD,MAAM,eAAe,KAAK,aAAa,IAAI,QAAQ;AACnD,MAAI,cAAc;AAChB,SAAM;AACN;;EAGF,MAAM,iBAAiB,KAAK,UAAU,cAAc,WAAW;AAC/D,OAAK,aAAa,IAAI,SAAS,eAAe;AAE9C,MAAI;AACF,SAAM;YACE;AACR,QAAK,aAAa,OAAO,QAAQ;;;CAIrC,MAAc,UAAU,cAAsB,YAA6C;EACzF,MAAM,WAAW,YAAY,aAAa;EAC1C,MAAM,EAAE,iBAAiB,WAAW;AAEpC,MAAI,CAAC,aACH,OAAM,IAAI,kBAAkB,aAAa;EAG3C,IAAI;AACJ,MAAI;AACF,iBAAc,MAAM,SAAS,QAAQ,aAAa;WAC3C,KAAK;AACZ,SAAM,IAAI,kBAAkB,cAAc,IAAI;;AAIhD,MAAI,CAAC,YAAY,gBAAgB,aAC/B,aAAY,eAAe;EAG7B,MAAM,oBAAsC;GAC1C,UAAU;GACV,UAAU;IACR,GAAG,WAAW;IACd,iBAAiB,KAAK,KAAK;IAC5B;GACF;AAED,QAAM,KAAK,MAAM,IAAI,cAAc,WAAW,SAAS,WAAW,kBAAkB;;;;;ACrLxF,MAAM,YAAY;AAClB,MAAM,aAAa;AACnB,MAAM,YAAY;AAClB,MAAM,cAAc;AACpB,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;;AAcxB,IAAa,YAAb,MAA6C;CAC3C;CAEA,YAAY,UAAmB;AAC7B,OAAK,WACH,aAAA,GAAA,UAAA,MACK,QAAQ,IAAI,uBAAA,GAAA,UAAA,MAA2BA,WAAS,EAAE,iBAAiB,EAAE,mBAAmB;;CAGjG,MAAM,IAAI,UAAkB,WAAqD;EAC/E,MAAM,OAAO,KAAK,UAAU;EAC5B,MAAM,MAAM,GAAG,SAAS,IAAI;EAC5B,MAAM,QAAQ,KAAK,YAAY;AAC/B,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI;GACF,MAAM,YAAY,KAAK,QAAQ,MAAM;AACrC,UAAO,KAAK,MAAM,UAAU;UACtB;AACN,UAAO;;;CAIX,MAAM,IAAI,UAAkB,WAAmB,YAA6C;EAC1F,MAAM,OAAO,KAAK,UAAU;EAC5B,MAAM,MAAM,GAAG,SAAS,IAAI;AAC5B,OAAK,YAAY,OAAO,KAAK,QAAQ,KAAK,UAAU,WAAW,CAAC;AAChE,OAAK,UAAU,KAAK;;CAGtB,MAAM,OAAO,UAAkB,WAAkC;EAC/D,MAAM,OAAO,KAAK,UAAU;EAC5B,MAAM,MAAM,GAAG,SAAS,IAAI;AAC5B,SAAO,KAAK,YAAY;AACxB,OAAK,UAAU,KAAK;;CAGtB,MAAM,KAAK,UAAgD;EACzD,MAAM,OAAO,KAAK,UAAU;EAC5B,MAAM,UAA8B,EAAE;AAEtC,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,YAAY,EAAE;GAC3D,MAAM,CAAC,eAAe,IAAI,MAAM,KAAK;AACrC,OAAI,YAAY,gBAAgB,SAAU;AAE1C,OAAI;IACF,MAAM,YAAY,KAAK,QAAQ,MAAM;AACrC,YAAQ,KAAK,KAAK,MAAM,UAAU,CAAqB;WACjD;;AAKV,SAAO;;CAGT,UAAkB,MAAsB;AAEtC,UAAA,GAAA,YAAA,YADkB,IAAA,GAAA,QAAA,WAAa,CAAC,IAAA,GAAA,QAAA,WAAa,CAAC,YACjB,MAAM,mBAAmB,YAAY,SAAS;;CAG7E,QAAgB,WAAmC;EACjD,MAAM,QAAA,GAAA,YAAA,aAAmB,YAAY;EACrC,MAAM,MAAM,KAAK,UAAU,KAAK;EAChC,MAAM,MAAA,GAAA,YAAA,aAAiB,UAAU;EAEjC,MAAM,UAAA,GAAA,YAAA,gBAAwB,WAAW,KAAK,IAAI,EAChD,eAAe,iBAChB,CAAC;AAOF,SAAO;GACL,MAPgB,OAAO,OAAO;IAC9B,OAAO,OAAO,WAAW,OAAO;IAChC,OAAO,OAAO;IACd,OAAO,YAAY;IACpB,CAAC,CAGgB,SAAS,SAAS;GAClC,IAAI,GAAG,SAAS,SAAS;GACzB,MAAM,KAAK,SAAS,SAAS;GAC9B;;CAGH,QAAgB,OAA+B;EAC7C,MAAM,OAAO,OAAO,KAAK,MAAM,MAAM,SAAS;EAC9C,MAAM,MAAM,KAAK,UAAU,KAAK;EAChC,MAAM,KAAK,OAAO,KAAK,MAAM,IAAI,SAAS;EAC1C,MAAM,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS;EAE7C,MAAM,UAAU,IAAI,SAAS,IAAI,SAAS,gBAAgB;EAC1D,MAAM,aAAa,IAAI,SAAS,GAAG,IAAI,SAAS,gBAAgB;EAEhE,MAAM,YAAA,GAAA,YAAA,kBAA4B,WAAW,KAAK,IAAI,EACpD,eAAe,iBAChB,CAAC;AACF,WAAS,WAAW,QAAQ;AAE5B,SAAO,SAAS,OAAO,WAAW,GAAG,SAAS,MAAM,OAAO;;CAG7D,WAAkC;AAChC,MAAI;GACF,MAAM,WAAA,GAAA,QAAA,cAAuB,KAAK,UAAU,OAAO;AACnD,UAAO,KAAK,MAAM,QAAQ;UACpB;AACN,UAAO;IAAE,SAAS;IAAG,aAAa,EAAE;IAAE;;;CAI1C,UAAkB,MAA2B;AAC3C,GAAA,GAAA,QAAA,YAAA,GAAA,UAAA,SAAkB,KAAK,SAAS,EAAE;GAAE,WAAW;GAAM,MAAM;GAAO,CAAC;AACnE,GAAA,GAAA,QAAA,eAAc,KAAK,UAAU,KAAK,UAAU,MAAM,MAAM,EAAE,EAAE,EAAE,MAAM,KAAO,CAAC;;;AAIhF,SAASA,YAAkB;AACzB,QAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;;;;AC1IxD,MAAM,eAAe;AACrB,MAAM,gBAAgB;;AAGtB,IAAa,gBAAb,MAAiD;CAC/C,WAA2D;CAE3D,MAAc,cAAc;AAC1B,MAAI,CAAC,KAAK,SACR,MAAK,WAAW,MAAM,OAAO;AAE/B,SAAO,KAAK;;CAGd,QAAgB,UAAkB,WAA2B;AAC3D,SAAO,GAAG,SAAS,IAAI;;CAGzB,MAAM,IAAI,UAAkB,WAAqD;EAC/E,MAAM,KAAK,MAAM,KAAK,aAAa;AACnC,MAAI;GACF,MAAM,QAAQ,MAAM,GAAG,YAAY,cAAc,KAAK,QAAQ,UAAU,UAAU,CAAC;AACnF,OAAI,CAAC,MAAO,QAAO;AACnB,UAAO,KAAK,MAAM,MAAM;UAClB;AACN,UAAO;;;CAIX,MAAM,IAAI,UAAkB,WAAmB,YAA6C;EAC1F,MAAM,KAAK,MAAM,KAAK,aAAa;EACnC,MAAM,MAAM,KAAK,QAAQ,UAAU,UAAU;EAC7C,MAAM,QAAQ,KAAK,UAAU,WAAW;AAExC,MAAI;AACF,SAAM,GAAG,eAAe,cAAc,IAAI;UACpC;AAGR,QAAM,GAAG,YAAY,cAAc,KAAK,MAAM;AAC9C,QAAM,KAAK,WAAW,IAAI;;CAG5B,MAAM,OAAO,UAAkB,WAAkC;EAC/D,MAAM,KAAK,MAAM,KAAK,aAAa;EACnC,MAAM,MAAM,KAAK,QAAQ,UAAU,UAAU;AAC7C,MAAI;AACF,SAAM,GAAG,eAAe,cAAc,IAAI;UACpC;AAGR,QAAM,KAAK,gBAAgB,IAAI;;CAGjC,MAAM,KAAK,UAAgD;EACzD,MAAM,QAAQ,MAAM,KAAK,UAAU;EACnC,MAAM,UAA8B,EAAE;AAEtC,OAAK,MAAM,OAAO,OAAO;GACvB,MAAM,CAAC,aAAa,gBAAgB,IAAI,MAAM,KAAK;AACnD,OAAI,YAAY,gBAAgB,SAAU;AAC1C,OAAI,CAAC,eAAe,CAAC,aAAc;GAEnC,MAAM,aAAa,MAAM,KAAK,IAAI,aAAa,aAAa;AAC5D,OAAI,WACF,SAAQ,KAAK,WAAW;;AAI5B,SAAO;;CAGT,MAAc,WAA8B;EAC1C,MAAM,KAAK,MAAM,KAAK,aAAa;AACnC,MAAI;GACF,MAAM,QAAQ,MAAM,GAAG,YAAY,cAAc,cAAc;AAC/D,OAAI,CAAC,MAAO,QAAO,EAAE;AACrB,UAAO,KAAK,MAAM,MAAM;UAClB;AACN,UAAO,EAAE;;;CAIb,MAAc,SAAS,OAAgC;EACrD,MAAM,KAAK,MAAM,KAAK,aAAa;AACnC,MAAI;AACF,SAAM,GAAG,eAAe,cAAc,cAAc;UAC9C;AAGR,QAAM,GAAG,YAAY,cAAc,eAAe,KAAK,UAAU,MAAM,CAAC;;CAG1E,MAAc,WAAW,KAA4B;EACnD,MAAM,QAAQ,MAAM,KAAK,UAAU;AACnC,MAAI,CAAC,MAAM,SAAS,IAAI,EAAE;AACxB,SAAM,KAAK,IAAI;AACf,SAAM,KAAK,SAAS,MAAM;;;CAI9B,MAAc,gBAAgB,KAA4B;EAExD,MAAM,YADQ,MAAM,KAAK,UAAU,EACZ,QAAQ,MAAM,MAAM,IAAI;AAC/C,QAAM,KAAK,SAAS,SAAS;;;;;;;;;AClGjC,eAAsB,iBAAiB,iBAAiB,MAA2B;AACjF,KAAI,eACF,KAAI;EACF,MAAM,QAAQ,IAAI,eAAe;AAEjC,QAAM,MAAM,IAAI,aAAa,YAAY;AACzC,SAAO;SACD;AAIV,QAAO,IAAI,WAAW;;;;;ACfxB,SAAgB,YAAY,KAAmB;CAC7C,MAAM,MAAA,GAAA,QAAA,WAAe;AACrB,KAAI;AACF,MAAI,OAAO,SACT,EAAA,GAAA,mBAAA,UAAS,QAAQ,CAAC,IAAI,CAAC;WACd,OAAO,QAChB,EAAA,GAAA,mBAAA,UAAS,OAAO;GAAC;GAAM;GAAS;GAAI;GAAI,CAAC;MAEzC,EAAA,GAAA,mBAAA,UAAS,YAAY,CAAC,IAAI,CAAC;SAEvB;;;;ACVV,MAAM,eAAe;;;;;;AAOrB,MAAM,aAAa;;;;;;;AAmBnB,SAAgB,oBACd,SACoF;CACpF,MAAM,EAAE,eAAe,UAAU,MAAS,OAAO,gBAAgB;CACjE,MAAM,OAAO,QAAQ,QAAQ;AAE7B,QAAO,IAAI,SAAS,cAAc,iBAAiB;EACjD,IAAI;EACJ,IAAI;EAEJ,MAAM,gBAAgB,IAAI,SAA8B,eAAe,iBAAiB;AACtF,aAAA,GAAA,UAAA,eAAuB,KAAK,QAAQ;IAClC,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,mBAAmB;AAEvD,QAAI,IAAI,aAAa,MAAM;AACzB,SAAI,UAAU,IAAI;AAClB,SAAI,IAAI,YAAY;AACpB;;IAGF,MAAM,OAAO,IAAI,aAAa,IAAI,OAAO;IACzC,MAAM,QAAQ,IAAI,aAAa,IAAI,QAAQ;IAC3C,MAAM,QAAQ,IAAI,aAAa,IAAI,QAAQ;AAE3C,QAAI,OAAO;KACT,MAAM,YAAY,IAAI,aAAa,IAAI,oBAAoB,IAAI;AAC/D,SAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACnD,SAAI,IAAI,WAAW;AACnB,cAAS;AACT,kBAAa,IAAI,mBAAmB,gBAAgB,YAAY,CAAC;AACjE;;AAGF,QAAI,CAAC,MAAM;AACT,SAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACnD,SAAI,IAAI,WAAW;AACnB,cAAS;AACT,kBAAa,IAAI,mBAAmB,oCAAoC,CAAC;AACzE;;AAGF,QAAI,SAAS,UAAU,eAAe;AACpC,SAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACnD,SAAI,IAAI,WAAW;AACnB,cAAS;AACT,kBACE,IAAI,mBAAmB,2DAA2D,CACnF;AACD;;AAGF,QAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACnD,QAAI,IAAI,aAAa;AACrB,aAAS;AACT,kBAAc;KAAE;KAAM,OAAO,SAAS;KAAe,CAAC;KACtD;GAEF,MAAM,gBAAgB;AACpB,QAAI,UAAW,cAAa,UAAU;AACtC,WAAO,OAAO;;AAGhB,eAAY,iBAAiB;AAC3B,aAAS;AACT,iBAAa,IAAI,kBAAkB,QAAQ,CAAC;MAC3C,QAAQ;AAEX,UAAO,GAAG,UAAU,QAAQ;AAC1B,aAAS;AACT,iBAAa,IAAI,mBAAmB,0BAA0B,IAAI,UAAU,CAAC;KAC7E;AAEF,UAAO,OAAO,MAAM,mBAAmB;IACrC,MAAM,OAAO,OAAO,SAAS;AAE7B,iBAAa;KACX,QAAQ;KACR,MAHiB,OAAO,SAAS,YAAY,OAAO,KAAK,OAAO;KAIhE,OAAO;KACR,CAAC;KACF;IACF;GACF;;;;;AC5GJ,SAAgB,uBAA+B;AAC7C,SAAA,GAAA,YAAA,aAAmB,GAAG,CAAC,SAAS,YAAY;;;AAI9C,SAAgB,sBAAsB,UAA0B;AAC9D,SAAA,GAAA,YAAA,YAAkB,SAAS,CAAC,OAAO,SAAS,CAAC,OAAO,YAAY;;;AAIlE,SAAgB,eAA2B;CACzC,MAAM,eAAe,sBAAsB;AAE3C,QAAO;EAAE;EAAc,eADD,sBAAsB,aAAa;EACnB;;;AAIxC,SAAgB,gBAAwB;AACtC,SAAA,GAAA,YAAA,aAAmB,GAAG,CAAC,SAAS,YAAY;;;;;;;;ACd9C,SAAgB,kBAAkB,OAAe,eAA4C;CAC3F,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,CAAC,QACH,OAAM,IAAI,mBAAmB,iCAAiC;CAGhE,MAAM,YAAY,QAAQ,QAAQ,IAAI;AACtC,KAAI,cAAc,IAAI;EACpB,MAAM,OAAO,QAAQ,MAAM,GAAG,UAAU;EACxC,MAAM,QAAQ,QAAQ,MAAM,YAAY,EAAE;AAC1C,MAAI,CAAC,KACH,OAAM,IAAI,mBAAmB,+CAA+C;AAE9E,MAAI,UAAU,cACZ,OAAM,IAAI,oBAAoB;AAEhC,SAAO;GAAE;GAAM;GAAO;;AAIxB,QAAO;EAAE,MAAM;EAAS,OAAO;EAAe;;;AAIhD,SAAgB,cAAc,eAAqD;AACjF,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,MAAA,GAAA,cAAA,iBAAqB;GACzB,OAAO,QAAQ;GACf,QAAQ,QAAQ;GACjB,CAAC;AAEF,KAAG,SAAS,qEAAqE,WAAW;AAC1F,MAAG,OAAO;AACV,OAAI;AACF,YAAQ,kBAAkB,QAAQ,cAAc,CAAC;YAC1C,KAAK;AACZ,WAAO,IAAI;;IAEb;GACF;;;;;ACvCJ,SAAgB,sBACd,QACA,eACA,OACA,aACQ;AACR,KAAI,CAAC,OAAO,sBACV,OAAM,IAAI,mBAAmB,aAAa,OAAO,KAAK,iCAAiC;CAGzF,MAAM,MAAM,IAAI,IAAI,OAAO,sBAAsB;AACjD,KAAI,aAAa,IAAI,iBAAiB,OAAO;AAC7C,KAAI,aAAa,IAAI,aAAa,OAAO,SAAS;AAClD,KAAI,aAAa,IAAI,gBAAgB,YAAY;AACjD,KAAI,aAAa,IAAI,kBAAkB,cAAc;AACrD,KAAI,aAAa,IAAI,yBAAyB,OAAO;AACrD,KAAI,aAAa,IAAI,SAAS,MAAM;AAEpC,KAAI,OAAO,QAAQ,OACjB,KAAI,aAAa,IAAI,SAAS,OAAO,OAAO,KAAK,IAAI,CAAC;AAGxD,QAAO,IAAI,UAAU;;;AAIvB,eAAsB,aACpB,QACA,MACA,cACA,aACA,OACmB;CACnB,MAAM,UAAU,OAAO,oBAAoB;CAE3C,MAAM,SAAiC;EACrC,YAAY;EACZ;EACA,eAAe;EACf,WAAW,OAAO;EAClB,cAAc;EACf;AACD,KAAI,UAAU,KAAA,EACZ,QAAO,QAAQ;CAGjB,MAAM,WAAW,MAAM,MAAM,OAAO,eAAe;EACjD,QAAQ;EACR,SAAS,EAAE,gBAAgB,UAAU,qBAAqB,qCAAqC;EAC/F,MAAM,UAAU,KAAK,UAAU,OAAO,GAAG,IAAI,gBAAgB,OAAO,CAAC,UAAU;EAChF,CAAC;AAEF,KAAI,CAAC,SAAS,IAAI;EAChB,MAAM,YAAY,MAAM,SAAS,MAAM;AACvC,QAAM,IAAI,mBAAmB,0BAA0B,SAAS,OAAO,KAAK,YAAY;;AAI1F,QAAO,mBADO,MAAM,SAAS,MAAM,CACJ;;;AAIjC,eAAsB,mBACpB,QACA,cACmB;CACnB,MAAM,UAAU,OAAO,oBAAoB;CAE3C,MAAM,SAAiC;EACrC,YAAY;EACZ,eAAe;EACf,WAAW,OAAO;EACnB;CAED,MAAM,WAAW,MAAM,MAAM,OAAO,eAAe;EACjD,QAAQ;EACR,SAAS,EAAE,gBAAgB,UAAU,qBAAqB,qCAAqC;EAC/F,MAAM,UAAU,KAAK,UAAU,OAAO,GAAG,IAAI,gBAAgB,OAAO,CAAC,UAAU;EAChF,CAAC;AAEF,KAAI,CAAC,SAAS,IAAI;EAChB,MAAM,YAAY,MAAM,SAAS,MAAM;AACvC,QAAM,IAAI,mBAAmB,yBAAyB,SAAS,OAAO,KAAK,YAAY;;AAIzF,QAAO,mBADO,MAAM,SAAS,MAAM,CACJ;;;AAcjC,eAAsB,gBAAgB,SAA6C;CACjF,MAAM,EAAE,QAAQ,iBAAiB;CACjC,MAAM,EAAE,cAAc,kBAAkB,cAAc;CAEtD,MAAM,QAAQ,OAAO,kBAAkB,eAAe,eAAe;CACrE,MAAM,SAAS,cAAc,UAAU;CAEvC,IAAI;CACJ,IAAI;AAEJ,KAAI,UAAU,QAAQ,mBAAmB;AAEvC,gBAAc,QAAQ;EACtB,MAAM,UAAU,sBAAsB,QAAQ,eAAe,OAAO,YAAY;AAEhF,MAAI,cAAc,cAChB,cAAa,cAAc,QAAQ;MAEnC,aAAY,QAAQ;AAGtB,UAAQ,OAAO,MACb,qEAAqE,QAAQ,MAC9E;AACD,eAAa,MAAM,cAAc,MAAM;QAClC;EAKL,MAAM,SAAS,MAAM,oBAAoB;GACvC,MAJW,cAAc,QAAQ;GAKjC,eAAe;GACf,SALc,cAAc,WAAW;GAMxC,CAAC;AAEF,gBAAc,oBAAoB,OAAO,KAAK;EAC9C,MAAM,UAAU,sBAAsB,QAAQ,eAAe,OAAO,YAAY;AAEhF,MAAI,cAAc,cAChB,cAAa,cAAc,QAAQ;MAEnC,aAAY,QAAQ;AAGtB,UAAQ,OAAO,MACb,qEAAqE,QAAQ,oCAC9E;AAED,MAAI;AACF,gBAAa,MAAM,OAAO;WACnB,KAAK;AACZ,UAAO,OAAO;AACd,SAAM;;;CAKV,MAAM,gBAAgB,OAAO,oBAAoB,SAAS,WAAW,QAAQ,KAAA;AAC7E,QAAO,aAAa,QAAQ,WAAW,MAAM,cAAc,aAAa,cAAc;;AAGxF,SAAS,mBAAmB,MAAyC;CACnE,MAAM,cAAc,KAAK;AACzB,KAAI,CAAC,YACH,OAAM,IAAI,mBAAmB,oCAAoC;CAGnE,MAAM,YAAa,KAAK,cAAqC;CAC7D,MAAM,YAAY,KAAK,KAAK,GAAG,YAAY;AAE3C,QAAO;EACL;EACA,cAAe,KAAK,iBAAwC;EAC5D;EACA,SAAS,KAAK;EACd,YAAa,KAAK,cAAqC,UAAU,aAAa;EAG9E,QAAQ,KAAK,QAAS,KAAK,MAAiB,MAAM,IAAI,GAAG,KAAA;EACzD,KAAK;EACN;;;;ACxLH,MAAM,gBAAgC;CACpC,MAAM;CACN,aAAa;CACb,uBAAuB;CACvB,eAAe;CACf,UAAU;CACV,QAAQ;EAAC;EAAsB;EAAgB;EAAiB;CAChE,WAAW;CAGX,iBAAiB;CACjB,iBAAiB;CAClB;;AAGD,MAAM,sBAAsB;AAE5B,IAAa,iBAAb,MAAgD;CAC9C,SAAkB;CAElB,MAAM,MAAM,SAA2C;AAGrD,SAAO,gBAAgB;GACrB,QAAQ,KAAK;GACb,cAAc;IAAE,GAAG;IAAS,QAAQ;IAAM;GAC1C,mBAAmB;GACpB,CAAC;;CAGJ,MAAM,QAAQ,cAAyC;AACrD,SAAO,mBAAmB,KAAK,QAAQ,aAAa;;CAGtD,eAAe,aAAkC;AAC/C,SAAO;GACL,eAAe,UAAU;GACzB,qBAAqB;GACrB,kBAAkB;GAClB,gBAAgB;GACjB;;CAGH,aAAa,UAA4B;EAEvC,MAAM,SAAS,SAAS,gBAAgB,SAAS;AACjD,UAAA,GAAA,YAAA,YAAkB,SAAS,CAAC,OAAO,OAAO,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG;;CAGvE,gBAAgB,WAAyC;;AAO3D,iBAAiB,gBAAgB,IAAI,gBAAgB,CAAC;;;;;;;AC7CtD,SAAgB,UAAU,OAA0B;CAClD,MAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,KAAI,MAAM,WAAW,EACnB,OAAM,IAAI,MAAM,uCAAuC;CAGzD,MAAM,UAAU,MAAM;AACtB,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,6BAA6B;CAI/C,MAAM,SAAS,QAAQ,QAAQ,MAAM,IAAI,CAAC,QAAQ,MAAM,IAAI;CAC5D,MAAM,UAAU,OAAO,KAAK,QAAQ,SAAS,CAAC,SAAS,OAAO;AAE9D,QAAO,KAAK,MAAM,QAAQ;;;;ACtB5B,MAAM,sBAAsC;CAC1C,MAAM;CACN,aAAa;CACb,uBAAuB;CACvB,eAAe;CACf,UAAU;CACV,aAAa;CACb,QAAQ;EAAC;EAAU;EAAW;EAAS;EAAiB;CACxD,WAAW;CACZ;AAED,IAAa,sBAAb,MAAqD;CACnD,SAAkB;CAElB,MAAM,MAAM,SAA2C;AACrD,SAAO,gBAAgB;GACrB,QAAQ,KAAK;GACb,cAAc;IACZ,GAAG;IACH,MAAM,SAAS,QAAQ;IACxB;GACF,CAAC;;CAGJ,MAAM,QAAQ,cAAyC;AACrD,SAAO,mBAAmB,KAAK,QAAQ,aAAa;;CAGtD,eAAe,aAAkC;AAC/C,SAAO;GACL,eAAe,UAAU;GACzB,gBAAgB;GACjB;;CAGH,aAAa,UAA4B;AACvC,MAAI,SAAS,QACX,KAAI;GACF,MAAM,SAAS,UAAU,SAAS,QAAQ;AAC1C,OAAI,OAAO,IAAK,QAAO,OAAO;UACxB;AAIV,UAAA,GAAA,YAAA,YAAkB,SAAS,CAAC,OAAO,SAAS,YAAY,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG;;CAGrF,gBAAgB,UAAwC;AACtD,MAAI,SAAS,QACX,KAAI;GACF,MAAM,SAAS,UAAU,SAAS,QAAQ;AAC1C,OAAI,OAAO,MAAO,QAAO,OAAO;UAC1B;;;;;;;AAYd,SAAgB,qBAAsC;CACpD,MAAM,QAAQ,CACZ,QAAQ,IAAI,cAAA,GAAA,UAAA,MAAkB,QAAQ,IAAI,YAAY,YAAY,GAAG,OAAA,GAAA,UAAA,OAAA,GAAA,QAAA,UACvD,EAAE,UAAU,YAAY,CACvC,CAAC,OAAO,QAAQ;AAEjB,MAAK,MAAM,YAAY,OAAO;AAC5B,MAAI,EAAA,GAAA,QAAA,YAAY,SAAS,CAAE;AAE3B,MAAI;GACF,MAAM,WAAA,GAAA,QAAA,cAAuB,UAAU,OAAO;GAC9C,MAAM,OAAO,KAAK,MAAM,QAAQ;AAQhC,OAAI,CAAC,KAAK,QAAQ,aAAc;AAEhC,UAAO;IACL,aAAa,KAAK,OAAO;IACzB,cAAc,KAAK,OAAO,iBAAiB;IAC3C,WAAW,KAAK,KAAK,GAAG;IACxB,SAAS,KAAK,OAAO;IACrB,WAAW;IACZ;UACK;AACN;;;AAIJ,QAAO;;AAIT,iBAAiB,sBAAsB,IAAI,qBAAqB,CAAC"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/** OAuth token set returned after successful authentication */
|
|
3
|
+
interface TokenSet {
|
|
4
|
+
accessToken: string;
|
|
5
|
+
refreshToken: string | null;
|
|
6
|
+
/** Unix timestamp in milliseconds when the access token expires */
|
|
7
|
+
expiresAt: number;
|
|
8
|
+
/** JWT id_token (present for OpenAI, contains email/plan info) */
|
|
9
|
+
idToken?: string;
|
|
10
|
+
tokenType: "bearer" | "api-key";
|
|
11
|
+
scopes?: string[];
|
|
12
|
+
/** Raw response from the token endpoint for extensibility */
|
|
13
|
+
raw?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/** Configuration for an OAuth provider */
|
|
16
|
+
interface ProviderConfig {
|
|
17
|
+
/** Unique identifier: "claude", "openai-codex", "github-copilot" */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Human-readable display name */
|
|
20
|
+
displayName: string;
|
|
21
|
+
/** Authorization endpoint URL (for PKCE flow) */
|
|
22
|
+
authorizationEndpoint?: string;
|
|
23
|
+
/** Token exchange endpoint URL */
|
|
24
|
+
tokenEndpoint: string;
|
|
25
|
+
/** OAuth client ID */
|
|
26
|
+
clientId: string;
|
|
27
|
+
/** Redirect URI for callback (for PKCE flow) */
|
|
28
|
+
redirectUri?: string;
|
|
29
|
+
/** OAuth scopes to request */
|
|
30
|
+
scopes?: string[];
|
|
31
|
+
/** Device code endpoint URL (for device code flow) */
|
|
32
|
+
deviceCodeEndpoint?: string;
|
|
33
|
+
/** OAuth grant type */
|
|
34
|
+
grantType: "authorization_code" | "device_code";
|
|
35
|
+
/** Token exchange request body format (default: "form") */
|
|
36
|
+
tokenBodyFormat?: "form" | "json";
|
|
37
|
+
/** Use PKCE verifier as state parameter (non-standard, required by some providers) */
|
|
38
|
+
stateIsVerifier?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** Provider interface that all providers must implement */
|
|
41
|
+
interface Provider {
|
|
42
|
+
readonly config: ProviderConfig;
|
|
43
|
+
/** Run the full interactive login flow */
|
|
44
|
+
login(options?: LoginOptions): Promise<TokenSet>;
|
|
45
|
+
/** Refresh an expired access token */
|
|
46
|
+
refresh(refreshToken: string): Promise<TokenSet>;
|
|
47
|
+
/** Build auth headers for making API calls */
|
|
48
|
+
getAuthHeaders(accessToken: string): AuthHeaders;
|
|
49
|
+
/** Extract a stable account identifier from tokens */
|
|
50
|
+
getAccountId(tokenSet: TokenSet): string;
|
|
51
|
+
/** Optional: human-readable account label (e.g., email) */
|
|
52
|
+
getAccountLabel?(tokenSet: TokenSet): string | undefined;
|
|
53
|
+
}
|
|
54
|
+
/** Options for the login flow */
|
|
55
|
+
interface LoginOptions {
|
|
56
|
+
/** Override port for the local callback server */
|
|
57
|
+
port?: number;
|
|
58
|
+
/** Timeout in ms for the entire login flow (default: 120000) */
|
|
59
|
+
timeout?: number;
|
|
60
|
+
/** Use manual code input mode instead of local callback server */
|
|
61
|
+
manual?: boolean;
|
|
62
|
+
/** Callback when the browser should be opened */
|
|
63
|
+
onOpenBrowser?: (url: string) => void;
|
|
64
|
+
/** Callback for device code flow: display code to user */
|
|
65
|
+
onDeviceCode?: (code: DeviceCodeInfo) => void;
|
|
66
|
+
}
|
|
67
|
+
/** Device code information displayed to the user */
|
|
68
|
+
interface DeviceCodeInfo {
|
|
69
|
+
userCode: string;
|
|
70
|
+
verificationUri: string;
|
|
71
|
+
expiresIn: number;
|
|
72
|
+
interval: number;
|
|
73
|
+
}
|
|
74
|
+
/** HTTP headers for authenticated API calls */
|
|
75
|
+
type AuthHeaders = Record<string, string>;
|
|
76
|
+
/** Stored credential with metadata */
|
|
77
|
+
interface StoredCredential {
|
|
78
|
+
tokenSet: TokenSet;
|
|
79
|
+
metadata: TokenMetadata;
|
|
80
|
+
}
|
|
81
|
+
/** Metadata about a stored credential */
|
|
82
|
+
interface TokenMetadata {
|
|
83
|
+
provider: string;
|
|
84
|
+
accountId: string;
|
|
85
|
+
accountLabel?: string;
|
|
86
|
+
createdAt: number;
|
|
87
|
+
lastRefreshedAt: number;
|
|
88
|
+
}
|
|
89
|
+
/** Token storage interface */
|
|
90
|
+
interface TokenStore {
|
|
91
|
+
get(provider: string, accountId: string): Promise<StoredCredential | null>;
|
|
92
|
+
set(provider: string, accountId: string, credential: StoredCredential): Promise<void>;
|
|
93
|
+
delete(provider: string, accountId: string): Promise<void>;
|
|
94
|
+
list(provider?: string): Promise<StoredCredential[]>;
|
|
95
|
+
}
|
|
96
|
+
/** Status of a stored credential */
|
|
97
|
+
interface CredentialStatus {
|
|
98
|
+
provider: string;
|
|
99
|
+
displayName: string;
|
|
100
|
+
accountId: string;
|
|
101
|
+
accountLabel?: string;
|
|
102
|
+
isExpired: boolean;
|
|
103
|
+
expiresAt: number;
|
|
104
|
+
hasRefreshToken: boolean;
|
|
105
|
+
}
|
|
106
|
+
/** PKCE parameters for OAuth flow */
|
|
107
|
+
interface PKCEParams {
|
|
108
|
+
codeVerifier: string;
|
|
109
|
+
codeChallenge: string;
|
|
110
|
+
}
|
|
111
|
+
/** Result from the callback server or manual code input */
|
|
112
|
+
interface AuthorizationResult {
|
|
113
|
+
code: string;
|
|
114
|
+
state: string;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/errors.d.ts
|
|
118
|
+
declare class OpenSubAuthError extends Error {
|
|
119
|
+
constructor(message: string);
|
|
120
|
+
}
|
|
121
|
+
declare class AuthenticationError extends OpenSubAuthError {
|
|
122
|
+
readonly provider?: string | undefined;
|
|
123
|
+
constructor(message: string, provider?: string | undefined);
|
|
124
|
+
}
|
|
125
|
+
declare class TokenExpiredError extends OpenSubAuthError {
|
|
126
|
+
readonly provider: string;
|
|
127
|
+
constructor(provider: string);
|
|
128
|
+
}
|
|
129
|
+
declare class TokenRefreshError extends OpenSubAuthError {
|
|
130
|
+
readonly provider: string;
|
|
131
|
+
readonly cause_: unknown;
|
|
132
|
+
constructor(provider: string, cause_: unknown);
|
|
133
|
+
}
|
|
134
|
+
declare class ProviderNotFoundError extends OpenSubAuthError {
|
|
135
|
+
readonly providerName: string;
|
|
136
|
+
constructor(providerName: string);
|
|
137
|
+
}
|
|
138
|
+
declare class NoCredentialError extends OpenSubAuthError {
|
|
139
|
+
readonly provider: string;
|
|
140
|
+
readonly accountId?: string | undefined;
|
|
141
|
+
constructor(provider: string, accountId?: string | undefined);
|
|
142
|
+
}
|
|
143
|
+
declare class OAuthCallbackError extends OpenSubAuthError {
|
|
144
|
+
constructor(message: string);
|
|
145
|
+
}
|
|
146
|
+
declare class OAuthTimeoutError extends OpenSubAuthError {
|
|
147
|
+
readonly timeoutMs: number;
|
|
148
|
+
constructor(timeoutMs: number);
|
|
149
|
+
}
|
|
150
|
+
declare class StateMismatchError extends OpenSubAuthError {
|
|
151
|
+
constructor();
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/token/manager.d.ts
|
|
155
|
+
/** Central token lifecycle manager */
|
|
156
|
+
declare class TokenManager {
|
|
157
|
+
private readonly store;
|
|
158
|
+
/** Per-provider+account mutex to prevent concurrent refreshes */
|
|
159
|
+
private readonly refreshLocks;
|
|
160
|
+
constructor(store: TokenStore);
|
|
161
|
+
/** Run the interactive login flow for a provider, store the tokens */
|
|
162
|
+
login(providerName: string, options?: LoginOptions): Promise<StoredCredential>;
|
|
163
|
+
/** Get a valid access token, refreshing if needed */
|
|
164
|
+
getToken(providerName: string, accountId?: string): Promise<string>;
|
|
165
|
+
/** Get authenticated headers for API calls */
|
|
166
|
+
getAuthHeaders(providerName: string, accountId?: string): Promise<AuthHeaders>;
|
|
167
|
+
/** Remove stored tokens for a provider */
|
|
168
|
+
logout(providerName: string, accountId?: string): Promise<void>;
|
|
169
|
+
/** Get status of all stored credentials */
|
|
170
|
+
status(): Promise<CredentialStatus[]>;
|
|
171
|
+
private isTokenValid;
|
|
172
|
+
private resolveCredential;
|
|
173
|
+
private refreshWithLock;
|
|
174
|
+
private doRefresh;
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/storage/file-store.d.ts
|
|
178
|
+
/** Token store backed by an encrypted JSON file */
|
|
179
|
+
declare class FileStore implements TokenStore {
|
|
180
|
+
private readonly filePath;
|
|
181
|
+
constructor(filePath?: string);
|
|
182
|
+
get(provider: string, accountId: string): Promise<StoredCredential | null>;
|
|
183
|
+
set(provider: string, accountId: string, credential: StoredCredential): Promise<void>;
|
|
184
|
+
delete(provider: string, accountId: string): Promise<void>;
|
|
185
|
+
list(provider?: string): Promise<StoredCredential[]>;
|
|
186
|
+
private deriveKey;
|
|
187
|
+
private encrypt;
|
|
188
|
+
private decrypt;
|
|
189
|
+
private readFile;
|
|
190
|
+
private writeFile;
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/storage/keychain-store.d.ts
|
|
194
|
+
/** Token store backed by the OS keychain via cross-keychain */
|
|
195
|
+
declare class KeychainStore implements TokenStore {
|
|
196
|
+
private keychain;
|
|
197
|
+
private getKeychain;
|
|
198
|
+
private makeKey;
|
|
199
|
+
get(provider: string, accountId: string): Promise<StoredCredential | null>;
|
|
200
|
+
set(provider: string, accountId: string, credential: StoredCredential): Promise<void>;
|
|
201
|
+
delete(provider: string, accountId: string): Promise<void>;
|
|
202
|
+
list(provider?: string): Promise<StoredCredential[]>;
|
|
203
|
+
private getIndex;
|
|
204
|
+
private setIndex;
|
|
205
|
+
private addToIndex;
|
|
206
|
+
private removeFromIndex;
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/storage/store.d.ts
|
|
210
|
+
/**
|
|
211
|
+
* Create a token store, preferring the OS keychain with encrypted file fallback.
|
|
212
|
+
* @param preferKeychain - If true (default), try keychain first
|
|
213
|
+
*/
|
|
214
|
+
declare function createTokenStore(preferKeychain?: boolean): Promise<TokenStore>;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/providers/registry.d.ts
|
|
217
|
+
/** Register a provider factory */
|
|
218
|
+
declare function registerProvider(name: string, factory: () => Provider): void;
|
|
219
|
+
/** Get a provider instance by name */
|
|
220
|
+
declare function getProvider(name: string): Provider;
|
|
221
|
+
/** List all registered provider names */
|
|
222
|
+
declare function listProviders(): string[];
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/providers/claude.d.ts
|
|
225
|
+
declare class ClaudeProvider implements Provider {
|
|
226
|
+
readonly config: ProviderConfig;
|
|
227
|
+
login(options?: LoginOptions): Promise<TokenSet>;
|
|
228
|
+
refresh(refreshToken: string): Promise<TokenSet>;
|
|
229
|
+
getAuthHeaders(accessToken: string): AuthHeaders;
|
|
230
|
+
getAccountId(tokenSet: TokenSet): string;
|
|
231
|
+
getAccountLabel(_tokenSet: TokenSet): string | undefined;
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/providers/openai-codex.d.ts
|
|
235
|
+
declare class OpenAICodexProvider implements Provider {
|
|
236
|
+
readonly config: ProviderConfig;
|
|
237
|
+
login(options?: LoginOptions): Promise<TokenSet>;
|
|
238
|
+
refresh(refreshToken: string): Promise<TokenSet>;
|
|
239
|
+
getAuthHeaders(accessToken: string): AuthHeaders;
|
|
240
|
+
getAccountId(tokenSet: TokenSet): string;
|
|
241
|
+
getAccountLabel(tokenSet: TokenSet): string | undefined;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Import tokens from an existing Codex CLI installation.
|
|
245
|
+
* Reads from ~/.codex/auth.json or $CODEX_HOME/auth.json.
|
|
246
|
+
*/
|
|
247
|
+
declare function importFromCodexCli(): TokenSet | null;
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region src/core/crypto.d.ts
|
|
250
|
+
/** Generate a cryptographically random PKCE code verifier (43-128 chars, base64url) */
|
|
251
|
+
declare function generateCodeVerifier(): string;
|
|
252
|
+
/** Generate a PKCE code challenge from a verifier using S256 method */
|
|
253
|
+
declare function generateCodeChallenge(verifier: string): string;
|
|
254
|
+
/** Generate both PKCE code verifier and challenge */
|
|
255
|
+
declare function generatePKCE(): PKCEParams;
|
|
256
|
+
/** Generate a random state parameter for CSRF protection */
|
|
257
|
+
declare function generateState(): string;
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/core/oauth-pkce.d.ts
|
|
260
|
+
/** Build the full OAuth authorization URL with PKCE and state params */
|
|
261
|
+
declare function buildAuthorizationUrl(config: ProviderConfig, codeChallenge: string, state: string, redirectUri: string): string;
|
|
262
|
+
/** Exchange an authorization code for tokens */
|
|
263
|
+
declare function exchangeCode(config: ProviderConfig, code: string, codeVerifier: string, redirectUri: string, state?: string): Promise<TokenSet>;
|
|
264
|
+
/** Refresh an access token using a refresh token */
|
|
265
|
+
declare function refreshAccessToken(config: ProviderConfig, refreshToken: string): Promise<TokenSet>;
|
|
266
|
+
interface PKCEFlowOptions {
|
|
267
|
+
config: ProviderConfig;
|
|
268
|
+
loginOptions?: LoginOptions;
|
|
269
|
+
/**
|
|
270
|
+
* Manual mode redirect URI (used when manual=true).
|
|
271
|
+
* If not provided, manual mode is not available.
|
|
272
|
+
*/
|
|
273
|
+
manualRedirectUri?: string;
|
|
274
|
+
}
|
|
275
|
+
/** Execute the full PKCE flow: generate params, open browser, wait for callback, exchange code */
|
|
276
|
+
declare function executePKCEFlow(options: PKCEFlowOptions): Promise<TokenSet>;
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/core/callback-server.d.ts
|
|
279
|
+
interface CallbackServerOptions {
|
|
280
|
+
/** Port to listen on (0 = random available port) */
|
|
281
|
+
port?: number;
|
|
282
|
+
/** Expected state parameter for CSRF validation */
|
|
283
|
+
expectedState: string;
|
|
284
|
+
/** Timeout in milliseconds (default: 120000) */
|
|
285
|
+
timeout?: number;
|
|
286
|
+
/** Path to listen on (default: "/callback") */
|
|
287
|
+
path?: string;
|
|
288
|
+
}
|
|
289
|
+
/** Start a local HTTP server to receive the OAuth callback redirect */
|
|
290
|
+
declare function startCallbackServer(options: CallbackServerOptions): Promise<{
|
|
291
|
+
result: Promise<AuthorizationResult>;
|
|
292
|
+
port: number;
|
|
293
|
+
close: () => void;
|
|
294
|
+
}>;
|
|
295
|
+
//#endregion
|
|
296
|
+
//#region src/core/manual-code-input.d.ts
|
|
297
|
+
/**
|
|
298
|
+
* Parse a "code#state" string (Anthropic's manual paste format).
|
|
299
|
+
* Also supports a plain authorization code (state validated separately).
|
|
300
|
+
*/
|
|
301
|
+
declare function parseCodeAndState(input: string, expectedState: string): AuthorizationResult;
|
|
302
|
+
/** Prompt the user to paste the authorization code from their browser */
|
|
303
|
+
declare function promptForCode(expectedState: string): Promise<AuthorizationResult>;
|
|
304
|
+
//#endregion
|
|
305
|
+
//#region src/core/browser.d.ts
|
|
306
|
+
/** Open a URL in the user's default browser. Does not throw on failure. */
|
|
307
|
+
declare function openBrowser(url: string): void;
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/token/jwt.d.ts
|
|
310
|
+
/** Decoded JWT claims (no signature verification) */
|
|
311
|
+
interface JWTClaims {
|
|
312
|
+
sub?: string;
|
|
313
|
+
email?: string;
|
|
314
|
+
name?: string;
|
|
315
|
+
iss?: string;
|
|
316
|
+
aud?: string | string[];
|
|
317
|
+
exp?: number;
|
|
318
|
+
iat?: number;
|
|
319
|
+
[key: string]: unknown;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Decode a JWT token's payload without verifying the signature.
|
|
323
|
+
* Used to extract user info from id_tokens (e.g., OpenAI).
|
|
324
|
+
*/
|
|
325
|
+
declare function decodeJWT(token: string): JWTClaims;
|
|
326
|
+
//#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 };
|
|
328
|
+
//# sourceMappingURL=index.d.cts.map
|