cartethyia 2.4.6 → 2.5.0

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 (302) hide show
  1. package/README.md +45 -13
  2. package/dist/dashboard/assets/BannedPage-9igfctfZ.js +1 -0
  3. package/dist/dashboard/assets/BannedPage-9igfctfZ.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-Bcy150Jo.js → CliToolDetailPage-ZRr2K2sF.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-ZRr2K2sF.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-DyNXefph.js → CliToolsPage-D3xqZbN-.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-D3xqZbN-.js.gz +0 -0
  8. package/dist/dashboard/assets/CombosPage-CVnQPBBz.js +1 -0
  9. package/dist/dashboard/assets/CombosPage-CVnQPBBz.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-CATpAEOz.js → ConsoleLogPage-u6W1UXSc.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-D6jEU7nL.js → CustomizationPage-CTTpCb25.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-CTTpCb25.js.gz +0 -0
  14. package/dist/dashboard/assets/{HealthEventsModal-CZkpn0a7.js → HealthEventsModal-C5XpWlvy.js} +1 -1
  15. package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js.gz +0 -0
  16. package/dist/dashboard/assets/{LoginPage-BJlMVN4U.js → LoginPage-DHoB3Agn.js} +1 -1
  17. package/dist/dashboard/assets/LoginPage-DHoB3Agn.js.gz +0 -0
  18. package/dist/dashboard/assets/{ModelPicker-Br3CXEau.js → ModelPicker-oQ-SDXp8.js} +1 -1
  19. package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
  20. package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js +1 -0
  21. package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js.gz +0 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js +3 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +1 -0
  25. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  26. package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js +1 -0
  27. package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js.gz +0 -0
  28. package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js +7 -0
  29. package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js.gz +0 -0
  30. package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js +1 -0
  31. package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js.gz +0 -0
  32. package/dist/dashboard/assets/{SettingsPage-BHW4Zt06.js → SettingsPage-CvjZxHE4.js} +1 -1
  33. package/dist/dashboard/assets/SettingsPage-CvjZxHE4.js.gz +0 -0
  34. package/dist/dashboard/assets/{SetupPage-VOYC_lIa.js → SetupPage-D3nza7LH.js} +1 -1
  35. package/dist/dashboard/assets/SetupPage-D3nza7LH.js.gz +0 -0
  36. package/dist/dashboard/assets/SortableList-Dtt12WT1.js +1 -0
  37. package/dist/dashboard/assets/SortableList-Dtt12WT1.js.gz +0 -0
  38. package/dist/dashboard/assets/{StudioPage-BNIDLiU6.js → StudioPage-CbK3hXvU.js} +4 -4
  39. package/dist/dashboard/assets/StudioPage-CbK3hXvU.js.gz +0 -0
  40. package/dist/dashboard/assets/UsagePage-BQlWbWg2.js +56 -0
  41. package/dist/dashboard/assets/UsagePage-BQlWbWg2.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BETfaMYZ.js → api-keys-BmDbsior.js} +1 -1
  43. package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
  44. package/dist/dashboard/assets/app-BbpSarVV.css +2 -0
  45. package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
  46. package/dist/dashboard/assets/assert-BHfaei-X.js +1 -0
  47. package/dist/dashboard/assets/assert-BHfaei-X.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-BTyGk_DL.js → badge-DpXT4Ixt.js} +1 -1
  49. package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-bujTWYM4.js → banner-CSAAWLJv.js} +1 -1
  51. package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-B0Bnx5CA.js → card-DEhRXXw4.js} +1 -1
  53. package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-DE2ug812.js → cli-tools-B0wBq-RL.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-R_rArH8S.js → clipboard-button-DOo6EU8O.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-BWIP_EvW.js → dialog-BvQeBMfP.js} +1 -1
  59. package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-BKcgVugF.js → drawer-CMwPXwQB.js} +1 -1
  61. package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-CcWkGR4a.js → entry-D4O2229X.js} +1 -1
  63. package/dist/dashboard/assets/{entry-CcWkGR4a.js.gz → entry-D4O2229X.js.gz} +0 -0
  64. package/dist/dashboard/assets/{entry-kTiTFrvh.js → entry-DKvr5Q9K.js} +1 -1
  65. package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-FQCejoDY.js +4 -0
  67. package/dist/dashboard/assets/entry-FQCejoDY.js.gz +0 -0
  68. package/dist/dashboard/assets/{github-badge-Booc5mNe.js → github-badge-zliLvBwr.js} +1 -1
  69. package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-D70pChfn.js → index-CXm3FIyt.js} +2 -2
  71. package/dist/dashboard/assets/index-CXm3FIyt.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-D1TCqM2D.js → inline-D1IlZZ8H.js} +1 -1
  73. package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-B9oyU93y.js → input-BpVZdrbc.js} +1 -1
  75. package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-BArzXirQ.js → layout-BioLW7gN.js} +1 -1
  77. package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-BVZzaoHF.js → list-row-CwVcGAIc.js} +1 -1
  79. package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-EmuIayqp.js → live-yfEJFF9T.js} +1 -1
  81. package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
  82. package/dist/dashboard/assets/{model-bans-CSPQ9xB0.js → model-bans-CT-TxSNX.js} +1 -1
  83. package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
  84. package/dist/dashboard/assets/providers/cursor.webp +0 -0
  85. package/dist/dashboard/assets/providers/trae.webp +0 -0
  86. package/dist/dashboard/assets/{routing-BExgxxBU.js → routing-Cs6jl49L.js} +1 -1
  87. package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
  88. package/dist/dashboard/assets/select-C0Rer3gF.js +41 -0
  89. package/dist/dashboard/assets/select-C0Rer3gF.js.gz +0 -0
  90. package/dist/dashboard/assets/settings-CfcTcOyU.js +1 -0
  91. package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
  92. package/dist/dashboard/assets/{stack-Ca5HxI5i.js → stack-C9_uUjaA.js} +1 -1
  93. package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
  94. package/dist/dashboard/assets/{state-CzUPfLXo.js → state-DA0B95UQ.js} +1 -1
  95. package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
  96. package/dist/dashboard/assets/{switch-DOUIky_i.js → switch-Cittxy-x.js} +1 -1
  97. package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
  98. package/dist/dashboard/assets/{toast-C76WrMhc.js → toast-CNt_jzO4.js} +1 -1
  99. package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
  100. package/dist/dashboard/assets/{useMutation-D87FZwH0.js → useMutation-BQKMcJ1H.js} +1 -1
  101. package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
  102. package/dist/dashboard/index.html +1 -1
  103. package/dist/dashboard/index.html.gz +0 -0
  104. package/dist/dashboard/precompressed-assets.json +51 -52
  105. package/migrations/full-postgres/0015_add_payload_stages.sql +18 -0
  106. package/migrations/full-postgres/0016_add_share_link_slug.sql +20 -0
  107. package/migrations/full-postgres/0017_add_models_pro_only.sql +18 -0
  108. package/migrations/full-postgres/0018_add_accounts_quota_plan.sql +18 -0
  109. package/migrations/full-postgres/0019_add_models_free_tier.sql +15 -0
  110. package/migrations/lite-sqlite3/0015_add_payload_stages.sql +18 -0
  111. package/migrations/lite-sqlite3/0016_add_share_link_slug.sql +20 -0
  112. package/migrations/lite-sqlite3/0017_add_models_pro_only.sql +18 -0
  113. package/migrations/lite-sqlite3/0018_add_accounts_quota_plan.sql +18 -0
  114. package/migrations/lite-sqlite3/0019_add_models_free_tier.sql +15 -0
  115. package/package.json +3 -2
  116. package/scripts/cli/command/console-menu.ts +30 -11
  117. package/scripts/cli/command/start.ts +77 -13
  118. package/scripts/cli/command/update.ts +1 -1
  119. package/scripts/cli/gateway/instance-lock.ts +105 -0
  120. package/scripts/cli/gateway/spawn.ts +173 -101
  121. package/scripts/cli/ui/background.ts +110 -49
  122. package/src/boot/app.ts +20 -6
  123. package/src/boot/dependencies.ts +19 -0
  124. package/src/boot/elysia-typebox-bridge.ts +37 -0
  125. package/src/boot/lifecycle.ts +2 -0
  126. package/src/config/cooldowns.ts +138 -138
  127. package/src/config/providers.ts +5 -5
  128. package/src/config/runtime.ts +19 -3
  129. package/src/config/spec.ts +9 -0
  130. package/src/console/api-keys/contracts.ts +414 -391
  131. package/src/console/api-keys/routes.ts +880 -870
  132. package/src/console/auth/account-reset-store.ts +14 -0
  133. package/src/console/auth/account-reset.ts +3 -2
  134. package/src/console/auth/setup-service.ts +6 -3
  135. package/src/console/domain-registration.ts +9 -2
  136. package/src/console/observability/contracts.ts +475 -455
  137. package/src/console/observability/store.ts +1015 -952
  138. package/src/console/providers/catalog/contracts.ts +31 -0
  139. package/src/console/providers/catalog/provider-operations.ts +8 -0
  140. package/src/console/providers/catalog/store.ts +1532 -1525
  141. package/src/console/providers/oauth/callback-listener.ts +18 -3
  142. package/src/console/providers/oauth/routes.ts +38 -15
  143. package/src/console/quota/refresh.ts +19 -0
  144. package/src/console/updates/routes.ts +162 -4
  145. package/src/main.ts +3 -0
  146. package/src/network/http2-bidi.ts +85 -0
  147. package/src/network/outbound-fetch.ts +1 -26
  148. package/src/network/pool/agent.ts +478 -460
  149. package/src/network/pool/resolver.ts +1104 -544
  150. package/src/network/pool-health-machine.ts +12 -5
  151. package/src/observability/payload-capture.ts +344 -324
  152. package/src/persistence/db-pg.ts +9 -3
  153. package/src/persistence/db-sqlite.ts +497 -495
  154. package/src/persistence/health-event-flush.ts +137 -0
  155. package/src/persistence/migrate.ts +181 -129
  156. package/src/persistence/schema.ts +1360 -1295
  157. package/src/persistence/settings-revision.ts +25 -0
  158. package/src/persistence/shared-cache.ts +217 -0
  159. package/src/persistence/stores/share-store.ts +556 -528
  160. package/src/persistence/usage-flush.ts +61 -1
  161. package/src/protocol/canonical-model.ts +23 -1
  162. package/src/protocol/response/chat.ts +143 -10
  163. package/src/protocol/response/omp-tool-call-markup.ts +221 -0
  164. package/src/providers/default-registry.ts +173 -1
  165. package/src/providers/discovery/openai-model-discovery.ts +39 -3
  166. package/src/providers/discovery/openrouter-free-roster.ts +98 -0
  167. package/src/providers/discovery/probing-service.ts +1022 -1001
  168. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +39 -0
  169. package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
  170. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +18 -5
  171. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +4 -0
  172. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -175
  173. package/src/providers/integrations/buddy/codebuddy/shared.ts +15 -6
  174. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
  175. package/src/providers/integrations/buddy/workbuddy/shared.ts +3 -3
  176. package/src/providers/integrations/claude/claude.ts +606 -582
  177. package/src/providers/integrations/cline/cline-quota.ts +18 -16
  178. package/src/providers/integrations/cline/cline.ts +539 -468
  179. package/src/providers/integrations/commandcode/commandcode.ts +675 -541
  180. package/src/providers/integrations/commandcode/discovery.ts +116 -0
  181. package/src/providers/integrations/commandcode/quota.ts +160 -0
  182. package/src/providers/integrations/cursor/cursor-auth.ts +418 -0
  183. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -0
  184. package/src/providers/integrations/cursor/cursor-http2.ts +300 -0
  185. package/src/providers/integrations/cursor/cursor-identity.ts +92 -0
  186. package/src/providers/integrations/cursor/cursor-request.ts +471 -0
  187. package/src/providers/integrations/cursor/cursor-session-manager.ts +281 -0
  188. package/src/providers/integrations/cursor/cursor-shared.ts +108 -0
  189. package/src/providers/integrations/cursor/cursor.ts +960 -0
  190. package/src/providers/integrations/cursor/protocol/framing.ts +129 -0
  191. package/src/providers/integrations/cursor/protocol/messages.ts +2355 -0
  192. package/src/providers/integrations/cursor/protocol/struct.ts +305 -0
  193. package/src/providers/integrations/github/github.ts +4 -4
  194. package/src/providers/integrations/hermes.ts +79 -8
  195. package/src/providers/integrations/kilo/kilo-discovery.ts +10 -1
  196. package/src/providers/integrations/kilo/kilo.ts +153 -146
  197. package/src/providers/integrations/opencode/provider.ts +532 -538
  198. package/src/providers/integrations/openrouter/provider.ts +139 -281
  199. package/src/providers/integrations/qoder/qoder-catalog.ts +450 -0
  200. package/src/providers/integrations/qoder/qoder-oauth.ts +310 -257
  201. package/src/providers/integrations/qoder/qoder-realm.ts +106 -0
  202. package/src/providers/integrations/qoder/qoder-virtual.ts +77 -0
  203. package/src/providers/integrations/qoder/qoder.ts +865 -730
  204. package/src/providers/integrations/trae/trae-catalog.ts +190 -0
  205. package/src/providers/integrations/trae/trae-headers.ts +149 -0
  206. package/src/providers/integrations/trae/trae-oauth.ts +773 -0
  207. package/src/providers/integrations/trae/trae-protocol.ts +400 -0
  208. package/src/providers/integrations/trae/trae-realm.ts +220 -0
  209. package/src/providers/integrations/trae/trae.ts +458 -0
  210. package/src/providers/operations/account-health-service.ts +107 -10
  211. package/src/providers/operations/client-versions.ts +133 -6
  212. package/src/providers/operations/provider-catalog-seeder.ts +95 -86
  213. package/src/providers/operations/provider-credential-service.ts +46 -7
  214. package/src/providers/provider-contracts.ts +76 -0
  215. package/src/providers/provider-metadata.ts +78 -6
  216. package/src/providers/provider-model-definition.ts +373 -349
  217. package/src/security/api-key-auth.ts +126 -8
  218. package/src/transport/contracts.ts +3 -0
  219. package/src/transport/dispatch/attempt-finalize.ts +32 -1
  220. package/src/transport/dispatch/attempt-loop.ts +530 -515
  221. package/src/transport/dispatch/generation.ts +88 -21
  222. package/src/transport/dispatch/leases.ts +99 -2
  223. package/src/transport/dispatch/proxy-request.ts +60 -3
  224. package/src/transport/dispatch/streaming/finalize.ts +16 -1
  225. package/src/transport/dispatch/streaming/stream.ts +22 -2
  226. package/src/transport/dispatch/upstream.ts +19 -1
  227. package/src/transport/guards/auth-guard.ts +26 -1
  228. package/src/transport/guards/body-policy.ts +5 -4
  229. package/src/transport/request/preparer.ts +103 -0
  230. package/src/transport/request/state.ts +14 -0
  231. package/src/transport/routing/plan-gate.ts +45 -0
  232. package/src/transport/routing/route-catalog.ts +799 -777
  233. package/src/transport/routing/route-model.ts +52 -1
  234. package/src/transport/routing/router.ts +51 -0
  235. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js +0 -1
  236. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js.gz +0 -0
  237. package/dist/dashboard/assets/CliToolDetailPage-Bcy150Jo.js.gz +0 -0
  238. package/dist/dashboard/assets/CliToolsPage-DyNXefph.js.gz +0 -0
  239. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js +0 -1
  240. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js.gz +0 -0
  241. package/dist/dashboard/assets/ConsoleLogPage-CATpAEOz.js.gz +0 -0
  242. package/dist/dashboard/assets/CustomizationPage-D6jEU7nL.js.gz +0 -0
  243. package/dist/dashboard/assets/HealthEventsModal-CZkpn0a7.js.gz +0 -0
  244. package/dist/dashboard/assets/LoginPage-BJlMVN4U.js.gz +0 -0
  245. package/dist/dashboard/assets/ModelPicker-Br3CXEau.js.gz +0 -0
  246. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js +0 -1
  247. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js.gz +0 -0
  248. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js +0 -3
  249. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js.gz +0 -0
  250. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
  251. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js.gz +0 -0
  252. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js +0 -1
  253. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js.gz +0 -0
  254. package/dist/dashboard/assets/ProxyPage-CXiumA53.js +0 -7
  255. package/dist/dashboard/assets/ProxyPage-CXiumA53.js.gz +0 -0
  256. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js +0 -1
  257. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js.gz +0 -0
  258. package/dist/dashboard/assets/SettingsPage-BHW4Zt06.js.gz +0 -0
  259. package/dist/dashboard/assets/SetupPage-VOYC_lIa.js.gz +0 -0
  260. package/dist/dashboard/assets/SortableList-DpOTnGuH.js +0 -1
  261. package/dist/dashboard/assets/SortableList-DpOTnGuH.js.gz +0 -0
  262. package/dist/dashboard/assets/StudioPage-BNIDLiU6.js.gz +0 -0
  263. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js +0 -56
  264. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js.gz +0 -0
  265. package/dist/dashboard/assets/api-keys-BETfaMYZ.js.gz +0 -0
  266. package/dist/dashboard/assets/app-BLCvGdzb.css +0 -2
  267. package/dist/dashboard/assets/app-BLCvGdzb.css.gz +0 -0
  268. package/dist/dashboard/assets/assert-7J6_LVD3.js +0 -1
  269. package/dist/dashboard/assets/assert-7J6_LVD3.js.gz +0 -0
  270. package/dist/dashboard/assets/badge-BTyGk_DL.js.gz +0 -0
  271. package/dist/dashboard/assets/banner-bujTWYM4.js.gz +0 -0
  272. package/dist/dashboard/assets/card-B0Bnx5CA.js.gz +0 -0
  273. package/dist/dashboard/assets/cli-tools-DE2ug812.js.gz +0 -0
  274. package/dist/dashboard/assets/clipboard-button-R_rArH8S.js.gz +0 -0
  275. package/dist/dashboard/assets/dialog-BWIP_EvW.js.gz +0 -0
  276. package/dist/dashboard/assets/drawer-BKcgVugF.js.gz +0 -0
  277. package/dist/dashboard/assets/entry-7dpgvpJ3.js +0 -4
  278. package/dist/dashboard/assets/entry-7dpgvpJ3.js.gz +0 -0
  279. package/dist/dashboard/assets/entry-kTiTFrvh.js.gz +0 -0
  280. package/dist/dashboard/assets/github-badge-Booc5mNe.js.gz +0 -0
  281. package/dist/dashboard/assets/index-D70pChfn.js.gz +0 -0
  282. package/dist/dashboard/assets/inline-D1TCqM2D.js.gz +0 -0
  283. package/dist/dashboard/assets/input-B9oyU93y.js.gz +0 -0
  284. package/dist/dashboard/assets/layout-BArzXirQ.js.gz +0 -0
  285. package/dist/dashboard/assets/list-row-BVZzaoHF.js.gz +0 -0
  286. package/dist/dashboard/assets/live-EmuIayqp.js.gz +0 -0
  287. package/dist/dashboard/assets/model-bans-CSPQ9xB0.js.gz +0 -0
  288. package/dist/dashboard/assets/routing-BExgxxBU.js.gz +0 -0
  289. package/dist/dashboard/assets/select-C9mMoEpJ.js +0 -41
  290. package/dist/dashboard/assets/select-C9mMoEpJ.js.gz +0 -0
  291. package/dist/dashboard/assets/settings-C4_Gn_wq.js +0 -1
  292. package/dist/dashboard/assets/settings-C4_Gn_wq.js.gz +0 -0
  293. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
  294. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js.gz +0 -0
  295. package/dist/dashboard/assets/stack-Ca5HxI5i.js.gz +0 -0
  296. package/dist/dashboard/assets/state-CzUPfLXo.js.gz +0 -0
  297. package/dist/dashboard/assets/switch-DOUIky_i.js.gz +0 -0
  298. package/dist/dashboard/assets/toast-C76WrMhc.js.gz +0 -0
  299. package/dist/dashboard/assets/useMutation-D87FZwH0.js.gz +0 -0
  300. package/src/network/tls/bun-client-hello.ts +0 -112
  301. /package/dist/dashboard/assets/{app-Cjq7nrwu.js → app-C-Y4oZT0.js} +0 -0
  302. /package/dist/dashboard/assets/{app-Cjq7nrwu.js.gz → app-C-Y4oZT0.js.gz} +0 -0
@@ -0,0 +1,400 @@
1
+ // Trae wire protocol: request body and SSE translation.
2
+ //
3
+ // Two upstream rules drive everything here:
4
+ //
5
+ // 1. The model key is `model_name`, not `model`. Sending `model` is silently
6
+ // ignored and upstream answers with its default model, so the caller gets a
7
+ // different model than it asked for with no error.
8
+ // 2. Message `content` must be an array of parts. A plain string is refused
9
+ // (400/4001), so every message is normalized to parts even when there is
10
+ // only text.
11
+ //
12
+ // Sourced from the donor hub `Public/trae2api-hub` (`build_chat_body` and
13
+ // `iter_events` in `trae_client.py`).
14
+
15
+ import type { CanonicalRequest, ContentPart } from "../../../protocol/canonical-model";
16
+ import {
17
+ TRAE_AGENT_CHAT_PATH,
18
+ TRAE_AGENT_FUNCTION,
19
+ TRAE_CHAT_PATH,
20
+ TRAE_MODEL_LIST_PATH,
21
+ TRAE_REALMS,
22
+ type TraeRealm,
23
+ } from "./trae-realm";
24
+
25
+ /** Upstream chat body. */
26
+ export interface TraeChatBody {
27
+ readonly model_name: string;
28
+ readonly messages: readonly TraeMessage[];
29
+ readonly stream: boolean;
30
+ readonly region: string;
31
+ readonly ai_region: string;
32
+ readonly temperature?: number;
33
+ readonly top_p?: number;
34
+ readonly max_tokens?: number;
35
+ readonly stop?: readonly string[];
36
+ readonly presence_penalty?: number;
37
+ readonly frequency_penalty?: number;
38
+ readonly session_id?: string;
39
+ /** Tool declarations; `parameters` is a JSON string, not a schema object. */
40
+ readonly tools?: readonly TraeToolDeclaration[];
41
+ }
42
+
43
+ /** One declared tool, in the shape upstream binds. */
44
+ export interface TraeToolDeclaration {
45
+ readonly type: "function";
46
+ readonly function: {
47
+ readonly name: string;
48
+ readonly description?: string;
49
+ readonly parameters: string;
50
+ };
51
+ }
52
+
53
+ export interface TraeMessage {
54
+ readonly role: "system" | "user" | "assistant" | "tool";
55
+ readonly content: readonly TraeContentPart[];
56
+ readonly tool_call_id?: string;
57
+ readonly tool_calls?: readonly TraeToolCall[];
58
+ }
59
+
60
+ export type TraeContentPart =
61
+ | { readonly type: "text"; readonly text: string }
62
+ | { readonly type: "image_url"; readonly image_url: { readonly url: string } };
63
+
64
+ /**
65
+ * One tool call, in the shape upstream's protobuf expects.
66
+ *
67
+ * The nested key is `function_call`, not OpenAI's `function`, and `Name` is a
68
+ * **required** field on replay: an assistant turn sent back with
69
+ * `function: {name: ""}` is rejected before it reaches the model with
70
+ * `required field Name is not set`. Both spellings therefore have to carry the
71
+ * name, and the arguments stay a JSON string rather than an object.
72
+ */
73
+ export interface TraeToolCall {
74
+ readonly id: string;
75
+ readonly type: "function";
76
+ readonly function_call: { readonly name: string; readonly arguments: string };
77
+ }
78
+
79
+ /**
80
+ * Generation fields upstream accepts.
81
+ *
82
+ * Undeclared fields are deliberately omitted: the donor notes that sending
83
+ * anything outside this set risks a 4001, so `provider_options` is never
84
+ * spread into the body wholesale.
85
+ */
86
+ const PASSTHROUGH_OPTIONS = ["temperature", "top_p", "max_tokens", "stop"] as const;
87
+
88
+ function textOf(part: ContentPart): string {
89
+ return part.kind === "text" ? part.text : "";
90
+ }
91
+
92
+ /**
93
+ * Renders an image part as a URL upstream accepts.
94
+ *
95
+ * The canonical image payload is opaque (`payload`), because every surface
96
+ * spells an image differently: base64 with a media type, a bare URL, or a
97
+ * provider file id. Only the first two have an upstream representation, so a
98
+ * payload this adapter cannot recognize is dropped rather than sent as a
99
+ * malformed data URI.
100
+ */
101
+ function dataUriOf(part: Extract<ContentPart, { kind: "image" }>): string | undefined {
102
+ const payload = part.payload;
103
+ if (typeof payload === "string") {
104
+ return /^https?:\/\//i.test(payload) ? payload : undefined;
105
+ }
106
+ if (typeof payload !== "object" || payload === null) return undefined;
107
+ const record = payload as Record<string, unknown>;
108
+ const url = record["url"];
109
+ if (typeof url === "string" && url.length > 0) return url;
110
+ const data = record["data"];
111
+ const mediaType = record["media_type"] ?? record["mediaType"];
112
+ if (typeof data === "string" && typeof mediaType === "string") {
113
+ // `data` may already carry its own `data:` prefix from a previous hop.
114
+ return data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
115
+ }
116
+ const b64 = record["b64_json"] ?? record["base64"];
117
+ if (typeof b64 === "string" && typeof mediaType === "string") {
118
+ return `data:${mediaType};base64,${b64}`;
119
+ }
120
+ return undefined;
121
+ }
122
+
123
+ /** Maps one canonical part onto the upstream part shape. */
124
+ function toTraePart(part: ContentPart): TraeContentPart | undefined {
125
+ if (part.kind === "text") return { type: "text", text: part.text };
126
+ if (part.kind === "image") {
127
+ const url = dataUriOf(part);
128
+ return url === undefined ? undefined : { type: "image_url", image_url: { url } };
129
+ }
130
+ return undefined;
131
+ }
132
+
133
+ /** Serializes tool-call arguments, which arrive parsed rather than as text. */
134
+ function toolArguments(args: unknown): string {
135
+ if (typeof args === "string") return args;
136
+ try {
137
+ return JSON.stringify(args ?? {});
138
+ } catch {
139
+ return "{}";
140
+ }
141
+ }
142
+
143
+ /** Flattens a tool result's content to the single string upstream takes. */
144
+ function toolResultText(content: readonly ContentPart[] | string): string {
145
+ if (typeof content === "string") return content;
146
+ return content
147
+ .map((part) => (part.kind === "text" ? part.text : ""))
148
+ .filter((text) => text.length > 0)
149
+ .join("\n");
150
+ }
151
+
152
+ /**
153
+ * Builds the upstream chat body from a canonical request.
154
+ *
155
+ * System content is promoted to a leading `system` message: upstream has no
156
+ * separate system field, and dropping it (rather than promoting it) is what
157
+ * silently loses the caller's instructions.
158
+ */
159
+ export function buildTraeChatBody(
160
+ modelId: string,
161
+ request: CanonicalRequest,
162
+ realm: TraeRealm,
163
+ ): TraeChatBody {
164
+ if (modelId.length === 0) throw new Error("trae model name is required");
165
+
166
+ const messages: TraeMessage[] = [];
167
+ const systemParts: string[] = [];
168
+ for (const part of request.system ?? []) {
169
+ const text = textOf(part);
170
+ if (text.length > 0) systemParts.push(text);
171
+ }
172
+ for (const part of request.instructions ?? []) {
173
+ const text = textOf(part);
174
+ if (text.length > 0) systemParts.push(text);
175
+ }
176
+ if (systemParts.length > 0) {
177
+ messages.push({ role: "system", content: [{ type: "text", text: systemParts.join("\n\n") }] });
178
+ }
179
+
180
+ for (const message of request.messages) {
181
+ // `system` was promoted into the leading turn above; appending it again
182
+ // would leave a second, late instruction turn. `developer` has no upstream
183
+ // equivalent and is folded into the same leading turn rather than dropped.
184
+ if (message.role === "system" || message.role === "developer") {
185
+ const text = message.content.map(textOf).filter((t) => t.length > 0).join("\n\n");
186
+ if (text.length > 0) systemParts.push(text);
187
+ continue;
188
+ }
189
+ const parts: TraeContentPart[] = [];
190
+ const toolCalls: TraeToolCall[] = [];
191
+ let toolResult: { callId: string; text: string } | undefined;
192
+ for (const part of message.content) {
193
+ if (part.kind === "toolCall") {
194
+ toolCalls.push({
195
+ id: part.call_id,
196
+ type: "function",
197
+ function_call: { name: part.name, arguments: toolArguments(part.arguments) },
198
+ });
199
+ continue;
200
+ }
201
+ if (part.kind === "toolResult") {
202
+ toolResult = { callId: part.call_id, text: toolResultText(part.content) };
203
+ continue;
204
+ }
205
+ const mapped = toTraePart(part);
206
+ if (mapped !== undefined) parts.push(mapped);
207
+ }
208
+
209
+ // A tool answer becomes its own `tool` turn, which is the only shape
210
+ // upstream accepts for it — leaving it inside the assistant turn is
211
+ // rejected as a broken tool sequence.
212
+ if (toolResult !== undefined) {
213
+ messages.push({
214
+ role: "tool",
215
+ content: [{ type: "text", text: toolResult.text }],
216
+ tool_call_id: toolResult.callId,
217
+ });
218
+ continue;
219
+ }
220
+ // Empty turns are skipped: upstream rejects them, and emitting
221
+ // `content: ""` turns leaves a ghost turn in the conversation.
222
+ if (parts.length === 0 && toolCalls.length === 0) continue;
223
+ messages.push({
224
+ role: message.role === "assistant" ? "assistant" : "user",
225
+ content: parts.length > 0 ? parts : [{ type: "text", text: "" }],
226
+ ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
227
+ });
228
+ }
229
+
230
+ const config = TRAE_REALMS[realm];
231
+ const controls = request.generation_controls;
232
+ const body: Record<string, unknown> = {
233
+ model_name: modelId,
234
+ messages,
235
+ stream: request.stream,
236
+ region: config.region,
237
+ ai_region: config.region,
238
+ ...(request.session_id ? { session_id: request.session_id } : {}),
239
+ };
240
+ /**
241
+ * Tool declarations, with `parameters` serialized to a string.
242
+ *
243
+ * Upstream's `FunctionDefinition` types that field as a Go `string`, so an
244
+ * object is rejected outright: `cannot unmarshal object into Go struct field
245
+ * FunctionDefinition.tools.function.parameters of type string`. Sending the
246
+ * schema as a JSON string is the only shape it binds.
247
+ */
248
+ const tools = request.tools;
249
+ if (tools !== undefined && tools.length > 0) {
250
+ body["tools"] = tools.map((tool) => ({
251
+ type: "function",
252
+ function: {
253
+ name: tool.name,
254
+ ...(tool.description ? { description: tool.description } : {}),
255
+ parameters: toolArguments(tool.jsonSchema),
256
+ },
257
+ }));
258
+ }
259
+ const options: Record<string, unknown> = {
260
+ temperature: controls.temperature,
261
+ top_p: controls.top_p,
262
+ max_tokens: controls.max_tokens,
263
+ stop: controls.stop,
264
+
265
+ };
266
+ for (const key of PASSTHROUGH_OPTIONS) {
267
+ const value = options[key];
268
+ if (value !== undefined && value !== null) body[key] = value;
269
+ }
270
+ return body as unknown as TraeChatBody;
271
+ }
272
+
273
+ /** Body for the live model list. Upstream requires a body even to list. */
274
+ export function buildTraeModelListBody(): Record<string, unknown> {
275
+ return { type: "chat" };
276
+ }
277
+
278
+ export const TRAE_CHAT_ENDPOINT = TRAE_CHAT_PATH;
279
+ export const TRAE_AGENT_ENDPOINT = TRAE_AGENT_CHAT_PATH;
280
+ export const TRAE_MODEL_LIST_ENDPOINT = TRAE_MODEL_LIST_PATH;
281
+
282
+ /**
283
+ * Rewrites a chat-surface body into the agent surface's shape.
284
+ *
285
+ * Two changes, both required and neither optional:
286
+ *
287
+ * 1. The model key moves from `model_name` to `config_name`, and
288
+ * `config_source: 1` is added. Without `config_source` the server resolves
289
+ * the right upstream model and then streams nothing at all, which presents
290
+ * as a broken model rather than a missing field.
291
+ * 2. `function` selects the roster. Without it the call fails before model
292
+ * resolution with `[LLMUtilsChat.resolveByUsage] function is empty`.
293
+ *
294
+ * Everything else — messages, generation controls, session id — is carried
295
+ * over unchanged; the agent surface reads the same message shape.
296
+ */
297
+ export function toTraeAgentBody(body: TraeChatBody): Record<string, unknown> {
298
+ const draft = body as unknown as Record<string, unknown>;
299
+ const rewritten: Record<string, unknown> = { function: TRAE_AGENT_FUNCTION };
300
+ for (const [key, value] of Object.entries(draft)) {
301
+ if (key === "model_name") continue;
302
+ rewritten[key] = value;
303
+ }
304
+ rewritten["config_name"] = draft["model_name"];
305
+ rewritten["config_source"] = 1;
306
+ return rewritten;
307
+ }
308
+
309
+ /**
310
+ * True when a dispatch target belongs to the agent surface.
311
+ *
312
+ * Compared by suffix so an absolute URL target still resolves: a catalog row
313
+ * may carry a full `https://…/api/agent/v3/llm_utils_chat` rather than a bare
314
+ * path, and prefixing host logic elsewhere must not flip the surface.
315
+ */
316
+ export function isTraeAgentEndpoint(endpointPath: string | null | undefined): boolean {
317
+ if (typeof endpointPath !== "string" || endpointPath.length === 0) return true;
318
+ return endpointPath.endsWith(TRAE_AGENT_CHAT_PATH);
319
+ }
320
+
321
+ /** One parsed SSE frame. */
322
+ export interface TraeSseFrame {
323
+ readonly event: string;
324
+ readonly data: unknown;
325
+ }
326
+
327
+ /**
328
+ * Parses an SSE byte stream into frames.
329
+ *
330
+ * The done sentinel is a `data:` payload equal to `[DONE]`, which upstream
331
+ * sends instead of a named `done` event. Comment lines (leading `:`) are
332
+ * heartbeats and are skipped rather than treated as data.
333
+ */
334
+ export async function* parseTraeSse(
335
+ body: ReadableStream<Uint8Array>,
336
+ ): AsyncIterable<TraeSseFrame> {
337
+ const decoder = new TextDecoder();
338
+ const reader = body.getReader();
339
+ let buffer = "";
340
+ let event = "message";
341
+ let data: string[] = [];
342
+ try {
343
+ while (true) {
344
+ const { done, value } = await reader.read();
345
+ if (done) break;
346
+ buffer += decoder.decode(value, { stream: true });
347
+ let newline = buffer.indexOf("\n");
348
+ while (newline >= 0) {
349
+ const line = buffer.slice(0, newline).replace(/\r$/, "");
350
+ buffer = buffer.slice(newline + 1);
351
+ if (line === "") {
352
+ if (data.length > 0) {
353
+ const payload = data.join("\n");
354
+ data = [];
355
+ if (payload.trim() === "[DONE]") {
356
+ yield { event: "done", data: { finish_reason: "stop" } };
357
+ } else {
358
+ let parsed: unknown = payload;
359
+ try {
360
+ parsed = JSON.parse(payload);
361
+ } catch {
362
+ // Upstream occasionally emits non-JSON keepalives; a frame we
363
+ // cannot parse is dropped rather than failing the stream.
364
+ parsed = payload;
365
+ }
366
+ yield { event, data: parsed };
367
+ }
368
+ event = "message";
369
+ }
370
+ } else if (line.startsWith(":")) {
371
+ continue;
372
+ } else {
373
+ const separator = line.indexOf(":");
374
+ const field = separator < 0 ? line : line.slice(0, separator);
375
+ let value = separator < 0 ? "" : line.slice(separator + 1);
376
+ if (value.startsWith(" ")) value = value.slice(1);
377
+ if (field === "event") event = value;
378
+ else if (field === "data") data.push(value);
379
+ }
380
+ newline = buffer.indexOf("\n");
381
+ }
382
+ }
383
+ if (data.length > 0) {
384
+ const payload = data.join("\n");
385
+ if (payload.trim() === "[DONE]") {
386
+ yield { event: "done", data: { finish_reason: "stop" } };
387
+ } else {
388
+ let parsed: unknown = payload;
389
+ try {
390
+ parsed = JSON.parse(payload);
391
+ } catch {
392
+ parsed = payload;
393
+ }
394
+ yield { event, data: parsed };
395
+ }
396
+ }
397
+ } finally {
398
+ reader.releaseLock();
399
+ }
400
+ }
@@ -0,0 +1,220 @@
1
+ // Trae realms: one vendor, two unrelated deployments.
2
+ //
3
+ // `trae` (global / intl) and `traecn` (China) share the wire protocol — an
4
+ // `x-ide-token` credential, a `model_name` body, an SSE stream — but nothing
5
+ // else about the address: different core host, different account/oauth host,
6
+ // different UA, a *different authorization scheme*, and a different model
7
+ // roster. Treating them as one provider with a region flag is what would send
8
+ // a CN account to `*.traeapi.us` and get it refused.
9
+ //
10
+ // Everything per-realm lives here so the adapter, the auth builder, and the
11
+ // discovery fetcher cannot drift apart on host, UA, or version code.
12
+ //
13
+ // Sourced from the donor hub `Public/trae2api-hub` (`REALMS` in
14
+ // `trae_client.py`, `TRAEREALMS`-shaped config in `trae_accounts.py`).
15
+
16
+ /** One Trae deployment. */
17
+ export type TraeRealm = "intl" | "cn";
18
+
19
+ /** Per-realm upstream addresses, client identity, and auth scheme. */
20
+ export interface TraeRealmConfig {
21
+ /** Data-plane host: chat + model list (`/api/ide/v1/*`). */
22
+ readonly core: string;
23
+ /** Fallback core hosts, tried in order after `core`. */
24
+ readonly coreFallbacks: readonly string[];
25
+ /** Business host: CloudIDE OAuth token exchange, entitlement. */
26
+ readonly account: string;
27
+ /** Fallback account hosts. */
28
+ readonly accountFallbacks: readonly string[];
29
+ /** Entitlement / payment host. */
30
+ readonly pay: string;
31
+ /** Growth host (daily check-in credits). CN-only in practice. */
32
+ readonly ug: string;
33
+ /** Upstream-facing User-Agent for the non-chat endpoints. */
34
+ readonly userAgent: string;
35
+ /** Version code sent as `x-ide-version-code`. */
36
+ readonly versionCode: string;
37
+ /** Region tag sent in the request body. */
38
+ readonly region: string;
39
+ /** True when this realm's `/api/ide/v1/model_list` answers. */
40
+ readonly modelListSupported: boolean;
41
+ }
42
+
43
+ /**
44
+ * Chat path, identical across realms.
45
+ *
46
+ * The list endpoint takes a body (`{"type":"chat"}`); the donor posts it, so
47
+ * this is not a bare GET.
48
+ */
49
+ export const TRAE_CHAT_PATH = "/api/ide/v1/llm_raw_chat";
50
+ export const TRAE_MODEL_LIST_PATH = "/api/ide/v1/model_list";
51
+
52
+ /**
53
+ * Agent path, serving the IDE picker's roster.
54
+ *
55
+ * A different surface from {@link TRAE_CHAT_PATH}, not a version of it: this
56
+ * one selects models by `config_name` + `config_source` and ignores
57
+ * `model_name` entirely, so the two rosters cannot be merged into one call.
58
+ */
59
+ export const TRAE_AGENT_CHAT_PATH = "/api/agent/v3/llm_utils_chat";
60
+
61
+ /**
62
+ * Resolves which roster the agent surface serves for an account.
63
+ *
64
+ * `solo_agent` holds the picker's chat-capable configs. Other values are
65
+ * either rejected (`solo_coder` is `4001 param is invalid`) or resolve to a
66
+ * different, non-chat set.
67
+ */
68
+ export const TRAE_AGENT_FUNCTION = "solo_agent";
69
+
70
+ /** OAuth / business paths, relative to the realm's `account` host. */
71
+ export const TRAE_EXCHANGE_TOKEN_PATH = "/cloudide/api/v3/trae/oauth/ExchangeToken";
72
+
73
+ /**
74
+ * Endpoints that redeem a PKCE `AuthCode`, tried in order.
75
+ *
76
+ * Distinct from {@link TRAE_EXCHANGE_TOKEN_PATH}, which is the authenticated
77
+ * refresh-token rotation and refuses an `AuthCode` with "Invalid param".
78
+ *
79
+ * The `/trae/api/v3` form is tried first because it is the one that answers:
80
+ * the `/cloudide/api/v3/public` form returns 500 for every input, including a
81
+ * garbage code, with an internal DNS failure (`cloudide.coreservice.apiserver`),
82
+ * so it is kept only as a fallback in case that is repaired.
83
+ *
84
+ * Sourced from the login page's own client (`TraeExchangeTokenV2` and
85
+ * `PublicExchangeToken` in `9839.*.js`).
86
+ */
87
+ export const TRAE_AUTH_CODE_EXCHANGE_PATHS: readonly string[] = [
88
+ "/trae/api/v3/oauth/ExchangeToken",
89
+ "/cloudide/api/v3/public/oauth/ExchangeToken",
90
+ ];
91
+ export const TRAE_TEMP_TOKEN_PATH = "/cloudide/api/v3/trae/GenerateTempToken";
92
+ export const TRAE_GET_USER_INFO_PATH = "/cloudide/api/v3/trae/GetUserInfo";
93
+
94
+ /** Browser-login path on the host `GetLoginGuidance` names. */
95
+ export const TRAE_AUTHORIZATION_PATH = "/authorization";
96
+
97
+ /**
98
+ * Path the login page will accept as `auth_callback_url`.
99
+ *
100
+ * Verified against the live page: it renders the login form only when the
101
+ * callback path is `/authorize`. Any other path — `/callback`, `/auth/callback`,
102
+ * `/`, a trailing slash — is answered with "Login Failed" before the operator
103
+ * gets to sign in, so the gateway's own `/callback` default cannot be used.
104
+ *
105
+ * The donor serves this on its own dynamic port; here the port comes from the
106
+ * gateway's redirect setting and only the path is fixed.
107
+ */
108
+ export const TRAE_CALLBACK_PATH = "/authorize";
109
+
110
+ /**
111
+ * Login-guidance endpoints, tried in order.
112
+ *
113
+ * This is the one Trae OAuth step that is not per-realm: the guidance call is
114
+ * what *discovers* the login host, so asking a realm's own account host would
115
+ * presuppose the answer. Verified live: all three answer 200 and return
116
+ * `Result.LoginHost` (`www.trae.ai`).
117
+ */
118
+ export const TRAE_LOGIN_GUIDANCE_URLS: readonly string[] = [
119
+ "https://api.marscode.com/cloudide/api/v3/trae/GetLoginGuidance",
120
+ "https://api.trae.ai/cloudide/api/v3/trae/GetLoginGuidance",
121
+ "https://www.trae.ai/cloudide/api/v3/trae/GetLoginGuidance",
122
+ ];
123
+
124
+ /** Usage / entitlement paths, relative to the realm's `pay` host. */
125
+ export const TRAE_SESSION_USAGE_PATH = "/api/v1/commercial/get_session_usage";
126
+ export const TRAE_ACTIVITY_PATH = "/api/v1/commercial/get_user_activity";
127
+ export const TRAE_ENT_USAGE_PATH = "/trae/api/v1/pay/ide_user_ent_usage";
128
+ export const TRAE_PAY_STATUS_PATH = "/trae/api/v1/pay/ide_user_pay_status";
129
+
130
+ /** Growth paths, relative to the realm's `ug` host. CN-only upstream. */
131
+ export const TRAE_CHECKIN_STATUS_PATH = "/trae/api/v2/ug/checkin_credits/status";
132
+ export const TRAE_CHECKIN_CLAIM_PATH = "/trae/api/v2/ug/checkin_credits/claim";
133
+
134
+ /**
135
+ * Realm → deployment.
136
+ *
137
+ * `intl` carries two core fallbacks and an account fallback; `cn` has exactly
138
+ * one host per role. That is upstream fact, not an oversight.
139
+ *
140
+ * `modelListSupported` is false for intl: upstream answers
141
+ * `403 no schema found` there, so discovery must not pretend a live fetch
142
+ * succeeded and must fall back to the static roster.
143
+ */
144
+ export const TRAE_REALMS: Readonly<Record<TraeRealm, TraeRealmConfig>> = {
145
+ cn: {
146
+ // Resolved dynamically by the client. `api.trae.com.cn` only serves
147
+ // CloudIDE/OAuth/pay — `/api/ide/v1/*` there is a 404.
148
+ core: "https://trae-api-cn.mchost.guru",
149
+ coreFallbacks: [],
150
+ account: "https://api.trae.com.cn",
151
+ accountFallbacks: [],
152
+ pay: "https://api.trae.com.cn",
153
+ // The check-in routes are registered on `api.trae.cn` only; the same path
154
+ // on `api.trae.com.cn` is a 404.
155
+ ug: "https://api.trae.cn",
156
+ userAgent: "Trae CN/3.4.3",
157
+ versionCode: "20260212",
158
+ region: "CN",
159
+ modelListSupported: true,
160
+ },
161
+ intl: {
162
+ core: "https://core-normal.traeapi.us",
163
+ coreFallbacks: ["https://core-normal.trae.ai", "https://coresg-normal.trae.ai"],
164
+ account: "https://grow-normal.trae.ai",
165
+ accountFallbacks: ["https://grow-normal.traeapi.us"],
166
+ pay: "https://grow-normal.traeapi.us",
167
+ ug: "https://grow-normal.traeapi.us",
168
+ userAgent: "Trae/3.6.2",
169
+ versionCode: "20260212",
170
+ region: "US",
171
+ // Verified against an SG account: the region host answers `model_list`.
172
+ modelListSupported: true,
173
+ },
174
+ };
175
+
176
+ /**
177
+ * Data-plane host per account region, when the login reported one.
178
+ *
179
+ * An intl account is provisioned into a region and its token only authenticates
180
+ * against that region's core host. Verified: a `userRegion=sg` account sent to
181
+ * `core-normal.traeapi.us` answers `1001 "we are not able to authenticate you"`,
182
+ * while `coresg-normal.trae.ai` accepts the same token and streams normally.
183
+ * Without this, a correct login produced an account that could never dispatch.
184
+ *
185
+ * `store-country-code` follows the same value: the US host was rejecting the
186
+ * token *and* the roster it serves is a different catalog.
187
+ */
188
+ export const TRAE_REGION_CORE_HOSTS: Readonly<Record<string, string>> = {
189
+ sg: "https://coresg-normal.trae.ai",
190
+ us: "https://core-normal.traeapi.us",
191
+ };
192
+
193
+ /** The app id is constant across realms. */
194
+ export const TRAE_APP_ID = "6eefa01c-1036-4c7e-9ca5-d891f63bfcd8";
195
+
196
+ /** Plugin channel, constant across realms. */
197
+ export const TRAE_PLUGIN_CHANNEL = "icube-ai";
198
+
199
+ /**
200
+ * Provider id → realm.
201
+ *
202
+ * CN is a separate provider id rather than a flag on `trae`, matching
203
+ * `qodercn`/`cbcn`: the two deployments share no host, no OAuth client, and no
204
+ * model roster, so a single provider could not describe either one.
205
+ */
206
+ export function traeRealmForProvider(providerId: string): TraeRealm {
207
+ return providerId === "traecn" ? "cn" : "intl";
208
+ }
209
+
210
+ /** Data-plane host candidates in failover order: primary first, then fallbacks. */
211
+ export function traeCoreCandidates(realm: TraeRealm): readonly string[] {
212
+ const config = TRAE_REALMS[realm];
213
+ return [config.core, ...config.coreFallbacks];
214
+ }
215
+
216
+ /** Business host candidates in failover order. */
217
+ export function traeAccountCandidates(realm: TraeRealm): readonly string[] {
218
+ const config = TRAE_REALMS[realm];
219
+ return [config.account, ...config.accountFallbacks];
220
+ }