@bastani/pi-ai 0.9.21-alpha.1 → 0.9.21

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 (336) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/NOTICE.md +1 -1
  3. package/dist/api/anthropic-messages.d.ts.map +1 -1
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/anthropic-messages.lazy.d.ts.map +1 -1
  6. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  7. package/dist/api/azure-openai-responses.js +2 -4
  8. package/dist/api/azure-openai-responses.js.map +1 -1
  9. package/dist/api/azure-openai-responses.lazy.d.ts.map +1 -1
  10. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  11. package/dist/api/bedrock-converse-stream.js.map +1 -1
  12. package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -1
  13. package/dist/api/bedrock-converse-stream.lazy.js.map +1 -1
  14. package/dist/api/cloudflare-gateway-binding.d.ts.map +1 -1
  15. package/dist/api/cloudflare-gateway-binding.js.map +1 -1
  16. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  17. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  18. package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts.map +1 -1
  19. package/dist/api/cloudflare.d.ts.map +1 -1
  20. package/dist/api/cloudflare.js.map +1 -1
  21. package/dist/api/constrained-sampling.d.ts.map +1 -1
  22. package/dist/api/constrained-sampling.js.map +1 -1
  23. package/dist/api/github-copilot-headers.d.ts.map +1 -1
  24. package/dist/api/github-copilot-headers.js.map +1 -1
  25. package/dist/api/google-generative-ai.d.ts.map +1 -1
  26. package/dist/api/google-generative-ai.js.map +1 -1
  27. package/dist/api/google-generative-ai.lazy.d.ts.map +1 -1
  28. package/dist/api/google-shared.d.ts.map +1 -1
  29. package/dist/api/google-shared.js.map +1 -1
  30. package/dist/api/google-vertex.d.ts.map +1 -1
  31. package/dist/api/google-vertex.js.map +1 -1
  32. package/dist/api/google-vertex.lazy.d.ts.map +1 -1
  33. package/dist/api/lazy.d.ts.map +1 -1
  34. package/dist/api/lazy.js.map +1 -1
  35. package/dist/api/mistral-conversations.d.ts.map +1 -1
  36. package/dist/api/mistral-conversations.js +6 -0
  37. package/dist/api/mistral-conversations.js.map +1 -1
  38. package/dist/api/mistral-conversations.lazy.d.ts.map +1 -1
  39. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  40. package/dist/api/openai-codex-responses.js.map +1 -1
  41. package/dist/api/openai-codex-responses.lazy.d.ts.map +1 -1
  42. package/dist/api/openai-completions.d.ts.map +1 -1
  43. package/dist/api/openai-completions.js +5 -6
  44. package/dist/api/openai-completions.js.map +1 -1
  45. package/dist/api/openai-completions.lazy.d.ts.map +1 -1
  46. package/dist/api/openai-prompt-cache.d.ts.map +1 -1
  47. package/dist/api/openai-prompt-cache.js.map +1 -1
  48. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  49. package/dist/api/openai-responses-shared.js.map +1 -1
  50. package/dist/api/openai-responses.d.ts.map +1 -1
  51. package/dist/api/openai-responses.js +3 -4
  52. package/dist/api/openai-responses.js.map +1 -1
  53. package/dist/api/openai-responses.lazy.d.ts.map +1 -1
  54. package/dist/api/openrouter-images.d.ts.map +1 -1
  55. package/dist/api/openrouter-images.js.map +1 -1
  56. package/dist/api/openrouter-images.lazy.d.ts.map +1 -1
  57. package/dist/api/pi-messages.d.ts.map +1 -1
  58. package/dist/api/pi-messages.js.map +1 -1
  59. package/dist/api/pi-messages.lazy.d.ts.map +1 -1
  60. package/dist/api/simple-options.d.ts.map +1 -1
  61. package/dist/api/simple-options.js +1 -4
  62. package/dist/api/simple-options.js.map +1 -1
  63. package/dist/api/system-one-shared.d.ts.map +1 -1
  64. package/dist/api/system-one-shared.js.map +1 -1
  65. package/dist/api/transform-messages.d.ts.map +1 -1
  66. package/dist/api/transform-messages.js.map +1 -1
  67. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  68. package/dist/api/typesafe-system-one.js.map +1 -1
  69. package/dist/api/typesafe-system-one.lazy.d.ts.map +1 -1
  70. package/dist/auth/context.d.ts.map +1 -1
  71. package/dist/auth/context.js.map +1 -1
  72. package/dist/auth/credential-store.d.ts.map +1 -1
  73. package/dist/auth/credential-store.js.map +1 -1
  74. package/dist/auth/helpers.d.ts.map +1 -1
  75. package/dist/auth/helpers.js.map +1 -1
  76. package/dist/auth/oauth/anthropic.d.ts.map +1 -1
  77. package/dist/auth/oauth/anthropic.js.map +1 -1
  78. package/dist/auth/oauth/device-code.d.ts.map +1 -1
  79. package/dist/auth/oauth/device-code.js.map +1 -1
  80. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  81. package/dist/auth/oauth/github-copilot.js.map +1 -1
  82. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  83. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  84. package/dist/auth/oauth/load.d.ts.map +1 -1
  85. package/dist/auth/oauth/load.js.map +1 -1
  86. package/dist/auth/oauth/meta.d.ts.map +1 -1
  87. package/dist/auth/oauth/meta.js.map +1 -1
  88. package/dist/auth/oauth/oauth-page.d.ts.map +1 -1
  89. package/dist/auth/oauth/oauth-page.js.map +1 -1
  90. package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  91. package/dist/auth/oauth/openai-codex.js.map +1 -1
  92. package/dist/auth/oauth/openrouter.d.ts.map +1 -1
  93. package/dist/auth/oauth/openrouter.js.map +1 -1
  94. package/dist/auth/oauth/pkce.d.ts.map +1 -1
  95. package/dist/auth/oauth/pkce.js.map +1 -1
  96. package/dist/auth/oauth/radius.d.ts.map +1 -1
  97. package/dist/auth/oauth/radius.js.map +1 -1
  98. package/dist/auth/oauth/xai.d.ts.map +1 -1
  99. package/dist/auth/oauth/xai.js.map +1 -1
  100. package/dist/auth/resolve.d.ts.map +1 -1
  101. package/dist/auth/resolve.js.map +1 -1
  102. package/dist/auth/types.d.ts.map +1 -1
  103. package/dist/bedrock-provider.d.ts.map +1 -1
  104. package/dist/bun-oauth.d.ts.map +1 -1
  105. package/dist/bun-oauth.js.map +1 -1
  106. package/dist/cli.d.ts.map +1 -1
  107. package/dist/cli.js.map +1 -1
  108. package/dist/compat/extension-oauth-types.d.ts.map +1 -1
  109. package/dist/compat.d.ts.map +1 -1
  110. package/dist/compat.js.map +1 -1
  111. package/dist/decision-models.d.ts.map +1 -1
  112. package/dist/decision-models.generated.d.ts.map +1 -1
  113. package/dist/decision-models.js.map +1 -1
  114. package/dist/env-api-keys.d.ts.map +1 -1
  115. package/dist/env-api-keys.js.map +1 -1
  116. package/dist/image-models.d.ts.map +1 -1
  117. package/dist/image-models.js.map +1 -1
  118. package/dist/images-api-registry.d.ts.map +1 -1
  119. package/dist/images-api-registry.js.map +1 -1
  120. package/dist/images.d.ts.map +1 -1
  121. package/dist/images.js.map +1 -1
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/legacy-api-aliases.d.ts.map +1 -1
  124. package/dist/model-catalog.d.ts.map +1 -1
  125. package/dist/model-catalog.js.map +1 -1
  126. package/dist/models-store.d.ts.map +1 -1
  127. package/dist/models-store.js.map +1 -1
  128. package/dist/models.d.ts.map +1 -1
  129. package/dist/models.generated.d.ts.map +1 -1
  130. package/dist/models.js.map +1 -1
  131. package/dist/oauth.d.ts.map +1 -1
  132. package/dist/providers/all.d.ts.map +1 -1
  133. package/dist/providers/all.js.map +1 -1
  134. package/dist/providers/amazon-bedrock.d.ts.map +1 -1
  135. package/dist/providers/amazon-bedrock.js.map +1 -1
  136. package/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
  137. package/dist/providers/ant-ling.d.ts.map +1 -1
  138. package/dist/providers/ant-ling.js.map +1 -1
  139. package/dist/providers/ant-ling.models.d.ts.map +1 -1
  140. package/dist/providers/anthropic.d.ts.map +1 -1
  141. package/dist/providers/anthropic.js.map +1 -1
  142. package/dist/providers/anthropic.models.d.ts.map +1 -1
  143. package/dist/providers/azure-openai-responses.d.ts.map +1 -1
  144. package/dist/providers/azure-openai-responses.js.map +1 -1
  145. package/dist/providers/azure-openai-responses.models.d.ts.map +1 -1
  146. package/dist/providers/baseten.d.ts.map +1 -1
  147. package/dist/providers/baseten.js.map +1 -1
  148. package/dist/providers/baseten.models.d.ts.map +1 -1
  149. package/dist/providers/cerebras.d.ts.map +1 -1
  150. package/dist/providers/cerebras.js.map +1 -1
  151. package/dist/providers/cerebras.models.d.ts.map +1 -1
  152. package/dist/providers/cloudflare-ai-gateway.d.ts.map +1 -1
  153. package/dist/providers/cloudflare-ai-gateway.js.map +1 -1
  154. package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
  155. package/dist/providers/cloudflare-auth.d.ts.map +1 -1
  156. package/dist/providers/cloudflare-auth.js.map +1 -1
  157. package/dist/providers/cloudflare-stream.d.ts.map +1 -1
  158. package/dist/providers/cloudflare-stream.js.map +1 -1
  159. package/dist/providers/cloudflare-workers-ai.d.ts.map +1 -1
  160. package/dist/providers/cloudflare-workers-ai.js.map +1 -1
  161. package/dist/providers/cloudflare-workers-ai.models.d.ts.map +1 -1
  162. package/dist/providers/data/.manifest.json +1 -1
  163. package/dist/providers/data/fireworks.json +1 -1
  164. package/dist/providers/data/opencode-go.json +1 -1
  165. package/dist/providers/data/opencode.json +1 -1
  166. package/dist/providers/data/openrouter.json +1 -1
  167. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  168. package/dist/providers/deepseek.d.ts.map +1 -1
  169. package/dist/providers/deepseek.js.map +1 -1
  170. package/dist/providers/deepseek.models.d.ts.map +1 -1
  171. package/dist/providers/faux.d.ts +2 -2
  172. package/dist/providers/faux.d.ts.map +1 -1
  173. package/dist/providers/faux.js.map +1 -1
  174. package/dist/providers/fireworks.d.ts.map +1 -1
  175. package/dist/providers/fireworks.js.map +1 -1
  176. package/dist/providers/fireworks.models.d.ts.map +1 -1
  177. package/dist/providers/github-copilot-env.d.ts.map +1 -1
  178. package/dist/providers/github-copilot-env.js.map +1 -1
  179. package/dist/providers/github-copilot.d.ts.map +1 -1
  180. package/dist/providers/github-copilot.js.map +1 -1
  181. package/dist/providers/github-copilot.models.d.ts.map +1 -1
  182. package/dist/providers/google-vertex.d.ts.map +1 -1
  183. package/dist/providers/google-vertex.js.map +1 -1
  184. package/dist/providers/google-vertex.models.d.ts.map +1 -1
  185. package/dist/providers/google.d.ts.map +1 -1
  186. package/dist/providers/google.js.map +1 -1
  187. package/dist/providers/google.models.d.ts.map +1 -1
  188. package/dist/providers/groq.d.ts.map +1 -1
  189. package/dist/providers/groq.js.map +1 -1
  190. package/dist/providers/groq.models.d.ts.map +1 -1
  191. package/dist/providers/huggingface.d.ts.map +1 -1
  192. package/dist/providers/huggingface.js.map +1 -1
  193. package/dist/providers/huggingface.models.d.ts.map +1 -1
  194. package/dist/providers/images/register-builtins.d.ts.map +1 -1
  195. package/dist/providers/images/register-builtins.js.map +1 -1
  196. package/dist/providers/kimi-coding.d.ts.map +1 -1
  197. package/dist/providers/kimi-coding.js.map +1 -1
  198. package/dist/providers/kimi-coding.models.d.ts.map +1 -1
  199. package/dist/providers/meta.d.ts.map +1 -1
  200. package/dist/providers/meta.js.map +1 -1
  201. package/dist/providers/meta.models.d.ts.map +1 -1
  202. package/dist/providers/minimax-cn.d.ts.map +1 -1
  203. package/dist/providers/minimax-cn.js.map +1 -1
  204. package/dist/providers/minimax-cn.models.d.ts.map +1 -1
  205. package/dist/providers/minimax.d.ts.map +1 -1
  206. package/dist/providers/minimax.js.map +1 -1
  207. package/dist/providers/minimax.models.d.ts.map +1 -1
  208. package/dist/providers/mistral.d.ts.map +1 -1
  209. package/dist/providers/mistral.js.map +1 -1
  210. package/dist/providers/mistral.models.d.ts.map +1 -1
  211. package/dist/providers/moonshotai-cn.d.ts.map +1 -1
  212. package/dist/providers/moonshotai-cn.js.map +1 -1
  213. package/dist/providers/moonshotai-cn.models.d.ts.map +1 -1
  214. package/dist/providers/moonshotai.d.ts.map +1 -1
  215. package/dist/providers/moonshotai.js.map +1 -1
  216. package/dist/providers/moonshotai.models.d.ts.map +1 -1
  217. package/dist/providers/nvidia.d.ts.map +1 -1
  218. package/dist/providers/nvidia.js.map +1 -1
  219. package/dist/providers/nvidia.models.d.ts.map +1 -1
  220. package/dist/providers/openai-codex.d.ts.map +1 -1
  221. package/dist/providers/openai-codex.js.map +1 -1
  222. package/dist/providers/openai-codex.models.d.ts.map +1 -1
  223. package/dist/providers/openai.d.ts.map +1 -1
  224. package/dist/providers/openai.js.map +1 -1
  225. package/dist/providers/openai.models.d.ts.map +1 -1
  226. package/dist/providers/opencode-go.d.ts.map +1 -1
  227. package/dist/providers/opencode-go.js.map +1 -1
  228. package/dist/providers/opencode-go.models.d.ts.map +1 -1
  229. package/dist/providers/opencode.d.ts.map +1 -1
  230. package/dist/providers/opencode.js.map +1 -1
  231. package/dist/providers/opencode.models.d.ts.map +1 -1
  232. package/dist/providers/openrouter.d.ts.map +1 -1
  233. package/dist/providers/openrouter.js.map +1 -1
  234. package/dist/providers/openrouter.models.d.ts.map +1 -1
  235. package/dist/providers/qwen-token-plan-cn.d.ts.map +1 -1
  236. package/dist/providers/qwen-token-plan-cn.js.map +1 -1
  237. package/dist/providers/qwen-token-plan-cn.models.d.ts.map +1 -1
  238. package/dist/providers/qwen-token-plan-individual.d.ts.map +1 -1
  239. package/dist/providers/qwen-token-plan-individual.js.map +1 -1
  240. package/dist/providers/qwen-token-plan-individual.models.d.ts.map +1 -1
  241. package/dist/providers/qwen-token-plan.d.ts.map +1 -1
  242. package/dist/providers/qwen-token-plan.js.map +1 -1
  243. package/dist/providers/qwen-token-plan.models.d.ts.map +1 -1
  244. package/dist/providers/radius-config.d.ts.map +1 -1
  245. package/dist/providers/radius-config.js.map +1 -1
  246. package/dist/providers/radius.d.ts.map +1 -1
  247. package/dist/providers/radius.js.map +1 -1
  248. package/dist/providers/radius.models.d.ts.map +1 -1
  249. package/dist/providers/together.d.ts.map +1 -1
  250. package/dist/providers/together.js.map +1 -1
  251. package/dist/providers/together.models.d.ts.map +1 -1
  252. package/dist/providers/typesafe.d.ts.map +1 -1
  253. package/dist/providers/typesafe.js.map +1 -1
  254. package/dist/providers/typesafe.models.d.ts.map +1 -1
  255. package/dist/providers/vercel-ai-gateway.d.ts.map +1 -1
  256. package/dist/providers/vercel-ai-gateway.js.map +1 -1
  257. package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
  258. package/dist/providers/xai.d.ts.map +1 -1
  259. package/dist/providers/xai.js.map +1 -1
  260. package/dist/providers/xai.models.d.ts.map +1 -1
  261. package/dist/providers/xiaomi-token-plan-ams.d.ts.map +1 -1
  262. package/dist/providers/xiaomi-token-plan-ams.js.map +1 -1
  263. package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
  264. package/dist/providers/xiaomi-token-plan-cn.d.ts.map +1 -1
  265. package/dist/providers/xiaomi-token-plan-cn.js.map +1 -1
  266. package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
  267. package/dist/providers/xiaomi-token-plan-sgp.d.ts.map +1 -1
  268. package/dist/providers/xiaomi-token-plan-sgp.js.map +1 -1
  269. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
  270. package/dist/providers/xiaomi.d.ts.map +1 -1
  271. package/dist/providers/xiaomi.js.map +1 -1
  272. package/dist/providers/xiaomi.models.d.ts.map +1 -1
  273. package/dist/providers/zai-coding-cn.d.ts.map +1 -1
  274. package/dist/providers/zai-coding-cn.js.map +1 -1
  275. package/dist/providers/zai-coding-cn.models.d.ts.map +1 -1
  276. package/dist/providers/zai.d.ts.map +1 -1
  277. package/dist/providers/zai.js.map +1 -1
  278. package/dist/providers/zai.models.d.ts.map +1 -1
  279. package/dist/session-resources.d.ts.map +1 -1
  280. package/dist/session-resources.js.map +1 -1
  281. package/dist/types.d.ts.map +1 -1
  282. package/dist/utils/abort-signals.d.ts.map +1 -1
  283. package/dist/utils/abort-signals.js.map +1 -1
  284. package/dist/utils/abort.d.ts.map +1 -1
  285. package/dist/utils/abort.js.map +1 -1
  286. package/dist/utils/assistant-message-frame.d.ts.map +1 -1
  287. package/dist/utils/assistant-message-frame.js.map +1 -1
  288. package/dist/utils/diagnostics.d.ts.map +1 -1
  289. package/dist/utils/diagnostics.js.map +1 -1
  290. package/dist/utils/document-input.d.ts.map +1 -1
  291. package/dist/utils/document-input.js.map +1 -1
  292. package/dist/utils/error-body.d.ts.map +1 -1
  293. package/dist/utils/error-body.js.map +1 -1
  294. package/dist/utils/estimate.d.ts.map +1 -1
  295. package/dist/utils/estimate.js.map +1 -1
  296. package/dist/utils/event-stream.d.ts.map +1 -1
  297. package/dist/utils/event-stream.js.map +1 -1
  298. package/dist/utils/hash.d.ts.map +1 -1
  299. package/dist/utils/hash.js.map +1 -1
  300. package/dist/utils/headers.d.ts.map +1 -1
  301. package/dist/utils/headers.js.map +1 -1
  302. package/dist/utils/json-parse.d.ts.map +1 -1
  303. package/dist/utils/json-parse.js.map +1 -1
  304. package/dist/utils/model-operations.d.ts.map +1 -1
  305. package/dist/utils/model-operations.js.map +1 -1
  306. package/dist/utils/models-error.d.ts.map +1 -1
  307. package/dist/utils/models-error.js.map +1 -1
  308. package/dist/utils/node-http-proxy.d.ts.map +1 -1
  309. package/dist/utils/node-http-proxy.js.map +1 -1
  310. package/dist/utils/overflow.d.ts.map +1 -1
  311. package/dist/utils/overflow.js.map +1 -1
  312. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  313. package/dist/utils/pi-user-agent.js.map +1 -1
  314. package/dist/utils/provider-env.d.ts.map +1 -1
  315. package/dist/utils/provider-env.js.map +1 -1
  316. package/dist/utils/provider-retry.d.ts.map +1 -1
  317. package/dist/utils/provider-retry.js.map +1 -1
  318. package/dist/utils/retry.d.ts.map +1 -1
  319. package/dist/utils/retry.js.map +1 -1
  320. package/dist/utils/sanitize-unicode.d.ts.map +1 -1
  321. package/dist/utils/sanitize-unicode.js.map +1 -1
  322. package/dist/utils/sleep.d.ts.map +1 -1
  323. package/dist/utils/sleep.js.map +1 -1
  324. package/dist/utils/stream-deadline.d.ts.map +1 -1
  325. package/dist/utils/stream-deadline.js.map +1 -1
  326. package/dist/utils/text.d.ts.map +1 -1
  327. package/dist/utils/text.js.map +1 -1
  328. package/dist/utils/transcript.d.ts.map +1 -1
  329. package/dist/utils/transcript.js.map +1 -1
  330. package/dist/utils/typebox-helpers.d.ts.map +1 -1
  331. package/dist/utils/typebox-helpers.js.map +1 -1
  332. package/dist/utils/uuid.d.ts.map +1 -1
  333. package/dist/utils/uuid.js.map +1 -1
  334. package/dist/utils/validation.d.ts.map +1 -1
  335. package/dist/utils/validation.js.map +1 -1
  336. package/package.json +5 -6
@@ -1 +1 @@
1
- {"version":3,"file":"openai-codex.js","sourceRoot":"","sources":["../../../src/auth/oauth/openai-codex.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OpenAI Codex (ChatGPT OAuth) flow\n *\n * NOTE: This module uses Node.js crypto and http for the OAuth callback.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _randomBytes: typeof import(\"node:crypto\").randomBytes | null = null;\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:crypto\").then((m) => {\n\t\t_randomBytes = m.randomBytes;\n\t});\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\";\nconst AUTH_BASE_URL = \"https://auth.openai.com\";\nconst AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;\nconst TOKEN_URL = `${AUTH_BASE_URL}/oauth/token`;\nconst REDIRECT_URI = \"http://localhost:1455/auth/callback\";\nconst DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;\nconst DEVICE_TOKEN_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;\nconst DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;\nconst DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;\nconst DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;\nconst OPENAI_CODEX_BROWSER_LOGIN_METHOD = \"browser\";\nconst OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = \"device_code\";\nconst SCOPE = \"openid profile email offline_access\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\";\n\ntype OAuthToken = { access: string; refresh: string; expires: number };\ntype TokenOperation = \"exchange\" | \"refresh\";\n\nfunction getCallbackHost(): string {\n\treturn getProviderEnvValue(\"PI_OAUTH_CALLBACK_HOST\") || \"127.0.0.1\";\n}\n\ntype DeviceAuthInfo = {\n\tdeviceAuthId: string;\n\tuserCode: string;\n\tintervalSeconds: number;\n};\n\ntype DeviceTokenSuccess = {\n\tauthorizationCode: string;\n\tcodeVerifier: string;\n};\n\ntype JwtPayload = {\n\t[JWT_CLAIM_PATH]?: {\n\t\tchatgpt_account_id?: string;\n\t};\n\t[key: string]: unknown;\n};\n\nfunction createState(): string {\n\tif (!_randomBytes) {\n\t\tthrow new Error(\"OpenAI Codex OAuth is only available in Node.js environments\");\n\t}\n\treturn _randomBytes(16).toString(\"hex\");\n}\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 decodeJwt(token: string): JwtPayload | null {\n\ttry {\n\t\tconst parts = token.split(\".\");\n\t\tif (parts.length !== 3) return null;\n\t\tconst payload = parts[1] ?? \"\";\n\t\tconst decoded = atob(payload);\n\t\treturn JSON.parse(decoded) as JwtPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function fetchWithLoginCancellation(input: string, init: RequestInit): Promise<Response> {\n\ttry {\n\t\treturn await fetch(input, init);\n\t} catch (error) {\n\t\tif (init.signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nasync function readTokenResponse(response: Response, operation: TokenOperation): Promise<OAuthToken> {\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\tthrow new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\taccess_token?: string;\n\t\trefresh_token?: string;\n\t\texpires_in?: number;\n\t} | null;\n\tif (!json?.access_token || !json.refresh_token || typeof json.expires_in !== \"number\") {\n\t\tthrow new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\n\t\taccess: json.access_token,\n\t\trefresh: json.refresh_token,\n\t\texpires: Date.now() + json.expires_in * 1000,\n\t};\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal: AbortSignal,\n): Promise<OAuthToken> {\n\tconst response = await fetchWithLoginCancellation(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tcode,\n\t\t\tcode_verifier: verifier,\n\t\t\tredirect_uri: redirectUri,\n\t\t}),\n\t\tsignal,\n\t});\n\n\treturn readTokenResponse(response, \"exchange\");\n}\n\nasync function refreshAccessToken(refreshToken: string, signal: AbortSignal): Promise<OAuthToken> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody: new URLSearchParams({\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t}),\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tthrow new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\n\treturn readTokenResponse(response, \"refresh\");\n}\n\nasync function startOpenAICodexDeviceAuth(signal: AbortSignal): Promise<DeviceAuthInfo> {\n\tconst response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ client_id: CLIENT_ID }),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(\n\t\t\t\t\"OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.\",\n\t\t\t);\n\t\t}\n\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\tthrow new Error(\n\t\t\t`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\tdevice_auth_id?: string;\n\t\tuser_code?: string;\n\t\tinterval?: number | string;\n\t} | null;\n\tconst intervalSeconds = typeof json?.interval === \"string\" ? Number(json.interval.trim()) : json?.interval;\n\tif (\n\t\t!json?.device_auth_id ||\n\t\t!json.user_code ||\n\t\ttypeof intervalSeconds !== \"number\" ||\n\t\t!Number.isFinite(intervalSeconds) ||\n\t\tintervalSeconds < 0\n\t) {\n\t\tthrow new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\n\t\tdeviceAuthId: json.device_auth_id,\n\t\tuserCode: json.user_code,\n\t\tintervalSeconds,\n\t};\n}\n\nasync function pollOpenAICodexDeviceAuth(device: DeviceAuthInfo, signal: AbortSignal): Promise<DeviceTokenSuccess> {\n\treturn pollOAuthDeviceCodeFlow<DeviceTokenSuccess>({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tdevice_auth_id: device.deviceAuthId,\n\t\t\t\t\tuser_code: device.userCode,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst rawJson = await response.json();\n\t\t\t\tconst json = rawJson as { authorization_code?: string; code_verifier?: string } | null;\n\t\t\t\tif (!json?.authorization_code || !json.code_verifier) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tmessage: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tvalue: { authorizationCode: json.authorization_code, codeVerifier: json.code_verifier },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (response.status === 403 || response.status === 404) {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\n\t\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\t\tlet errorCode: unknown;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(responseBody) as { error?: string | { code?: string } } | null;\n\t\t\t\tconst error = json?.error;\n\t\t\t\terrorCode = typeof error === \"object\" ? error?.code : error;\n\t\t\t} catch {}\n\n\t\t\tif (errorCode === \"deviceauth_authorization_pending\") {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\t\t\tif (errorCode === \"slow_down\") {\n\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\nasync function createAuthorizationFlow(\n\toriginator: string = \"pi\",\n): Promise<{ verifier: string; state: string; url: string }> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = createState();\n\n\tconst url = new URL(AUTHORIZE_URL);\n\turl.searchParams.set(\"response_type\", \"code\");\n\turl.searchParams.set(\"client_id\", CLIENT_ID);\n\turl.searchParams.set(\"redirect_uri\", REDIRECT_URI);\n\turl.searchParams.set(\"scope\", SCOPE);\n\turl.searchParams.set(\"code_challenge\", challenge);\n\turl.searchParams.set(\"code_challenge_method\", \"S256\");\n\turl.searchParams.set(\"state\", state);\n\turl.searchParams.set(\"id_token_add_organizations\", \"true\");\n\turl.searchParams.set(\"codex_cli_simplified_flow\", \"true\");\n\turl.searchParams.set(\"originator\", originator);\n\n\treturn { verifier, state, url: url.toString() };\n}\n\ntype OAuthServerInfo = {\n\tclose: () => void;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string } | null>;\n};\n\nfunction startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {\n\tif (!_http) {\n\t\tthrow new Error(\"OpenAI Codex OAuth is only available in Node.js environments\");\n\t}\n\n\tlet settleWait: ((value: { code: string } | null) => void) | undefined;\n\tconst waitForCodePromise = new Promise<{ code: string } | null>((resolve) => {\n\t\tlet settled = false;\n\t\tsettleWait = (value) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tresolve(value);\n\t\t};\n\t});\n\n\tconst server = _http.createServer((req, res) => {\n\t\ttry {\n\t\t\tconst url = new URL(req.url || \"\", \"http://localhost\");\n\t\t\tif (url.pathname !== \"/auth/callback\") {\n\t\t\t\tres.statusCode = 404;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"Callback route not found.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (url.searchParams.get(\"state\") !== state) {\n\t\t\t\tres.statusCode = 400;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"State mismatch.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst code = url.searchParams.get(\"code\");\n\t\t\tif (!code) {\n\t\t\t\tres.statusCode = 400;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"Missing authorization code.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tres.statusCode = 200;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthSuccessHtml(\"OpenAI authentication completed. You can close this window.\"));\n\t\t\tsettleWait?.({ code });\n\t\t} catch {\n\t\t\tres.statusCode = 500;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthErrorHtml(\"Internal error while processing OAuth callback.\"));\n\t\t}\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(1455, getCallbackHost(), () => {\n\t\t\t\tresolve({\n\t\t\t\t\tclose: () => server.close(),\n\t\t\t\t\tcancelWait: () => {\n\t\t\t\t\t\tsettleWait?.(null);\n\t\t\t\t\t},\n\t\t\t\t\twaitForCode: () => waitForCodePromise,\n\t\t\t\t});\n\t\t\t})\n\t\t\t.on(\"error\", (_err: NodeJS.ErrnoException) => {\n\t\t\t\tsettleWait?.(null);\n\t\t\t\tresolve({\n\t\t\t\t\tclose: () => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tserver.close();\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// ignore\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tcancelWait: () => {},\n\t\t\t\t\twaitForCode: async () => null,\n\t\t\t\t});\n\t\t\t});\n\t});\n}\n\nfunction getAccountId(accessToken: string): string | null {\n\tconst payload = decodeJwt(accessToken);\n\tconst auth = payload?.[JWT_CLAIM_PATH];\n\tconst accountId = auth?.chatgpt_account_id;\n\treturn typeof accountId === \"string\" && accountId.length > 0 ? accountId : null;\n}\n\nfunction credentialsFromToken(token: OAuthToken): OAuthCredential {\n\tconst accountId = getAccountId(token.access);\n\tif (!accountId) {\n\t\tthrow new Error(\"Failed to extract accountId from token\");\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: token.access,\n\t\trefresh: token.refresh,\n\t\texpires: token.expires,\n\t\taccountId,\n\t};\n}\n\nasync function exchangeAuthorizationCodeForCredentials(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\treturn credentialsFromToken(await exchangeAuthorizationCode(code, verifier, redirectUri, signal));\n}\n\nasync function loginOpenAICodexDeviceCode(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst device = await startOpenAICodexDeviceAuth(interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.userCode,\n\t\tverificationUri: DEVICE_VERIFICATION_URI,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t});\n\tconst code = await pollOpenAICodexDeviceAuth(device, interaction.signal);\n\treturn exchangeAuthorizationCodeForCredentials(\n\t\tcode.authorizationCode,\n\t\tcode.codeVerifier,\n\t\tDEVICE_REDIRECT_URI,\n\t\tinteraction.signal,\n\t);\n}\n\nasync function loginOpenAICodex(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, state, url } = await createAuthorizationFlow();\n\tconst server = await startLocalOAuthServer(state);\n\tconst manualAbort = new AbortController();\n\tconst onAbort = () => server.cancelWait();\n\tinteraction.signal.addEventListener(\"abort\", onAbort, { once: true });\n\tif (interaction.signal.aborted) onAbort();\n\tlet code: string | undefined;\n\tlet manualCode: string | undefined;\n\tlet manualError: Error | undefined;\n\n\tinteraction.notify({\n\t\ttype: \"auth_url\",\n\t\turl,\n\t\tinstructions: \"A browser window should open. Complete login to finish.\",\n\t});\n\n\ttry {\n\t\tconst manualPromise = interaction\n\t\t\t.prompt({\n\t\t\t\ttype: \"manual_code\",\n\t\t\t\tmessage: \"Complete login in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\t\tplaceholder: REDIRECT_URI,\n\t\t\t\tsignal: manualAbort.signal,\n\t\t\t})\n\t\t\t.then((input) => {\n\t\t\t\tmanualCode = input;\n\t\t\t\tserver.cancelWait();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tserver.cancelWait();\n\t\t\t});\n\n\t\tconst result = await server.waitForCode();\n\t\tif (manualError) throw manualError;\n\t\tif (result?.code) {\n\t\t\tcode = result.code;\n\t\t} else if (manualCode) {\n\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\tif (parsed.state && parsed.state !== state) throw new Error(\"State mismatch\");\n\t\t\tcode = parsed.code;\n\t\t}\n\n\t\tif (!code) {\n\t\t\tawait manualPromise;\n\t\t\tif (manualError) throw manualError;\n\t\t\tif (manualCode) {\n\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\tif (parsed.state && parsed.state !== state) throw new Error(\"State mismatch\");\n\t\t\t\tcode = parsed.code;\n\t\t\t}\n\t\t}\n\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\treturn exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI, interaction.signal);\n\t} finally {\n\t\tinteraction.signal.removeEventListener(\"abort\", onAbort);\n\t\tmanualAbort.abort();\n\t\tserver.close();\n\t}\n}\n\n/**\n * Refresh OpenAI Codex OAuth token\n */\nasync function refreshOpenAICodexToken(refreshToken: string, signal: AbortSignal): Promise<OAuthCredential> {\n\treturn credentialsFromToken(await refreshAccessToken(refreshToken, signal));\n}\n\nexport const openaiCodexOAuth: OAuthAuth = {\n\tname: \"OpenAI (ChatGPT Plus/Pro)\",\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 OpenAI Codex login method:\",\n\t\t\toptions: [\n\t\t\t\t{ id: OPENAI_CODEX_BROWSER_LOGIN_METHOD, label: \"Browser login (default)\" },\n\t\t\t\t{ id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD, label: \"Device code login (headless)\" },\n\t\t\t],\n\t\t});\n\n\t\tif (method === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) {\n\t\t\treturn loginOpenAICodexDeviceCode(interaction);\n\t\t}\n\t\tif (method !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) {\n\t\t\tthrow new Error(`Unknown OpenAI Codex login method: ${method}`);\n\t\t}\n\n\t\treturn loginOpenAICodex(interaction);\n\t},\n\n\trefresh: (credential, signal) => refreshOpenAICodexToken(credential.refresh, signal),\n\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
1
+ {"version":3,"file":"openai-codex.js","sourceRoot":"","sources":["../../../src/auth/oauth/openai-codex.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,kEAAkE;AAClE,IAAI,YAAY,GAAoD,IAAI,CAAC;AACzE,IAAI,KAAK,GAAsC,IAAI,CAAC;AACpD,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC;IACzF,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QAChC,YAAY,GAAG,CAAC,CAAC,WAAW,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QAC9B,KAAK,GAAG,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,SAAS,GAAG,8BAA8B,CAAC;AACjD,MAAM,aAAa,GAAG,yBAAyB,CAAC;AAChD,MAAM,aAAa,GAAG,GAAG,aAAa,kBAAkB,CAAC;AACzD,MAAM,SAAS,GAAG,GAAG,aAAa,cAAc,CAAC;AACjD,MAAM,YAAY,GAAG,qCAAqC,CAAC;AAC3D,MAAM,oBAAoB,GAAG,GAAG,aAAa,mCAAmC,CAAC;AACjF,MAAM,gBAAgB,GAAG,GAAG,aAAa,gCAAgC,CAAC;AAC1E,MAAM,uBAAuB,GAAG,GAAG,aAAa,eAAe,CAAC;AAChE,MAAM,mBAAmB,GAAG,GAAG,aAAa,sBAAsB,CAAC;AACnE,MAAM,2BAA2B,GAAG,EAAE,GAAG,EAAE,CAAC;AAC5C,MAAM,iCAAiC,GAAG,SAAS,CAAC;AACpD,MAAM,qCAAqC,GAAG,aAAa,CAAC;AAC5D,MAAM,KAAK,GAAG,qCAAqC,CAAC;AACpD,MAAM,cAAc,GAAG,6BAA6B,CAAC;AAKrD,SAAS,eAAe;IACvB,OAAO,mBAAmB,CAAC,wBAAwB,CAAC,IAAI,WAAW,CAAC;AACrE,CAAC;AAoBD,SAAS,WAAW;IACnB,IAAI,CAAC,YAAY,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,YAAY,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,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,SAAS,CAAC,KAAa;IAC/B,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAe,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,KAAK,UAAU,0BAA0B,CAAC,KAAa,EAAE,IAAiB;IACzE,IAAI,CAAC;QACJ,OAAO,MAAM,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,QAAkB,EAAE,SAAyB;IAC7E,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,sBAAsB,SAAS,YAAY,QAAQ,CAAC,MAAM,MAAM,IAAI,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAChH,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,OAIL,CAAC;IACT,IAAI,CAAC,IAAI,EAAE,YAAY,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QACvF,MAAM,IAAI,KAAK,CAAC,sBAAsB,SAAS,6BAA6B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrG,CAAC;IAED,OAAO;QACN,MAAM,EAAE,IAAI,CAAC,YAAY;QACzB,OAAO,EAAE,IAAI,CAAC,aAAa;QAC3B,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI;KAC5C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,yBAAyB,CACvC,IAAY,EACZ,QAAgB,EAChB,WAAmB,EACnB,MAAmB;IAEnB,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC,SAAS,EAAE;QAC5D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,eAAe,CAAC;YACzB,UAAU,EAAE,oBAAoB;YAChC,SAAS,EAAE,SAAS;YACpB,IAAI;YACJ,aAAa,EAAE,QAAQ;YACvB,YAAY,EAAE,WAAW;SACzB,CAAC;QACF,MAAM;KACN,CAAC,CAAC;IAEH,OAAO,iBAAiB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,YAAoB,EAAE,MAAmB;IAC1E,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACJ,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;YACjC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;YAChE,IAAI,EAAE,IAAI,eAAe,CAAC;gBACzB,UAAU,EAAE,eAAe;gBAC3B,aAAa,EAAE,YAAY;gBAC3B,SAAS,EAAE,SAAS;aACpB,CAAC;YACF,MAAM;SACN,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChH,CAAC;IAED,OAAO,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC;AAED,KAAK,UAAU,0BAA0B,CAAC,MAAmB;IAC5D,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC,oBAAoB,EAAE;QACvE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;QAC9C,MAAM;KACN,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACd,4GAA4G,CAC5G,CAAC;QACH,CAAC;QACD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3D,MAAM,IAAI,KAAK,CACd,uDAAuD,QAAQ,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAClH,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,OAIL,CAAC;IACT,MAAM,eAAe,GAAG,OAAO,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC;IAC3G,IACC,CAAC,IAAI,EAAE,cAAc;QACrB,CAAC,IAAI,CAAC,SAAS;QACf,OAAO,eAAe,KAAK,QAAQ;QACnC,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;QACjC,eAAe,GAAG,CAAC,EAClB,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,8CAA8C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,OAAO;QACN,YAAY,EAAE,IAAI,CAAC,cAAc;QACjC,QAAQ,EAAE,IAAI,CAAC,SAAS;QACxB,eAAe;KACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,yBAAyB,CAAC,MAAsB,EAAE,MAAmB;IACnF,OAAO,uBAAuB,CAAqB;QAClD,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,gBAAgB,EAAE,2BAA2B;QAC7C,MAAM;QACN,IAAI,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC,gBAAgB,EAAE;gBACnE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACpB,cAAc,EAAE,MAAM,CAAC,YAAY;oBACnC,SAAS,EAAE,MAAM,CAAC,QAAQ;iBAC1B,CAAC;gBACF,MAAM;aACN,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACtC,MAAM,IAAI,GAAG,OAAyE,CAAC;gBACvF,IAAI,CAAC,IAAI,EAAE,kBAAkB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;oBACtD,OAAO;wBACN,MAAM,EAAE,QAAQ;wBAChB,OAAO,EAAE,oDAAoD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;qBACnF,CAAC;gBACH,CAAC;gBACD,OAAO;oBACN,MAAM,EAAE,UAAU;oBAClB,KAAK,EAAE,EAAE,iBAAiB,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE;iBACvF,CAAC;YACH,CAAC;YAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACxD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAC9B,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC3D,IAAI,SAAkB,CAAC;YACvB,IAAI,CAAC;gBACJ,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAkD,CAAC;gBACvF,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC;gBAC1B,SAAS,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;YAC7D,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YAEV,IAAI,SAAS,KAAK,kCAAkC,EAAE,CAAC;gBACtD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAC9B,CAAC;YACD,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;gBAC/B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;YAChC,CAAC;YAED,OAAO;gBACN,MAAM,EAAE,QAAQ;gBAChB,OAAO,EAAE,+CAA+C,QAAQ,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;aACnH,CAAC;QACH,CAAC;KACD,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,uBAAuB,CACrC,UAAU,GAAW,IAAI;IAEzB,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;IAE5B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;IACnC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IAC9C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC7C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IACnD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACrC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IAClD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;IACtD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACrC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,4BAA4B,EAAE,MAAM,CAAC,CAAC;IAC3D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,CAAC;IAC1D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAE/C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC;AACjD,CAAC;AAQD,SAAS,qBAAqB,CAAC,KAAa;IAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IACjF,CAAC;IAED,IAAI,UAAkE,CAAC;IACvE,MAAM,kBAAkB,GAAG,IAAI,OAAO,CAA0B,CAAC,OAAO,EAAE,EAAE;QAC3E,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,UAAU,GAAG,CAAC,KAAK,EAAE,EAAE;YACtB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9C,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,kBAAkB,CAAC,CAAC;YACvD,IAAI,GAAG,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;gBACvC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;gBAC1D,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBACrD,OAAO;YACR,CAAC;YACD,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC;gBAC7C,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;gBAC1D,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAC;gBAC3C,OAAO;YACR,CAAC;YACD,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACX,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;gBAC1D,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,6BAA6B,CAAC,CAAC,CAAC;gBACvD,OAAO;YACR,CAAC;YACD,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;YAC1D,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,6DAA6D,CAAC,CAAC,CAAC;YACzF,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACR,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;YAC1D,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,iDAAiD,CAAC,CAAC,CAAC;QAC5E,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM;aACJ,MAAM,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,GAAG,EAAE;YACrC,OAAO,CAAC;gBACP,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;gBAC3B,UAAU,EAAE,GAAG,EAAE;oBAChB,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC;gBACD,WAAW,EAAE,GAAG,EAAE,CAAC,kBAAkB;aACrC,CAAC,CAAC;QACJ,CAAC,CAAC;aACD,EAAE,CAAC,OAAO,EAAE,CAAC,IAA2B,EAAE,EAAE;YAC5C,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC;YACnB,OAAO,CAAC;gBACP,KAAK,EAAE,GAAG,EAAE;oBACX,IAAI,CAAC;wBACJ,MAAM,CAAC,KAAK,EAAE,CAAC;oBAChB,CAAC;oBAAC,MAAM,CAAC;wBACR,SAAS;oBACV,CAAC;gBACF,CAAC;gBACD,UAAU,EAAE,GAAG,EAAE,GAAE,CAAC;gBACpB,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;aAC7B,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,WAAmB;IACxC,MAAM,OAAO,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC,cAAc,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,EAAE,kBAAkB,CAAC;IAC3C,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACjF,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAiB;IAC9C,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO;QACN,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS;KACT,CAAC;AACH,CAAC;AAED,KAAK,UAAU,uCAAuC,CACrD,IAAY,EACZ,QAAgB,EAChB,WAAmB,EACnB,MAAmB;IAEnB,OAAO,oBAAoB,CAAC,MAAM,yBAAyB,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,KAAK,UAAU,0BAA0B,CAAC,WAAoC;IAC7E,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACpE,WAAW,CAAC,MAAM,CAAC;QAClB,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,eAAe,EAAE,uBAAuB;QACxC,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,gBAAgB,EAAE,2BAA2B;KAC7C,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACzE,OAAO,uCAAuC,CAC7C,IAAI,CAAC,iBAAiB,EACtB,IAAI,CAAC,YAAY,EACjB,mBAAmB,EACnB,WAAW,CAAC,MAAM,CAClB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,WAAoC;IACnE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,MAAM,uBAAuB,EAAE,CAAC;IACjE,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAC1C,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,IAAI,WAAW,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAC1C,IAAI,IAAwB,CAAC;IAC7B,IAAI,UAA8B,CAAC;IACnC,IAAI,WAA8B,CAAC;IAEnC,WAAW,CAAC,MAAM,CAAC;QAClB,IAAI,EAAE,UAAU;QAChB,GAAG;QACH,YAAY,EAAE,yDAAyD;KACvE,CAAC,CAAC;IAEH,IAAI,CAAC;QACJ,MAAM,aAAa,GAAG,WAAW;aAC/B,MAAM,CAAC;YACP,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,sFAAsF;YAC/F,WAAW,EAAE,YAAY;YACzB,MAAM,EAAE,WAAW,CAAC,MAAM;SAC1B,CAAC;aACD,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,UAAU,GAAG,KAAK,CAAC;YACnB,MAAM,CAAC,UAAU,EAAE,CAAC;QACrB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAChB,WAAW,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACxE,MAAM,CAAC,UAAU,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;QAEJ,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,WAAW;YAAE,MAAM,WAAW,CAAC;QACnC,IAAI,MAAM,EAAE,IAAI,EAAE,CAAC;YAClB,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACpB,CAAC;aAAM,IAAI,UAAU,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;YACnD,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YAC9E,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,MAAM,aAAa,CAAC;YACpB,IAAI,WAAW;gBAAE,MAAM,WAAW,CAAC;YACnC,IAAI,UAAU,EAAE,CAAC;gBAChB,MAAM,MAAM,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;gBACnD,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK;oBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;gBAC9E,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YACpB,CAAC;QACF,CAAC;QAED,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACzD,OAAO,uCAAuC,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClG,CAAC;YAAS,CAAC;QACV,WAAW,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzD,WAAW,CAAC,KAAK,EAAE,CAAC;QACpB,MAAM,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AACF,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,uBAAuB,CAAC,YAAoB,EAAE,MAAmB;IAC/E,OAAO,oBAAoB,CAAC,MAAM,kBAAkB,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAc;IAC1C,IAAI,EAAE,2BAA2B;IACjC,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,mCAAmC;YAC5C,OAAO,EAAE;gBACR,EAAE,EAAE,EAAE,iCAAiC,EAAE,KAAK,EAAE,yBAAyB,EAAE;gBAC3E,EAAE,EAAE,EAAE,qCAAqC,EAAE,KAAK,EAAE,8BAA8B,EAAE;aACpF;SACD,CAAC,CAAC;QAEH,IAAI,MAAM,KAAK,qCAAqC,EAAE,CAAC;YACtD,OAAO,0BAA0B,CAAC,WAAW,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,MAAM,KAAK,iCAAiC,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,uBAAuB,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC;IAEpF,KAAK,CAAC,MAAM,CAAC,UAAU;QACtB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;IACtC,CAAC;CACD,CAAC","sourcesContent":["/**\n * OpenAI Codex (ChatGPT OAuth) flow\n *\n * NOTE: This module uses Node.js crypto and http for the OAuth callback.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _randomBytes: typeof import(\"node:crypto\").randomBytes | null = null;\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:crypto\").then((m) => {\n\t\t_randomBytes = m.randomBytes;\n\t});\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\";\nconst AUTH_BASE_URL = \"https://auth.openai.com\";\nconst AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;\nconst TOKEN_URL = `${AUTH_BASE_URL}/oauth/token`;\nconst REDIRECT_URI = \"http://localhost:1455/auth/callback\";\nconst DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;\nconst DEVICE_TOKEN_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;\nconst DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;\nconst DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;\nconst DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;\nconst OPENAI_CODEX_BROWSER_LOGIN_METHOD = \"browser\";\nconst OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = \"device_code\";\nconst SCOPE = \"openid profile email offline_access\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\";\n\ntype OAuthToken = { access: string; refresh: string; expires: number };\ntype TokenOperation = \"exchange\" | \"refresh\";\n\nfunction getCallbackHost(): string {\n\treturn getProviderEnvValue(\"PI_OAUTH_CALLBACK_HOST\") || \"127.0.0.1\";\n}\n\ntype DeviceAuthInfo = {\n\tdeviceAuthId: string;\n\tuserCode: string;\n\tintervalSeconds: number;\n};\n\ntype DeviceTokenSuccess = {\n\tauthorizationCode: string;\n\tcodeVerifier: string;\n};\n\ntype JwtPayload = {\n\t[JWT_CLAIM_PATH]?: {\n\t\tchatgpt_account_id?: string;\n\t};\n\t[key: string]: unknown;\n};\n\nfunction createState(): string {\n\tif (!_randomBytes) {\n\t\tthrow new Error(\"OpenAI Codex OAuth is only available in Node.js environments\");\n\t}\n\treturn _randomBytes(16).toString(\"hex\");\n}\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 decodeJwt(token: string): JwtPayload | null {\n\ttry {\n\t\tconst parts = token.split(\".\");\n\t\tif (parts.length !== 3) return null;\n\t\tconst payload = parts[1] ?? \"\";\n\t\tconst decoded = atob(payload);\n\t\treturn JSON.parse(decoded) as JwtPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function fetchWithLoginCancellation(input: string, init: RequestInit): Promise<Response> {\n\ttry {\n\t\treturn await fetch(input, init);\n\t} catch (error) {\n\t\tif (init.signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nasync function readTokenResponse(response: Response, operation: TokenOperation): Promise<OAuthToken> {\n\tif (!response.ok) {\n\t\tconst text = await response.text().catch(() => \"\");\n\t\tthrow new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\taccess_token?: string;\n\t\trefresh_token?: string;\n\t\texpires_in?: number;\n\t} | null;\n\tif (!json?.access_token || !json.refresh_token || typeof json.expires_in !== \"number\") {\n\t\tthrow new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\n\t\taccess: json.access_token,\n\t\trefresh: json.refresh_token,\n\t\texpires: Date.now() + json.expires_in * 1000,\n\t};\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal: AbortSignal,\n): Promise<OAuthToken> {\n\tconst response = await fetchWithLoginCancellation(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tcode,\n\t\t\tcode_verifier: verifier,\n\t\t\tredirect_uri: redirectUri,\n\t\t}),\n\t\tsignal,\n\t});\n\n\treturn readTokenResponse(response, \"exchange\");\n}\n\nasync function refreshAccessToken(refreshToken: string, signal: AbortSignal): Promise<OAuthToken> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody: new URLSearchParams({\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t\tclient_id: CLIENT_ID,\n\t\t\t}),\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tthrow new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\n\treturn readTokenResponse(response, \"refresh\");\n}\n\nasync function startOpenAICodexDeviceAuth(signal: AbortSignal): Promise<DeviceAuthInfo> {\n\tconst response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ client_id: CLIENT_ID }),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(\n\t\t\t\t\"OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.\",\n\t\t\t);\n\t\t}\n\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\tthrow new Error(\n\t\t\t`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst rawJson = await response.json();\n\tconst json = rawJson as {\n\t\tdevice_auth_id?: string;\n\t\tuser_code?: string;\n\t\tinterval?: number | string;\n\t} | null;\n\tconst intervalSeconds = typeof json?.interval === \"string\" ? Number(json.interval.trim()) : json?.interval;\n\tif (\n\t\t!json?.device_auth_id ||\n\t\t!json.user_code ||\n\t\ttypeof intervalSeconds !== \"number\" ||\n\t\t!Number.isFinite(intervalSeconds) ||\n\t\tintervalSeconds < 0\n\t) {\n\t\tthrow new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);\n\t}\n\n\treturn {\n\t\tdeviceAuthId: json.device_auth_id,\n\t\tuserCode: json.user_code,\n\t\tintervalSeconds,\n\t};\n}\n\nasync function pollOpenAICodexDeviceAuth(device: DeviceAuthInfo, signal: AbortSignal): Promise<DeviceTokenSuccess> {\n\treturn pollOAuthDeviceCodeFlow<DeviceTokenSuccess>({\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t\tsignal,\n\t\tpoll: async () => {\n\t\t\tconst response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tdevice_auth_id: device.deviceAuthId,\n\t\t\t\t\tuser_code: device.userCode,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\tif (response.ok) {\n\t\t\t\tconst rawJson = await response.json();\n\t\t\t\tconst json = rawJson as { authorization_code?: string; code_verifier?: string } | null;\n\t\t\t\tif (!json?.authorization_code || !json.code_verifier) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tmessage: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tvalue: { authorizationCode: json.authorization_code, codeVerifier: json.code_verifier },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (response.status === 403 || response.status === 404) {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\n\t\t\tconst responseBody = await response.text().catch(() => \"\");\n\t\t\tlet errorCode: unknown;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(responseBody) as { error?: string | { code?: string } } | null;\n\t\t\t\tconst error = json?.error;\n\t\t\t\terrorCode = typeof error === \"object\" ? error?.code : error;\n\t\t\t} catch {}\n\n\t\t\tif (errorCode === \"deviceauth_authorization_pending\") {\n\t\t\t\treturn { status: \"pending\" };\n\t\t\t}\n\t\t\tif (errorCode === \"slow_down\") {\n\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tmessage: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : \"\"}`,\n\t\t\t};\n\t\t},\n\t});\n}\n\nasync function createAuthorizationFlow(\n\toriginator: string = \"pi\",\n): Promise<{ verifier: string; state: string; url: string }> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = createState();\n\n\tconst url = new URL(AUTHORIZE_URL);\n\turl.searchParams.set(\"response_type\", \"code\");\n\turl.searchParams.set(\"client_id\", CLIENT_ID);\n\turl.searchParams.set(\"redirect_uri\", REDIRECT_URI);\n\turl.searchParams.set(\"scope\", SCOPE);\n\turl.searchParams.set(\"code_challenge\", challenge);\n\turl.searchParams.set(\"code_challenge_method\", \"S256\");\n\turl.searchParams.set(\"state\", state);\n\turl.searchParams.set(\"id_token_add_organizations\", \"true\");\n\turl.searchParams.set(\"codex_cli_simplified_flow\", \"true\");\n\turl.searchParams.set(\"originator\", originator);\n\n\treturn { verifier, state, url: url.toString() };\n}\n\ntype OAuthServerInfo = {\n\tclose: () => void;\n\tcancelWait: () => void;\n\twaitForCode: () => Promise<{ code: string } | null>;\n};\n\nfunction startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {\n\tif (!_http) {\n\t\tthrow new Error(\"OpenAI Codex OAuth is only available in Node.js environments\");\n\t}\n\n\tlet settleWait: ((value: { code: string } | null) => void) | undefined;\n\tconst waitForCodePromise = new Promise<{ code: string } | null>((resolve) => {\n\t\tlet settled = false;\n\t\tsettleWait = (value) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tresolve(value);\n\t\t};\n\t});\n\n\tconst server = _http.createServer((req, res) => {\n\t\ttry {\n\t\t\tconst url = new URL(req.url || \"\", \"http://localhost\");\n\t\t\tif (url.pathname !== \"/auth/callback\") {\n\t\t\t\tres.statusCode = 404;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"Callback route not found.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (url.searchParams.get(\"state\") !== state) {\n\t\t\t\tres.statusCode = 400;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"State mismatch.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst code = url.searchParams.get(\"code\");\n\t\t\tif (!code) {\n\t\t\t\tres.statusCode = 400;\n\t\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\t\tres.end(oauthErrorHtml(\"Missing authorization code.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tres.statusCode = 200;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthSuccessHtml(\"OpenAI authentication completed. You can close this window.\"));\n\t\t\tsettleWait?.({ code });\n\t\t} catch {\n\t\t\tres.statusCode = 500;\n\t\t\tres.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n\t\t\tres.end(oauthErrorHtml(\"Internal error while processing OAuth callback.\"));\n\t\t}\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(1455, getCallbackHost(), () => {\n\t\t\t\tresolve({\n\t\t\t\t\tclose: () => server.close(),\n\t\t\t\t\tcancelWait: () => {\n\t\t\t\t\t\tsettleWait?.(null);\n\t\t\t\t\t},\n\t\t\t\t\twaitForCode: () => waitForCodePromise,\n\t\t\t\t});\n\t\t\t})\n\t\t\t.on(\"error\", (_err: NodeJS.ErrnoException) => {\n\t\t\t\tsettleWait?.(null);\n\t\t\t\tresolve({\n\t\t\t\t\tclose: () => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tserver.close();\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// ignore\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tcancelWait: () => {},\n\t\t\t\t\twaitForCode: async () => null,\n\t\t\t\t});\n\t\t\t});\n\t});\n}\n\nfunction getAccountId(accessToken: string): string | null {\n\tconst payload = decodeJwt(accessToken);\n\tconst auth = payload?.[JWT_CLAIM_PATH];\n\tconst accountId = auth?.chatgpt_account_id;\n\treturn typeof accountId === \"string\" && accountId.length > 0 ? accountId : null;\n}\n\nfunction credentialsFromToken(token: OAuthToken): OAuthCredential {\n\tconst accountId = getAccountId(token.access);\n\tif (!accountId) {\n\t\tthrow new Error(\"Failed to extract accountId from token\");\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: token.access,\n\t\trefresh: token.refresh,\n\t\texpires: token.expires,\n\t\taccountId,\n\t};\n}\n\nasync function exchangeAuthorizationCodeForCredentials(\n\tcode: string,\n\tverifier: string,\n\tredirectUri: string,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\treturn credentialsFromToken(await exchangeAuthorizationCode(code, verifier, redirectUri, signal));\n}\n\nasync function loginOpenAICodexDeviceCode(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst device = await startOpenAICodexDeviceAuth(interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.userCode,\n\t\tverificationUri: DEVICE_VERIFICATION_URI,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,\n\t});\n\tconst code = await pollOpenAICodexDeviceAuth(device, interaction.signal);\n\treturn exchangeAuthorizationCodeForCredentials(\n\t\tcode.authorizationCode,\n\t\tcode.codeVerifier,\n\t\tDEVICE_REDIRECT_URI,\n\t\tinteraction.signal,\n\t);\n}\n\nasync function loginOpenAICodex(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, state, url } = await createAuthorizationFlow();\n\tconst server = await startLocalOAuthServer(state);\n\tconst manualAbort = new AbortController();\n\tconst onAbort = () => server.cancelWait();\n\tinteraction.signal.addEventListener(\"abort\", onAbort, { once: true });\n\tif (interaction.signal.aborted) onAbort();\n\tlet code: string | undefined;\n\tlet manualCode: string | undefined;\n\tlet manualError: Error | undefined;\n\n\tinteraction.notify({\n\t\ttype: \"auth_url\",\n\t\turl,\n\t\tinstructions: \"A browser window should open. Complete login to finish.\",\n\t});\n\n\ttry {\n\t\tconst manualPromise = interaction\n\t\t\t.prompt({\n\t\t\t\ttype: \"manual_code\",\n\t\t\t\tmessage: \"Complete login in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\t\tplaceholder: REDIRECT_URI,\n\t\t\t\tsignal: manualAbort.signal,\n\t\t\t})\n\t\t\t.then((input) => {\n\t\t\t\tmanualCode = input;\n\t\t\t\tserver.cancelWait();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tserver.cancelWait();\n\t\t\t});\n\n\t\tconst result = await server.waitForCode();\n\t\tif (manualError) throw manualError;\n\t\tif (result?.code) {\n\t\t\tcode = result.code;\n\t\t} else if (manualCode) {\n\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\tif (parsed.state && parsed.state !== state) throw new Error(\"State mismatch\");\n\t\t\tcode = parsed.code;\n\t\t}\n\n\t\tif (!code) {\n\t\t\tawait manualPromise;\n\t\t\tif (manualError) throw manualError;\n\t\t\tif (manualCode) {\n\t\t\t\tconst parsed = parseAuthorizationInput(manualCode);\n\t\t\t\tif (parsed.state && parsed.state !== state) throw new Error(\"State mismatch\");\n\t\t\t\tcode = parsed.code;\n\t\t\t}\n\t\t}\n\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\treturn exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI, interaction.signal);\n\t} finally {\n\t\tinteraction.signal.removeEventListener(\"abort\", onAbort);\n\t\tmanualAbort.abort();\n\t\tserver.close();\n\t}\n}\n\n/**\n * Refresh OpenAI Codex OAuth token\n */\nasync function refreshOpenAICodexToken(refreshToken: string, signal: AbortSignal): Promise<OAuthCredential> {\n\treturn credentialsFromToken(await refreshAccessToken(refreshToken, signal));\n}\n\nexport const openaiCodexOAuth: OAuthAuth = {\n\tname: \"OpenAI (ChatGPT Plus/Pro)\",\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 OpenAI Codex login method:\",\n\t\t\toptions: [\n\t\t\t\t{ id: OPENAI_CODEX_BROWSER_LOGIN_METHOD, label: \"Browser login (default)\" },\n\t\t\t\t{ id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD, label: \"Device code login (headless)\" },\n\t\t\t],\n\t\t});\n\n\t\tif (method === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) {\n\t\t\treturn loginOpenAICodexDeviceCode(interaction);\n\t\t}\n\t\tif (method !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) {\n\t\t\tthrow new Error(`Unknown OpenAI Codex login method: ${method}`);\n\t\t}\n\n\t\treturn loginOpenAICodex(interaction);\n\t},\n\n\trefresh: (credential, signal) => refreshOpenAICodexToken(credential.refresh, signal),\n\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/openrouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA6RvF,eAAO,MAAM,eAAe,EAAE,SAU7B,CAAC","sourcesContent":["/**\n * OpenRouter OAuth PKCE flow.\n *\n * OpenRouter exchanges an authorization code for a permanent, user-controlled\n * API key rather than an expiring access/refresh token pair. The callback is\n * handled by a one-shot loopback server on an ephemeral port, raced against a\n * manual prompt so remote/headless sessions can paste the redirect URL when\n * the browser cannot reach the loopback server.\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 { createServer, type Server, type ServerResponse } from \"node:http\";\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst AUTHORIZE_URL = \"https://openrouter.ai/auth\";\nconst TOKEN_URL = \"https://openrouter.ai/api/v1/auth/keys\";\nconst LOGIN_TIMEOUT_MS = 5 * 60 * 1000;\nconst TOKEN_EXCHANGE_TIMEOUT_MS = 30_000;\n\nfunction getCallbackHost(): string {\n\treturn getProviderEnvValue(\"PI_OAUTH_CALLBACK_HOST\") || \"127.0.0.1\";\n}\n\ntype JsonObject = Record<string, unknown>;\n\ntype OpenRouterCallbackServer = {\n\tcallbackUrl: string;\n\t/** Stop listening and release timers without settling `waitForCredential`. */\n\tclose: () => void;\n\t/** Hand the login over to manual code entry unless a callback already claimed the exchange. */\n\tcancelWait: () => void;\n\t/**\n\t * Resolves with the credential once a browser callback completes the key\n\t * exchange, or with null once `cancelWait` hands the login over to manual\n\t * code entry. Rejects on timeout, cancellation, or a failed exchange.\n\t */\n\twaitForCredential: () => Promise<OAuthCredential | null>;\n};\n\nfunction sendHtml(response: ServerResponse, status: number, html: string): void {\n\tresponse.statusCode = status;\n\tresponse.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n\tresponse.setHeader(\"cache-control\", \"no-store\");\n\tresponse.end(html);\n}\n\nfunction parseAuthorizationInput(input: string): string | undefined {\n\tconst value = input.trim();\n\tif (!value) return undefined;\n\n\ttry {\n\t\treturn new URL(value).searchParams.get(\"code\") ?? undefined;\n\t} catch {\n\t\t// not a URL\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\treturn new URLSearchParams(value).get(\"code\") ?? undefined;\n\t}\n\n\treturn value;\n}\n\nfunction errorDetail(body: JsonObject): string | undefined {\n\tif (typeof body.error_description === \"string\") return body.error_description;\n\tif (typeof body.message === \"string\") return body.message;\n\tif (typeof body.error === \"string\") return body.error;\n\tif (body.error && typeof body.error === \"object\" && !Array.isArray(body.error)) {\n\t\tconst message = (body.error as JsonObject).message;\n\t\tif (typeof message === \"string\") return message;\n\t}\n\treturn undefined;\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\tconst controller = new AbortController();\n\tconst onAbort = () => controller.abort(signal.reason);\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\tconst timeout = setTimeout(\n\t\t() => controller.abort(new Error(\"OpenRouter OAuth token exchange timed out\")),\n\t\tTOKEN_EXCHANGE_TIMEOUT_MS,\n\t);\n\n\tlet response: Response;\n\tlet body: JsonObject = {};\n\ttry {\n\t\tresponse = await fetch(TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ code, code_verifier: verifier, code_challenge_method: \"S256\" }),\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\ttry {\n\t\t\tconst parsed = (await response.json()) as unknown;\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) body = parsed as JsonObject;\n\t\t} catch {\n\t\t\tif (response.ok) throw new Error(\"OpenRouter OAuth returned invalid JSON\");\n\t\t}\n\t} catch (error) {\n\t\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\t\tif (controller.signal.aborted) throw new Error(\"OpenRouter OAuth token exchange timed out\");\n\t\tthrow error;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t}\n\n\tif (!response.ok) {\n\t\tconst detail = errorDetail(body);\n\t\tthrow new Error(`OpenRouter OAuth key exchange failed (HTTP ${response.status})${detail ? `: ${detail}` : \"\"}`);\n\t}\n\n\tif (typeof body.key !== \"string\" || body.key.length === 0) {\n\t\tthrow new Error('OpenRouter OAuth response carries no \"key\"');\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: body.key,\n\t\trefresh: \"\",\n\t\texpires: Number.MAX_SAFE_INTEGER,\n\t};\n}\n\nasync function startCallbackServer(\n\tcallbackPath: string,\n\tverifier: string,\n\tsignal: AbortSignal,\n): Promise<OpenRouterCallbackServer> {\n\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\tconst callbackHost = getCallbackHost();\n\tlet resolveCredential: (credential: OAuthCredential | null) => void = () => {};\n\tlet rejectCredential: (error: Error) => void = () => {};\n\tconst credential = new Promise<OAuthCredential | null>((resolve, reject) => {\n\t\tresolveCredential = resolve;\n\t\trejectCredential = reject;\n\t});\n\n\tlet server: Server;\n\tlet claimed = false;\n\tlet settled = false;\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\tlet onAbort: (() => void) | undefined;\n\n\tconst close = (): void => {\n\t\tif (timeout) clearTimeout(timeout);\n\t\tif (onAbort) signal.removeEventListener(\"abort\", onAbort);\n\t\tserver.close();\n\t};\n\n\tconst finish = (result: { credential: OAuthCredential | null } | { error: Error }): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tclose();\n\t\tif (\"credential\" in result) resolveCredential(result.credential);\n\t\telse rejectCredential(result.error);\n\t};\n\n\tserver = createServer((request, response) => {\n\t\tvoid (async () => {\n\t\t\tconst requestUrl = new URL(request.url ?? \"/\", `http://${callbackHost}`);\n\t\t\tif (request.method !== \"GET\" || requestUrl.pathname !== callbackPath) {\n\t\t\t\tsendHtml(response, 404, oauthErrorHtml(\"OAuth callback route not found.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (claimed || settled) {\n\t\t\t\tsendHtml(response, 409, oauthErrorHtml(\"This OAuth callback has already been used.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst oauthError = requestUrl.searchParams.get(\"error\");\n\t\t\tif (oauthError) {\n\t\t\t\tconst description = requestUrl.searchParams.get(\"error_description\") ?? oauthError;\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"OpenRouter authorization was denied.\", description));\n\t\t\t\tfinish({ error: new Error(`OpenRouter authorization failed: ${description}`) });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst code = requestUrl.searchParams.get(\"code\");\n\t\t\tif (!code) {\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"OpenRouter returned no authorization code.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclaimed = true;\n\n\t\t\ttry {\n\t\t\t\tconst result = await exchangeAuthorizationCode(code, verifier, signal);\n\t\t\t\tsendHtml(response, 200, oauthSuccessHtml(\"Signed in to OpenRouter. You may now close this page.\"));\n\t\t\t\tfinish({ credential: result });\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"Unknown token exchange error\";\n\t\t\t\tsendHtml(response, 502, oauthErrorHtml(\"OpenRouter key exchange failed.\", message));\n\t\t\t\tfinish({ error: error instanceof Error ? error : new Error(message) });\n\t\t\t}\n\t\t})();\n\t});\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(0, callbackHost, () => {\n\t\t\tserver.removeListener(\"error\", reject);\n\t\t\tresolve();\n\t\t});\n\t});\n\n\tserver.on(\"error\", (error) => finish({ error }));\n\tonAbort = () => finish({ error: new Error(\"Login cancelled\") });\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\tif (signal.aborted) {\n\t\tclose();\n\t\tthrow new Error(\"Login cancelled\");\n\t}\n\ttimeout = setTimeout(() => finish({ error: new Error(\"OpenRouter OAuth login timed out\") }), LOGIN_TIMEOUT_MS);\n\n\tconst address = server.address();\n\tif (!address || typeof address === \"string\") {\n\t\tclose();\n\t\tthrow new Error(\"Could not determine the OpenRouter OAuth callback port\");\n\t}\n\n\treturn {\n\t\tcallbackUrl: `http://${callbackHost}:${address.port}${callbackPath}`,\n\t\tclose,\n\t\t// A claimed callback is already exchanging its code; let that exchange settle the login.\n\t\tcancelWait: () => {\n\t\t\tif (!claimed) finish({ credential: null });\n\t\t},\n\t\twaitForCredential: () => credential,\n\t};\n}\n\nasync function loginOpenRouter(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst callbackPath = `/oauth/callback/${crypto.randomUUID()}`;\n\tconst callback = await startCallbackServer(callbackPath, verifier, interaction.signal);\n\tconst manualAbort = new AbortController();\n\tlet manualInput: string | undefined;\n\tlet manualError: Error | undefined;\n\n\ttry {\n\t\tconst authorizeUrl = new URL(AUTHORIZE_URL);\n\t\tauthorizeUrl.search = new URLSearchParams({\n\t\t\tcallback_url: callback.callbackUrl,\n\t\t\tcode_challenge: challenge,\n\t\t\tcode_challenge_method: \"S256\",\n\t\t}).toString();\n\n\t\tinteraction.notify({\n\t\t\ttype: \"progress\",\n\t\t\tmessage: `Listening for OpenRouter OAuth callback on ${callback.callbackUrl}`,\n\t\t});\n\t\tinteraction.notify({\n\t\t\ttype: \"auth_url\",\n\t\t\turl: authorizeUrl.toString(),\n\t\t\tinstructions:\n\t\t\t\t\"Complete sign-in in your browser. If the browser is on another machine, paste the final redirect URL here.\",\n\t\t});\n\n\t\tconst manualPromise = interaction\n\t\t\t.prompt({\n\t\t\t\ttype: \"manual_code\",\n\t\t\t\tmessage: \"Complete sign-in in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\t\tplaceholder: callback.callbackUrl,\n\t\t\t\tsignal: manualAbort.signal,\n\t\t\t})\n\t\t\t.then((input) => {\n\t\t\t\tmanualInput = input;\n\t\t\t\tcallback.cancelWait();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tcallback.cancelWait();\n\t\t\t});\n\n\t\tconst credential = await callback.waitForCredential();\n\t\tif (manualError) throw manualError;\n\t\tif (credential) return credential;\n\n\t\tawait manualPromise;\n\t\tif (manualError) throw manualError;\n\t\tconst code = manualInput ? parseAuthorizationInput(manualInput) : undefined;\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\tinteraction.notify({ type: \"progress\", message: \"Exchanging authorization code for an API key...\" });\n\t\treturn await exchangeAuthorizationCode(code, verifier, interaction.signal);\n\t} finally {\n\t\tmanualAbort.abort();\n\t\tcallback.close();\n\t}\n}\n\nexport const openRouterOAuth: OAuthAuth = {\n\tname: \"OpenRouter OAuth\",\n\tloginLabel: \"Sign in with OpenRouter\",\n\tlogin: loginOpenRouter,\n\tasync refresh(credential, _signal) {\n\t\treturn credential;\n\t},\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
1
+ {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/openrouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA6RvF,eAAO,MAAM,eAAe,EAAE,SAU7B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../../src/auth/oauth/openrouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OpenRouter OAuth PKCE flow.\n *\n * OpenRouter exchanges an authorization code for a permanent, user-controlled\n * API key rather than an expiring access/refresh token pair. The callback is\n * handled by a one-shot loopback server on an ephemeral port, raced against a\n * manual prompt so remote/headless sessions can paste the redirect URL when\n * the browser cannot reach the loopback server.\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 { createServer, type Server, type ServerResponse } from \"node:http\";\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst AUTHORIZE_URL = \"https://openrouter.ai/auth\";\nconst TOKEN_URL = \"https://openrouter.ai/api/v1/auth/keys\";\nconst LOGIN_TIMEOUT_MS = 5 * 60 * 1000;\nconst TOKEN_EXCHANGE_TIMEOUT_MS = 30_000;\n\nfunction getCallbackHost(): string {\n\treturn getProviderEnvValue(\"PI_OAUTH_CALLBACK_HOST\") || \"127.0.0.1\";\n}\n\ntype JsonObject = Record<string, unknown>;\n\ntype OpenRouterCallbackServer = {\n\tcallbackUrl: string;\n\t/** Stop listening and release timers without settling `waitForCredential`. */\n\tclose: () => void;\n\t/** Hand the login over to manual code entry unless a callback already claimed the exchange. */\n\tcancelWait: () => void;\n\t/**\n\t * Resolves with the credential once a browser callback completes the key\n\t * exchange, or with null once `cancelWait` hands the login over to manual\n\t * code entry. Rejects on timeout, cancellation, or a failed exchange.\n\t */\n\twaitForCredential: () => Promise<OAuthCredential | null>;\n};\n\nfunction sendHtml(response: ServerResponse, status: number, html: string): void {\n\tresponse.statusCode = status;\n\tresponse.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n\tresponse.setHeader(\"cache-control\", \"no-store\");\n\tresponse.end(html);\n}\n\nfunction parseAuthorizationInput(input: string): string | undefined {\n\tconst value = input.trim();\n\tif (!value) return undefined;\n\n\ttry {\n\t\treturn new URL(value).searchParams.get(\"code\") ?? undefined;\n\t} catch {\n\t\t// not a URL\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\treturn new URLSearchParams(value).get(\"code\") ?? undefined;\n\t}\n\n\treturn value;\n}\n\nfunction errorDetail(body: JsonObject): string | undefined {\n\tif (typeof body.error_description === \"string\") return body.error_description;\n\tif (typeof body.message === \"string\") return body.message;\n\tif (typeof body.error === \"string\") return body.error;\n\tif (body.error && typeof body.error === \"object\" && !Array.isArray(body.error)) {\n\t\tconst message = (body.error as JsonObject).message;\n\t\tif (typeof message === \"string\") return message;\n\t}\n\treturn undefined;\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\tconst controller = new AbortController();\n\tconst onAbort = () => controller.abort(signal.reason);\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\tconst timeout = setTimeout(\n\t\t() => controller.abort(new Error(\"OpenRouter OAuth token exchange timed out\")),\n\t\tTOKEN_EXCHANGE_TIMEOUT_MS,\n\t);\n\n\tlet response: Response;\n\tlet body: JsonObject = {};\n\ttry {\n\t\tresponse = await fetch(TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ code, code_verifier: verifier, code_challenge_method: \"S256\" }),\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\ttry {\n\t\t\tconst parsed = (await response.json()) as unknown;\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) body = parsed as JsonObject;\n\t\t} catch {\n\t\t\tif (response.ok) throw new Error(\"OpenRouter OAuth returned invalid JSON\");\n\t\t}\n\t} catch (error) {\n\t\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\t\tif (controller.signal.aborted) throw new Error(\"OpenRouter OAuth token exchange timed out\");\n\t\tthrow error;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t}\n\n\tif (!response.ok) {\n\t\tconst detail = errorDetail(body);\n\t\tthrow new Error(`OpenRouter OAuth key exchange failed (HTTP ${response.status})${detail ? `: ${detail}` : \"\"}`);\n\t}\n\n\tif (typeof body.key !== \"string\" || body.key.length === 0) {\n\t\tthrow new Error('OpenRouter OAuth response carries no \"key\"');\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: body.key,\n\t\trefresh: \"\",\n\t\texpires: Number.MAX_SAFE_INTEGER,\n\t};\n}\n\nasync function startCallbackServer(\n\tcallbackPath: string,\n\tverifier: string,\n\tsignal: AbortSignal,\n): Promise<OpenRouterCallbackServer> {\n\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\tconst callbackHost = getCallbackHost();\n\tlet resolveCredential: (credential: OAuthCredential | null) => void = () => {};\n\tlet rejectCredential: (error: Error) => void = () => {};\n\tconst credential = new Promise<OAuthCredential | null>((resolve, reject) => {\n\t\tresolveCredential = resolve;\n\t\trejectCredential = reject;\n\t});\n\n\tlet server: Server;\n\tlet claimed = false;\n\tlet settled = false;\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\tlet onAbort: (() => void) | undefined;\n\n\tconst close = (): void => {\n\t\tif (timeout) clearTimeout(timeout);\n\t\tif (onAbort) signal.removeEventListener(\"abort\", onAbort);\n\t\tserver.close();\n\t};\n\n\tconst finish = (result: { credential: OAuthCredential | null } | { error: Error }): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tclose();\n\t\tif (\"credential\" in result) resolveCredential(result.credential);\n\t\telse rejectCredential(result.error);\n\t};\n\n\tserver = createServer((request, response) => {\n\t\tvoid (async () => {\n\t\t\tconst requestUrl = new URL(request.url ?? \"/\", `http://${callbackHost}`);\n\t\t\tif (request.method !== \"GET\" || requestUrl.pathname !== callbackPath) {\n\t\t\t\tsendHtml(response, 404, oauthErrorHtml(\"OAuth callback route not found.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (claimed || settled) {\n\t\t\t\tsendHtml(response, 409, oauthErrorHtml(\"This OAuth callback has already been used.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst oauthError = requestUrl.searchParams.get(\"error\");\n\t\t\tif (oauthError) {\n\t\t\t\tconst description = requestUrl.searchParams.get(\"error_description\") ?? oauthError;\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"OpenRouter authorization was denied.\", description));\n\t\t\t\tfinish({ error: new Error(`OpenRouter authorization failed: ${description}`) });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst code = requestUrl.searchParams.get(\"code\");\n\t\t\tif (!code) {\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"OpenRouter returned no authorization code.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclaimed = true;\n\n\t\t\ttry {\n\t\t\t\tconst result = await exchangeAuthorizationCode(code, verifier, signal);\n\t\t\t\tsendHtml(response, 200, oauthSuccessHtml(\"Signed in to OpenRouter. You may now close this page.\"));\n\t\t\t\tfinish({ credential: result });\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"Unknown token exchange error\";\n\t\t\t\tsendHtml(response, 502, oauthErrorHtml(\"OpenRouter key exchange failed.\", message));\n\t\t\t\tfinish({ error: error instanceof Error ? error : new Error(message) });\n\t\t\t}\n\t\t})();\n\t});\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(0, callbackHost, () => {\n\t\t\tserver.removeListener(\"error\", reject);\n\t\t\tresolve();\n\t\t});\n\t});\n\n\tserver.on(\"error\", (error) => finish({ error }));\n\tonAbort = () => finish({ error: new Error(\"Login cancelled\") });\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\tif (signal.aborted) {\n\t\tclose();\n\t\tthrow new Error(\"Login cancelled\");\n\t}\n\ttimeout = setTimeout(() => finish({ error: new Error(\"OpenRouter OAuth login timed out\") }), LOGIN_TIMEOUT_MS);\n\n\tconst address = server.address();\n\tif (!address || typeof address === \"string\") {\n\t\tclose();\n\t\tthrow new Error(\"Could not determine the OpenRouter OAuth callback port\");\n\t}\n\n\treturn {\n\t\tcallbackUrl: `http://${callbackHost}:${address.port}${callbackPath}`,\n\t\tclose,\n\t\t// A claimed callback is already exchanging its code; let that exchange settle the login.\n\t\tcancelWait: () => {\n\t\t\tif (!claimed) finish({ credential: null });\n\t\t},\n\t\twaitForCredential: () => credential,\n\t};\n}\n\nasync function loginOpenRouter(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst callbackPath = `/oauth/callback/${crypto.randomUUID()}`;\n\tconst callback = await startCallbackServer(callbackPath, verifier, interaction.signal);\n\tconst manualAbort = new AbortController();\n\tlet manualInput: string | undefined;\n\tlet manualError: Error | undefined;\n\n\ttry {\n\t\tconst authorizeUrl = new URL(AUTHORIZE_URL);\n\t\tauthorizeUrl.search = new URLSearchParams({\n\t\t\tcallback_url: callback.callbackUrl,\n\t\t\tcode_challenge: challenge,\n\t\t\tcode_challenge_method: \"S256\",\n\t\t}).toString();\n\n\t\tinteraction.notify({\n\t\t\ttype: \"progress\",\n\t\t\tmessage: `Listening for OpenRouter OAuth callback on ${callback.callbackUrl}`,\n\t\t});\n\t\tinteraction.notify({\n\t\t\ttype: \"auth_url\",\n\t\t\turl: authorizeUrl.toString(),\n\t\t\tinstructions:\n\t\t\t\t\"Complete sign-in in your browser. If the browser is on another machine, paste the final redirect URL here.\",\n\t\t});\n\n\t\tconst manualPromise = interaction\n\t\t\t.prompt({\n\t\t\t\ttype: \"manual_code\",\n\t\t\t\tmessage: \"Complete sign-in in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\t\tplaceholder: callback.callbackUrl,\n\t\t\t\tsignal: manualAbort.signal,\n\t\t\t})\n\t\t\t.then((input) => {\n\t\t\t\tmanualInput = input;\n\t\t\t\tcallback.cancelWait();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tcallback.cancelWait();\n\t\t\t});\n\n\t\tconst credential = await callback.waitForCredential();\n\t\tif (manualError) throw manualError;\n\t\tif (credential) return credential;\n\n\t\tawait manualPromise;\n\t\tif (manualError) throw manualError;\n\t\tconst code = manualInput ? parseAuthorizationInput(manualInput) : undefined;\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\tinteraction.notify({ type: \"progress\", message: \"Exchanging authorization code for an API key...\" });\n\t\treturn await exchangeAuthorizationCode(code, verifier, interaction.signal);\n\t} finally {\n\t\tmanualAbort.abort();\n\t\tcallback.close();\n\t}\n}\n\nexport const openRouterOAuth: OAuthAuth = {\n\tname: \"OpenRouter OAuth\",\n\tloginLabel: \"Sign in with OpenRouter\",\n\tlogin: loginOpenRouter,\n\tasync refresh(credential, _signal) {\n\t\treturn credential;\n\t},\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
1
+ {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../../src/auth/oauth/openrouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OpenRouter OAuth PKCE flow.\n *\n * OpenRouter exchanges an authorization code for a permanent, user-controlled\n * API key rather than an expiring access/refresh token pair. The callback is\n * handled by a one-shot loopback server on an ephemeral port, raced against a\n * manual prompt so remote/headless sessions can paste the redirect URL when\n * the browser cannot reach the loopback server.\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 { createServer, type Server, type ServerResponse } from \"node:http\";\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst AUTHORIZE_URL = \"https://openrouter.ai/auth\";\nconst TOKEN_URL = \"https://openrouter.ai/api/v1/auth/keys\";\nconst LOGIN_TIMEOUT_MS = 5 * 60 * 1000;\nconst TOKEN_EXCHANGE_TIMEOUT_MS = 30_000;\n\nfunction getCallbackHost(): string {\n\treturn getProviderEnvValue(\"PI_OAUTH_CALLBACK_HOST\") || \"127.0.0.1\";\n}\n\ntype JsonObject = Record<string, unknown>;\n\ntype OpenRouterCallbackServer = {\n\tcallbackUrl: string;\n\t/** Stop listening and release timers without settling `waitForCredential`. */\n\tclose: () => void;\n\t/** Hand the login over to manual code entry unless a callback already claimed the exchange. */\n\tcancelWait: () => void;\n\t/**\n\t * Resolves with the credential once a browser callback completes the key\n\t * exchange, or with null once `cancelWait` hands the login over to manual\n\t * code entry. Rejects on timeout, cancellation, or a failed exchange.\n\t */\n\twaitForCredential: () => Promise<OAuthCredential | null>;\n};\n\nfunction sendHtml(response: ServerResponse, status: number, html: string): void {\n\tresponse.statusCode = status;\n\tresponse.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n\tresponse.setHeader(\"cache-control\", \"no-store\");\n\tresponse.end(html);\n}\n\nfunction parseAuthorizationInput(input: string): string | undefined {\n\tconst value = input.trim();\n\tif (!value) return undefined;\n\n\ttry {\n\t\treturn new URL(value).searchParams.get(\"code\") ?? undefined;\n\t} catch {\n\t\t// not a URL\n\t}\n\n\tif (value.includes(\"code=\")) {\n\t\treturn new URLSearchParams(value).get(\"code\") ?? undefined;\n\t}\n\n\treturn value;\n}\n\nfunction errorDetail(body: JsonObject): string | undefined {\n\tif (typeof body.error_description === \"string\") return body.error_description;\n\tif (typeof body.message === \"string\") return body.message;\n\tif (typeof body.error === \"string\") return body.error;\n\tif (body.error && typeof body.error === \"object\" && !Array.isArray(body.error)) {\n\t\tconst message = (body.error as JsonObject).message;\n\t\tif (typeof message === \"string\") return message;\n\t}\n\treturn undefined;\n}\n\nasync function exchangeAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\tconst controller = new AbortController();\n\tconst onAbort = () => controller.abort(signal.reason);\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\tconst timeout = setTimeout(\n\t\t() => controller.abort(new Error(\"OpenRouter OAuth token exchange timed out\")),\n\t\tTOKEN_EXCHANGE_TIMEOUT_MS,\n\t);\n\n\tlet response: Response;\n\tlet body: JsonObject = {};\n\ttry {\n\t\tresponse = await fetch(TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ code, code_verifier: verifier, code_challenge_method: \"S256\" }),\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\ttry {\n\t\t\tconst parsed = (await response.json()) as unknown;\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) body = parsed as JsonObject;\n\t\t} catch {\n\t\t\tif (response.ok) throw new Error(\"OpenRouter OAuth returned invalid JSON\");\n\t\t}\n\t} catch (error) {\n\t\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\t\tif (controller.signal.aborted) throw new Error(\"OpenRouter OAuth token exchange timed out\");\n\t\tthrow error;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t}\n\n\tif (!response.ok) {\n\t\tconst detail = errorDetail(body);\n\t\tthrow new Error(`OpenRouter OAuth key exchange failed (HTTP ${response.status})${detail ? `: ${detail}` : \"\"}`);\n\t}\n\n\tif (typeof body.key !== \"string\" || body.key.length === 0) {\n\t\tthrow new Error('OpenRouter OAuth response carries no \"key\"');\n\t}\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: body.key,\n\t\trefresh: \"\",\n\t\texpires: Number.MAX_SAFE_INTEGER,\n\t};\n}\n\nasync function startCallbackServer(\n\tcallbackPath: string,\n\tverifier: string,\n\tsignal: AbortSignal,\n): Promise<OpenRouterCallbackServer> {\n\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\tconst callbackHost = getCallbackHost();\n\tlet resolveCredential: (credential: OAuthCredential | null) => void = () => {};\n\tlet rejectCredential: (error: Error) => void = () => {};\n\tconst credential = new Promise<OAuthCredential | null>((resolve, reject) => {\n\t\tresolveCredential = resolve;\n\t\trejectCredential = reject;\n\t});\n\n\tlet server: Server;\n\tlet claimed = false;\n\tlet settled = false;\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\tlet onAbort: (() => void) | undefined;\n\n\tconst close = (): void => {\n\t\tif (timeout) clearTimeout(timeout);\n\t\tif (onAbort) signal.removeEventListener(\"abort\", onAbort);\n\t\tserver.close();\n\t};\n\n\tconst finish = (result: { credential: OAuthCredential | null } | { error: Error }): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tclose();\n\t\tif (\"credential\" in result) resolveCredential(result.credential);\n\t\telse rejectCredential(result.error);\n\t};\n\n\tserver = createServer((request, response) => {\n\t\tvoid (async () => {\n\t\t\tconst requestUrl = new URL(request.url ?? \"/\", `http://${callbackHost}`);\n\t\t\tif (request.method !== \"GET\" || requestUrl.pathname !== callbackPath) {\n\t\t\t\tsendHtml(response, 404, oauthErrorHtml(\"OAuth callback route not found.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (claimed || settled) {\n\t\t\t\tsendHtml(response, 409, oauthErrorHtml(\"This OAuth callback has already been used.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst oauthError = requestUrl.searchParams.get(\"error\");\n\t\t\tif (oauthError) {\n\t\t\t\tconst description = requestUrl.searchParams.get(\"error_description\") ?? oauthError;\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"OpenRouter authorization was denied.\", description));\n\t\t\t\tfinish({ error: new Error(`OpenRouter authorization failed: ${description}`) });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst code = requestUrl.searchParams.get(\"code\");\n\t\t\tif (!code) {\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"OpenRouter returned no authorization code.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclaimed = true;\n\n\t\t\ttry {\n\t\t\t\tconst result = await exchangeAuthorizationCode(code, verifier, signal);\n\t\t\t\tsendHtml(response, 200, oauthSuccessHtml(\"Signed in to OpenRouter. You may now close this page.\"));\n\t\t\t\tfinish({ credential: result });\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"Unknown token exchange error\";\n\t\t\t\tsendHtml(response, 502, oauthErrorHtml(\"OpenRouter key exchange failed.\", message));\n\t\t\t\tfinish({ error: error instanceof Error ? error : new Error(message) });\n\t\t\t}\n\t\t})();\n\t});\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(0, callbackHost, () => {\n\t\t\tserver.removeListener(\"error\", reject);\n\t\t\tresolve();\n\t\t});\n\t});\n\n\tserver.on(\"error\", (error) => finish({ error }));\n\tonAbort = () => finish({ error: new Error(\"Login cancelled\") });\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\tif (signal.aborted) {\n\t\tclose();\n\t\tthrow new Error(\"Login cancelled\");\n\t}\n\ttimeout = setTimeout(() => finish({ error: new Error(\"OpenRouter OAuth login timed out\") }), LOGIN_TIMEOUT_MS);\n\n\tconst address = server.address();\n\tif (!address || typeof address === \"string\") {\n\t\tclose();\n\t\tthrow new Error(\"Could not determine the OpenRouter OAuth callback port\");\n\t}\n\n\treturn {\n\t\tcallbackUrl: `http://${callbackHost}:${address.port}${callbackPath}`,\n\t\tclose,\n\t\t// A claimed callback is already exchanging its code; let that exchange settle the login.\n\t\tcancelWait: () => {\n\t\t\tif (!claimed) finish({ credential: null });\n\t\t},\n\t\twaitForCredential: () => credential,\n\t};\n}\n\nasync function loginOpenRouter(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst callbackPath = `/oauth/callback/${crypto.randomUUID()}`;\n\tconst callback = await startCallbackServer(callbackPath, verifier, interaction.signal);\n\tconst manualAbort = new AbortController();\n\tlet manualInput: string | undefined;\n\tlet manualError: Error | undefined;\n\n\ttry {\n\t\tconst authorizeUrl = new URL(AUTHORIZE_URL);\n\t\tauthorizeUrl.search = new URLSearchParams({\n\t\t\tcallback_url: callback.callbackUrl,\n\t\t\tcode_challenge: challenge,\n\t\t\tcode_challenge_method: \"S256\",\n\t\t}).toString();\n\n\t\tinteraction.notify({\n\t\t\ttype: \"progress\",\n\t\t\tmessage: `Listening for OpenRouter OAuth callback on ${callback.callbackUrl}`,\n\t\t});\n\t\tinteraction.notify({\n\t\t\ttype: \"auth_url\",\n\t\t\turl: authorizeUrl.toString(),\n\t\t\tinstructions:\n\t\t\t\t\"Complete sign-in in your browser. If the browser is on another machine, paste the final redirect URL here.\",\n\t\t});\n\n\t\tconst manualPromise = interaction\n\t\t\t.prompt({\n\t\t\t\ttype: \"manual_code\",\n\t\t\t\tmessage: \"Complete sign-in in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\t\tplaceholder: callback.callbackUrl,\n\t\t\t\tsignal: manualAbort.signal,\n\t\t\t})\n\t\t\t.then((input) => {\n\t\t\t\tmanualInput = input;\n\t\t\t\tcallback.cancelWait();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tcallback.cancelWait();\n\t\t\t});\n\n\t\tconst credential = await callback.waitForCredential();\n\t\tif (manualError) throw manualError;\n\t\tif (credential) return credential;\n\n\t\tawait manualPromise;\n\t\tif (manualError) throw manualError;\n\t\tconst code = manualInput ? parseAuthorizationInput(manualInput) : undefined;\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\tinteraction.notify({ type: \"progress\", message: \"Exchanging authorization code for an API key...\" });\n\t\treturn await exchangeAuthorizationCode(code, verifier, interaction.signal);\n\t} finally {\n\t\tmanualAbort.abort();\n\t\tcallback.close();\n\t}\n}\n\nexport const openRouterOAuth: OAuthAuth = {\n\tname: \"OpenRouter OAuth\",\n\tloginLabel: \"Sign in with OpenRouter\",\n\tlogin: loginOpenRouter,\n\tasync refresh(credential, _signal) {\n\t\treturn credential;\n\t},\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/pkce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAarF","sourcesContent":["/**\n * PKCE utilities using Web Crypto API.\n * Works in both Node.js 20+ and browsers.\n */\n\n/**\n * Encode bytes as base64url string.\n */\nfunction base64urlEncode(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\n\n/**\n * Generate PKCE code verifier and challenge.\n * Uses Web Crypto API for cross-platform compatibility.\n */\nexport async function generatePKCE(): Promise<{ verifier: string; challenge: string }> {\n\t// Generate random verifier\n\tconst verifierBytes = new Uint8Array(32);\n\tcrypto.getRandomValues(verifierBytes);\n\tconst verifier = base64urlEncode(verifierBytes);\n\n\t// Compute SHA-256 challenge\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(verifier);\n\tconst hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n\tconst challenge = base64urlEncode(new Uint8Array(hashBuffer));\n\n\treturn { verifier, challenge };\n}\n"]}
1
+ {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/pkce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAarF"}
@@ -1 +1 @@
1
- {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../src/auth/oauth/pkce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,SAAS,eAAe,CAAC,KAAiB,EAAU;IACnD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAAA,CAC9E;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,GAAqD;IACtF,2BAA2B;IAC3B,MAAM,aAAa,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;IAEhD,4BAA4B;IAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IAE9D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AAAA,CAC/B","sourcesContent":["/**\n * PKCE utilities using Web Crypto API.\n * Works in both Node.js 20+ and browsers.\n */\n\n/**\n * Encode bytes as base64url string.\n */\nfunction base64urlEncode(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\n\n/**\n * Generate PKCE code verifier and challenge.\n * Uses Web Crypto API for cross-platform compatibility.\n */\nexport async function generatePKCE(): Promise<{ verifier: string; challenge: string }> {\n\t// Generate random verifier\n\tconst verifierBytes = new Uint8Array(32);\n\tcrypto.getRandomValues(verifierBytes);\n\tconst verifier = base64urlEncode(verifierBytes);\n\n\t// Compute SHA-256 challenge\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(verifier);\n\tconst hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n\tconst challenge = base64urlEncode(new Uint8Array(hashBuffer));\n\n\treturn { verifier, challenge };\n}\n"]}
1
+ {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../src/auth/oauth/pkce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,SAAS,eAAe,CAAC,KAAiB;IACzC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IACjC,2BAA2B;IAC3B,MAAM,aAAa,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;IAEhD,4BAA4B;IAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IAE9D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC","sourcesContent":["/**\n * PKCE utilities using Web Crypto API.\n * Works in both Node.js 20+ and browsers.\n */\n\n/**\n * Encode bytes as base64url string.\n */\nfunction base64urlEncode(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\n\n/**\n * Generate PKCE code verifier and challenge.\n * Uses Web Crypto API for cross-platform compatibility.\n */\nexport async function generatePKCE(): Promise<{ verifier: string; challenge: string }> {\n\t// Generate random verifier\n\tconst verifierBytes = new Uint8Array(32);\n\tcrypto.getRandomValues(verifierBytes);\n\tconst verifier = base64urlEncode(verifierBytes);\n\n\t// Compute SHA-256 challenge\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(verifier);\n\tconst hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n\tconst challenge = base64urlEncode(new Uint8Array(hashBuffer));\n\n\treturn { verifier, challenge };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"radius.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/radius.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA2UvF,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,SAAS,CA8CxE","sourcesContent":["/**\n * Radius gateway OAuth flow.\n *\n * Radius is a pi-messages gateway. OAuth client APIs live on the configured\n * gateway; only the interactive browser authorization endpoint is discovered.\n * Model catalog loading is owned by the Radius provider.\n *\n * NOTE: This module uses node:http for the OAuth callback server.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { normalizeRadiusGatewayUrl } from \"../../providers/radius-config.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst CALLBACK_HOST = \"127.0.0.1\";\nconst CALLBACK_PORT = 1456;\nconst CALLBACK_PATH = \"/oauth/callback\";\nconst REDIRECT_URI = `http://${CALLBACK_HOST}:${CALLBACK_PORT}${CALLBACK_PATH}`;\nconst TOKEN_EXPIRY_SKEW_MS = 60_000;\nconst LOGIN_METHOD_BROWSER = \"browser\";\nconst LOGIN_METHOD_DEVICE_CODE = \"device-code\";\nconst OAUTH_CLIENT_ID = \"pi-gateway\";\nconst OAUTH_SCOPE = \"gateway offline_access\";\nconst OAUTH_DEVICE_CODE_GRANT_TYPE = \"urn:ietf:params:oauth:grant-type:device_code\";\n\ntype RadiusOAuthDiscovery = {\n\tauthorizationEndpoint: string;\n};\n\ntype DeviceAuthorizationResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\texpires_in: number;\n\tinterval?: number;\n};\n\nasync function loadRadiusOAuthDiscovery(gateway: string, signal: AbortSignal): Promise<RadiusOAuthDiscovery> {\n\tconst response = await fetch(new URL(\"/v1/oauth\", gateway), {\n\t\theaders: { accept: \"application/json\" },\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Could not load Radius OAuth config from ${gateway}: ${response.status} ${await response.text()}`,\n\t\t);\n\t}\n\n\tconst discovery = (await response.json()) as Partial<RadiusOAuthDiscovery>;\n\tif (typeof discovery.authorizationEndpoint !== \"string\") {\n\t\tthrow new Error(`Invalid Radius OAuth config from ${gateway}`);\n\t}\n\treturn { authorizationEndpoint: discovery.authorizationEndpoint };\n}\n\nclass OAuthResponseError extends Error {\n\treadonly status: number;\n\treadonly oauthError?: string;\n\n\tconstructor(status: number, oauthError: string | undefined, description: string | undefined, message: string) {\n\t\tconst detail = oauthError\n\t\t\t? description\n\t\t\t\t? `${oauthError}: ${description}`\n\t\t\t\t: oauthError\n\t\t\t: description || String(status);\n\t\tsuper(`${message}: ${detail}`);\n\t\tthis.status = status;\n\t\tthis.oauthError = oauthError;\n\t}\n}\n\nasync function readOAuthResponseError(response: Response, message: string): Promise<OAuthResponseError> {\n\tconst text = await response.text().catch(() => \"\");\n\tlet oauthError: string | undefined;\n\tlet description: string | undefined;\n\n\tif (text) {\n\t\ttry {\n\t\t\tconst data = JSON.parse(text) as { error?: unknown; error_description?: unknown };\n\t\t\toauthError = typeof data.error === \"string\" ? data.error : undefined;\n\t\t\tdescription = typeof data.error_description === \"string\" ? data.error_description : undefined;\n\t\t} catch {\n\t\t\tdescription = text;\n\t\t}\n\t}\n\n\treturn new OAuthResponseError(response.status, oauthError, description, message);\n}\n\nasync function requestOAuthToken(\n\tgateway: string,\n\tbody: URLSearchParams,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(new URL(\"/v1/oauth/token\", gateway), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody,\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tif (!response.ok) {\n\t\tthrow await readOAuthResponseError(response, \"Radius OAuth token request failed\");\n\t}\n\n\tconst data = (await response.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t\tscope?: string;\n\t};\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: data.access_token,\n\t\trefresh: data.refresh_token,\n\t\texpires: Date.now() + data.expires_in * 1000 - TOKEN_EXPIRY_SKEW_MS,\n\t\tscope: data.scope,\n\t};\n}\n\ntype OAuthCallbackServer = {\n\twaitForCode(): Promise<string | null>;\n\tclose(): void;\n};\n\nfunction startOAuthCallbackServer(expectedState: string, signal: AbortSignal): Promise<OAuthCallbackServer> {\n\tif (!_http) {\n\t\tthrow new Error(\"Radius OAuth is only available in Node.js environments\");\n\t}\n\n\tlet settle: (code: string | null) => void = () => {};\n\tlet settled = false;\n\tconst wait = new Promise<string | null>((resolve) => {\n\t\tsettle = resolve;\n\t});\n\tconst finish = (code: string | null) => {\n\t\tif (settled) {\n\t\t\treturn;\n\t\t}\n\t\tsettled = true;\n\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\tsettle(code);\n\t};\n\tconst onAbort = () => finish(null);\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\n\tconst sendPage = (response: import(\"node:http\").ServerResponse, status: number, html: string) => {\n\t\tresponse.statusCode = status;\n\t\tresponse.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n\t\tresponse.end(html);\n\t};\n\n\tconst server = _http.createServer((request, response) => {\n\t\tconst url = new URL(request.url ?? \"/\", REDIRECT_URI);\n\t\tif (url.pathname !== CALLBACK_PATH) {\n\t\t\tsendPage(response, 404, oauthErrorHtml(\"Callback route not found.\"));\n\t\t\treturn;\n\t\t}\n\t\tif (url.searchParams.get(\"state\") !== expectedState) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(\"OAuth state mismatch.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst error = url.searchParams.get(\"error\");\n\t\tif (error) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(url.searchParams.get(\"error_description\") ?? error));\n\t\t\tfinish(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst code = url.searchParams.get(\"code\");\n\t\tif (!code) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(\"Missing authorization code.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tsendPage(response, 200, oauthSuccessHtml(\"Signed in to Radius. You may now close this page.\"));\n\t\tfinish(code);\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(CALLBACK_PORT, CALLBACK_HOST, () => {\n\t\t\t\tresolve({\n\t\t\t\t\twaitForCode: () => wait,\n\t\t\t\t\tclose: () => {\n\t\t\t\t\t\tfinish(null);\n\t\t\t\t\t\tserver.close();\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t})\n\t\t\t.once(\"error\", () => {\n\t\t\t\tfinish(null);\n\t\t\t\tresolve({ waitForCode: async () => null, close: () => {} });\n\t\t\t});\n\t});\n}\n\nasync function loginWithBrowser(\n\tgateway: string,\n\tauthorizationEndpoint: string,\n\tinteraction: ProviderAuthInteraction,\n): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = crypto.randomUUID();\n\tconst authorizeUrl = new URL(authorizationEndpoint);\n\tauthorizeUrl.search = new URLSearchParams({\n\t\tresponse_type: \"code\",\n\t\tclient_id: OAUTH_CLIENT_ID,\n\t\tredirect_uri: REDIRECT_URI,\n\t\tscope: OAUTH_SCOPE,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\thandoff: \"url\",\n\t\tstate,\n\t}).toString();\n\n\tconst callbackServer = await startOAuthCallbackServer(state, interaction.signal);\n\tinteraction.notify({ type: \"progress\", message: `Listening for OAuth callback on ${REDIRECT_URI}` });\n\tinteraction.notify({\n\t\ttype: \"auth_url\",\n\t\turl: authorizeUrl.toString(),\n\t\tinstructions: \"Continue in your browser.\",\n\t});\n\n\ttry {\n\t\tconst code = await callbackServer.waitForCode();\n\t\tif (!code) {\n\t\t\tif (interaction.signal.aborted) {\n\t\t\t\tthrow new Error(\"Login cancelled\");\n\t\t\t}\n\t\t\tthrow new Error(\"OAuth callback did not complete.\");\n\t\t}\n\t\treturn await requestOAuthToken(\n\t\t\tgateway,\n\t\t\tnew URLSearchParams({\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\tredirect_uri: REDIRECT_URI,\n\t\t\t\tcode,\n\t\t\t\tcode_verifier: verifier,\n\t\t\t}),\n\t\t\tinteraction.signal,\n\t\t);\n\t} finally {\n\t\tcallbackServer.close();\n\t}\n}\n\nasync function requestDeviceAuthorization(gateway: string, signal: AbortSignal): Promise<DeviceAuthorizationResponse> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(new URL(\"/v1/oauth/device\", gateway), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody: new URLSearchParams({ client_id: OAUTH_CLIENT_ID, scope: OAUTH_SCOPE }),\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tif (!response.ok) {\n\t\tthrow await readOAuthResponseError(response, \"Radius OAuth device authorization failed\");\n\t}\n\n\tconst data = (await response.json()) as Partial<DeviceAuthorizationResponse>;\n\tif (!data.device_code || !data.user_code || !data.verification_uri || !data.expires_in) {\n\t\tthrow new Error(\"Radius OAuth device authorization response is missing required fields\");\n\t}\n\n\treturn {\n\t\tdevice_code: data.device_code,\n\t\tuser_code: data.user_code,\n\t\tverification_uri: data.verification_uri,\n\t\texpires_in: data.expires_in,\n\t\tinterval: data.interval,\n\t};\n}\n\nasync function loginWithDeviceCode(gateway: string, interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst device = await requestDeviceAuthorization(gateway, 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\treturn pollOAuthDeviceCodeFlow<OAuthCredential>({\n\t\tintervalSeconds: device.interval,\n\t\texpiresInSeconds: device.expires_in,\n\t\tsignal: interaction.signal,\n\t\tpoll: async () => {\n\t\t\ttry {\n\t\t\t\tconst credentials = await requestOAuthToken(\n\t\t\t\t\tgateway,\n\t\t\t\t\tnew URLSearchParams({\n\t\t\t\t\t\tgrant_type: OAUTH_DEVICE_CODE_GRANT_TYPE,\n\t\t\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\t\t\tdevice_code: device.device_code,\n\t\t\t\t\t}),\n\t\t\t\t\tinteraction.signal,\n\t\t\t\t);\n\t\t\t\treturn { status: \"complete\", value: credentials };\n\t\t\t} catch (error) {\n\t\t\t\tif (!(error instanceof OAuthResponseError)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tswitch (error.oauthError) {\n\t\t\t\t\tcase \"authorization_pending\":\n\t\t\t\t\t\treturn { status: \"pending\" };\n\t\t\t\t\tcase \"slow_down\":\n\t\t\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t\t\tcase \"expired_token\":\n\t\t\t\t\t\treturn { status: \"failed\", message: \"Device authorization expired.\" };\n\t\t\t\t\tcase \"access_denied\":\n\t\t\t\t\t\treturn { status: \"failed\", message: \"Device authorization was denied.\" };\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n}\n\nexport interface RadiusOAuthOptions {\n\tname: string;\n\tgateway: string;\n}\n\nexport function createRadiusOAuth(options: RadiusOAuthOptions): OAuthAuth {\n\tconst gateway = normalizeRadiusGatewayUrl(options.gateway);\n\n\treturn {\n\t\tname: options.name,\n\n\t\tasync login(interaction): Promise<OAuthCredential> {\n\t\t\tconst loginMethod = await interaction.prompt({\n\t\t\t\ttype: \"select\",\n\t\t\t\tmessage: `Sign in to ${options.name}:`,\n\t\t\t\toptions: [\n\t\t\t\t\t{ id: LOGIN_METHOD_BROWSER, label: \"Sign in with browser (recommended)\" },\n\t\t\t\t\t{\n\t\t\t\t\t\tid: LOGIN_METHOD_DEVICE_CODE,\n\t\t\t\t\t\tlabel: \"Sign in with device code (when signing in from another device)\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (loginMethod === LOGIN_METHOD_DEVICE_CODE) {\n\t\t\t\treturn loginWithDeviceCode(gateway, interaction);\n\t\t\t}\n\t\t\tif (loginMethod === LOGIN_METHOD_BROWSER) {\n\t\t\t\tconst discovery = await loadRadiusOAuthDiscovery(gateway, interaction.signal);\n\t\t\t\treturn loginWithBrowser(gateway, discovery.authorizationEndpoint, interaction);\n\t\t\t}\n\t\t\tthrow new Error(`Unknown ${options.name} sign-in method: ${loginMethod}`);\n\t\t},\n\n\t\tasync refresh(credential, signal): Promise<OAuthCredential> {\n\t\t\tconst refreshed = await requestOAuthToken(\n\t\t\t\tgateway,\n\t\t\t\tnew URLSearchParams({\n\t\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\t\trefresh_token: credential.refresh,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\treturn refreshed;\n\t\t},\n\n\t\tasync toAuth(credential) {\n\t\t\treturn { apiKey: credential.access };\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"radius.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/radius.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA2UvF,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,SAAS,CA8CxE"}
@@ -1 +1 @@
1
- {"version":3,"file":"radius.js","sourceRoot":"","sources":["../../../src/auth/oauth/radius.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Radius gateway OAuth flow.\n *\n * Radius is a pi-messages gateway. OAuth client APIs live on the configured\n * gateway; only the interactive browser authorization endpoint is discovered.\n * Model catalog loading is owned by the Radius provider.\n *\n * NOTE: This module uses node:http for the OAuth callback server.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { normalizeRadiusGatewayUrl } from \"../../providers/radius-config.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst CALLBACK_HOST = \"127.0.0.1\";\nconst CALLBACK_PORT = 1456;\nconst CALLBACK_PATH = \"/oauth/callback\";\nconst REDIRECT_URI = `http://${CALLBACK_HOST}:${CALLBACK_PORT}${CALLBACK_PATH}`;\nconst TOKEN_EXPIRY_SKEW_MS = 60_000;\nconst LOGIN_METHOD_BROWSER = \"browser\";\nconst LOGIN_METHOD_DEVICE_CODE = \"device-code\";\nconst OAUTH_CLIENT_ID = \"pi-gateway\";\nconst OAUTH_SCOPE = \"gateway offline_access\";\nconst OAUTH_DEVICE_CODE_GRANT_TYPE = \"urn:ietf:params:oauth:grant-type:device_code\";\n\ntype RadiusOAuthDiscovery = {\n\tauthorizationEndpoint: string;\n};\n\ntype DeviceAuthorizationResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\texpires_in: number;\n\tinterval?: number;\n};\n\nasync function loadRadiusOAuthDiscovery(gateway: string, signal: AbortSignal): Promise<RadiusOAuthDiscovery> {\n\tconst response = await fetch(new URL(\"/v1/oauth\", gateway), {\n\t\theaders: { accept: \"application/json\" },\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Could not load Radius OAuth config from ${gateway}: ${response.status} ${await response.text()}`,\n\t\t);\n\t}\n\n\tconst discovery = (await response.json()) as Partial<RadiusOAuthDiscovery>;\n\tif (typeof discovery.authorizationEndpoint !== \"string\") {\n\t\tthrow new Error(`Invalid Radius OAuth config from ${gateway}`);\n\t}\n\treturn { authorizationEndpoint: discovery.authorizationEndpoint };\n}\n\nclass OAuthResponseError extends Error {\n\treadonly status: number;\n\treadonly oauthError?: string;\n\n\tconstructor(status: number, oauthError: string | undefined, description: string | undefined, message: string) {\n\t\tconst detail = oauthError\n\t\t\t? description\n\t\t\t\t? `${oauthError}: ${description}`\n\t\t\t\t: oauthError\n\t\t\t: description || String(status);\n\t\tsuper(`${message}: ${detail}`);\n\t\tthis.status = status;\n\t\tthis.oauthError = oauthError;\n\t}\n}\n\nasync function readOAuthResponseError(response: Response, message: string): Promise<OAuthResponseError> {\n\tconst text = await response.text().catch(() => \"\");\n\tlet oauthError: string | undefined;\n\tlet description: string | undefined;\n\n\tif (text) {\n\t\ttry {\n\t\t\tconst data = JSON.parse(text) as { error?: unknown; error_description?: unknown };\n\t\t\toauthError = typeof data.error === \"string\" ? data.error : undefined;\n\t\t\tdescription = typeof data.error_description === \"string\" ? data.error_description : undefined;\n\t\t} catch {\n\t\t\tdescription = text;\n\t\t}\n\t}\n\n\treturn new OAuthResponseError(response.status, oauthError, description, message);\n}\n\nasync function requestOAuthToken(\n\tgateway: string,\n\tbody: URLSearchParams,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(new URL(\"/v1/oauth/token\", gateway), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody,\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tif (!response.ok) {\n\t\tthrow await readOAuthResponseError(response, \"Radius OAuth token request failed\");\n\t}\n\n\tconst data = (await response.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t\tscope?: string;\n\t};\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: data.access_token,\n\t\trefresh: data.refresh_token,\n\t\texpires: Date.now() + data.expires_in * 1000 - TOKEN_EXPIRY_SKEW_MS,\n\t\tscope: data.scope,\n\t};\n}\n\ntype OAuthCallbackServer = {\n\twaitForCode(): Promise<string | null>;\n\tclose(): void;\n};\n\nfunction startOAuthCallbackServer(expectedState: string, signal: AbortSignal): Promise<OAuthCallbackServer> {\n\tif (!_http) {\n\t\tthrow new Error(\"Radius OAuth is only available in Node.js environments\");\n\t}\n\n\tlet settle: (code: string | null) => void = () => {};\n\tlet settled = false;\n\tconst wait = new Promise<string | null>((resolve) => {\n\t\tsettle = resolve;\n\t});\n\tconst finish = (code: string | null) => {\n\t\tif (settled) {\n\t\t\treturn;\n\t\t}\n\t\tsettled = true;\n\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\tsettle(code);\n\t};\n\tconst onAbort = () => finish(null);\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\n\tconst sendPage = (response: import(\"node:http\").ServerResponse, status: number, html: string) => {\n\t\tresponse.statusCode = status;\n\t\tresponse.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n\t\tresponse.end(html);\n\t};\n\n\tconst server = _http.createServer((request, response) => {\n\t\tconst url = new URL(request.url ?? \"/\", REDIRECT_URI);\n\t\tif (url.pathname !== CALLBACK_PATH) {\n\t\t\tsendPage(response, 404, oauthErrorHtml(\"Callback route not found.\"));\n\t\t\treturn;\n\t\t}\n\t\tif (url.searchParams.get(\"state\") !== expectedState) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(\"OAuth state mismatch.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst error = url.searchParams.get(\"error\");\n\t\tif (error) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(url.searchParams.get(\"error_description\") ?? error));\n\t\t\tfinish(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst code = url.searchParams.get(\"code\");\n\t\tif (!code) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(\"Missing authorization code.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tsendPage(response, 200, oauthSuccessHtml(\"Signed in to Radius. You may now close this page.\"));\n\t\tfinish(code);\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(CALLBACK_PORT, CALLBACK_HOST, () => {\n\t\t\t\tresolve({\n\t\t\t\t\twaitForCode: () => wait,\n\t\t\t\t\tclose: () => {\n\t\t\t\t\t\tfinish(null);\n\t\t\t\t\t\tserver.close();\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t})\n\t\t\t.once(\"error\", () => {\n\t\t\t\tfinish(null);\n\t\t\t\tresolve({ waitForCode: async () => null, close: () => {} });\n\t\t\t});\n\t});\n}\n\nasync function loginWithBrowser(\n\tgateway: string,\n\tauthorizationEndpoint: string,\n\tinteraction: ProviderAuthInteraction,\n): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = crypto.randomUUID();\n\tconst authorizeUrl = new URL(authorizationEndpoint);\n\tauthorizeUrl.search = new URLSearchParams({\n\t\tresponse_type: \"code\",\n\t\tclient_id: OAUTH_CLIENT_ID,\n\t\tredirect_uri: REDIRECT_URI,\n\t\tscope: OAUTH_SCOPE,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\thandoff: \"url\",\n\t\tstate,\n\t}).toString();\n\n\tconst callbackServer = await startOAuthCallbackServer(state, interaction.signal);\n\tinteraction.notify({ type: \"progress\", message: `Listening for OAuth callback on ${REDIRECT_URI}` });\n\tinteraction.notify({\n\t\ttype: \"auth_url\",\n\t\turl: authorizeUrl.toString(),\n\t\tinstructions: \"Continue in your browser.\",\n\t});\n\n\ttry {\n\t\tconst code = await callbackServer.waitForCode();\n\t\tif (!code) {\n\t\t\tif (interaction.signal.aborted) {\n\t\t\t\tthrow new Error(\"Login cancelled\");\n\t\t\t}\n\t\t\tthrow new Error(\"OAuth callback did not complete.\");\n\t\t}\n\t\treturn await requestOAuthToken(\n\t\t\tgateway,\n\t\t\tnew URLSearchParams({\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\tredirect_uri: REDIRECT_URI,\n\t\t\t\tcode,\n\t\t\t\tcode_verifier: verifier,\n\t\t\t}),\n\t\t\tinteraction.signal,\n\t\t);\n\t} finally {\n\t\tcallbackServer.close();\n\t}\n}\n\nasync function requestDeviceAuthorization(gateway: string, signal: AbortSignal): Promise<DeviceAuthorizationResponse> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(new URL(\"/v1/oauth/device\", gateway), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody: new URLSearchParams({ client_id: OAUTH_CLIENT_ID, scope: OAUTH_SCOPE }),\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tif (!response.ok) {\n\t\tthrow await readOAuthResponseError(response, \"Radius OAuth device authorization failed\");\n\t}\n\n\tconst data = (await response.json()) as Partial<DeviceAuthorizationResponse>;\n\tif (!data.device_code || !data.user_code || !data.verification_uri || !data.expires_in) {\n\t\tthrow new Error(\"Radius OAuth device authorization response is missing required fields\");\n\t}\n\n\treturn {\n\t\tdevice_code: data.device_code,\n\t\tuser_code: data.user_code,\n\t\tverification_uri: data.verification_uri,\n\t\texpires_in: data.expires_in,\n\t\tinterval: data.interval,\n\t};\n}\n\nasync function loginWithDeviceCode(gateway: string, interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst device = await requestDeviceAuthorization(gateway, 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\treturn pollOAuthDeviceCodeFlow<OAuthCredential>({\n\t\tintervalSeconds: device.interval,\n\t\texpiresInSeconds: device.expires_in,\n\t\tsignal: interaction.signal,\n\t\tpoll: async () => {\n\t\t\ttry {\n\t\t\t\tconst credentials = await requestOAuthToken(\n\t\t\t\t\tgateway,\n\t\t\t\t\tnew URLSearchParams({\n\t\t\t\t\t\tgrant_type: OAUTH_DEVICE_CODE_GRANT_TYPE,\n\t\t\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\t\t\tdevice_code: device.device_code,\n\t\t\t\t\t}),\n\t\t\t\t\tinteraction.signal,\n\t\t\t\t);\n\t\t\t\treturn { status: \"complete\", value: credentials };\n\t\t\t} catch (error) {\n\t\t\t\tif (!(error instanceof OAuthResponseError)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tswitch (error.oauthError) {\n\t\t\t\t\tcase \"authorization_pending\":\n\t\t\t\t\t\treturn { status: \"pending\" };\n\t\t\t\t\tcase \"slow_down\":\n\t\t\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t\t\tcase \"expired_token\":\n\t\t\t\t\t\treturn { status: \"failed\", message: \"Device authorization expired.\" };\n\t\t\t\t\tcase \"access_denied\":\n\t\t\t\t\t\treturn { status: \"failed\", message: \"Device authorization was denied.\" };\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n}\n\nexport interface RadiusOAuthOptions {\n\tname: string;\n\tgateway: string;\n}\n\nexport function createRadiusOAuth(options: RadiusOAuthOptions): OAuthAuth {\n\tconst gateway = normalizeRadiusGatewayUrl(options.gateway);\n\n\treturn {\n\t\tname: options.name,\n\n\t\tasync login(interaction): Promise<OAuthCredential> {\n\t\t\tconst loginMethod = await interaction.prompt({\n\t\t\t\ttype: \"select\",\n\t\t\t\tmessage: `Sign in to ${options.name}:`,\n\t\t\t\toptions: [\n\t\t\t\t\t{ id: LOGIN_METHOD_BROWSER, label: \"Sign in with browser (recommended)\" },\n\t\t\t\t\t{\n\t\t\t\t\t\tid: LOGIN_METHOD_DEVICE_CODE,\n\t\t\t\t\t\tlabel: \"Sign in with device code (when signing in from another device)\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (loginMethod === LOGIN_METHOD_DEVICE_CODE) {\n\t\t\t\treturn loginWithDeviceCode(gateway, interaction);\n\t\t\t}\n\t\t\tif (loginMethod === LOGIN_METHOD_BROWSER) {\n\t\t\t\tconst discovery = await loadRadiusOAuthDiscovery(gateway, interaction.signal);\n\t\t\t\treturn loginWithBrowser(gateway, discovery.authorizationEndpoint, interaction);\n\t\t\t}\n\t\t\tthrow new Error(`Unknown ${options.name} sign-in method: ${loginMethod}`);\n\t\t},\n\n\t\tasync refresh(credential, signal): Promise<OAuthCredential> {\n\t\t\tconst refreshed = await requestOAuthToken(\n\t\t\t\tgateway,\n\t\t\t\tnew URLSearchParams({\n\t\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\t\trefresh_token: credential.refresh,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\treturn refreshed;\n\t\t},\n\n\t\tasync toAuth(credential) {\n\t\t\treturn { apiKey: credential.access };\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"radius.js","sourceRoot":"","sources":["../../../src/auth/oauth/radius.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Radius gateway OAuth flow.\n *\n * Radius is a pi-messages gateway. OAuth client APIs live on the configured\n * gateway; only the interactive browser authorization endpoint is discovered.\n * Model catalog loading is owned by the Radius provider.\n *\n * NOTE: This module uses node:http for the OAuth callback server.\n * It is only intended for CLI use, not browser environments.\n */\n\n// NEVER convert to top-level imports - breaks browser/Vite builds\nlet _http: typeof import(\"node:http\") | null = null;\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\timport(\"node:http\").then((m) => {\n\t\t_http = m;\n\t});\n}\n\nimport { normalizeRadiusGatewayUrl } from \"../../providers/radius-config.ts\";\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst CALLBACK_HOST = \"127.0.0.1\";\nconst CALLBACK_PORT = 1456;\nconst CALLBACK_PATH = \"/oauth/callback\";\nconst REDIRECT_URI = `http://${CALLBACK_HOST}:${CALLBACK_PORT}${CALLBACK_PATH}`;\nconst TOKEN_EXPIRY_SKEW_MS = 60_000;\nconst LOGIN_METHOD_BROWSER = \"browser\";\nconst LOGIN_METHOD_DEVICE_CODE = \"device-code\";\nconst OAUTH_CLIENT_ID = \"pi-gateway\";\nconst OAUTH_SCOPE = \"gateway offline_access\";\nconst OAUTH_DEVICE_CODE_GRANT_TYPE = \"urn:ietf:params:oauth:grant-type:device_code\";\n\ntype RadiusOAuthDiscovery = {\n\tauthorizationEndpoint: string;\n};\n\ntype DeviceAuthorizationResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\texpires_in: number;\n\tinterval?: number;\n};\n\nasync function loadRadiusOAuthDiscovery(gateway: string, signal: AbortSignal): Promise<RadiusOAuthDiscovery> {\n\tconst response = await fetch(new URL(\"/v1/oauth\", gateway), {\n\t\theaders: { accept: \"application/json\" },\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Could not load Radius OAuth config from ${gateway}: ${response.status} ${await response.text()}`,\n\t\t);\n\t}\n\n\tconst discovery = (await response.json()) as Partial<RadiusOAuthDiscovery>;\n\tif (typeof discovery.authorizationEndpoint !== \"string\") {\n\t\tthrow new Error(`Invalid Radius OAuth config from ${gateway}`);\n\t}\n\treturn { authorizationEndpoint: discovery.authorizationEndpoint };\n}\n\nclass OAuthResponseError extends Error {\n\treadonly status: number;\n\treadonly oauthError?: string;\n\n\tconstructor(status: number, oauthError: string | undefined, description: string | undefined, message: string) {\n\t\tconst detail = oauthError\n\t\t\t? description\n\t\t\t\t? `${oauthError}: ${description}`\n\t\t\t\t: oauthError\n\t\t\t: description || String(status);\n\t\tsuper(`${message}: ${detail}`);\n\t\tthis.status = status;\n\t\tthis.oauthError = oauthError;\n\t}\n}\n\nasync function readOAuthResponseError(response: Response, message: string): Promise<OAuthResponseError> {\n\tconst text = await response.text().catch(() => \"\");\n\tlet oauthError: string | undefined;\n\tlet description: string | undefined;\n\n\tif (text) {\n\t\ttry {\n\t\t\tconst data = JSON.parse(text) as { error?: unknown; error_description?: unknown };\n\t\t\toauthError = typeof data.error === \"string\" ? data.error : undefined;\n\t\t\tdescription = typeof data.error_description === \"string\" ? data.error_description : undefined;\n\t\t} catch {\n\t\t\tdescription = text;\n\t\t}\n\t}\n\n\treturn new OAuthResponseError(response.status, oauthError, description, message);\n}\n\nasync function requestOAuthToken(\n\tgateway: string,\n\tbody: URLSearchParams,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(new URL(\"/v1/oauth/token\", gateway), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody,\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tif (!response.ok) {\n\t\tthrow await readOAuthResponseError(response, \"Radius OAuth token request failed\");\n\t}\n\n\tconst data = (await response.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t\tscope?: string;\n\t};\n\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: data.access_token,\n\t\trefresh: data.refresh_token,\n\t\texpires: Date.now() + data.expires_in * 1000 - TOKEN_EXPIRY_SKEW_MS,\n\t\tscope: data.scope,\n\t};\n}\n\ntype OAuthCallbackServer = {\n\twaitForCode(): Promise<string | null>;\n\tclose(): void;\n};\n\nfunction startOAuthCallbackServer(expectedState: string, signal: AbortSignal): Promise<OAuthCallbackServer> {\n\tif (!_http) {\n\t\tthrow new Error(\"Radius OAuth is only available in Node.js environments\");\n\t}\n\n\tlet settle: (code: string | null) => void = () => {};\n\tlet settled = false;\n\tconst wait = new Promise<string | null>((resolve) => {\n\t\tsettle = resolve;\n\t});\n\tconst finish = (code: string | null) => {\n\t\tif (settled) {\n\t\t\treturn;\n\t\t}\n\t\tsettled = true;\n\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\tsettle(code);\n\t};\n\tconst onAbort = () => finish(null);\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\n\tconst sendPage = (response: import(\"node:http\").ServerResponse, status: number, html: string) => {\n\t\tresponse.statusCode = status;\n\t\tresponse.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n\t\tresponse.end(html);\n\t};\n\n\tconst server = _http.createServer((request, response) => {\n\t\tconst url = new URL(request.url ?? \"/\", REDIRECT_URI);\n\t\tif (url.pathname !== CALLBACK_PATH) {\n\t\t\tsendPage(response, 404, oauthErrorHtml(\"Callback route not found.\"));\n\t\t\treturn;\n\t\t}\n\t\tif (url.searchParams.get(\"state\") !== expectedState) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(\"OAuth state mismatch.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst error = url.searchParams.get(\"error\");\n\t\tif (error) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(url.searchParams.get(\"error_description\") ?? error));\n\t\t\tfinish(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst code = url.searchParams.get(\"code\");\n\t\tif (!code) {\n\t\t\tsendPage(response, 400, oauthErrorHtml(\"Missing authorization code.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tsendPage(response, 200, oauthSuccessHtml(\"Signed in to Radius. You may now close this page.\"));\n\t\tfinish(code);\n\t});\n\n\treturn new Promise((resolve) => {\n\t\tserver\n\t\t\t.listen(CALLBACK_PORT, CALLBACK_HOST, () => {\n\t\t\t\tresolve({\n\t\t\t\t\twaitForCode: () => wait,\n\t\t\t\t\tclose: () => {\n\t\t\t\t\t\tfinish(null);\n\t\t\t\t\t\tserver.close();\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t})\n\t\t\t.once(\"error\", () => {\n\t\t\t\tfinish(null);\n\t\t\t\tresolve({ waitForCode: async () => null, close: () => {} });\n\t\t\t});\n\t});\n}\n\nasync function loginWithBrowser(\n\tgateway: string,\n\tauthorizationEndpoint: string,\n\tinteraction: ProviderAuthInteraction,\n): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = crypto.randomUUID();\n\tconst authorizeUrl = new URL(authorizationEndpoint);\n\tauthorizeUrl.search = new URLSearchParams({\n\t\tresponse_type: \"code\",\n\t\tclient_id: OAUTH_CLIENT_ID,\n\t\tredirect_uri: REDIRECT_URI,\n\t\tscope: OAUTH_SCOPE,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\thandoff: \"url\",\n\t\tstate,\n\t}).toString();\n\n\tconst callbackServer = await startOAuthCallbackServer(state, interaction.signal);\n\tinteraction.notify({ type: \"progress\", message: `Listening for OAuth callback on ${REDIRECT_URI}` });\n\tinteraction.notify({\n\t\ttype: \"auth_url\",\n\t\turl: authorizeUrl.toString(),\n\t\tinstructions: \"Continue in your browser.\",\n\t});\n\n\ttry {\n\t\tconst code = await callbackServer.waitForCode();\n\t\tif (!code) {\n\t\t\tif (interaction.signal.aborted) {\n\t\t\t\tthrow new Error(\"Login cancelled\");\n\t\t\t}\n\t\t\tthrow new Error(\"OAuth callback did not complete.\");\n\t\t}\n\t\treturn await requestOAuthToken(\n\t\t\tgateway,\n\t\t\tnew URLSearchParams({\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\tredirect_uri: REDIRECT_URI,\n\t\t\t\tcode,\n\t\t\t\tcode_verifier: verifier,\n\t\t\t}),\n\t\t\tinteraction.signal,\n\t\t);\n\t} finally {\n\t\tcallbackServer.close();\n\t}\n}\n\nasync function requestDeviceAuthorization(gateway: string, signal: AbortSignal): Promise<DeviceAuthorizationResponse> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(new URL(\"/v1/oauth/device\", gateway), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/x-www-form-urlencoded\" },\n\t\t\tbody: new URLSearchParams({ client_id: OAUTH_CLIENT_ID, scope: OAUTH_SCOPE }),\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tif (!response.ok) {\n\t\tthrow await readOAuthResponseError(response, \"Radius OAuth device authorization failed\");\n\t}\n\n\tconst data = (await response.json()) as Partial<DeviceAuthorizationResponse>;\n\tif (!data.device_code || !data.user_code || !data.verification_uri || !data.expires_in) {\n\t\tthrow new Error(\"Radius OAuth device authorization response is missing required fields\");\n\t}\n\n\treturn {\n\t\tdevice_code: data.device_code,\n\t\tuser_code: data.user_code,\n\t\tverification_uri: data.verification_uri,\n\t\texpires_in: data.expires_in,\n\t\tinterval: data.interval,\n\t};\n}\n\nasync function loginWithDeviceCode(gateway: string, interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst device = await requestDeviceAuthorization(gateway, 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\treturn pollOAuthDeviceCodeFlow<OAuthCredential>({\n\t\tintervalSeconds: device.interval,\n\t\texpiresInSeconds: device.expires_in,\n\t\tsignal: interaction.signal,\n\t\tpoll: async () => {\n\t\t\ttry {\n\t\t\t\tconst credentials = await requestOAuthToken(\n\t\t\t\t\tgateway,\n\t\t\t\t\tnew URLSearchParams({\n\t\t\t\t\t\tgrant_type: OAUTH_DEVICE_CODE_GRANT_TYPE,\n\t\t\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\t\t\tdevice_code: device.device_code,\n\t\t\t\t\t}),\n\t\t\t\t\tinteraction.signal,\n\t\t\t\t);\n\t\t\t\treturn { status: \"complete\", value: credentials };\n\t\t\t} catch (error) {\n\t\t\t\tif (!(error instanceof OAuthResponseError)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tswitch (error.oauthError) {\n\t\t\t\t\tcase \"authorization_pending\":\n\t\t\t\t\t\treturn { status: \"pending\" };\n\t\t\t\t\tcase \"slow_down\":\n\t\t\t\t\t\treturn { status: \"slow_down\" };\n\t\t\t\t\tcase \"expired_token\":\n\t\t\t\t\t\treturn { status: \"failed\", message: \"Device authorization expired.\" };\n\t\t\t\t\tcase \"access_denied\":\n\t\t\t\t\t\treturn { status: \"failed\", message: \"Device authorization was denied.\" };\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n}\n\nexport interface RadiusOAuthOptions {\n\tname: string;\n\tgateway: string;\n}\n\nexport function createRadiusOAuth(options: RadiusOAuthOptions): OAuthAuth {\n\tconst gateway = normalizeRadiusGatewayUrl(options.gateway);\n\n\treturn {\n\t\tname: options.name,\n\n\t\tasync login(interaction): Promise<OAuthCredential> {\n\t\t\tconst loginMethod = await interaction.prompt({\n\t\t\t\ttype: \"select\",\n\t\t\t\tmessage: `Sign in to ${options.name}:`,\n\t\t\t\toptions: [\n\t\t\t\t\t{ id: LOGIN_METHOD_BROWSER, label: \"Sign in with browser (recommended)\" },\n\t\t\t\t\t{\n\t\t\t\t\t\tid: LOGIN_METHOD_DEVICE_CODE,\n\t\t\t\t\t\tlabel: \"Sign in with device code (when signing in from another device)\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (loginMethod === LOGIN_METHOD_DEVICE_CODE) {\n\t\t\t\treturn loginWithDeviceCode(gateway, interaction);\n\t\t\t}\n\t\t\tif (loginMethod === LOGIN_METHOD_BROWSER) {\n\t\t\t\tconst discovery = await loadRadiusOAuthDiscovery(gateway, interaction.signal);\n\t\t\t\treturn loginWithBrowser(gateway, discovery.authorizationEndpoint, interaction);\n\t\t\t}\n\t\t\tthrow new Error(`Unknown ${options.name} sign-in method: ${loginMethod}`);\n\t\t},\n\n\t\tasync refresh(credential, signal): Promise<OAuthCredential> {\n\t\t\tconst refreshed = await requestOAuthToken(\n\t\t\t\tgateway,\n\t\t\t\tnew URLSearchParams({\n\t\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\t\trefresh_token: credential.refresh,\n\t\t\t\t}),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\treturn refreshed;\n\t\t},\n\n\t\tasync toAuth(credential) {\n\t\t\treturn { apiKey: credential.access };\n\t\t},\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/xai.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AAgOvF,eAAO,MAAM,QAAQ,EAAE,SAUtB,CAAC","sourcesContent":["/**\n * xAI OAuth device-code flow.\n */\n\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\n\nconst XAI_CLIENT_ID = \"b1a00492-073a-47ea-816f-4c329264a828\";\nconst XAI_SCOPE = \"openid profile email offline_access grok-cli:access api:access\";\nconst XAI_DEVICE_CODE_URL = \"https://auth.x.ai/oauth2/device/code\";\nconst XAI_TOKEN_URL = \"https://auth.x.ai/oauth2/token\";\n// Refresh slightly before the reported expiry to avoid using a token that dies mid-request.\nconst REFRESH_SKEW_MS = 5 * 60 * 1000;\nconst DEFAULT_TOKEN_LIFETIME_SECONDS = 3600;\n\ntype JsonObject = Record<string, unknown>;\n\ntype OAuthHttpResponse = {\n\tok: boolean;\n\tstatus: number;\n\tbody: JsonObject;\n};\n\ntype XaiDeviceCode = {\n\tdeviceCode: string;\n\tuserCode: string;\n\tverificationUri: string;\n\tverificationUriComplete?: string;\n\tintervalSeconds?: number;\n\texpiresInSeconds: number;\n};\n\nfunction requiredString(body: JsonObject, field: string): string {\n\tconst value = body[field];\n\tif (typeof value !== \"string\" || value.length === 0) {\n\t\tthrow new Error(`Invalid xAI OAuth response field: ${field}`);\n\t}\n\treturn value;\n}\n\nfunction positiveNumber(body: JsonObject, field: string): number {\n\tconst value = body[field];\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n\t\tthrow new Error(`Invalid xAI OAuth response field: ${field}`);\n\t}\n\treturn value;\n}\n\n// The verification URI is opened in the user's browser; force it to be an https URL\n// so a malicious response cannot make `open` launch something else.\nfunction validateVerificationUri(raw: string): string {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(raw);\n\t} catch {\n\t\tthrow new Error(\"Untrusted verification URI in xAI OAuth response\");\n\t}\n\tif (url.protocol !== \"https:\") {\n\t\tthrow new Error(\"Untrusted verification URI in xAI OAuth response\");\n\t}\n\treturn url.href;\n}\n\nasync function postForm(url: string, fields: Record<string, string>, signal: AbortSignal): Promise<OAuthHttpResponse> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t},\n\t\t\tbody: new URLSearchParams(fields),\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tlet body: JsonObject;\n\ttry {\n\t\tconst parsed = (await response.json()) as unknown;\n\t\tbody = parsed && typeof parsed === \"object\" && !Array.isArray(parsed) ? (parsed as JsonObject) : {};\n\t} catch {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow new Error(`xAI OAuth returned invalid JSON (HTTP ${response.status})`);\n\t}\n\treturn {\n\t\tok: response.ok,\n\t\tstatus: response.status,\n\t\tbody,\n\t};\n}\n\nfunction requestFailure(action: string, response: OAuthHttpResponse): Error {\n\tconst error = typeof response.body.error === \"string\" ? response.body.error : undefined;\n\tconst description =\n\t\ttypeof response.body.error_description === \"string\" ? response.body.error_description : undefined;\n\tconst detail = [error, description].filter(Boolean).join(\": \");\n\treturn new Error(`xAI OAuth ${action} failed (HTTP ${response.status})${detail ? `: ${detail}` : \"\"}`);\n}\n\nfunction parseDeviceCode(body: JsonObject): XaiDeviceCode {\n\t// RFC 8628 allows interval 0 (no minimum wait); fall back to the poller's\n\t// default instead of failing on non-positive or malformed values.\n\tconst interval = body.interval;\n\tconst intervalSeconds =\n\t\ttypeof interval === \"number\" && Number.isFinite(interval) && interval > 0 ? interval : undefined;\n\tconst verificationUriComplete =\n\t\ttypeof body.verification_uri_complete === \"string\" && body.verification_uri_complete.length > 0\n\t\t\t? validateVerificationUri(body.verification_uri_complete)\n\t\t\t: undefined;\n\treturn {\n\t\tdeviceCode: requiredString(body, \"device_code\"),\n\t\tuserCode: requiredString(body, \"user_code\"),\n\t\tverificationUri: validateVerificationUri(requiredString(body, \"verification_uri\")),\n\t\tverificationUriComplete,\n\t\tintervalSeconds,\n\t\texpiresInSeconds: positiveNumber(body, \"expires_in\"),\n\t};\n}\n\nfunction credentialsFromTokenResponse(body: JsonObject, previousRefreshToken?: string): OAuthCredential {\n\tconst access = requiredString(body, \"access_token\");\n\t// xAI may omit refresh_token on refresh when the token is not rotated.\n\tconst refresh =\n\t\tbody.refresh_token === undefined && previousRefreshToken\n\t\t\t? previousRefreshToken\n\t\t\t: requiredString(body, \"refresh_token\");\n\tconst expiresInSeconds =\n\t\tbody.expires_in === undefined ? DEFAULT_TOKEN_LIFETIME_SECONDS : positiveNumber(body, \"expires_in\");\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess,\n\t\trefresh,\n\t\texpires: Date.now() + expiresInSeconds * 1000 - REFRESH_SKEW_MS,\n\t};\n}\n\nasync function requestDeviceCode(signal: AbortSignal): Promise<XaiDeviceCode> {\n\tconst response = await postForm(\n\t\tXAI_DEVICE_CODE_URL,\n\t\t{\n\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\tscope: XAI_SCOPE,\n\t\t\treferrer: \"pi\",\n\t\t},\n\t\tsignal,\n\t);\n\tif (!response.ok) {\n\t\tthrow requestFailure(\"device authorization\", response);\n\t}\n\treturn parseDeviceCode(response.body);\n}\n\nasync function pollForTokens(device: XaiDeviceCode, signal: AbortSignal): Promise<OAuthCredential> {\n\treturn pollOAuthDeviceCodeFlow<OAuthCredential>({\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 postForm(\n\t\t\t\tXAI_TOKEN_URL,\n\t\t\t\t{\n\t\t\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\t\t\tdevice_code: device.deviceCode,\n\t\t\t\t},\n\t\t\t\tsignal,\n\t\t\t);\n\n\t\t\tif (response.ok) {\n\t\t\t\treturn { status: \"complete\", value: credentialsFromTokenResponse(response.body) };\n\t\t\t}\n\n\t\t\tconst error = response.body.error;\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 = response.body.interval;\n\t\t\t\treturn { status: \"slow_down\", intervalSeconds: typeof interval === \"number\" ? interval : undefined };\n\t\t\t}\n\t\t\tif (error === \"access_denied\" || error === \"authorization_denied\") {\n\t\t\t\treturn { status: \"failed\", message: \"xAI device authorization was denied\" };\n\t\t\t}\n\t\t\tif (error === \"expired_token\") {\n\t\t\t\treturn { status: \"failed\", message: \"xAI device code expired\" };\n\t\t\t}\n\t\t\treturn { status: \"failed\", message: requestFailure(\"device token polling\", response).message };\n\t\t},\n\t});\n}\n\nasync function loginXai(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst device = await requestDeviceCode(interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.userCode,\n\t\tverificationUri: device.verificationUriComplete ?? device.verificationUri,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: device.expiresInSeconds,\n\t});\n\treturn pollForTokens(device, interaction.signal);\n}\n\nasync function refreshXaiToken(refreshToken: string, signal: AbortSignal): Promise<OAuthCredential> {\n\tconst response = await postForm(\n\t\tXAI_TOKEN_URL,\n\t\t{\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t},\n\t\tsignal,\n\t);\n\tif (!response.ok) {\n\t\tthrow requestFailure(\"token refresh\", response);\n\t}\n\treturn credentialsFromTokenResponse(response.body, refreshToken);\n}\n\nexport const xaiOAuth: OAuthAuth = {\n\tname: \"xAI (Grok/X subscription)\",\n\tisSubscription: true,\n\tloginLabel: \"Sign in with SuperGrok or X Premium\",\n\tlogin: loginXai,\n\trefresh: (credential, signal) => refreshXaiToken(credential.refresh, signal),\n\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
1
+ {"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/xai.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AAgOvF,eAAO,MAAM,QAAQ,EAAE,SAUtB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"xai.js","sourceRoot":"","sources":["../../../src/auth/oauth/xai.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,MAAM,aAAa,GAAG,sCAAsC,CAAC;AAC7D,MAAM,SAAS,GAAG,gEAAgE,CAAC;AACnF,MAAM,mBAAmB,GAAG,sCAAsC,CAAC;AACnE,MAAM,aAAa,GAAG,gCAAgC,CAAC;AACvD,4FAA4F;AAC5F,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AACtC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AAmB5C,SAAS,cAAc,CAAC,IAAgB,EAAE,KAAa,EAAU;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,cAAc,CAAC,IAAgB,EAAE,KAAa,EAAU;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,oFAAoF;AACpF,oEAAoE;AACpE,SAAS,uBAAuB,CAAC,GAAW,EAAU;IACrD,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACJ,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC;AAAA,CAChB;AAED,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,MAA8B,EAAE,MAAmB,EAA8B;IACrH,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACJ,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACR,MAAM,EAAE,kBAAkB;gBAC1B,cAAc,EAAE,mCAAmC;aACnD;YACD,IAAI,EAAE,IAAI,eAAe,CAAC,MAAM,CAAC;YACjC,MAAM;SACN,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,KAAK,CAAC;IACb,CAAC;IAED,IAAI,IAAgB,CAAC;IACrB,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAY,CAAC;QAClD,IAAI,GAAG,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;IACrG,CAAC;IAAC,MAAM,CAAC;QACR,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO;QACN,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,IAAI;KACJ,CAAC;AAAA,CACF;AAED,SAAS,cAAc,CAAC,MAAc,EAAE,QAA2B,EAAS;IAC3E,MAAM,KAAK,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACxF,MAAM,WAAW,GAChB,OAAO,QAAQ,CAAC,IAAI,CAAC,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;IACnG,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,OAAO,IAAI,KAAK,CAAC,aAAa,MAAM,iBAAiB,QAAQ,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAAA,CACvG;AAED,SAAS,eAAe,CAAC,IAAgB,EAAiB;IACzD,0EAA0E;IAC1E,kEAAkE;IAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,MAAM,eAAe,GACpB,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAClG,MAAM,uBAAuB,GAC5B,OAAO,IAAI,CAAC,yBAAyB,KAAK,QAAQ,IAAI,IAAI,CAAC,yBAAyB,CAAC,MAAM,GAAG,CAAC;QAC9F,CAAC,CAAC,uBAAuB,CAAC,IAAI,CAAC,yBAAyB,CAAC;QACzD,CAAC,CAAC,SAAS,CAAC;IACd,OAAO;QACN,UAAU,EAAE,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC;QAC/C,QAAQ,EAAE,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC;QAC3C,eAAe,EAAE,uBAAuB,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;QAClF,uBAAuB;QACvB,eAAe;QACf,gBAAgB,EAAE,cAAc,CAAC,IAAI,EAAE,YAAY,CAAC;KACpD,CAAC;AAAA,CACF;AAED,SAAS,4BAA4B,CAAC,IAAgB,EAAE,oBAA6B,EAAmB;IACvG,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IACpD,uEAAuE;IACvE,MAAM,OAAO,GACZ,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,oBAAoB;QACvD,CAAC,CAAC,oBAAoB;QACtB,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IAC1C,MAAM,gBAAgB,GACrB,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACrG,OAAO;QACN,IAAI,EAAE,OAAO;QACb,MAAM;QACN,OAAO;QACP,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,GAAG,IAAI,GAAG,eAAe;KAC/D,CAAC;AAAA,CACF;AAED,KAAK,UAAU,iBAAiB,CAAC,MAAmB,EAA0B;IAC7E,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAC9B,mBAAmB,EACnB;QACC,SAAS,EAAE,aAAa;QACxB,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE,IAAI;KACd,EACD,MAAM,CACN,CAAC;IACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,cAAc,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAAA,CACtC;AAED,KAAK,UAAU,aAAa,CAAC,MAAqB,EAAE,MAAmB,EAA4B;IAClG,OAAO,uBAAuB,CAAkB;QAC/C,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,mBAAmB,EAAE,IAAI;QACzB,MAAM;QACN,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAC9B,aAAa,EACb;gBACC,UAAU,EAAE,8CAA8C;gBAC1D,SAAS,EAAE,aAAa;gBACxB,WAAW,EAAE,MAAM,CAAC,UAAU;aAC9B,EACD,MAAM,CACN,CAAC;YAEF,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,4BAA4B,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACnF,CAAC;YAED,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;YAClC,IAAI,KAAK,KAAK,uBAAuB,EAAE,CAAC;gBACvC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAC9B,CAAC;YACD,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;gBAC3B,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACxC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;YACtG,CAAC;YACD,IAAI,KAAK,KAAK,eAAe,IAAI,KAAK,KAAK,sBAAsB,EAAE,CAAC;gBACnE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC;YAC7E,CAAC;YACD,IAAI,KAAK,KAAK,eAAe,EAAE,CAAC;gBAC/B,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;YACjE,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QAAA,CAC/F;KACD,CAAC,CAAC;AAAA,CACH;AAED,KAAK,UAAU,QAAQ,CAAC,WAAoC,EAA4B;IACvF,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3D,WAAW,CAAC,MAAM,CAAC;QAClB,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,eAAe,EAAE,MAAM,CAAC,uBAAuB,IAAI,MAAM,CAAC,eAAe;QACzE,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;KACzC,CAAC,CAAC;IACH,OAAO,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;AAAA,CACjD;AAED,KAAK,UAAU,eAAe,CAAC,YAAoB,EAAE,MAAmB,EAA4B;IACnG,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAC9B,aAAa,EACb;QACC,UAAU,EAAE,eAAe;QAC3B,SAAS,EAAE,aAAa;QACxB,aAAa,EAAE,YAAY;KAC3B,EACD,MAAM,CACN,CAAC;IACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,cAAc,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,4BAA4B,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;AAAA,CACjE;AAED,MAAM,CAAC,MAAM,QAAQ,GAAc;IAClC,IAAI,EAAE,2BAA2B;IACjC,cAAc,EAAE,IAAI;IACpB,UAAU,EAAE,qCAAqC;IACjD,KAAK,EAAE,QAAQ;IACf,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC;IAE5E,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE;QACxB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;IAAA,CACrC;CACD,CAAC","sourcesContent":["/**\n * xAI OAuth device-code flow.\n */\n\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\n\nconst XAI_CLIENT_ID = \"b1a00492-073a-47ea-816f-4c329264a828\";\nconst XAI_SCOPE = \"openid profile email offline_access grok-cli:access api:access\";\nconst XAI_DEVICE_CODE_URL = \"https://auth.x.ai/oauth2/device/code\";\nconst XAI_TOKEN_URL = \"https://auth.x.ai/oauth2/token\";\n// Refresh slightly before the reported expiry to avoid using a token that dies mid-request.\nconst REFRESH_SKEW_MS = 5 * 60 * 1000;\nconst DEFAULT_TOKEN_LIFETIME_SECONDS = 3600;\n\ntype JsonObject = Record<string, unknown>;\n\ntype OAuthHttpResponse = {\n\tok: boolean;\n\tstatus: number;\n\tbody: JsonObject;\n};\n\ntype XaiDeviceCode = {\n\tdeviceCode: string;\n\tuserCode: string;\n\tverificationUri: string;\n\tverificationUriComplete?: string;\n\tintervalSeconds?: number;\n\texpiresInSeconds: number;\n};\n\nfunction requiredString(body: JsonObject, field: string): string {\n\tconst value = body[field];\n\tif (typeof value !== \"string\" || value.length === 0) {\n\t\tthrow new Error(`Invalid xAI OAuth response field: ${field}`);\n\t}\n\treturn value;\n}\n\nfunction positiveNumber(body: JsonObject, field: string): number {\n\tconst value = body[field];\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n\t\tthrow new Error(`Invalid xAI OAuth response field: ${field}`);\n\t}\n\treturn value;\n}\n\n// The verification URI is opened in the user's browser; force it to be an https URL\n// so a malicious response cannot make `open` launch something else.\nfunction validateVerificationUri(raw: string): string {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(raw);\n\t} catch {\n\t\tthrow new Error(\"Untrusted verification URI in xAI OAuth response\");\n\t}\n\tif (url.protocol !== \"https:\") {\n\t\tthrow new Error(\"Untrusted verification URI in xAI OAuth response\");\n\t}\n\treturn url.href;\n}\n\nasync function postForm(url: string, fields: Record<string, string>, signal: AbortSignal): Promise<OAuthHttpResponse> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t},\n\t\t\tbody: new URLSearchParams(fields),\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tlet body: JsonObject;\n\ttry {\n\t\tconst parsed = (await response.json()) as unknown;\n\t\tbody = parsed && typeof parsed === \"object\" && !Array.isArray(parsed) ? (parsed as JsonObject) : {};\n\t} catch {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow new Error(`xAI OAuth returned invalid JSON (HTTP ${response.status})`);\n\t}\n\treturn {\n\t\tok: response.ok,\n\t\tstatus: response.status,\n\t\tbody,\n\t};\n}\n\nfunction requestFailure(action: string, response: OAuthHttpResponse): Error {\n\tconst error = typeof response.body.error === \"string\" ? response.body.error : undefined;\n\tconst description =\n\t\ttypeof response.body.error_description === \"string\" ? response.body.error_description : undefined;\n\tconst detail = [error, description].filter(Boolean).join(\": \");\n\treturn new Error(`xAI OAuth ${action} failed (HTTP ${response.status})${detail ? `: ${detail}` : \"\"}`);\n}\n\nfunction parseDeviceCode(body: JsonObject): XaiDeviceCode {\n\t// RFC 8628 allows interval 0 (no minimum wait); fall back to the poller's\n\t// default instead of failing on non-positive or malformed values.\n\tconst interval = body.interval;\n\tconst intervalSeconds =\n\t\ttypeof interval === \"number\" && Number.isFinite(interval) && interval > 0 ? interval : undefined;\n\tconst verificationUriComplete =\n\t\ttypeof body.verification_uri_complete === \"string\" && body.verification_uri_complete.length > 0\n\t\t\t? validateVerificationUri(body.verification_uri_complete)\n\t\t\t: undefined;\n\treturn {\n\t\tdeviceCode: requiredString(body, \"device_code\"),\n\t\tuserCode: requiredString(body, \"user_code\"),\n\t\tverificationUri: validateVerificationUri(requiredString(body, \"verification_uri\")),\n\t\tverificationUriComplete,\n\t\tintervalSeconds,\n\t\texpiresInSeconds: positiveNumber(body, \"expires_in\"),\n\t};\n}\n\nfunction credentialsFromTokenResponse(body: JsonObject, previousRefreshToken?: string): OAuthCredential {\n\tconst access = requiredString(body, \"access_token\");\n\t// xAI may omit refresh_token on refresh when the token is not rotated.\n\tconst refresh =\n\t\tbody.refresh_token === undefined && previousRefreshToken\n\t\t\t? previousRefreshToken\n\t\t\t: requiredString(body, \"refresh_token\");\n\tconst expiresInSeconds =\n\t\tbody.expires_in === undefined ? DEFAULT_TOKEN_LIFETIME_SECONDS : positiveNumber(body, \"expires_in\");\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess,\n\t\trefresh,\n\t\texpires: Date.now() + expiresInSeconds * 1000 - REFRESH_SKEW_MS,\n\t};\n}\n\nasync function requestDeviceCode(signal: AbortSignal): Promise<XaiDeviceCode> {\n\tconst response = await postForm(\n\t\tXAI_DEVICE_CODE_URL,\n\t\t{\n\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\tscope: XAI_SCOPE,\n\t\t\treferrer: \"pi\",\n\t\t},\n\t\tsignal,\n\t);\n\tif (!response.ok) {\n\t\tthrow requestFailure(\"device authorization\", response);\n\t}\n\treturn parseDeviceCode(response.body);\n}\n\nasync function pollForTokens(device: XaiDeviceCode, signal: AbortSignal): Promise<OAuthCredential> {\n\treturn pollOAuthDeviceCodeFlow<OAuthCredential>({\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 postForm(\n\t\t\t\tXAI_TOKEN_URL,\n\t\t\t\t{\n\t\t\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\t\t\tdevice_code: device.deviceCode,\n\t\t\t\t},\n\t\t\t\tsignal,\n\t\t\t);\n\n\t\t\tif (response.ok) {\n\t\t\t\treturn { status: \"complete\", value: credentialsFromTokenResponse(response.body) };\n\t\t\t}\n\n\t\t\tconst error = response.body.error;\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 = response.body.interval;\n\t\t\t\treturn { status: \"slow_down\", intervalSeconds: typeof interval === \"number\" ? interval : undefined };\n\t\t\t}\n\t\t\tif (error === \"access_denied\" || error === \"authorization_denied\") {\n\t\t\t\treturn { status: \"failed\", message: \"xAI device authorization was denied\" };\n\t\t\t}\n\t\t\tif (error === \"expired_token\") {\n\t\t\t\treturn { status: \"failed\", message: \"xAI device code expired\" };\n\t\t\t}\n\t\t\treturn { status: \"failed\", message: requestFailure(\"device token polling\", response).message };\n\t\t},\n\t});\n}\n\nasync function loginXai(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst device = await requestDeviceCode(interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.userCode,\n\t\tverificationUri: device.verificationUriComplete ?? device.verificationUri,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: device.expiresInSeconds,\n\t});\n\treturn pollForTokens(device, interaction.signal);\n}\n\nasync function refreshXaiToken(refreshToken: string, signal: AbortSignal): Promise<OAuthCredential> {\n\tconst response = await postForm(\n\t\tXAI_TOKEN_URL,\n\t\t{\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t},\n\t\tsignal,\n\t);\n\tif (!response.ok) {\n\t\tthrow requestFailure(\"token refresh\", response);\n\t}\n\treturn credentialsFromTokenResponse(response.body, refreshToken);\n}\n\nexport const xaiOAuth: OAuthAuth = {\n\tname: \"xAI (Grok/X subscription)\",\n\tisSubscription: true,\n\tloginLabel: \"Sign in with SuperGrok or X Premium\",\n\tlogin: loginXai,\n\trefresh: (credential, signal) => refreshXaiToken(credential.refresh, signal),\n\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
1
+ {"version":3,"file":"xai.js","sourceRoot":"","sources":["../../../src/auth/oauth/xai.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * xAI OAuth device-code flow.\n */\n\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\n\nconst XAI_CLIENT_ID = \"b1a00492-073a-47ea-816f-4c329264a828\";\nconst XAI_SCOPE = \"openid profile email offline_access grok-cli:access api:access\";\nconst XAI_DEVICE_CODE_URL = \"https://auth.x.ai/oauth2/device/code\";\nconst XAI_TOKEN_URL = \"https://auth.x.ai/oauth2/token\";\n// Refresh slightly before the reported expiry to avoid using a token that dies mid-request.\nconst REFRESH_SKEW_MS = 5 * 60 * 1000;\nconst DEFAULT_TOKEN_LIFETIME_SECONDS = 3600;\n\ntype JsonObject = Record<string, unknown>;\n\ntype OAuthHttpResponse = {\n\tok: boolean;\n\tstatus: number;\n\tbody: JsonObject;\n};\n\ntype XaiDeviceCode = {\n\tdeviceCode: string;\n\tuserCode: string;\n\tverificationUri: string;\n\tverificationUriComplete?: string;\n\tintervalSeconds?: number;\n\texpiresInSeconds: number;\n};\n\nfunction requiredString(body: JsonObject, field: string): string {\n\tconst value = body[field];\n\tif (typeof value !== \"string\" || value.length === 0) {\n\t\tthrow new Error(`Invalid xAI OAuth response field: ${field}`);\n\t}\n\treturn value;\n}\n\nfunction positiveNumber(body: JsonObject, field: string): number {\n\tconst value = body[field];\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n\t\tthrow new Error(`Invalid xAI OAuth response field: ${field}`);\n\t}\n\treturn value;\n}\n\n// The verification URI is opened in the user's browser; force it to be an https URL\n// so a malicious response cannot make `open` launch something else.\nfunction validateVerificationUri(raw: string): string {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(raw);\n\t} catch {\n\t\tthrow new Error(\"Untrusted verification URI in xAI OAuth response\");\n\t}\n\tif (url.protocol !== \"https:\") {\n\t\tthrow new Error(\"Untrusted verification URI in xAI OAuth response\");\n\t}\n\treturn url.href;\n}\n\nasync function postForm(url: string, fields: Record<string, string>, signal: AbortSignal): Promise<OAuthHttpResponse> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t},\n\t\t\tbody: new URLSearchParams(fields),\n\t\t\tsignal,\n\t\t});\n\t} catch (error) {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tlet body: JsonObject;\n\ttry {\n\t\tconst parsed = (await response.json()) as unknown;\n\t\tbody = parsed && typeof parsed === \"object\" && !Array.isArray(parsed) ? (parsed as JsonObject) : {};\n\t} catch {\n\t\tif (signal.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\t\tthrow new Error(`xAI OAuth returned invalid JSON (HTTP ${response.status})`);\n\t}\n\treturn {\n\t\tok: response.ok,\n\t\tstatus: response.status,\n\t\tbody,\n\t};\n}\n\nfunction requestFailure(action: string, response: OAuthHttpResponse): Error {\n\tconst error = typeof response.body.error === \"string\" ? response.body.error : undefined;\n\tconst description =\n\t\ttypeof response.body.error_description === \"string\" ? response.body.error_description : undefined;\n\tconst detail = [error, description].filter(Boolean).join(\": \");\n\treturn new Error(`xAI OAuth ${action} failed (HTTP ${response.status})${detail ? `: ${detail}` : \"\"}`);\n}\n\nfunction parseDeviceCode(body: JsonObject): XaiDeviceCode {\n\t// RFC 8628 allows interval 0 (no minimum wait); fall back to the poller's\n\t// default instead of failing on non-positive or malformed values.\n\tconst interval = body.interval;\n\tconst intervalSeconds =\n\t\ttypeof interval === \"number\" && Number.isFinite(interval) && interval > 0 ? interval : undefined;\n\tconst verificationUriComplete =\n\t\ttypeof body.verification_uri_complete === \"string\" && body.verification_uri_complete.length > 0\n\t\t\t? validateVerificationUri(body.verification_uri_complete)\n\t\t\t: undefined;\n\treturn {\n\t\tdeviceCode: requiredString(body, \"device_code\"),\n\t\tuserCode: requiredString(body, \"user_code\"),\n\t\tverificationUri: validateVerificationUri(requiredString(body, \"verification_uri\")),\n\t\tverificationUriComplete,\n\t\tintervalSeconds,\n\t\texpiresInSeconds: positiveNumber(body, \"expires_in\"),\n\t};\n}\n\nfunction credentialsFromTokenResponse(body: JsonObject, previousRefreshToken?: string): OAuthCredential {\n\tconst access = requiredString(body, \"access_token\");\n\t// xAI may omit refresh_token on refresh when the token is not rotated.\n\tconst refresh =\n\t\tbody.refresh_token === undefined && previousRefreshToken\n\t\t\t? previousRefreshToken\n\t\t\t: requiredString(body, \"refresh_token\");\n\tconst expiresInSeconds =\n\t\tbody.expires_in === undefined ? DEFAULT_TOKEN_LIFETIME_SECONDS : positiveNumber(body, \"expires_in\");\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess,\n\t\trefresh,\n\t\texpires: Date.now() + expiresInSeconds * 1000 - REFRESH_SKEW_MS,\n\t};\n}\n\nasync function requestDeviceCode(signal: AbortSignal): Promise<XaiDeviceCode> {\n\tconst response = await postForm(\n\t\tXAI_DEVICE_CODE_URL,\n\t\t{\n\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\tscope: XAI_SCOPE,\n\t\t\treferrer: \"pi\",\n\t\t},\n\t\tsignal,\n\t);\n\tif (!response.ok) {\n\t\tthrow requestFailure(\"device authorization\", response);\n\t}\n\treturn parseDeviceCode(response.body);\n}\n\nasync function pollForTokens(device: XaiDeviceCode, signal: AbortSignal): Promise<OAuthCredential> {\n\treturn pollOAuthDeviceCodeFlow<OAuthCredential>({\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 postForm(\n\t\t\t\tXAI_TOKEN_URL,\n\t\t\t\t{\n\t\t\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\t\t\tdevice_code: device.deviceCode,\n\t\t\t\t},\n\t\t\t\tsignal,\n\t\t\t);\n\n\t\t\tif (response.ok) {\n\t\t\t\treturn { status: \"complete\", value: credentialsFromTokenResponse(response.body) };\n\t\t\t}\n\n\t\t\tconst error = response.body.error;\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 = response.body.interval;\n\t\t\t\treturn { status: \"slow_down\", intervalSeconds: typeof interval === \"number\" ? interval : undefined };\n\t\t\t}\n\t\t\tif (error === \"access_denied\" || error === \"authorization_denied\") {\n\t\t\t\treturn { status: \"failed\", message: \"xAI device authorization was denied\" };\n\t\t\t}\n\t\t\tif (error === \"expired_token\") {\n\t\t\t\treturn { status: \"failed\", message: \"xAI device code expired\" };\n\t\t\t}\n\t\t\treturn { status: \"failed\", message: requestFailure(\"device token polling\", response).message };\n\t\t},\n\t});\n}\n\nasync function loginXai(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst device = await requestDeviceCode(interaction.signal);\n\tinteraction.notify({\n\t\ttype: \"device_code\",\n\t\tuserCode: device.userCode,\n\t\tverificationUri: device.verificationUriComplete ?? device.verificationUri,\n\t\tintervalSeconds: device.intervalSeconds,\n\t\texpiresInSeconds: device.expiresInSeconds,\n\t});\n\treturn pollForTokens(device, interaction.signal);\n}\n\nasync function refreshXaiToken(refreshToken: string, signal: AbortSignal): Promise<OAuthCredential> {\n\tconst response = await postForm(\n\t\tXAI_TOKEN_URL,\n\t\t{\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: XAI_CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t},\n\t\tsignal,\n\t);\n\tif (!response.ok) {\n\t\tthrow requestFailure(\"token refresh\", response);\n\t}\n\treturn credentialsFromTokenResponse(response.body, refreshToken);\n}\n\nexport const xaiOAuth: OAuthAuth = {\n\tname: \"xAI (Grok/X subscription)\",\n\tisSubscription: true,\n\tloginLabel: \"Sign in with SuperGrok or X Premium\",\n\tlogin: loginXai,\n\trefresh: (credential, signal) => refreshXaiToken(credential.refresh, signal),\n\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}