@aryee337/aery-ai 0.2.27 → 0.2.29

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 (417) hide show
  1. package/CHANGELOG.md +2914 -0
  2. package/README.md +614 -813
  3. package/package.json +140 -105
  4. package/src/api-registry.ts +96 -0
  5. package/src/auth-broker/client.ts +358 -0
  6. package/src/auth-broker/index.ts +5 -0
  7. package/src/auth-broker/refresher.ts +117 -0
  8. package/src/auth-broker/remote-store.ts +623 -0
  9. package/src/auth-broker/server.ts +644 -0
  10. package/src/auth-broker/types.ts +127 -0
  11. package/src/auth-broker/wire-schemas.ts +200 -0
  12. package/src/auth-gateway/http.ts +194 -0
  13. package/src/auth-gateway/index.ts +3 -0
  14. package/src/auth-gateway/server.ts +818 -0
  15. package/src/auth-gateway/types.ts +143 -0
  16. package/src/auth-storage.ts +4422 -0
  17. package/src/index.ts +54 -0
  18. package/src/model-cache.ts +129 -0
  19. package/src/model-manager.ts +469 -0
  20. package/src/model-thinking.ts +782 -0
  21. package/src/models.json +83530 -0
  22. package/src/models.json.d.ts +9 -0
  23. package/src/models.ts +56 -0
  24. package/src/prompts/turn-aborted-guidance.md +4 -0
  25. package/src/provider-details.ts +90 -0
  26. package/src/provider-models/bundled-references.ts +38 -0
  27. package/src/provider-models/descriptors.ts +355 -0
  28. package/src/provider-models/google.ts +88 -0
  29. package/src/provider-models/index.ts +5 -0
  30. package/src/provider-models/ollama.ts +153 -0
  31. package/src/provider-models/openai-compat.ts +2817 -0
  32. package/src/provider-models/special.ts +67 -0
  33. package/src/providers/aery-native-client.ts +228 -0
  34. package/src/providers/aery-native-server.ts +212 -0
  35. package/src/providers/amazon-bedrock.ts +873 -0
  36. package/src/providers/anthropic-client.ts +318 -0
  37. package/src/providers/anthropic-messages-server-schema.ts +243 -0
  38. package/src/providers/anthropic-messages-server.ts +683 -0
  39. package/src/providers/anthropic-wire.ts +268 -0
  40. package/src/providers/anthropic.ts +3094 -0
  41. package/src/providers/aws-credentials.ts +501 -0
  42. package/src/providers/aws-eventstream.ts +185 -0
  43. package/src/providers/aws-sigv4.ts +218 -0
  44. package/src/providers/azure-openai-responses.ts +361 -0
  45. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  46. package/src/providers/cursor/proto/agent.proto +3526 -0
  47. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  48. package/src/providers/cursor/proto/buf.yaml +17 -0
  49. package/src/providers/cursor.ts +2621 -0
  50. package/src/providers/error-message.ts +21 -0
  51. package/src/providers/github-copilot-headers.ts +140 -0
  52. package/src/providers/gitlab-duo.ts +372 -0
  53. package/src/providers/google-auth.ts +252 -0
  54. package/src/providers/google-gemini-cli.ts +809 -0
  55. package/src/providers/google-gemini-headers.ts +41 -0
  56. package/src/providers/google-shared.ts +917 -0
  57. package/src/providers/google-types.ts +167 -0
  58. package/src/providers/google-vertex.ts +91 -0
  59. package/src/providers/google.ts +41 -0
  60. package/src/providers/grammar.ts +70 -0
  61. package/src/providers/kimi.ts +52 -0
  62. package/src/providers/mock.ts +496 -0
  63. package/src/providers/ollama.ts +644 -0
  64. package/src/providers/openai-anthropic-shim.ts +138 -0
  65. package/src/providers/openai-chat-server-schema.ts +252 -0
  66. package/src/providers/openai-chat-server.ts +647 -0
  67. package/src/providers/openai-codex/constants.ts +43 -0
  68. package/src/providers/openai-codex/request-transformer.ts +161 -0
  69. package/src/providers/openai-codex/response-handler.ts +81 -0
  70. package/src/providers/openai-codex-responses.ts +3018 -0
  71. package/src/providers/openai-completions-compat.ts +300 -0
  72. package/src/providers/openai-completions.ts +1979 -0
  73. package/src/providers/openai-responses-server-schema.ts +290 -0
  74. package/src/providers/openai-responses-server.ts +1183 -0
  75. package/src/providers/openai-responses-shared.ts +873 -0
  76. package/src/providers/openai-responses.ts +679 -0
  77. package/src/providers/register-builtins.ts +436 -0
  78. package/src/providers/synthetic.ts +50 -0
  79. package/src/providers/transform-messages.ts +382 -0
  80. package/src/providers/vision-guard.ts +31 -0
  81. package/src/providers/xai-responses.ts +82 -0
  82. package/src/rate-limit-utils.ts +84 -0
  83. package/src/stream.ts +1065 -0
  84. package/src/types.ts +944 -0
  85. package/src/usage/claude.ts +482 -0
  86. package/src/usage/gemini.ts +250 -0
  87. package/src/usage/github-copilot.ts +421 -0
  88. package/src/usage/google-antigravity.ts +201 -0
  89. package/src/usage/kimi.ts +271 -0
  90. package/src/usage/minimax-code.ts +31 -0
  91. package/src/usage/openai-codex.ts +503 -0
  92. package/src/usage/shared.ts +10 -0
  93. package/src/usage/zai.ts +247 -0
  94. package/src/usage.ts +185 -0
  95. package/src/utils/abort.ts +51 -0
  96. package/src/utils/abortable-iterator.ts +69 -0
  97. package/src/utils/anthropic-auth.ts +93 -0
  98. package/src/utils/discovery/antigravity.ts +261 -0
  99. package/src/utils/discovery/codex.ts +371 -0
  100. package/src/utils/discovery/cursor.ts +306 -0
  101. package/src/utils/discovery/gemini.ts +248 -0
  102. package/src/utils/discovery/index.ts +4 -0
  103. package/src/utils/discovery/openai-compatible.ts +224 -0
  104. package/src/utils/event-stream.ts +142 -0
  105. package/src/utils/fireworks-model-id.ts +30 -0
  106. package/src/utils/foundry.ts +8 -0
  107. package/src/utils/http-inspector.ts +176 -0
  108. package/src/utils/idle-iterator.ts +267 -0
  109. package/src/utils/json-parse.ts +182 -0
  110. package/src/utils/oauth/__tests__/xai-oauth.test.ts +107 -0
  111. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  112. package/src/utils/oauth/anthropic.ts +273 -0
  113. package/src/utils/oauth/api-key-login.ts +87 -0
  114. package/src/utils/oauth/api-key-validation.ts +92 -0
  115. package/src/utils/oauth/callback-server.ts +276 -0
  116. package/src/utils/oauth/cerebras.ts +16 -0
  117. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  118. package/src/utils/oauth/cursor.ts +157 -0
  119. package/src/utils/oauth/deepseek.ts +53 -0
  120. package/src/utils/oauth/firepass.ts +24 -0
  121. package/src/utils/oauth/fireworks.ts +15 -0
  122. package/src/utils/oauth/github-copilot.ts +362 -0
  123. package/src/utils/oauth/gitlab-duo.ts +123 -0
  124. package/src/utils/oauth/google-antigravity.ts +200 -0
  125. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  126. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  127. package/src/utils/oauth/huggingface.ts +62 -0
  128. package/src/utils/oauth/index.ts +484 -0
  129. package/src/utils/oauth/kagi.ts +47 -0
  130. package/src/utils/oauth/kilo.ts +87 -0
  131. package/src/utils/oauth/kimi.ts +254 -0
  132. package/src/utils/oauth/litellm.ts +47 -0
  133. package/src/utils/oauth/lm-studio.ts +38 -0
  134. package/src/utils/oauth/minimax-code.ts +78 -0
  135. package/src/utils/oauth/moonshot.ts +23 -0
  136. package/src/utils/oauth/nanogpt.ts +15 -0
  137. package/src/utils/oauth/nvidia.ts +70 -0
  138. package/src/utils/oauth/oauth.html +203 -0
  139. package/src/utils/oauth/ollama-cloud.ts +28 -0
  140. package/src/utils/oauth/ollama.ts +47 -0
  141. package/src/utils/oauth/openai-codex.ts +299 -0
  142. package/src/utils/oauth/opencode.ts +49 -0
  143. package/src/utils/oauth/openrouter.ts +20 -0
  144. package/src/utils/oauth/parallel.ts +46 -0
  145. package/src/utils/oauth/perplexity.ts +206 -0
  146. package/src/utils/oauth/pkce.ts +18 -0
  147. package/src/utils/oauth/qianfan.ts +58 -0
  148. package/src/utils/oauth/qwen-portal.ts +60 -0
  149. package/src/utils/oauth/synthetic.ts +15 -0
  150. package/src/utils/oauth/tavily.ts +46 -0
  151. package/src/utils/oauth/together.ts +16 -0
  152. package/src/utils/oauth/types.ts +99 -0
  153. package/src/utils/oauth/venice.ts +59 -0
  154. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  155. package/src/utils/oauth/vllm.ts +40 -0
  156. package/src/utils/oauth/wafer.ts +50 -0
  157. package/src/utils/oauth/xai-oauth.ts +342 -0
  158. package/src/utils/oauth/xiaomi.ts +139 -0
  159. package/src/utils/oauth/zai.ts +60 -0
  160. package/src/utils/oauth/zenmux.ts +15 -0
  161. package/src/utils/oauth/zhipu.ts +60 -0
  162. package/src/utils/overflow.ts +137 -0
  163. package/src/utils/parse-bind.ts +54 -0
  164. package/src/utils/provider-response.ts +30 -0
  165. package/src/utils/request-debug.ts +336 -0
  166. package/src/utils/retry-after.ts +110 -0
  167. package/src/utils/retry.ts +54 -0
  168. package/src/utils/schema/CONSTRAINTS.md +164 -0
  169. package/src/utils/schema/adapt.ts +36 -0
  170. package/src/utils/schema/compatibility.ts +435 -0
  171. package/src/utils/schema/dereference.ts +98 -0
  172. package/src/utils/schema/draft.ts +341 -0
  173. package/src/utils/schema/equality.ts +97 -0
  174. package/src/utils/schema/fields.ts +191 -0
  175. package/src/utils/schema/index.ts +13 -0
  176. package/src/utils/schema/json-schema-validator.ts +577 -0
  177. package/src/utils/schema/meta-validator.ts +167 -0
  178. package/src/utils/schema/normalize.ts +1588 -0
  179. package/src/utils/schema/spill.ts +43 -0
  180. package/src/utils/schema/stamps.ts +97 -0
  181. package/src/utils/schema/types.ts +10 -0
  182. package/src/utils/schema/wire.ts +293 -0
  183. package/src/utils/schema/zod-decontaminate.ts +331 -0
  184. package/src/utils/sdk-stream-timeout.ts +43 -0
  185. package/src/utils/sse-debug.ts +289 -0
  186. package/src/utils/stream-markup-healing.ts +612 -0
  187. package/src/utils/tool-choice.ts +99 -0
  188. package/src/utils/validation.ts +1024 -0
  189. package/src/utils.ts +166 -0
  190. package/dist/api-registry.d.ts +0 -20
  191. package/dist/api-registry.d.ts.map +0 -1
  192. package/dist/api-registry.js +0 -44
  193. package/dist/api-registry.js.map +0 -1
  194. package/dist/bedrock-provider.d.ts +0 -5
  195. package/dist/bedrock-provider.d.ts.map +0 -1
  196. package/dist/bedrock-provider.js +0 -6
  197. package/dist/bedrock-provider.js.map +0 -1
  198. package/dist/cli.d.ts +0 -3
  199. package/dist/cli.d.ts.map +0 -1
  200. package/dist/cli.js +0 -130
  201. package/dist/cli.js.map +0 -1
  202. package/dist/env-api-keys.d.ts +0 -18
  203. package/dist/env-api-keys.d.ts.map +0 -1
  204. package/dist/env-api-keys.js +0 -178
  205. package/dist/env-api-keys.js.map +0 -1
  206. package/dist/image-models.d.ts +0 -10
  207. package/dist/image-models.d.ts.map +0 -1
  208. package/dist/image-models.generated.d.ts +0 -440
  209. package/dist/image-models.generated.d.ts.map +0 -1
  210. package/dist/image-models.generated.js +0 -442
  211. package/dist/image-models.generated.js.map +0 -1
  212. package/dist/image-models.js +0 -23
  213. package/dist/image-models.js.map +0 -1
  214. package/dist/images-api-registry.d.ts +0 -14
  215. package/dist/images-api-registry.d.ts.map +0 -1
  216. package/dist/images-api-registry.js +0 -22
  217. package/dist/images-api-registry.js.map +0 -1
  218. package/dist/images.d.ts +0 -4
  219. package/dist/images.d.ts.map +0 -1
  220. package/dist/images.js +0 -14
  221. package/dist/images.js.map +0 -1
  222. package/dist/index.d.ts +0 -32
  223. package/dist/index.d.ts.map +0 -1
  224. package/dist/index.js +0 -20
  225. package/dist/index.js.map +0 -1
  226. package/dist/models.d.ts +0 -18
  227. package/dist/models.d.ts.map +0 -1
  228. package/dist/models.generated.d.ts +0 -17707
  229. package/dist/models.generated.d.ts.map +0 -1
  230. package/dist/models.generated.js +0 -16561
  231. package/dist/models.generated.js.map +0 -1
  232. package/dist/models.js +0 -71
  233. package/dist/models.js.map +0 -1
  234. package/dist/oauth.d.ts +0 -2
  235. package/dist/oauth.d.ts.map +0 -1
  236. package/dist/oauth.js +0 -2
  237. package/dist/oauth.js.map +0 -1
  238. package/dist/providers/aery-error-formatting.d.ts +0 -13
  239. package/dist/providers/aery-error-formatting.d.ts.map +0 -1
  240. package/dist/providers/aery-error-formatting.js +0 -112
  241. package/dist/providers/aery-error-formatting.js.map +0 -1
  242. package/dist/providers/amazon-bedrock.d.ts +0 -38
  243. package/dist/providers/amazon-bedrock.d.ts.map +0 -1
  244. package/dist/providers/amazon-bedrock.js +0 -763
  245. package/dist/providers/amazon-bedrock.js.map +0 -1
  246. package/dist/providers/anthropic.d.ts +0 -71
  247. package/dist/providers/anthropic.d.ts.map +0 -1
  248. package/dist/providers/anthropic.js +0 -949
  249. package/dist/providers/anthropic.js.map +0 -1
  250. package/dist/providers/azure-openai-responses.d.ts +0 -15
  251. package/dist/providers/azure-openai-responses.d.ts.map +0 -1
  252. package/dist/providers/azure-openai-responses.js +0 -225
  253. package/dist/providers/azure-openai-responses.js.map +0 -1
  254. package/dist/providers/cloudflare.d.ts +0 -13
  255. package/dist/providers/cloudflare.d.ts.map +0 -1
  256. package/dist/providers/cloudflare.js +0 -26
  257. package/dist/providers/cloudflare.js.map +0 -1
  258. package/dist/providers/faux.d.ts +0 -56
  259. package/dist/providers/faux.d.ts.map +0 -1
  260. package/dist/providers/faux.js +0 -368
  261. package/dist/providers/faux.js.map +0 -1
  262. package/dist/providers/github-copilot-headers.d.ts +0 -8
  263. package/dist/providers/github-copilot-headers.d.ts.map +0 -1
  264. package/dist/providers/github-copilot-headers.js +0 -29
  265. package/dist/providers/github-copilot-headers.js.map +0 -1
  266. package/dist/providers/google-gemini-cli.d.ts +0 -74
  267. package/dist/providers/google-gemini-cli.d.ts.map +0 -1
  268. package/dist/providers/google-gemini-cli.js +0 -779
  269. package/dist/providers/google-gemini-cli.js.map +0 -1
  270. package/dist/providers/google-shared.d.ts +0 -70
  271. package/dist/providers/google-shared.d.ts.map +0 -1
  272. package/dist/providers/google-shared.js +0 -329
  273. package/dist/providers/google-shared.js.map +0 -1
  274. package/dist/providers/google-vertex.d.ts +0 -15
  275. package/dist/providers/google-vertex.d.ts.map +0 -1
  276. package/dist/providers/google-vertex.js +0 -442
  277. package/dist/providers/google-vertex.js.map +0 -1
  278. package/dist/providers/google.d.ts +0 -13
  279. package/dist/providers/google.d.ts.map +0 -1
  280. package/dist/providers/google.js +0 -400
  281. package/dist/providers/google.js.map +0 -1
  282. package/dist/providers/images/openrouter.d.ts +0 -3
  283. package/dist/providers/images/openrouter.d.ts.map +0 -1
  284. package/dist/providers/images/openrouter.js +0 -129
  285. package/dist/providers/images/openrouter.js.map +0 -1
  286. package/dist/providers/images/register-builtins.d.ts +0 -4
  287. package/dist/providers/images/register-builtins.d.ts.map +0 -1
  288. package/dist/providers/images/register-builtins.js +0 -34
  289. package/dist/providers/images/register-builtins.js.map +0 -1
  290. package/dist/providers/mistral.d.ts +0 -25
  291. package/dist/providers/mistral.d.ts.map +0 -1
  292. package/dist/providers/mistral.js +0 -535
  293. package/dist/providers/mistral.js.map +0 -1
  294. package/dist/providers/openai-codex-responses.d.ts +0 -30
  295. package/dist/providers/openai-codex-responses.d.ts.map +0 -1
  296. package/dist/providers/openai-codex-responses.js +0 -1090
  297. package/dist/providers/openai-codex-responses.js.map +0 -1
  298. package/dist/providers/openai-completions.d.ts +0 -19
  299. package/dist/providers/openai-completions.d.ts.map +0 -1
  300. package/dist/providers/openai-completions.js +0 -950
  301. package/dist/providers/openai-completions.js.map +0 -1
  302. package/dist/providers/openai-prompt-cache.d.ts +0 -3
  303. package/dist/providers/openai-prompt-cache.d.ts.map +0 -1
  304. package/dist/providers/openai-prompt-cache.js +0 -10
  305. package/dist/providers/openai-prompt-cache.js.map +0 -1
  306. package/dist/providers/openai-responses-shared.d.ts +0 -18
  307. package/dist/providers/openai-responses-shared.d.ts.map +0 -1
  308. package/dist/providers/openai-responses-shared.js +0 -492
  309. package/dist/providers/openai-responses-shared.js.map +0 -1
  310. package/dist/providers/openai-responses.d.ts +0 -13
  311. package/dist/providers/openai-responses.d.ts.map +0 -1
  312. package/dist/providers/openai-responses.js +0 -237
  313. package/dist/providers/openai-responses.js.map +0 -1
  314. package/dist/providers/register-builtins.d.ts +0 -38
  315. package/dist/providers/register-builtins.d.ts.map +0 -1
  316. package/dist/providers/register-builtins.js +0 -278
  317. package/dist/providers/register-builtins.js.map +0 -1
  318. package/dist/providers/simple-options.d.ts +0 -8
  319. package/dist/providers/simple-options.d.ts.map +0 -1
  320. package/dist/providers/simple-options.js +0 -41
  321. package/dist/providers/simple-options.js.map +0 -1
  322. package/dist/providers/transform-messages.d.ts +0 -8
  323. package/dist/providers/transform-messages.d.ts.map +0 -1
  324. package/dist/providers/transform-messages.js +0 -184
  325. package/dist/providers/transform-messages.js.map +0 -1
  326. package/dist/session-resources.d.ts +0 -4
  327. package/dist/session-resources.d.ts.map +0 -1
  328. package/dist/session-resources.js +0 -22
  329. package/dist/session-resources.js.map +0 -1
  330. package/dist/stream.d.ts +0 -8
  331. package/dist/stream.d.ts.map +0 -1
  332. package/dist/stream.js +0 -27
  333. package/dist/stream.js.map +0 -1
  334. package/dist/types.d.ts +0 -498
  335. package/dist/types.d.ts.map +0 -1
  336. package/dist/types.js +0 -2
  337. package/dist/types.js.map +0 -1
  338. package/dist/utils/diagnostics.d.ts +0 -19
  339. package/dist/utils/diagnostics.d.ts.map +0 -1
  340. package/dist/utils/diagnostics.js +0 -25
  341. package/dist/utils/diagnostics.js.map +0 -1
  342. package/dist/utils/event-stream.d.ts +0 -21
  343. package/dist/utils/event-stream.d.ts.map +0 -1
  344. package/dist/utils/event-stream.js +0 -81
  345. package/dist/utils/event-stream.js.map +0 -1
  346. package/dist/utils/hash.d.ts +0 -3
  347. package/dist/utils/hash.d.ts.map +0 -1
  348. package/dist/utils/hash.js +0 -14
  349. package/dist/utils/hash.js.map +0 -1
  350. package/dist/utils/headers.d.ts +0 -2
  351. package/dist/utils/headers.d.ts.map +0 -1
  352. package/dist/utils/headers.js +0 -8
  353. package/dist/utils/headers.js.map +0 -1
  354. package/dist/utils/json-parse.d.ts +0 -16
  355. package/dist/utils/json-parse.d.ts.map +0 -1
  356. package/dist/utils/json-parse.js +0 -113
  357. package/dist/utils/json-parse.js.map +0 -1
  358. package/dist/utils/node-http-proxy.d.ts +0 -10
  359. package/dist/utils/node-http-proxy.d.ts.map +0 -1
  360. package/dist/utils/node-http-proxy.js +0 -97
  361. package/dist/utils/node-http-proxy.js.map +0 -1
  362. package/dist/utils/oauth/anthropic.d.ts +0 -25
  363. package/dist/utils/oauth/anthropic.d.ts.map +0 -1
  364. package/dist/utils/oauth/anthropic.js +0 -335
  365. package/dist/utils/oauth/anthropic.js.map +0 -1
  366. package/dist/utils/oauth/device-code.d.ts +0 -19
  367. package/dist/utils/oauth/device-code.d.ts.map +0 -1
  368. package/dist/utils/oauth/device-code.js +0 -55
  369. package/dist/utils/oauth/device-code.js.map +0 -1
  370. package/dist/utils/oauth/github-copilot.d.ts +0 -30
  371. package/dist/utils/oauth/github-copilot.d.ts.map +0 -1
  372. package/dist/utils/oauth/github-copilot.js +0 -268
  373. package/dist/utils/oauth/github-copilot.js.map +0 -1
  374. package/dist/utils/oauth/google-antigravity.d.ts +0 -26
  375. package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
  376. package/dist/utils/oauth/google-antigravity.js +0 -377
  377. package/dist/utils/oauth/google-antigravity.js.map +0 -1
  378. package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
  379. package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
  380. package/dist/utils/oauth/google-gemini-cli.js +0 -482
  381. package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
  382. package/dist/utils/oauth/index.d.ts +0 -63
  383. package/dist/utils/oauth/index.d.ts.map +0 -1
  384. package/dist/utils/oauth/index.js +0 -131
  385. package/dist/utils/oauth/index.js.map +0 -1
  386. package/dist/utils/oauth/oauth-page.d.ts +0 -3
  387. package/dist/utils/oauth/oauth-page.d.ts.map +0 -1
  388. package/dist/utils/oauth/oauth-page.js +0 -105
  389. package/dist/utils/oauth/oauth-page.js.map +0 -1
  390. package/dist/utils/oauth/openai-codex.d.ts +0 -34
  391. package/dist/utils/oauth/openai-codex.d.ts.map +0 -1
  392. package/dist/utils/oauth/openai-codex.js +0 -385
  393. package/dist/utils/oauth/openai-codex.js.map +0 -1
  394. package/dist/utils/oauth/pkce.d.ts +0 -13
  395. package/dist/utils/oauth/pkce.d.ts.map +0 -1
  396. package/dist/utils/oauth/pkce.js +0 -31
  397. package/dist/utils/oauth/pkce.js.map +0 -1
  398. package/dist/utils/oauth/types.d.ts +0 -64
  399. package/dist/utils/oauth/types.d.ts.map +0 -1
  400. package/dist/utils/oauth/types.js +0 -2
  401. package/dist/utils/oauth/types.js.map +0 -1
  402. package/dist/utils/overflow.d.ts +0 -56
  403. package/dist/utils/overflow.d.ts.map +0 -1
  404. package/dist/utils/overflow.js +0 -151
  405. package/dist/utils/overflow.js.map +0 -1
  406. package/dist/utils/sanitize-unicode.d.ts +0 -22
  407. package/dist/utils/sanitize-unicode.d.ts.map +0 -1
  408. package/dist/utils/sanitize-unicode.js +0 -26
  409. package/dist/utils/sanitize-unicode.js.map +0 -1
  410. package/dist/utils/typebox-helpers.d.ts +0 -17
  411. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  412. package/dist/utils/typebox-helpers.js +0 -21
  413. package/dist/utils/typebox-helpers.js.map +0 -1
  414. package/dist/utils/validation.d.ts +0 -18
  415. package/dist/utils/validation.d.ts.map +0 -1
  416. package/dist/utils/validation.js +0 -281
  417. package/dist/utils/validation.js.map +0 -1
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Wafer login flows.
3
+ *
4
+ * Wafer (https://wafer.ai) exposes a single OpenAI-compatible base URL
5
+ * (`https://pass.wafer.ai/v1`) for two SKUs:
6
+ *
7
+ * - **Wafer Pass** — flat-rate subscription. The key authorizes models whose
8
+ * catalog entries carry `wafer.tier = "pass_included"`.
9
+ * - **Wafer Serverless** — pay-as-you-go. Superset of Pass; the same `/v1/models`
10
+ * endpoint returns the full per-account model list.
11
+ *
12
+ * Both SKUs issue `wfr_…` keys. The key prefix alone does not distinguish
13
+ * tiers — the entitlement is per-account on the server side — so we expose
14
+ * two parallel logins / env vars (`WAFER_PASS_API_KEY`, `WAFER_SERVERLESS_API_KEY`)
15
+ * mirroring the firepass/fireworks split, letting users with both
16
+ * subscriptions switch between them without re-pasting.
17
+ *
18
+ * Validation uses the shared `/v1/models` endpoint, which works for both
19
+ * tiers and is cheap (no token spend).
20
+ */
21
+ import { createApiKeyLogin } from "./api-key-login";
22
+
23
+ const WAFER_AUTH_URL = "https://wafer.ai/dashboard";
24
+ const WAFER_MODELS_URL = "https://pass.wafer.ai/v1/models";
25
+
26
+ export const loginWaferPass = createApiKeyLogin({
27
+ providerLabel: "Wafer Pass",
28
+ authUrl: WAFER_AUTH_URL,
29
+ instructions: "Create or copy your Wafer Pass API key from the Wafer dashboard",
30
+ promptMessage: "Paste your Wafer Pass API key",
31
+ placeholder: "wfr_...",
32
+ validation: {
33
+ kind: "models-endpoint",
34
+ provider: "Wafer Pass",
35
+ modelsUrl: WAFER_MODELS_URL,
36
+ },
37
+ });
38
+
39
+ export const loginWaferServerless = createApiKeyLogin({
40
+ providerLabel: "Wafer Serverless",
41
+ authUrl: WAFER_AUTH_URL,
42
+ instructions: "Create or copy your Wafer Serverless API key from the Wafer dashboard",
43
+ promptMessage: "Paste your Wafer Serverless API key",
44
+ placeholder: "wfr_...",
45
+ validation: {
46
+ kind: "models-endpoint",
47
+ provider: "Wafer Serverless",
48
+ modelsUrl: WAFER_MODELS_URL,
49
+ },
50
+ });
@@ -0,0 +1,342 @@
1
+ // Ported from NousResearch/hermes-agent (MIT) — hermes_cli/auth.py xAI sections (L93-111, L2979-3160, L5286-5469).
2
+
3
+ /**
4
+ * xAI Grok (SuperGrok Subscription) OAuth flow.
5
+ *
6
+ * Loopback PKCE flow on `127.0.0.1:56121/callback`. One token unlocks Grok-4.x
7
+ * chat, Grok Imagine image generation, and Grok Voice TTS via subsequent
8
+ * commits. Endpoint discovery is hardened against MITM via
9
+ * {@link validateXAIEndpoint}: any non-HTTPS or non-`x.ai`/`*.x.ai` host is
10
+ * rejected on every call site, not just the first.
11
+ */
12
+
13
+ import { OAuthCallbackFlow, type OAuthCallbackFlowOptions } from "./callback-server";
14
+ import { generatePKCE } from "./pkce";
15
+ import type { OAuthController, OAuthCredentials } from "./types";
16
+
17
+ // Hermes hermes_cli/auth.py L93-111
18
+ const XAI_OAUTH_ISSUER = "https://auth.x.ai";
19
+ const XAI_OAUTH_DISCOVERY_URL = `${XAI_OAUTH_ISSUER}/.well-known/openid-configuration`;
20
+ const XAI_OAUTH_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
21
+ const XAI_OAUTH_SCOPE = "openid profile email offline_access grok-cli:access api:access";
22
+ const XAI_OAUTH_REDIRECT_HOST = "127.0.0.1";
23
+ const XAI_OAUTH_REDIRECT_PORT = 56121;
24
+ const XAI_OAUTH_REDIRECT_PATH = "/callback";
25
+ const XAI_OAUTH_DOCS_URL = "https://hermes-agent.nousresearch.com/docs/guides/xai-grok-oauth";
26
+
27
+ // Mirrors the 5-min skew used by anthropic.ts:160 — keeps every provider on the
28
+ // same conservative client-side expiry window.
29
+ const ACCESS_TOKEN_CLIENT_SKEW_MS = 5 * 60 * 1000;
30
+
31
+ const DISCOVERY_TIMEOUT_MS = 15_000;
32
+ const TOKEN_REQUEST_TIMEOUT_MS = 20_000;
33
+
34
+ interface XAIOAuthDiscovery {
35
+ authorization_endpoint: string;
36
+ token_endpoint: string;
37
+ }
38
+
39
+ /**
40
+ * Validate an xAI OIDC discovery endpoint against scheme + host.
41
+ *
42
+ * Hermes `_xai_validate_oauth_endpoint` L2997-3035. The discovery response is
43
+ * long-lived and cached in {@link OAuthCredentials}; a single MITM during
44
+ * initial login could substitute a malicious `token_endpoint` that would then
45
+ * receive every future refresh_token. Rejecting non-HTTPS or non-`x.ai` /
46
+ * `*.x.ai` hosts pins the cached endpoint to the xAI auth origin.
47
+ *
48
+ * @throws Error with message `Invalid xAI <field>: <url>` when the URL fails
49
+ * either scheme or host validation.
50
+ */
51
+ export function validateXAIEndpoint(url: string, field: string): string {
52
+ let parsed: URL;
53
+ try {
54
+ parsed = new URL(url);
55
+ } catch {
56
+ throw new Error(`Invalid xAI ${field}: ${url}`);
57
+ }
58
+ if (parsed.protocol !== "https:") {
59
+ throw new Error(`Invalid xAI ${field}: ${url}`);
60
+ }
61
+ const host = parsed.hostname.toLowerCase();
62
+ if (!host || (host !== "x.ai" && !host.endsWith(".x.ai"))) {
63
+ throw new Error(`Invalid xAI ${field}: ${url}`);
64
+ }
65
+ return url;
66
+ }
67
+
68
+ /**
69
+ * Fetch xAI's OIDC discovery document and validate both endpoints.
70
+ *
71
+ * Hermes `_xai_oauth_discovery` L3038-3084.
72
+ */
73
+ async function xaiOAuthDiscovery(timeoutMs: number = DISCOVERY_TIMEOUT_MS): Promise<XAIOAuthDiscovery> {
74
+ let response: Response;
75
+ try {
76
+ response = await fetch(XAI_OAUTH_DISCOVERY_URL, {
77
+ method: "GET",
78
+ headers: { Accept: "application/json" },
79
+ signal: AbortSignal.timeout(timeoutMs),
80
+ });
81
+ } catch (error) {
82
+ throw new Error(`xAI OIDC discovery failed: ${error instanceof Error ? error.message : String(error)}`);
83
+ }
84
+ if (response.status !== 200) {
85
+ throw new Error(`xAI OIDC discovery returned status ${response.status}.`);
86
+ }
87
+ let payload: unknown;
88
+ try {
89
+ payload = await response.json();
90
+ } catch (error) {
91
+ throw new Error(
92
+ `xAI OIDC discovery returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
93
+ );
94
+ }
95
+ if (!payload || typeof payload !== "object") {
96
+ throw new Error("xAI OIDC discovery response was not a JSON object.");
97
+ }
98
+ const obj = payload as Record<string, unknown>;
99
+ const authorizationEndpoint =
100
+ typeof obj.authorization_endpoint === "string" ? obj.authorization_endpoint.trim() : "";
101
+ const tokenEndpoint = typeof obj.token_endpoint === "string" ? obj.token_endpoint.trim() : "";
102
+ if (!authorizationEndpoint || !tokenEndpoint) {
103
+ throw new Error("xAI OIDC discovery response was missing required endpoints.");
104
+ }
105
+ validateXAIEndpoint(authorizationEndpoint, "authorization_endpoint");
106
+ validateXAIEndpoint(tokenEndpoint, "token_endpoint");
107
+ return {
108
+ authorization_endpoint: authorizationEndpoint,
109
+ token_endpoint: tokenEndpoint,
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Check whether a JWT access token is at or past its `exp` claim (with an
115
+ * optional refresh-skew margin).
116
+ *
117
+ * Hermes `_xai_access_token_is_expiring` L2979-2994. Returns `false` for any
118
+ * malformed input — this is a refresh-trigger check, not a validation, so
119
+ * non-JWTs ("no token in cache") must NOT trigger a spurious refresh.
120
+ */
121
+ export function isXAIAccessTokenExpiring(jwt: string, skewSeconds: number = 0): boolean {
122
+ try {
123
+ if (typeof jwt !== "string" || !jwt.includes(".")) return false;
124
+ const parts = jwt.split(".");
125
+ if (parts.length < 2) return false;
126
+ const payloadPart = parts[1];
127
+ if (!payloadPart) return false;
128
+ const decoded = Buffer.from(payloadPart, "base64url").toString("utf8");
129
+ const payload = JSON.parse(decoded) as { exp?: unknown };
130
+ const exp = payload.exp;
131
+ if (typeof exp !== "number" || !Number.isFinite(exp)) return false;
132
+ const now = Math.floor(Date.now() / 1000);
133
+ const skew = Math.max(0, Math.floor(skewSeconds));
134
+ return exp <= now + skew;
135
+ } catch {
136
+ return false;
137
+ }
138
+ }
139
+
140
+ interface BuildXAIAuthorizeUrlOptions {
141
+ authorizationEndpoint: string;
142
+ redirectUri: string;
143
+ codeChallenge: string;
144
+ state: string;
145
+ nonce: string;
146
+ }
147
+
148
+ /**
149
+ * Build the xAI authorization URL.
150
+ *
151
+ * Hermes `_xai_oauth_build_authorize_url` L5286-5312. `plan=generic` opts the
152
+ * consent screen into xAI's generic OAuth plan tier; without it,
153
+ * `accounts.x.ai` rejects loopback OAuth from non-allowlisted clients.
154
+ * `referrer=aery` lets xAI attribute aery-originated logins in their
155
+ * OAuth server logs (Hermes uses `referrer=hermes-agent`; aery mirrors the
156
+ * pattern with its own attribution string).
157
+ */
158
+ function buildXAIAuthorizeUrl(opts: BuildXAIAuthorizeUrlOptions): string {
159
+ const params = new URLSearchParams({
160
+ response_type: "code",
161
+ client_id: XAI_OAUTH_CLIENT_ID,
162
+ redirect_uri: opts.redirectUri,
163
+ scope: XAI_OAUTH_SCOPE,
164
+ code_challenge: opts.codeChallenge,
165
+ code_challenge_method: "S256",
166
+ state: opts.state,
167
+ nonce: opts.nonce,
168
+ plan: "generic",
169
+ referrer: "aery",
170
+ });
171
+ return `${opts.authorizationEndpoint}?${params.toString()}`;
172
+ }
173
+
174
+ /**
175
+ * xAI Grok OAuth loopback flow (Hermes `_xai_oauth_loopback_login` L5315-5469).
176
+ *
177
+ * Uses a fixed redirect URI so the callback server fails fast instead of
178
+ * falling back to a random port that xAI's redirect_uri allowlist rejects.
179
+ */
180
+ export class XAIOAuthFlow extends OAuthCallbackFlow {
181
+ #verifier: string = "";
182
+
183
+ constructor(ctrl: OAuthController) {
184
+ super(ctrl, {
185
+ preferredPort: XAI_OAUTH_REDIRECT_PORT,
186
+ callbackPath: XAI_OAUTH_REDIRECT_PATH,
187
+ callbackHostname: XAI_OAUTH_REDIRECT_HOST,
188
+ redirectUri: `http://${XAI_OAUTH_REDIRECT_HOST}:${XAI_OAUTH_REDIRECT_PORT}${XAI_OAUTH_REDIRECT_PATH}`,
189
+ } satisfies OAuthCallbackFlowOptions);
190
+ }
191
+
192
+ async generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }> {
193
+ const pkce = await generatePKCE();
194
+ this.#verifier = pkce.verifier;
195
+ const nonce = crypto.randomUUID().replace(/-/g, "");
196
+
197
+ const discovery = await xaiOAuthDiscovery();
198
+ const url = buildXAIAuthorizeUrl({
199
+ authorizationEndpoint: discovery.authorization_endpoint,
200
+ redirectUri,
201
+ codeChallenge: pkce.challenge,
202
+ state,
203
+ nonce,
204
+ });
205
+
206
+ return {
207
+ url,
208
+ instructions: `Complete login in your browser for xAI Grok (SuperGrok). Docs: ${XAI_OAUTH_DOCS_URL}`,
209
+ };
210
+ }
211
+
212
+ async exchangeToken(code: string, _state: string, redirectUri: string): Promise<OAuthCredentials> {
213
+ const discovery = await xaiOAuthDiscovery();
214
+ const tokenEndpoint = validateXAIEndpoint(discovery.token_endpoint, "token_endpoint");
215
+
216
+ const body = new URLSearchParams({
217
+ grant_type: "authorization_code",
218
+ client_id: XAI_OAUTH_CLIENT_ID,
219
+ code,
220
+ redirect_uri: redirectUri,
221
+ code_verifier: this.#verifier,
222
+ });
223
+
224
+ const response = await fetch(tokenEndpoint, {
225
+ method: "POST",
226
+ headers: {
227
+ "Content-Type": "application/x-www-form-urlencoded",
228
+ Accept: "application/json",
229
+ },
230
+ body,
231
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS),
232
+ });
233
+
234
+ if (!response.ok) {
235
+ let detail = "";
236
+ try {
237
+ detail = (await response.text()).trim();
238
+ } catch {
239
+ // Ignore body-read failures; the status code is the diagnostic.
240
+ }
241
+ throw new Error(`xAI token exchange failed: ${response.status}${detail ? ` ${detail}` : ""}`);
242
+ }
243
+
244
+ let tokenData: { access_token?: unknown; refresh_token?: unknown; expires_in?: unknown };
245
+ try {
246
+ tokenData = (await response.json()) as typeof tokenData;
247
+ } catch (error) {
248
+ throw new Error(
249
+ `xAI token exchange returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
250
+ );
251
+ }
252
+
253
+ if (typeof tokenData.access_token !== "string" || !tokenData.access_token) {
254
+ throw new Error("xAI token exchange response missing access_token");
255
+ }
256
+ if (typeof tokenData.refresh_token !== "string" || !tokenData.refresh_token) {
257
+ throw new Error("xAI token exchange response missing refresh_token");
258
+ }
259
+ if (typeof tokenData.expires_in !== "number" || !Number.isFinite(tokenData.expires_in)) {
260
+ throw new Error("xAI token exchange response missing expires_in");
261
+ }
262
+
263
+ return {
264
+ access: tokenData.access_token,
265
+ refresh: tokenData.refresh_token,
266
+ expires: Date.now() + tokenData.expires_in * 1000 - ACCESS_TOKEN_CLIENT_SKEW_MS,
267
+ };
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Login with xAI Grok OAuth (SuperGrok Subscription).
273
+ */
274
+ export async function loginXAIOAuth(ctrl: OAuthController): Promise<OAuthCredentials> {
275
+ return new XAIOAuthFlow(ctrl).login();
276
+ }
277
+
278
+ /**
279
+ * Refresh an xAI OAuth access token using a stored refresh_token.
280
+ *
281
+ * Hermes `refresh_xai_oauth_pure` L3087-3160. Re-runs OIDC discovery and
282
+ * re-validates the cached `token_endpoint` on the refresh hot path so a
283
+ * cached-but-poisoned endpoint cannot silently leak a refresh_token.
284
+ */
285
+ export async function refreshXAIOAuthToken(refreshToken: string): Promise<OAuthCredentials> {
286
+ if (typeof refreshToken !== "string" || !refreshToken.trim()) {
287
+ throw new Error("missing refresh_token");
288
+ }
289
+
290
+ const discovery = await xaiOAuthDiscovery();
291
+ const tokenEndpoint = validateXAIEndpoint(discovery.token_endpoint, "token_endpoint");
292
+
293
+ const body = new URLSearchParams({
294
+ grant_type: "refresh_token",
295
+ client_id: XAI_OAUTH_CLIENT_ID,
296
+ refresh_token: refreshToken,
297
+ });
298
+
299
+ const response = await fetch(tokenEndpoint, {
300
+ method: "POST",
301
+ headers: {
302
+ "Content-Type": "application/x-www-form-urlencoded",
303
+ Accept: "application/json",
304
+ },
305
+ body,
306
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS),
307
+ });
308
+
309
+ if (!response.ok) {
310
+ let detail = "";
311
+ try {
312
+ detail = (await response.text()).trim();
313
+ } catch {
314
+ // Ignore body-read failures; the status code is the diagnostic.
315
+ }
316
+ throw new Error(`xAI token refresh failed: ${response.status}${detail ? ` ${detail}` : ""}`);
317
+ }
318
+
319
+ let data: { access_token?: unknown; refresh_token?: unknown; expires_in?: unknown };
320
+ try {
321
+ data = (await response.json()) as typeof data;
322
+ } catch (error) {
323
+ throw new Error(
324
+ `xAI token refresh returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
325
+ );
326
+ }
327
+
328
+ if (typeof data.access_token !== "string" || !data.access_token) {
329
+ throw new Error("xAI token refresh response missing access_token");
330
+ }
331
+ if (typeof data.expires_in !== "number" || !Number.isFinite(data.expires_in)) {
332
+ throw new Error("xAI token refresh response missing expires_in");
333
+ }
334
+
335
+ const newRefresh = typeof data.refresh_token === "string" && data.refresh_token ? data.refresh_token : refreshToken;
336
+
337
+ return {
338
+ access: data.access_token,
339
+ refresh: newRefresh,
340
+ expires: Date.now() + data.expires_in * 1000 - ACCESS_TOKEN_CLIENT_SKEW_MS,
341
+ };
342
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Xiaomi MiMo login flow.
3
+ *
4
+ * Xiaomi MiMo provides OpenAI-compatible models via
5
+ * https://api.xiaomimimo.com/v1.
6
+ *
7
+ * This is not OAuth - it's a simple API key flow:
8
+ * 1. Open browser to Xiaomi MiMo API key console
9
+ * 2. User copies their API key
10
+ * 3. User pastes the API key into the CLI
11
+ */
12
+
13
+ import type { OAuthController } from "./types";
14
+
15
+ const PROVIDER_ID = "xiaomi";
16
+ const PROVIDER_NAME = "Xiaomi MiMo";
17
+ const STANDARD_AUTH_URL = "https://platform.xiaomimimo.com/#/console/api-keys";
18
+ const STANDARD_API_BASE_URL = "https://api.xiaomimimo.com/v1";
19
+ const TOKEN_PLAN_SGP_API_BASE_URL = "https://token-plan-sgp.xiaomimimo.com/v1";
20
+ const TOKEN_PLAN_AMS_API_BASE_URL = "https://token-plan-ams.xiaomimimo.com/v1";
21
+ const TOKEN_PLAN_CN_API_BASE_URL = "https://token-plan-cn.xiaomimimo.com/v1";
22
+ const TOKEN_PLAN_KEY_PREFIX = "tp-";
23
+ const STANDARD_VALIDATION_MODEL = "mimo-v2-flash";
24
+ const TOKEN_PLAN_VALIDATION_MODEL = "mimo-v2.5";
25
+
26
+ function isTokenPlanKey(apiKey: string): boolean {
27
+ return apiKey.startsWith(TOKEN_PLAN_KEY_PREFIX);
28
+ }
29
+
30
+ const VALIDATION_TIMEOUT_MS = 15_000;
31
+
32
+ async function validateXiaomiApiKey(apiKey: string, signal?: AbortSignal): Promise<void> {
33
+ // For token-plan keys try SGP → AMS → CN in order until one succeeds.
34
+ // Standard sk- keys only hit the one endpoint.
35
+ const endpoints = isTokenPlanKey(apiKey)
36
+ ? [
37
+ { baseUrl: TOKEN_PLAN_SGP_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
38
+ { baseUrl: TOKEN_PLAN_AMS_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
39
+ { baseUrl: TOKEN_PLAN_CN_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
40
+ ]
41
+ : [{ baseUrl: STANDARD_API_BASE_URL, model: STANDARD_VALIDATION_MODEL }];
42
+
43
+ let lastError: Error | null = null;
44
+
45
+ for (const ep of endpoints) {
46
+ // Fresh timeout per endpoint so SGP→AMS fallback works after a regional
47
+ // timeout: a shared AbortSignal.timeout would stay aborted and instantly
48
+ // abort the AMS fetch.
49
+ const timeoutSignal = AbortSignal.timeout(VALIDATION_TIMEOUT_MS);
50
+ const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
51
+ try {
52
+ const response = await fetch(`${ep.baseUrl}/chat/completions`, {
53
+ method: "POST",
54
+ headers: {
55
+ "Content-Type": "application/json",
56
+ Authorization: `Bearer ${apiKey}`,
57
+ },
58
+ body: JSON.stringify({
59
+ model: ep.model,
60
+ max_tokens: 1,
61
+ messages: [{ role: "user", content: "ping" }],
62
+ }),
63
+ signal: requestSignal,
64
+ });
65
+
66
+ if (response.ok) {
67
+ return;
68
+ }
69
+
70
+ // 401 means this endpoint didn't accept the key; try the next one
71
+ if (response.status === 401) {
72
+ let details = "";
73
+ try {
74
+ details = (await response.text()).trim();
75
+ } catch {
76
+ // ignore body parse errors, status is enough
77
+ }
78
+ lastError = new Error(
79
+ details
80
+ ? `${PROVIDER_NAME} API key validation failed (${response.status}): ${details}`
81
+ : `${PROVIDER_NAME} API key validation failed (${response.status})`,
82
+ );
83
+ continue;
84
+ }
85
+
86
+ // Non-auth errors are real failures
87
+ let details = "";
88
+ try {
89
+ details = (await response.text()).trim();
90
+ } catch {
91
+ // ignore body parse errors, status is enough
92
+ }
93
+ const message = details
94
+ ? `${PROVIDER_NAME} API key validation failed (${response.status}): ${details}`
95
+ : `${PROVIDER_NAME} API key validation failed (${response.status})`;
96
+ throw new Error(message);
97
+ } catch (e) {
98
+ // Only re-throw AbortError when the caller explicitly cancelled.
99
+ // Timeout aborts (from AbortSignal.timeout) should fall through to
100
+ // the next endpoint so SGP→AMS fallback works during regional outages.
101
+ if (e instanceof DOMException && e.name === "AbortError" && signal?.aborted) {
102
+ throw e;
103
+ }
104
+ lastError = e instanceof Error ? e : new Error(String(e));
105
+ }
106
+ }
107
+ throw lastError ?? new Error(`${PROVIDER_NAME} API key validation failed`);
108
+ }
109
+
110
+ /**
111
+ * Login to Xiaomi MiMo.
112
+ *
113
+ * Opens browser to API keys page, prompts user to paste their API key.
114
+ * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
115
+ */
116
+ export async function loginXiaomi(options: OAuthController): Promise<string> {
117
+ if (!options.onPrompt) {
118
+ throw new Error(`${PROVIDER_NAME} login requires onPrompt callback`);
119
+ }
120
+ options.onAuth?.({
121
+ url: STANDARD_AUTH_URL,
122
+ instructions: "Copy your API key from the Xiaomi MiMo console",
123
+ });
124
+ const apiKey = await options.onPrompt({
125
+ message: "Paste your Xiaomi API key (sk-... or token-plan tp-...)",
126
+ placeholder: "sk-... or tp-...",
127
+ });
128
+ if (options.signal?.aborted) {
129
+ throw new Error("Login cancelled");
130
+ }
131
+ const trimmed = apiKey.trim();
132
+ if (!trimmed) {
133
+ throw new Error("API key is required");
134
+ }
135
+
136
+ options.onProgress?.(`Validating ${PROVIDER_ID} API key...`);
137
+ await validateXiaomiApiKey(trimmed, options.signal);
138
+ return trimmed;
139
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Z.AI login flow.
3
+ *
4
+ * Z.AI is a platform that provides access to GLM models through an OpenAI-compatible API.
5
+ * API docs: https://docs.z.ai/guides/overview/quick-start
6
+ *
7
+ * This is not OAuth - it's a simple API key flow:
8
+ * 1. User gets their API key from https://z.ai/settings/api-keys
9
+ * 2. User pastes the API key into the CLI
10
+ */
11
+
12
+ import { validateOpenAICompatibleApiKey } from "./api-key-validation";
13
+ import type { OAuthController } from "./types";
14
+
15
+ const AUTH_URL = "https://z.ai/manage-apikey/apikey-list";
16
+ const API_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
17
+ const VALIDATION_MODEL = "glm-4.7";
18
+
19
+ /**
20
+ * Login to Z.AI.
21
+ *
22
+ * Opens browser to API keys page, prompts user to paste their API key.
23
+ * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
24
+ */
25
+ export async function loginZai(options: OAuthController): Promise<string> {
26
+ if (!options.onPrompt) {
27
+ throw new Error("Z.AI login requires onPrompt callback");
28
+ }
29
+
30
+ // Open browser to API keys page
31
+ options.onAuth?.({
32
+ url: AUTH_URL,
33
+ instructions: "Copy your API key from the dashboard",
34
+ });
35
+
36
+ // Prompt user to paste their API key
37
+ const apiKey = await options.onPrompt({
38
+ message: "Paste your Z.AI API key",
39
+ placeholder: "sk-...",
40
+ });
41
+
42
+ if (options.signal?.aborted) {
43
+ throw new Error("Login cancelled");
44
+ }
45
+
46
+ const trimmed = apiKey.trim();
47
+ if (!trimmed) {
48
+ throw new Error("API key is required");
49
+ }
50
+
51
+ options.onProgress?.("Validating API key...");
52
+ await validateOpenAICompatibleApiKey({
53
+ provider: "Z.AI",
54
+ apiKey: trimmed,
55
+ baseUrl: API_BASE_URL,
56
+ model: VALIDATION_MODEL,
57
+ signal: options.signal,
58
+ });
59
+ return trimmed;
60
+ }
@@ -0,0 +1,15 @@
1
+ /** ZenMux login flow (API key paste, validated via /models). */
2
+ import { createApiKeyLogin } from "./api-key-login";
3
+
4
+ export const loginZenMux = createApiKeyLogin({
5
+ providerLabel: "ZenMux",
6
+ authUrl: "https://zenmux.ai/settings/keys",
7
+ instructions: "Create or copy your ZenMux API key",
8
+ promptMessage: "Paste your ZenMux API key",
9
+ placeholder: "sk-...",
10
+ validation: {
11
+ kind: "models-endpoint",
12
+ provider: "ZenMux",
13
+ modelsUrl: "https://zenmux.ai/api/v1/models",
14
+ },
15
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Zhipu Coding Plan login flow.
3
+ *
4
+ * GLM Coding Plan provides an OpenAI-compatible API on the dedicated coding
5
+ * endpoint. API docs: https://docs.bigmodel.cn/cn/coding-plan/quick-start
6
+ *
7
+ * Simple API key flow:
8
+ * 1. User gets a Coding Plan API key from https://bigmodel.cn/coding-plan/personal/overview
9
+ * 2. User pastes the API key into the CLI
10
+ */
11
+
12
+ import { validateOpenAICompatibleApiKey } from "./api-key-validation";
13
+ import type { OAuthController } from "./types";
14
+
15
+ const AUTH_URL = "https://bigmodel.cn/coding-plan/personal/overview";
16
+ const API_BASE_URL = "https://open.bigmodel.cn/api/coding/paas/v4";
17
+ const VALIDATION_MODEL = "glm-5.1";
18
+
19
+ /**
20
+ * Login to Zhipu Coding Plan.
21
+ *
22
+ * Opens browser to API keys page, prompts user to paste their API key.
23
+ * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
24
+ */
25
+ export async function loginZhipuCodingPlan(options: OAuthController): Promise<string> {
26
+ if (!options.onPrompt) {
27
+ throw new Error("Zhipu Coding Plan login requires onPrompt callback");
28
+ }
29
+
30
+ // Open browser to API keys page
31
+ options.onAuth?.({
32
+ url: AUTH_URL,
33
+ instructions: "Copy your API key from the Coding Plan dashboard",
34
+ });
35
+
36
+ // Prompt user to paste their API key
37
+ const apiKey = await options.onPrompt({
38
+ message: "Paste your Zhipu API key",
39
+ placeholder: "sk-...",
40
+ });
41
+
42
+ if (options.signal?.aborted) {
43
+ throw new Error("Login cancelled");
44
+ }
45
+
46
+ const trimmed = apiKey.trim();
47
+ if (!trimmed) {
48
+ throw new Error("API key is required");
49
+ }
50
+
51
+ options.onProgress?.("Validating API key...");
52
+ await validateOpenAICompatibleApiKey({
53
+ provider: "Zhipu",
54
+ apiKey: trimmed,
55
+ baseUrl: API_BASE_URL,
56
+ model: VALIDATION_MODEL,
57
+ signal: options.signal,
58
+ });
59
+ return trimmed;
60
+ }