cartethyia 2.8.0 → 2.8.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 (348) hide show
  1. package/.env.example +3 -3
  2. package/dist/dashboard/assets/{BannedPage-Cc0vKpMG.js → BannedPage-Dq530awD.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-Bqj2aa1T.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-CXsaOAO2.js → CliToolsPage-DaZZ3cqP.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-BeLpJ4ma.js → CombosPage-BlC_xdkX.js} +1 -1
  9. package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-DnV0uZ3m.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-CBxAjo63.js → CustomizationPage-7xT5yg2E.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
  14. package/dist/dashboard/assets/{HealthEventsModal-B5yEE0k6.js → HealthEventsModal-Clq8E9cW.js} +1 -1
  15. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
  16. package/dist/dashboard/assets/{InjectSystemPromptPanel-BWFLECmz.js → InjectSystemPromptPanel-6Teoq9Se.js} +1 -1
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-CgoPc5B9.js → LoginPage-CZuI0MAR.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
  20. package/dist/dashboard/assets/{ModelPicker-D9bFQern.js → ModelPicker-C1hNTlkr.js} +1 -1
  21. package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
  22. package/dist/dashboard/assets/{OverviewPage-B_p4tL8k.js → OverviewPage-0yfhSI1C.js} +1 -1
  23. package/dist/dashboard/assets/OverviewPage-0yfhSI1C.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js +8 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js.gz +0 -0
  26. package/dist/dashboard/assets/{ProviderIcon-D_0PiZtg.js → ProviderIcon-Ddhm1InL.js} +1 -1
  27. package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
  28. package/dist/dashboard/assets/{ProvidersPage-CLI2bJTK.js → ProvidersPage-C3fYuu6i.js} +1 -1
  29. package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js.gz +0 -0
  30. package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js +9 -0
  31. package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js.gz +0 -0
  32. package/dist/dashboard/assets/{QuotaPage-DA-a1iSN.js → QuotaPage-D1RV_2BX.js} +1 -1
  33. package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
  34. package/dist/dashboard/assets/{SettingsPage-CctTga2C.js → SettingsPage-BzZAvYSX.js} +1 -1
  35. package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
  36. package/dist/dashboard/assets/{SetupPage-febLU4r1.js → SetupPage--s-EfEIL.js} +1 -1
  37. package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
  38. package/dist/dashboard/assets/{StudioPage-M6fvR5Wz.js → StudioPage-BjXFGSK4.js} +1 -1
  39. package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
  40. package/dist/dashboard/assets/{UsagePage-nyeuRqz9.js → UsagePage-CP8F8BN7.js} +1 -1
  41. package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BFOlvrJC.js → api-keys-2hc12Uz2.js} +1 -1
  43. package/dist/dashboard/assets/api-keys-2hc12Uz2.js.gz +0 -0
  44. package/dist/dashboard/assets/app-DGXbNfAR.css +2 -0
  45. package/dist/dashboard/assets/app-DGXbNfAR.css.gz +0 -0
  46. package/dist/dashboard/assets/{assert-CqTLw5e7.js → assert-BMcj1xZE.js} +1 -1
  47. package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-C83EqLB0.js → badge-DkRUuVfz.js} +1 -1
  49. package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-D1xn7pIe.js → banner-CDjW841Q.js} +1 -1
  51. package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-DU3Lq5Lm.js → card-Bto5UlL9.js} +1 -1
  53. package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-Dw1U_drK.js → cli-tools-DaH6g9hL.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-CEL5Sg_J.js → clipboard-button-BYKBCzjk.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-BjFR5UHy.js → dialog-DL0Zftwv.js} +1 -1
  59. package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-HvBW0AjI.js → drawer-Dpx_UXaS.js} +1 -1
  61. package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-CECVELyy.js → entry-BV7-1NoM.js} +1 -1
  63. package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
  64. package/dist/dashboard/assets/{entry-DYYofQ7W.js → entry-CGvdNU6r.js} +1 -1
  65. package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
  66. package/dist/dashboard/assets/{entry-BSu3gFlt.js → entry-DScKYvZj.js} +3 -3
  67. package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
  68. package/dist/dashboard/assets/{formatters-DLW4i8zK.js → formatters-BS3DZlZj.js} +1 -1
  69. package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-1-fCqe64.js → index-SQnvvTWX.js} +2 -2
  71. package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-DGvU2oLK.js → inline-Dl-cuLK0.js} +1 -1
  73. package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-iNnO1X2K.js → input-Bfx0EzRc.js} +1 -1
  75. package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-0q8UGypb.js → layout-BxhbQ_JS.js} +1 -1
  77. package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-C0w2bQjJ.js → list-row-D49Y4ptn.js} +1 -1
  79. package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-M5aJ_xCa.js → live-CbVTdX9V.js} +1 -1
  81. package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
  82. package/dist/dashboard/assets/{network-BKeQGT3K.js → network-JGvDveof.js} +1 -1
  83. package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
  84. package/dist/dashboard/assets/{numbers-CivAk3da.js → numbers-Dw6kiew-.js} +1 -1
  85. package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
  86. package/dist/dashboard/assets/{pagination-CpzvI5ep.js → pagination-iUoI8GeE.js} +1 -1
  87. package/dist/dashboard/assets/pagination-iUoI8GeE.js.gz +0 -0
  88. package/dist/dashboard/assets/providers/sensenova-intl.png +0 -0
  89. package/dist/dashboard/assets/providers/sensenova.png +0 -0
  90. package/dist/dashboard/assets/{routing-BFQX2VBc.js → routing-8jZr-3WE.js} +1 -1
  91. package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
  92. package/dist/dashboard/assets/{select-CXbck0jG.js → select-oO1FxMVb.js} +1 -1
  93. package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
  94. package/dist/dashboard/assets/{settings-C_XvMHh9.js → settings-CLr3kdIx.js} +1 -1
  95. package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
  96. package/dist/dashboard/assets/{stack-DggRiCxE.js → stack-Y3pDteZr.js} +1 -1
  97. package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
  98. package/dist/dashboard/assets/{state-ClFzfbYp.js → state-CArX_Iak.js} +1 -1
  99. package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
  100. package/dist/dashboard/assets/{switch-CTIFxKqS.js → switch-DwlwmDO-.js} +1 -1
  101. package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
  102. package/dist/dashboard/assets/{useMutation-BClG3yWj.js → useMutation-DJ9GYEgf.js} +1 -1
  103. package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
  104. package/dist/dashboard/index.html +2 -2
  105. package/dist/dashboard/index.html.gz +0 -0
  106. package/dist/dashboard/precompressed-assets.json +51 -51
  107. package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
  108. package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
  109. package/package.json +1 -1
  110. package/src/boot/app.ts +1 -1
  111. package/src/boot/dependencies.ts +4 -6
  112. package/src/config/providers.ts +6 -5
  113. package/src/config/spec.ts +8 -8
  114. package/src/console/backup/nine-router.ts +71 -10
  115. package/src/console/backup/service.ts +8 -1
  116. package/src/console/console-router.ts +1 -1
  117. package/src/console/domain-registration.ts +7 -2
  118. package/src/console/inferhub/client.ts +1 -1
  119. package/src/console/observability/live.ts +2 -2
  120. package/src/console/providers/catalog/contracts.ts +9 -3
  121. package/src/console/providers/catalog/model-operations.ts +2 -3
  122. package/src/console/providers/catalog/provider-operations.ts +1 -1
  123. package/src/console/providers/catalog/routes.ts +2 -2
  124. package/src/console/providers/catalog/store.ts +1914 -1907
  125. package/src/console/providers/detail/routes.ts +1 -0
  126. package/src/console/providers/detail/store.ts +4 -0
  127. package/src/console/providers/oauth/callback-listener.ts +30 -11
  128. package/src/console/providers/oauth/device-egress.ts +123 -0
  129. package/src/console/providers/oauth/routes.ts +123 -16
  130. package/src/console/quota/account-quota-tenant.ts +5 -5
  131. package/src/console/quota/account-test-egress.ts +2 -2
  132. package/src/console/quota/daily-growth-job.ts +1 -1
  133. package/src/console/routing/pools/contracts.ts +1 -1
  134. package/src/console/routing/pools/operations.ts +1 -1
  135. package/src/console/routing/pools/probe-verdict.ts +2 -2
  136. package/src/console/routing/pools/relay-deploy.ts +1 -1
  137. package/src/console/routing/pools/store.ts +4 -4
  138. package/src/network/client.ts +67 -0
  139. package/src/network/egress/bidi-session.ts +322 -0
  140. package/src/network/egress/dial.ts +201 -0
  141. package/src/network/egress/factory.ts +191 -0
  142. package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
  143. package/src/network/egress/tls-profile.ts +123 -0
  144. package/src/network/egress/ws.ts +175 -0
  145. package/src/network/{types.ts → pool/agent-config.ts} +1 -1
  146. package/src/network/pool/agent.ts +2 -2
  147. package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +7 -7
  148. package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
  149. package/src/network/pool/resolver.ts +10 -849
  150. package/src/network/pool/selector.ts +2 -2
  151. package/src/network/ssrf.ts +1 -1
  152. package/src/network/{retry.ts → util/retry.ts} +1 -1
  153. package/src/observability/telemetry-buffer.ts +1 -1
  154. package/src/persistence/schema.ts +9 -0
  155. package/src/persistence/ttl-cache.ts +1 -1
  156. package/src/protocol/codecs.ts +58 -1
  157. package/src/protocol/request/messages.ts +31 -18
  158. package/src/protocol/response/messages.ts +34 -7
  159. package/src/protocol/translation/quirks.ts +15 -0
  160. package/src/protocol/upstream/send-claude-messages.ts +2 -2
  161. package/src/providers/adapters/compatible-adapter.ts +5 -0
  162. package/src/providers/authentication/oauth-client.ts +40 -6
  163. package/src/providers/authentication/oauth-device-flow.ts +7 -3
  164. package/src/providers/authentication/oauth-flow-store.ts +82 -1
  165. package/src/providers/authentication/oauth-refresh-service.ts +766 -739
  166. package/src/providers/default-registry.ts +76 -0
  167. package/src/providers/discovery/discovery-types.ts +7 -0
  168. package/src/providers/discovery/probe-phases.ts +6 -2
  169. package/src/providers/discovery/probing-service.ts +7 -0
  170. package/src/providers/integrations/antigravity/antigravity-protocol.ts +11 -0
  171. package/src/providers/integrations/buddy/account.ts +969 -0
  172. package/src/providers/integrations/buddy/capabilities.ts +10 -10
  173. package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
  174. package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +5 -5
  175. package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
  176. package/src/providers/integrations/buddy/codebuddy/shared.ts +4 -4
  177. package/src/providers/integrations/buddy/{buddy-identity.ts → headers.ts} +138 -140
  178. package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
  179. package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
  180. package/src/providers/integrations/cline/cline-oauth.ts +10 -5
  181. package/src/providers/integrations/cline/cline.ts +3 -4
  182. package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
  183. package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
  184. package/src/providers/integrations/clixad/provider.ts +145 -0
  185. package/src/providers/integrations/codex/codex-oauth.ts +8 -5
  186. package/src/providers/integrations/cursor/cursor-auth.ts +1 -1
  187. package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
  188. package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
  189. package/src/providers/integrations/cursor/cursor-shared.ts +1 -1
  190. package/src/providers/integrations/cursor/cursor.ts +485 -70
  191. package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
  192. package/src/providers/integrations/deepai/deepai-common.ts +145 -0
  193. package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
  194. package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
  195. package/src/providers/integrations/deepai/provider.ts +42 -0
  196. package/src/providers/integrations/devin/devin-auth.ts +7 -0
  197. package/src/providers/integrations/factory/factory-oauth.ts +16 -7
  198. package/src/providers/integrations/freebuff/freebuff.ts +2 -2
  199. package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
  200. package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
  201. package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
  202. package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
  203. package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
  204. package/src/providers/integrations/freepi/freepi-session.ts +158 -0
  205. package/src/providers/integrations/freepi/freepi.ts +80 -54
  206. package/src/providers/integrations/github/github-discovery.ts +30 -13
  207. package/src/providers/integrations/github/github-oauth.ts +217 -44
  208. package/src/providers/integrations/github/github-quota.ts +22 -5
  209. package/src/providers/integrations/github/github.ts +40 -32
  210. package/src/providers/integrations/grok/grok-oauth.ts +4 -4
  211. package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
  212. package/src/providers/integrations/hermes/hermes.ts +34 -5
  213. package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
  214. package/src/providers/integrations/kilo/kilo.ts +1 -1
  215. package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
  216. package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
  217. package/src/providers/integrations/muse/muse-oauth.ts +5 -5
  218. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  219. package/src/providers/integrations/openrouter/provider.ts +1 -1
  220. package/src/providers/integrations/perch/perch-protocol.ts +0 -3
  221. package/src/providers/integrations/perch/perch.ts +3 -3
  222. package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
  223. package/src/providers/integrations/qoder/qoder-adapter.ts +19 -113
  224. package/src/providers/integrations/qoder/qoder-auth.ts +8 -1
  225. package/src/providers/integrations/qoder/qoder-catalog.ts +1 -1
  226. package/src/providers/integrations/qoder/qoder-chat.ts +25 -8
  227. package/src/providers/integrations/qoder/qoder-checkin.ts +1 -1
  228. package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
  229. package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
  230. package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
  231. package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
  232. package/src/providers/integrations/qoder/qoder-types.ts +9 -0
  233. package/src/providers/integrations/qoder/qoder.ts +3 -25
  234. package/src/providers/integrations/sensenova/provider.ts +144 -0
  235. package/src/providers/integrations/trae/trae-quota.ts +15 -1
  236. package/src/providers/integrations/vercel/provider.ts +117 -0
  237. package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
  238. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  239. package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
  240. package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
  241. package/src/providers/integrations/zro/zro.ts +2 -1
  242. package/src/providers/operations/account-refresh-status.ts +14 -0
  243. package/src/providers/operations/client-versions.ts +95 -30
  244. package/src/providers/operations/oauth-refresh-lead.ts +46 -22
  245. package/src/providers/operations/provider-caches.ts +1 -1
  246. package/src/providers/operations/provider-credential-service.ts +22 -25
  247. package/src/providers/operations/session-resolution.ts +17 -1
  248. package/src/providers/provider-contracts.ts +2 -2
  249. package/src/providers/provider-metadata.ts +30 -0
  250. package/src/providers/quota/quota-contracts.ts +2 -0
  251. package/src/providers/quota/refresh.ts +25 -3
  252. package/src/security/ip-boundary.ts +1 -1
  253. package/src/transport/dispatch/attempt-finalize.ts +1 -1
  254. package/src/transport/dispatch/attempt-loop.ts +3 -3
  255. package/src/transport/dispatch/handler-deps.ts +1 -1
  256. package/src/transport/dispatch/leases.ts +1 -1
  257. package/src/transport/dispatch/retry-policy.ts +1 -1
  258. package/src/transport/dispatch/streaming/finalize.ts +2 -2
  259. package/src/transport/dispatch/streaming/stream.ts +1 -1
  260. package/src/transport/dispatch/upstream.ts +1 -1
  261. package/src/transport/shutdown-notice.ts +1 -1
  262. package/src/workers/buddy-growth-tasks.ts +8 -8
  263. package/src/workers/daily-checkin.ts +4 -4
  264. package/src/workers/quota-refresh-sweep.ts +1 -1
  265. package/src/workers/zcode-gift-claim.ts +1 -1
  266. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
  267. package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
  268. package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
  269. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
  270. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
  271. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
  272. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
  273. package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
  274. package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
  275. package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
  276. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
  277. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +0 -8
  278. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
  279. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
  280. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
  281. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js +0 -9
  282. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
  283. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
  284. package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
  285. package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
  286. package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
  287. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
  288. package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
  289. package/dist/dashboard/assets/app-Dx2QgsNj.css +0 -2
  290. package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
  291. package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
  292. package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
  293. package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
  294. package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
  295. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
  296. package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
  297. package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
  298. package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
  299. package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
  300. package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
  301. package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
  302. package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
  303. package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
  304. package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
  305. package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
  306. package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
  307. package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
  308. package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
  309. package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
  310. package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
  311. package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
  312. package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
  313. package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
  314. package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
  315. package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
  316. package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
  317. package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
  318. package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
  319. package/src/console/providers/catalog/discover-constants.ts +0 -6
  320. package/src/network/pool-health.ts +0 -198
  321. package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
  322. package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
  323. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
  324. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
  325. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
  326. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
  327. package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
  328. package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
  329. package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
  330. package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
  331. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
  332. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -188
  333. package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
  334. package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -179
  335. package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
  336. package/src/providers/integrations/qoder/chat-proto.ts +0 -328
  337. package/src/providers/integrations/qoder/qoder-grpc.ts +0 -870
  338. package/src/providers/integrations/qoder/qoder-ide.ts +0 -481
  339. /package/dist/dashboard/assets/{app-DIXLjojg.js → app-C0gTXEoN.js} +0 -0
  340. /package/dist/dashboard/assets/{app-DIXLjojg.js.gz → app-C0gTXEoN.js.gz} +0 -0
  341. /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
  342. /package/src/network/pool/{byte-accounting.ts → accounting.ts} +0 -0
  343. /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
  344. /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
  345. /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
  346. /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
  347. /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
  348. /package/src/network/{rate-limit-verdict.ts → verdict.ts} +0 -0
@@ -0,0 +1,149 @@
1
+ // DeepAI model catalog + discovery.
2
+ //
3
+ // All 22 rows below were fetched live from `GET /deepai_code/models` on
4
+ // 2026-11-11 (see `documentation/research/deepai/2026-11-11-deepai-code-live-wire-verification.md`).
5
+ // The 11 zero-Theos rows carry the daily free-tier allowance (`freeTier: true`
6
+ // + `free: true` — they bill zero and do not draw from the balance). The
7
+ // metered rows keep their `theosPerRequest` price as a per-request cost
8
+ // estimate for the operator.
9
+ //
10
+ // Wire routing: every row is a chat-wire model except `gpt-6-astra`, which the
11
+ // upstream serves only on `/responses` (verified live: it answers 400
12
+ // `model_not_found` on `/chat/completions`, and the rest answer 400 on
13
+ // `/responses`).
14
+ import type { ModelDefinition } from "../../provider-model-definition";
15
+ import { defineModel } from "../../provider-model-definition";
16
+ import {
17
+ DEEPAI_CHAT_PATH,
18
+ DEEPAI_HEADER_INSTANCE,
19
+ DEEPAI_HEADER_THEOS,
20
+ DEEPAI_LANE_VERSION,
21
+ DEEPAI_MODELS_PATH,
22
+ DEEPAI_RESPONSES_PATH,
23
+ deepAIInstanceId,
24
+ } from "./deepai-common";
25
+
26
+ interface DeepAIRosterRow {
27
+ readonly id: string;
28
+ readonly name: string;
29
+ readonly reasoning: boolean;
30
+ readonly vision: boolean;
31
+ readonly ctx: number;
32
+ readonly out: number;
33
+ readonly theosPerRequest: number;
34
+ readonly responsesWire?: boolean;
35
+ }
36
+
37
+ const DEEPAI_ROSTER: readonly DeepAIRosterRow[] = [
38
+ { id: "glm-5.3-flash", name: "GLM 5.3 Flash", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
39
+ { id: "deepseek-v4.1-flash", name: "DeepSeek V4.1 Flash 1017", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
40
+ { id: "mimo-2.6-flash", name: "MiMo 2.6 Flash", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
41
+ { id: "qwen-3.8-flash", name: "Qwen 3.8 Flash", reasoning: true, vision: true, ctx: 991_000, out: 128_000, theosPerRequest: 0 },
42
+ { id: "minimax-m3.1-flash", name: "MiniMax M3.1 Flash", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
43
+ { id: "solar-mini-4", name: "Solar Mini 4", reasoning: true, vision: false, ctx: 524_288, out: 131_072, theosPerRequest: 0 },
44
+ { id: "gpt-6-luna", name: "GPT-6 Luna", reasoning: true, vision: true, ctx: 1_048_576, out: 128_000, theosPerRequest: 0 },
45
+ { id: "mimo-2.6-pro", name: "MiMo 2.6 Pro", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
46
+ { id: "crouton-alpha", name: "Crouton Alpha", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
47
+ { id: "glyph-cluster", name: "Glyph Cluster", reasoning: true, vision: false, ctx: 256_000, out: 131_072, theosPerRequest: 0 },
48
+ { id: "deepai-cyber", name: "DeepAI Cyber", reasoning: true, vision: false, ctx: 1_048_576, out: 131_072, theosPerRequest: 0 },
49
+ { id: "minimax-m3", name: "MiniMax M3", reasoning: true, vision: false, ctx: 512_000, out: 131_000, theosPerRequest: 2 },
50
+ { id: "gpt-6.1-sol", name: "GPT-6.1 Sol", reasoning: true, vision: true, ctx: 1_048_576, out: 128_000, theosPerRequest: 10 },
51
+ { id: "gemini-4-argon", name: "Gemini 4 Argon", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 10 },
52
+ { id: "glm-5.3", name: "GLM 5.3", reasoning: true, vision: false, ctx: 1_048_576, out: 131_072, theosPerRequest: 3 },
53
+ { id: "kimi-k3", name: "Kimi K3", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 3 },
54
+ { id: "muse-spark-1.3", name: "Muse Spark 1.3", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 6 },
55
+ { id: "claude-opus-5-5", name: "Claude Opus 5.5", reasoning: true, vision: true, ctx: 1_000_000, out: 64_000, theosPerRequest: 15 },
56
+ { id: "muse-wonder", name: "Muse Wonder", reasoning: true, vision: true, ctx: 1_048_576, out: 131_072, theosPerRequest: 15 },
57
+ { id: "qwen-3.8-max-prime", name: "Qwen 3.8 Max Prime", reasoning: true, vision: true, ctx: 1_000_000, out: 131_072, theosPerRequest: 15 },
58
+ { id: "claude-fable-5-2", name: "Claude Fable 5.2", reasoning: true, vision: true, ctx: 1_000_000, out: 64_000, theosPerRequest: 20 },
59
+ { id: "gpt-6-astra", name: "GPT-6 Astra", reasoning: true, vision: true, ctx: 1_048_576, out: 128_000, theosPerRequest: 50, responsesWire: true },
60
+ ];
61
+
62
+ function rowToModel(row: DeepAIRosterRow): ModelDefinition {
63
+ const free = row.theosPerRequest === 0;
64
+ return defineModel({
65
+ id: row.id,
66
+ providerId: "deepai",
67
+ wireFamily: row.responsesWire === true ? "responses" : "chat",
68
+ endpoint: row.responsesWire === true ? DEEPAI_RESPONSES_PATH : DEEPAI_CHAT_PATH,
69
+ ctx: row.ctx,
70
+ out: row.out,
71
+ vision: row.vision,
72
+ reasoning: row.reasoning,
73
+ toolCall: true,
74
+ ...(free ? { free: true, freeTier: true } : {}),
75
+ });
76
+ }
77
+
78
+ /** Static roster — the live-verified free tier plus metered rows. */
79
+ export const DEEPAI_DEFAULT_MODELS: readonly ModelDefinition[] =
80
+ DEEPAI_ROSTER.map(rowToModel);
81
+
82
+ /** Live `/deepai_code/models` listing. */
83
+ export async function discoverDeepAIModels(options: {
84
+ readonly baseUrl: string;
85
+ readonly fetcher?: typeof fetch;
86
+ }): Promise<readonly ModelDefinition[] | null> {
87
+ const fetcher = options.fetcher ?? fetch;
88
+ const url = `${options.baseUrl.replace(/\/+$/, "")}${DEEPAI_MODELS_PATH}`;
89
+ try {
90
+ const response = await fetcher(url, {
91
+ headers: {
92
+ accept: "application/json",
93
+ // The catalog is lane-aware: the 22-row roster only comes back with the
94
+ // lane headers (verified live 2026-11-11 — bare GET returns a smaller
95
+ // 9-row default roster).
96
+ [DEEPAI_HEADER_THEOS]: DEEPAI_LANE_VERSION,
97
+ [DEEPAI_HEADER_INSTANCE]: deepAIInstanceId(),
98
+ },
99
+ signal: AbortSignal.timeout(10_000),
100
+ });
101
+ if (!response.ok) return null;
102
+ const payload = (await response.json()) as unknown;
103
+ const list = payload !== null && typeof payload === "object" && Array.isArray((payload as { models?: unknown }).models)
104
+ ? ((payload as { models: unknown }).models as readonly Record<string, unknown>[])
105
+ : null;
106
+ if (list === null || list.length === 0) return null;
107
+ const rows: DeepAIRosterRow[] = [];
108
+ for (const entry of list) {
109
+ const id = typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : null;
110
+ if (id === null) continue;
111
+ // `theosPerRequest` arrives as a string ("0", "2", "50") or a number.
112
+ const theosNumber = parseTheosAmount(entry.theosPerRequest);
113
+ const input = entry.input;
114
+ const hasImage = Array.isArray(input) && input.some((m) => m === "image");
115
+ rows.push({
116
+ id,
117
+ name: typeof entry.name === "string" ? entry.name : id,
118
+ reasoning: entry.reasoning === true,
119
+ vision: hasImage,
120
+ ctx: positiveInt(entry.contextWindow) ?? positiveInt(entry.context_window) ?? 1_048_576,
121
+ out: positiveInt(entry.maxTokens) ?? positiveInt(entry.max_completion_tokens) ?? 131_072,
122
+ theosPerRequest: theosNumber,
123
+ responsesWire: entry.api === "openai-responses",
124
+ });
125
+ }
126
+ if (rows.length === 0) return null;
127
+ return rows.map(rowToModel);
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+
133
+ function positiveInt(value: unknown): number | null {
134
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : null;
135
+ }
136
+
137
+ /** Parses `theosPerRequest` — a numeric string ("0"/"50") or a number. */
138
+ function parseTheosAmount(value: unknown): number {
139
+ if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, value);
140
+ if (typeof value === "string") {
141
+ const trimmed = value.trim();
142
+ if (/^\d+(?:\.\d+)?$/.test(trimmed)) {
143
+ const n = Number(trimmed);
144
+ if (Number.isFinite(n)) return Math.max(0, n);
145
+ }
146
+ }
147
+ // Absent / unparseable means the server did not meter it → free.
148
+ return 0;
149
+ }
@@ -0,0 +1,145 @@
1
+ // DeepAI shared wire helpers.
2
+ //
3
+ // Wire facts reconstructed from the DeepAI Code 0.3.35 bundle and verified
4
+ // live 2026-11-11 (see `documentation/research/deepai/`):
5
+ //
6
+ // - Model base is `https://api.deepai.org/deepai_code`; the sign-in flow
7
+ // (`/deepai_code/desktop_auth/`) returns an opaque `api_key`.
8
+ // - Every model call carries:
9
+ // `X-DeepAI-Code-Theos: 4` (lane version)
10
+ // `X-DeepAI-Code-Instance` (16 random bytes hex, per process)
11
+ // `X-DeepAI-Code-Machine` (sha256hex of the machine id)
12
+ // `Api-Key: <api_key>` (the credential; NOT a Bearer)
13
+ // - The machine header is a digest of a platform identifier (SMBIOS UUID /
14
+ // MachineGuid / `/etc/machine-id`); the raw identifier never leaves the
15
+ // machine. The grant is bound to the account, not the machine, so a
16
+ // missing/mangled machine header is tolerated (verified live).
17
+ import { randomBytes } from "node:crypto";
18
+ import { createHash } from "node:crypto";
19
+ import { execFile } from "node:child_process";
20
+ import { readFileSync } from "node:fs";
21
+
22
+ export const DEEPAI_PROVIDER_ID = "deepai" as const;
23
+ export const DEEPAI_API_ORIGIN = "https://api.deepai.org";
24
+ export const DEEPAI_MODEL_BASE = `${DEEPAI_API_ORIGIN}/deepai_code`;
25
+
26
+ export const DEEPAI_LANE_VERSION = "4";
27
+ export const DEEPAI_HEADER_THEOS = "X-DeepAI-Code-Theos";
28
+ export const DEEPAI_HEADER_INSTANCE = "X-DeepAI-Code-Instance";
29
+ export const DEEPAI_HEADER_MACHINE = "X-DeepAI-Code-Machine";
30
+ export const DEEPAI_HEADER_API_KEY = "Api-Key";
31
+
32
+ export const DEEPAI_CHAT_PATH = "/deepai_code/chat/completions";
33
+ export const DEEPAI_RESPONSES_PATH = "/deepai_code/responses";
34
+ export const DEEPAI_MODELS_PATH = "/deepai_code/models";
35
+ export const DEEPAI_THEOS_PATH = "/deepai_code/theos";
36
+ export const DEEPAI_USAGE_SUMMARY_PATH = "/get_usage_summary";
37
+ export const DEEPAI_SIGNIN_PATH = "/deepai_code/desktop_auth/";
38
+ export const DEEPAI_EXCHANGE_PATH = "/deepai_code/desktop_auth/exchange/";
39
+
40
+ const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
41
+ const SMBIOS_PLACEHOLDERS = new Set([
42
+ "00000000-0000-0000-0000-000000000000",
43
+ "ffffffff-ffff-ffff-ffff-ffffffffffff",
44
+ "03000200-0400-0500-0006-000700080009",
45
+ ]);
46
+
47
+ function run(file: string, args: readonly string[]): Promise<string> {
48
+ return new Promise((resolve) => {
49
+ execFile(
50
+ file,
51
+ [...args],
52
+ { encoding: "utf8", timeout: 10_000, windowsHide: true },
53
+ (error, stdout) => resolve(error ? "" : stdout),
54
+ );
55
+ });
56
+ }
57
+
58
+ function parseSmbiosUUID(output: string): string | null {
59
+ const uuid = UUID_RE.exec(output)?.[0]?.toLowerCase() ?? null;
60
+ return uuid && !SMBIOS_PLACEHOLDERS.has(uuid) ? uuid : null;
61
+ }
62
+
63
+ function parseMachineGuid(regQuery: string): string | null {
64
+ const match = /MachineGuid\s+REG_SZ\s+(\S+)/.exec(regQuery);
65
+ return match?.[1] && UUID_RE.test(match[1]) ? match[1].toLowerCase() : null;
66
+ }
67
+
68
+ function parseLinuxMachineId(file: string): string | null {
69
+ const id = file.trim().toLowerCase();
70
+ return /^[0-9a-f]{32}$/.test(id) && !/^0+$/.test(id) ? id : null;
71
+ }
72
+
73
+ function detectMachineId(): Promise<string | null> {
74
+ try {
75
+ if (process.platform === "win32") {
76
+ return (async () => {
77
+ const smbios = await run("powershell", [
78
+ "-NoProfile",
79
+ "-NonInteractive",
80
+ "-Command",
81
+ "(Get-CimInstance Win32_ComputerSystemProduct).UUID",
82
+ ]);
83
+ const uuid = parseSmbiosUUID(smbios);
84
+ if (uuid) return uuid;
85
+ const guid = await run("reg", [
86
+ "query",
87
+ "HKLM\\SOFTWARE\\Microsoft\\Cryptography",
88
+ "/v",
89
+ "MachineGuid",
90
+ ]);
91
+ return parseMachineGuid(guid);
92
+ })();
93
+ }
94
+ if (process.platform === "darwin") {
95
+ return (async () => {
96
+ const ioreg = await run("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"]);
97
+ const match = /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(ioreg);
98
+ return match?.[1] && UUID_RE.test(match[1]) ? match[1].toLowerCase() : null;
99
+ })();
100
+ }
101
+ for (const path of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
102
+ try {
103
+ const id = parseLinuxMachineId(readFileSync(path, "utf8"));
104
+ if (id) return Promise.resolve(id);
105
+ } catch {
106
+ // try the next path
107
+ }
108
+ }
109
+ return Promise.resolve(null);
110
+ } catch {
111
+ return Promise.resolve(null);
112
+ }
113
+ }
114
+
115
+ let machineDigestPromise: Promise<string | null> | undefined;
116
+
117
+ /** sha256hex(machineId), computed once per process; null when unreadable. */
118
+ export function deepAIMachineDigest(): Promise<string | null> {
119
+ machineDigestPromise ??= detectMachineId().then(
120
+ (raw) => (raw === null ? null : createHash("sha256").update(raw).digest("hex")),
121
+ );
122
+ return machineDigestPromise;
123
+ }
124
+
125
+ /** Fresh per-process instance id (16 random bytes hex). */
126
+ export function deepAIInstanceId(): string {
127
+ return randomBytes(16).toString("hex");
128
+ }
129
+
130
+ /**
131
+ * Baseline DeepAI headers shared by every model API call. `apiKey` is the
132
+ * account credential (the sign-in `api_key`); omit it for unauthenticated
133
+ * reads (model catalog).
134
+ */
135
+ export async function deepAIBaseHeaders(apiKey?: string): Promise<Record<string, string>> {
136
+ const headers: Record<string, string> = {
137
+ "content-type": "application/json",
138
+ [DEEPAI_HEADER_THEOS]: DEEPAI_LANE_VERSION,
139
+ [DEEPAI_HEADER_INSTANCE]: deepAIInstanceId(),
140
+ };
141
+ const machine = await deepAIMachineDigest();
142
+ if (machine !== null) headers[DEEPAI_HEADER_MACHINE] = machine;
143
+ if (apiKey !== undefined && apiKey.length > 0) headers[DEEPAI_HEADER_API_KEY] = apiKey;
144
+ return headers;
145
+ }
@@ -0,0 +1,129 @@
1
+ // DeepAI browser login client.
2
+ //
3
+ // DeepAI's desktop sign-in is a loopback flow: the app builds
4
+ // `https://api.deepai.org/deepai_code/desktop_auth/?state=<state>&port=<port>`,
5
+ // the operator signs in in the browser, and the server redirects to
6
+ // `http://127.0.0.1:<port>/callback?code=<code>&state=<state>`. The code is
7
+ // then exchanged at `/deepai_code/desktop_auth/exchange/` for an opaque
8
+ // `api_key`. There is no PKCE verifier and no refresh token — the key is
9
+ // long-lived and the flow is re-run when needed.
10
+ //
11
+ // Verified live 2026-11-11: the exchange answers 200 with
12
+ // `{ api_key, username, is_pro }`, and the api_key authenticates the model
13
+ // API via the `Api-Key` header (a Bearer or `x-api-key` spelling is rejected
14
+ // with 401).
15
+ import { randomBytes } from "node:crypto";
16
+ import { OAuthClient } from "../../authentication/oauth-client";
17
+ import type {
18
+ OAuthAuthorizeRequest,
19
+ OAuthCodeExchangeContext,
20
+ OAuthExchangeResult,
21
+ OAuthImportInput,
22
+ } from "../../authentication/oauth-flow-store";
23
+ import {
24
+ DEEPAI_API_ORIGIN,
25
+ DEEPAI_EXCHANGE_PATH,
26
+ DEEPAI_THEOS_PATH,
27
+ deepAIBaseHeaders,
28
+ } from "./deepai-common";
29
+
30
+ const DEEPAI_LOGIN_TTL_MS = 365 * 24 * 60 * 60 * 1000;
31
+
32
+ function loopbackPortFromRedirectUri(redirectUri: string): number {
33
+ const url = new URL(redirectUri);
34
+ const port = Number(url.port);
35
+ if (!Number.isInteger(port) || port <= 0 || port > 65_535) {
36
+ throw new Error("deepai: redirect URI names an invalid loopback port");
37
+ }
38
+ return port;
39
+ }
40
+
41
+ export class DeepAIOAuthClient extends OAuthClient {
42
+ override readonly supportsDeviceCode = false;
43
+ override readonly supportsBrowserCode = true;
44
+ override readonly providerLabel = "deepai";
45
+ override readonly clientId = "";
46
+ override readonly tokenUrl = "";
47
+ override readonly scopes = "";
48
+
49
+ /**
50
+ * DeepAI's hosted sign-in validates the state as 32 hex characters; the
51
+ * shared base64url state (which can contain `-`/`_`) is rejected with
52
+ * "Invalid desktop sign-in request".
53
+ */
54
+ override generateState(): string {
55
+ return randomBytes(16).toString("hex");
56
+ }
57
+
58
+ /** DeepAI's hosted sign-in URL, carrying the loopback port + state. */
59
+ override buildAuthorizeUrl(request: OAuthAuthorizeRequest): string {
60
+ const port = loopbackPortFromRedirectUri(request.redirectUri);
61
+ const signIn = new URL(DEEPAI_EXCHANGE_PATH.replace("/desktop_auth/exchange/", "/desktop_auth/"), DEEPAI_API_ORIGIN);
62
+ signIn.search = "";
63
+ signIn.searchParams.set("state", request.state);
64
+ signIn.searchParams.set("port", String(port));
65
+ return signIn.toString();
66
+ }
67
+
68
+ /** Exchanges the callback code for the DeepAI api_key. */
69
+ override async exchangeCode(
70
+ code: string,
71
+ _codeVerifier: string,
72
+ _redirectUri: string,
73
+ _state?: string,
74
+ _context?: OAuthCodeExchangeContext,
75
+ ): Promise<OAuthExchangeResult> {
76
+ const response = await this.fetchFn(new URL(DEEPAI_EXCHANGE_PATH, DEEPAI_API_ORIGIN), {
77
+ method: "POST",
78
+ headers: { "content-type": "application/json", accept: "application/json" },
79
+ body: JSON.stringify({ code }),
80
+ });
81
+ if (!response.ok) {
82
+ const text = await response.text().catch(() => "");
83
+ throw new Error(`deepai sign-in exchange failed: HTTP ${response.status} ${text.slice(0, 300)}`);
84
+ }
85
+ return this.parseExchangeBody(await response.json());
86
+ }
87
+
88
+ /** Accepts a pasted DeepAI api_key, validated against `/theos`. */
89
+ async importCredential(input: OAuthImportInput): Promise<OAuthExchangeResult> {
90
+ const apiKey = input.credential.trim();
91
+ if (!apiKey) throw new Error("deepai: pasted API key is empty");
92
+ const headers = await deepAIBaseHeaders(apiKey);
93
+ const response = await this.fetchFn(new URL(DEEPAI_THEOS_PATH, DEEPAI_API_ORIGIN), {
94
+ method: "GET",
95
+ headers,
96
+ });
97
+ if (!response.ok) {
98
+ throw new Error(
99
+ `deepai: API key validation failed (HTTP ${response.status}) — paste a key minted by the DeepAI sign-in flow`,
100
+ );
101
+ }
102
+ return {
103
+ access: apiKey,
104
+ refresh: apiKey,
105
+ expiresAt: new Date(Date.now() + DEEPAI_LOGIN_TTL_MS),
106
+ accountLabel: input.accountLabel || "deepai",
107
+ auth_state: { via: "import" },
108
+ };
109
+ }
110
+
111
+ private parseExchangeBody(body: unknown): OAuthExchangeResult {
112
+ const record = body !== null && typeof body === "object" ? (body as Record<string, unknown>) : null;
113
+ const apiKey = typeof record?.api_key === "string" ? record.api_key.trim() : "";
114
+ if (!apiKey || /[\r\n]/.test(apiKey)) {
115
+ throw new Error("deepai: sign-in response did not carry a valid api_key");
116
+ }
117
+ const username = typeof record?.username === "string" ? record.username : "";
118
+ return {
119
+ access: apiKey,
120
+ refresh: apiKey,
121
+ expiresAt: new Date(Date.now() + DEEPAI_LOGIN_TTL_MS),
122
+ ...(username ? { accountLabel: username, accountName: username } : {}),
123
+ auth_state: { is_pro: record?.is_pro === true },
124
+ };
125
+ }
126
+ }
127
+
128
+ /** Shared DeepAI browser-login client instance (registry-facing). */
129
+ export const deepAIOAuthClient = new DeepAIOAuthClient();
@@ -0,0 +1,97 @@
1
+ // DeepAI quota collector.
2
+ //
3
+ // Reads the account's DeepAI allowance from `GET /deepai_code/theos`
4
+ // (`balance`/`granted`/`refresh_at`), which is the daily free-tier grant. The
5
+ // entitlement surface (`/get_usage_summary`, `is_pro`) is probed second to
6
+ // distinguish a free vs pro account. Both calls require the `Api-Key` header
7
+ // (not Bearer).
8
+ import type {
9
+ FetchLike,
10
+ ProviderQuotaResult,
11
+ ProviderQuotaWindow,
12
+ } from "../../quota/quota-contracts";
13
+ import { cleanError, number, percentWindow, text } from "../../quota/quota-contracts";
14
+ import type { QuotaCollectionContext } from "../../quota/quota-support";
15
+ import {
16
+ DEEPAI_API_ORIGIN,
17
+ DEEPAI_THEOS_PATH,
18
+ DEEPAI_USAGE_SUMMARY_PATH,
19
+ deepAIBaseHeaders,
20
+ } from "./deepai-common";
21
+
22
+ const LABEL = "DeepAI";
23
+
24
+ async function readJson(
25
+ url: string,
26
+ headers: Record<string, string>,
27
+ fetcher: FetchLike,
28
+ ): Promise<Record<string, unknown> | null> {
29
+ try {
30
+ const res = await fetcher(url, {
31
+ method: "GET",
32
+ headers,
33
+ signal: AbortSignal.timeout(15_000),
34
+ });
35
+ if (!res.ok) return null;
36
+ const parsed = (await res.json()) as unknown;
37
+ return parsed !== null && typeof parsed === "object" ? (parsed as Record<string, unknown>) : null;
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Reads the DeepAI allowance and returns a normalized result.
45
+ *
46
+ * The Daily Theos window is pooled (the balance funds model calls) with the
47
+ * daily grant as the ceiling and the account's `refresh_at` as the reset. The
48
+ * plan is `free` when the allowance is enabled, `pro` when `is_pro` reads
49
+ * true from the usage summary.
50
+ */
51
+ export async function fetchDeepAIQuota(
52
+ credential: string,
53
+ fetcher: FetchLike,
54
+ _context?: QuotaCollectionContext,
55
+ ): Promise<ProviderQuotaResult> {
56
+ try {
57
+ const apiKey = text(credential);
58
+ if (!apiKey) {
59
+ return { source: LABEL, plan: null, windows: [], error: "DeepAI credential missing" };
60
+ }
61
+ const headers = await deepAIBaseHeaders(apiKey);
62
+ const theos = await readJson(`${DEEPAI_API_ORIGIN}${DEEPAI_THEOS_PATH}`, headers, fetcher);
63
+ if (!theos) {
64
+ return { source: LABEL, plan: null, windows: [], error: "DeepAI /theos endpoint unavailable" };
65
+ }
66
+
67
+ const windows: ProviderQuotaWindow[] = [];
68
+ const enabled = theos.enabled === true;
69
+ const granted = number(theos.granted);
70
+ const balance = number(theos.balance);
71
+ if (enabled && granted !== null && granted > 0) {
72
+ const used = balance === null ? null : Math.max(0, granted - balance);
73
+ const resetsAt =
74
+ typeof theos.refresh_at === "string" && Number.isFinite(Date.parse(theos.refresh_at))
75
+ ? new Date(theos.refresh_at).toISOString()
76
+ : null;
77
+ windows.push(
78
+ percentWindow(
79
+ "theos_daily",
80
+ "Daily Theos",
81
+ used === null ? null : (used / granted) * 100,
82
+ resetsAt,
83
+ used,
84
+ granted,
85
+ "pooled",
86
+ ),
87
+ );
88
+ }
89
+
90
+ const summary = await readJson(`${DEEPAI_API_ORIGIN}${DEEPAI_USAGE_SUMMARY_PATH}`, headers, fetcher);
91
+ const isPro = summary?.is_pro === true;
92
+ const plan = enabled ? (isPro ? "pro" : "free") : null;
93
+ return { source: LABEL, plan, windows, error: null };
94
+ } catch (error) {
95
+ return { source: LABEL, plan: null, windows: [], error: cleanError(error) };
96
+ }
97
+ }
@@ -0,0 +1,42 @@
1
+ // DeepAI provider — TypeScript, DeepAI Code model API, Free Limited.
2
+ //
3
+ // DeepAI Code's model API (`https://api.deepai.org/deepai_code`) is served
4
+ // with a non-Bearer credential: the sign-in flow returns an opaque `api_key`
5
+ // that travels in the `Api-Key` header (a Bearer or `x-api-key` spelling is
6
+ // rejected with 401 — verified live 2026-11-11). Every model call also needs
7
+ // the lane headers (`X-DeepAI-Code-Theos` / `-Instance` / `-Machine`), which
8
+ // the adapter stamps per dispatch. The daily free allowance is 50 Theos and
9
+ // the 11 zero-Theos models draw nothing from it.
10
+ //
11
+ // Verified live 2026-11-11 (evidence: `documentation/research/deepai/`):
12
+ // - `GET /deepai_code/models` → 200, 22 rows (unauthenticated).
13
+ // - `POST /deepai_code/chat/completions` → 200 stream + non-stream with
14
+ // lane headers + `Api-Key`.
15
+ // - `POST /deepai_code/responses` → 200 SSE for `gpt-6-astra` only.
16
+ // - `GET /deepai_code/theos` → 200 allowance (balance/granted/refresh).
17
+ import type { ApiKeyProviderSpec } from "../../configured-provider";
18
+ import {
19
+ DEEPAI_API_ORIGIN,
20
+ DEEPAI_CHAT_PATH,
21
+ DEEPAI_RESPONSES_PATH,
22
+ deepAIBaseHeaders,
23
+ } from "./deepai-common";
24
+ import type { ProviderDispatchContext } from "../../provider-contracts";
25
+
26
+ export const DEEPAI_SPEC: ApiKeyProviderSpec = {
27
+ provider_id: "deepai",
28
+ base_url: DEEPAI_API_ORIGIN,
29
+ endpoint_paths_by_wire_family: {
30
+ chat: DEEPAI_CHAT_PATH,
31
+ responses: DEEPAI_RESPONSES_PATH,
32
+ },
33
+ // The credential is not a bearer: sending `Authorization` would be wrong
34
+ // (and the upstream rejects a stray bearer). `buildExtraHeaders` stamps the
35
+ // `Api-Key` + lane headers instead.
36
+ credential_forwarding: "never",
37
+ buildExtraHeaders: async (context: ProviderDispatchContext) => {
38
+ const secret = context.credential.secret;
39
+ const apiKey = secret === undefined || secret.length === 0 ? "" : new TextDecoder().decode(secret);
40
+ return deepAIBaseHeaders(apiKey);
41
+ },
42
+ };
@@ -137,6 +137,13 @@ export async function resolveDevinAccountLabel(
137
137
 
138
138
  const DEVIN_AUTHORIZE_URL = "https://app.devin.ai/auth/cli/continue";
139
139
  const DEVIN_TOKEN_URL = "https://api.devin.ai/auth/cli/token";
140
+ // Devin's authorization server only accepts this exact loopback redirect; a
141
+ // public-origin redirect is refused with "Invalid redirect URI". The browser
142
+ // opens it on the operator's own machine (the loopback listener belongs to the
143
+ // desktop flow), so the gateway cannot intercept it — the operator pastes the
144
+ // resulting URL back into the modal. On a server, the browser will show
145
+ // "can't be reached" for 127.0.0.1: that is expected; the address bar still
146
+ // carries the `code` to paste.
140
147
  const DEVIN_BROWSER_REDIRECT_URI = "http://127.0.0.1:59653/callback";
141
148
 
142
149
  export const devinOAuthClient: OAuthLoginClient = {
@@ -30,6 +30,7 @@ import type {
30
30
  import {
31
31
  decodeJwtPayload,
32
32
  nonEmptyTrimmedString,
33
+ oauthAccountIdentity,
33
34
  readJsonResponse,
34
35
  record,
35
36
  } from "../../authentication/oauth-flow-store";
@@ -104,9 +105,11 @@ function buildEnvelope(
104
105
  // cause; failing upstream would surface as an opaque 401.
105
106
  throw new Error("Factory Droid token carries no organization claim; sign in again");
106
107
  }
107
- const email = nonEmptyTrimmedString(claims?.["email"]);
108
- const subject = nonEmptyTrimmedString(claims?.["sub"]);
109
- const label = accountLabel ?? email ?? subject;
108
+ const responseIdentity = oauthAccountIdentity(payload);
109
+ const email = nonEmptyTrimmedString(claims?.["email"]) ?? responseIdentity.accountEmail;
110
+ const accountName = responseIdentity.accountName ?? nonEmptyTrimmedString(claims?.["name"]);
111
+ const subject = nonEmptyTrimmedString(claims?.["sub"]) ?? nonEmptyTrimmedString(record(payload["user"])?.["id"]);
112
+ const label = accountLabel ?? email ?? accountName ?? subject;
110
113
  const envelope: FactoryDroidEnvelope = { accessToken, orgId };
111
114
  const result: OAuthExchangeResult = {
112
115
  access: encodeFactoryDroidCredential(envelope),
@@ -117,12 +120,15 @@ function buildEnvelope(
117
120
  refresh: nonEmptyTrimmedString(payload["refresh_token"]) ?? accessToken,
118
121
  expiresAt: expiryFromClaims(claims),
119
122
  ...(label === undefined ? {} : { accountLabel: label }),
123
+ ...(email === undefined ? {} : { accountEmail: email }),
124
+ ...(accountName === undefined ? {} : { accountName }),
120
125
  };
121
126
  return {
122
127
  result,
123
128
  authState: {
124
129
  orgId,
125
130
  ...(email === undefined ? {} : { email }),
131
+ ...(accountName === undefined ? {} : { accountName }),
126
132
  ...(subject === undefined ? {} : { userId: subject }),
127
133
  },
128
134
  };
@@ -139,7 +145,7 @@ export class FactoryOAuthClient extends OAuthDeviceFlow {
139
145
  protected override readonly scopes = "";
140
146
 
141
147
  override async startDeviceAuth(
142
- _context?: OAuthDeviceFlowContext,
148
+ context?: OAuthDeviceFlowContext,
143
149
  ): Promise<OAuthDeviceStartResult> {
144
150
  return this.startGenericDeviceAuth({
145
151
  tokenUrl: FACTORY_DROID_TOKEN_URL,
@@ -147,7 +153,7 @@ export class FactoryOAuthClient extends OAuthDeviceFlow {
147
153
  clientId: FACTORY_DROID_CLIENT_ID,
148
154
  formHeaders: WORKOS_HEADERS,
149
155
  startParams: { user_agent: "cartethyia" },
150
- });
156
+ }, context);
151
157
  }
152
158
 
153
159
  /**
@@ -159,14 +165,14 @@ export class FactoryOAuthClient extends OAuthDeviceFlow {
159
165
  */
160
166
  override async pollDeviceAuth(
161
167
  deviceAuthId: string,
162
- _context?: OAuthDeviceFlowContext,
168
+ context?: OAuthDeviceFlowContext,
163
169
  ): Promise<OAuthDevicePollResult> {
164
170
  return this.pollGenericDeviceAuth(deviceAuthId, {
165
171
  tokenUrl: FACTORY_DROID_TOKEN_URL,
166
172
  deviceAuthUrl: FACTORY_DROID_DEVICE_AUTH_URL,
167
173
  clientId: FACTORY_DROID_CLIENT_ID,
168
174
  formHeaders: { ...WORKOS_HEADERS, "content-type": "application/x-www-form-urlencoded" },
169
- });
175
+ }, context);
170
176
  }
171
177
 
172
178
  protected override afterDeviceTokenExchange(
@@ -200,6 +206,9 @@ export class FactoryOAuthClient extends OAuthDeviceFlow {
200
206
  refresh: refreshToken,
201
207
  expiresAt: result.expiresAt,
202
208
  auth_state: authState,
209
+ ...(result.accountEmail === undefined ? {} : { accountEmail: result.accountEmail }),
210
+ ...(result.accountName === undefined ? {} : { accountName: result.accountName }),
211
+ ...(result.accountLabel === undefined ? {} : { accountLabel: result.accountLabel }),
203
212
  };
204
213
  }
205
214
  }