@codeworksh/aikit 0.8.0 → 0.9.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/README.md +10 -1
- package/cli.mjs +75 -83
- package/cli.mjs.map +1 -1
- package/{codex-MtP1574D.mjs → codex-CLDBTrFm.mjs} +132 -134
- package/codex-CLDBTrFm.mjs.map +1 -0
- package/copilot-CcD3g6H8.mjs +425 -0
- package/copilot-CcD3g6H8.mjs.map +1 -0
- package/copilot-headers-DceqXlpj.mjs +104 -0
- package/copilot-headers-DceqXlpj.mjs.map +1 -0
- package/github-copilot-BJIrxOzT.mjs +3 -0
- package/github-copilot-SIUCJz1G.mjs +126 -0
- package/github-copilot-SIUCJz1G.mjs.map +1 -0
- package/index.d.mts +980 -932
- package/index.mjs +63 -11
- package/index.mjs.map +1 -1
- package/interactive-eoNnscGB.mjs +57 -0
- package/interactive-eoNnscGB.mjs.map +1 -0
- package/{model-CpX11Oob.d.mts → model-CrVLp622.d.mts} +159 -130
- package/{model-CBnredXj.mjs → model-gCDbmGqT.mjs} +31 -2
- package/model-gCDbmGqT.mjs.map +1 -0
- package/{modelgen-BjSsusLQ.mjs → modelgen-DvorXOjJ.mjs} +148 -29
- package/modelgen-DvorXOjJ.mjs.map +1 -0
- package/modelgen.d.mts +2 -3
- package/modelgen.mjs +1 -1
- package/oauth/github/copilot.d.mts +169 -0
- package/oauth/github/copilot.mjs +2 -0
- package/oauth/interactive.d.mts +21 -0
- package/oauth/interactive.mjs +2 -0
- package/oauth/openai/codex.d.mts +46 -28
- package/oauth/openai/codex.mjs +2 -2
- package/oauth/summary.d.mts +67 -0
- package/oauth/summary.mjs +2 -0
- package/{openai-codex-t4B7g-gv.mjs → openai-codex-Bqzti6Mc.mjs} +3 -3
- package/{openai-codex-t4B7g-gv.mjs.map → openai-codex-Bqzti6Mc.mjs.map} +1 -1
- package/openai-codex-DA1Q7mLN.mjs +2 -0
- package/package.json +34 -22
- package/{registry-DYdYTwFt.mjs → registry-ZxwIW0gX.mjs} +5 -3
- package/registry-ZxwIW0gX.mjs.map +1 -0
- package/storage-v6qjTQ0v.mjs +178 -0
- package/storage-v6qjTQ0v.mjs.map +1 -0
- package/summary-C_QyCVWa.mjs +68 -0
- package/summary-C_QyCVWa.mjs.map +1 -0
- package/codex-MtP1574D.mjs.map +0 -1
- package/model-CBnredXj.mjs.map +0 -1
- package/modelgen-BjSsusLQ.mjs.map +0 -1
- package/openai-codex-BRH4Qa7l.mjs +0 -2
- package/registry-DYdYTwFt.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-CLDBTrFm.mjs","names":[],"sources":["../../src/oauth/openai/codex.ts"],"sourcesContent":["/**\n * OpenAI Codex OAuth.\n *\n * Public interface, by use case:\n *\n * 1. Server OAuth integration (storage-free, bring your own routes):\n * `createOpenAICodexAuthorizationFlow`, `exchangeOpenAICodexAuthorizationCode`,\n * `parseOpenAICodexAuthorizationInput`, `refreshOpenAICodexToken`, `getOpenAICodexAccountId`.\n * 2. CLI / interactive login (localhost callback server, persisted credentials):\n * `OpenAICodexOAuthClient`, `JsonOpenAICodexAuthStorage`, `OpenAICodexAuthStorage`.\n * 3. Application credential access (get key, auto-refresh, request headers):\n * `getOpenAICodexApiKey`, `OpenAICodexOAuthClient.getCredentials/getApiKey/getHeaders`,\n * `openAICodexHeaders`, `OPENAI_CODEX_API_KEY_ENV`.\n *\n * Everything else in this module is an implementation detail and intentionally\n * not exported.\n */\n\nimport dedent from \"dedent\";\n\nimport { pollDeviceCode } from \"../devcode.ts\";\nimport { JsonAuthStorage, isObject, readEnv } from \"../storage.ts\";\n\nconst CODEWORK_OAUTH_CALLBACK_HOST = \"CODEWORK_OAUTH_CALLBACK_HOST\";\n\nconst CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\";\nconst AUTHORIZE_URL = \"https://auth.openai.com/oauth/authorize\";\nconst TOKEN_URL = \"https://auth.openai.com/oauth/token\";\nconst DEFAULT_REDIRECT_URI = \"http://localhost:1455/auth/callback\";\n// Device code: no local port, so it works over SSH, in a container, and when\n// something else already holds 1455. The server mints the PKCE verifier and\n// redeems the code against its own callback.\nconst DEVICE_USER_CODE_URL = \"https://auth.openai.com/api/accounts/deviceauth/usercode\";\nconst DEVICE_TOKEN_URL = \"https://auth.openai.com/api/accounts/deviceauth/token\";\nconst DEVICE_VERIFICATION_URI = \"https://auth.openai.com/codex/device\";\nconst DEVICE_REDIRECT_URI = \"https://auth.openai.com/deviceauth/callback\";\nconst DEVICE_TIMEOUT_SECONDS = 15 * 60;\nconst DEFAULT_SCOPE = \"openid profile email offline_access\";\nconst DEFAULT_ORIGINATOR = \"codework\";\nconst DEFAULT_PROVIDER_ID = \"openai-codex\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\";\nconst REFRESH_SKEW_MS = 60 * 1000;\n\nexport type OpenAICodexOAuthCredentials = {\n\taccess: string;\n\trefresh: string;\n\texpires: number;\n\taccountId: string;\n};\n\nexport type OpenAICodexTokenSuccess = {\n\ttype: \"success\";\n\taccess: string;\n\trefresh: string;\n\texpires: number;\n};\n\nexport type OpenAICodexTokenFailure = {\n\ttype: \"failed\";\n\tmessage: string;\n\tstatus?: number;\n};\n\nexport type OpenAICodexTokenResult = OpenAICodexTokenSuccess | OpenAICodexTokenFailure;\n\nexport type OpenAICodexAuthorizationFlow = {\n\turl: string;\n\tverifier: string;\n\tstate: string;\n\tredirectUri: string;\n};\n\nexport type OpenAICodexAuthorizationOptions = {\n\toriginator?: string;\n\tredirectUri?: string;\n\tscope?: string;\n};\n\nexport type OpenAICodexLoginPrompt = {\n\tmessage: string;\n\tplaceholder?: string;\n\tallowEmpty?: boolean;\n};\n\nexport type OpenAICodexDeviceAuth = {\n\tdeviceAuthId: string;\n\tuserCode: string;\n\tverificationUri: string;\n\tintervalSeconds: number;\n};\n\nexport type OpenAICodexLoginOptions = OpenAICodexAuthorizationOptions & {\n\t/** Use the device-code flow instead of the localhost browser callback. */\n\tdevice?: boolean | undefined;\n\tsignal?: AbortSignal | undefined;\n\tonAuth: (info: { url: string; userCode?: string | undefined; instructions?: string | undefined }) => void;\n\tonPrompt: (prompt: OpenAICodexLoginPrompt) => Promise<string>;\n\tonProgress?: (message: string) => void;\n};\n\nexport interface OpenAICodexAuthStorage {\n\tget(): Promise<OpenAICodexOAuthCredentials | undefined>;\n\tset(credentials: OpenAICodexOAuthCredentials): Promise<void>;\n\tclear(): Promise<void>;\n}\n\nexport type JsonOpenAICodexAuthStorageOptions = {\n\tpath?: string;\n\tproviderId?: string;\n};\n\ntype JwtPayload = {\n\t[JWT_CLAIM_PATH]?: {\n\t\tchatgpt_account_id?: string;\n\t};\n\t[key: string]: unknown;\n};\n\ntype OAuthServerInfo = {\n\tclose: () => Promise<void>;\n\twaitForCode: () => Promise<{ code: string } | null>;\n\t/** Set when the callback port could not be bound; login falls back to pasting. */\n\tlistenError?: Error;\n};\n\ntype TokenEndpointPayload = {\n\taccess_token?: string;\n\trefresh_token?: string;\n\texpires_in?: number;\n};\n\nfunction isOpenAICodexCredentials(value: unknown): value is OpenAICodexOAuthCredentials {\n\tif (!isObject(value)) return false;\n\treturn (\n\t\ttypeof value.access === \"string\" &&\n\t\ttypeof value.refresh === \"string\" &&\n\t\ttypeof value.expires === \"number\" &&\n\t\ttypeof value.accountId === \"string\"\n\t);\n}\n\nfunction redactOAuthSecrets(value: string, secrets: readonly string[] = []): string {\n\tlet redacted = value;\n\tfor (const secret of secrets.filter(Boolean).sort((left, right) => right.length - left.length)) {\n\t\tredacted = redacted.replaceAll(secret, \"[REDACTED]\");\n\t}\n\treturn redacted\n\t\t.replace(\n\t\t\t/((?:\"?(?:access_token|refresh_token|id_token|code|code_verifier)\"?)\\s*[:=]\\s*)(?:\"[^\"]*\"|'[^']*'|[^&\\s,}]+)/gi,\n\t\t\t'$1\"[REDACTED]\"',\n\t\t)\n\t\t.replace(/Bearer\\s+\\S+/gi, \"Bearer [REDACTED]\");\n}\n\nfunction tokenEndpointResult(payload: TokenEndpointPayload, operation: \"exchange\" | \"refresh\"): OpenAICodexTokenResult {\n\tconst missing = [\n\t\t!payload.access_token && \"access_token\",\n\t\t!payload.refresh_token && \"refresh_token\",\n\t\ttypeof payload.expires_in !== \"number\" && \"expires_in\",\n\t].filter((field): field is string => Boolean(field));\n\tif (missing.length > 0) {\n\t\treturn {\n\t\t\ttype: \"failed\",\n\t\t\tmessage: `OpenAI Codex token ${operation} response missing fields: ${missing.join(\", \")}`,\n\t\t};\n\t}\n\n\treturn {\n\t\ttype: \"success\",\n\t\taccess: payload.access_token!,\n\t\trefresh: payload.refresh_token!,\n\t\texpires: Date.now() + payload.expires_in! * 1000,\n\t};\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replaceAll(\"=\", \"\");\n}\n\nfunction base64UrlDecode(value: string): string {\n\tconst base64 = value.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n\tconst padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, \"=\");\n\treturn atob(padded);\n}\n\ntype WebCrypto = typeof globalThis.crypto;\n\nfunction getCrypto(): WebCrypto {\n\tif (!globalThis.crypto?.getRandomValues || !globalThis.crypto.subtle) {\n\t\tthrow new Error(\"openAI codex OAuth requires web crypto support\");\n\t}\n\treturn globalThis.crypto;\n}\n\nfunction createState(): string {\n\tconst bytes = new Uint8Array(16);\n\tgetCrypto().getRandomValues(bytes);\n\treturn Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nasync function generatePKCE(): Promise<{\n\tverifier: string;\n\tchallenge: string;\n}> {\n\tconst bytes = new Uint8Array(32);\n\tgetCrypto().getRandomValues(bytes);\n\tconst verifier = base64UrlEncode(bytes);\n\n\tconst data = new TextEncoder().encode(verifier);\n\tconst hash = await getCrypto().subtle.digest(\"SHA-256\", data);\n\tconst challenge = base64UrlEncode(new Uint8Array(hash));\n\n\treturn { verifier, challenge };\n}\n\nfunction authorizationInput(code: string | null | undefined, state: string | null | undefined) {\n\treturn {\n\t\t...(code != null && { code }),\n\t\t...(state != null && { state }),\n\t};\n}\n\nexport function parseOpenAICodexAuthorizationInput(input: string): {\n\tcode?: string;\n\tstate?: string;\n} {\n\tconst value = input.trim();\n\tif (!value) return {};\n\n\ttry {\n\t\tconst url = new URL(value);\n\t\treturn authorizationInput(url.searchParams.get(\"code\"), url.searchParams.get(\"state\"));\n\t} catch {\n\t\t// Not a URL.\n\t}\n\n\tif (value.includes(\"#\")) {\n\t\tconst [code, state] = value.split(\"#\", 2);\n\t\treturn authorizationInput(code, state);\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\tconst params = new URLSearchParams(value);\n\t\treturn authorizationInput(params.get(\"code\"), params.get(\"state\"));\n\t}\n\n\treturn { code: value };\n}\n\nfunction decodeJwt(token: string): JwtPayload | null {\n\ttry {\n\t\tconst parts = token.split(\".\");\n\t\tif (parts.length !== 3) return null;\n\t\treturn JSON.parse(base64UrlDecode(parts[1] ?? \"\")) as JwtPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function getOpenAICodexAccountId(accessToken: string): string | null {\n\tconst payload = decodeJwt(accessToken);\n\tconst auth = payload?.[JWT_CLAIM_PATH];\n\tconst accountId = auth?.chatgpt_account_id;\n\treturn typeof accountId === \"string\" && accountId.length > 0 ? accountId : null;\n}\n\nexport async function createOpenAICodexAuthorizationFlow(\n\toptions: OpenAICodexAuthorizationOptions = {},\n): Promise<OpenAICodexAuthorizationFlow> {\n\tconst redirectUri = options.redirectUri ?? DEFAULT_REDIRECT_URI;\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = createState();\n\n\tconst url = new URL(AUTHORIZE_URL);\n\turl.searchParams.set(\"response_type\", \"code\");\n\turl.searchParams.set(\"client_id\", CLIENT_ID);\n\turl.searchParams.set(\"redirect_uri\", redirectUri);\n\turl.searchParams.set(\"scope\", options.scope ?? DEFAULT_SCOPE);\n\turl.searchParams.set(\"code_challenge\", challenge);\n\turl.searchParams.set(\"code_challenge_method\", \"S256\");\n\turl.searchParams.set(\"state\", state);\n\turl.searchParams.set(\"id_token_add_organizations\", \"true\");\n\turl.searchParams.set(\"codex_cli_simplified_flow\", \"true\");\n\turl.searchParams.set(\"originator\", options.originator ?? DEFAULT_ORIGINATOR);\n\n\treturn { verifier, state, redirectUri, url: url.toString() };\n}\n\nexport async function exchangeOpenAICodexAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string = DEFAULT_REDIRECT_URI,\n): Promise<OpenAICodexTokenResult> {\n\ttry {\n\t\tconst response = await fetch(TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody: new URLSearchParams({\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\tcode,\n\t\t\t\tcode_verifier: verifier,\n\t\t\t\tredirect_uri: redirectUri,\n\t\t\t}),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\treturn {\n\t\t\t\ttype: \"failed\",\n\t\t\t\tstatus: response.status,\n\t\t\t\tmessage: `OpenAI Codex token exchange failed (${response.status}): ${redactOAuthSecrets(\n\t\t\t\t\ttext || response.statusText,\n\t\t\t\t\t[code, verifier],\n\t\t\t\t)}`,\n\t\t\t};\n\t\t}\n\n\t\treturn tokenEndpointResult((await response.json()) as TokenEndpointPayload, \"exchange\");\n\t} catch (error) {\n\t\treturn {\n\t\t\ttype: \"failed\",\n\t\t\tmessage: `OpenAI Codex token exchange error: ${redactOAuthSecrets(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t[code, verifier],\n\t\t\t)}`,\n\t\t};\n\t}\n}\n\nasync function refreshAccessToken(refreshToken: string): Promise<OpenAICodexTokenResult> {\n\ttry {\n\t\tconst response = await fetch(TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody: new URLSearchParams({\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t}),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\treturn {\n\t\t\t\ttype: \"failed\",\n\t\t\t\tstatus: response.status,\n\t\t\t\tmessage: `OpenAI Codex token refresh failed (${response.status}): ${redactOAuthSecrets(\n\t\t\t\t\ttext || response.statusText,\n\t\t\t\t\t[refreshToken],\n\t\t\t\t)}`,\n\t\t\t};\n\t\t}\n\n\t\treturn tokenEndpointResult((await response.json()) as TokenEndpointPayload, \"refresh\");\n\t} catch (error) {\n\t\treturn {\n\t\t\ttype: \"failed\",\n\t\t\tmessage: `OpenAI Codex token refresh error: ${redactOAuthSecrets(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t[refreshToken],\n\t\t\t)}`,\n\t\t};\n\t}\n}\n\nfunction oauthPage(options: { title: string; heading: string; message: string; details?: string }): string {\n\tconst escapeHtml = (value: string) =>\n\t\tvalue\n\t\t\t.replaceAll(\"&\", \"&\")\n\t\t\t.replaceAll(\"<\", \"<\")\n\t\t\t.replaceAll(\">\", \">\")\n\t\t\t.replaceAll('\"', \""\")\n\t\t\t.replaceAll(\"'\", \"'\");\n\tconst details = options.details ? escapeHtml(options.details) : undefined;\n\n\treturn dedent`\n\t<!doctype html>\n\t<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t<title>${escapeHtml(options.title)}</title>\n\t\t<style>\n\t\t\thtml { color-scheme: dark; }\n\t\t\tbody {\n\t\t\tmargin: 0;\n\t\t\tmin-height: 100vh;\n\t\t\tdisplay: grid;\n\t\t\tplace-items: center;\n\t\t\tpadding: 24px;\n\t\t\tbackground: #09090b;\n\t\t\tcolor: #fafafa;\n\t\t\tfont-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n\t\t\ttext-align: center;\n\t\t\t}\n\t\t\tmain { width: 100%; max-width: 560px; }\n\t\t\th1 { margin: 0 0 10px; font-size: 28px; line-height: 1.15; font-weight: 650; }\n\t\t\tp { margin: 0; line-height: 1.7; color: #a1a1aa; font-size: 15px; }\n\t\t\t.details { margin-top: 16px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; color: #a1a1aa; white-space: pre-wrap; word-break: break-word; }\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<main>\n\t\t\t<h1>${escapeHtml(options.heading)}</h1>\n\t\t\t<p>${escapeHtml(options.message)}</p>\n\t\t\t${details ? `<div class=\"details\">${details}</div>` : \"\"}\n\t\t</main>\n\t</body>\n\t</html>\n\t`;\n}\n\nfunction oauthSuccessHtml(message: string): string {\n\treturn oauthPage({\n\t\ttitle: \"Authentication successful\",\n\t\theading: \"Authentication successful\",\n\t\tmessage,\n\t});\n}\n\nfunction oauthErrorHtml(message: string, details?: string): string {\n\treturn oauthPage({\n\t\ttitle: \"Authentication failed\",\n\t\theading: \"Authentication failed\",\n\t\tmessage,\n\t\t...(details !== undefined && { details }),\n\t});\n}\n\nasync function startLocalOAuthServer(state: string, redirectUri: string): Promise<OAuthServerInfo> {\n\t// The port and path are the ones the authorization server will redirect to:\n\t// binding anything else means the callback arrives somewhere we are not.\n\tconst callback = new URL(redirectUri);\n\tconst port = Number(callback.port || (callback.protocol === \"https:\" ? 443 : 80));\n\n\tconst http = await import(\"node:http\").catch(() => undefined);\n\tif (!http) {\n\t\tthrow new Error(\"openAI codex OAuth callback server is only available in Node.js environments\");\n\t}\n\n\tlet settleWait: ((value: { code: string } | null) => void) | undefined;\n\tconst waitForCodePromise = new Promise<{ code: string } | null>((resolve) => {\n\t\tlet settled = false;\n\t\tsettleWait = (value) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tresolve(value);\n\t\t};\n\t});\n\n\tconst server = http.createServer((req, res) => {\n\t\ttry {\n\t\t\tconst url = new URL(req.url || \"\", \"http://localhost\");\n\t\t\tif (url.pathname !== callback.pathname) {\n\t\t\t\tres.statusCode = 404;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"Callback route not found.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (url.searchParams.get(\"state\") !== state) {\n\t\t\t\tres.statusCode = 400;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"State mismatch.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst code = url.searchParams.get(\"code\");\n\t\t\tif (!code) {\n\t\t\t\tres.statusCode = 400;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"Missing authorization code.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.statusCode = 200;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthSuccessHtml(\"OpenAI authentication completed. You can close this window.\"));\n\t\t\tsettleWait?.({ code });\n\t\t} catch (error) {\n\t\t\tres.statusCode = 500;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(\n\t\t\t\toauthErrorHtml(\n\t\t\t\t\t\"Internal error while processing OAuth callback.\",\n\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t});\n\tconst close = async (): Promise<void> => {\n\t\tif (!server.listening) return;\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tserver.close(() => resolve());\n\t\t\tserver.closeAllConnections();\n\t\t});\n\t};\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(port, readEnv(CODEWORK_OAUTH_CALLBACK_HOST) || \"127.0.0.1\", () => {\n\t\t\t\tresolve({\n\t\t\t\t\tclose,\n\t\t\t\t\twaitForCode: () => waitForCodePromise,\n\t\t\t\t});\n\t\t\t})\n\t\t\t.on(\"error\", (error: Error) => {\n\t\t\t\tsettleWait?.(null);\n\t\t\t\tresolve({\n\t\t\t\t\tclose,\n\t\t\t\t\twaitForCode: async () => null,\n\t\t\t\t\tlistenError: error,\n\t\t\t\t});\n\t\t\t});\n\t});\n}\n\n// Internal: the public login entry point is `OpenAICodexOAuthClient#login`,\n// which persists the credentials. Server integrations should use the\n// storage-free authorization helpers instead of this localhost-callback flow.\nasync function loginOpenAICodex(options: OpenAICodexLoginOptions): Promise<OpenAICodexOAuthCredentials> {\n\tif (options.device) return loginOpenAICodexDeviceCode(options);\n\n\tconst flow = await createOpenAICodexAuthorizationFlow(options);\n\tconst server = await startLocalOAuthServer(flow.state, flow.redirectUri);\n\n\toptions.onAuth({\n\t\turl: flow.url,\n\t\tinstructions: \"Complete the browser login to finish OpenAI Codex authentication.\",\n\t});\n\n\tif (server.listenError) {\n\t\t// The redirect URI is registered against the OAuth client, so the port\n\t\t// cannot move. Whatever holds it -- usually another Codex login already in\n\t\t// progress -- will receive this callback instead of us.\n\t\toptions.onProgress?.(\n\t\t\t`Could not listen on ${flow.redirectUri}: ${server.listenError.message}. ` +\n\t\t\t\t\"Another sign-in is probably already running on that port and would receive the callback. \" +\n\t\t\t\t\"Finish the login in the browser, then paste the redirect URL here.\",\n\t\t);\n\t}\n\n\tlet code: string | undefined;\n\ttry {\n\t\tconst result = await server.waitForCode();\n\t\tif (result?.code) code = result.code;\n\n\t\t// The callback never arrived -- the port was taken, or the browser could\n\t\t// not reach us. Ask for the redirect URL instead.\n\t\tif (!code) {\n\t\t\tconst input = await options.onPrompt({\n\t\t\t\tmessage: \"Paste the authorization code or full redirect URL:\",\n\t\t\t});\n\t\t\tconst parsed = parseOpenAICodexAuthorizationInput(input);\n\t\t\tif (parsed.state && parsed.state !== flow.state) throw new Error(\"state mismatch\");\n\t\t\tcode = parsed.code;\n\t\t}\n\n\t\tif (!code) throw new Error(\"missing authorization code\");\n\n\t\treturn await credentialsFromCode(code, flow.verifier, flow.redirectUri);\n\t} finally {\n\t\tawait server.close();\n\t}\n}\n\n/** The step both flows share: redeem the code, then read the account off the JWT. */\nasync function credentialsFromCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n): Promise<OpenAICodexOAuthCredentials> {\n\tconst tokenResult = await exchangeOpenAICodexAuthorizationCode(code, verifier, redirectUri);\n\tif (tokenResult.type !== \"success\") throw new Error(tokenResult.message);\n\n\tconst accountId = getOpenAICodexAccountId(tokenResult.access);\n\tif (!accountId) throw new Error(\"failed to extract accountId from token\");\n\n\treturn {\n\t\taccess: tokenResult.access,\n\t\trefresh: tokenResult.refresh,\n\t\texpires: tokenResult.expires,\n\t\taccountId,\n\t};\n}\n\n/** Ask for a user code to type at {@link DEVICE_VERIFICATION_URI}. */\nexport async function createOpenAICodexDeviceAuth(\n\toptions: { signal?: AbortSignal | undefined } = {},\n): Promise<OpenAICodexDeviceAuth> {\n\tconst response = await fetch(DEVICE_USER_CODE_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ client_id: CLIENT_ID }),\n\t\tsignal: options.signal ?? null,\n\t});\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(\n\t\t\t\t\"OpenAI Codex device code login is not available for this account. Use the browser login instead.\",\n\t\t\t);\n\t\t}\n\t\tconst body = await response.text().catch(() => \"\");\n\t\tthrow new Error(\n\t\t\t`OpenAI Codex device code request failed (${response.status})${body ? `: ${redactOAuthSecrets(body)}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst data: unknown = await response.json();\n\tif (!isObject(data)) throw new Error(\"Invalid OpenAI Codex device code response\");\n\tconst interval = typeof data.interval === \"string\" ? Number(data.interval.trim()) : data.interval;\n\tif (\n\t\ttypeof data.device_auth_id !== \"string\" ||\n\t\ttypeof data.user_code !== \"string\" ||\n\t\ttypeof interval !== \"number\" ||\n\t\t!Number.isFinite(interval) ||\n\t\tinterval < 0\n\t) {\n\t\tthrow new Error(\"Invalid OpenAI Codex device code response fields\");\n\t}\n\n\treturn {\n\t\tdeviceAuthId: data.device_auth_id,\n\t\tuserCode: data.user_code,\n\t\tverificationUri: DEVICE_VERIFICATION_URI,\n\t\tintervalSeconds: interval,\n\t};\n}\n\n/** Wait for the code to be approved; the server returns the code and its verifier. */\nexport async function pollOpenAICodexDeviceAuth(\n\tdevice: OpenAICodexDeviceAuth,\n\toptions: { signal?: AbortSignal | undefined } = {},\n): Promise<{ authorizationCode: string; codeVerifier: string }> {\n\treturn pollDeviceCode({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_TIMEOUT_SECONDS,\n\t\t...(options.signal !== undefined && { signal: options.signal }),\n\t\tpoll: async () => {\n\t\t\tconst response = await fetch(DEVICE_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ device_auth_id: device.deviceAuthId, user_code: device.userCode }),\n\t\t\t\tsignal: options.signal ?? null,\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst data: unknown = await response.json();\n\t\t\t\tif (\n\t\t\t\t\t!isObject(data) ||\n\t\t\t\t\ttypeof data.authorization_code !== \"string\" ||\n\t\t\t\t\ttypeof data.code_verifier !== \"string\"\n\t\t\t\t) {\n\t\t\t\t\treturn { status: \"failed\", message: \"Invalid OpenAI Codex device auth token response\" };\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tvalue: { authorizationCode: data.authorization_code, codeVerifier: data.code_verifier },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// The endpoint answers 403/404 while the code is still unclaimed.\n\t\t\tif (response.status === 403 || response.status === 404) return { status: \"pending\" };\n\n\t\t\tconst body = await response.text().catch(() => \"\");\n\t\t\tlet errorCode: unknown;\n\t\t\ttry {\n\t\t\t\tconst parsed: unknown = JSON.parse(body);\n\t\t\t\tconst error = isObject(parsed) ? parsed.error : undefined;\n\t\t\t\terrorCode = isObject(error) ? error.code : error;\n\t\t\t} catch {\n\t\t\t\t// Not JSON; fall through to the generic failure below.\n\t\t\t}\n\t\t\tif (errorCode === \"deviceauth_authorization_pending\") return { status: \"pending\" };\n\t\t\tif (errorCode === \"slow_down\") return { status: \"slow_down\" };\n\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `OpenAI Codex device authorization failed (${response.status})${\n\t\t\t\t\tbody ? `: ${redactOAuthSecrets(body)}` : \"\"\n\t\t\t\t}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\nasync function loginOpenAICodexDeviceCode(options: OpenAICodexLoginOptions): Promise<OpenAICodexOAuthCredentials> {\n\tconst device = await createOpenAICodexDeviceAuth(options);\n\toptions.onAuth({\n\t\turl: device.verificationUri,\n\t\tuserCode: device.userCode,\n\t\tinstructions: \"Open the URL and enter the code to finish OpenAI Codex authentication.\",\n\t});\n\n\tconst { authorizationCode, codeVerifier } = await pollOpenAICodexDeviceAuth(device, options);\n\treturn credentialsFromCode(authorizationCode, codeVerifier, DEVICE_REDIRECT_URI);\n}\n\nexport async function refreshOpenAICodexToken(refreshToken: string): Promise<OpenAICodexOAuthCredentials> {\n\tconst result = await refreshAccessToken(refreshToken);\n\tif (result.type !== \"success\") {\n\t\tthrow new Error(result.message);\n\t}\n\n\tconst accountId = getOpenAICodexAccountId(result.access);\n\tif (!accountId) {\n\t\tthrow new Error(\"failed to extract accountId from token\");\n\t}\n\n\treturn {\n\t\taccess: result.access,\n\t\trefresh: result.refresh,\n\t\texpires: result.expires,\n\t\taccountId,\n\t};\n}\n\nexport function openAICodexHeaders(credentials: OpenAICodexOAuthCredentials): Record<string, string> {\n\treturn {\n\t\tAuthorization: `Bearer ${credentials.access}`,\n\t\t\"chatgpt-account-id\": credentials.accountId,\n\t\t\"OpenAI-Beta\": \"responses=experimental\",\n\t};\n}\n\nexport class JsonOpenAICodexAuthStorage implements OpenAICodexAuthStorage {\n\tprivate readonly storage: JsonAuthStorage<OpenAICodexOAuthCredentials>;\n\n\tconstructor(options: JsonOpenAICodexAuthStorageOptions = {}) {\n\t\tthis.storage = new JsonAuthStorage({\n\t\t\tproviderId: options.providerId ?? DEFAULT_PROVIDER_ID,\n\t\t\tisCredentials: isOpenAICodexCredentials,\n\t\t\t...(options.path !== undefined && { path: options.path }),\n\t\t});\n\t}\n\n\tget path(): string {\n\t\treturn this.storage.path;\n\t}\n\n\tget providerId(): string {\n\t\treturn this.storage.providerId;\n\t}\n\n\tget(): Promise<OpenAICodexOAuthCredentials | undefined> {\n\t\treturn this.storage.get();\n\t}\n\n\tset(credentials: OpenAICodexOAuthCredentials): Promise<void> {\n\t\treturn this.storage.set(credentials);\n\t}\n\n\tclear(): Promise<void> {\n\t\treturn this.storage.clear();\n\t}\n}\n\nexport type OpenAICodexOAuthClientOptions = {\n\tstorage?: OpenAICodexAuthStorage;\n\trefreshSkewMs?: number;\n};\n\nexport class OpenAICodexOAuthClient {\n\treadonly storage: OpenAICodexAuthStorage;\n\treadonly refreshSkewMs: number;\n\n\tconstructor(options: OpenAICodexOAuthClientOptions = {}) {\n\t\tthis.storage = options.storage ?? new JsonOpenAICodexAuthStorage();\n\t\tthis.refreshSkewMs = options.refreshSkewMs ?? REFRESH_SKEW_MS;\n\t}\n\n\tasync login(options: OpenAICodexLoginOptions): Promise<OpenAICodexOAuthCredentials> {\n\t\tconst credentials = await loginOpenAICodex(options);\n\t\tawait this.storage.set(credentials);\n\t\treturn credentials;\n\t}\n\n\tasync getCredentials(): Promise<OpenAICodexOAuthCredentials | undefined> {\n\t\tconst credentials = await this.storage.get();\n\t\tif (!credentials) return undefined;\n\n\t\tif (credentials.expires > Date.now() + this.refreshSkewMs) {\n\t\t\treturn credentials;\n\t\t}\n\n\t\tconst refreshed = await refreshOpenAICodexToken(credentials.refresh);\n\t\tawait this.storage.set(refreshed);\n\t\treturn refreshed;\n\t}\n\n\tasync getApiKey(): Promise<string | undefined> {\n\t\treturn (await this.getCredentials())?.access;\n\t}\n\n\tasync getHeaders(): Promise<Record<string, string> | undefined> {\n\t\tconst credentials = await this.getCredentials();\n\t\treturn credentials ? openAICodexHeaders(credentials) : undefined;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tawait this.storage.clear();\n\t}\n}\n\nexport const OPENAI_CODEX_API_KEY_ENV = \"OPENAI_CODEX_API_KEY\";\n\n/**\n * Resolve the OpenAI Codex API key the same way other provider API keys are\n * resolved: prefer the OPENAI_CODEX_API_KEY environment variable, then fall\n * back to stored OAuth credentials (refreshing them when close to expiry).\n *\n * The result can be passed directly as `apiKey` to the runtime options or to\n * `createOpenAICodex`. Returns undefined when no key is available.\n */\nexport async function getOpenAICodexApiKey(options: OpenAICodexOAuthClientOptions = {}): Promise<string | undefined> {\n\tconst envKey = readEnv(OPENAI_CODEX_API_KEY_ENV);\n\tif (envKey) return envKey;\n\n\treturn new OpenAICodexOAuthClient(options).getApiKey();\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { i as pollDeviceCode, n as isObject, r as readEnv, t as JsonAuthStorage } from "./storage-v6qjTQ0v.mjs";
|
|
2
|
+
import { n as GITHUB_COPILOT_STATIC_HEADERS, t as GITHUB_COPILOT_API_VERSION } from "./copilot-headers-DceqXlpj.mjs";
|
|
3
|
+
//#region src/oauth/github/copilot.ts
|
|
4
|
+
/**
|
|
5
|
+
* GitHub Copilot OAuth.
|
|
6
|
+
*
|
|
7
|
+
* The device flow yields a plain GitHub OAuth token (`ghu_`/`gho_`), which the
|
|
8
|
+
* Copilot API accepts directly as a Bearer credential — no session-token
|
|
9
|
+
* exchange and no refresh path. `getGitHubCopilotApiKey` resolves a token from
|
|
10
|
+
* `COPILOT_GITHUB_TOKEN` or the codework auth.json store, and nothing else;
|
|
11
|
+
* `GitHubCopilotOAuthClient#login` performs the device flow.
|
|
12
|
+
*/
|
|
13
|
+
const GITHUB_COPILOT_API_KEY_ENV = "COPILOT_GITHUB_TOKEN";
|
|
14
|
+
const CLIENT_ID = "Iv1.b507a08c87ecfe98";
|
|
15
|
+
const SCOPE = "read:user";
|
|
16
|
+
const DEFAULT_DOMAIN = "github.com";
|
|
17
|
+
const DEFAULT_BASE_URL = "https://api.githubcopilot.com";
|
|
18
|
+
const USER_API_VERSION = "2025-04-01";
|
|
19
|
+
const DEFAULT_PROVIDER_ID = "github-copilot";
|
|
20
|
+
const GITHUB_COPILOT_PROVIDER_ID = DEFAULT_PROVIDER_ID;
|
|
21
|
+
const DEFAULT_MODELS_RETRY = {
|
|
22
|
+
maxRetries: 2,
|
|
23
|
+
maxElapsedMs: 5e3
|
|
24
|
+
};
|
|
25
|
+
function fetchWith(options) {
|
|
26
|
+
return options.fetch ?? globalThis.fetch;
|
|
27
|
+
}
|
|
28
|
+
function sleep(ms, signal) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
if (signal?.aborted) {
|
|
31
|
+
reject(/* @__PURE__ */ new Error("GitHub Copilot login cancelled"));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const timeout = setTimeout(() => {
|
|
35
|
+
signal?.removeEventListener("abort", onAbort);
|
|
36
|
+
resolve();
|
|
37
|
+
}, ms);
|
|
38
|
+
const onAbort = () => {
|
|
39
|
+
clearTimeout(timeout);
|
|
40
|
+
reject(/* @__PURE__ */ new Error("GitHub Copilot login cancelled"));
|
|
41
|
+
};
|
|
42
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function normalizeGitHubDomain(input) {
|
|
46
|
+
const trimmed = input?.trim();
|
|
47
|
+
if (!trimmed) return void 0;
|
|
48
|
+
try {
|
|
49
|
+
return (trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`)).hostname;
|
|
50
|
+
} catch {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** The `api.` host serving `copilot_internal/*` for a given GitHub domain. */
|
|
55
|
+
function gitHubApiDomain(domain) {
|
|
56
|
+
return domain === DEFAULT_DOMAIN ? "api.github.com" : `api.${domain}`;
|
|
57
|
+
}
|
|
58
|
+
/** The Copilot inference host for a credential or enterprise domain. */
|
|
59
|
+
function gitHubCopilotBaseUrl(options) {
|
|
60
|
+
const apiEndpoint = options?.apiEndpoint;
|
|
61
|
+
if (typeof apiEndpoint === "string" && apiEndpoint) return apiEndpoint.replace(/\/+$/, "");
|
|
62
|
+
const domain = normalizeGitHubDomain(options?.enterpriseUrl);
|
|
63
|
+
return domain ? `https://copilot-api.${domain}` : DEFAULT_BASE_URL;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* GitHub reports Copilot access on `/copilot_internal/user`; OAuth itself
|
|
67
|
+
* succeeds for any GitHub account, so this is the only signal that the account
|
|
68
|
+
* can chat.
|
|
69
|
+
*/
|
|
70
|
+
function gitHubCopilotEntitlementError(user) {
|
|
71
|
+
if (user.chat_enabled !== false) return void 0;
|
|
72
|
+
if (user.can_signup_for_limited) return "This GitHub account is not signed up for GitHub Copilot. Sign up for Copilot Free at https://github.com/features/copilot/plans and connect again.";
|
|
73
|
+
return "This GitHub account does not have GitHub Copilot access. It needs an active Copilot subscription or a seat assigned by an organization.";
|
|
74
|
+
}
|
|
75
|
+
async function createGitHubCopilotDeviceFlow(options = {}) {
|
|
76
|
+
const domain = options.domain ?? DEFAULT_DOMAIN;
|
|
77
|
+
const response = await fetchWith(options)(`https://${domain}/login/device/code`, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: {
|
|
80
|
+
Accept: "application/json",
|
|
81
|
+
"Content-Type": "application/json"
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
client_id: CLIENT_ID,
|
|
85
|
+
scope: SCOPE
|
|
86
|
+
}),
|
|
87
|
+
signal: options.signal ?? null
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) throw new Error(`GitHub device code request failed (${response.status})`);
|
|
90
|
+
const data = await response.json();
|
|
91
|
+
if (!isObject(data)) throw new Error("Invalid GitHub device code response");
|
|
92
|
+
const { device_code, user_code, verification_uri, interval, expires_in } = data;
|
|
93
|
+
if (typeof device_code !== "string" || typeof user_code !== "string" || typeof verification_uri !== "string" || interval !== void 0 && typeof interval !== "number" || expires_in !== void 0 && typeof expires_in !== "number") throw new Error("Invalid GitHub device code response fields");
|
|
94
|
+
let parsed;
|
|
95
|
+
try {
|
|
96
|
+
parsed = new URL(verification_uri);
|
|
97
|
+
} catch {
|
|
98
|
+
throw new Error("Untrusted verification_uri in GitHub device code response");
|
|
99
|
+
}
|
|
100
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error("Untrusted verification_uri in GitHub device code response");
|
|
101
|
+
return {
|
|
102
|
+
deviceCode: device_code,
|
|
103
|
+
userCode: user_code,
|
|
104
|
+
verificationUri: parsed.href,
|
|
105
|
+
intervalSeconds: typeof interval === "number" && interval > 0 ? interval : 5,
|
|
106
|
+
...typeof expires_in === "number" && { expiresInSeconds: expires_in }
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async function pollGitHubCopilotDeviceToken(flow, options = {}) {
|
|
110
|
+
const domain = options.domain ?? DEFAULT_DOMAIN;
|
|
111
|
+
const send = fetchWith(options);
|
|
112
|
+
return pollDeviceCode({
|
|
113
|
+
intervalSeconds: flow.intervalSeconds,
|
|
114
|
+
...flow.expiresInSeconds !== void 0 && { expiresInSeconds: flow.expiresInSeconds },
|
|
115
|
+
waitBeforeFirstPoll: true,
|
|
116
|
+
...options.signal !== void 0 && { signal: options.signal },
|
|
117
|
+
poll: async () => {
|
|
118
|
+
const response = await send(`https://${domain}/login/oauth/access_token`, {
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: {
|
|
121
|
+
Accept: "application/json",
|
|
122
|
+
"Content-Type": "application/json"
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
client_id: CLIENT_ID,
|
|
126
|
+
device_code: flow.deviceCode,
|
|
127
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
128
|
+
}),
|
|
129
|
+
signal: options.signal ?? null
|
|
130
|
+
});
|
|
131
|
+
if (!response.ok) return {
|
|
132
|
+
status: "failed",
|
|
133
|
+
message: `GitHub device token poll failed (${response.status})`
|
|
134
|
+
};
|
|
135
|
+
const data = await response.json();
|
|
136
|
+
if (!isObject(data)) return {
|
|
137
|
+
status: "failed",
|
|
138
|
+
message: "Invalid GitHub device token response"
|
|
139
|
+
};
|
|
140
|
+
if (typeof data.access_token === "string" && data.access_token) return {
|
|
141
|
+
status: "complete",
|
|
142
|
+
value: data.access_token
|
|
143
|
+
};
|
|
144
|
+
const error = typeof data.error === "string" ? data.error : void 0;
|
|
145
|
+
if (error === "authorization_pending") return { status: "pending" };
|
|
146
|
+
if (error === "slow_down") return {
|
|
147
|
+
status: "slow_down",
|
|
148
|
+
...typeof data.interval === "number" && data.interval > 0 && { intervalSeconds: data.interval }
|
|
149
|
+
};
|
|
150
|
+
const description = typeof data.error_description === "string" ? `: ${data.error_description}` : "";
|
|
151
|
+
return {
|
|
152
|
+
status: "failed",
|
|
153
|
+
message: `GitHub device authorization failed: ${error ?? "unknown error"}${description}`
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Look up the account's Copilot entitlement and per-plan API endpoint.
|
|
160
|
+
*
|
|
161
|
+
* A failed or malformed lookup returns `undefined` rather than denying login —
|
|
162
|
+
* only an explicit `chat_enabled: false` answer blocks.
|
|
163
|
+
*/
|
|
164
|
+
async function fetchGitHubCopilotUser(token, options = {}) {
|
|
165
|
+
const domain = options.domain ?? DEFAULT_DOMAIN;
|
|
166
|
+
try {
|
|
167
|
+
const response = await fetchWith(options)(`https://${gitHubApiDomain(domain)}/copilot_internal/user`, {
|
|
168
|
+
headers: {
|
|
169
|
+
Accept: "application/json",
|
|
170
|
+
Authorization: `Bearer ${token}`,
|
|
171
|
+
"User-Agent": GITHUB_COPILOT_STATIC_HEADERS["User-Agent"],
|
|
172
|
+
"X-GitHub-Api-Version": USER_API_VERSION
|
|
173
|
+
},
|
|
174
|
+
signal: options.signal ?? null
|
|
175
|
+
});
|
|
176
|
+
if (!response.ok) return void 0;
|
|
177
|
+
const data = await response.json();
|
|
178
|
+
if (!isObject(data)) return void 0;
|
|
179
|
+
const endpoints = isObject(data.endpoints) ? data.endpoints : void 0;
|
|
180
|
+
return {
|
|
181
|
+
...typeof data.chat_enabled === "boolean" && { chatEnabled: data.chat_enabled },
|
|
182
|
+
...typeof data.can_signup_for_limited === "boolean" && { canSignupForLimited: data.can_signup_for_limited },
|
|
183
|
+
...typeof endpoints?.api === "string" && endpoints.api && { apiEndpoint: endpoints.api.replace(/\/+$/, "") }
|
|
184
|
+
};
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (options.signal?.aborted) throw error;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function fetchWithRateLimitRetry(send, url, init, options) {
|
|
191
|
+
const deadline = Date.now() + options.maxElapsedMs;
|
|
192
|
+
for (let retry = 0;; retry++) {
|
|
193
|
+
const response = await send(url, {
|
|
194
|
+
...init,
|
|
195
|
+
signal: options.signal ?? null
|
|
196
|
+
});
|
|
197
|
+
if (response.status !== 429 || retry >= options.maxRetries) return response;
|
|
198
|
+
const retryAfter = response.headers.get("retry-after");
|
|
199
|
+
let delayMs = 500 * 2 ** retry;
|
|
200
|
+
if (retryAfter) {
|
|
201
|
+
const seconds = Number.parseFloat(retryAfter);
|
|
202
|
+
delayMs = Number.isNaN(seconds) ? Date.parse(retryAfter) - Date.now() : seconds * 1e3;
|
|
203
|
+
if (!Number.isFinite(delayMs)) return response;
|
|
204
|
+
}
|
|
205
|
+
delayMs = Math.max(0, delayMs);
|
|
206
|
+
if (delayMs >= deadline - Date.now()) return response;
|
|
207
|
+
await response.body?.cancel();
|
|
208
|
+
await sleep(delayMs, options.signal);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The account-visible Copilot model catalog. `GET {base}/models` is the ground
|
|
213
|
+
* truth for endpoint routing (`supported_endpoints`) and enablement (`policy`,
|
|
214
|
+
* `model_picker_enabled`) — models.dev has no equivalent data.
|
|
215
|
+
*/
|
|
216
|
+
async function fetchGitHubCopilotModels(token, options = {}) {
|
|
217
|
+
const baseURL = options.baseURL ?? gitHubCopilotBaseUrl({ enterpriseUrl: options.domain });
|
|
218
|
+
const response = await fetchWithRateLimitRetry(fetchWith(options), `${baseURL}/models`, { headers: {
|
|
219
|
+
Accept: "application/json",
|
|
220
|
+
Authorization: `Bearer ${token}`,
|
|
221
|
+
...GITHUB_COPILOT_STATIC_HEADERS,
|
|
222
|
+
"X-GitHub-Api-Version": GITHUB_COPILOT_API_VERSION
|
|
223
|
+
} }, {
|
|
224
|
+
...options.signal !== void 0 && { signal: options.signal },
|
|
225
|
+
maxRetries: options.maxRetries ?? DEFAULT_MODELS_RETRY.maxRetries,
|
|
226
|
+
maxElapsedMs: options.maxElapsedMs ?? DEFAULT_MODELS_RETRY.maxElapsedMs
|
|
227
|
+
});
|
|
228
|
+
if (!response.ok) throw new Error(`GitHub Copilot models request failed (${response.status})`);
|
|
229
|
+
const data = await response.json();
|
|
230
|
+
const list = isObject(data) ? data.data : void 0;
|
|
231
|
+
if (!Array.isArray(list)) throw new Error("Invalid GitHub Copilot models response");
|
|
232
|
+
return list.flatMap((raw) => {
|
|
233
|
+
if (!isObject(raw) || typeof raw.id !== "string") return [];
|
|
234
|
+
const capabilities = isObject(raw.capabilities) ? raw.capabilities : void 0;
|
|
235
|
+
const supports = isObject(capabilities?.supports) ? capabilities.supports : void 0;
|
|
236
|
+
const policy = isObject(raw.policy) ? raw.policy : void 0;
|
|
237
|
+
return [{
|
|
238
|
+
id: raw.id,
|
|
239
|
+
supportedEndpoints: Array.isArray(raw.supported_endpoints) ? raw.supported_endpoints.filter((entry) => typeof entry === "string") : [],
|
|
240
|
+
...typeof policy?.state === "string" && { policyState: policy.state },
|
|
241
|
+
pickerEnabled: raw.model_picker_enabled === true,
|
|
242
|
+
toolCalls: supports?.tool_calls !== false
|
|
243
|
+
}];
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Enable models the account has not configured yet (`policy.state ===
|
|
248
|
+
* "unconfigured"`). Best effort per model; a rate limit that outlives the
|
|
249
|
+
* retry budget stops the batch.
|
|
250
|
+
*/
|
|
251
|
+
async function enableGitHubCopilotModels(token, modelIds, options = {}) {
|
|
252
|
+
const baseURL = options.baseURL ?? gitHubCopilotBaseUrl({ enterpriseUrl: options.domain });
|
|
253
|
+
const send = fetchWith(options);
|
|
254
|
+
const enabled = [];
|
|
255
|
+
for (const modelId of modelIds) try {
|
|
256
|
+
const response = await fetchWithRateLimitRetry(send, `${baseURL}/models/${modelId}/policy`, {
|
|
257
|
+
method: "POST",
|
|
258
|
+
headers: {
|
|
259
|
+
"Content-Type": "application/json",
|
|
260
|
+
Authorization: `Bearer ${token}`,
|
|
261
|
+
...GITHUB_COPILOT_STATIC_HEADERS,
|
|
262
|
+
"openai-intent": "chat-policy",
|
|
263
|
+
"x-interaction-type": "chat-policy"
|
|
264
|
+
},
|
|
265
|
+
body: JSON.stringify({ state: "enabled" })
|
|
266
|
+
}, {
|
|
267
|
+
...options.signal !== void 0 && { signal: options.signal },
|
|
268
|
+
maxRetries: 2,
|
|
269
|
+
maxElapsedMs: 5e3
|
|
270
|
+
});
|
|
271
|
+
if (response.status === 429) break;
|
|
272
|
+
if (response.ok) enabled.push(modelId);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (options.signal?.aborted) throw error;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
return enabled;
|
|
278
|
+
}
|
|
279
|
+
function isGitHubCopilotCredentials(value) {
|
|
280
|
+
if (!isObject(value)) return false;
|
|
281
|
+
return typeof value.access === "string" && typeof value.refresh === "string" && typeof value.expires === "number";
|
|
282
|
+
}
|
|
283
|
+
var JsonGitHubCopilotAuthStorage = class {
|
|
284
|
+
storage;
|
|
285
|
+
constructor(options = {}) {
|
|
286
|
+
this.storage = new JsonAuthStorage({
|
|
287
|
+
providerId: options.providerId ?? DEFAULT_PROVIDER_ID,
|
|
288
|
+
isCredentials: isGitHubCopilotCredentials,
|
|
289
|
+
...options.path !== void 0 && { path: options.path }
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
get path() {
|
|
293
|
+
return this.storage.path;
|
|
294
|
+
}
|
|
295
|
+
get providerId() {
|
|
296
|
+
return this.storage.providerId;
|
|
297
|
+
}
|
|
298
|
+
get() {
|
|
299
|
+
return this.storage.get();
|
|
300
|
+
}
|
|
301
|
+
set(credentials) {
|
|
302
|
+
return this.storage.set(credentials);
|
|
303
|
+
}
|
|
304
|
+
clear() {
|
|
305
|
+
return this.storage.clear();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
var GitHubCopilotOAuthClient = class {
|
|
309
|
+
storage;
|
|
310
|
+
constructor(options = {}) {
|
|
311
|
+
this.storage = options.storage ?? new JsonGitHubCopilotAuthStorage();
|
|
312
|
+
}
|
|
313
|
+
async login(options) {
|
|
314
|
+
const enterpriseUrl = options.enterpriseUrl?.trim() || void 0;
|
|
315
|
+
const domain = normalizeGitHubDomain(enterpriseUrl) ?? DEFAULT_DOMAIN;
|
|
316
|
+
if (enterpriseUrl && !normalizeGitHubDomain(enterpriseUrl)) throw new Error("Invalid GitHub Enterprise URL/domain");
|
|
317
|
+
const flow = await createGitHubCopilotDeviceFlow({
|
|
318
|
+
domain,
|
|
319
|
+
fetch: options.fetch,
|
|
320
|
+
signal: options.signal
|
|
321
|
+
});
|
|
322
|
+
options.onAuth({
|
|
323
|
+
url: flow.verificationUri,
|
|
324
|
+
userCode: flow.userCode,
|
|
325
|
+
instructions: "Complete the GitHub device authorization to finish GitHub Copilot authentication."
|
|
326
|
+
});
|
|
327
|
+
const access = await pollGitHubCopilotDeviceToken(flow, {
|
|
328
|
+
domain,
|
|
329
|
+
fetch: options.fetch,
|
|
330
|
+
...options.signal !== void 0 && { signal: options.signal }
|
|
331
|
+
});
|
|
332
|
+
const user = await fetchGitHubCopilotUser(access, {
|
|
333
|
+
domain,
|
|
334
|
+
fetch: options.fetch,
|
|
335
|
+
signal: options.signal
|
|
336
|
+
});
|
|
337
|
+
const denied = user ? gitHubCopilotEntitlementError({
|
|
338
|
+
chat_enabled: user.chatEnabled,
|
|
339
|
+
can_signup_for_limited: user.canSignupForLimited
|
|
340
|
+
}) : void 0;
|
|
341
|
+
if (denied) throw new Error(denied);
|
|
342
|
+
const apiEndpoint = user?.apiEndpoint ?? gitHubCopilotBaseUrl({ enterpriseUrl: domain === DEFAULT_DOMAIN ? void 0 : domain });
|
|
343
|
+
options.onProgress?.("Fetching available Copilot models...");
|
|
344
|
+
let availableModelIds;
|
|
345
|
+
let unconfigured = [];
|
|
346
|
+
try {
|
|
347
|
+
const usable = (await fetchGitHubCopilotModels(access, {
|
|
348
|
+
baseURL: apiEndpoint,
|
|
349
|
+
fetch: options.fetch,
|
|
350
|
+
...options.signal !== void 0 && { signal: options.signal }
|
|
351
|
+
})).filter((model) => model.toolCalls && model.policyState !== "disabled");
|
|
352
|
+
const pickerIds = usable.filter((model) => model.pickerEnabled).map((model) => model.id);
|
|
353
|
+
const host = new URL(apiEndpoint).hostname;
|
|
354
|
+
const policyFallback = pickerIds.length === 0 && host === "api.individual.githubcopilot.com";
|
|
355
|
+
availableModelIds = pickerIds.length ? pickerIds : policyFallback ? usable.filter((model) => model.policyState === "enabled").map((model) => model.id) : [];
|
|
356
|
+
unconfigured = usable.filter((model) => model.policyState === "unconfigured").map((model) => model.id);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
if (options.signal?.aborted) throw error;
|
|
359
|
+
options.onProgress?.("Could not fetch the Copilot model catalog; continuing without it.");
|
|
360
|
+
}
|
|
361
|
+
if (options.enableModels && unconfigured.length > 0) {
|
|
362
|
+
options.onProgress?.("Enabling Copilot models...");
|
|
363
|
+
const enabled = await enableGitHubCopilotModels(access, unconfigured, {
|
|
364
|
+
baseURL: apiEndpoint,
|
|
365
|
+
fetch: options.fetch,
|
|
366
|
+
...options.signal !== void 0 && { signal: options.signal }
|
|
367
|
+
});
|
|
368
|
+
availableModelIds = [.../* @__PURE__ */ new Set([...availableModelIds ?? [], ...enabled])];
|
|
369
|
+
}
|
|
370
|
+
const credentials = {
|
|
371
|
+
access,
|
|
372
|
+
refresh: access,
|
|
373
|
+
expires: 0,
|
|
374
|
+
...domain !== DEFAULT_DOMAIN && { enterpriseUrl: domain },
|
|
375
|
+
apiEndpoint,
|
|
376
|
+
...availableModelIds !== void 0 && availableModelIds.length > 0 && { availableModelIds }
|
|
377
|
+
};
|
|
378
|
+
await this.storage.set(credentials);
|
|
379
|
+
return credentials;
|
|
380
|
+
}
|
|
381
|
+
/** The stored `ghu_` token, returned as-is — it never expires. */
|
|
382
|
+
async getCredentials() {
|
|
383
|
+
return this.storage.get();
|
|
384
|
+
}
|
|
385
|
+
async getApiKey() {
|
|
386
|
+
return (await this.getCredentials())?.access;
|
|
387
|
+
}
|
|
388
|
+
async getHeaders() {
|
|
389
|
+
const credentials = await this.getCredentials();
|
|
390
|
+
return credentials ? gitHubCopilotHeaders(credentials) : void 0;
|
|
391
|
+
}
|
|
392
|
+
async logout() {
|
|
393
|
+
await this.storage.clear();
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
function gitHubCopilotHeaders(credentials) {
|
|
397
|
+
return {
|
|
398
|
+
Authorization: `Bearer ${credentials.access}`,
|
|
399
|
+
...GITHUB_COPILOT_STATIC_HEADERS,
|
|
400
|
+
"X-GitHub-Api-Version": GITHUB_COPILOT_API_VERSION
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Resolve the GitHub Copilot API credential: `COPILOT_GITHUB_TOKEN` first, then
|
|
405
|
+
* an explicit `auth --github-copilot` login from codework auth.json.
|
|
406
|
+
*
|
|
407
|
+
* Nothing else is consulted, by design. `GITHUB_TOKEN` and `GH_TOKEN` are
|
|
408
|
+
* usually set for git or `gh` and carry no Copilot access — in GitHub Actions
|
|
409
|
+
* `GITHUB_TOKEN` is a workflow token — so reading them turns a working login
|
|
410
|
+
* into an unexplained 401. Credentials another application stored for itself
|
|
411
|
+
* (an editor's Copilot sign-in) are not consulted either: a token codework was
|
|
412
|
+
* never given should not silently become the identity its runs bill and act as.
|
|
413
|
+
*
|
|
414
|
+
* Returns undefined when nothing is available; the device flow only runs via
|
|
415
|
+
* `GitHubCopilotOAuthClient#login`.
|
|
416
|
+
*/
|
|
417
|
+
async function getGitHubCopilotApiKey(options = {}) {
|
|
418
|
+
const dedicated = readEnv(GITHUB_COPILOT_API_KEY_ENV);
|
|
419
|
+
if (dedicated) return dedicated;
|
|
420
|
+
return new GitHubCopilotOAuthClient(options).getApiKey();
|
|
421
|
+
}
|
|
422
|
+
//#endregion
|
|
423
|
+
export { createGitHubCopilotDeviceFlow as a, fetchGitHubCopilotUser as c, gitHubCopilotBaseUrl as d, gitHubCopilotEntitlementError as f, pollGitHubCopilotDeviceToken as h, JsonGitHubCopilotAuthStorage as i, getGitHubCopilotApiKey as l, normalizeGitHubDomain as m, GITHUB_COPILOT_PROVIDER_ID as n, enableGitHubCopilotModels as o, gitHubCopilotHeaders as p, GitHubCopilotOAuthClient as r, fetchGitHubCopilotModels as s, GITHUB_COPILOT_API_KEY_ENV as t, gitHubApiDomain as u };
|
|
424
|
+
|
|
425
|
+
//# sourceMappingURL=copilot-CcD3g6H8.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot-CcD3g6H8.mjs","names":[],"sources":["../../src/oauth/github/copilot.ts"],"sourcesContent":["/**\n * GitHub Copilot OAuth.\n *\n * The device flow yields a plain GitHub OAuth token (`ghu_`/`gho_`), which the\n * Copilot API accepts directly as a Bearer credential — no session-token\n * exchange and no refresh path. `getGitHubCopilotApiKey` resolves a token from\n * `COPILOT_GITHUB_TOKEN` or the codework auth.json store, and nothing else;\n * `GitHubCopilotOAuthClient#login` performs the device flow.\n */\n\nimport {\n\tGITHUB_COPILOT_API_VERSION,\n\tGITHUB_COPILOT_STATIC_HEADERS,\n} from \"../../providers/github-copilot/copilot-headers.ts\";\nimport { pollDeviceCode } from \"../devcode.ts\";\nimport { JsonAuthStorage, isObject, readEnv } from \"../storage.ts\";\n\nexport const GITHUB_COPILOT_API_KEY_ENV = \"COPILOT_GITHUB_TOKEN\";\n\n// The official GitHub Copilot OAuth client id — the same one copilot.vim,\n// copilot.lua, and pi use, so the consent screen shows \"GitHub Copilot\".\nconst CLIENT_ID = \"Iv1.b507a08c87ecfe98\";\nconst SCOPE = \"read:user\";\nconst DEFAULT_DOMAIN = \"github.com\";\nconst DEFAULT_BASE_URL = \"https://api.githubcopilot.com\";\nconst USER_API_VERSION = \"2025-04-01\";\nconst DEFAULT_PROVIDER_ID = \"github-copilot\";\nexport const GITHUB_COPILOT_PROVIDER_ID = DEFAULT_PROVIDER_ID;\nconst DEFAULT_MODELS_RETRY = { maxRetries: 2, maxElapsedMs: 5000 };\n\nexport type GitHubCopilotOAuthCredentials = {\n\t/** GitHub OAuth token (`ghu_`/`gho_`), used directly as the Copilot Bearer credential. */\n\taccess: string;\n\t/** Same token; kept for shape parity with other OAuth credentials. */\n\trefresh: string;\n\t/** Always 0 — GitHub OAuth tokens do not expire; re-login only on persistent 401. */\n\texpires: number;\n\tenterpriseUrl?: string;\n\t/** `endpoints.api` from `/copilot_internal/user` — the plan-specific Copilot API host. */\n\tapiEndpoint?: string;\n\t/** Model ids visible to the account at login time, from `GET {base}/models`. */\n\tavailableModelIds?: string[];\n};\n\nexport type GitHubCopilotDeviceFlow = {\n\tdeviceCode: string;\n\tuserCode: string;\n\tverificationUri: string;\n\tintervalSeconds: number;\n\texpiresInSeconds?: number;\n};\n\nexport type GitHubCopilotUser = {\n\tchatEnabled?: boolean;\n\tcanSignupForLimited?: boolean;\n\tapiEndpoint?: string;\n};\n\nexport type GitHubCopilotRemoteModel = {\n\tid: string;\n\tsupportedEndpoints: string[];\n\tpolicyState?: string;\n\tpickerEnabled: boolean;\n\ttoolCalls: boolean;\n};\n\nexport type GitHubCopilotLoginOptions = {\n\tenterpriseUrl?: string | undefined;\n\t/** Enable `policy.state === \"unconfigured\"` catalog models after login. Off by default — it writes account policy. */\n\tenableModels?: boolean | undefined;\n\tsignal?: AbortSignal | undefined;\n\tfetch?: typeof globalThis.fetch | undefined;\n\tonAuth: (info: { url: string; userCode: string; instructions?: string | undefined }) => void;\n\tonProgress?: ((message: string) => void) | undefined;\n};\n\nexport interface GitHubCopilotAuthStorage {\n\tget(): Promise<GitHubCopilotOAuthCredentials | undefined>;\n\tset(credentials: GitHubCopilotOAuthCredentials): Promise<void>;\n\tclear(): Promise<void>;\n}\n\nexport type JsonGitHubCopilotAuthStorageOptions = {\n\tpath?: string;\n\tproviderId?: string;\n};\n\ntype FetchLike = typeof globalThis.fetch;\n\nfunction fetchWith(options: { fetch?: FetchLike | undefined }): FetchLike {\n\treturn options.fetch ?? globalThis.fetch;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new Error(\"GitHub Copilot login cancelled\"));\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(() => {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst onAbort = () => {\n\t\t\tclearTimeout(timeout);\n\t\t\treject(new Error(\"GitHub Copilot login cancelled\"));\n\t\t};\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\nexport function normalizeGitHubDomain(input: string | undefined): string | undefined {\n\tconst trimmed = input?.trim();\n\tif (!trimmed) return undefined;\n\ttry {\n\t\tconst url = trimmed.includes(\"://\") ? new URL(trimmed) : new URL(`https://${trimmed}`);\n\t\treturn url.hostname;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** The `api.` host serving `copilot_internal/*` for a given GitHub domain. */\nexport function gitHubApiDomain(domain: string): string {\n\treturn domain === DEFAULT_DOMAIN ? \"api.github.com\" : `api.${domain}`;\n}\n\n/** The Copilot inference host for a credential or enterprise domain. */\nexport function gitHubCopilotBaseUrl(options?: {\n\tenterpriseUrl?: string | undefined;\n\tapiEndpoint?: string | undefined;\n}): string {\n\tconst apiEndpoint = options?.apiEndpoint;\n\tif (typeof apiEndpoint === \"string\" && apiEndpoint) return apiEndpoint.replace(/\\/+$/, \"\");\n\tconst domain = normalizeGitHubDomain(options?.enterpriseUrl);\n\treturn domain ? `https://copilot-api.${domain}` : DEFAULT_BASE_URL;\n}\n\n/**\n * GitHub reports Copilot access on `/copilot_internal/user`; OAuth itself\n * succeeds for any GitHub account, so this is the only signal that the account\n * can chat.\n */\nexport function gitHubCopilotEntitlementError(user: {\n\tchat_enabled?: boolean | undefined;\n\tcan_signup_for_limited?: boolean | undefined;\n}): string | undefined {\n\tif (user.chat_enabled !== false) return undefined;\n\tif (user.can_signup_for_limited) {\n\t\treturn \"This GitHub account is not signed up for GitHub Copilot. Sign up for Copilot Free at https://github.com/features/copilot/plans and connect again.\";\n\t}\n\treturn \"This GitHub account does not have GitHub Copilot access. It needs an active Copilot subscription or a seat assigned by an organization.\";\n}\n\nexport async function createGitHubCopilotDeviceFlow(\n\toptions: { domain?: string | undefined; fetch?: FetchLike | undefined; signal?: AbortSignal | undefined } = {},\n): Promise<GitHubCopilotDeviceFlow> {\n\tconst domain = options.domain ?? DEFAULT_DOMAIN;\n\tconst response = await fetchWith(options)(`https://${domain}/login/device/code`, {\n\t\tmethod: \"POST\",\n\t\theaders: { Accept: \"application/json\", \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ client_id: CLIENT_ID, scope: SCOPE }),\n\t\tsignal: options.signal ?? null,\n\t});\n\tif (!response.ok) {\n\t\tthrow new Error(`GitHub device code request failed (${response.status})`);\n\t}\n\n\tconst data: unknown = await response.json();\n\tif (!isObject(data)) throw new Error(\"Invalid GitHub device code response\");\n\tconst { device_code, user_code, verification_uri, interval, expires_in } = data;\n\tif (\n\t\ttypeof device_code !== \"string\" ||\n\t\ttypeof user_code !== \"string\" ||\n\t\ttypeof verification_uri !== \"string\" ||\n\t\t(interval !== undefined && typeof interval !== \"number\") ||\n\t\t(expires_in !== undefined && typeof expires_in !== \"number\")\n\t) {\n\t\tthrow new Error(\"Invalid GitHub device code response fields\");\n\t}\n\n\t// The verification URI is opened in the user's browser; force it to be a URL\n\t// so `open` cannot be tricked into launching something else.\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(verification_uri);\n\t} catch {\n\t\tthrow new Error(\"Untrusted verification_uri in GitHub device code response\");\n\t}\n\tif (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") {\n\t\tthrow new Error(\"Untrusted verification_uri in GitHub device code response\");\n\t}\n\n\treturn {\n\t\tdeviceCode: device_code,\n\t\tuserCode: user_code,\n\t\tverificationUri: parsed.href,\n\t\tintervalSeconds: typeof interval === \"number\" && interval > 0 ? interval : 5,\n\t\t...(typeof expires_in === \"number\" && { expiresInSeconds: expires_in }),\n\t};\n}\n\nexport async function pollGitHubCopilotDeviceToken(\n\tflow: GitHubCopilotDeviceFlow,\n\toptions: { domain?: string | undefined; fetch?: FetchLike | undefined; signal?: AbortSignal | undefined } = {},\n): Promise<string> {\n\tconst domain = options.domain ?? DEFAULT_DOMAIN;\n\tconst send = fetchWith(options);\n\n\treturn pollDeviceCode<string>({\n\t\tintervalSeconds: flow.intervalSeconds,\n\t\t...(flow.expiresInSeconds !== undefined && { expiresInSeconds: flow.expiresInSeconds }),\n\t\twaitBeforeFirstPoll: true,\n\t\t...(options.signal !== undefined && { signal: options.signal }),\n\t\tpoll: async () => {\n\t\t\tconst response = await send(`https://${domain}/login/oauth/access_token`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { Accept: \"application/json\", \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\t\tdevice_code: flow.deviceCode,\n\t\t\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal ?? null,\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\treturn { status: \"failed\", message: `GitHub device token poll failed (${response.status})` };\n\t\t\t}\n\n\t\t\tconst data: unknown = await response.json();\n\t\t\tif (!isObject(data)) return { status: \"failed\", message: \"Invalid GitHub device token response\" };\n\n\t\t\tif (typeof data.access_token === \"string\" && data.access_token) {\n\t\t\t\treturn { status: \"complete\", value: data.access_token };\n\t\t\t}\n\n\t\t\tconst error = typeof data.error === \"string\" ? data.error : undefined;\n\t\t\tif (error === \"authorization_pending\") return { status: \"pending\" };\n\t\t\tif (error === \"slow_down\") {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"slow_down\",\n\t\t\t\t\t...(typeof data.interval === \"number\" && data.interval > 0 && { intervalSeconds: data.interval }),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst description = typeof data.error_description === \"string\" ? `: ${data.error_description}` : \"\";\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `GitHub device authorization failed: ${error ?? \"unknown error\"}${description}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\n/**\n * Look up the account's Copilot entitlement and per-plan API endpoint.\n *\n * A failed or malformed lookup returns `undefined` rather than denying login —\n * only an explicit `chat_enabled: false` answer blocks.\n */\nexport async function fetchGitHubCopilotUser(\n\ttoken: string,\n\toptions: { domain?: string | undefined; fetch?: FetchLike | undefined; signal?: AbortSignal | undefined } = {},\n): Promise<GitHubCopilotUser | undefined> {\n\tconst domain = options.domain ?? DEFAULT_DOMAIN;\n\ttry {\n\t\tconst response = await fetchWith(options)(`https://${gitHubApiDomain(domain)}/copilot_internal/user`, {\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\"User-Agent\": GITHUB_COPILOT_STATIC_HEADERS[\"User-Agent\"],\n\t\t\t\t\"X-GitHub-Api-Version\": USER_API_VERSION,\n\t\t\t},\n\t\t\tsignal: options.signal ?? null,\n\t\t});\n\t\tif (!response.ok) return undefined;\n\t\tconst data: unknown = await response.json();\n\t\tif (!isObject(data)) return undefined;\n\t\tconst endpoints = isObject(data.endpoints) ? data.endpoints : undefined;\n\t\treturn {\n\t\t\t...(typeof data.chat_enabled === \"boolean\" && { chatEnabled: data.chat_enabled }),\n\t\t\t...(typeof data.can_signup_for_limited === \"boolean\" && { canSignupForLimited: data.can_signup_for_limited }),\n\t\t\t...(typeof endpoints?.api === \"string\" && endpoints.api && { apiEndpoint: endpoints.api.replace(/\\/+$/, \"\") }),\n\t\t};\n\t} catch (error) {\n\t\tif (options.signal?.aborted) throw error;\n\t\treturn undefined;\n\t}\n}\n\nasync function fetchWithRateLimitRetry(\n\tsend: FetchLike,\n\turl: string,\n\tinit: RequestInit,\n\toptions: { signal?: AbortSignal; maxRetries: number; maxElapsedMs: number },\n): Promise<Response> {\n\tconst deadline = Date.now() + options.maxElapsedMs;\n\tfor (let retry = 0; ; retry++) {\n\t\tconst response = await send(url, { ...init, signal: options.signal ?? null });\n\t\tif (response.status !== 429 || retry >= options.maxRetries) return response;\n\n\t\tconst retryAfter = response.headers.get(\"retry-after\");\n\t\tlet delayMs = 500 * 2 ** retry;\n\t\tif (retryAfter) {\n\t\t\tconst seconds = Number.parseFloat(retryAfter);\n\t\t\tdelayMs = Number.isNaN(seconds) ? Date.parse(retryAfter) - Date.now() : seconds * 1000;\n\t\t\tif (!Number.isFinite(delayMs)) return response;\n\t\t}\n\t\tdelayMs = Math.max(0, delayMs);\n\t\tif (delayMs >= deadline - Date.now()) return response;\n\t\tawait response.body?.cancel();\n\t\tawait sleep(delayMs, options.signal);\n\t}\n}\n\n/**\n * The account-visible Copilot model catalog. `GET {base}/models` is the ground\n * truth for endpoint routing (`supported_endpoints`) and enablement (`policy`,\n * `model_picker_enabled`) — models.dev has no equivalent data.\n */\nexport async function fetchGitHubCopilotModels(\n\ttoken: string,\n\toptions: {\n\t\tbaseURL?: string | undefined;\n\t\tdomain?: string | undefined;\n\t\tfetch?: FetchLike | undefined;\n\t\tsignal?: AbortSignal | undefined;\n\t\tmaxRetries?: number | undefined;\n\t\tmaxElapsedMs?: number | undefined;\n\t} = {},\n): Promise<GitHubCopilotRemoteModel[]> {\n\tconst baseURL = options.baseURL ?? gitHubCopilotBaseUrl({ enterpriseUrl: options.domain });\n\tconst response = await fetchWithRateLimitRetry(\n\t\tfetchWith(options),\n\t\t`${baseURL}/models`,\n\t\t{\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t...GITHUB_COPILOT_STATIC_HEADERS,\n\t\t\t\t\"X-GitHub-Api-Version\": GITHUB_COPILOT_API_VERSION,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t...(options.signal !== undefined && { signal: options.signal }),\n\t\t\tmaxRetries: options.maxRetries ?? DEFAULT_MODELS_RETRY.maxRetries,\n\t\t\tmaxElapsedMs: options.maxElapsedMs ?? DEFAULT_MODELS_RETRY.maxElapsedMs,\n\t\t},\n\t);\n\tif (!response.ok) {\n\t\tthrow new Error(`GitHub Copilot models request failed (${response.status})`);\n\t}\n\n\tconst data: unknown = await response.json();\n\tconst list = isObject(data) ? data.data : undefined;\n\tif (!Array.isArray(list)) throw new Error(\"Invalid GitHub Copilot models response\");\n\n\treturn list.flatMap((raw) => {\n\t\tif (!isObject(raw) || typeof raw.id !== \"string\") return [];\n\t\tconst capabilities = isObject(raw.capabilities) ? raw.capabilities : undefined;\n\t\tconst supports = isObject(capabilities?.supports) ? capabilities.supports : undefined;\n\t\tconst policy = isObject(raw.policy) ? raw.policy : undefined;\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: raw.id,\n\t\t\t\tsupportedEndpoints: Array.isArray(raw.supported_endpoints)\n\t\t\t\t\t? raw.supported_endpoints.filter((entry): entry is string => typeof entry === \"string\")\n\t\t\t\t\t: [],\n\t\t\t\t...(typeof policy?.state === \"string\" && { policyState: policy.state }),\n\t\t\t\tpickerEnabled: raw.model_picker_enabled === true,\n\t\t\t\ttoolCalls: supports?.tool_calls !== false,\n\t\t\t},\n\t\t];\n\t});\n}\n\n/**\n * Enable models the account has not configured yet (`policy.state ===\n * \"unconfigured\"`). Best effort per model; a rate limit that outlives the\n * retry budget stops the batch.\n */\nexport async function enableGitHubCopilotModels(\n\ttoken: string,\n\tmodelIds: readonly string[],\n\toptions: {\n\t\tbaseURL?: string | undefined;\n\t\tdomain?: string | undefined;\n\t\tfetch?: FetchLike | undefined;\n\t\tsignal?: AbortSignal | undefined;\n\t} = {},\n): Promise<string[]> {\n\tconst baseURL = options.baseURL ?? gitHubCopilotBaseUrl({ enterpriseUrl: options.domain });\n\tconst send = fetchWith(options);\n\tconst enabled: string[] = [];\n\tfor (const modelId of modelIds) {\n\t\ttry {\n\t\t\tconst response = await fetchWithRateLimitRetry(\n\t\t\t\tsend,\n\t\t\t\t`${baseURL}/models/${modelId}/policy`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\t\t...GITHUB_COPILOT_STATIC_HEADERS,\n\t\t\t\t\t\t\"openai-intent\": \"chat-policy\",\n\t\t\t\t\t\t\"x-interaction-type\": \"chat-policy\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({ state: \"enabled\" }),\n\t\t\t\t},\n\t\t\t\t{ ...(options.signal !== undefined && { signal: options.signal }), maxRetries: 2, maxElapsedMs: 5000 },\n\t\t\t);\n\t\t\tif (response.status === 429) break;\n\t\t\tif (response.ok) enabled.push(modelId);\n\t\t} catch (error) {\n\t\t\tif (options.signal?.aborted) throw error;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn enabled;\n}\n\nfunction isGitHubCopilotCredentials(value: unknown): value is GitHubCopilotOAuthCredentials {\n\tif (!isObject(value)) return false;\n\treturn typeof value.access === \"string\" && typeof value.refresh === \"string\" && typeof value.expires === \"number\";\n}\n\nexport class JsonGitHubCopilotAuthStorage implements GitHubCopilotAuthStorage {\n\tprivate readonly storage: JsonAuthStorage<GitHubCopilotOAuthCredentials>;\n\n\tconstructor(options: JsonGitHubCopilotAuthStorageOptions = {}) {\n\t\tthis.storage = new JsonAuthStorage({\n\t\t\tproviderId: options.providerId ?? DEFAULT_PROVIDER_ID,\n\t\t\tisCredentials: isGitHubCopilotCredentials,\n\t\t\t...(options.path !== undefined && { path: options.path }),\n\t\t});\n\t}\n\n\tget path(): string {\n\t\treturn this.storage.path;\n\t}\n\n\tget providerId(): string {\n\t\treturn this.storage.providerId;\n\t}\n\n\tget(): Promise<GitHubCopilotOAuthCredentials | undefined> {\n\t\treturn this.storage.get();\n\t}\n\n\tset(credentials: GitHubCopilotOAuthCredentials): Promise<void> {\n\t\treturn this.storage.set(credentials);\n\t}\n\n\tclear(): Promise<void> {\n\t\treturn this.storage.clear();\n\t}\n}\n\nexport type GitHubCopilotOAuthClientOptions = {\n\tstorage?: GitHubCopilotAuthStorage;\n};\n\nexport class GitHubCopilotOAuthClient {\n\treadonly storage: GitHubCopilotAuthStorage;\n\n\tconstructor(options: GitHubCopilotOAuthClientOptions = {}) {\n\t\tthis.storage = options.storage ?? new JsonGitHubCopilotAuthStorage();\n\t}\n\n\tasync login(options: GitHubCopilotLoginOptions): Promise<GitHubCopilotOAuthCredentials> {\n\t\tconst enterpriseUrl = options.enterpriseUrl?.trim() || undefined;\n\t\tconst domain = normalizeGitHubDomain(enterpriseUrl) ?? DEFAULT_DOMAIN;\n\t\tif (enterpriseUrl && !normalizeGitHubDomain(enterpriseUrl)) {\n\t\t\tthrow new Error(\"Invalid GitHub Enterprise URL/domain\");\n\t\t}\n\n\t\tconst flow = await createGitHubCopilotDeviceFlow({ domain, fetch: options.fetch, signal: options.signal });\n\t\toptions.onAuth({\n\t\t\turl: flow.verificationUri,\n\t\t\tuserCode: flow.userCode,\n\t\t\tinstructions: \"Complete the GitHub device authorization to finish GitHub Copilot authentication.\",\n\t\t});\n\n\t\tconst access = await pollGitHubCopilotDeviceToken(flow, {\n\t\t\tdomain,\n\t\t\tfetch: options.fetch,\n\t\t\t...(options.signal !== undefined && { signal: options.signal }),\n\t\t});\n\n\t\tconst user = await fetchGitHubCopilotUser(access, { domain, fetch: options.fetch, signal: options.signal });\n\t\tconst denied = user\n\t\t\t? gitHubCopilotEntitlementError({\n\t\t\t\t\tchat_enabled: user.chatEnabled,\n\t\t\t\t\tcan_signup_for_limited: user.canSignupForLimited,\n\t\t\t\t})\n\t\t\t: undefined;\n\t\tif (denied) throw new Error(denied);\n\n\t\tconst apiEndpoint =\n\t\t\tuser?.apiEndpoint ?? gitHubCopilotBaseUrl({ enterpriseUrl: domain === DEFAULT_DOMAIN ? undefined : domain });\n\n\t\toptions.onProgress?.(\"Fetching available Copilot models...\");\n\t\tlet availableModelIds: string[] | undefined;\n\t\tlet unconfigured: string[] = [];\n\t\ttry {\n\t\t\tconst models = await fetchGitHubCopilotModels(access, {\n\t\t\t\tbaseURL: apiEndpoint,\n\t\t\t\tfetch: options.fetch,\n\t\t\t\t...(options.signal !== undefined && { signal: options.signal }),\n\t\t\t});\n\t\t\tconst usable = models.filter((model) => model.toolCalls && model.policyState !== \"disabled\");\n\t\t\tconst pickerIds = usable.filter((model) => model.pickerEnabled).map((model) => model.id);\n\t\t\t// Some Individual accounts report picker=false for every model despite\n\t\t\t// enabled policies; only that endpoint gets the policy fallback.\n\t\t\tconst host = new URL(apiEndpoint).hostname;\n\t\t\tconst policyFallback = pickerIds.length === 0 && host === \"api.individual.githubcopilot.com\";\n\t\t\tavailableModelIds = pickerIds.length\n\t\t\t\t? pickerIds\n\t\t\t\t: policyFallback\n\t\t\t\t\t? usable.filter((model) => model.policyState === \"enabled\").map((model) => model.id)\n\t\t\t\t\t: [];\n\t\t\tunconfigured = usable.filter((model) => model.policyState === \"unconfigured\").map((model) => model.id);\n\t\t} catch (error) {\n\t\t\tif (options.signal?.aborted) throw error;\n\t\t\toptions.onProgress?.(\"Could not fetch the Copilot model catalog; continuing without it.\");\n\t\t}\n\n\t\tif (options.enableModels && unconfigured.length > 0) {\n\t\t\toptions.onProgress?.(\"Enabling Copilot models...\");\n\t\t\tconst enabled = await enableGitHubCopilotModels(access, unconfigured, {\n\t\t\t\tbaseURL: apiEndpoint,\n\t\t\t\tfetch: options.fetch,\n\t\t\t\t...(options.signal !== undefined && { signal: options.signal }),\n\t\t\t});\n\t\t\tavailableModelIds = [...new Set([...(availableModelIds ?? []), ...enabled])];\n\t\t}\n\n\t\tconst credentials: GitHubCopilotOAuthCredentials = {\n\t\t\taccess,\n\t\t\trefresh: access,\n\t\t\texpires: 0,\n\t\t\t...(domain !== DEFAULT_DOMAIN && { enterpriseUrl: domain }),\n\t\t\tapiEndpoint,\n\t\t\t...(availableModelIds !== undefined && availableModelIds.length > 0 && { availableModelIds }),\n\t\t};\n\t\tawait this.storage.set(credentials);\n\t\treturn credentials;\n\t}\n\n\t/** The stored `ghu_` token, returned as-is — it never expires. */\n\tasync getCredentials(): Promise<GitHubCopilotOAuthCredentials | undefined> {\n\t\treturn this.storage.get();\n\t}\n\n\tasync getApiKey(): Promise<string | undefined> {\n\t\treturn (await this.getCredentials())?.access;\n\t}\n\n\tasync getHeaders(): Promise<Record<string, string> | undefined> {\n\t\tconst credentials = await this.getCredentials();\n\t\treturn credentials ? gitHubCopilotHeaders(credentials) : undefined;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tawait this.storage.clear();\n\t}\n}\n\nexport function gitHubCopilotHeaders(credentials: GitHubCopilotOAuthCredentials): Record<string, string> {\n\treturn {\n\t\tAuthorization: `Bearer ${credentials.access}`,\n\t\t...GITHUB_COPILOT_STATIC_HEADERS,\n\t\t\"X-GitHub-Api-Version\": GITHUB_COPILOT_API_VERSION,\n\t};\n}\n\n/**\n * Resolve the GitHub Copilot API credential: `COPILOT_GITHUB_TOKEN` first, then\n * an explicit `auth --github-copilot` login from codework auth.json.\n *\n * Nothing else is consulted, by design. `GITHUB_TOKEN` and `GH_TOKEN` are\n * usually set for git or `gh` and carry no Copilot access — in GitHub Actions\n * `GITHUB_TOKEN` is a workflow token — so reading them turns a working login\n * into an unexplained 401. Credentials another application stored for itself\n * (an editor's Copilot sign-in) are not consulted either: a token codework was\n * never given should not silently become the identity its runs bill and act as.\n *\n * Returns undefined when nothing is available; the device flow only runs via\n * `GitHubCopilotOAuthClient#login`.\n */\nexport async function getGitHubCopilotApiKey(\n\toptions: GitHubCopilotOAuthClientOptions = {},\n): Promise<string | undefined> {\n\tconst dedicated = readEnv(GITHUB_COPILOT_API_KEY_ENV);\n\tif (dedicated) return dedicated;\n\n\treturn new GitHubCopilotOAuthClient(options).getApiKey();\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAa,6BAA6B;AAI1C,MAAM,YAAY;AAClB,MAAM,QAAQ;AACd,MAAM,iBAAiB;AACvB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAa,6BAA6B;AAC1C,MAAM,uBAAuB;CAAE,YAAY;CAAG,cAAc;AAAK;AA6DjE,SAAS,UAAU,SAAuD;CACzE,OAAO,QAAQ,SAAS,WAAW;AACpC;AAEA,SAAS,MAAM,IAAY,QAAqC;CAC/D,OAAO,IAAI,SAAS,SAAS,WAAW;EACvC,IAAI,QAAQ,SAAS;GACpB,uBAAO,IAAI,MAAM,gCAAgC,CAAC;GAClD;EACD;EACA,MAAM,UAAU,iBAAiB;GAChC,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,QAAQ;EACT,GAAG,EAAE;EACL,MAAM,gBAAgB;GACrB,aAAa,OAAO;GACpB,uBAAO,IAAI,MAAM,gCAAgC,CAAC;EACnD;EACA,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC1D,CAAC;AACF;AAEA,SAAgB,sBAAsB,OAA+C;CACpF,MAAM,UAAU,OAAO,KAAK;CAC5B,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,IAAI;EAEH,QADY,QAAQ,SAAS,KAAK,IAAI,IAAI,IAAI,OAAO,IAAI,IAAI,IAAI,WAAW,SAAS,EAAA,CAC1E;CACZ,QAAQ;EACP;CACD;AACD;;AAGA,SAAgB,gBAAgB,QAAwB;CACvD,OAAO,WAAW,iBAAiB,mBAAmB,OAAO;AAC9D;;AAGA,SAAgB,qBAAqB,SAG1B;CACV,MAAM,cAAc,SAAS;CAC7B,IAAI,OAAO,gBAAgB,YAAY,aAAa,OAAO,YAAY,QAAQ,QAAQ,EAAE;CACzF,MAAM,SAAS,sBAAsB,SAAS,aAAa;CAC3D,OAAO,SAAS,uBAAuB,WAAW;AACnD;;;;;;AAOA,SAAgB,8BAA8B,MAGvB;CACtB,IAAI,KAAK,iBAAiB,OAAO,OAAO,KAAA;CACxC,IAAI,KAAK,wBACR,OAAO;CAER,OAAO;AACR;AAEA,eAAsB,8BACrB,UAA4G,CAAC,GAC1E;CACnC,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,WAAW,MAAM,UAAU,OAAO,CAAC,CAAC,WAAW,OAAO,qBAAqB;EAChF,QAAQ;EACR,SAAS;GAAE,QAAQ;GAAoB,gBAAgB;EAAmB;EAC1E,MAAM,KAAK,UAAU;GAAE,WAAW;GAAW,OAAO;EAAM,CAAC;EAC3D,QAAQ,QAAQ,UAAU;CAC3B,CAAC;CACD,IAAI,CAAC,SAAS,IACb,MAAM,IAAI,MAAM,sCAAsC,SAAS,OAAO,EAAE;CAGzE,MAAM,OAAgB,MAAM,SAAS,KAAK;CAC1C,IAAI,CAAC,SAAS,IAAI,GAAG,MAAM,IAAI,MAAM,qCAAqC;CAC1E,MAAM,EAAE,aAAa,WAAW,kBAAkB,UAAU,eAAe;CAC3E,IACC,OAAO,gBAAgB,YACvB,OAAO,cAAc,YACrB,OAAO,qBAAqB,YAC3B,aAAa,KAAA,KAAa,OAAO,aAAa,YAC9C,eAAe,KAAA,KAAa,OAAO,eAAe,UAEnD,MAAM,IAAI,MAAM,4CAA4C;CAK7D,IAAI;CACJ,IAAI;EACH,SAAS,IAAI,IAAI,gBAAgB;CAClC,QAAQ;EACP,MAAM,IAAI,MAAM,2DAA2D;CAC5E;CACA,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SACvD,MAAM,IAAI,MAAM,2DAA2D;CAG5E,OAAO;EACN,YAAY;EACZ,UAAU;EACV,iBAAiB,OAAO;EACxB,iBAAiB,OAAO,aAAa,YAAY,WAAW,IAAI,WAAW;EAC3E,GAAI,OAAO,eAAe,YAAY,EAAE,kBAAkB,WAAW;CACtE;AACD;AAEA,eAAsB,6BACrB,MACA,UAA4G,CAAC,GAC3F;CAClB,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,OAAO,UAAU,OAAO;CAE9B,OAAO,eAAuB;EAC7B,iBAAiB,KAAK;EACtB,GAAI,KAAK,qBAAqB,KAAA,KAAa,EAAE,kBAAkB,KAAK,iBAAiB;EACrF,qBAAqB;EACrB,GAAI,QAAQ,WAAW,KAAA,KAAa,EAAE,QAAQ,QAAQ,OAAO;EAC7D,MAAM,YAAY;GACjB,MAAM,WAAW,MAAM,KAAK,WAAW,OAAO,4BAA4B;IACzE,QAAQ;IACR,SAAS;KAAE,QAAQ;KAAoB,gBAAgB;IAAmB;IAC1E,MAAM,KAAK,UAAU;KACpB,WAAW;KACX,aAAa,KAAK;KAClB,YAAY;IACb,CAAC;IACD,QAAQ,QAAQ,UAAU;GAC3B,CAAC;GACD,IAAI,CAAC,SAAS,IACb,OAAO;IAAE,QAAQ;IAAU,SAAS,oCAAoC,SAAS,OAAO;GAAG;GAG5F,MAAM,OAAgB,MAAM,SAAS,KAAK;GAC1C,IAAI,CAAC,SAAS,IAAI,GAAG,OAAO;IAAE,QAAQ;IAAU,SAAS;GAAuC;GAEhG,IAAI,OAAO,KAAK,iBAAiB,YAAY,KAAK,cACjD,OAAO;IAAE,QAAQ;IAAY,OAAO,KAAK;GAAa;GAGvD,MAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;GAC5D,IAAI,UAAU,yBAAyB,OAAO,EAAE,QAAQ,UAAU;GAClE,IAAI,UAAU,aACb,OAAO;IACN,QAAQ;IACR,GAAI,OAAO,KAAK,aAAa,YAAY,KAAK,WAAW,KAAK,EAAE,iBAAiB,KAAK,SAAS;GAChG;GAGD,MAAM,cAAc,OAAO,KAAK,sBAAsB,WAAW,KAAK,KAAK,sBAAsB;GACjG,OAAO;IACN,QAAQ;IACR,SAAS,uCAAuC,SAAS,kBAAkB;GAC5E;EACD;CACD,CAAC;AACF;;;;;;;AAQA,eAAsB,uBACrB,OACA,UAA4G,CAAC,GACpE;CACzC,MAAM,SAAS,QAAQ,UAAU;CACjC,IAAI;EACH,MAAM,WAAW,MAAM,UAAU,OAAO,CAAC,CAAC,WAAW,gBAAgB,MAAM,EAAE,yBAAyB;GACrG,SAAS;IACR,QAAQ;IACR,eAAe,UAAU;IACzB,cAAc,8BAA8B;IAC5C,wBAAwB;GACzB;GACA,QAAQ,QAAQ,UAAU;EAC3B,CAAC;EACD,IAAI,CAAC,SAAS,IAAI,OAAO,KAAA;EACzB,MAAM,OAAgB,MAAM,SAAS,KAAK;EAC1C,IAAI,CAAC,SAAS,IAAI,GAAG,OAAO,KAAA;EAC5B,MAAM,YAAY,SAAS,KAAK,SAAS,IAAI,KAAK,YAAY,KAAA;EAC9D,OAAO;GACN,GAAI,OAAO,KAAK,iBAAiB,aAAa,EAAE,aAAa,KAAK,aAAa;GAC/E,GAAI,OAAO,KAAK,2BAA2B,aAAa,EAAE,qBAAqB,KAAK,uBAAuB;GAC3G,GAAI,OAAO,WAAW,QAAQ,YAAY,UAAU,OAAO,EAAE,aAAa,UAAU,IAAI,QAAQ,QAAQ,EAAE,EAAE;EAC7G;CACD,SAAS,OAAO;EACf,IAAI,QAAQ,QAAQ,SAAS,MAAM;EACnC;CACD;AACD;AAEA,eAAe,wBACd,MACA,KACA,MACA,SACoB;CACpB,MAAM,WAAW,KAAK,IAAI,IAAI,QAAQ;CACtC,KAAK,IAAI,QAAQ,IAAK,SAAS;EAC9B,MAAM,WAAW,MAAM,KAAK,KAAK;GAAE,GAAG;GAAM,QAAQ,QAAQ,UAAU;EAAK,CAAC;EAC5E,IAAI,SAAS,WAAW,OAAO,SAAS,QAAQ,YAAY,OAAO;EAEnE,MAAM,aAAa,SAAS,QAAQ,IAAI,aAAa;EACrD,IAAI,UAAU,MAAM,KAAK;EACzB,IAAI,YAAY;GACf,MAAM,UAAU,OAAO,WAAW,UAAU;GAC5C,UAAU,OAAO,MAAM,OAAO,IAAI,KAAK,MAAM,UAAU,IAAI,KAAK,IAAI,IAAI,UAAU;GAClF,IAAI,CAAC,OAAO,SAAS,OAAO,GAAG,OAAO;EACvC;EACA,UAAU,KAAK,IAAI,GAAG,OAAO;EAC7B,IAAI,WAAW,WAAW,KAAK,IAAI,GAAG,OAAO;EAC7C,MAAM,SAAS,MAAM,OAAO;EAC5B,MAAM,MAAM,SAAS,QAAQ,MAAM;CACpC;AACD;;;;;;AAOA,eAAsB,yBACrB,OACA,UAOI,CAAC,GACiC;CACtC,MAAM,UAAU,QAAQ,WAAW,qBAAqB,EAAE,eAAe,QAAQ,OAAO,CAAC;CACzF,MAAM,WAAW,MAAM,wBACtB,UAAU,OAAO,GACjB,GAAG,QAAQ,UACX,EACC,SAAS;EACR,QAAQ;EACR,eAAe,UAAU;EACzB,GAAG;EACH,wBAAwB;CACzB,EACD,GACA;EACC,GAAI,QAAQ,WAAW,KAAA,KAAa,EAAE,QAAQ,QAAQ,OAAO;EAC7D,YAAY,QAAQ,cAAc,qBAAqB;EACvD,cAAc,QAAQ,gBAAgB,qBAAqB;CAC5D,CACD;CACA,IAAI,CAAC,SAAS,IACb,MAAM,IAAI,MAAM,yCAAyC,SAAS,OAAO,EAAE;CAG5E,MAAM,OAAgB,MAAM,SAAS,KAAK;CAC1C,MAAM,OAAO,SAAS,IAAI,IAAI,KAAK,OAAO,KAAA;CAC1C,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,MAAM,IAAI,MAAM,wCAAwC;CAElF,OAAO,KAAK,SAAS,QAAQ;EAC5B,IAAI,CAAC,SAAS,GAAG,KAAK,OAAO,IAAI,OAAO,UAAU,OAAO,CAAC;EAC1D,MAAM,eAAe,SAAS,IAAI,YAAY,IAAI,IAAI,eAAe,KAAA;EACrE,MAAM,WAAW,SAAS,cAAc,QAAQ,IAAI,aAAa,WAAW,KAAA;EAC5E,MAAM,SAAS,SAAS,IAAI,MAAM,IAAI,IAAI,SAAS,KAAA;EACnD,OAAO,CACN;GACC,IAAI,IAAI;GACR,oBAAoB,MAAM,QAAQ,IAAI,mBAAmB,IACtD,IAAI,oBAAoB,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IACpF,CAAC;GACJ,GAAI,OAAO,QAAQ,UAAU,YAAY,EAAE,aAAa,OAAO,MAAM;GACrE,eAAe,IAAI,yBAAyB;GAC5C,WAAW,UAAU,eAAe;EACrC,CACD;CACD,CAAC;AACF;;;;;;AAOA,eAAsB,0BACrB,OACA,UACA,UAKI,CAAC,GACe;CACpB,MAAM,UAAU,QAAQ,WAAW,qBAAqB,EAAE,eAAe,QAAQ,OAAO,CAAC;CACzF,MAAM,OAAO,UAAU,OAAO;CAC9B,MAAM,UAAoB,CAAC;CAC3B,KAAK,MAAM,WAAW,UACrB,IAAI;EACH,MAAM,WAAW,MAAM,wBACtB,MACA,GAAG,QAAQ,UAAU,QAAQ,UAC7B;GACC,QAAQ;GACR,SAAS;IACR,gBAAgB;IAChB,eAAe,UAAU;IACzB,GAAG;IACH,iBAAiB;IACjB,sBAAsB;GACvB;GACA,MAAM,KAAK,UAAU,EAAE,OAAO,UAAU,CAAC;EAC1C,GACA;GAAE,GAAI,QAAQ,WAAW,KAAA,KAAa,EAAE,QAAQ,QAAQ,OAAO;GAAI,YAAY;GAAG,cAAc;EAAK,CACtG;EACA,IAAI,SAAS,WAAW,KAAK;EAC7B,IAAI,SAAS,IAAI,QAAQ,KAAK,OAAO;CACtC,SAAS,OAAO;EACf,IAAI,QAAQ,QAAQ,SAAS,MAAM;EACnC;CACD;CAED,OAAO;AACR;AAEA,SAAS,2BAA2B,OAAwD;CAC3F,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO;CAC7B,OAAO,OAAO,MAAM,WAAW,YAAY,OAAO,MAAM,YAAY,YAAY,OAAO,MAAM,YAAY;AAC1G;AAEA,IAAa,+BAAb,MAA8E;CAC7E;CAEA,YAAY,UAA+C,CAAC,GAAG;EAC9D,KAAK,UAAU,IAAI,gBAAgB;GAClC,YAAY,QAAQ,cAAc;GAClC,eAAe;GACf,GAAI,QAAQ,SAAS,KAAA,KAAa,EAAE,MAAM,QAAQ,KAAK;EACxD,CAAC;CACF;CAEA,IAAI,OAAe;EAClB,OAAO,KAAK,QAAQ;CACrB;CAEA,IAAI,aAAqB;EACxB,OAAO,KAAK,QAAQ;CACrB;CAEA,MAA0D;EACzD,OAAO,KAAK,QAAQ,IAAI;CACzB;CAEA,IAAI,aAA2D;EAC9D,OAAO,KAAK,QAAQ,IAAI,WAAW;CACpC;CAEA,QAAuB;EACtB,OAAO,KAAK,QAAQ,MAAM;CAC3B;AACD;AAMA,IAAa,2BAAb,MAAsC;CACrC;CAEA,YAAY,UAA2C,CAAC,GAAG;EAC1D,KAAK,UAAU,QAAQ,WAAW,IAAI,6BAA6B;CACpE;CAEA,MAAM,MAAM,SAA4E;EACvF,MAAM,gBAAgB,QAAQ,eAAe,KAAK,KAAK,KAAA;EACvD,MAAM,SAAS,sBAAsB,aAAa,KAAK;EACvD,IAAI,iBAAiB,CAAC,sBAAsB,aAAa,GACxD,MAAM,IAAI,MAAM,sCAAsC;EAGvD,MAAM,OAAO,MAAM,8BAA8B;GAAE;GAAQ,OAAO,QAAQ;GAAO,QAAQ,QAAQ;EAAO,CAAC;EACzG,QAAQ,OAAO;GACd,KAAK,KAAK;GACV,UAAU,KAAK;GACf,cAAc;EACf,CAAC;EAED,MAAM,SAAS,MAAM,6BAA6B,MAAM;GACvD;GACA,OAAO,QAAQ;GACf,GAAI,QAAQ,WAAW,KAAA,KAAa,EAAE,QAAQ,QAAQ,OAAO;EAC9D,CAAC;EAED,MAAM,OAAO,MAAM,uBAAuB,QAAQ;GAAE;GAAQ,OAAO,QAAQ;GAAO,QAAQ,QAAQ;EAAO,CAAC;EAC1G,MAAM,SAAS,OACZ,8BAA8B;GAC9B,cAAc,KAAK;GACnB,wBAAwB,KAAK;EAC9B,CAAC,IACA,KAAA;EACH,IAAI,QAAQ,MAAM,IAAI,MAAM,MAAM;EAElC,MAAM,cACL,MAAM,eAAe,qBAAqB,EAAE,eAAe,WAAW,iBAAiB,KAAA,IAAY,OAAO,CAAC;EAE5G,QAAQ,aAAa,sCAAsC;EAC3D,IAAI;EACJ,IAAI,eAAyB,CAAC;EAC9B,IAAI;GAMH,MAAM,UAAS,MALM,yBAAyB,QAAQ;IACrD,SAAS;IACT,OAAO,QAAQ;IACf,GAAI,QAAQ,WAAW,KAAA,KAAa,EAAE,QAAQ,QAAQ,OAAO;GAC9D,CAAC,EAAA,CACqB,QAAQ,UAAU,MAAM,aAAa,MAAM,gBAAgB,UAAU;GAC3F,MAAM,YAAY,OAAO,QAAQ,UAAU,MAAM,aAAa,CAAC,CAAC,KAAK,UAAU,MAAM,EAAE;GAGvF,MAAM,OAAO,IAAI,IAAI,WAAW,CAAC,CAAC;GAClC,MAAM,iBAAiB,UAAU,WAAW,KAAK,SAAS;GAC1D,oBAAoB,UAAU,SAC3B,YACA,iBACC,OAAO,QAAQ,UAAU,MAAM,gBAAgB,SAAS,CAAC,CAAC,KAAK,UAAU,MAAM,EAAE,IACjF,CAAC;GACL,eAAe,OAAO,QAAQ,UAAU,MAAM,gBAAgB,cAAc,CAAC,CAAC,KAAK,UAAU,MAAM,EAAE;EACtG,SAAS,OAAO;GACf,IAAI,QAAQ,QAAQ,SAAS,MAAM;GACnC,QAAQ,aAAa,mEAAmE;EACzF;EAEA,IAAI,QAAQ,gBAAgB,aAAa,SAAS,GAAG;GACpD,QAAQ,aAAa,4BAA4B;GACjD,MAAM,UAAU,MAAM,0BAA0B,QAAQ,cAAc;IACrE,SAAS;IACT,OAAO,QAAQ;IACf,GAAI,QAAQ,WAAW,KAAA,KAAa,EAAE,QAAQ,QAAQ,OAAO;GAC9D,CAAC;GACD,oBAAoB,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAI,qBAAqB,CAAC,GAAI,GAAG,OAAO,CAAC,CAAC;EAC5E;EAEA,MAAM,cAA6C;GAClD;GACA,SAAS;GACT,SAAS;GACT,GAAI,WAAW,kBAAkB,EAAE,eAAe,OAAO;GACzD;GACA,GAAI,sBAAsB,KAAA,KAAa,kBAAkB,SAAS,KAAK,EAAE,kBAAkB;EAC5F;EACA,MAAM,KAAK,QAAQ,IAAI,WAAW;EAClC,OAAO;CACR;;CAGA,MAAM,iBAAqE;EAC1E,OAAO,KAAK,QAAQ,IAAI;CACzB;CAEA,MAAM,YAAyC;EAC9C,QAAQ,MAAM,KAAK,eAAe,EAAA,EAAI;CACvC;CAEA,MAAM,aAA0D;EAC/D,MAAM,cAAc,MAAM,KAAK,eAAe;EAC9C,OAAO,cAAc,qBAAqB,WAAW,IAAI,KAAA;CAC1D;CAEA,MAAM,SAAwB;EAC7B,MAAM,KAAK,QAAQ,MAAM;CAC1B;AACD;AAEA,SAAgB,qBAAqB,aAAoE;CACxG,OAAO;EACN,eAAe,UAAU,YAAY;EACrC,GAAG;EACH,wBAAwB;CACzB;AACD;;;;;;;;;;;;;;;AAgBA,eAAsB,uBACrB,UAA2C,CAAC,GACd;CAC9B,MAAM,YAAY,QAAQ,0BAA0B;CACpD,IAAI,WAAW,OAAO;CAEtB,OAAO,IAAI,yBAAyB,OAAO,CAAC,CAAC,UAAU;AACxD"}
|