@gajae-code/ai 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/CHANGELOG.md +2644 -0
  2. package/README.md +1181 -0
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth-broker/client.d.ts +66 -0
  5. package/dist/types/auth-broker/index.d.ts +5 -0
  6. package/dist/types/auth-broker/refresher.d.ts +25 -0
  7. package/dist/types/auth-broker/remote-store.d.ts +96 -0
  8. package/dist/types/auth-broker/server.d.ts +32 -0
  9. package/dist/types/auth-broker/types.d.ts +105 -0
  10. package/dist/types/auth-broker/wire-schemas.d.ts +412 -0
  11. package/dist/types/auth-gateway/http.d.ts +39 -0
  12. package/dist/types/auth-gateway/index.d.ts +3 -0
  13. package/dist/types/auth-gateway/server.d.ts +17 -0
  14. package/dist/types/auth-gateway/types.d.ts +115 -0
  15. package/dist/types/auth-storage.d.ts +641 -0
  16. package/dist/types/cli.d.ts +2 -0
  17. package/dist/types/index.d.ts +49 -0
  18. package/dist/types/model-cache.d.ts +17 -0
  19. package/dist/types/model-manager.d.ts +62 -0
  20. package/dist/types/model-thinking.d.ts +71 -0
  21. package/dist/types/models.d.ts +12 -0
  22. package/dist/types/provider-details.d.ts +24 -0
  23. package/dist/types/provider-models/bundled-references.d.ts +4 -0
  24. package/dist/types/provider-models/descriptors.d.ts +48 -0
  25. package/dist/types/provider-models/google.d.ts +20 -0
  26. package/dist/types/provider-models/index.d.ts +5 -0
  27. package/dist/types/provider-models/ollama.d.ts +7 -0
  28. package/dist/types/provider-models/openai-compat.d.ts +237 -0
  29. package/dist/types/provider-models/special.d.ts +16 -0
  30. package/dist/types/providers/amazon-bedrock.d.ts +36 -0
  31. package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
  32. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  33. package/dist/types/providers/anthropic.d.ts +188 -0
  34. package/dist/types/providers/aws-credentials.d.ts +43 -0
  35. package/dist/types/providers/aws-eventstream.d.ts +38 -0
  36. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  37. package/dist/types/providers/azure-openai-responses.d.ts +15 -0
  38. package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
  39. package/dist/types/providers/cursor.d.ts +42 -0
  40. package/dist/types/providers/error-message.d.ts +27 -0
  41. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  42. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  43. package/dist/types/providers/google-auth.d.ts +24 -0
  44. package/dist/types/providers/google-gemini-cli.d.ts +72 -0
  45. package/dist/types/providers/google-gemini-headers.d.ts +18 -0
  46. package/dist/types/providers/google-shared.d.ts +163 -0
  47. package/dist/types/providers/google-types.d.ts +138 -0
  48. package/dist/types/providers/google-vertex.d.ts +7 -0
  49. package/dist/types/providers/google.d.ts +4 -0
  50. package/dist/types/providers/grammar.d.ts +1 -0
  51. package/dist/types/providers/kimi.d.ts +27 -0
  52. package/dist/types/providers/mock.d.ts +175 -0
  53. package/dist/types/providers/ollama.d.ts +6 -0
  54. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  55. package/dist/types/providers/openai-chat-server-schema.d.ts +814 -0
  56. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  57. package/dist/types/providers/openai-codex/constants.d.ts +26 -0
  58. package/dist/types/providers/openai-codex/request-transformer.d.ts +49 -0
  59. package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
  60. package/dist/types/providers/openai-codex-responses.d.ts +67 -0
  61. package/dist/types/providers/openai-completions-compat.d.ts +25 -0
  62. package/dist/types/providers/openai-completions.d.ts +33 -0
  63. package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
  64. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  65. package/dist/types/providers/openai-responses-shared.d.ts +89 -0
  66. package/dist/types/providers/openai-responses.d.ts +32 -0
  67. package/dist/types/providers/pi-native-client.d.ts +13 -0
  68. package/dist/types/providers/pi-native-server.d.ts +68 -0
  69. package/dist/types/providers/register-builtins.d.ts +31 -0
  70. package/dist/types/providers/synthetic.d.ts +26 -0
  71. package/dist/types/providers/transform-messages.d.ts +12 -0
  72. package/dist/types/providers/vision-guard.d.ts +8 -0
  73. package/dist/types/rate-limit-utils.d.ts +19 -0
  74. package/dist/types/stream.d.ts +24 -0
  75. package/dist/types/types.d.ts +746 -0
  76. package/dist/types/usage/claude.d.ts +3 -0
  77. package/dist/types/usage/gemini.d.ts +2 -0
  78. package/dist/types/usage/github-copilot.d.ts +7 -0
  79. package/dist/types/usage/google-antigravity.d.ts +2 -0
  80. package/dist/types/usage/kimi.d.ts +2 -0
  81. package/dist/types/usage/minimax-code.d.ts +2 -0
  82. package/dist/types/usage/openai-codex.d.ts +3 -0
  83. package/dist/types/usage/shared.d.ts +1 -0
  84. package/dist/types/usage/zai.d.ts +2 -0
  85. package/dist/types/usage.d.ts +258 -0
  86. package/dist/types/utils/abort.d.ts +19 -0
  87. package/dist/types/utils/anthropic-auth.d.ts +31 -0
  88. package/dist/types/utils/discovery/antigravity.d.ts +61 -0
  89. package/dist/types/utils/discovery/codex.d.ts +38 -0
  90. package/dist/types/utils/discovery/cursor.d.ts +23 -0
  91. package/dist/types/utils/discovery/gemini.d.ts +25 -0
  92. package/dist/types/utils/discovery/index.d.ts +4 -0
  93. package/dist/types/utils/discovery/openai-compatible.d.ts +72 -0
  94. package/dist/types/utils/event-stream.d.ts +28 -0
  95. package/dist/types/utils/fireworks-model-id.d.ts +10 -0
  96. package/dist/types/utils/foundry.d.ts +1 -0
  97. package/dist/types/utils/h2-fetch.d.ts +22 -0
  98. package/dist/types/utils/http-inspector.d.ts +31 -0
  99. package/dist/types/utils/idle-iterator.d.ts +67 -0
  100. package/dist/types/utils/json-parse.d.ts +10 -0
  101. package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
  102. package/dist/types/utils/oauth/anthropic.d.ts +22 -0
  103. package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
  104. package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
  105. package/dist/types/utils/oauth/callback-server.d.ts +57 -0
  106. package/dist/types/utils/oauth/cerebras.d.ts +1 -0
  107. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
  108. package/dist/types/utils/oauth/cursor.d.ts +15 -0
  109. package/dist/types/utils/oauth/deepseek.d.ts +10 -0
  110. package/dist/types/utils/oauth/firepass.d.ts +1 -0
  111. package/dist/types/utils/oauth/fireworks.d.ts +1 -0
  112. package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
  113. package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
  114. package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
  115. package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
  116. package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
  117. package/dist/types/utils/oauth/huggingface.d.ts +19 -0
  118. package/dist/types/utils/oauth/index.d.ts +38 -0
  119. package/dist/types/utils/oauth/kagi.d.ts +17 -0
  120. package/dist/types/utils/oauth/kilo.d.ts +5 -0
  121. package/dist/types/utils/oauth/kimi.d.ts +21 -0
  122. package/dist/types/utils/oauth/litellm.d.ts +18 -0
  123. package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
  124. package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
  125. package/dist/types/utils/oauth/moonshot.d.ts +1 -0
  126. package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
  127. package/dist/types/utils/oauth/nvidia.d.ts +18 -0
  128. package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
  129. package/dist/types/utils/oauth/ollama.d.ts +18 -0
  130. package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
  131. package/dist/types/utils/oauth/opencode.d.ts +18 -0
  132. package/dist/types/utils/oauth/parallel.d.ts +17 -0
  133. package/dist/types/utils/oauth/perplexity.d.ts +9 -0
  134. package/dist/types/utils/oauth/pkce.d.ts +8 -0
  135. package/dist/types/utils/oauth/qianfan.d.ts +17 -0
  136. package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
  137. package/dist/types/utils/oauth/synthetic.d.ts +1 -0
  138. package/dist/types/utils/oauth/tavily.d.ts +17 -0
  139. package/dist/types/utils/oauth/together.d.ts +1 -0
  140. package/dist/types/utils/oauth/types.d.ts +44 -0
  141. package/dist/types/utils/oauth/venice.d.ts +18 -0
  142. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
  143. package/dist/types/utils/oauth/vllm.d.ts +16 -0
  144. package/dist/types/utils/oauth/xiaomi.d.ts +19 -0
  145. package/dist/types/utils/oauth/zai.d.ts +18 -0
  146. package/dist/types/utils/oauth/zenmux.d.ts +1 -0
  147. package/dist/types/utils/overflow.d.ts +54 -0
  148. package/dist/types/utils/parse-bind.d.ts +23 -0
  149. package/dist/types/utils/provider-response.d.ts +3 -0
  150. package/dist/types/utils/retry-after.d.ts +3 -0
  151. package/dist/types/utils/retry.d.ts +26 -0
  152. package/dist/types/utils/schema/adapt.d.ts +24 -0
  153. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  154. package/dist/types/utils/schema/dereference.d.ts +11 -0
  155. package/dist/types/utils/schema/draft.d.ts +10 -0
  156. package/dist/types/utils/schema/equality.d.ts +4 -0
  157. package/dist/types/utils/schema/fields.d.ts +49 -0
  158. package/dist/types/utils/schema/index.d.ts +13 -0
  159. package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
  160. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  161. package/dist/types/utils/schema/normalize.d.ts +93 -0
  162. package/dist/types/utils/schema/spill.d.ts +8 -0
  163. package/dist/types/utils/schema/stamps.d.ts +25 -0
  164. package/dist/types/utils/schema/types.d.ts +4 -0
  165. package/dist/types/utils/schema/wire.d.ts +54 -0
  166. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  167. package/dist/types/utils/sse-debug.d.ts +10 -0
  168. package/dist/types/utils/tool-call-healing.d.ts +71 -0
  169. package/dist/types/utils/tool-choice.d.ts +50 -0
  170. package/dist/types/utils/validation.d.ts +17 -0
  171. package/dist/types/utils.d.ts +28 -0
  172. package/package.json +146 -0
  173. package/src/api-registry.ts +96 -0
  174. package/src/auth-broker/client.ts +358 -0
  175. package/src/auth-broker/index.ts +5 -0
  176. package/src/auth-broker/refresher.ts +127 -0
  177. package/src/auth-broker/remote-store.ts +623 -0
  178. package/src/auth-broker/server.ts +644 -0
  179. package/src/auth-broker/types.ts +127 -0
  180. package/src/auth-broker/wire-schemas.ts +200 -0
  181. package/src/auth-gateway/http.ts +194 -0
  182. package/src/auth-gateway/index.ts +3 -0
  183. package/src/auth-gateway/server.ts +717 -0
  184. package/src/auth-gateway/types.ts +134 -0
  185. package/src/auth-storage.ts +4104 -0
  186. package/src/cli.ts +262 -0
  187. package/src/index.ts +54 -0
  188. package/src/model-cache.ts +129 -0
  189. package/src/model-manager.ts +450 -0
  190. package/src/model-thinking.ts +691 -0
  191. package/src/models.json +73853 -0
  192. package/src/models.json.d.ts +9 -0
  193. package/src/models.ts +56 -0
  194. package/src/prompts/turn-aborted-guidance.md +4 -0
  195. package/src/provider-details.ts +90 -0
  196. package/src/provider-models/bundled-references.ts +38 -0
  197. package/src/provider-models/descriptors.ts +308 -0
  198. package/src/provider-models/google.ts +91 -0
  199. package/src/provider-models/index.ts +5 -0
  200. package/src/provider-models/ollama.ts +153 -0
  201. package/src/provider-models/openai-compat.ts +2275 -0
  202. package/src/provider-models/special.ts +67 -0
  203. package/src/providers/amazon-bedrock.ts +849 -0
  204. package/src/providers/anthropic-messages-server-schema.ts +229 -0
  205. package/src/providers/anthropic-messages-server.ts +677 -0
  206. package/src/providers/anthropic.ts +2696 -0
  207. package/src/providers/aws-credentials.ts +501 -0
  208. package/src/providers/aws-eventstream.ts +185 -0
  209. package/src/providers/aws-sigv4.ts +218 -0
  210. package/src/providers/azure-openai-responses.ts +337 -0
  211. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  212. package/src/providers/cursor/proto/agent.proto +3526 -0
  213. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  214. package/src/providers/cursor/proto/buf.yaml +17 -0
  215. package/src/providers/cursor.ts +2561 -0
  216. package/src/providers/error-message.ts +21 -0
  217. package/src/providers/github-copilot-headers.ts +140 -0
  218. package/src/providers/gitlab-duo.ts +372 -0
  219. package/src/providers/google-auth.ts +252 -0
  220. package/src/providers/google-gemini-cli.ts +795 -0
  221. package/src/providers/google-gemini-headers.ts +41 -0
  222. package/src/providers/google-shared.ts +902 -0
  223. package/src/providers/google-types.ts +167 -0
  224. package/src/providers/google-vertex.ts +88 -0
  225. package/src/providers/google.ts +41 -0
  226. package/src/providers/grammar.ts +70 -0
  227. package/src/providers/kimi.ts +52 -0
  228. package/src/providers/mock.ts +500 -0
  229. package/src/providers/ollama.ts +544 -0
  230. package/src/providers/openai-anthropic-shim.ts +138 -0
  231. package/src/providers/openai-chat-server-schema.ts +243 -0
  232. package/src/providers/openai-chat-server.ts +628 -0
  233. package/src/providers/openai-codex/constants.ts +43 -0
  234. package/src/providers/openai-codex/request-transformer.ts +161 -0
  235. package/src/providers/openai-codex/response-handler.ts +81 -0
  236. package/src/providers/openai-codex-responses.ts +2598 -0
  237. package/src/providers/openai-completions-compat.ts +279 -0
  238. package/src/providers/openai-completions.ts +1853 -0
  239. package/src/providers/openai-responses-server-schema.ts +290 -0
  240. package/src/providers/openai-responses-server.ts +1183 -0
  241. package/src/providers/openai-responses-shared.ts +800 -0
  242. package/src/providers/openai-responses.ts +621 -0
  243. package/src/providers/pi-native-client.ts +228 -0
  244. package/src/providers/pi-native-server.ts +210 -0
  245. package/src/providers/register-builtins.ts +412 -0
  246. package/src/providers/synthetic.ts +50 -0
  247. package/src/providers/transform-messages.ts +309 -0
  248. package/src/providers/vision-guard.ts +31 -0
  249. package/src/rate-limit-utils.ts +84 -0
  250. package/src/stream.ts +895 -0
  251. package/src/types.ts +884 -0
  252. package/src/usage/claude.ts +431 -0
  253. package/src/usage/gemini.ts +250 -0
  254. package/src/usage/github-copilot.ts +421 -0
  255. package/src/usage/google-antigravity.ts +201 -0
  256. package/src/usage/kimi.ts +271 -0
  257. package/src/usage/minimax-code.ts +31 -0
  258. package/src/usage/openai-codex.ts +503 -0
  259. package/src/usage/shared.ts +10 -0
  260. package/src/usage/zai.ts +247 -0
  261. package/src/usage.ts +183 -0
  262. package/src/utils/abort.ts +51 -0
  263. package/src/utils/anthropic-auth.ts +87 -0
  264. package/src/utils/discovery/antigravity.ts +261 -0
  265. package/src/utils/discovery/codex.ts +371 -0
  266. package/src/utils/discovery/cursor.ts +306 -0
  267. package/src/utils/discovery/gemini.ts +248 -0
  268. package/src/utils/discovery/index.ts +4 -0
  269. package/src/utils/discovery/openai-compatible.ts +224 -0
  270. package/src/utils/event-stream.ts +142 -0
  271. package/src/utils/fireworks-model-id.ts +30 -0
  272. package/src/utils/foundry.ts +8 -0
  273. package/src/utils/h2-fetch.ts +60 -0
  274. package/src/utils/http-inspector.ts +176 -0
  275. package/src/utils/idle-iterator.ts +250 -0
  276. package/src/utils/json-parse.ts +148 -0
  277. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  278. package/src/utils/oauth/anthropic.ts +200 -0
  279. package/src/utils/oauth/api-key-login.ts +87 -0
  280. package/src/utils/oauth/api-key-validation.ts +92 -0
  281. package/src/utils/oauth/callback-server.ts +276 -0
  282. package/src/utils/oauth/cerebras.ts +16 -0
  283. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  284. package/src/utils/oauth/cursor.ts +157 -0
  285. package/src/utils/oauth/deepseek.ts +53 -0
  286. package/src/utils/oauth/firepass.ts +24 -0
  287. package/src/utils/oauth/fireworks.ts +15 -0
  288. package/src/utils/oauth/github-copilot.ts +362 -0
  289. package/src/utils/oauth/gitlab-duo.ts +123 -0
  290. package/src/utils/oauth/google-antigravity.ts +200 -0
  291. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  292. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  293. package/src/utils/oauth/huggingface.ts +62 -0
  294. package/src/utils/oauth/index.ts +444 -0
  295. package/src/utils/oauth/kagi.ts +47 -0
  296. package/src/utils/oauth/kilo.ts +87 -0
  297. package/src/utils/oauth/kimi.ts +254 -0
  298. package/src/utils/oauth/litellm.ts +47 -0
  299. package/src/utils/oauth/lm-studio.ts +38 -0
  300. package/src/utils/oauth/minimax-code.ts +78 -0
  301. package/src/utils/oauth/moonshot.ts +16 -0
  302. package/src/utils/oauth/nanogpt.ts +15 -0
  303. package/src/utils/oauth/nvidia.ts +70 -0
  304. package/src/utils/oauth/oauth.html +199 -0
  305. package/src/utils/oauth/ollama-cloud.ts +28 -0
  306. package/src/utils/oauth/ollama.ts +47 -0
  307. package/src/utils/oauth/openai-codex.ts +299 -0
  308. package/src/utils/oauth/opencode.ts +49 -0
  309. package/src/utils/oauth/parallel.ts +46 -0
  310. package/src/utils/oauth/perplexity.ts +206 -0
  311. package/src/utils/oauth/pkce.ts +18 -0
  312. package/src/utils/oauth/qianfan.ts +58 -0
  313. package/src/utils/oauth/qwen-portal.ts +60 -0
  314. package/src/utils/oauth/synthetic.ts +16 -0
  315. package/src/utils/oauth/tavily.ts +46 -0
  316. package/src/utils/oauth/together.ts +16 -0
  317. package/src/utils/oauth/types.ts +94 -0
  318. package/src/utils/oauth/venice.ts +59 -0
  319. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  320. package/src/utils/oauth/vllm.ts +40 -0
  321. package/src/utils/oauth/xiaomi.ts +137 -0
  322. package/src/utils/oauth/zai.ts +60 -0
  323. package/src/utils/oauth/zenmux.ts +15 -0
  324. package/src/utils/overflow.ts +137 -0
  325. package/src/utils/parse-bind.ts +54 -0
  326. package/src/utils/provider-response.ts +30 -0
  327. package/src/utils/retry-after.ts +110 -0
  328. package/src/utils/retry.ts +54 -0
  329. package/src/utils/schema/CONSTRAINTS.md +164 -0
  330. package/src/utils/schema/adapt.ts +36 -0
  331. package/src/utils/schema/compatibility.ts +435 -0
  332. package/src/utils/schema/dereference.ts +98 -0
  333. package/src/utils/schema/draft.ts +341 -0
  334. package/src/utils/schema/equality.ts +97 -0
  335. package/src/utils/schema/fields.ts +190 -0
  336. package/src/utils/schema/index.ts +13 -0
  337. package/src/utils/schema/json-schema-validator.ts +577 -0
  338. package/src/utils/schema/meta-validator.ts +167 -0
  339. package/src/utils/schema/normalize.ts +1588 -0
  340. package/src/utils/schema/spill.ts +43 -0
  341. package/src/utils/schema/stamps.ts +97 -0
  342. package/src/utils/schema/types.ts +11 -0
  343. package/src/utils/schema/wire.ts +213 -0
  344. package/src/utils/schema/zod-decontaminate.ts +331 -0
  345. package/src/utils/sse-debug.ts +289 -0
  346. package/src/utils/tool-call-healing.ts +271 -0
  347. package/src/utils/tool-choice.ts +99 -0
  348. package/src/utils/validation.ts +1019 -0
  349. package/src/utils.ts +166 -0
@@ -0,0 +1,199 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Authentication</title>
7
+ <style>
8
+ :root {
9
+ --bg: #09090b;
10
+ --card-bg: #18181b;
11
+ --text-main: #fafafa;
12
+ --text-muted: #a1a1aa;
13
+ --success: #22c55e;
14
+ --error: #ef4444;
15
+ --border: #27272a;
16
+ }
17
+ body {
18
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
19
+ background-color: var(--bg);
20
+ color: var(--text-main);
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ height: 100vh;
25
+ margin: 0;
26
+ overflow: hidden;
27
+ }
28
+ .container {
29
+ background: var(--card-bg);
30
+ border: 1px solid var(--border);
31
+ border-radius: 12px;
32
+ padding: 2.5rem;
33
+ width: 100%;
34
+ max-width: 400px;
35
+ text-align: center;
36
+ box-shadow:
37
+ 0 20px 25px -5px rgba(0, 0, 0, 0.1),
38
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
39
+ opacity: 0;
40
+ transform: translateY(10px);
41
+ animation: fadeIn 0.4s ease-out forwards;
42
+ }
43
+ @keyframes fadeIn {
44
+ to {
45
+ opacity: 1;
46
+ transform: translateY(0);
47
+ }
48
+ }
49
+ .icon-circle {
50
+ position: relative;
51
+ width: 64px;
52
+ height: 64px;
53
+ border-radius: 50%;
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ margin: 0 auto 1.5rem;
58
+ background: rgba(255, 255, 255, 0.05);
59
+ }
60
+ .icon {
61
+ width: 32px;
62
+ height: 32px;
63
+ z-index: 2;
64
+ }
65
+ .timer-svg {
66
+ position: absolute;
67
+ top: -2px;
68
+ left: -2px;
69
+ width: 68px;
70
+ height: 68px;
71
+ transform: rotate(-90deg);
72
+ z-index: 1;
73
+ pointer-events: none;
74
+ }
75
+ .timer-circle {
76
+ fill: none;
77
+ stroke-width: 2;
78
+ stroke-linecap: round;
79
+ stroke-dasharray: 201;
80
+ stroke-dashoffset: 0;
81
+ }
82
+ .countdown .timer-circle {
83
+ animation: countdown 3s linear forwards;
84
+ }
85
+ @keyframes countdown {
86
+ to {
87
+ stroke-dashoffset: 201;
88
+ }
89
+ }
90
+ h1 {
91
+ font-size: 1.5rem;
92
+ font-weight: 600;
93
+ margin: 0 0 0.75rem;
94
+ letter-spacing: -0.025em;
95
+ }
96
+ p {
97
+ color: var(--text-muted);
98
+ line-height: 1.5;
99
+ margin: 0 0 1.5rem;
100
+ font-size: 0.95rem;
101
+ }
102
+ .btn {
103
+ display: inline-block;
104
+ background: var(--text-main);
105
+ color: var(--bg);
106
+ font-weight: 500;
107
+ padding: 0.6rem 1.2rem;
108
+ border-radius: 6px;
109
+ text-decoration: none;
110
+ font-size: 0.9rem;
111
+ transition: opacity 0.2s;
112
+ cursor: pointer;
113
+ border: none;
114
+ }
115
+ .btn:hover {
116
+ opacity: 0.9;
117
+ }
118
+
119
+ /* State: success */
120
+ .success .icon-circle {
121
+ background: rgba(34, 197, 94, 0.1);
122
+ }
123
+ .success .icon {
124
+ color: var(--success);
125
+ }
126
+ .success .timer-circle {
127
+ stroke: var(--success);
128
+ }
129
+ .success .icon-error {
130
+ display: none;
131
+ }
132
+
133
+ /* State: error */
134
+ .error .icon-circle {
135
+ background: rgba(239, 68, 68, 0.1);
136
+ }
137
+ .error .icon {
138
+ color: var(--error);
139
+ }
140
+ .error .timer-circle {
141
+ stroke: var(--error);
142
+ }
143
+ .error .icon-success,
144
+ .error .timer-svg {
145
+ display: none;
146
+ }
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <div id="app" class="container">
151
+ <div class="icon-circle">
152
+ <svg class="timer-svg" viewBox="0 0 68 68">
153
+ <circle class="timer-circle" cx="34" cy="34" r="32"></circle>
154
+ </svg>
155
+ <svg class="icon icon-success" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
156
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
157
+ </svg>
158
+ <svg class="icon icon-error" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
159
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
160
+ </svg>
161
+ </div>
162
+ <h1 id="title">Authentication</h1>
163
+ <p id="message"></p>
164
+ <button onclick="window.close()" class="btn">Close Window</button>
165
+ </div>
166
+
167
+ <script id="server-state" type="application/json">
168
+ __OAUTH_STATE__
169
+ </script>
170
+
171
+ <script>
172
+ let serverState;
173
+ try {
174
+ serverState = JSON.parse(document.getElementById("server-state").textContent);
175
+ } catch {
176
+ const params = new URLSearchParams(window.location.search);
177
+ serverState = {
178
+ ok: params.get("ok") === "1",
179
+ error: params.get("error") || "Authentication failed",
180
+ };
181
+ }
182
+
183
+ const app = document.getElementById("app");
184
+ const title = document.getElementById("title");
185
+ const message = document.getElementById("message");
186
+
187
+ if (serverState.ok) {
188
+ app.classList.add("success", "countdown");
189
+ title.textContent = "Authentication Successful";
190
+ message.innerHTML = "You have successfully logged in.<br>This window will close automatically.";
191
+ setTimeout(() => window.close(), 3000);
192
+ } else {
193
+ app.classList.add("error");
194
+ title.textContent = "Authentication Failed";
195
+ message.textContent = serverState.error || "An error occurred";
196
+ }
197
+ </script>
198
+ </body>
199
+ </html>
@@ -0,0 +1,28 @@
1
+ import type { OAuthController } from "./types";
2
+
3
+ const OLLAMA_CLOUD_KEYS_URL = "https://ollama.com/settings/keys";
4
+
5
+ export async function loginOllamaCloud(options: OAuthController): Promise<string> {
6
+ if (options.signal?.aborted) {
7
+ throw new Error("Login cancelled");
8
+ }
9
+ if (!options.onPrompt) {
10
+ throw new Error("Interactive prompt is required for Ollama Cloud login");
11
+ }
12
+ options.onAuth?.({
13
+ url: OLLAMA_CLOUD_KEYS_URL,
14
+ instructions: "Create an Ollama Cloud API key, then paste it here.",
15
+ });
16
+ const apiKey = await options.onPrompt({
17
+ message: "Paste your Ollama Cloud API key",
18
+ placeholder: "ollama-cloud-api-key",
19
+ });
20
+ if (options.signal?.aborted) {
21
+ throw new Error("Login cancelled");
22
+ }
23
+ const trimmed = apiKey.trim();
24
+ if (!trimmed) {
25
+ throw new Error("Ollama Cloud API key is required");
26
+ }
27
+ return trimmed;
28
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Ollama login flow.
3
+ *
4
+ * Ollama is typically used locally without authentication, but some hosted
5
+ * deployments require a bearer token/API key.
6
+ *
7
+ * This flow is API-key based (not OAuth):
8
+ * 1. Optionally open Ollama docs
9
+ * 2. Prompt user for API key/token (optional)
10
+ * 3. Persist key only when provided
11
+ */
12
+
13
+ import type { OAuthController } from "./types";
14
+
15
+ const OLLAMA_DOCS_URL = "https://github.com/ollama/ollama/blob/main/docs/api.md";
16
+
17
+ /**
18
+ * Login to Ollama.
19
+ *
20
+ * Returns a trimmed API key/token string. Empty string means local no-auth mode.
21
+ */
22
+ export async function loginOllama(options: OAuthController): Promise<string> {
23
+ if (options.signal?.aborted) {
24
+ throw new Error("Login cancelled");
25
+ }
26
+ if (!options.onPrompt) {
27
+ return "";
28
+ }
29
+
30
+ options.onAuth?.({
31
+ url: OLLAMA_DOCS_URL,
32
+ instructions:
33
+ "Optional: paste an Ollama API key/token for authenticated hosts. Leave empty for local no-auth mode.",
34
+ });
35
+
36
+ const apiKey = await options.onPrompt({
37
+ message: "Paste your Ollama API key/token (optional)",
38
+ placeholder: "ollama-local",
39
+ allowEmpty: true,
40
+ });
41
+
42
+ if (options.signal?.aborted) {
43
+ throw new Error("Login cancelled");
44
+ }
45
+
46
+ return apiKey.trim();
47
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * OpenAI code provider (ChatGPT OAuth) flow — browser and device-code flows.
3
+ */
4
+ import { OAuthCallbackFlow, type OAuthCallbackFlowOptions } from "./callback-server";
5
+ import { generatePKCE } from "./pkce";
6
+ import type { OAuthController, OAuthCredentials } from "./types";
7
+
8
+ const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
9
+ const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
10
+ const TOKEN_URL = "https://auth.openai.com/oauth/token";
11
+ const CALLBACK_PORT = 1455;
12
+ const CALLBACK_PATH = "/auth/callback";
13
+ const SCOPE = "openid profile email offline_access";
14
+ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
15
+ const JWT_PROFILE_CLAIM = "https://api.openai.com/profile";
16
+ const TOKEN_REQUEST_TIMEOUT_MS = 15_000;
17
+ const DEVICE_USERCODE_URL = "https://auth.openai.com/api/accounts/deviceauth/usercode";
18
+ const DEVICE_TOKEN_URL = "https://auth.openai.com/api/accounts/deviceauth/token";
19
+ const DEVICE_REDIRECT_URI = "https://auth.openai.com/deviceauth/callback";
20
+ const DEVICE_AUTH_URL = "https://auth.openai.com/codex/device";
21
+ const DEVICE_POLL_INTERVAL_MS = 5_000;
22
+ const DEVICE_POLL_SAFETY_MARGIN_MS = 3_000;
23
+ /** Upper bound on device-code polling to avoid infinite loops on server errors. */
24
+ const DEVICE_MAX_POLLS = 120;
25
+
26
+ type JwtPayload = {
27
+ [JWT_CLAIM_PATH]?: {
28
+ chatgpt_account_id?: string;
29
+ };
30
+ [JWT_PROFILE_CLAIM]?: {
31
+ email?: string;
32
+ };
33
+ [key: string]: unknown;
34
+ };
35
+
36
+ export function decodeJwt<T = Record<string, unknown>>(token: string): T | null {
37
+ try {
38
+ const parts = token.split(".");
39
+ if (parts.length !== 3) return null;
40
+ const payload = parts[1] ?? "";
41
+ const decoded = Buffer.from(payload, "base64").toString("utf-8");
42
+ return JSON.parse(decoded) as T;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ function getTokenProfile(accessToken: string): { accountId?: string; email?: string } {
49
+ const payload = decodeJwt<JwtPayload>(accessToken);
50
+ const auth = payload?.[JWT_CLAIM_PATH];
51
+ const accountId = auth?.chatgpt_account_id;
52
+ const email = payload?.[JWT_PROFILE_CLAIM]?.email?.trim().toLowerCase();
53
+ return {
54
+ accountId: typeof accountId === "string" && accountId.length > 0 ? accountId : undefined,
55
+ email: typeof email === "string" && email.length > 0 ? email : undefined,
56
+ };
57
+ }
58
+
59
+ interface PKCE {
60
+ verifier: string;
61
+ challenge: string;
62
+ }
63
+
64
+ class OpenAICodexOAuthFlow extends OAuthCallbackFlow {
65
+ constructor(
66
+ ctrl: OAuthController,
67
+ private readonly pkce: PKCE,
68
+ private readonly originator: string,
69
+ ) {
70
+ super(ctrl, {
71
+ preferredPort: CALLBACK_PORT,
72
+ callbackPath: CALLBACK_PATH,
73
+ // Enforce the fixed port: OpenAI only allows http://localhost:1455/auth/callback.
74
+ // Without this, a busy port 1455 falls back to a random port, and the token
75
+ // exchange would fail with 403 because the redirect_uri no longer matches the
76
+ // registered allowlist entry.
77
+ redirectUri: `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`,
78
+ } satisfies OAuthCallbackFlowOptions);
79
+ }
80
+
81
+ async generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }> {
82
+ const searchParams = new URLSearchParams({
83
+ response_type: "code",
84
+ client_id: CLIENT_ID,
85
+ redirect_uri: redirectUri,
86
+ scope: SCOPE,
87
+ code_challenge: this.pkce.challenge,
88
+ code_challenge_method: "S256",
89
+ state,
90
+ id_token_add_organizations: "true",
91
+ codex_cli_simplified_flow: "true",
92
+ originator: this.originator,
93
+ });
94
+
95
+ const url = `${AUTHORIZE_URL}?${searchParams.toString()}`;
96
+ return { url, instructions: "A browser window should open. Complete login to finish." };
97
+ }
98
+
99
+ async exchangeToken(code: string, _state: string, redirectUri: string): Promise<OAuthCredentials> {
100
+ return exchangeCodeForToken(code, this.pkce.verifier, redirectUri);
101
+ }
102
+ }
103
+
104
+ async function exchangeCodeForToken(code: string, verifier: string, redirectUri: string): Promise<OAuthCredentials> {
105
+ const tokenResponse = await fetch(TOKEN_URL, {
106
+ method: "POST",
107
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
108
+ body: new URLSearchParams({
109
+ grant_type: "authorization_code",
110
+ client_id: CLIENT_ID,
111
+ code,
112
+ code_verifier: verifier,
113
+ redirect_uri: redirectUri,
114
+ }),
115
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS),
116
+ });
117
+
118
+ if (!tokenResponse.ok) {
119
+ let detail = `${tokenResponse.status}`;
120
+ try {
121
+ const body = (await tokenResponse.json()) as { error?: string; error_description?: string };
122
+ if (body.error)
123
+ detail = `${tokenResponse.status} ${body.error}${body.error_description ? `: ${body.error_description}` : ""}`;
124
+ } catch {}
125
+ throw new Error(`Token exchange failed: ${detail}`);
126
+ }
127
+
128
+ const tokenData = (await tokenResponse.json()) as {
129
+ access_token?: string;
130
+ refresh_token?: string;
131
+ expires_in?: number;
132
+ };
133
+
134
+ if (!tokenData.access_token || !tokenData.refresh_token || typeof tokenData.expires_in !== "number") {
135
+ throw new Error("Token response missing required fields");
136
+ }
137
+
138
+ const { accountId, email } = getTokenProfile(tokenData.access_token);
139
+ if (!accountId) {
140
+ throw new Error("Failed to extract accountId from token");
141
+ }
142
+
143
+ return {
144
+ access: tokenData.access_token,
145
+ refresh: tokenData.refresh_token,
146
+ expires: Date.now() + tokenData.expires_in * 1000,
147
+ accountId,
148
+ email,
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Login with OpenAI code provider OAuth
154
+ */
155
+ export type OpenAICodexLoginOptions = OAuthController & {
156
+ /** Optional originator value for OpenAI code provider OAuth. Default: "opencode". */
157
+ originator?: string;
158
+ };
159
+
160
+ export async function loginOpenAICodex(options: OpenAICodexLoginOptions): Promise<OAuthCredentials> {
161
+ const pkce = await generatePKCE();
162
+ const originator = options.originator?.trim() || "opencode";
163
+ const flow = new OpenAICodexOAuthFlow(options, pkce, originator);
164
+
165
+ return flow.login();
166
+ }
167
+
168
+ /**
169
+ * Login with OpenAI code provider using the device-code (headless) flow.
170
+ *
171
+ * Avoids a local callback server entirely — useful when port 1455 is unavailable
172
+ * or when the browser callback flow fails with 403 (e.g. network/proxy issues).
173
+ */
174
+ export async function loginOpenAICodexDevice(ctrl: OAuthController): Promise<OAuthCredentials> {
175
+ ctrl.onProgress?.("Initiating device authorization…");
176
+
177
+ const initResponse = await fetch(DEVICE_USERCODE_URL, {
178
+ method: "POST",
179
+ headers: { "Content-Type": "application/json" },
180
+ body: JSON.stringify({ client_id: CLIENT_ID }),
181
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS),
182
+ });
183
+
184
+ if (!initResponse.ok) {
185
+ throw new Error(`Device authorization initiation failed: ${initResponse.status}`);
186
+ }
187
+
188
+ const initData = (await initResponse.json()) as {
189
+ device_auth_id?: string;
190
+ user_code?: string;
191
+ interval?: string | number;
192
+ };
193
+
194
+ if (!initData.device_auth_id || !initData.user_code) {
195
+ throw new Error("Device authorization response missing required fields");
196
+ }
197
+
198
+ const userCode = initData.user_code;
199
+ const pollIntervalMs =
200
+ (typeof initData.interval === "number"
201
+ ? initData.interval
202
+ : parseInt(String(initData.interval ?? "5"), 10) || 5) *
203
+ 1000 +
204
+ DEVICE_POLL_SAFETY_MARGIN_MS;
205
+
206
+ ctrl.onAuth?.({
207
+ url: DEVICE_AUTH_URL,
208
+ instructions: `Enter code: ${userCode}`,
209
+ });
210
+
211
+ ctrl.onProgress?.(`Waiting for browser authorization (code: ${userCode})…`);
212
+
213
+ for (let poll = 0; poll < DEVICE_MAX_POLLS; poll++) {
214
+ await Bun.sleep(poll === 0 ? Math.min(pollIntervalMs, DEVICE_POLL_INTERVAL_MS) : pollIntervalMs);
215
+
216
+ if (ctrl.signal?.aborted) {
217
+ throw new Error("Device authorization cancelled");
218
+ }
219
+
220
+ const pollResponse = await fetch(DEVICE_TOKEN_URL, {
221
+ method: "POST",
222
+ headers: { "Content-Type": "application/json" },
223
+ body: JSON.stringify({
224
+ device_auth_id: initData.device_auth_id,
225
+ user_code: userCode,
226
+ }),
227
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS),
228
+ });
229
+
230
+ // 403/404 = authorization pending, keep polling
231
+ if (pollResponse.status === 403 || pollResponse.status === 404) {
232
+ continue;
233
+ }
234
+
235
+ if (!pollResponse.ok) {
236
+ throw new Error(`Device token polling failed: ${pollResponse.status}`);
237
+ }
238
+
239
+ const pollData = (await pollResponse.json()) as {
240
+ authorization_code?: string;
241
+ code_verifier?: string;
242
+ };
243
+
244
+ if (!pollData.authorization_code || !pollData.code_verifier) {
245
+ throw new Error("Device token response missing authorization_code or code_verifier");
246
+ }
247
+
248
+ ctrl.onProgress?.("Exchanging authorization code for tokens…");
249
+ return exchangeCodeForToken(pollData.authorization_code, pollData.code_verifier, DEVICE_REDIRECT_URI);
250
+ }
251
+
252
+ throw new Error("Device authorization timed out — user did not complete login in time");
253
+ }
254
+
255
+ /**
256
+ * Refresh OpenAI code provider OAuth token
257
+ */
258
+ export async function refreshOpenAICodexToken(refreshToken: string): Promise<OAuthCredentials> {
259
+ const response = await fetch(TOKEN_URL, {
260
+ method: "POST",
261
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
262
+ body: new URLSearchParams({
263
+ grant_type: "refresh_token",
264
+ refresh_token: refreshToken,
265
+ client_id: CLIENT_ID,
266
+ }),
267
+ signal: AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS),
268
+ });
269
+
270
+ if (!response.ok) {
271
+ let detail = `${response.status}`;
272
+ try {
273
+ const body = (await response.json()) as { error?: string; error_description?: string };
274
+ if (body.error)
275
+ detail = `${response.status} ${body.error}${body.error_description ? `: ${body.error_description}` : ""}`;
276
+ } catch {}
277
+ throw new Error(`OpenAI Codex token refresh failed: ${detail}`);
278
+ }
279
+
280
+ const tokenData = (await response.json()) as {
281
+ access_token?: string;
282
+ refresh_token?: string;
283
+ expires_in?: number;
284
+ };
285
+
286
+ if (!tokenData.access_token || !tokenData.refresh_token || typeof tokenData.expires_in !== "number") {
287
+ throw new Error("Token response missing required fields");
288
+ }
289
+
290
+ const { accountId, email } = getTokenProfile(tokenData.access_token);
291
+
292
+ return {
293
+ access: tokenData.access_token,
294
+ refresh: tokenData.refresh_token || refreshToken,
295
+ expires: Date.now() + tokenData.expires_in * 1000,
296
+ accountId: accountId ?? undefined,
297
+ email,
298
+ };
299
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * OpenCode Zen login flow.
3
+ *
4
+ * OpenCode Zen is a subscription service that provides access to various AI models
5
+ * (GPT-5.x, Anthropic model 4.x, Gemini 3, etc.) through a unified API at opencode.ai/zen.
6
+ * This is not OAuth - it's a simple API key flow:
7
+ * 1. Open browser to https://opencode.ai/auth
8
+ * 2. User logs in and copies their API key
9
+ * 3. User pastes the API key back into the CLI
10
+ */
11
+
12
+ import type { OAuthController } from "./types";
13
+
14
+ const AUTH_URL = "https://opencode.ai/auth";
15
+
16
+ /**
17
+ * Login to OpenCode Zen.
18
+ *
19
+ * Opens browser to auth page, prompts user to paste their API key.
20
+ * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
21
+ */
22
+ export async function loginOpenCode(options: OAuthController): Promise<string> {
23
+ if (!options.onPrompt) {
24
+ throw new Error("OpenCode Zen login requires onPrompt callback");
25
+ }
26
+
27
+ // Open browser to auth page
28
+ options.onAuth?.({
29
+ url: AUTH_URL,
30
+ instructions: "Log in and copy your API key",
31
+ });
32
+
33
+ // Prompt user to paste their API key
34
+ const apiKey = await options.onPrompt({
35
+ message: "Paste your OpenCode Zen API key",
36
+ placeholder: "sk-...",
37
+ });
38
+
39
+ if (options.signal?.aborted) {
40
+ throw new Error("Login cancelled");
41
+ }
42
+
43
+ const trimmed = apiKey.trim();
44
+ if (!trimmed) {
45
+ throw new Error("API key is required");
46
+ }
47
+
48
+ return trimmed;
49
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Parallel login flow.
3
+ *
4
+ * Parallel uses an API key from the account settings page.
5
+ * This is an API key flow:
6
+ * 1. Open browser to Parallel API key settings
7
+ * 2. User copies API key
8
+ * 3. User pastes key into the CLI/TUI
9
+ */
10
+
11
+ import type { OAuthController } from "./types";
12
+
13
+ const AUTH_URL = "https://platform.parallel.ai/settings?tab=api-keys";
14
+
15
+ /**
16
+ * Login to Parallel.
17
+ *
18
+ * Opens browser to the API keys page, prompts the user to paste their API key,
19
+ * and returns the API key directly.
20
+ */
21
+ export async function loginParallel(options: OAuthController): Promise<string> {
22
+ if (!options.onPrompt) {
23
+ throw new Error("Parallel login requires onPrompt callback");
24
+ }
25
+
26
+ options.onAuth?.({
27
+ url: AUTH_URL,
28
+ instructions: "Copy your Parallel API key from the Parallel settings page.",
29
+ });
30
+
31
+ const apiKey = await options.onPrompt({
32
+ message: "Paste your Parallel API key",
33
+ placeholder: "sk_...",
34
+ });
35
+
36
+ if (options.signal?.aborted) {
37
+ throw new Error("Login cancelled");
38
+ }
39
+
40
+ const trimmed = apiKey.trim();
41
+ if (!trimmed) {
42
+ throw new Error("API key is required");
43
+ }
44
+
45
+ return trimmed;
46
+ }