@bastani/pi-ai 0.9.40-alpha.2 → 0.9.41-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +28 -0
  3. package/dist/api/anthropic-messages.d.ts.map +1 -1
  4. package/dist/api/anthropic-messages.js +7 -10
  5. package/dist/api/anthropic-messages.js.map +1 -1
  6. package/dist/api/azure-openai-responses.js +2 -2
  7. package/dist/api/azure-openai-responses.js.map +1 -1
  8. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  9. package/dist/api/bedrock-converse-stream.js +12 -0
  10. package/dist/api/bedrock-converse-stream.js.map +1 -1
  11. package/dist/api/classifier-shared.d.ts.map +1 -1
  12. package/dist/api/classifier-shared.js +4 -3
  13. package/dist/api/classifier-shared.js.map +1 -1
  14. package/dist/api/google-generative-ai.js +4 -4
  15. package/dist/api/google-generative-ai.js.map +1 -1
  16. package/dist/api/google-vertex.d.ts.map +1 -1
  17. package/dist/api/google-vertex.js +9 -9
  18. package/dist/api/google-vertex.js.map +1 -1
  19. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  20. package/dist/api/llama-cpp-classify.js +2 -1
  21. package/dist/api/llama-cpp-classify.js.map +1 -1
  22. package/dist/api/mistral-conversations.js +2 -2
  23. package/dist/api/mistral-conversations.js.map +1 -1
  24. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  25. package/dist/api/openai-codex-responses.js +12 -9
  26. package/dist/api/openai-codex-responses.js.map +1 -1
  27. package/dist/api/openai-completions.d.ts.map +1 -1
  28. package/dist/api/openai-completions.js +4 -4
  29. package/dist/api/openai-completions.js.map +1 -1
  30. package/dist/api/openai-responses.d.ts.map +1 -1
  31. package/dist/api/openai-responses.js +15 -7
  32. package/dist/api/openai-responses.js.map +1 -1
  33. package/dist/api/openrouter-images.d.ts.map +1 -1
  34. package/dist/api/openrouter-images.js +4 -3
  35. package/dist/api/openrouter-images.js.map +1 -1
  36. package/dist/api/pi-messages.d.ts.map +1 -1
  37. package/dist/api/pi-messages.js +3 -3
  38. package/dist/api/pi-messages.js.map +1 -1
  39. package/dist/api/simple-options.d.ts.map +1 -1
  40. package/dist/api/simple-options.js +1 -0
  41. package/dist/api/simple-options.js.map +1 -1
  42. package/dist/auth/oauth/anthropic.d.ts.map +1 -1
  43. package/dist/auth/oauth/anthropic.js +15 -2
  44. package/dist/auth/oauth/anthropic.js.map +1 -1
  45. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  46. package/dist/auth/oauth/github-copilot.js +7 -2
  47. package/dist/auth/oauth/github-copilot.js.map +1 -1
  48. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  49. package/dist/auth/oauth/kimi-coding.js +24 -21
  50. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  51. package/dist/auth/oauth/meta.d.ts.map +1 -1
  52. package/dist/auth/oauth/meta.js +7 -2
  53. package/dist/auth/oauth/meta.js.map +1 -1
  54. package/dist/auth/oauth/openai-chatgpt.d.ts.map +1 -1
  55. package/dist/auth/oauth/openai-chatgpt.js +10 -0
  56. package/dist/auth/oauth/openai-chatgpt.js.map +1 -1
  57. package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  58. package/dist/auth/oauth/openai-codex.js +11 -2
  59. package/dist/auth/oauth/openai-codex.js.map +1 -1
  60. package/dist/auth/oauth/radius.d.ts.map +1 -1
  61. package/dist/auth/oauth/radius.js +24 -8
  62. package/dist/auth/oauth/radius.js.map +1 -1
  63. package/dist/auth/oauth/xai.d.ts.map +1 -1
  64. package/dist/auth/oauth/xai.js +11 -0
  65. package/dist/auth/oauth/xai.js.map +1 -1
  66. package/dist/auth/oauth-refresh-errors.d.ts +46 -0
  67. package/dist/auth/oauth-refresh-errors.d.ts.map +1 -0
  68. package/dist/auth/oauth-refresh-errors.js +150 -0
  69. package/dist/auth/oauth-refresh-errors.js.map +1 -0
  70. package/dist/auth/resolve.d.ts +23 -0
  71. package/dist/auth/resolve.d.ts.map +1 -1
  72. package/dist/auth/resolve.js +194 -5
  73. package/dist/auth/resolve.js.map +1 -1
  74. package/dist/index.d.ts +3 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +3 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/models.d.ts +4 -1
  79. package/dist/models.d.ts.map +1 -1
  80. package/dist/models.js +15 -4
  81. package/dist/models.js.map +1 -1
  82. package/dist/providers/data/.manifest.json +1 -1
  83. package/dist/providers/data/amazon-bedrock.json +1 -1
  84. package/dist/providers/data/baseten.json +1 -1
  85. package/dist/providers/data/cerebras.json +1 -1
  86. package/dist/providers/data/github-copilot.json +1 -1
  87. package/dist/providers/data/groq.json +1 -1
  88. package/dist/providers/data/huggingface.json +1 -1
  89. package/dist/providers/data/nvidia.json +1 -1
  90. package/dist/providers/data/opencode.json +1 -1
  91. package/dist/providers/data/openrouter.json +1 -1
  92. package/dist/providers/data/radius.json +1 -1
  93. package/dist/providers/data/together.json +1 -1
  94. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  95. package/dist/types.d.ts +6 -0
  96. package/dist/types.d.ts.map +1 -1
  97. package/dist/types.js.map +1 -1
  98. package/dist/utils/identity.d.ts +65 -0
  99. package/dist/utils/identity.d.ts.map +1 -0
  100. package/dist/utils/identity.js +142 -0
  101. package/dist/utils/identity.js.map +1 -0
  102. package/dist/utils/retry.d.ts.map +1 -1
  103. package/dist/utils/retry.js +9 -8
  104. package/dist/utils/retry.js.map +1 -1
  105. package/dist/utils/version.d.ts +2 -0
  106. package/dist/utils/version.d.ts.map +1 -0
  107. package/dist/utils/version.js +3 -0
  108. package/dist/utils/version.js.map +1 -0
  109. package/package.json +1 -1
  110. package/dist/utils/pi-user-agent.d.ts +0 -2
  111. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  112. package/dist/utils/pi-user-agent.js +0 -12
  113. package/dist/utils/pi-user-agent.js.map +0 -1
@@ -5,6 +5,7 @@
5
5
  * It is only intended for CLI use, not browser environments.
6
6
  */
7
7
  import { getProviderEnvValue } from "../../utils/provider-env.js";
8
+ import { isPermanentRefreshRejection, OAuthRefreshRejectedError, parseOAuthErrorBody } from "../oauth-refresh-errors.js";
8
9
  import { startOAuthCallbackServer, waitForCallbackOrManualInput } from "./callback-server.js";
9
10
  import { generatePKCE } from "./pkce.js";
10
11
  const decode = (s) => atob(s);
@@ -14,6 +15,8 @@ const TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
14
15
  const CALLBACK_HOST = getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
15
16
  const CALLBACK_PORT = 53692;
16
17
  const CALLBACK_PATH = "/callback";
18
+ /** Login gives up on a stalled authorization-code exchange; a refresh exchange is never cut off (it may have rotated). */
19
+ const AUTHORIZATION_CODE_EXCHANGE_TIMEOUT_MS = 30_000;
17
20
  const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
18
21
  const COPY_CODE_REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
19
22
  const ANTHROPIC_BROWSER_LOGIN_METHOD = "browser";
@@ -72,10 +75,18 @@ async function postJson(url, body, signal) {
72
75
  Accept: "application/json",
73
76
  },
74
77
  body: JSON.stringify(body),
75
- signal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),
78
+ signal,
76
79
  });
77
80
  const responseBody = await response.text();
78
81
  if (!response.ok) {
82
+ if (body.grant_type === "refresh_token" && typeof body.refresh_token === "string") {
83
+ const rejection = { status: response.status, ...parseOAuthErrorBody(responseBody) };
84
+ if (isPermanentRefreshRejection(rejection)) {
85
+ throw new OAuthRefreshRejectedError(`Anthropic token refresh failed (HTTP ${response.status})`, rejection, [
86
+ body.refresh_token,
87
+ ]);
88
+ }
89
+ }
79
90
  throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
80
91
  }
81
92
  return responseBody;
@@ -90,7 +101,7 @@ async function exchangeAuthorizationCode(code, state, verifier, redirectUri, sig
90
101
  state,
91
102
  redirect_uri: redirectUri,
92
103
  code_verifier: verifier,
93
- }, signal);
104
+ }, AbortSignal.any([signal, AbortSignal.timeout(AUTHORIZATION_CODE_EXCHANGE_TIMEOUT_MS)]));
94
105
  }
95
106
  catch (error) {
96
107
  throw new Error(`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`);
@@ -210,6 +221,8 @@ async function refreshAnthropicToken(refreshToken, signal) {
210
221
  }, signal);
211
222
  }
212
223
  catch (error) {
224
+ if (error instanceof OAuthRefreshRejectedError)
225
+ throw error;
213
226
  throw new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`);
214
227
  }
215
228
  let data;
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/auth/oauth/anthropic.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Anthropic OAuth flow (Claude Pro/Max)\n *\n * NOTE: This module uses Node.js http.createServer for the OAuth callback server.\n * It is only intended for CLI use, not browser environments.\n */\n\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { startOAuthCallbackServer, waitForCallbackOrManualInput } from \"./callback-server.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl\");\nconst AUTHORIZE_URL = \"https://claude.ai/oauth/authorize\";\nconst TOKEN_URL = \"https://platform.claude.com/v1/oauth/token\";\nconst CALLBACK_HOST = getProviderEnvValue(\"PI_OAUTH_CALLBACK_HOST\") || \"127.0.0.1\";\nconst CALLBACK_PORT = 53692;\nconst CALLBACK_PATH = \"/callback\";\nconst REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;\nconst COPY_CODE_REDIRECT_URI = \"https://platform.claude.com/oauth/code/callback\";\nconst ANTHROPIC_BROWSER_LOGIN_METHOD = \"browser\";\nconst ANTHROPIC_COPY_CODE_LOGIN_METHOD = \"copy_code\";\nconst SCOPES =\n\t\"org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload\";\n\nfunction parseAuthorizationInput(input: string): { code?: string; state?: string } {\n\tconst value = input.trim();\n\tif (!value) return {};\n\n\ttry {\n\t\tconst url = new URL(value);\n\t\treturn {\n\t\t\tcode: url.searchParams.get(\"code\") ?? undefined,\n\t\t\tstate: url.searchParams.get(\"state\") ?? undefined,\n\t\t};\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 { code, state };\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\tconst params = new URLSearchParams(value);\n\t\treturn {\n\t\t\tcode: params.get(\"code\") ?? undefined,\n\t\t\tstate: params.get(\"state\") ?? undefined,\n\t\t};\n\t}\n\n\treturn { code: value };\n}\n\nfunction formatErrorDetails(error: unknown): string {\n\tif (error instanceof Error) {\n\t\tconst details: string[] = [`${error.name}: ${error.message}`];\n\t\tconst errorWithCode = error as Error & { code?: string; errno?: number | string; cause?: unknown };\n\t\tif (errorWithCode.code) details.push(`code=${errorWithCode.code}`);\n\t\tif (typeof errorWithCode.errno !== \"undefined\") details.push(`errno=${String(errorWithCode.errno)}`);\n\t\tif (typeof error.cause !== \"undefined\") {\n\t\t\tdetails.push(`cause=${formatErrorDetails(error.cause)}`);\n\t\t}\n\t\tif (error.stack) {\n\t\t\tdetails.push(`stack=${error.stack}`);\n\t\t}\n\t\treturn details.join(\"; \");\n\t}\n\treturn String(error);\n}\n\nasync function postJson(url: string, body: Record<string, string | number>, signal: AbortSignal): Promise<string> {\n\tconst response = await fetch(url, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t\tsignal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),\n\t});\n\n\tconst responseBody = await response.text();\n\n\tif (!response.ok) {\n\t\tthrow new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);\n\t}\n\n\treturn responseBody;\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tstate: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tlet responseBody: string;\n\ttry {\n\t\tresponseBody = await postJson(\n\t\t\tTOKEN_URL,\n\t\t\t{\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\tstate,\n\t\t\t\tredirect_uri: redirectUri,\n\t\t\t\tcode_verifier: verifier,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\tlet tokenData: { access_token: string; refresh_token: string; expires_in: number };\n\ttry {\n\t\ttokenData = JSON.parse(responseBody) as { access_token: string; refresh_token: string; expires_in: number };\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\trefresh: tokenData.refresh_token,\n\t\taccess: tokenData.access_token,\n\t\texpires: Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nasync function loginAnthropic(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst startCallbackServer = (port: number) =>\n\t\tstartOAuthCallbackServer({\n\t\t\tproviderName: \"Anthropic\",\n\t\t\thost: CALLBACK_HOST,\n\t\t\tport,\n\t\t\tpath: CALLBACK_PATH,\n\t\t\tredirectHost: \"localhost\",\n\t\t\tstate: verifier,\n\t\t\tcomplete: async (code) => code,\n\t\t\tsignal: interaction.signal,\n\t\t});\n\tconst callback = await startCallbackServer(CALLBACK_PORT)\n\t\t.catch(() => startCallbackServer(0))\n\t\t.catch(() => undefined);\n\tconst redirectUri = callback?.redirectUri ?? REDIRECT_URI;\n\n\ttry {\n\t\tconst authParams = new URLSearchParams({\n\t\t\tcode: \"true\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tresponse_type: \"code\",\n\t\t\tredirect_uri: redirectUri,\n\t\t\tscope: SCOPES,\n\t\t\tcode_challenge: challenge,\n\t\t\tcode_challenge_method: \"S256\",\n\t\t\tstate: verifier,\n\t\t});\n\t\tinteraction.notify({\n\t\t\ttype: \"auth_url\",\n\t\t\turl: `${AUTHORIZE_URL}?${authParams.toString()}`,\n\t\t\tinstructions:\n\t\t\t\t\"Complete login in your browser. If the browser is on another machine, paste the final redirect URL here.\",\n\t\t});\n\n\t\tconst result = await waitForCallbackOrManualInput(interaction, callback, {\n\t\t\tmessage: \"Complete login in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\tplaceholder: redirectUri,\n\t\t});\n\t\tlet code: string | undefined;\n\t\tlet state = verifier;\n\t\tif (result.type === \"callback\") {\n\t\t\tcode = result.value;\n\t\t} else {\n\t\t\tconst parsed = parseAuthorizationInput(result.input);\n\t\t\tif (parsed.state && parsed.state !== verifier) throw new Error(\"OAuth state mismatch\");\n\t\t\tcode = parsed.code;\n\t\t\tstate = parsed.state ?? verifier;\n\t\t}\n\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\tinteraction.notify({ type: \"progress\", message: \"Exchanging authorization code for tokens...\" });\n\t\treturn await exchangeAuthorizationCode(code, state, verifier, redirectUri, interaction.signal);\n\t} finally {\n\t\tcallback?.close();\n\t}\n}\n\nasync function loginAnthropicCopyCode(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst authParams = new URLSearchParams({\n\t\tcode: \"true\",\n\t\tclient_id: CLIENT_ID,\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: COPY_CODE_REDIRECT_URI,\n\t\tscope: SCOPES,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\tstate: verifier,\n\t});\n\tinteraction.notify({\n\t\ttype: \"auth_url\",\n\t\turl: `${AUTHORIZE_URL}?${authParams.toString()}`,\n\t\tinstructions: \"Complete login in your browser, then copy the code Anthropic shows and paste it here.\",\n\t});\n\n\tconst input = await interaction.prompt({\n\t\ttype: \"manual_code\",\n\t\tmessage: \"Paste the code Anthropic shows after you sign in:\",\n\t\tplaceholder: \"code#state\",\n\t\tsignal: interaction.signal,\n\t});\n\tconst parsed = parseAuthorizationInput(input);\n\tif (parsed.state && parsed.state !== verifier) throw new Error(\"OAuth state mismatch\");\n\tif (!parsed.code) throw new Error(\"Missing authorization code\");\n\tinteraction.notify({ type: \"progress\", message: \"Exchanging authorization code for tokens...\" });\n\treturn await exchangeAuthorizationCode(\n\t\tparsed.code,\n\t\tparsed.state ?? verifier,\n\t\tverifier,\n\t\tCOPY_CODE_REDIRECT_URI,\n\t\tinteraction.signal,\n\t);\n}\n\n/**\n * Refresh Anthropic OAuth token\n */\nasync function refreshAnthropicToken(refreshToken: string, signal: AbortSignal): Promise<OAuthCredential> {\n\tlet responseBody: string;\n\ttry {\n\t\tresponseBody = await postJson(\n\t\t\tTOKEN_URL,\n\t\t\t{\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t} catch (error) {\n\t\tthrow new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`);\n\t}\n\n\tlet data: { access_token: string; refresh_token: string; expires_in: number; scope?: string };\n\ttry {\n\t\tdata = JSON.parse(responseBody) as {\n\t\t\taccess_token: string;\n\t\t\trefresh_token: string;\n\t\t\texpires_in: number;\n\t\t\tscope?: string;\n\t\t};\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Anthropic token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\trefresh: data.refresh_token,\n\t\taccess: data.access_token,\n\t\texpires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nexport const anthropicOAuth: OAuthAuth = {\n\tname: \"Anthropic (Claude Pro/Max)\",\n\tusesCallbackServer: true,\n\tisSubscription: true,\n\n\tasync login(interaction) {\n\t\tconst method = await interaction.prompt({\n\t\t\ttype: \"select\",\n\t\t\tmessage: \"Select Anthropic login method:\",\n\t\t\toptions: [\n\t\t\t\t{ id: ANTHROPIC_BROWSER_LOGIN_METHOD, label: \"Browser login (default)\" },\n\t\t\t\t{ id: ANTHROPIC_COPY_CODE_LOGIN_METHOD, label: \"Copy code login (headless)\" },\n\t\t\t],\n\t\t});\n\n\t\tif (method === ANTHROPIC_COPY_CODE_LOGIN_METHOD) {\n\t\t\treturn loginAnthropicCopyCode(interaction);\n\t\t}\n\t\tif (method !== ANTHROPIC_BROWSER_LOGIN_METHOD) {\n\t\t\tthrow new Error(`Unknown Anthropic login method: ${method}`);\n\t\t}\n\n\t\treturn loginAnthropic(interaction);\n\t},\n\n\trefresh: (credential, signal) => refreshAnthropicToken(credential.refresh, signal),\n\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/auth/oauth/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEzH,OAAO,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACtC,MAAM,SAAS,GAAG,MAAM,CAAC,kDAAkD,CAAC,CAAC;AAC7E,MAAM,aAAa,GAAG,mCAAmC,CAAC;AAC1D,MAAM,SAAS,GAAG,4CAA4C,CAAC;AAC/D,MAAM,aAAa,GAAG,mBAAmB,CAAC,wBAAwB,CAAC,IAAI,WAAW,CAAC;AACnF,MAAM,aAAa,GAAG,KAAK,CAAC;AAC5B,MAAM,aAAa,GAAG,WAAW,CAAC;AAClC,0HAA0H;AAC1H,MAAM,sCAAsC,GAAG,MAAM,CAAC;AACtD,MAAM,YAAY,GAAG,oBAAoB,aAAa,GAAG,aAAa,EAAE,CAAC;AACzE,MAAM,sBAAsB,GAAG,iDAAiD,CAAC;AACjF,MAAM,8BAA8B,GAAG,SAAS,CAAC;AACjD,MAAM,gCAAgC,GAAG,WAAW,CAAC;AACrD,MAAM,MAAM,GACX,4GAA4G,CAAC;AAE9G,SAAS,uBAAuB,CAAC,KAAa;IAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO;YACN,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS;YAC/C,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,SAAS;SACjD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,YAAY;IACb,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC;QAC1C,OAAO;YACN,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS;YACrC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,SAAS;SACvC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACzC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAa,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9D,MAAM,aAAa,GAAG,KAA4E,CAAC;QACnG,IAAI,aAAa,CAAC,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,IAAI,OAAO,aAAa,CAAC,KAAK,KAAK,WAAW;YAAE,OAAO,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrG,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC,SAAS,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAqC,EAAE,MAAmB;IAC9F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QACjC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACR,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,kBAAkB;SAC1B;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;QAC1B,MAAM;KACN,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAE3C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,IAAI,IAAI,CAAC,UAAU,KAAK,eAAe,IAAI,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ,EAAE,CAAC;YACnF,MAAM,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,GAAG,mBAAmB,CAAC,YAAY,CAAC,EAAE,CAAC;YACpF,IAAI,2BAA2B,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC5C,MAAM,IAAI,yBAAyB,CAAC,wCAAwC,QAAQ,CAAC,MAAM,GAAG,EAAE,SAAS,EAAE;oBAC1G,IAAI,CAAC,aAAa;iBAClB,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,MAAM,SAAS,GAAG,UAAU,YAAY,EAAE,CAAC,CAAC;IACrG,CAAC;IAED,OAAO,YAAY,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,yBAAyB,CACvC,IAAY,EACZ,KAAa,EACb,QAAgB,EAChB,WAAmB,EACnB,MAAmB;IAEnB,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,YAAY,GAAG,MAAM,QAAQ,CAC5B,SAAS,EACT;YACC,UAAU,EAAE,oBAAoB;YAChC,SAAS,EAAE,SAAS;YACpB,IAAI;YACJ,KAAK;YACL,YAAY,EAAE,WAAW;YACzB,aAAa,EAAE,QAAQ;SACvB,EACD,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,CAAC,CACtF,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACd,sCAAsC,SAAS,kBAAkB,WAAW,+CAA+C,kBAAkB,CAAC,KAAK,CAAC,EAAE,CACtJ,CAAC;IACH,CAAC;IAED,IAAI,SAA8E,CAAC;IACnF,IAAI,CAAC;QACJ,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAwE,CAAC;IAC7G,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACd,6CAA6C,SAAS,UAAU,YAAY,aAAa,kBAAkB,CAAC,KAAK,CAAC,EAAE,CACpH,CAAC;IACH,CAAC;IAED,OAAO;QACN,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,SAAS,CAAC,aAAa;QAChC,MAAM,EAAE,SAAS,CAAC,YAAY;QAC9B,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI;KACjE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,WAAoC;IACjE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;IACrD,MAAM,mBAAmB,GAAG,CAAC,IAAY,EAAE,EAAE,CAC5C,wBAAwB,CAAC;QACxB,YAAY,EAAE,WAAW;QACzB,IAAI,EAAE,aAAa;QACnB,IAAI;QACJ,IAAI,EAAE,aAAa;QACnB,YAAY,EAAE,WAAW;QACzB,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI;QAC9B,MAAM,EAAE,WAAW,CAAC,MAAM;KAC1B,CAAC,CAAC;IACJ,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,aAAa,CAAC;SACvD,KAAK,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;SACnC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACzB,MAAM,WAAW,GAAG,QAAQ,EAAE,WAAW,IAAI,YAAY,CAAC;IAE1D,IAAI,CAAC;QACJ,MAAM,UAAU,GAAG,IAAI,eAAe,CAAC;YACtC,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE,SAAS;YACpB,aAAa,EAAE,MAAM;YACrB,YAAY,EAAE,WAAW;YACzB,KAAK,EAAE,MAAM;YACb,cAAc,EAAE,SAAS;YACzB,qBAAqB,EAAE,MAAM;YAC7B,KAAK,EAAE,QAAQ;SACf,CAAC,CAAC;QACH,WAAW,CAAC,MAAM,CAAC;YAClB,IAAI,EAAE,UAAU;YAChB,GAAG,EAAE,GAAG,aAAa,IAAI,UAAU,CAAC,QAAQ,EAAE,EAAE;YAChD,YAAY,EACX,0GAA0G;SAC3G,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,WAAW,EAAE,QAAQ,EAAE;YACxE,OAAO,EAAE,sFAAsF;YAC/F,WAAW,EAAE,WAAW;SACxB,CAAC,CAAC;QACH,IAAI,IAAwB,CAAC;QAC7B,IAAI,KAAK,GAAG,QAAQ,CAAC;QACrB,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAChC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC;QACrB,CAAC;aAAM,CAAC;YACP,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrD,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACvF,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YACnB,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC;QAClC,CAAC;QAED,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACzD,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,6CAA6C,EAAE,CAAC,CAAC;QACjG,OAAO,MAAM,yBAAyB,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChG,CAAC;YAAS,CAAC;QACV,QAAQ,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;AACF,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,WAAoC;IACzE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;IACrD,MAAM,UAAU,GAAG,IAAI,eAAe,CAAC;QACtC,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,SAAS;QACpB,aAAa,EAAE,MAAM;QACrB,YAAY,EAAE,sBAAsB;QACpC,KAAK,EAAE,MAAM;QACb,cAAc,EAAE,SAAS;QACzB,qBAAqB,EAAE,MAAM;QAC7B,KAAK,EAAE,QAAQ;KACf,CAAC,CAAC;IACH,WAAW,CAAC,MAAM,CAAC;QAClB,IAAI,EAAE,UAAU;QAChB,GAAG,EAAE,GAAG,aAAa,IAAI,UAAU,CAAC,QAAQ,EAAE,EAAE;QAChD,YAAY,EAAE,uFAAuF;KACrG,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC;QACtC,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,mDAAmD;QAC5D,WAAW,EAAE,YAAY;QACzB,MAAM,EAAE,WAAW,CAAC,MAAM;KAC1B,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACvF,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChE,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,6CAA6C,EAAE,CAAC,CAAC;IACjG,OAAO,MAAM,yBAAyB,CACrC,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,KAAK,IAAI,QAAQ,EACxB,QAAQ,EACR,sBAAsB,EACtB,WAAW,CAAC,MAAM,CAClB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,qBAAqB,CAAC,YAAoB,EAAE,MAAmB;IAC7E,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,YAAY,GAAG,MAAM,QAAQ,CAC5B,SAAS,EACT;YACC,UAAU,EAAE,eAAe;YAC3B,SAAS,EAAE,SAAS;YACpB,aAAa,EAAE,YAAY;SAC3B,EACD,MAAM,CACN,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,yBAAyB;YAAE,MAAM,KAAK,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,+CAA+C,SAAS,aAAa,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACnH,CAAC;IAED,IAAI,IAAyF,CAAC;IAC9F,IAAI,CAAC;QACJ,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAK7B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACd,sDAAsD,SAAS,UAAU,YAAY,aAAa,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAC7H,CAAC;IACH,CAAC;IAED,OAAO;QACN,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,IAAI,CAAC,aAAa;QAC3B,MAAM,EAAE,IAAI,CAAC,YAAY;QACzB,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI;KAC5D,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAc;IACxC,IAAI,EAAE,4BAA4B;IAClC,kBAAkB,EAAE,IAAI;IACxB,cAAc,EAAE,IAAI;IAEpB,KAAK,CAAC,KAAK,CAAC,WAAW;QACtB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC;YACvC,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,gCAAgC;YACzC,OAAO,EAAE;gBACR,EAAE,EAAE,EAAE,8BAA8B,EAAE,KAAK,EAAE,yBAAyB,EAAE;gBACxE,EAAE,EAAE,EAAE,gCAAgC,EAAE,KAAK,EAAE,4BAA4B,EAAE;aAC7E;SACD,CAAC,CAAC;QAEH,IAAI,MAAM,KAAK,gCAAgC,EAAE,CAAC;YACjD,OAAO,sBAAsB,CAAC,WAAW,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,MAAM,KAAK,8BAA8B,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,mCAAmC,MAAM,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,cAAc,CAAC,WAAW,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,qBAAqB,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC;IAElF,KAAK,CAAC,MAAM,CAAC,UAAU;QACtB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;IACtC,CAAC;CACD,CAAC","sourcesContent":["/**\n * Anthropic OAuth flow (Claude Pro/Max)\n *\n * NOTE: This module uses Node.js http.createServer for the OAuth callback server.\n * It is only intended for CLI use, not browser environments.\n */\n\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport { isPermanentRefreshRejection, OAuthRefreshRejectedError, parseOAuthErrorBody } from \"../oauth-refresh-errors.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { startOAuthCallbackServer, waitForCallbackOrManualInput } from \"./callback-server.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl\");\nconst AUTHORIZE_URL = \"https://claude.ai/oauth/authorize\";\nconst TOKEN_URL = \"https://platform.claude.com/v1/oauth/token\";\nconst CALLBACK_HOST = getProviderEnvValue(\"PI_OAUTH_CALLBACK_HOST\") || \"127.0.0.1\";\nconst CALLBACK_PORT = 53692;\nconst CALLBACK_PATH = \"/callback\";\n/** Login gives up on a stalled authorization-code exchange; a refresh exchange is never cut off (it may have rotated). */\nconst AUTHORIZATION_CODE_EXCHANGE_TIMEOUT_MS = 30_000;\nconst REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;\nconst COPY_CODE_REDIRECT_URI = \"https://platform.claude.com/oauth/code/callback\";\nconst ANTHROPIC_BROWSER_LOGIN_METHOD = \"browser\";\nconst ANTHROPIC_COPY_CODE_LOGIN_METHOD = \"copy_code\";\nconst SCOPES =\n\t\"org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload\";\n\nfunction parseAuthorizationInput(input: string): { code?: string; state?: string } {\n\tconst value = input.trim();\n\tif (!value) return {};\n\n\ttry {\n\t\tconst url = new URL(value);\n\t\treturn {\n\t\t\tcode: url.searchParams.get(\"code\") ?? undefined,\n\t\t\tstate: url.searchParams.get(\"state\") ?? undefined,\n\t\t};\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 { code, state };\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\tconst params = new URLSearchParams(value);\n\t\treturn {\n\t\t\tcode: params.get(\"code\") ?? undefined,\n\t\t\tstate: params.get(\"state\") ?? undefined,\n\t\t};\n\t}\n\n\treturn { code: value };\n}\n\nfunction formatErrorDetails(error: unknown): string {\n\tif (error instanceof Error) {\n\t\tconst details: string[] = [`${error.name}: ${error.message}`];\n\t\tconst errorWithCode = error as Error & { code?: string; errno?: number | string; cause?: unknown };\n\t\tif (errorWithCode.code) details.push(`code=${errorWithCode.code}`);\n\t\tif (typeof errorWithCode.errno !== \"undefined\") details.push(`errno=${String(errorWithCode.errno)}`);\n\t\tif (typeof error.cause !== \"undefined\") {\n\t\t\tdetails.push(`cause=${formatErrorDetails(error.cause)}`);\n\t\t}\n\t\tif (error.stack) {\n\t\t\tdetails.push(`stack=${error.stack}`);\n\t\t}\n\t\treturn details.join(\"; \");\n\t}\n\treturn String(error);\n}\n\nasync function postJson(url: string, body: Record<string, string | number>, signal: AbortSignal): Promise<string> {\n\tconst response = await fetch(url, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t\tsignal,\n\t});\n\n\tconst responseBody = await response.text();\n\n\tif (!response.ok) {\n\t\tif (body.grant_type === \"refresh_token\" && typeof body.refresh_token === \"string\") {\n\t\t\tconst rejection = { status: response.status, ...parseOAuthErrorBody(responseBody) };\n\t\t\tif (isPermanentRefreshRejection(rejection)) {\n\t\t\t\tthrow new OAuthRefreshRejectedError(`Anthropic token refresh failed (HTTP ${response.status})`, rejection, [\n\t\t\t\t\tbody.refresh_token,\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t\tthrow new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);\n\t}\n\n\treturn responseBody;\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tstate: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tlet responseBody: string;\n\ttry {\n\t\tresponseBody = await postJson(\n\t\t\tTOKEN_URL,\n\t\t\t{\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\tstate,\n\t\t\t\tredirect_uri: redirectUri,\n\t\t\t\tcode_verifier: verifier,\n\t\t\t},\n\t\t\tAbortSignal.any([signal, AbortSignal.timeout(AUTHORIZATION_CODE_EXCHANGE_TIMEOUT_MS)]),\n\t\t);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\tlet tokenData: { access_token: string; refresh_token: string; expires_in: number };\n\ttry {\n\t\ttokenData = JSON.parse(responseBody) as { access_token: string; refresh_token: string; expires_in: number };\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\trefresh: tokenData.refresh_token,\n\t\taccess: tokenData.access_token,\n\t\texpires: Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nasync function loginAnthropic(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst startCallbackServer = (port: number) =>\n\t\tstartOAuthCallbackServer({\n\t\t\tproviderName: \"Anthropic\",\n\t\t\thost: CALLBACK_HOST,\n\t\t\tport,\n\t\t\tpath: CALLBACK_PATH,\n\t\t\tredirectHost: \"localhost\",\n\t\t\tstate: verifier,\n\t\t\tcomplete: async (code) => code,\n\t\t\tsignal: interaction.signal,\n\t\t});\n\tconst callback = await startCallbackServer(CALLBACK_PORT)\n\t\t.catch(() => startCallbackServer(0))\n\t\t.catch(() => undefined);\n\tconst redirectUri = callback?.redirectUri ?? REDIRECT_URI;\n\n\ttry {\n\t\tconst authParams = new URLSearchParams({\n\t\t\tcode: \"true\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tresponse_type: \"code\",\n\t\t\tredirect_uri: redirectUri,\n\t\t\tscope: SCOPES,\n\t\t\tcode_challenge: challenge,\n\t\t\tcode_challenge_method: \"S256\",\n\t\t\tstate: verifier,\n\t\t});\n\t\tinteraction.notify({\n\t\t\ttype: \"auth_url\",\n\t\t\turl: `${AUTHORIZE_URL}?${authParams.toString()}`,\n\t\t\tinstructions:\n\t\t\t\t\"Complete login in your browser. If the browser is on another machine, paste the final redirect URL here.\",\n\t\t});\n\n\t\tconst result = await waitForCallbackOrManualInput(interaction, callback, {\n\t\t\tmessage: \"Complete login in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\tplaceholder: redirectUri,\n\t\t});\n\t\tlet code: string | undefined;\n\t\tlet state = verifier;\n\t\tif (result.type === \"callback\") {\n\t\t\tcode = result.value;\n\t\t} else {\n\t\t\tconst parsed = parseAuthorizationInput(result.input);\n\t\t\tif (parsed.state && parsed.state !== verifier) throw new Error(\"OAuth state mismatch\");\n\t\t\tcode = parsed.code;\n\t\t\tstate = parsed.state ?? verifier;\n\t\t}\n\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\tinteraction.notify({ type: \"progress\", message: \"Exchanging authorization code for tokens...\" });\n\t\treturn await exchangeAuthorizationCode(code, state, verifier, redirectUri, interaction.signal);\n\t} finally {\n\t\tcallback?.close();\n\t}\n}\n\nasync function loginAnthropicCopyCode(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst authParams = new URLSearchParams({\n\t\tcode: \"true\",\n\t\tclient_id: CLIENT_ID,\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: COPY_CODE_REDIRECT_URI,\n\t\tscope: SCOPES,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\tstate: verifier,\n\t});\n\tinteraction.notify({\n\t\ttype: \"auth_url\",\n\t\turl: `${AUTHORIZE_URL}?${authParams.toString()}`,\n\t\tinstructions: \"Complete login in your browser, then copy the code Anthropic shows and paste it here.\",\n\t});\n\n\tconst input = await interaction.prompt({\n\t\ttype: \"manual_code\",\n\t\tmessage: \"Paste the code Anthropic shows after you sign in:\",\n\t\tplaceholder: \"code#state\",\n\t\tsignal: interaction.signal,\n\t});\n\tconst parsed = parseAuthorizationInput(input);\n\tif (parsed.state && parsed.state !== verifier) throw new Error(\"OAuth state mismatch\");\n\tif (!parsed.code) throw new Error(\"Missing authorization code\");\n\tinteraction.notify({ type: \"progress\", message: \"Exchanging authorization code for tokens...\" });\n\treturn await exchangeAuthorizationCode(\n\t\tparsed.code,\n\t\tparsed.state ?? verifier,\n\t\tverifier,\n\t\tCOPY_CODE_REDIRECT_URI,\n\t\tinteraction.signal,\n\t);\n}\n\n/**\n * Refresh Anthropic OAuth token\n */\nasync function refreshAnthropicToken(refreshToken: string, signal: AbortSignal): Promise<OAuthCredential> {\n\tlet responseBody: string;\n\ttry {\n\t\tresponseBody = await postJson(\n\t\t\tTOKEN_URL,\n\t\t\t{\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t} catch (error) {\n\t\tif (error instanceof OAuthRefreshRejectedError) throw error;\n\t\tthrow new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`);\n\t}\n\n\tlet data: { access_token: string; refresh_token: string; expires_in: number; scope?: string };\n\ttry {\n\t\tdata = JSON.parse(responseBody) as {\n\t\t\taccess_token: string;\n\t\t\trefresh_token: string;\n\t\t\texpires_in: number;\n\t\t\tscope?: string;\n\t\t};\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Anthropic token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`,\n\t\t);\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\trefresh: data.refresh_token,\n\t\taccess: data.access_token,\n\t\texpires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nexport const anthropicOAuth: OAuthAuth = {\n\tname: \"Anthropic (Claude Pro/Max)\",\n\tusesCallbackServer: true,\n\tisSubscription: true,\n\n\tasync login(interaction) {\n\t\tconst method = await interaction.prompt({\n\t\t\ttype: \"select\",\n\t\t\tmessage: \"Select Anthropic login method:\",\n\t\t\toptions: [\n\t\t\t\t{ id: ANTHROPIC_BROWSER_LOGIN_METHOD, label: \"Browser login (default)\" },\n\t\t\t\t{ id: ANTHROPIC_COPY_CODE_LOGIN_METHOD, label: \"Copy code login (headless)\" },\n\t\t\t],\n\t\t});\n\n\t\tif (method === ANTHROPIC_COPY_CODE_LOGIN_METHOD) {\n\t\t\treturn loginAnthropicCopyCode(interaction);\n\t\t}\n\t\tif (method !== ANTHROPIC_BROWSER_LOGIN_METHOD) {\n\t\t\tthrow new Error(`Unknown Anthropic login method: ${method}`);\n\t\t}\n\n\t\treturn loginAnthropic(interaction);\n\t},\n\n\trefresh: (credential, signal) => refreshAnthropicToken(credential.refresh, signal),\n\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/github-copilot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AAkfvF,eAAO,MAAM,kBAAkB,EAAE,SAchC,CAAC"}
1
+ {"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/github-copilot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA8fvF,eAAO,MAAM,kBAAkB,EAAE,SAchC,CAAC"}
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { GITHUB_COPILOT_MODELS } from "../../providers/github-copilot.models.js";
5
5
  import { sleep } from "../../utils/sleep.js";
6
+ import { OAuthRefreshRejectedError, parseOAuthErrorBody } from "../oauth-refresh-errors.js";
6
7
  import { pollOAuthDeviceCodeFlow } from "./device-code.js";
7
8
  const decode = (s) => atob(s);
8
9
  const CLIENT_ID = decode("SXYxLmI1MDdhMDhjODdlY2ZlOTg=");
@@ -148,10 +149,14 @@ async function fetchGitHubCopilotModels(copilotToken, enterpriseDomain, signal,
148
149
  }
149
150
  return parseGitHubCopilotModelCatalog(await response.json(), allowPolicyFallback);
150
151
  }
151
- async function fetchJson(url, init) {
152
+ /** `presentedToken` marks a request authenticated by the stored login: a 401 then means that login was revoked. */
153
+ async function fetchJson(url, init, presentedToken) {
152
154
  const response = await fetch(url, init);
153
155
  if (!response.ok) {
154
156
  const text = await response.text();
157
+ if (presentedToken !== undefined && response.status === 401) {
158
+ throw new OAuthRefreshRejectedError(`GitHub Copilot token request failed (${response.status})`, { status: response.status, ...parseOAuthErrorBody(text) }, [presentedToken]);
159
+ }
155
160
  throw new Error(`${response.status} ${response.statusText}: ${text}`);
156
161
  }
157
162
  return response.json();
@@ -256,7 +261,7 @@ async function refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, s
256
261
  ...COPILOT_HEADERS,
257
262
  },
258
263
  signal,
259
- });
264
+ }, refreshToken);
260
265
  if (!raw || typeof raw !== "object") {
261
266
  throw new Error("Invalid Copilot token response");
262
267
  }
@@ -1 +1 @@
1
- {"version":3,"file":"github-copilot.js","sourceRoot":"","sources":["../../../src/auth/oauth/github-copilot.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * GitHub Copilot OAuth flow\n */\n\nimport { GITHUB_COPILOT_MODELS } from \"../../providers/github-copilot.models.ts\";\nimport { sleep } from \"../../utils/sleep.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"SXYxLmI1MDdhMDhjODdlY2ZlOTg=\");\n\nconst COPILOT_HEADERS = {\n\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\"Editor-Version\": \"vscode/1.107.0\",\n\t\"Editor-Plugin-Version\": \"copilot-chat/0.35.0\",\n\t\"Copilot-Integration-Id\": \"vscode-chat\",\n} as const;\nconst COPILOT_API_VERSION = \"2026-06-01\";\n\ntype DeviceCodeResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\tinterval?: number;\n\texpires_in: number;\n};\n\ntype DeviceTokenSuccessResponse = {\n\taccess_token: string;\n\ttoken_type?: string;\n\tscope?: string;\n};\n\ntype DeviceTokenErrorResponse = {\n\terror: string;\n\terror_description?: string;\n\tinterval?: number;\n};\n\nfunction normalizeDomain(input: string): string | null {\n\tconst trimmed = input.trim();\n\tif (!trimmed) return null;\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 null;\n\t}\n}\n\nfunction getUrls(domain: string): {\n\tdeviceCodeUrl: string;\n\taccessTokenUrl: string;\n\tcopilotTokenUrl: string;\n} {\n\treturn {\n\t\tdeviceCodeUrl: `https://${domain}/login/device/code`,\n\t\taccessTokenUrl: `https://${domain}/login/oauth/access_token`,\n\t\tcopilotTokenUrl: `https://api.${domain}/copilot_internal/v2/token`,\n\t};\n}\n\n/**\n * Parse the proxy-ep from a Copilot token and convert to API base URL.\n * Token format: tid=...;exp=...;proxy-ep=proxy.individual.githubcopilot.com;...\n * Returns API URL like https://api.individual.githubcopilot.com\n */\nfunction getBaseUrlFromToken(token: string): string | null {\n\tconst match = token.match(/proxy-ep=([^;]+)/);\n\tif (!match) return null;\n\tconst proxyHost = match[1];\n\t// Convert proxy.xxx to api.xxx\n\tconst apiHost = proxyHost.replace(/^proxy\\./, \"api.\");\n\treturn `https://${apiHost}`;\n}\n\nfunction getGitHubCopilotBaseUrl(token?: string, enterpriseDomain?: string): string {\n\t// If we have a token, extract the base URL from proxy-ep\n\tif (token) {\n\t\tconst urlFromToken = getBaseUrlFromToken(token);\n\t\tif (urlFromToken) return urlFromToken;\n\t}\n\t// Fallback for enterprise or if token parsing fails\n\tif (enterpriseDomain) return `https://copilot-api.${enterpriseDomain}`;\n\treturn \"https://api.individual.githubcopilot.com\";\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn value && typeof value === \"object\" ? (value as Record<string, unknown>) : undefined;\n}\n\nfunction parseGitHubCopilotModelCatalog(raw: unknown, allowPolicyFallback: boolean) {\n\tconst data = asRecord(raw)?.data;\n\tif (!Array.isArray(data)) {\n\t\tthrow new Error(\"Invalid Copilot models response\");\n\t}\n\n\tconst accountModels = data.flatMap((rawItem) => {\n\t\tconst item = asRecord(rawItem);\n\t\tconst id = item?.id;\n\t\tif (!item || typeof id !== \"string\") return [];\n\n\t\tconst capabilities = asRecord(item.capabilities);\n\t\tconst supports = asRecord(capabilities?.supports);\n\t\tif (supports?.tool_calls === false) return [];\n\n\t\treturn [\n\t\t\t{\n\t\t\t\tid,\n\t\t\t\tpickerEnabled: item.model_picker_enabled === true,\n\t\t\t\tpolicyState: asRecord(item.policy)?.state,\n\t\t\t},\n\t\t];\n\t});\n\tconst pickerModelIds = accountModels\n\t\t.filter((model) => model.pickerEnabled && model.policyState !== \"disabled\")\n\t\t.map((model) => model.id);\n\tconst usePolicyFallback = allowPolicyFallback && pickerModelIds.length === 0;\n\tconst availableModelIds =\n\t\tpickerModelIds.length > 0 || !allowPolicyFallback\n\t\t\t? pickerModelIds\n\t\t\t: accountModels\n\t\t\t\t\t.filter((model) => !model.id.endsWith(\"-fast\") && model.policyState === \"enabled\")\n\t\t\t\t\t.map((model) => model.id);\n\tconst fastModelIds = accountModels\n\t\t.filter((model) => model.id.endsWith(\"-fast\") && model.policyState !== \"disabled\")\n\t\t.map((model) => model.id);\n\tconst policyModelIds = accountModels\n\t\t.filter(\n\t\t\t(model) =>\n\t\t\t\tmodel.policyState === \"unconfigured\" &&\n\t\t\t\tObject.hasOwn(GITHUB_COPILOT_MODELS, model.id) &&\n\t\t\t\t(model.pickerEnabled || usePolicyFallback),\n\t\t)\n\t\t.map((model) => model.id);\n\treturn { availableModelIds, fastModelIds, policyModelIds };\n}\n\nasync function fetchWithRateLimitRetry(\n\turl: string,\n\tinit: RequestInit,\n\tsignal: AbortSignal,\n\tretryPolicy: { maxRetries: number; maxElapsedMs: number },\n): Promise<Response> {\n\tconst retryBudgetSignal =\n\t\tretryPolicy.maxRetries > 0 && retryPolicy.maxElapsedMs > 0\n\t\t\t? AbortSignal.timeout(retryPolicy.maxElapsedMs)\n\t\t\t: undefined;\n\tconst requestSignal = retryBudgetSignal ? AbortSignal.any([signal, retryBudgetSignal]) : signal;\n\tconst retryDeadline = retryBudgetSignal ? Date.now() + retryPolicy.maxElapsedMs : undefined;\n\tfor (let retry = 0; ; retry++) {\n\t\tconst response = await fetch(url, {\n\t\t\t...init,\n\t\t\tsignal: AbortSignal.any([requestSignal, AbortSignal.timeout(5000)]),\n\t\t});\n\t\tif (response.status !== 429 || retry === retryPolicy.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 (retryDeadline !== undefined && delayMs >= retryDeadline - Date.now()) return response;\n\t\tawait response.body?.cancel();\n\t\tawait sleep(delayMs, requestSignal);\n\t}\n}\n\nasync function fetchGitHubCopilotModels(\n\tcopilotToken: string,\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n\tretryPolicy: { maxRetries: number; maxElapsedMs: number },\n) {\n\tconst baseUrl = getGitHubCopilotBaseUrl(copilotToken, enterpriseDomain);\n\t// Some Individual accounts return false for every picker flag despite explicit enabled policies.\n\t// Limit the fallback to that endpoint so other account types keep strict picker semantics.\n\tconst allowPolicyFallback = baseUrl === \"https://api.individual.githubcopilot.com\";\n\tconst response = await fetchWithRateLimitRetry(\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 ${copilotToken}`,\n\t\t\t\t...COPILOT_HEADERS,\n\t\t\t\t\"X-GitHub-Api-Version\": COPILOT_API_VERSION,\n\t\t\t},\n\t\t},\n\t\tsignal,\n\t\tretryPolicy,\n\t);\n\tif (!response.ok) {\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${await response.text()}`);\n\t}\n\treturn parseGitHubCopilotModelCatalog(await response.json(), allowPolicyFallback);\n}\n\nasync function fetchJson(url: string, init: RequestInit): Promise<unknown> {\n\tconst response = await fetch(url, init);\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${text}`);\n\t}\n\treturn response.json();\n}\n\nasync function startDeviceFlow(domain: string, signal: AbortSignal): Promise<DeviceCodeResponse> {\n\tconst urls = getUrls(domain);\n\tconst data = await fetchJson(urls.deviceCodeUrl, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tscope: \"read:user\",\n\t\t}),\n\t\tsignal,\n\t});\n\n\tif (!data || typeof data !== \"object\") {\n\t\tthrow new Error(\"Invalid device code response\");\n\t}\n\n\tconst deviceCode = (data as Record<string, unknown>).device_code;\n\tconst userCode = (data as Record<string, unknown>).user_code;\n\tconst verificationUri = (data as Record<string, unknown>).verification_uri;\n\tconst interval = (data as Record<string, unknown>).interval;\n\tconst expiresIn = (data as Record<string, unknown>).expires_in;\n\n\tif (\n\t\ttypeof deviceCode !== \"string\" ||\n\t\ttypeof userCode !== \"string\" ||\n\t\ttypeof verificationUri !== \"string\" ||\n\t\t(interval !== undefined && typeof interval !== \"number\") ||\n\t\ttypeof expiresIn !== \"number\"\n\t) {\n\t\tthrow new Error(\"Invalid device code response fields\");\n\t}\n\n\t// The verification URI is opened in the user's browser and to prevent `open` from\n\t// opening an executable or similar, we force it to be a URL.\n\tlet parsedUri: URL;\n\ttry {\n\t\tparsedUri = new URL(verificationUri);\n\t} catch {\n\t\tthrow new Error(\"Untrusted verification_uri in device code response\");\n\t}\n\tif (parsedUri.protocol !== \"https:\" && parsedUri.protocol !== \"http:\") {\n\t\tthrow new Error(\"Untrusted verification_uri in device code response\");\n\t}\n\n\treturn {\n\t\tdevice_code: deviceCode,\n\t\tuser_code: userCode,\n\t\tverification_uri: parsedUri.href,\n\t\tinterval,\n\t\texpires_in: expiresIn,\n\t};\n}\n\nasync function pollForGitHubAccessToken(\n\tdomain: string,\n\tdevice: DeviceCodeResponse,\n\tsignal: AbortSignal,\n): Promise<string> {\n\tconst urls = getUrls(domain);\n\treturn pollOAuthDeviceCodeFlow<string>({\n\t\tintervalSeconds: device.interval,\n\t\texpiresInSeconds: device.expires_in,\n\t\twaitBeforeFirstPoll: true,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst raw = await fetchJson(urls.accessTokenUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t\t\t},\n\t\t\t\tbody: new URLSearchParams({\n\t\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\t\tdevice_code: device.device_code,\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,\n\t\t\t});\n\n\t\t\tif (raw && typeof raw === \"object\" && typeof (raw as DeviceTokenSuccessResponse).access_token === \"string\") {\n\t\t\t\treturn { status: \"complete\", value: (raw as DeviceTokenSuccessResponse).access_token };\n\t\t\t}\n\n\t\t\tif (raw && typeof raw === \"object\" && typeof (raw as DeviceTokenErrorResponse).error === \"string\") {\n\t\t\t\tconst { error, error_description: description, interval } = raw as DeviceTokenErrorResponse;\n\t\t\t\tif (error === \"authorization_pending\") {\n\t\t\t\t\treturn { status: \"pending\" };\n\t\t\t\t}\n\n\t\t\t\tif (error === \"slow_down\") {\n\t\t\t\t\treturn { status: \"slow_down\", intervalSeconds: typeof interval === \"number\" ? interval : undefined };\n\t\t\t\t}\n\n\t\t\t\tconst descriptionSuffix = description ? `: ${description}` : \"\";\n\t\t\t\treturn { status: \"failed\", message: `Device flow failed: ${error}${descriptionSuffix}` };\n\t\t\t}\n\n\t\t\treturn { status: \"failed\", message: \"Invalid device token response\" };\n\t\t},\n\t});\n}\n\nasync function refreshGitHubCopilotAccessToken(\n\trefreshToken: string,\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tconst domain = enterpriseDomain || \"github.com\";\n\tconst urls = getUrls(domain);\n\n\tconst raw = await fetchJson(urls.copilotTokenUrl, {\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t\tAuthorization: `Bearer ${refreshToken}`,\n\t\t\t...COPILOT_HEADERS,\n\t\t},\n\t\tsignal,\n\t});\n\n\tif (!raw || typeof raw !== \"object\") {\n\t\tthrow new Error(\"Invalid Copilot token response\");\n\t}\n\n\tconst token = (raw as Record<string, unknown>).token;\n\tconst expiresAt = (raw as Record<string, unknown>).expires_at;\n\n\tif (typeof token !== \"string\" || typeof expiresAt !== \"number\") {\n\t\tthrow new Error(\"Invalid Copilot token response fields\");\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\trefresh: refreshToken,\n\t\taccess: token,\n\t\texpires: expiresAt * 1000 - 5 * 60 * 1000,\n\t\tenterpriseUrl: enterpriseDomain,\n\t};\n}\n\n/**\n * Refresh GitHub Copilot token\n */\nasync function refreshGitHubCopilotToken(\n\trefreshToken: string,\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tconst credentials = await refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, signal);\n\tconst { availableModelIds, fastModelIds } = await fetchGitHubCopilotModels(\n\t\tcredentials.access,\n\t\tenterpriseDomain,\n\t\tsignal,\n\t\t{\n\t\t\tmaxRetries: 0,\n\t\t\tmaxElapsedMs: 0,\n\t\t},\n\t);\n\treturn {\n\t\t...credentials,\n\t\tavailableModelIds,\n\t\tfastModelIds,\n\t};\n}\n\n/**\n * Enable a model for the user's GitHub Copilot account.\n * This is required for some models (like Claude, Grok) before they can be used.\n */\nasync function enableGitHubCopilotModel(\n\ttoken: string,\n\tmodelId: string,\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n): Promise<boolean> {\n\tconst baseUrl = getGitHubCopilotBaseUrl(token, enterpriseDomain);\n\tconst url = `${baseUrl}/models/${modelId}/policy`;\n\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetchWithRateLimitRetry(\n\t\t\turl,\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\t...COPILOT_HEADERS,\n\t\t\t\t\t\"openai-intent\": \"chat-policy\",\n\t\t\t\t\t\"x-interaction-type\": \"chat-policy\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({ state: \"enabled\" }),\n\t\t\t},\n\t\t\tsignal,\n\t\t\t{ maxRetries: 2, maxElapsedMs: 5000 },\n\t\t);\n\t} catch (error) {\n\t\tif (signal.aborted) throw error;\n\t\treturn false;\n\t}\n\tif (response.status === 429) {\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${await response.text()}`);\n\t}\n\treturn response.ok;\n}\n\n/**\n * Enable the requested GitHub Copilot models and return the successful IDs.\n * Policy updates are best effort; exhausted rate limiting stops the batch.\n */\nasync function enableGitHubCopilotModels(\n\ttoken: string,\n\tmodelIds: readonly string[],\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n): Promise<string[]> {\n\tconst enabledModelIds: string[] = [];\n\tfor (const modelId of modelIds) {\n\t\ttry {\n\t\t\tif (await enableGitHubCopilotModel(token, modelId, enterpriseDomain, signal)) {\n\t\t\t\tenabledModelIds.push(modelId);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (signal.aborted) throw error;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn enabledModelIds;\n}\n\nasync function loginGitHubCopilot(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst input = await interaction.prompt({\n\t\ttype: \"text\",\n\t\tmessage: \"GitHub Enterprise URL/domain (blank for github.com)\",\n\t\tplaceholder: \"company.ghe.com\",\n\t});\n\tif (interaction.signal.aborted) throw new Error(\"Login cancelled\");\n\n\tconst trimmed = input.trim();\n\tconst enterpriseDomain = normalizeDomain(input);\n\tif (trimmed && !enterpriseDomain) throw new Error(\"Invalid GitHub Enterprise URL/domain\");\n\tconst domain = enterpriseDomain || \"github.com\";\n\n\tconst device = await startDeviceFlow(domain, interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.user_code,\n\t\tverificationUri: device.verification_uri,\n\t\tintervalSeconds: device.interval,\n\t\texpiresInSeconds: device.expires_in,\n\t});\n\n\tconst githubAccessToken = await pollForGitHubAccessToken(domain, device, interaction.signal);\n\tconst credentials = await refreshGitHubCopilotAccessToken(\n\t\tgithubAccessToken,\n\t\tenterpriseDomain ?? undefined,\n\t\tinteraction.signal,\n\t);\n\tconst models = await fetchGitHubCopilotModels(\n\t\tcredentials.access,\n\t\tenterpriseDomain ?? undefined,\n\t\tinteraction.signal,\n\t\t{\n\t\t\tmaxRetries: 2,\n\t\t\tmaxElapsedMs: 5000,\n\t\t},\n\t);\n\tlet enabledModelIds: string[] = [];\n\tif (models.policyModelIds.length > 0) {\n\t\tinteraction.notify({ type: \"progress\", message: \"Enabling models...\" });\n\t\tenabledModelIds = await enableGitHubCopilotModels(\n\t\t\tcredentials.access,\n\t\t\tmodels.policyModelIds,\n\t\t\tenterpriseDomain ?? undefined,\n\t\t\tinteraction.signal,\n\t\t);\n\t}\n\treturn {\n\t\t...credentials,\n\t\tavailableModelIds: [...new Set([...models.availableModelIds, ...enabledModelIds])],\n\t\tfastModelIds: models.fastModelIds,\n\t};\n}\n\nfunction copilotEnterpriseDomain(credential: OAuthCredential): string | undefined {\n\tconst enterpriseUrl = credential.enterpriseUrl;\n\tif (typeof enterpriseUrl !== \"string\" || !enterpriseUrl) return undefined;\n\treturn normalizeDomain(enterpriseUrl) ?? undefined;\n}\n\nexport const githubCopilotOAuth: OAuthAuth = {\n\tname: \"GitHub Copilot\",\n\tisSubscription: true,\n\tlogin: loginGitHubCopilot,\n\trefresh: (credential, signal) =>\n\t\trefreshGitHubCopilotToken(credential.refresh, copilotEnterpriseDomain(credential), signal),\n\n\t/** Derive the credential-specific proxy endpoint for each request. */\n\tasync toAuth(credential) {\n\t\treturn {\n\t\t\tapiKey: credential.access,\n\t\t\tbaseUrl: getGitHubCopilotBaseUrl(credential.access, copilotEnterpriseDomain(credential)),\n\t\t};\n\t},\n};\n"]}
1
+ {"version":3,"file":"github-copilot.js","sourceRoot":"","sources":["../../../src/auth/oauth/github-copilot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAE5F,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACtC,MAAM,SAAS,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC;AAEzD,MAAM,eAAe,GAAG;IACvB,YAAY,EAAE,0BAA0B;IACxC,gBAAgB,EAAE,gBAAgB;IAClC,uBAAuB,EAAE,qBAAqB;IAC9C,wBAAwB,EAAE,aAAa;CAC9B,CAAC;AACX,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAsBzC,SAAS,eAAe,CAAC,KAAa;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW,OAAO,EAAE,CAAC,CAAC;QACvF,OAAO,GAAG,CAAC,QAAQ,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,SAAS,OAAO,CAAC,MAAc;IAK9B,OAAO;QACN,aAAa,EAAE,WAAW,MAAM,oBAAoB;QACpD,cAAc,EAAE,WAAW,MAAM,2BAA2B;QAC5D,eAAe,EAAE,eAAe,MAAM,4BAA4B;KAClE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,KAAa;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,+BAA+B;IAC/B,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACtD,OAAO,WAAW,OAAO,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAc,EAAE,gBAAyB;IACzE,yDAAyD;IACzD,IAAI,KAAK,EAAE,CAAC;QACX,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,YAAY;YAAE,OAAO,YAAY,CAAC;IACvC,CAAC;IACD,oDAAoD;IACpD,IAAI,gBAAgB;QAAE,OAAO,uBAAuB,gBAAgB,EAAE,CAAC;IACvE,OAAO,0CAA0C,CAAC;AACnD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAE,KAAiC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5F,CAAC;AAED,SAAS,8BAA8B,CAAC,GAAY,EAAE,mBAA4B;IACjF,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;IACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/B,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,IAAI,OAAO,EAAE,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QAE/C,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAClD,IAAI,QAAQ,EAAE,UAAU,KAAK,KAAK;YAAE,OAAO,EAAE,CAAC;QAE9C,OAAO;YACN;gBACC,EAAE;gBACF,aAAa,EAAE,IAAI,CAAC,oBAAoB,KAAK,IAAI;gBACjD,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK;aACzC;SACD,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,aAAa;SAClC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,WAAW,KAAK,UAAU,CAAC;SAC1E,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3B,MAAM,iBAAiB,GAAG,mBAAmB,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,CAAC;IAC7E,MAAM,iBAAiB,GACtB,cAAc,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,mBAAmB;QAChD,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,aAAa;aACZ,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC;aACjF,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,YAAY,GAAG,aAAa;SAChC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,WAAW,KAAK,UAAU,CAAC;SACjF,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3B,MAAM,cAAc,GAAG,aAAa;SAClC,MAAM,CACN,CAAC,KAAK,EAAE,EAAE,CACT,KAAK,CAAC,WAAW,KAAK,cAAc;QACpC,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC;QAC9C,CAAC,KAAK,CAAC,aAAa,IAAI,iBAAiB,CAAC,CAC3C;SACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,uBAAuB,CACrC,GAAW,EACX,IAAiB,EACjB,MAAmB,EACnB,WAAyD;IAEzD,MAAM,iBAAiB,GACtB,WAAW,CAAC,UAAU,GAAG,CAAC,IAAI,WAAW,CAAC,YAAY,GAAG,CAAC;QACzD,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC,YAAY,CAAC;QAC/C,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,aAAa,GAAG,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAChG,MAAM,aAAa,GAAG,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5F,KAAK,IAAI,KAAK,GAAG,CAAC,GAAI,KAAK,EAAE,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YACjC,GAAG,IAAI;YACP,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;SACnE,CAAC,CAAC;QACH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,KAAK,WAAW,CAAC,UAAU;YAAE,OAAO,QAAQ,CAAC;QAEjF,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACvD,IAAI,OAAO,GAAG,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC;QAC/B,IAAI,UAAU,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YAC9C,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC;YACvF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO,QAAQ,CAAC;QAChD,CAAC;QACD,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/B,IAAI,aAAa,KAAK,SAAS,IAAI,OAAO,IAAI,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,QAAQ,CAAC;QAC1F,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAC9B,MAAM,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACrC,CAAC;AACF,CAAC;AAED,KAAK,UAAU,wBAAwB,CACtC,YAAoB,EACpB,gBAAoC,EACpC,MAAmB,EACnB,WAAyD;IAEzD,MAAM,OAAO,GAAG,uBAAuB,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC;IACxE,iGAAiG;IACjG,2FAA2F;IAC3F,MAAM,mBAAmB,GAAG,OAAO,KAAK,0CAA0C,CAAC;IACnF,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAC7C,GAAG,OAAO,SAAS,EACnB;QACC,OAAO,EAAE;YACR,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,UAAU,YAAY,EAAE;YACvC,GAAG,eAAe;YAClB,sBAAsB,EAAE,mBAAmB;SAC3C;KACD,EACD,MAAM,EACN,WAAW,CACX,CAAC;IACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,KAAK,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,8BAA8B,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,mBAAmB,CAAC,CAAC;AACnF,CAAC;AAED,mHAAmH;AACnH,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,IAAiB,EAAE,cAAuB;IAC/E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,cAAc,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC7D,MAAM,IAAI,yBAAyB,CAClC,wCAAwC,QAAQ,CAAC,MAAM,GAAG,EAC1D,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,GAAG,mBAAmB,CAAC,IAAI,CAAC,EAAE,EACzD,CAAC,cAAc,CAAC,CAChB,CAAC;QACH,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,MAAmB;IACjE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,aAAa,EAAE;QAChD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACR,MAAM,EAAE,kBAAkB;YAC1B,cAAc,EAAE,mCAAmC;YACnD,YAAY,EAAE,0BAA0B;SACxC;QACD,IAAI,EAAE,IAAI,eAAe,CAAC;YACzB,SAAS,EAAE,SAAS;YACpB,KAAK,EAAE,WAAW;SAClB,CAAC;QACF,MAAM;KACN,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,UAAU,GAAI,IAAgC,CAAC,WAAW,CAAC;IACjE,MAAM,QAAQ,GAAI,IAAgC,CAAC,SAAS,CAAC;IAC7D,MAAM,eAAe,GAAI,IAAgC,CAAC,gBAAgB,CAAC;IAC3E,MAAM,QAAQ,GAAI,IAAgC,CAAC,QAAQ,CAAC;IAC5D,MAAM,SAAS,GAAI,IAAgC,CAAC,UAAU,CAAC;IAE/D,IACC,OAAO,UAAU,KAAK,QAAQ;QAC9B,OAAO,QAAQ,KAAK,QAAQ;QAC5B,OAAO,eAAe,KAAK,QAAQ;QACnC,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,QAAQ,KAAK,QAAQ,CAAC;QACxD,OAAO,SAAS,KAAK,QAAQ,EAC5B,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACxD,CAAC;IAED,kFAAkF;IAClF,6DAA6D;IAC7D,IAAI,SAAc,CAAC;IACnB,IAAI,CAAC;QACJ,SAAS,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,SAAS,CAAC,QAAQ,KAAK,QAAQ,IAAI,SAAS,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACvE,CAAC;IAED,OAAO;QACN,WAAW,EAAE,UAAU;QACvB,SAAS,EAAE,QAAQ;QACnB,gBAAgB,EAAE,SAAS,CAAC,IAAI;QAChC,QAAQ;QACR,UAAU,EAAE,SAAS;KACrB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,wBAAwB,CACtC,MAAc,EACd,MAA0B,EAC1B,MAAmB;IAEnB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO,uBAAuB,CAAS;QACtC,eAAe,EAAE,MAAM,CAAC,QAAQ;QAChC,gBAAgB,EAAE,MAAM,CAAC,UAAU;QACnC,mBAAmB,EAAE,IAAI;QACzB,MAAM;QACN,IAAI,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,cAAc,EAAE;gBAChD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACR,MAAM,EAAE,kBAAkB;oBAC1B,cAAc,EAAE,mCAAmC;oBACnD,YAAY,EAAE,0BAA0B;iBACxC;gBACD,IAAI,EAAE,IAAI,eAAe,CAAC;oBACzB,SAAS,EAAE,SAAS;oBACpB,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,UAAU,EAAE,8CAA8C;iBAC1D,CAAC;gBACF,MAAM;aACN,CAAC,CAAC;YAEH,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAQ,GAAkC,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;gBAC5G,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAG,GAAkC,CAAC,YAAY,EAAE,CAAC;YACxF,CAAC;YAED,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAQ,GAAgC,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACnG,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,GAA+B,CAAC;gBAC5F,IAAI,KAAK,KAAK,uBAAuB,EAAE,CAAC;oBACvC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;gBAC9B,CAAC;gBAED,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;oBAC3B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;gBACtG,CAAC;gBAED,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,uBAAuB,KAAK,GAAG,iBAAiB,EAAE,EAAE,CAAC;YAC1F,CAAC;YAED,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC;QACvE,CAAC;KACD,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,+BAA+B,CAC7C,YAAoB,EACpB,gBAAoC,EACpC,MAAmB;IAEnB,MAAM,MAAM,GAAG,gBAAgB,IAAI,YAAY,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7B,MAAM,GAAG,GAAG,MAAM,SAAS,CAC1B,IAAI,CAAC,eAAe,EACpB;QACC,OAAO,EAAE;YACR,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,UAAU,YAAY,EAAE;YACvC,GAAG,eAAe;SAClB;QACD,MAAM;KACN,EACD,YAAY,CACZ,CAAC;IAEF,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,KAAK,GAAI,GAA+B,CAAC,KAAK,CAAC;IACrD,MAAM,SAAS,GAAI,GAA+B,CAAC,UAAU,CAAC;IAE9D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO;QACN,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI;QACzC,aAAa,EAAE,gBAAgB;KAC/B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CACvC,YAAoB,EACpB,gBAAoC,EACpC,MAAmB;IAEnB,MAAM,WAAW,GAAG,MAAM,+BAA+B,CAAC,YAAY,EAAE,gBAAgB,EAAE,MAAM,CAAC,CAAC;IAClG,MAAM,EAAE,iBAAiB,EAAE,YAAY,EAAE,GAAG,MAAM,wBAAwB,CACzE,WAAW,CAAC,MAAM,EAClB,gBAAgB,EAChB,MAAM,EACN;QACC,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,CAAC;KACf,CACD,CAAC;IACF,OAAO;QACN,GAAG,WAAW;QACd,iBAAiB;QACjB,YAAY;KACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,wBAAwB,CACtC,KAAa,EACb,OAAe,EACf,gBAAoC,EACpC,MAAmB;IAEnB,MAAM,OAAO,GAAG,uBAAuB,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IACjE,MAAM,GAAG,GAAG,GAAG,OAAO,WAAW,OAAO,SAAS,CAAC;IAElD,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACJ,QAAQ,GAAG,MAAM,uBAAuB,CACvC,GAAG,EACH;YACC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACR,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,GAAG,eAAe;gBAClB,eAAe,EAAE,aAAa;gBAC9B,oBAAoB,EAAE,aAAa;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;SAC1C,EACD,MAAM,EACN,EAAE,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CACrC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,MAAM,CAAC,OAAO;YAAE,MAAM,KAAK,CAAC;QAChC,OAAO,KAAK,CAAC;IACd,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,KAAK,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,QAAQ,CAAC,EAAE,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,yBAAyB,CACvC,KAAa,EACb,QAA2B,EAC3B,gBAAoC,EACpC,MAAmB;IAEnB,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAChC,IAAI,CAAC;YACJ,IAAI,MAAM,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,CAAC;gBAC9E,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,MAAM,CAAC,OAAO;gBAAE,MAAM,KAAK,CAAC;YAChC,MAAM;QACP,CAAC;IACF,CAAC;IACD,OAAO,eAAe,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,WAAoC;IACrE,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC;QACtC,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,qDAAqD;QAC9D,WAAW,EAAE,iBAAiB;KAC9B,CAAC,CAAC;IACH,IAAI,WAAW,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAEnE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,gBAAgB,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAChD,IAAI,OAAO,IAAI,CAAC,gBAAgB;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1F,MAAM,MAAM,GAAG,gBAAgB,IAAI,YAAY,CAAC;IAEhD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjE,WAAW,CAAC,MAAM,CAAC;QAClB,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,MAAM,CAAC,SAAS;QAC1B,eAAe,EAAE,MAAM,CAAC,gBAAgB;QACxC,eAAe,EAAE,MAAM,CAAC,QAAQ;QAChC,gBAAgB,EAAE,MAAM,CAAC,UAAU;KACnC,CAAC,CAAC;IAEH,MAAM,iBAAiB,GAAG,MAAM,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7F,MAAM,WAAW,GAAG,MAAM,+BAA+B,CACxD,iBAAiB,EACjB,gBAAgB,IAAI,SAAS,EAC7B,WAAW,CAAC,MAAM,CAClB,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAC5C,WAAW,CAAC,MAAM,EAClB,gBAAgB,IAAI,SAAS,EAC7B,WAAW,CAAC,MAAM,EAClB;QACC,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,IAAI;KAClB,CACD,CAAC;IACF,IAAI,eAAe,GAAa,EAAE,CAAC;IACnC,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAC;QACxE,eAAe,GAAG,MAAM,yBAAyB,CAChD,WAAW,CAAC,MAAM,EAClB,MAAM,CAAC,cAAc,EACrB,gBAAgB,IAAI,SAAS,EAC7B,WAAW,CAAC,MAAM,CAClB,CAAC;IACH,CAAC;IACD,OAAO;QACN,GAAG,WAAW;QACd,iBAAiB,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,iBAAiB,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC;QAClF,YAAY,EAAE,MAAM,CAAC,YAAY;KACjC,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,UAA2B;IAC3D,MAAM,aAAa,GAAG,UAAU,CAAC,aAAa,CAAC;IAC/C,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,CAAC,aAAa;QAAE,OAAO,SAAS,CAAC;IAC1E,OAAO,eAAe,CAAC,aAAa,CAAC,IAAI,SAAS,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAc;IAC5C,IAAI,EAAE,gBAAgB;IACtB,cAAc,EAAE,IAAI;IACpB,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,CAC/B,yBAAyB,CAAC,UAAU,CAAC,OAAO,EAAE,uBAAuB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE3F,sEAAsE;IACtE,KAAK,CAAC,MAAM,CAAC,UAAU;QACtB,OAAO;YACN,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE,uBAAuB,CAAC,UAAU,CAAC,MAAM,EAAE,uBAAuB,CAAC,UAAU,CAAC,CAAC;SACxF,CAAC;IACH,CAAC;CACD,CAAC","sourcesContent":["/**\n * GitHub Copilot OAuth flow\n */\n\nimport { GITHUB_COPILOT_MODELS } from \"../../providers/github-copilot.models.ts\";\nimport { sleep } from \"../../utils/sleep.ts\";\nimport { OAuthRefreshRejectedError, parseOAuthErrorBody } from \"../oauth-refresh-errors.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"SXYxLmI1MDdhMDhjODdlY2ZlOTg=\");\n\nconst COPILOT_HEADERS = {\n\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\"Editor-Version\": \"vscode/1.107.0\",\n\t\"Editor-Plugin-Version\": \"copilot-chat/0.35.0\",\n\t\"Copilot-Integration-Id\": \"vscode-chat\",\n} as const;\nconst COPILOT_API_VERSION = \"2026-06-01\";\n\ntype DeviceCodeResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\tinterval?: number;\n\texpires_in: number;\n};\n\ntype DeviceTokenSuccessResponse = {\n\taccess_token: string;\n\ttoken_type?: string;\n\tscope?: string;\n};\n\ntype DeviceTokenErrorResponse = {\n\terror: string;\n\terror_description?: string;\n\tinterval?: number;\n};\n\nfunction normalizeDomain(input: string): string | null {\n\tconst trimmed = input.trim();\n\tif (!trimmed) return null;\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 null;\n\t}\n}\n\nfunction getUrls(domain: string): {\n\tdeviceCodeUrl: string;\n\taccessTokenUrl: string;\n\tcopilotTokenUrl: string;\n} {\n\treturn {\n\t\tdeviceCodeUrl: `https://${domain}/login/device/code`,\n\t\taccessTokenUrl: `https://${domain}/login/oauth/access_token`,\n\t\tcopilotTokenUrl: `https://api.${domain}/copilot_internal/v2/token`,\n\t};\n}\n\n/**\n * Parse the proxy-ep from a Copilot token and convert to API base URL.\n * Token format: tid=...;exp=...;proxy-ep=proxy.individual.githubcopilot.com;...\n * Returns API URL like https://api.individual.githubcopilot.com\n */\nfunction getBaseUrlFromToken(token: string): string | null {\n\tconst match = token.match(/proxy-ep=([^;]+)/);\n\tif (!match) return null;\n\tconst proxyHost = match[1];\n\t// Convert proxy.xxx to api.xxx\n\tconst apiHost = proxyHost.replace(/^proxy\\./, \"api.\");\n\treturn `https://${apiHost}`;\n}\n\nfunction getGitHubCopilotBaseUrl(token?: string, enterpriseDomain?: string): string {\n\t// If we have a token, extract the base URL from proxy-ep\n\tif (token) {\n\t\tconst urlFromToken = getBaseUrlFromToken(token);\n\t\tif (urlFromToken) return urlFromToken;\n\t}\n\t// Fallback for enterprise or if token parsing fails\n\tif (enterpriseDomain) return `https://copilot-api.${enterpriseDomain}`;\n\treturn \"https://api.individual.githubcopilot.com\";\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn value && typeof value === \"object\" ? (value as Record<string, unknown>) : undefined;\n}\n\nfunction parseGitHubCopilotModelCatalog(raw: unknown, allowPolicyFallback: boolean) {\n\tconst data = asRecord(raw)?.data;\n\tif (!Array.isArray(data)) {\n\t\tthrow new Error(\"Invalid Copilot models response\");\n\t}\n\n\tconst accountModels = data.flatMap((rawItem) => {\n\t\tconst item = asRecord(rawItem);\n\t\tconst id = item?.id;\n\t\tif (!item || typeof id !== \"string\") return [];\n\n\t\tconst capabilities = asRecord(item.capabilities);\n\t\tconst supports = asRecord(capabilities?.supports);\n\t\tif (supports?.tool_calls === false) return [];\n\n\t\treturn [\n\t\t\t{\n\t\t\t\tid,\n\t\t\t\tpickerEnabled: item.model_picker_enabled === true,\n\t\t\t\tpolicyState: asRecord(item.policy)?.state,\n\t\t\t},\n\t\t];\n\t});\n\tconst pickerModelIds = accountModels\n\t\t.filter((model) => model.pickerEnabled && model.policyState !== \"disabled\")\n\t\t.map((model) => model.id);\n\tconst usePolicyFallback = allowPolicyFallback && pickerModelIds.length === 0;\n\tconst availableModelIds =\n\t\tpickerModelIds.length > 0 || !allowPolicyFallback\n\t\t\t? pickerModelIds\n\t\t\t: accountModels\n\t\t\t\t\t.filter((model) => !model.id.endsWith(\"-fast\") && model.policyState === \"enabled\")\n\t\t\t\t\t.map((model) => model.id);\n\tconst fastModelIds = accountModels\n\t\t.filter((model) => model.id.endsWith(\"-fast\") && model.policyState !== \"disabled\")\n\t\t.map((model) => model.id);\n\tconst policyModelIds = accountModels\n\t\t.filter(\n\t\t\t(model) =>\n\t\t\t\tmodel.policyState === \"unconfigured\" &&\n\t\t\t\tObject.hasOwn(GITHUB_COPILOT_MODELS, model.id) &&\n\t\t\t\t(model.pickerEnabled || usePolicyFallback),\n\t\t)\n\t\t.map((model) => model.id);\n\treturn { availableModelIds, fastModelIds, policyModelIds };\n}\n\nasync function fetchWithRateLimitRetry(\n\turl: string,\n\tinit: RequestInit,\n\tsignal: AbortSignal,\n\tretryPolicy: { maxRetries: number; maxElapsedMs: number },\n): Promise<Response> {\n\tconst retryBudgetSignal =\n\t\tretryPolicy.maxRetries > 0 && retryPolicy.maxElapsedMs > 0\n\t\t\t? AbortSignal.timeout(retryPolicy.maxElapsedMs)\n\t\t\t: undefined;\n\tconst requestSignal = retryBudgetSignal ? AbortSignal.any([signal, retryBudgetSignal]) : signal;\n\tconst retryDeadline = retryBudgetSignal ? Date.now() + retryPolicy.maxElapsedMs : undefined;\n\tfor (let retry = 0; ; retry++) {\n\t\tconst response = await fetch(url, {\n\t\t\t...init,\n\t\t\tsignal: AbortSignal.any([requestSignal, AbortSignal.timeout(5000)]),\n\t\t});\n\t\tif (response.status !== 429 || retry === retryPolicy.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 (retryDeadline !== undefined && delayMs >= retryDeadline - Date.now()) return response;\n\t\tawait response.body?.cancel();\n\t\tawait sleep(delayMs, requestSignal);\n\t}\n}\n\nasync function fetchGitHubCopilotModels(\n\tcopilotToken: string,\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n\tretryPolicy: { maxRetries: number; maxElapsedMs: number },\n) {\n\tconst baseUrl = getGitHubCopilotBaseUrl(copilotToken, enterpriseDomain);\n\t// Some Individual accounts return false for every picker flag despite explicit enabled policies.\n\t// Limit the fallback to that endpoint so other account types keep strict picker semantics.\n\tconst allowPolicyFallback = baseUrl === \"https://api.individual.githubcopilot.com\";\n\tconst response = await fetchWithRateLimitRetry(\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 ${copilotToken}`,\n\t\t\t\t...COPILOT_HEADERS,\n\t\t\t\t\"X-GitHub-Api-Version\": COPILOT_API_VERSION,\n\t\t\t},\n\t\t},\n\t\tsignal,\n\t\tretryPolicy,\n\t);\n\tif (!response.ok) {\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${await response.text()}`);\n\t}\n\treturn parseGitHubCopilotModelCatalog(await response.json(), allowPolicyFallback);\n}\n\n/** `presentedToken` marks a request authenticated by the stored login: a 401 then means that login was revoked. */\nasync function fetchJson(url: string, init: RequestInit, presentedToken?: string): Promise<unknown> {\n\tconst response = await fetch(url, init);\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tif (presentedToken !== undefined && response.status === 401) {\n\t\t\tthrow new OAuthRefreshRejectedError(\n\t\t\t\t`GitHub Copilot token request failed (${response.status})`,\n\t\t\t\t{ status: response.status, ...parseOAuthErrorBody(text) },\n\t\t\t\t[presentedToken],\n\t\t\t);\n\t\t}\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${text}`);\n\t}\n\treturn response.json();\n}\n\nasync function startDeviceFlow(domain: string, signal: AbortSignal): Promise<DeviceCodeResponse> {\n\tconst urls = getUrls(domain);\n\tconst data = await fetchJson(urls.deviceCodeUrl, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tscope: \"read:user\",\n\t\t}),\n\t\tsignal,\n\t});\n\n\tif (!data || typeof data !== \"object\") {\n\t\tthrow new Error(\"Invalid device code response\");\n\t}\n\n\tconst deviceCode = (data as Record<string, unknown>).device_code;\n\tconst userCode = (data as Record<string, unknown>).user_code;\n\tconst verificationUri = (data as Record<string, unknown>).verification_uri;\n\tconst interval = (data as Record<string, unknown>).interval;\n\tconst expiresIn = (data as Record<string, unknown>).expires_in;\n\n\tif (\n\t\ttypeof deviceCode !== \"string\" ||\n\t\ttypeof userCode !== \"string\" ||\n\t\ttypeof verificationUri !== \"string\" ||\n\t\t(interval !== undefined && typeof interval !== \"number\") ||\n\t\ttypeof expiresIn !== \"number\"\n\t) {\n\t\tthrow new Error(\"Invalid device code response fields\");\n\t}\n\n\t// The verification URI is opened in the user's browser and to prevent `open` from\n\t// opening an executable or similar, we force it to be a URL.\n\tlet parsedUri: URL;\n\ttry {\n\t\tparsedUri = new URL(verificationUri);\n\t} catch {\n\t\tthrow new Error(\"Untrusted verification_uri in device code response\");\n\t}\n\tif (parsedUri.protocol !== \"https:\" && parsedUri.protocol !== \"http:\") {\n\t\tthrow new Error(\"Untrusted verification_uri in device code response\");\n\t}\n\n\treturn {\n\t\tdevice_code: deviceCode,\n\t\tuser_code: userCode,\n\t\tverification_uri: parsedUri.href,\n\t\tinterval,\n\t\texpires_in: expiresIn,\n\t};\n}\n\nasync function pollForGitHubAccessToken(\n\tdomain: string,\n\tdevice: DeviceCodeResponse,\n\tsignal: AbortSignal,\n): Promise<string> {\n\tconst urls = getUrls(domain);\n\treturn pollOAuthDeviceCodeFlow<string>({\n\t\tintervalSeconds: device.interval,\n\t\texpiresInSeconds: device.expires_in,\n\t\twaitBeforeFirstPoll: true,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst raw = await fetchJson(urls.accessTokenUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t\t\t},\n\t\t\t\tbody: new URLSearchParams({\n\t\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\t\tdevice_code: device.device_code,\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,\n\t\t\t});\n\n\t\t\tif (raw && typeof raw === \"object\" && typeof (raw as DeviceTokenSuccessResponse).access_token === \"string\") {\n\t\t\t\treturn { status: \"complete\", value: (raw as DeviceTokenSuccessResponse).access_token };\n\t\t\t}\n\n\t\t\tif (raw && typeof raw === \"object\" && typeof (raw as DeviceTokenErrorResponse).error === \"string\") {\n\t\t\t\tconst { error, error_description: description, interval } = raw as DeviceTokenErrorResponse;\n\t\t\t\tif (error === \"authorization_pending\") {\n\t\t\t\t\treturn { status: \"pending\" };\n\t\t\t\t}\n\n\t\t\t\tif (error === \"slow_down\") {\n\t\t\t\t\treturn { status: \"slow_down\", intervalSeconds: typeof interval === \"number\" ? interval : undefined };\n\t\t\t\t}\n\n\t\t\t\tconst descriptionSuffix = description ? `: ${description}` : \"\";\n\t\t\t\treturn { status: \"failed\", message: `Device flow failed: ${error}${descriptionSuffix}` };\n\t\t\t}\n\n\t\t\treturn { status: \"failed\", message: \"Invalid device token response\" };\n\t\t},\n\t});\n}\n\nasync function refreshGitHubCopilotAccessToken(\n\trefreshToken: string,\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tconst domain = enterpriseDomain || \"github.com\";\n\tconst urls = getUrls(domain);\n\n\tconst raw = await fetchJson(\n\t\turls.copilotTokenUrl,\n\t\t{\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${refreshToken}`,\n\t\t\t\t...COPILOT_HEADERS,\n\t\t\t},\n\t\t\tsignal,\n\t\t},\n\t\trefreshToken,\n\t);\n\n\tif (!raw || typeof raw !== \"object\") {\n\t\tthrow new Error(\"Invalid Copilot token response\");\n\t}\n\n\tconst token = (raw as Record<string, unknown>).token;\n\tconst expiresAt = (raw as Record<string, unknown>).expires_at;\n\n\tif (typeof token !== \"string\" || typeof expiresAt !== \"number\") {\n\t\tthrow new Error(\"Invalid Copilot token response fields\");\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\trefresh: refreshToken,\n\t\taccess: token,\n\t\texpires: expiresAt * 1000 - 5 * 60 * 1000,\n\t\tenterpriseUrl: enterpriseDomain,\n\t};\n}\n\n/**\n * Refresh GitHub Copilot token\n */\nasync function refreshGitHubCopilotToken(\n\trefreshToken: string,\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tconst credentials = await refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, signal);\n\tconst { availableModelIds, fastModelIds } = await fetchGitHubCopilotModels(\n\t\tcredentials.access,\n\t\tenterpriseDomain,\n\t\tsignal,\n\t\t{\n\t\t\tmaxRetries: 0,\n\t\t\tmaxElapsedMs: 0,\n\t\t},\n\t);\n\treturn {\n\t\t...credentials,\n\t\tavailableModelIds,\n\t\tfastModelIds,\n\t};\n}\n\n/**\n * Enable a model for the user's GitHub Copilot account.\n * This is required for some models (like Claude, Grok) before they can be used.\n */\nasync function enableGitHubCopilotModel(\n\ttoken: string,\n\tmodelId: string,\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n): Promise<boolean> {\n\tconst baseUrl = getGitHubCopilotBaseUrl(token, enterpriseDomain);\n\tconst url = `${baseUrl}/models/${modelId}/policy`;\n\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetchWithRateLimitRetry(\n\t\t\turl,\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\t...COPILOT_HEADERS,\n\t\t\t\t\t\"openai-intent\": \"chat-policy\",\n\t\t\t\t\t\"x-interaction-type\": \"chat-policy\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({ state: \"enabled\" }),\n\t\t\t},\n\t\t\tsignal,\n\t\t\t{ maxRetries: 2, maxElapsedMs: 5000 },\n\t\t);\n\t} catch (error) {\n\t\tif (signal.aborted) throw error;\n\t\treturn false;\n\t}\n\tif (response.status === 429) {\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${await response.text()}`);\n\t}\n\treturn response.ok;\n}\n\n/**\n * Enable the requested GitHub Copilot models and return the successful IDs.\n * Policy updates are best effort; exhausted rate limiting stops the batch.\n */\nasync function enableGitHubCopilotModels(\n\ttoken: string,\n\tmodelIds: readonly string[],\n\tenterpriseDomain: string | undefined,\n\tsignal: AbortSignal,\n): Promise<string[]> {\n\tconst enabledModelIds: string[] = [];\n\tfor (const modelId of modelIds) {\n\t\ttry {\n\t\t\tif (await enableGitHubCopilotModel(token, modelId, enterpriseDomain, signal)) {\n\t\t\t\tenabledModelIds.push(modelId);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (signal.aborted) throw error;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn enabledModelIds;\n}\n\nasync function loginGitHubCopilot(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst input = await interaction.prompt({\n\t\ttype: \"text\",\n\t\tmessage: \"GitHub Enterprise URL/domain (blank for github.com)\",\n\t\tplaceholder: \"company.ghe.com\",\n\t});\n\tif (interaction.signal.aborted) throw new Error(\"Login cancelled\");\n\n\tconst trimmed = input.trim();\n\tconst enterpriseDomain = normalizeDomain(input);\n\tif (trimmed && !enterpriseDomain) throw new Error(\"Invalid GitHub Enterprise URL/domain\");\n\tconst domain = enterpriseDomain || \"github.com\";\n\n\tconst device = await startDeviceFlow(domain, interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.user_code,\n\t\tverificationUri: device.verification_uri,\n\t\tintervalSeconds: device.interval,\n\t\texpiresInSeconds: device.expires_in,\n\t});\n\n\tconst githubAccessToken = await pollForGitHubAccessToken(domain, device, interaction.signal);\n\tconst credentials = await refreshGitHubCopilotAccessToken(\n\t\tgithubAccessToken,\n\t\tenterpriseDomain ?? undefined,\n\t\tinteraction.signal,\n\t);\n\tconst models = await fetchGitHubCopilotModels(\n\t\tcredentials.access,\n\t\tenterpriseDomain ?? undefined,\n\t\tinteraction.signal,\n\t\t{\n\t\t\tmaxRetries: 2,\n\t\t\tmaxElapsedMs: 5000,\n\t\t},\n\t);\n\tlet enabledModelIds: string[] = [];\n\tif (models.policyModelIds.length > 0) {\n\t\tinteraction.notify({ type: \"progress\", message: \"Enabling models...\" });\n\t\tenabledModelIds = await enableGitHubCopilotModels(\n\t\t\tcredentials.access,\n\t\t\tmodels.policyModelIds,\n\t\t\tenterpriseDomain ?? undefined,\n\t\t\tinteraction.signal,\n\t\t);\n\t}\n\treturn {\n\t\t...credentials,\n\t\tavailableModelIds: [...new Set([...models.availableModelIds, ...enabledModelIds])],\n\t\tfastModelIds: models.fastModelIds,\n\t};\n}\n\nfunction copilotEnterpriseDomain(credential: OAuthCredential): string | undefined {\n\tconst enterpriseUrl = credential.enterpriseUrl;\n\tif (typeof enterpriseUrl !== \"string\" || !enterpriseUrl) return undefined;\n\treturn normalizeDomain(enterpriseUrl) ?? undefined;\n}\n\nexport const githubCopilotOAuth: OAuthAuth = {\n\tname: \"GitHub Copilot\",\n\tisSubscription: true,\n\tlogin: loginGitHubCopilot,\n\trefresh: (credential, signal) =>\n\t\trefreshGitHubCopilotToken(credential.refresh, copilotEnterpriseDomain(credential), signal),\n\n\t/** Derive the credential-specific proxy endpoint for each request. */\n\tasync toAuth(credential) {\n\t\treturn {\n\t\t\tapiKey: credential.access,\n\t\t\tbaseUrl: getGitHubCopilotBaseUrl(credential.access, copilotEnterpriseDomain(credential)),\n\t\t};\n\t},\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"kimi-coding.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/kimi-coding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA8QvF,eAAO,MAAM,eAAe,EAAE,SAe7B,CAAC"}
1
+ {"version":3,"file":"kimi-coding.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/kimi-coding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAYH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AAkRvF,eAAO,MAAM,eAAe,EAAE,SAe7B,CAAC"}
@@ -7,13 +7,17 @@
7
7
  */
8
8
  import { getProviderEnvValue } from "../../utils/provider-env.js";
9
9
  import { sleep } from "../../utils/sleep.js";
10
+ import { formatOAuthErrorDetail, isPermanentRefreshRejection, OAuthRefreshRejectedError, parseOAuthErrorBody, requestNeverReachedServer, } from "../oauth-refresh-errors.js";
10
11
  import { pollOAuthDeviceCodeFlow } from "./device-code.js";
11
12
  const CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098";
12
13
  const DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
13
14
  const DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;
14
15
  const DEFAULT_POLL_INTERVAL_SECONDS = 5;
16
+ /** Login requests (device authorization and polling) give up after this; a refresh request never does. */
15
17
  const REQUEST_TIMEOUT_MS = 30 * 1000;
18
+ /** Retries of a refresh request that never reached the server, after 1 s, 2 s, then 4 s. */
16
19
  const REFRESH_MAX_RETRIES = 3;
20
+ const REFRESH_RETRY_BASE_DELAY_MS = 1000;
17
21
  function getOauthHost() {
18
22
  const override = getProviderEnvValue("KIMI_CODE_OAUTH_HOST") || getProviderEnvValue("KIMI_OAUTH_HOST");
19
23
  return (override || DEFAULT_OAUTH_HOST).replace(/\/+$/, "");
@@ -169,20 +173,22 @@ async function pollForToken(oauthHost, device, signal) {
169
173
  },
170
174
  });
171
175
  }
172
- function isRetryableRefreshFailure(response) {
173
- return response.status === 429 || response.status >= 500;
176
+ /** Kimi treats 401 and 403 from its token endpoint as a dead login, along with the usual permanent codes. */
177
+ function isUnauthorizedRefresh(rejection) {
178
+ return rejection.status === 401 || rejection.status === 403 || isPermanentRefreshRejection(rejection);
174
179
  }
175
180
  async function refreshToken(oauthHost, refreshTokenValue, signal) {
176
- let lastError;
177
- for (let attempt = 0; attempt <= REFRESH_MAX_RETRIES; attempt++) {
181
+ for (let attempt = 0;; attempt++) {
178
182
  if (attempt > 0) {
179
- await sleep(1000 * 2 ** (attempt - 1), signal);
183
+ await sleep(REFRESH_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1), signal);
180
184
  }
181
185
  if (signal.aborted) {
182
186
  throw new Error("Kimi Code token refresh aborted");
183
187
  }
184
188
  let response;
185
189
  try {
190
+ // No timeout of its own: once sent, the server may already have rotated the refresh token, and only
191
+ // this response carries the replacement.
186
192
  response = await fetch(`${oauthHost}/api/oauth/token`, {
187
193
  method: "POST",
188
194
  headers: {
@@ -194,30 +200,27 @@ async function refreshToken(oauthHost, refreshTokenValue, signal) {
194
200
  grant_type: "refresh_token",
195
201
  refresh_token: refreshTokenValue,
196
202
  }),
197
- signal: requestSignal(signal),
203
+ signal,
198
204
  });
199
205
  }
200
206
  catch (error) {
201
- lastError = error instanceof Error ? error : new Error(String(error));
202
- continue;
207
+ const failure = error instanceof Error ? error : new Error(String(error));
208
+ // Presenting the same refresh token again is safe only if this attempt provably never reached the server.
209
+ if (attempt < REFRESH_MAX_RETRIES && !signal.aborted && requestNeverReachedServer(failure))
210
+ continue;
211
+ throw failure;
203
212
  }
204
- const json = await readJson(response);
205
213
  if (response.ok) {
206
- return parseTokenResponse(json, "refresh");
207
- }
208
- // Unauthorized: the stored credential is dead; Models clears it and prompts re-login.
209
- if (response.status === 401 || response.status === 403 || json?.error === "invalid_grant") {
210
- const description = typeof json?.error_description === "string" ? `: ${json.error_description}` : "";
211
- throw new Error(`Kimi Code token refresh unauthorized (status ${response.status})${description}`);
214
+ return parseTokenResponse(await readJson(response), "refresh");
212
215
  }
213
- if (isRetryableRefreshFailure(response) && attempt < REFRESH_MAX_RETRIES) {
214
- lastError = new Error(`Kimi Code token refresh failed with status ${response.status}`);
215
- continue;
216
+ // Any answer, a 429 or 5xx included, means the request was delivered and may have rotated the token, so it
217
+ // is never retried with the same one.
218
+ const rejection = { status: response.status, ...parseOAuthErrorBody(await response.text().catch(() => "")) };
219
+ if (isUnauthorizedRefresh(rejection)) {
220
+ throw new OAuthRefreshRejectedError(`Kimi Code token refresh unauthorized (status ${response.status})`, rejection, [refreshTokenValue]);
216
221
  }
217
- const text = JSON.stringify(json);
218
- throw new Error(`Kimi Code token refresh failed with status ${response.status}${text ? `: ${text}` : ""}`);
222
+ throw new Error(`Kimi Code token refresh failed with status ${response.status}${formatOAuthErrorDetail(rejection, [refreshTokenValue])}`);
219
223
  }
220
- throw lastError ?? new Error("Kimi Code token refresh failed");
221
224
  }
222
225
  async function loginKimiCoding(interaction) {
223
226
  const oauthHost = getOauthHost();
@@ -1 +1 @@
1
- {"version":3,"file":"kimi-coding.js","sourceRoot":"","sources":["../../../src/auth/oauth/kimi-coding.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Kimi Code (subscription) OAuth flow\n *\n * RFC 8628 device authorization grant against https://auth.kimi.com with JSON\n * responses. The access token authenticates requests to\n * https://api.kimi.com/coding/v1 as an `Authorization: Bearer` header.\n */\n\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport { sleep } from \"../../utils/sleep.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\n\nconst CLIENT_ID = \"17e5f671-d194-4dfb-9706-5516cb48c098\";\nconst DEFAULT_OAUTH_HOST = \"https://auth.kimi.com\";\nconst DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;\nconst DEFAULT_POLL_INTERVAL_SECONDS = 5;\nconst REQUEST_TIMEOUT_MS = 30 * 1000;\nconst REFRESH_MAX_RETRIES = 3;\n\ntype DeviceAuthorization = {\n\tdeviceCode: string;\n\tuserCode: string;\n\tverificationUri: string;\n\tverificationUriComplete: string;\n\tintervalSeconds: number;\n\texpiresInSeconds: number;\n};\n\ntype TokenResponse = {\n\taccess: string;\n\trefresh: string;\n\texpires: number;\n};\n\nfunction getOauthHost(): string {\n\tconst override = getProviderEnvValue(\"KIMI_CODE_OAUTH_HOST\") || getProviderEnvValue(\"KIMI_OAUTH_HOST\");\n\treturn (override || DEFAULT_OAUTH_HOST).replace(/\\/+$/, \"\");\n}\n\nfunction requestSignal(signal: AbortSignal): AbortSignal {\n\treturn AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS), signal]);\n}\n\nfunction formUrlEncode(fields: Record<string, string>): string {\n\treturn new URLSearchParams(fields).toString();\n}\n\nasync function readJson(response: Response): Promise<Record<string, unknown> | null> {\n\ttry {\n\t\tconst json = await response.json();\n\t\treturn json && typeof json === \"object\" ? (json as Record<string, unknown>) : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** The verification URI is opened in the user's browser; only http(s) URLs are trusted. */\nfunction trustedHttpUrl(value: unknown): string | null {\n\tif (typeof value !== \"string\" || !value) return null;\n\ttry {\n\t\tconst url = new URL(value);\n\t\tif (url.protocol !== \"https:\" && url.protocol !== \"http:\") return null;\n\t\treturn url.href;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function startDeviceAuthorization(oauthHost: string, signal: AbortSignal): Promise<DeviceAuthorization> {\n\tconst response = await fetch(`${oauthHost}/api/oauth/device_authorization`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: formUrlEncode({ client_id: CLIENT_ID }),\n\t\tsignal: requestSignal(signal),\n\t});\n\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\tthrow new Error(`Kimi Code device authorization failed with status ${response.status}${text ? `: ${text}` : \"\"}`);\n\t}\n\n\tconst json = await readJson(response);\n\tconst deviceCode = json?.device_code;\n\tconst userCode = json?.user_code;\n\tconst verificationUri = json?.verification_uri;\n\tconst verificationUriComplete = json?.verification_uri_complete;\n\tif (\n\t\ttypeof deviceCode !== \"string\" ||\n\t\ttypeof userCode !== \"string\" ||\n\t\ttypeof verificationUri !== \"string\" ||\n\t\ttypeof verificationUriComplete !== \"string\" ||\n\t\t!trustedHttpUrl(verificationUriComplete) ||\n\t\t!trustedHttpUrl(verificationUri)\n\t) {\n\t\tthrow new Error(`Invalid Kimi Code device authorization response: ${JSON.stringify(json)}`);\n\t}\n\n\tconst interval = json?.interval;\n\tconst expiresIn = json?.expires_in;\n\treturn {\n\t\tdeviceCode,\n\t\tuserCode,\n\t\tverificationUri,\n\t\tverificationUriComplete,\n\t\tintervalSeconds:\n\t\t\ttypeof interval === \"number\" && Number.isFinite(interval) && interval > 0\n\t\t\t\t? interval\n\t\t\t\t: DEFAULT_POLL_INTERVAL_SECONDS,\n\t\texpiresInSeconds:\n\t\t\ttypeof expiresIn === \"number\" && Number.isFinite(expiresIn) && expiresIn > 0\n\t\t\t\t? expiresIn\n\t\t\t\t: DEVICE_CODE_TIMEOUT_SECONDS,\n\t};\n}\n\nfunction parseTokenResponse(json: Record<string, unknown> | null, operation: string): TokenResponse {\n\tconst accessToken = json?.access_token;\n\tconst refreshToken = json?.refresh_token;\n\tconst expiresIn = json?.expires_in;\n\tif (\n\t\ttypeof accessToken !== \"string\" ||\n\t\t!accessToken ||\n\t\ttypeof refreshToken !== \"string\" ||\n\t\t!refreshToken ||\n\t\ttypeof expiresIn !== \"number\" ||\n\t\t!Number.isFinite(expiresIn) ||\n\t\texpiresIn <= 0\n\t) {\n\t\tthrow new Error(`Kimi Code token ${operation} response missing fields: ${JSON.stringify(json)}`);\n\t}\n\treturn {\n\t\taccess: accessToken,\n\t\trefresh: refreshToken,\n\t\texpires: Date.now() + expiresIn * 1000,\n\t};\n}\n\nasync function pollForToken(\n\toauthHost: string,\n\tdevice: DeviceAuthorization,\n\tsignal: AbortSignal,\n): Promise<TokenResponse> {\n\treturn pollOAuthDeviceCodeFlow<TokenResponse>({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: device.expiresInSeconds,\n\t\twaitBeforeFirstPoll: true,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst response = await fetch(`${oauthHost}/api/oauth/token`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: formUrlEncode({\n\t\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\t\tdevice_code: device.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: requestSignal(signal),\n\t\t\t});\n\n\t\t\tif (response.status >= 500) {\n\t\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\tmessage: `Kimi Code device token request failed with status ${response.status}${text ? `: ${text}` : \"\"}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst json = await readJson(response);\n\t\t\tif (response.ok && typeof json?.access_token === \"string\") {\n\t\t\t\ttry {\n\t\t\t\t\treturn { status: \"complete\", value: parseTokenResponse(json, \"poll\") };\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { status: \"failed\", message: error instanceof Error ? error.message : String(error) };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst error = json?.error;\n\t\t\tconst description = typeof json?.error_description === \"string\" ? `: ${json.error_description}` : \"\";\n\t\t\tif (error === \"authorization_pending\") {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\t\t\tif (error === \"slow_down\") {\n\t\t\t\tconst interval = json?.interval;\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"slow_down\",\n\t\t\t\t\tintervalSeconds: typeof interval === \"number\" && interval > 0 ? interval : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (error === \"expired_token\") {\n\t\t\t\treturn { status: \"failed\", message: \"Kimi Code device authorization expired. Please restart login.\" };\n\t\t\t}\n\t\t\tif (error === \"access_denied\") {\n\t\t\t\treturn { status: \"failed\", message: \"Kimi Code login was denied.\" };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `Kimi Code device token request failed (status ${response.status})${typeof error === \"string\" ? `: ${error}${description}` : \"\"}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction isRetryableRefreshFailure(response: Response): boolean {\n\treturn response.status === 429 || response.status >= 500;\n}\n\nasync function refreshToken(oauthHost: string, refreshTokenValue: string, signal: AbortSignal): Promise<TokenResponse> {\n\tlet lastError: Error | undefined;\n\tfor (let attempt = 0; attempt <= REFRESH_MAX_RETRIES; attempt++) {\n\t\tif (attempt > 0) {\n\t\t\tawait sleep(1000 * 2 ** (attempt - 1), signal);\n\t\t}\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Kimi Code token refresh aborted\");\n\t\t}\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await fetch(`${oauthHost}/api/oauth/token`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: formUrlEncode({\n\t\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\t\trefresh_token: refreshTokenValue,\n\t\t\t\t}),\n\t\t\t\tsignal: requestSignal(signal),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlastError = error instanceof Error ? error : new Error(String(error));\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst json = await readJson(response);\n\t\tif (response.ok) {\n\t\t\treturn parseTokenResponse(json, \"refresh\");\n\t\t}\n\n\t\t// Unauthorized: the stored credential is dead; Models clears it and prompts re-login.\n\t\tif (response.status === 401 || response.status === 403 || json?.error === \"invalid_grant\") {\n\t\t\tconst description = typeof json?.error_description === \"string\" ? `: ${json.error_description}` : \"\";\n\t\t\tthrow new Error(`Kimi Code token refresh unauthorized (status ${response.status})${description}`);\n\t\t}\n\n\t\tif (isRetryableRefreshFailure(response) && attempt < REFRESH_MAX_RETRIES) {\n\t\t\tlastError = new Error(`Kimi Code token refresh failed with status ${response.status}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst text = JSON.stringify(json);\n\t\tthrow new Error(`Kimi Code token refresh failed with status ${response.status}${text ? `: ${text}` : \"\"}`);\n\t}\n\n\tthrow lastError ?? new Error(\"Kimi Code token refresh failed\");\n}\n\nasync function loginKimiCoding(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst oauthHost = getOauthHost();\n\tconst device = await startDeviceAuthorization(oauthHost, interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.userCode,\n\t\tverificationUri: device.verificationUriComplete,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: device.expiresInSeconds,\n\t});\n\tconst token = await pollForToken(oauthHost, device, interaction.signal);\n\treturn { type: \"oauth\", access: token.access, refresh: token.refresh, expires: token.expires };\n}\n\nexport const kimiCodingOAuth: OAuthAuth = {\n\tname: \"Kimi Code (subscription)\",\n\tisSubscription: true,\n\tloginLabel: \"Sign in with Kimi Code\",\n\n\tlogin: loginKimiCoding,\n\n\trefresh: async (credential, signal) => {\n\t\tconst token = await refreshToken(getOauthHost(), credential.refresh, signal);\n\t\treturn { type: \"oauth\", access: token.access, refresh: token.refresh, expires: token.expires };\n\t},\n\n\tasync toAuth(credential) {\n\t\treturn { headers: { Authorization: `Bearer ${credential.access}` } };\n\t},\n};\n"]}
1
+ {"version":3,"file":"kimi-coding.js","sourceRoot":"","sources":["../../../src/auth/oauth/kimi-coding.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Kimi Code (subscription) OAuth flow\n *\n * RFC 8628 device authorization grant against https://auth.kimi.com with JSON\n * responses. The access token authenticates requests to\n * https://api.kimi.com/coding/v1 as an `Authorization: Bearer` header.\n */\n\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport { sleep } from \"../../utils/sleep.ts\";\nimport {\n\tformatOAuthErrorDetail,\n\tisPermanentRefreshRejection,\n\tOAuthRefreshRejectedError,\n\ttype OAuthRefreshRejection,\n\tparseOAuthErrorBody,\n\trequestNeverReachedServer,\n} from \"../oauth-refresh-errors.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\n\nconst CLIENT_ID = \"17e5f671-d194-4dfb-9706-5516cb48c098\";\nconst DEFAULT_OAUTH_HOST = \"https://auth.kimi.com\";\nconst DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;\nconst DEFAULT_POLL_INTERVAL_SECONDS = 5;\n/** Login requests (device authorization and polling) give up after this; a refresh request never does. */\nconst REQUEST_TIMEOUT_MS = 30 * 1000;\n/** Retries of a refresh request that never reached the server, after 1 s, 2 s, then 4 s. */\nconst REFRESH_MAX_RETRIES = 3;\nconst REFRESH_RETRY_BASE_DELAY_MS = 1000;\n\ntype DeviceAuthorization = {\n\tdeviceCode: string;\n\tuserCode: string;\n\tverificationUri: string;\n\tverificationUriComplete: string;\n\tintervalSeconds: number;\n\texpiresInSeconds: number;\n};\n\ntype TokenResponse = {\n\taccess: string;\n\trefresh: string;\n\texpires: number;\n};\n\nfunction getOauthHost(): string {\n\tconst override = getProviderEnvValue(\"KIMI_CODE_OAUTH_HOST\") || getProviderEnvValue(\"KIMI_OAUTH_HOST\");\n\treturn (override || DEFAULT_OAUTH_HOST).replace(/\\/+$/, \"\");\n}\n\nfunction requestSignal(signal: AbortSignal): AbortSignal {\n\treturn AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS), signal]);\n}\n\nfunction formUrlEncode(fields: Record<string, string>): string {\n\treturn new URLSearchParams(fields).toString();\n}\n\nasync function readJson(response: Response): Promise<Record<string, unknown> | null> {\n\ttry {\n\t\tconst json = await response.json();\n\t\treturn json && typeof json === \"object\" ? (json as Record<string, unknown>) : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** The verification URI is opened in the user's browser; only http(s) URLs are trusted. */\nfunction trustedHttpUrl(value: unknown): string | null {\n\tif (typeof value !== \"string\" || !value) return null;\n\ttry {\n\t\tconst url = new URL(value);\n\t\tif (url.protocol !== \"https:\" && url.protocol !== \"http:\") return null;\n\t\treturn url.href;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function startDeviceAuthorization(oauthHost: string, signal: AbortSignal): Promise<DeviceAuthorization> {\n\tconst response = await fetch(`${oauthHost}/api/oauth/device_authorization`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: formUrlEncode({ client_id: CLIENT_ID }),\n\t\tsignal: requestSignal(signal),\n\t});\n\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\tthrow new Error(`Kimi Code device authorization failed with status ${response.status}${text ? `: ${text}` : \"\"}`);\n\t}\n\n\tconst json = await readJson(response);\n\tconst deviceCode = json?.device_code;\n\tconst userCode = json?.user_code;\n\tconst verificationUri = json?.verification_uri;\n\tconst verificationUriComplete = json?.verification_uri_complete;\n\tif (\n\t\ttypeof deviceCode !== \"string\" ||\n\t\ttypeof userCode !== \"string\" ||\n\t\ttypeof verificationUri !== \"string\" ||\n\t\ttypeof verificationUriComplete !== \"string\" ||\n\t\t!trustedHttpUrl(verificationUriComplete) ||\n\t\t!trustedHttpUrl(verificationUri)\n\t) {\n\t\tthrow new Error(`Invalid Kimi Code device authorization response: ${JSON.stringify(json)}`);\n\t}\n\n\tconst interval = json?.interval;\n\tconst expiresIn = json?.expires_in;\n\treturn {\n\t\tdeviceCode,\n\t\tuserCode,\n\t\tverificationUri,\n\t\tverificationUriComplete,\n\t\tintervalSeconds:\n\t\t\ttypeof interval === \"number\" && Number.isFinite(interval) && interval > 0\n\t\t\t\t? interval\n\t\t\t\t: DEFAULT_POLL_INTERVAL_SECONDS,\n\t\texpiresInSeconds:\n\t\t\ttypeof expiresIn === \"number\" && Number.isFinite(expiresIn) && expiresIn > 0\n\t\t\t\t? expiresIn\n\t\t\t\t: DEVICE_CODE_TIMEOUT_SECONDS,\n\t};\n}\n\nfunction parseTokenResponse(json: Record<string, unknown> | null, operation: string): TokenResponse {\n\tconst accessToken = json?.access_token;\n\tconst refreshToken = json?.refresh_token;\n\tconst expiresIn = json?.expires_in;\n\tif (\n\t\ttypeof accessToken !== \"string\" ||\n\t\t!accessToken ||\n\t\ttypeof refreshToken !== \"string\" ||\n\t\t!refreshToken ||\n\t\ttypeof expiresIn !== \"number\" ||\n\t\t!Number.isFinite(expiresIn) ||\n\t\texpiresIn <= 0\n\t) {\n\t\tthrow new Error(`Kimi Code token ${operation} response missing fields: ${JSON.stringify(json)}`);\n\t}\n\treturn {\n\t\taccess: accessToken,\n\t\trefresh: refreshToken,\n\t\texpires: Date.now() + expiresIn * 1000,\n\t};\n}\n\nasync function pollForToken(\n\toauthHost: string,\n\tdevice: DeviceAuthorization,\n\tsignal: AbortSignal,\n): Promise<TokenResponse> {\n\treturn pollOAuthDeviceCodeFlow<TokenResponse>({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: device.expiresInSeconds,\n\t\twaitBeforeFirstPoll: true,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst response = await fetch(`${oauthHost}/api/oauth/token`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: formUrlEncode({\n\t\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\t\tdevice_code: device.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: requestSignal(signal),\n\t\t\t});\n\n\t\t\tif (response.status >= 500) {\n\t\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\tmessage: `Kimi Code device token request failed with status ${response.status}${text ? `: ${text}` : \"\"}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst json = await readJson(response);\n\t\t\tif (response.ok && typeof json?.access_token === \"string\") {\n\t\t\t\ttry {\n\t\t\t\t\treturn { status: \"complete\", value: parseTokenResponse(json, \"poll\") };\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { status: \"failed\", message: error instanceof Error ? error.message : String(error) };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst error = json?.error;\n\t\t\tconst description = typeof json?.error_description === \"string\" ? `: ${json.error_description}` : \"\";\n\t\t\tif (error === \"authorization_pending\") {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\t\t\tif (error === \"slow_down\") {\n\t\t\t\tconst interval = json?.interval;\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"slow_down\",\n\t\t\t\t\tintervalSeconds: typeof interval === \"number\" && interval > 0 ? interval : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (error === \"expired_token\") {\n\t\t\t\treturn { status: \"failed\", message: \"Kimi Code device authorization expired. Please restart login.\" };\n\t\t\t}\n\t\t\tif (error === \"access_denied\") {\n\t\t\t\treturn { status: \"failed\", message: \"Kimi Code login was denied.\" };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `Kimi Code device token request failed (status ${response.status})${typeof error === \"string\" ? `: ${error}${description}` : \"\"}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** Kimi treats 401 and 403 from its token endpoint as a dead login, along with the usual permanent codes. */\nfunction isUnauthorizedRefresh(rejection: OAuthRefreshRejection): boolean {\n\treturn rejection.status === 401 || rejection.status === 403 || isPermanentRefreshRejection(rejection);\n}\n\nasync function refreshToken(oauthHost: string, refreshTokenValue: string, signal: AbortSignal): Promise<TokenResponse> {\n\tfor (let attempt = 0; ; attempt++) {\n\t\tif (attempt > 0) {\n\t\t\tawait sleep(REFRESH_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1), signal);\n\t\t}\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Kimi Code token refresh aborted\");\n\t\t}\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\t// No timeout of its own: once sent, the server may already have rotated the refresh token, and only\n\t\t\t// this response carries the replacement.\n\t\t\tresponse = await fetch(`${oauthHost}/api/oauth/token`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: formUrlEncode({\n\t\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\t\trefresh_token: refreshTokenValue,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst failure = error instanceof Error ? error : new Error(String(error));\n\t\t\t// Presenting the same refresh token again is safe only if this attempt provably never reached the server.\n\t\t\tif (attempt < REFRESH_MAX_RETRIES && !signal.aborted && requestNeverReachedServer(failure)) continue;\n\t\t\tthrow failure;\n\t\t}\n\n\t\tif (response.ok) {\n\t\t\treturn parseTokenResponse(await readJson(response), \"refresh\");\n\t\t}\n\n\t\t// Any answer, a 429 or 5xx included, means the request was delivered and may have rotated the token, so it\n\t\t// is never retried with the same one.\n\t\tconst rejection = { status: response.status, ...parseOAuthErrorBody(await response.text().catch(() => \"\")) };\n\t\tif (isUnauthorizedRefresh(rejection)) {\n\t\t\tthrow new OAuthRefreshRejectedError(\n\t\t\t\t`Kimi Code token refresh unauthorized (status ${response.status})`,\n\t\t\t\trejection,\n\t\t\t\t[refreshTokenValue],\n\t\t\t);\n\t\t}\n\t\tthrow new Error(\n\t\t\t`Kimi Code token refresh failed with status ${response.status}${formatOAuthErrorDetail(rejection, [refreshTokenValue])}`,\n\t\t);\n\t}\n}\n\nasync function loginKimiCoding(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst oauthHost = getOauthHost();\n\tconst device = await startDeviceAuthorization(oauthHost, interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.userCode,\n\t\tverificationUri: device.verificationUriComplete,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: device.expiresInSeconds,\n\t});\n\tconst token = await pollForToken(oauthHost, device, interaction.signal);\n\treturn { type: \"oauth\", access: token.access, refresh: token.refresh, expires: token.expires };\n}\n\nexport const kimiCodingOAuth: OAuthAuth = {\n\tname: \"Kimi Code (subscription)\",\n\tisSubscription: true,\n\tloginLabel: \"Sign in with Kimi Code\",\n\n\tlogin: loginKimiCoding,\n\n\trefresh: async (credential, signal) => {\n\t\tconst token = await refreshToken(getOauthHost(), credential.refresh, signal);\n\t\treturn { type: \"oauth\", access: token.access, refresh: token.refresh, expires: token.expires };\n\t},\n\n\tasync toAuth(credential) {\n\t\treturn { headers: { Authorization: `Bearer ${credential.access}` } };\n\t},\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AAoLvF,eAAO,MAAM,SAAS,EAAE,SAYvB,CAAC"}
1
+ {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA0LvF,eAAO,MAAM,SAAS,EAAE,SAYvB,CAAC"}
@@ -12,6 +12,7 @@
12
12
  * refresh_token), so a 401/403 from mint means the session is dead and the
13
13
  * user must sign in again.
14
14
  */
15
+ import { OAuthRefreshRejectedError } from "../oauth-refresh-errors.js";
15
16
  import { pollOAuthDeviceCodeFlow } from "./device-code.js";
16
17
  // Muse Code CLI client id.
17
18
  const CLIENT_ID = "1031625952748946";
@@ -143,8 +144,12 @@ async function mintApiKey(identityToken, signal) {
143
144
  });
144
145
  const json = await readJson(response);
145
146
  if (response.status === 401 || response.status === 403) {
146
- // Identity token is not renewable (see file header); only a fresh device flow helps.
147
- throw new Error(`Meta session expired (status ${response.status}). Run \`/login meta\` to sign in again.${errorDetail(json)}`);
147
+ // Identity token is not renewable (see file header); only a fresh device flow helps, which the refresh
148
+ // caller tells the user to start with /login for the provider they use.
149
+ const description = ["error_description", "detail", "message"]
150
+ .map((key) => json?.[key])
151
+ .find((value) => typeof value === "string" && value.trim().length > 0);
152
+ throw new OAuthRefreshRejectedError(`Meta session expired (status ${response.status})`, { status: response.status, code: typeof json?.error === "string" ? json.error : undefined, description }, [identityToken]);
148
153
  }
149
154
  if (!response.ok) {
150
155
  throw new Error(`Meta API key mint failed with status ${response.status}${errorDetail(json)}`);