@happyvertical/auth 0.103.4 → 0.103.6

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/dist/local.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"local.js","names":[],"sources":["../src/local.ts"],"sourcesContent":["/** Node-only local Sign in with ChatGPT session runtime. */\nimport { createHash, randomBytes, randomUUID } from 'node:crypto';\nimport { chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { createServer } from 'node:http';\nimport { tmpdir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\n\nconst AUTHORIZE = 'https://auth.openai.com/api/accounts/authorize';\nconst TOKEN = 'https://auth.openai.com/api/accounts/oauth/token';\nconst DISCOVERY = 'https://auth.openai.com/.well-known/openid-configuration';\nconst RESOURCE = 'https://api.openai.com/v1';\nconst PLAN_SCOPE = 'chatgpt.tokens.use.direct';\nconst SCOPES = [\n 'openid',\n 'profile',\n 'email',\n 'offline_access',\n 'resource.invoke',\n PLAN_SCOPE,\n];\n\nexport class ChatGPTSessionError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = 'ChatGPTSessionError';\n }\n}\nexport interface ChatGPTSession {\n subject: string;\n email?: string;\n clientId: string;\n hostId: string;\n idToken: string;\n accessToken: string;\n refreshToken: string;\n scopes: string[];\n expiresAt: number;\n}\nexport interface LocalChatGPTOptions {\n appName: string;\n path?: string;\n openBrowser?: (url: string) => Promise<void> | void;\n fetch?: typeof fetch;\n now?: () => number;\n}\ninterface Disk {\n hostId: string;\n sessions: ChatGPTSession[];\n generations?: Record<string, number>;\n refreshes?: Record<string, { id: string; pid: number }>;\n}\ninterface Pending {\n state: string;\n nonce: string;\n verifier: string;\n redirectUri: string;\n clientId: string;\n selected?: ChatGPTSession;\n generation: number;\n server: ReturnType<typeof createServer>;\n}\n\nfunction b64(bytes: Uint8Array) {\n return Buffer.from(bytes).toString('base64url');\n}\nfunction random() {\n return b64(randomBytes(32));\n}\nfunction pathFor(options: LocalChatGPTOptions) {\n return (\n options.path ??\n join(\n process.env.XDG_CONFIG_HOME ??\n join(process.env.HOME ?? tmpdir(), '.config'),\n options.appName,\n 'chatgpt.json',\n )\n );\n}\nfunction required(value: string | null, label: string) {\n if (!value)\n throw new ChatGPTSessionError(\n 'invalid_callback',\n `Missing ${label} in OAuth callback`,\n );\n return value;\n}\n\n/**\n * Stores credentials only in an owner-readable local file. Callers should pass\n * a per-application config path; no credential is ever written to a project.\n */\nexport class LocalChatGPTSessionManager {\n private pending = new Map<string, Pending>();\n private refreshing = new Map<string, Promise<ChatGPTSession>>();\n private readonly file: string;\n private hostId?: string;\n private readonly fetcher: typeof fetch;\n private readonly now: () => number;\n private readonly remoteRevocationTimeoutMs = 5_000;\n constructor(private readonly options: LocalChatGPTOptions) {\n this.file = pathFor(options);\n this.fetcher = options.fetch ?? fetch;\n this.now = options.now ?? Date.now;\n }\n private async disk(): Promise<Disk> {\n try {\n const disk = JSON.parse(await readFile(this.file, 'utf8')) as Disk;\n this.hostId = disk.hostId;\n return disk;\n } catch (e: any) {\n if (e?.code === 'ENOENT') {\n if (!this.hostId) this.hostId = `urn:uuid:${randomUUID()}`;\n return {\n hostId: this.hostId,\n sessions: [],\n };\n }\n throw e;\n }\n }\n private generation(disk: Disk, clientId: string) {\n return disk.generations?.[clientId] ?? 0;\n }\n private processIsAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error: any) {\n return error?.code !== 'ESRCH';\n }\n }\n private async locked<T>(operation: () => Promise<T>): Promise<T> {\n const mutex = `${this.file}.mutex.sqlite`;\n await mkdir(dirname(this.file), { recursive: true, mode: 0o700 });\n for (let attempt = 0; attempt < 200; attempt++) {\n const database = new DatabaseSync(mutex, { timeout: 0 });\n let busy = false;\n try {\n await chmod(mutex, 0o600);\n try {\n database.exec('BEGIN IMMEDIATE');\n } catch (error: any) {\n if (\n error?.code === 'ERR_SQLITE_ERROR' &&\n typeof error?.errcode === 'number' &&\n (error.errcode & 0xff) === 5\n )\n busy = true;\n else throw error;\n }\n if (!busy) {\n try {\n return await operation();\n } finally {\n database.exec('ROLLBACK');\n }\n }\n } finally {\n database.close();\n }\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n throw new ChatGPTSessionError('storage_busy', 'Credential storage is busy');\n }\n private async save(disk: Disk) {\n await mkdir(dirname(this.file), { recursive: true, mode: 0o700 });\n const temp = `${this.file}.${randomUUID()}.tmp`;\n await writeFile(temp, JSON.stringify(disk), { mode: 0o600 });\n await chmod(temp, 0o600);\n await rename(temp, this.file);\n await chmod(this.file, 0o600);\n }\n async sessions(): Promise<\n Array<Omit<ChatGPTSession, 'accessToken' | 'refreshToken' | 'idToken'>>\n > {\n return (await this.disk()).sessions.map(\n ({ accessToken, refreshToken, idToken, ...safe }) => safe,\n );\n }\n async begin(\n selectedClientId?: string,\n ): Promise<{ authorizationUrl: string; callback: Promise<ChatGPTSession> }> {\n const disk = await this.locked(async () => {\n const current = await this.disk();\n await this.save(current);\n return current;\n });\n const selected = selectedClientId\n ? disk.sessions.find((s) => s.clientId === selectedClientId)\n : undefined;\n if (selectedClientId && !selected)\n throw new ChatGPTSessionError(\n 'unknown_account',\n 'Selected ChatGPT account is not stored locally',\n );\n const state = random(),\n nonce = random(),\n verifier = random();\n const challenge = b64(createHash('sha256').update(verifier).digest());\n let resolve!: (value: ChatGPTSession) => void,\n reject!: (reason: Error) => void;\n const callback = new Promise<ChatGPTSession>((a, b) => {\n resolve = a;\n reject = b;\n });\n let redirectUri = '';\n const server = createServer((req, res) => {\n let url: URL;\n try {\n url = new URL(req.url ?? '/', redirectUri);\n } catch {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Invalid sign-in callback.');\n return;\n }\n if (!this.pending.has(state)) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Sign-in callback was already consumed.');\n return;\n }\n const finish = async () => {\n try {\n const result = await this.complete(url, state);\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end('Sign-in complete. You may close this window.');\n resolve(result);\n } catch (error) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Sign-in failed. Return to the application.');\n reject(error as Error);\n } finally {\n server.close();\n this.pending.delete(state);\n }\n };\n void finish();\n });\n await new Promise<void>((resolveListen, rejectListen) => {\n server.once('error', rejectListen);\n server.listen(0, '127.0.0.1', () => resolveListen());\n });\n const address = server.address();\n if (!address || typeof address === 'string')\n throw new ChatGPTSessionError(\n 'loopback_unavailable',\n 'Could not bind 127.0.0.1 loopback callback',\n );\n redirectUri = `http://127.0.0.1:${address.port}/auth/callback`;\n const clientId = selected?.clientId ?? 'dynamic_agent_client';\n this.pending.set(state, {\n state,\n nonce,\n verifier,\n redirectUri,\n clientId,\n selected,\n generation: selected ? this.generation(disk, selected.clientId) : 0,\n server,\n });\n const params = new URLSearchParams({\n client_id: clientId,\n response_type: 'code',\n redirect_uri: redirectUri,\n scope: SCOPES.join(' '),\n resource: RESOURCE,\n state,\n nonce,\n code_challenge_method: 'S256',\n code_challenge: challenge,\n ext_agent_host_id: disk.hostId,\n });\n if (selected) {\n params.set('id_token_hint', selected.idToken);\n if (selected.email) params.set('login_hint', selected.email);\n } else params.set('agent_name_hint', this.options.appName);\n const authorizationUrl = `${AUTHORIZE}?${params}`;\n try {\n await this.options.openBrowser?.(authorizationUrl);\n } catch (error) {\n this.pending.delete(state);\n await new Promise<void>((resolveClose) =>\n server.close(() => resolveClose()),\n );\n throw error;\n }\n return { authorizationUrl, callback };\n }\n private async complete(\n url: URL,\n expectedState: string,\n ): Promise<ChatGPTSession> {\n const pending = this.pending.get(expectedState);\n if (!pending || url.searchParams.get('state') !== expectedState)\n throw new ChatGPTSessionError(\n 'state_mismatch',\n 'Rejected OAuth callback with unbound state',\n );\n if (url.searchParams.get('error'))\n throw new ChatGPTSessionError(\n 'consent_declined',\n 'ChatGPT authorization was declined',\n );\n this.pending.delete(expectedState);\n const code = required(url.searchParams.get('code'), 'code');\n const callbackClientId = url.searchParams.get('client_id');\n const clientId = pending.selected\n ? pending.clientId\n : required(callbackClientId, 'issued client_id');\n if (!pending.selected && clientId === 'dynamic_agent_client')\n throw new ChatGPTSessionError(\n 'registration_incomplete',\n 'OAuth registration did not return an issued client id',\n );\n if (pending.selected && callbackClientId && callbackClientId !== clientId)\n throw new ChatGPTSessionError(\n 'client_mismatch',\n 'OAuth callback changed the registered client',\n );\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: clientId,\n code,\n code_verifier: pending.verifier,\n redirect_uri: pending.redirectUri,\n resource: RESOURCE,\n });\n const response = await this.fetcher(TOKEN, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body,\n });\n if (!response.ok)\n throw new ChatGPTSessionError(\n 'token_exchange_failed',\n 'OAuth token exchange failed',\n );\n const token: any = await response.json();\n const identity = await this.verify(token.id_token, clientId, pending.nonce);\n const scopes = String(token.scope ?? '')\n .split(' ')\n .filter(Boolean);\n if (!scopes.includes(PLAN_SCOPE))\n throw new ChatGPTSessionError(\n 'plan_permission_missing',\n 'ChatGPT plan usage permission was not granted',\n );\n const session: ChatGPTSession = {\n subject: identity.sub,\n email: identity.email,\n clientId,\n hostId: (await this.disk()).hostId,\n idToken: token.id_token,\n accessToken: token.access_token,\n refreshToken: token.refresh_token,\n scopes,\n expiresAt: this.now() + Number(token.expires_in ?? 0) * 1000,\n };\n if (!session.accessToken || !session.refreshToken)\n throw new ChatGPTSessionError(\n 'invalid_token_response',\n 'OAuth response omitted renewable credentials',\n );\n if (pending.selected && session.subject !== pending.selected.subject)\n throw new ChatGPTSessionError(\n 'account_mismatch',\n 'OAuth identity did not match the selected account',\n );\n await this.locked(async () => {\n const disk = await this.disk();\n if (\n pending.selected &&\n this.generation(disk, clientId) !== pending.generation\n )\n throw new ChatGPTSessionError(\n 'session_invalidated',\n 'ChatGPT session changed while authorization was in progress',\n );\n disk.sessions = [\n ...disk.sessions.filter((s) => s.clientId !== clientId),\n session,\n ];\n disk.generations = {\n ...disk.generations,\n [clientId]: this.generation(disk, clientId) + 1,\n };\n if (disk.refreshes) delete disk.refreshes[clientId];\n await this.save(disk);\n });\n return session;\n }\n private async verify(\n token: string,\n clientId: string,\n nonce: string,\n ): Promise<{ sub: string; email?: string }> {\n if (!token)\n throw new ChatGPTSessionError(\n 'invalid_token_response',\n 'OAuth response omitted id_token',\n );\n const config = (await (await this.fetcher(DISCOVERY)).json()) as {\n jwks_uri?: string;\n };\n if (!config.jwks_uri)\n throw new ChatGPTSessionError(\n 'discovery_failed',\n 'OpenID discovery did not supply JWKS',\n );\n const result = await jwtVerify(\n token,\n createRemoteJWKSet(new URL(config.jwks_uri)),\n { issuer: 'https://auth.openai.com', audience: clientId },\n );\n if (\n result.payload.nonce !== nonce ||\n typeof result.payload.sub !== 'string' ||\n typeof result.payload.exp !== 'number' ||\n typeof result.payload.iat !== 'number'\n )\n throw new ChatGPTSessionError(\n 'invalid_identity',\n 'OIDC token nonce or subject is invalid',\n );\n return {\n sub: result.payload.sub,\n email:\n typeof result.payload.email === 'string'\n ? result.payload.email\n : undefined,\n };\n }\n /** Return a currently usable token, serializing rotating-token refreshes per registration. */\n async accessToken(clientId: string): Promise<string> {\n const session = (await this.disk()).sessions.find(\n (s) => s.clientId === clientId,\n );\n if (!session)\n throw new ChatGPTSessionError(\n 'unknown_account',\n 'Selected ChatGPT account is not stored locally',\n );\n if (session.expiresAt > this.now() + 60_000) return session.accessToken;\n return (await this.refresh(clientId)).accessToken;\n }\n async refresh(clientId: string): Promise<ChatGPTSession> {\n const active = this.refreshing.get(clientId);\n if (active) return active;\n const refresh = this.doRefresh(clientId).finally(() =>\n this.refreshing.delete(clientId),\n );\n this.refreshing.set(clientId, refresh);\n return refresh;\n }\n private async doRefresh(clientId: string): Promise<ChatGPTSession> {\n const leaseId = randomUUID();\n let observedRefreshToken: string | undefined;\n let current: ChatGPTSession | undefined;\n let generation = 0;\n for (let attempt = 0; attempt < 200; attempt++) {\n const claim = await this.locked(async () => {\n const disk = await this.disk();\n const session = disk.sessions.find((s) => s.clientId === clientId);\n if (!session)\n throw new ChatGPTSessionError(\n 'unknown_account',\n 'Selected ChatGPT account is not stored locally',\n );\n observedRefreshToken ??= session.refreshToken;\n if (session.refreshToken !== observedRefreshToken)\n return { kind: 'updated' as const, session };\n const active = disk.refreshes?.[clientId];\n if (active && this.processIsAlive(active.pid))\n return { kind: 'wait' as const };\n disk.refreshes = {\n ...disk.refreshes,\n [clientId]: { id: leaseId, pid: process.pid },\n };\n await this.save(disk);\n return {\n kind: 'owned' as const,\n session,\n generation: this.generation(disk, clientId),\n };\n });\n if (claim.kind === 'updated') return claim.session;\n if (claim.kind === 'owned') {\n current = claim.session;\n generation = claim.generation;\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n if (!current)\n throw new ChatGPTSessionError(\n 'storage_busy',\n 'Credential refresh is already in progress',\n );\n\n const releaseLease = async () => {\n await this.locked(async () => {\n const disk = await this.disk();\n if (disk.refreshes?.[clientId]?.id === leaseId) {\n delete disk.refreshes[clientId];\n await this.save(disk);\n }\n });\n };\n const invalidate = async (code: string, message: string) => {\n await this.locked(async () => {\n const disk = await this.disk();\n if (\n disk.refreshes?.[clientId]?.id === leaseId &&\n this.generation(disk, clientId) === generation &&\n disk.sessions.find((session) => session.clientId === clientId)\n ?.refreshToken === current.refreshToken\n ) {\n disk.sessions = disk.sessions.filter((s) => s.clientId !== clientId);\n disk.generations = {\n ...disk.generations,\n [clientId]: generation + 1,\n };\n delete disk.refreshes[clientId];\n await this.save(disk);\n }\n });\n throw new ChatGPTSessionError(code, message);\n };\n\n let response: Response;\n try {\n response = await this.fetcher(TOKEN, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: current.clientId,\n refresh_token: current.refreshToken,\n resource: RESOURCE,\n }),\n });\n } catch (error) {\n await releaseLease();\n throw error;\n }\n if (!response.ok) {\n if (response.status === 400 || response.status === 401)\n return invalidate(\n 'refresh_invalid',\n 'Saved ChatGPT session is no longer renewable; sign in again',\n );\n await releaseLease();\n throw new ChatGPTSessionError(\n 'refresh_failed',\n 'ChatGPT session refresh failed; credentials were retained',\n );\n }\n let token: any;\n try {\n token = await response.json();\n } catch (error) {\n await releaseLease();\n throw error;\n }\n if (!token.access_token || !token.refresh_token) {\n await releaseLease();\n throw new ChatGPTSessionError(\n 'invalid_refresh_response',\n 'Refresh response omitted replacement credentials',\n );\n }\n const scopes = String(token.scope ?? current.scopes.join(' '))\n .split(' ')\n .filter(Boolean);\n if (!scopes.includes(PLAN_SCOPE))\n return invalidate(\n 'plan_permission_missing',\n 'ChatGPT plan usage permission was not granted',\n );\n const updated: ChatGPTSession = {\n ...current,\n accessToken: token.access_token,\n refreshToken: token.refresh_token,\n idToken: token.id_token ?? current.idToken,\n scopes,\n expiresAt: this.now() + Number(token.expires_in ?? 0) * 1000,\n };\n return this.locked(async () => {\n const disk = await this.disk();\n const stored = disk.sessions.find((s) => s.clientId === clientId);\n const ownsLease = disk.refreshes?.[clientId]?.id === leaseId;\n if (\n !ownsLease ||\n this.generation(disk, clientId) !== generation ||\n stored?.refreshToken !== current.refreshToken\n ) {\n if (ownsLease) {\n delete disk.refreshes![clientId];\n await this.save(disk);\n }\n throw new ChatGPTSessionError(\n 'session_invalidated',\n 'ChatGPT session changed while refresh was in progress',\n );\n }\n disk.sessions = disk.sessions.map((s) =>\n s.clientId === clientId ? updated : s,\n );\n delete disk.refreshes![clientId];\n await this.save(disk);\n return updated;\n });\n }\n /** Clear local credentials after attempting remote refresh-token revocation. */\n async logout(\n clientId: string,\n ): Promise<{ remoteRevocationConfirmed: boolean }> {\n const current = await this.locked(async () => {\n const disk = await this.disk();\n const current = disk.sessions.find((s) => s.clientId === clientId);\n if (!current) return undefined;\n disk.sessions = disk.sessions.filter((s) => s.clientId !== clientId);\n disk.generations = {\n ...disk.generations,\n [clientId]: this.generation(disk, clientId) + 1,\n };\n if (disk.refreshes) delete disk.refreshes[clientId];\n await this.save(disk);\n return current;\n });\n if (!current) return { remoteRevocationConfirmed: true };\n\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const confirmed = await Promise.race([\n (async () => {\n const discovery = (await (\n await this.fetcher(DISCOVERY, { signal: controller.signal })\n ).json()) as { revocation_endpoint?: string };\n if (!discovery.revocation_endpoint) return false;\n const body = new URLSearchParams({\n token: current.refreshToken,\n token_type_hint: 'refresh_token',\n client_id: current.clientId,\n });\n const response = await this.fetcher(discovery.revocation_endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body,\n signal: controller.signal,\n });\n return response.ok;\n })(),\n new Promise<boolean>((resolve) => {\n timer = setTimeout(() => {\n controller.abort();\n resolve(false);\n }, this.remoteRevocationTimeoutMs);\n }),\n ]);\n return { remoteRevocationConfirmed: confirmed };\n } catch {\n return { remoteRevocationConfirmed: false };\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"local.js","names":[],"sources":["../src/local.ts"],"sourcesContent":["/** Node-only local Sign in with ChatGPT session runtime. */\nimport { createHash, randomBytes, randomUUID } from 'node:crypto';\nimport { chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { createServer } from 'node:http';\nimport { tmpdir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\n\nconst AUTHORIZE = 'https://auth.openai.com/api/accounts/authorize';\nconst TOKEN = 'https://auth.openai.com/api/accounts/oauth/token';\nconst DISCOVERY = 'https://auth.openai.com/.well-known/openid-configuration';\nconst RESOURCE = 'https://api.openai.com/v1';\nconst PLAN_SCOPE = 'chatgpt.tokens.use.direct';\nconst SCOPES = [\n 'openid',\n 'profile',\n 'email',\n 'offline_access',\n 'resource.invoke',\n PLAN_SCOPE,\n];\n\nexport class ChatGPTSessionError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = 'ChatGPTSessionError';\n }\n}\nexport interface ChatGPTSession {\n subject: string;\n email?: string;\n clientId: string;\n hostId: string;\n idToken: string;\n accessToken: string;\n refreshToken: string;\n scopes: string[];\n expiresAt: number;\n}\nexport interface LocalChatGPTOptions {\n appName: string;\n path?: string;\n openBrowser?: (url: string) => Promise<void> | void;\n fetch?: typeof fetch;\n now?: () => number;\n}\ninterface Disk {\n hostId: string;\n sessions: ChatGPTSession[];\n generations?: Record<string, number>;\n refreshes?: Record<string, { id: string; pid: number }>;\n}\ninterface Pending {\n state: string;\n nonce: string;\n verifier: string;\n redirectUri: string;\n clientId: string;\n selected?: ChatGPTSession;\n generation: number;\n server: ReturnType<typeof createServer>;\n}\n\nfunction b64(bytes: Uint8Array) {\n return Buffer.from(bytes).toString('base64url');\n}\nfunction random() {\n return b64(randomBytes(32));\n}\nfunction pathFor(options: LocalChatGPTOptions) {\n return (\n options.path ??\n join(\n process.env.XDG_CONFIG_HOME ??\n join(process.env.HOME ?? tmpdir(), '.config'),\n options.appName,\n 'chatgpt.json',\n )\n );\n}\nfunction required(value: string | null, label: string) {\n if (!value)\n throw new ChatGPTSessionError(\n 'invalid_callback',\n `Missing ${label} in OAuth callback`,\n );\n return value;\n}\n\n/**\n * Stores credentials only in an owner-readable local file. Callers should pass\n * a per-application config path; no credential is ever written to a project.\n */\nexport class LocalChatGPTSessionManager {\n private pending = new Map<string, Pending>();\n private refreshing = new Map<string, Promise<ChatGPTSession>>();\n private readonly file: string;\n private hostId?: string;\n private readonly fetcher: typeof fetch;\n private readonly now: () => number;\n private readonly remoteRevocationTimeoutMs = 5_000;\n constructor(private readonly options: LocalChatGPTOptions) {\n this.file = pathFor(options);\n this.fetcher = options.fetch ?? fetch;\n this.now = options.now ?? Date.now;\n }\n private async disk(): Promise<Disk> {\n try {\n const disk = JSON.parse(await readFile(this.file, 'utf8')) as Disk;\n this.hostId = disk.hostId;\n return disk;\n } catch (e: any) {\n if (e?.code === 'ENOENT') {\n if (!this.hostId) this.hostId = `urn:uuid:${randomUUID()}`;\n return {\n hostId: this.hostId,\n sessions: [],\n };\n }\n throw e;\n }\n }\n private generation(disk: Disk, clientId: string) {\n return disk.generations?.[clientId] ?? 0;\n }\n private processIsAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error: any) {\n return error?.code !== 'ESRCH';\n }\n }\n private async locked<T>(operation: () => Promise<T>): Promise<T> {\n const mutex = `${this.file}.mutex.sqlite`;\n await mkdir(dirname(this.file), { recursive: true, mode: 0o700 });\n for (let attempt = 0; attempt < 200; attempt++) {\n const database = new DatabaseSync(mutex, { timeout: 0 });\n let busy = false;\n try {\n await chmod(mutex, 0o600);\n try {\n database.exec('BEGIN IMMEDIATE');\n } catch (error: any) {\n if (\n error?.code === 'ERR_SQLITE_ERROR' &&\n typeof error?.errcode === 'number' &&\n (error.errcode & 0xff) === 5\n )\n busy = true;\n else throw error;\n }\n if (!busy) {\n try {\n return await operation();\n } finally {\n database.exec('ROLLBACK');\n }\n }\n } finally {\n database.close();\n }\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n throw new ChatGPTSessionError('storage_busy', 'Credential storage is busy');\n }\n private async save(disk: Disk) {\n await mkdir(dirname(this.file), { recursive: true, mode: 0o700 });\n const temp = `${this.file}.${randomUUID()}.tmp`;\n await writeFile(temp, JSON.stringify(disk), { mode: 0o600 });\n await chmod(temp, 0o600);\n await rename(temp, this.file);\n await chmod(this.file, 0o600);\n }\n async sessions(): Promise<\n Array<Omit<ChatGPTSession, 'accessToken' | 'refreshToken' | 'idToken'>>\n > {\n return (await this.disk()).sessions.map(\n ({ accessToken, refreshToken, idToken, ...safe }) => safe,\n );\n }\n async begin(\n selectedClientId?: string,\n ): Promise<{ authorizationUrl: string; callback: Promise<ChatGPTSession> }> {\n const disk = await this.locked(async () => {\n const current = await this.disk();\n await this.save(current);\n return current;\n });\n const selected = selectedClientId\n ? disk.sessions.find((s) => s.clientId === selectedClientId)\n : undefined;\n if (selectedClientId && !selected)\n throw new ChatGPTSessionError(\n 'unknown_account',\n 'Selected ChatGPT account is not stored locally',\n );\n const state = random(),\n nonce = random(),\n verifier = random();\n const challenge = b64(createHash('sha256').update(verifier).digest());\n let resolve!: (value: ChatGPTSession) => void,\n reject!: (reason: Error) => void;\n const callback = new Promise<ChatGPTSession>((a, b) => {\n resolve = a;\n reject = b;\n });\n let redirectUri = '';\n const server = createServer((req, res) => {\n let url: URL;\n try {\n url = new URL(req.url ?? '/', redirectUri);\n } catch {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Invalid sign-in callback.');\n return;\n }\n if (!this.pending.has(state)) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Sign-in callback was already consumed.');\n return;\n }\n const finish = async () => {\n try {\n const result = await this.complete(url, state);\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end('Sign-in complete. You may close this window.');\n resolve(result);\n } catch (error) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Sign-in failed. Return to the application.');\n reject(error as Error);\n } finally {\n server.close();\n this.pending.delete(state);\n }\n };\n void finish();\n });\n await new Promise<void>((resolveListen, rejectListen) => {\n server.once('error', rejectListen);\n server.listen(0, '127.0.0.1', () => resolveListen());\n });\n const address = server.address();\n if (!address || typeof address === 'string')\n throw new ChatGPTSessionError(\n 'loopback_unavailable',\n 'Could not bind 127.0.0.1 loopback callback',\n );\n redirectUri = `http://127.0.0.1:${address.port}/auth/callback`;\n const clientId = selected?.clientId ?? 'dynamic_agent_client';\n this.pending.set(state, {\n state,\n nonce,\n verifier,\n redirectUri,\n clientId,\n selected,\n generation: selected ? this.generation(disk, selected.clientId) : 0,\n server,\n });\n const params = new URLSearchParams({\n client_id: clientId,\n response_type: 'code',\n redirect_uri: redirectUri,\n scope: SCOPES.join(' '),\n resource: RESOURCE,\n state,\n nonce,\n code_challenge_method: 'S256',\n code_challenge: challenge,\n ext_agent_host_id: disk.hostId,\n });\n if (selected) {\n params.set('id_token_hint', selected.idToken);\n if (selected.email) params.set('login_hint', selected.email);\n } else params.set('agent_name_hint', this.options.appName);\n const authorizationUrl = `${AUTHORIZE}?${params}`;\n try {\n await this.options.openBrowser?.(authorizationUrl);\n } catch (error) {\n this.pending.delete(state);\n await new Promise<void>((resolveClose) =>\n server.close(() => resolveClose()),\n );\n throw error;\n }\n return { authorizationUrl, callback };\n }\n private async complete(\n url: URL,\n expectedState: string,\n ): Promise<ChatGPTSession> {\n const pending = this.pending.get(expectedState);\n if (!pending || url.searchParams.get('state') !== expectedState)\n throw new ChatGPTSessionError(\n 'state_mismatch',\n 'Rejected OAuth callback with unbound state',\n );\n if (url.searchParams.get('error'))\n throw new ChatGPTSessionError(\n 'consent_declined',\n 'ChatGPT authorization was declined',\n );\n this.pending.delete(expectedState);\n const code = required(url.searchParams.get('code'), 'code');\n const callbackClientId = url.searchParams.get('client_id');\n const clientId = pending.selected\n ? pending.clientId\n : required(callbackClientId, 'issued client_id');\n if (!pending.selected && clientId === 'dynamic_agent_client')\n throw new ChatGPTSessionError(\n 'registration_incomplete',\n 'OAuth registration did not return an issued client id',\n );\n if (pending.selected && callbackClientId && callbackClientId !== clientId)\n throw new ChatGPTSessionError(\n 'client_mismatch',\n 'OAuth callback changed the registered client',\n );\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: clientId,\n code,\n code_verifier: pending.verifier,\n redirect_uri: pending.redirectUri,\n resource: RESOURCE,\n });\n const response = await this.fetcher(TOKEN, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body,\n });\n if (!response.ok)\n throw new ChatGPTSessionError(\n 'token_exchange_failed',\n 'OAuth token exchange failed',\n );\n const token: any = await response.json();\n const identity = await this.verify(token.id_token, clientId, pending.nonce);\n const scopes = String(token.scope ?? '')\n .split(' ')\n .filter(Boolean);\n if (!scopes.includes(PLAN_SCOPE))\n throw new ChatGPTSessionError(\n 'plan_permission_missing',\n 'ChatGPT plan usage permission was not granted',\n );\n const session: ChatGPTSession = {\n subject: identity.sub,\n email: identity.email,\n clientId,\n hostId: (await this.disk()).hostId,\n idToken: token.id_token,\n accessToken: token.access_token,\n refreshToken: token.refresh_token,\n scopes,\n expiresAt: this.now() + Number(token.expires_in ?? 0) * 1000,\n };\n if (!session.accessToken || !session.refreshToken)\n throw new ChatGPTSessionError(\n 'invalid_token_response',\n 'OAuth response omitted renewable credentials',\n );\n if (pending.selected && session.subject !== pending.selected.subject)\n throw new ChatGPTSessionError(\n 'account_mismatch',\n 'OAuth identity did not match the selected account',\n );\n await this.locked(async () => {\n const disk = await this.disk();\n if (\n pending.selected &&\n this.generation(disk, clientId) !== pending.generation\n )\n throw new ChatGPTSessionError(\n 'session_invalidated',\n 'ChatGPT session changed while authorization was in progress',\n );\n disk.sessions = [\n ...disk.sessions.filter((s) => s.clientId !== clientId),\n session,\n ];\n disk.generations = {\n ...disk.generations,\n [clientId]: this.generation(disk, clientId) + 1,\n };\n if (disk.refreshes) delete disk.refreshes[clientId];\n await this.save(disk);\n });\n return session;\n }\n private async verify(\n token: string,\n clientId: string,\n nonce: string,\n ): Promise<{ sub: string; email?: string }> {\n if (!token)\n throw new ChatGPTSessionError(\n 'invalid_token_response',\n 'OAuth response omitted id_token',\n );\n const config = (await (await this.fetcher(DISCOVERY)).json()) as {\n jwks_uri?: string;\n };\n if (!config.jwks_uri)\n throw new ChatGPTSessionError(\n 'discovery_failed',\n 'OpenID discovery did not supply JWKS',\n );\n const result = await jwtVerify(\n token,\n createRemoteJWKSet(new URL(config.jwks_uri)),\n { issuer: 'https://auth.openai.com', audience: clientId },\n );\n if (\n result.payload.nonce !== nonce ||\n typeof result.payload.sub !== 'string' ||\n typeof result.payload.exp !== 'number' ||\n typeof result.payload.iat !== 'number'\n )\n throw new ChatGPTSessionError(\n 'invalid_identity',\n 'OIDC token nonce or subject is invalid',\n );\n return {\n sub: result.payload.sub,\n email:\n typeof result.payload.email === 'string'\n ? result.payload.email\n : undefined,\n };\n }\n /** Return a currently usable token, serializing rotating-token refreshes per registration. */\n async accessToken(clientId: string): Promise<string> {\n const session = (await this.disk()).sessions.find(\n (s) => s.clientId === clientId,\n );\n if (!session)\n throw new ChatGPTSessionError(\n 'unknown_account',\n 'Selected ChatGPT account is not stored locally',\n );\n if (session.expiresAt > this.now() + 60_000) return session.accessToken;\n return (await this.refresh(clientId)).accessToken;\n }\n async refresh(clientId: string): Promise<ChatGPTSession> {\n const active = this.refreshing.get(clientId);\n if (active) return active;\n const refresh = this.doRefresh(clientId).finally(() =>\n this.refreshing.delete(clientId),\n );\n this.refreshing.set(clientId, refresh);\n return refresh;\n }\n private async doRefresh(clientId: string): Promise<ChatGPTSession> {\n const leaseId = randomUUID();\n let observedRefreshToken: string | undefined;\n let current: ChatGPTSession | undefined;\n let generation = 0;\n for (let attempt = 0; attempt < 200; attempt++) {\n const claim = await this.locked(async () => {\n const disk = await this.disk();\n const session = disk.sessions.find((s) => s.clientId === clientId);\n if (!session)\n throw new ChatGPTSessionError(\n 'unknown_account',\n 'Selected ChatGPT account is not stored locally',\n );\n observedRefreshToken ??= session.refreshToken;\n if (session.refreshToken !== observedRefreshToken)\n return { kind: 'updated' as const, session };\n const active = disk.refreshes?.[clientId];\n if (active && this.processIsAlive(active.pid))\n return { kind: 'wait' as const };\n disk.refreshes = {\n ...disk.refreshes,\n [clientId]: { id: leaseId, pid: process.pid },\n };\n await this.save(disk);\n return {\n kind: 'owned' as const,\n session,\n generation: this.generation(disk, clientId),\n };\n });\n if (claim.kind === 'updated') return claim.session;\n if (claim.kind === 'owned') {\n current = claim.session;\n generation = claim.generation;\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n if (!current)\n throw new ChatGPTSessionError(\n 'storage_busy',\n 'Credential refresh is already in progress',\n );\n\n const releaseLease = async () => {\n await this.locked(async () => {\n const disk = await this.disk();\n if (disk.refreshes?.[clientId]?.id === leaseId) {\n delete disk.refreshes[clientId];\n await this.save(disk);\n }\n });\n };\n const invalidate = async (code: string, message: string) => {\n await this.locked(async () => {\n const disk = await this.disk();\n if (\n disk.refreshes?.[clientId]?.id === leaseId &&\n this.generation(disk, clientId) === generation &&\n disk.sessions.find((session) => session.clientId === clientId)\n ?.refreshToken === current.refreshToken\n ) {\n disk.sessions = disk.sessions.filter((s) => s.clientId !== clientId);\n disk.generations = {\n ...disk.generations,\n [clientId]: generation + 1,\n };\n delete disk.refreshes[clientId];\n await this.save(disk);\n }\n });\n throw new ChatGPTSessionError(code, message);\n };\n\n let response: Response;\n try {\n response = await this.fetcher(TOKEN, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: current.clientId,\n refresh_token: current.refreshToken,\n resource: RESOURCE,\n }),\n });\n } catch (error) {\n await releaseLease();\n throw error;\n }\n if (!response.ok) {\n if (response.status === 400 || response.status === 401)\n return invalidate(\n 'refresh_invalid',\n 'Saved ChatGPT session is no longer renewable; sign in again',\n );\n await releaseLease();\n throw new ChatGPTSessionError(\n 'refresh_failed',\n 'ChatGPT session refresh failed; credentials were retained',\n );\n }\n let token: any;\n try {\n token = await response.json();\n } catch (error) {\n await releaseLease();\n throw error;\n }\n if (!token.access_token || !token.refresh_token) {\n await releaseLease();\n throw new ChatGPTSessionError(\n 'invalid_refresh_response',\n 'Refresh response omitted replacement credentials',\n );\n }\n const scopes = String(token.scope ?? current.scopes.join(' '))\n .split(' ')\n .filter(Boolean);\n if (!scopes.includes(PLAN_SCOPE))\n return invalidate(\n 'plan_permission_missing',\n 'ChatGPT plan usage permission was not granted',\n );\n const updated: ChatGPTSession = {\n ...current,\n accessToken: token.access_token,\n refreshToken: token.refresh_token,\n idToken: token.id_token ?? current.idToken,\n scopes,\n expiresAt: this.now() + Number(token.expires_in ?? 0) * 1000,\n };\n return this.locked(async () => {\n const disk = await this.disk();\n const stored = disk.sessions.find((s) => s.clientId === clientId);\n const ownsLease = disk.refreshes?.[clientId]?.id === leaseId;\n if (\n !ownsLease ||\n this.generation(disk, clientId) !== generation ||\n stored?.refreshToken !== current.refreshToken\n ) {\n if (ownsLease) {\n delete disk.refreshes![clientId];\n await this.save(disk);\n }\n throw new ChatGPTSessionError(\n 'session_invalidated',\n 'ChatGPT session changed while refresh was in progress',\n );\n }\n disk.sessions = disk.sessions.map((s) =>\n s.clientId === clientId ? updated : s,\n );\n delete disk.refreshes![clientId];\n await this.save(disk);\n return updated;\n });\n }\n /** Clear local credentials after attempting remote refresh-token revocation. */\n async logout(\n clientId: string,\n ): Promise<{ remoteRevocationConfirmed: boolean }> {\n const current = await this.locked(async () => {\n const disk = await this.disk();\n const current = disk.sessions.find((s) => s.clientId === clientId);\n if (!current) return undefined;\n disk.sessions = disk.sessions.filter((s) => s.clientId !== clientId);\n disk.generations = {\n ...disk.generations,\n [clientId]: this.generation(disk, clientId) + 1,\n };\n if (disk.refreshes) delete disk.refreshes[clientId];\n await this.save(disk);\n return current;\n });\n if (!current) return { remoteRevocationConfirmed: true };\n\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const confirmed = await Promise.race([\n (async () => {\n const discovery = (await (\n await this.fetcher(DISCOVERY, { signal: controller.signal })\n ).json()) as { revocation_endpoint?: string };\n if (!discovery.revocation_endpoint) return false;\n const body = new URLSearchParams({\n token: current.refreshToken,\n token_type_hint: 'refresh_token',\n client_id: current.clientId,\n });\n const response = await this.fetcher(discovery.revocation_endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body,\n signal: controller.signal,\n });\n return response.ok;\n })(),\n new Promise<boolean>((resolve) => {\n timer = setTimeout(() => {\n controller.abort();\n resolve(false);\n }, this.remoteRevocationTimeoutMs);\n }),\n ]);\n return { remoteRevocationConfirmed: confirmed };\n } catch {\n return { remoteRevocationConfirmed: false };\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import { type JWTPayload } from 'jose';
2
+ import { type OAuthAuthorizationRedirect, type OAuthAuthorizationRequest, type OAuthAuthorizationServerOptions, type OAuthConsentContext, type OAuthRegistrationResponse, type OAuthTokenResponse } from './types.js';
3
+ export { InMemoryOAuthAuthorizationStorage } from './memory.js';
4
+ export * from './types.js';
5
+ /** Framework-independent OAuth authorization server. Mount {@link handle} in any Fetch-compatible router. */
6
+ export declare class OAuthAuthorizationServer {
7
+ private readonly issuer;
8
+ private readonly issuerUrl;
9
+ private readonly options;
10
+ constructor(options: OAuthAuthorizationServerOptions);
11
+ /** Parse and validate an authorization request without authenticating a browser session. */
12
+ parseAuthorizationRequest(input: URLSearchParams): Promise<OAuthAuthorizationRequest>;
13
+ /** Records an application-approved authorization decision and returns its redirect URI. */
14
+ approve(request: OAuthAuthorizationRequest, consent: OAuthConsentContext): Promise<OAuthAuthorizationRedirect>;
15
+ /** Exchanges an authorization code or a rotating refresh token for an access token. */
16
+ token(input: URLSearchParams): Promise<OAuthTokenResponse>;
17
+ /** RFC 7009-style best-effort revocation; unknown token values deliberately succeed. */
18
+ revoke(input: URLSearchParams): Promise<void>;
19
+ /** Immediately makes a valid, unexpired access JWT inactive through the injected revocation store. */
20
+ revokeAccessToken(token: string): Promise<void>;
21
+ /**
22
+ * Verifies issuer, signature, expiry, the caller's own resource audience and
23
+ * live server-side access-token revocation. Resource servers must always pass
24
+ * the audience (resource URI) they serve so tokens minted for another
25
+ * resource are rejected.
26
+ */
27
+ verifyAccessToken(token: string, audience: string): Promise<JWTPayload>;
28
+ private verifyJwt;
29
+ /** Returns RFC 8414 authorization-server metadata. */
30
+ discovery(): {
31
+ response_types_supported: string[];
32
+ grant_types_supported: string[];
33
+ token_endpoint_auth_methods_supported: string[];
34
+ code_challenge_methods_supported: string[];
35
+ authorization_response_iss_parameter_supported: boolean;
36
+ scopes_supported: string[];
37
+ registration_endpoint?: string | undefined;
38
+ issuer: string;
39
+ authorization_endpoint: string;
40
+ token_endpoint: string;
41
+ jwks_uri: string;
42
+ };
43
+ /** Returns the public signing JWK. */
44
+ jwks(): {
45
+ keys: {
46
+ kid: string;
47
+ alg: "RS256" | "ES256";
48
+ use: string;
49
+ crv?: string;
50
+ d?: string;
51
+ dp?: string;
52
+ dq?: string;
53
+ e?: string;
54
+ k?: string;
55
+ n?: string;
56
+ p?: string;
57
+ q?: string;
58
+ qi?: string;
59
+ x?: string;
60
+ y?: string;
61
+ pub?: string;
62
+ priv?: string;
63
+ kty?: string;
64
+ key_ops?: string[];
65
+ ext?: boolean;
66
+ x5c?: string[];
67
+ x5t?: string;
68
+ 'x5t#S256'?: string;
69
+ x5u?: string;
70
+ }[];
71
+ };
72
+ /** Safely registers a public authorization-code client when DCR is explicitly enabled. */
73
+ register(input: unknown): Promise<OAuthRegistrationResponse>;
74
+ /** Fetch-native routing for discovery, JWKS, registration, token and revocation endpoints. Authorization approval stays application-owned. */
75
+ handle(request: Request): Promise<Response>;
76
+ private exchangeCode;
77
+ private refresh;
78
+ private issue;
79
+ private client;
80
+ private assertRedirect;
81
+ private assertScopes;
82
+ private assertResource;
83
+ private assertIssuer;
84
+ private revalidate;
85
+ private applyConsent;
86
+ private assertPublicRedirect;
87
+ }
88
+ /** Creates a framework-independent OAuth server; callers must provide durable storage in production. */
89
+ export declare function createAuthorizationServer(options: OAuthAuthorizationServerOptions): OAuthAuthorizationServer;
90
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,MAAM,CAAC;AACd,OAAO,EAEL,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,KAAK,+BAA+B,EAEpC,KAAK,mBAAmB,EAExB,KAAK,yBAAyB,EAE9B,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iCAAiC,EAAE,MAAM,aAAa,CAAC;AAChE,cAAc,YAAY,CAAC;AAsE3B,6GAA6G;AAC7G,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAUU;gBAEtB,OAAO,EAAE,+BAA+B;IAmCpD,4FAA4F;IACtF,yBAAyB,CAC7B,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,yBAAyB,CAAC;IAkCrC,2FAA2F;IACrF,OAAO,CACX,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,0BAA0B,CAAC;IAoCtC,uFAAuF;IACjF,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiBhE,wFAAwF;IAClF,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCnD,sGAAsG;IAChG,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrD;;;;;OAKG;IACG,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC;YAMR,SAAS;IA8CvB,sDAAsD;IACtD,SAAS;;;;;;;;;;;;;IAqBT,sCAAsC;IACtC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAaJ,0FAA0F;IACpF,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAkHlE,8IAA8I;IACxI,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;YAuCnC,YAAY;YAoCZ,OAAO;YA6DP,KAAK;YAyEL,MAAM;IAYpB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,YAAY;YAmBN,UAAU;IAqBxB,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,oBAAoB;CAoB7B;AAED,wGAAwG;AACxG,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,+BAA+B,4BAGzC"}