cartethyia 2.7.9 → 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 (496) hide show
  1. package/.env.example +6 -5
  2. package/dist/dashboard/assets/{BannedPage-BxoG1MrL.js → BannedPage-Dq530awD.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-ixwgKk53.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-BmttNV3U.js → CliToolsPage-DaZZ3cqP.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-BE3qwwfC.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-CRZdmMIX.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-Dv_FJ6Er.js → CustomizationPage-7xT5yg2E.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
  14. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js +1 -0
  15. package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
  16. package/dist/dashboard/assets/{InjectSystemPromptPanel-BOoO8rpz.js → InjectSystemPromptPanel-6Teoq9Se.js} +2 -2
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-ChJuNNVN.js → LoginPage-CZuI0MAR.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
  20. package/dist/dashboard/assets/{ModelPicker-BZAJtPg1.js → ModelPicker-C1hNTlkr.js} +1 -1
  21. package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
  22. package/dist/dashboard/assets/{OverviewPage-BE40s3u5.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-CQ6bPUf_.js → ProviderIcon-Ddhm1InL.js} +1 -1
  27. package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
  28. package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js +1 -0
  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-D1RV_2BX.js +1 -0
  33. package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
  34. package/dist/dashboard/assets/{SettingsPage-C5HPWqEv.js → SettingsPage-BzZAvYSX.js} +1 -1
  35. package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
  36. package/dist/dashboard/assets/{SetupPage-CFNcATJJ.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-YoD4tY5l.js → StudioPage-BjXFGSK4.js} +1 -1
  39. package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
  40. package/dist/dashboard/assets/{UsagePage-DxjaAhb_.js → UsagePage-CP8F8BN7.js} +1 -1
  41. package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BTlkq_Uk.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-BMcj1xZE.js +1 -0
  47. package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-DdbT_7Am.js → badge-DkRUuVfz.js} +1 -1
  49. package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-CsGN-nbm.js → banner-CDjW841Q.js} +1 -1
  51. package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-mgZ_TLLZ.js → card-Bto5UlL9.js} +1 -1
  53. package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-A1-v8-03.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-CJ4Zty89.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-B5L2jPQ_.js → dialog-DL0Zftwv.js} +1 -1
  59. package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-DkjUouAj.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-B-LVLV6o.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-CAhdYl3B.js → entry-CGvdNU6r.js} +1 -1
  65. package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-DScKYvZj.js +4 -0
  67. package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
  68. package/dist/dashboard/assets/{formatters-CrsdLVsn.js → formatters-BS3DZlZj.js} +1 -1
  69. package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-BU64Lqvp.js → index-SQnvvTWX.js} +2 -2
  71. package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-B2aDLyyg.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-BCVkdR9d.js → input-Bfx0EzRc.js} +1 -1
  75. package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-DNcAdMK-.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-DgrIH0hw.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-Cae01iu8.js → live-CbVTdX9V.js} +1 -1
  81. package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
  82. package/dist/dashboard/assets/{network-CRib7H_2.js → network-JGvDveof.js} +1 -1
  83. package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
  84. package/dist/dashboard/assets/{numbers-BKQE8Xpz.js → numbers-Dw6kiew-.js} +1 -1
  85. package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
  86. package/dist/dashboard/assets/{pagination-ecPvG_8g.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/providers/zro.svg +3 -0
  91. package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
  92. package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
  93. package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
  94. package/dist/dashboard/assets/{routing-Ckm1UXuC.js → routing-8jZr-3WE.js} +1 -1
  95. package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
  96. package/dist/dashboard/assets/{select-ChoFWyKz.js → select-oO1FxMVb.js} +2 -2
  97. package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
  98. package/dist/dashboard/assets/{settings-CKjFPlKW.js → settings-CLr3kdIx.js} +1 -1
  99. package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
  100. package/dist/dashboard/assets/{stack-DWCwU3fh.js → stack-Y3pDteZr.js} +1 -1
  101. package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
  102. package/dist/dashboard/assets/{state-Cg3VL5J_.js → state-CArX_Iak.js} +1 -1
  103. package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
  104. package/dist/dashboard/assets/{switch-Db70BMix.js → switch-DwlwmDO-.js} +1 -1
  105. package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
  106. package/dist/dashboard/assets/{useMutation-BqdE0LuM.js → useMutation-DJ9GYEgf.js} +1 -1
  107. package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
  108. package/dist/dashboard/index.html +2 -2
  109. package/dist/dashboard/index.html.gz +0 -0
  110. package/dist/dashboard/precompressed-assets.json +54 -53
  111. package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
  112. package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
  113. package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
  114. package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
  115. package/package.json +1 -1
  116. package/src/boot/app.ts +11 -14
  117. package/src/boot/dependencies.ts +5 -7
  118. package/src/boot/flush.ts +20 -0
  119. package/src/boot/lifecycle.ts +7 -5
  120. package/src/config/providers.ts +6 -5
  121. package/src/config/routing.ts +26 -0
  122. package/src/config/spec-metadata.ts +72 -0
  123. package/src/config/spec.ts +28 -8
  124. package/src/console/api-keys/contracts.ts +12 -1
  125. package/src/console/api-keys/routes.ts +40 -28
  126. package/src/console/auth/account-reset.ts +7 -9
  127. package/src/console/backup/contracts.ts +4 -0
  128. package/src/console/backup/export-endpoint.ts +7 -5
  129. package/src/console/backup/nine-router.ts +71 -10
  130. package/src/console/backup/service.ts +8 -1
  131. package/src/console/cli-tools/service.ts +6 -12
  132. package/src/console/console-router-guards.ts +23 -16
  133. package/src/console/console-router.ts +1 -1
  134. package/src/console/domain-registration.ts +7 -4
  135. package/src/console/inferhub/client.ts +1 -1
  136. package/src/console/inferhub/normalize.ts +24 -28
  137. package/src/console/inferhub/types.ts +3 -0
  138. package/src/console/ip-bans/routes.ts +7 -13
  139. package/src/console/observability/live.ts +2 -2
  140. package/src/console/observability/store.ts +0 -53
  141. package/src/console/providers/catalog/contracts.ts +9 -3
  142. package/src/console/providers/catalog/model-operations.ts +2 -3
  143. package/src/console/providers/catalog/provider-operations.ts +1 -1
  144. package/src/console/providers/catalog/routes.ts +3 -2
  145. package/src/console/providers/catalog/store.ts +1914 -1902
  146. package/src/console/providers/detail/routes.ts +1 -0
  147. package/src/console/providers/detail/store.ts +4 -0
  148. package/src/console/providers/oauth/callback-listener.ts +30 -11
  149. package/src/console/providers/oauth/device-egress.ts +123 -0
  150. package/src/console/providers/oauth/routes.ts +123 -16
  151. package/src/console/quota/account-quota-global.ts +14 -24
  152. package/src/console/quota/account-quota-shared.ts +49 -0
  153. package/src/console/quota/account-quota-tenant.ts +11 -23
  154. package/src/console/quota/account-test-egress.ts +2 -2
  155. package/src/console/quota/daily-growth-job.ts +1 -1
  156. package/src/console/quota/quota-view.ts +19 -3
  157. package/src/console/routing/pools/contracts.ts +1 -1
  158. package/src/console/routing/pools/operations.ts +32 -47
  159. package/src/console/routing/pools/probe-verdict.ts +2 -2
  160. package/src/console/routing/pools/relay-deploy.ts +1 -1
  161. package/src/console/routing/pools/store.ts +4 -4
  162. package/src/console/shared/errors.ts +33 -6
  163. package/src/main.ts +2 -3
  164. package/src/network/client.ts +67 -0
  165. package/src/network/egress/bidi-session.ts +322 -0
  166. package/src/network/egress/dial.ts +201 -0
  167. package/src/network/egress/factory.ts +191 -0
  168. package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
  169. package/src/network/egress/tls-profile.ts +123 -0
  170. package/src/network/egress/ws.ts +175 -0
  171. package/src/network/pool/{byte-accounting.ts → accounting.ts} +3 -2
  172. package/src/network/{types.ts → pool/agent-config.ts} +1 -1
  173. package/src/network/pool/agent.ts +2 -2
  174. package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +95 -20
  175. package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
  176. package/src/network/pool/resolver.ts +10 -849
  177. package/src/network/pool/selector.ts +184 -41
  178. package/src/network/ssrf.ts +7 -7
  179. package/src/network/{retry.ts → util/retry.ts} +1 -1
  180. package/src/network/{rate-limit-verdict.ts → verdict.ts} +1 -1
  181. package/src/observability/event-log.ts +7 -361
  182. package/src/observability/performance-metrics.ts +9 -0
  183. package/src/observability/runtime-metrics.ts +14 -9
  184. package/src/observability/telemetry-buffer.ts +16 -26
  185. package/src/observability/telemetry-reconciliation.ts +9 -37
  186. package/src/observability/telemetry-row.ts +125 -0
  187. package/src/persistence/db-mode.ts +4 -1
  188. package/src/persistence/dialect.ts +18 -0
  189. package/src/persistence/migrate.ts +6 -3
  190. package/src/persistence/schema.ts +17 -0
  191. package/src/persistence/ttl-cache.ts +21 -3
  192. package/src/protocol/codecs.ts +59 -2
  193. package/src/protocol/dsml.ts +34 -86
  194. package/src/protocol/markup-stream-parser.ts +195 -0
  195. package/src/protocol/reasoning-text.ts +19 -0
  196. package/src/protocol/request/chat.ts +10 -10
  197. package/src/protocol/request/gemini.ts +8 -2
  198. package/src/protocol/request/messages.ts +34 -22
  199. package/src/protocol/response/chat.ts +12 -20
  200. package/src/protocol/response/messages.ts +34 -7
  201. package/src/protocol/response/omp-tool-call-markup.ts +43 -86
  202. package/src/protocol/response/responses.ts +18 -2
  203. package/src/protocol/stop-reason.ts +23 -1
  204. package/src/protocol/think-split.ts +112 -0
  205. package/src/protocol/translation/dialect/qwen.ts +26 -58
  206. package/src/protocol/translation/quirks.ts +15 -0
  207. package/src/protocol/upstream/send-claude-messages.ts +2 -2
  208. package/src/providers/adapters/compatible-adapter.ts +76 -12
  209. package/src/providers/authentication/oauth-client.ts +40 -6
  210. package/src/providers/authentication/oauth-device-flow.ts +105 -11
  211. package/src/providers/authentication/oauth-flow-store.ts +82 -1
  212. package/src/providers/authentication/oauth-refresh-service.ts +766 -758
  213. package/src/providers/configured-provider.ts +148 -0
  214. package/src/providers/default-registry.ts +137 -36
  215. package/src/providers/discovery/discovery-types.ts +7 -0
  216. package/src/providers/discovery/probe-phases.ts +20 -6
  217. package/src/providers/discovery/probing-service.ts +22 -2
  218. package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
  219. package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
  220. package/src/providers/integrations/antigravity/antigravity-protocol.ts +16 -1
  221. package/src/providers/integrations/buddy/account.ts +969 -0
  222. package/src/providers/integrations/buddy/capabilities.ts +12 -12
  223. package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
  224. package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +176 -20
  225. package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
  226. package/src/providers/integrations/buddy/codebuddy/shared.ts +41 -26
  227. package/src/providers/integrations/buddy/headers.ts +311 -0
  228. package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
  229. package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
  230. package/src/providers/integrations/claude/claude-quota.ts +14 -17
  231. package/src/providers/integrations/cline/cline-oauth.ts +10 -5
  232. package/src/providers/integrations/cline/cline.ts +5 -6
  233. package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
  234. package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
  235. package/src/providers/integrations/clixad/provider.ts +145 -0
  236. package/src/providers/integrations/codex/codex-oauth.ts +8 -5
  237. package/src/providers/integrations/commandcode/quota.ts +22 -28
  238. package/src/providers/integrations/cursor/cursor-auth.ts +1 -9
  239. package/src/providers/integrations/cursor/cursor-exec.ts +9 -2
  240. package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
  241. package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
  242. package/src/providers/integrations/cursor/cursor-request.ts +26 -12
  243. package/src/providers/integrations/cursor/cursor-shared.ts +8 -5
  244. package/src/providers/integrations/cursor/cursor.ts +574 -110
  245. package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
  246. package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
  247. package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
  248. package/src/providers/integrations/deepai/deepai-common.ts +145 -0
  249. package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
  250. package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
  251. package/src/providers/integrations/deepai/provider.ts +42 -0
  252. package/src/providers/integrations/devin/devin-auth.ts +7 -0
  253. package/src/providers/integrations/devin/protocol/devin-proto.ts +1 -27
  254. package/src/providers/integrations/factory/factory-oauth.ts +33 -51
  255. package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
  256. package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
  257. package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
  258. package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
  259. package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
  260. package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
  261. package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
  262. package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
  263. package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
  264. package/src/providers/integrations/freebuff/freebuff.ts +682 -0
  265. package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
  266. package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
  267. package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
  268. package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
  269. package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
  270. package/src/providers/integrations/freepi/freepi-session.ts +158 -0
  271. package/src/providers/integrations/freepi/freepi.ts +80 -54
  272. package/src/providers/integrations/github/github-discovery.ts +30 -13
  273. package/src/providers/integrations/github/github-oauth.ts +217 -44
  274. package/src/providers/integrations/github/github-quota.ts +22 -5
  275. package/src/providers/integrations/github/github.ts +40 -32
  276. package/src/providers/integrations/grok/grok-oauth.ts +52 -49
  277. package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
  278. package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
  279. package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
  280. package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +42 -13
  281. package/src/providers/integrations/hermes/index.ts +2 -0
  282. package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
  283. package/src/providers/integrations/kilo/kilo.ts +1 -1
  284. package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
  285. package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
  286. package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
  287. package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
  288. package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
  289. package/src/providers/integrations/kiro/kiro.ts +15 -19
  290. package/src/providers/integrations/muse/muse-oauth.ts +5 -5
  291. package/src/providers/integrations/ollamacloud/index.ts +2 -0
  292. package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
  293. package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
  294. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  295. package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
  296. package/src/providers/integrations/openrouter/provider.ts +1 -1
  297. package/src/providers/integrations/perch/perch-credential.ts +25 -0
  298. package/src/providers/integrations/perch/perch-protocol.ts +0 -6
  299. package/src/providers/integrations/perch/perch-quota.ts +2 -16
  300. package/src/providers/integrations/perch/perch.ts +6 -16
  301. package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
  302. package/src/providers/integrations/qoder/qoder-adapter.ts +20 -125
  303. package/src/providers/integrations/qoder/qoder-auth.ts +109 -30
  304. package/src/providers/integrations/qoder/qoder-catalog.ts +21 -6
  305. package/src/providers/integrations/qoder/qoder-chat.ts +32 -29
  306. package/src/providers/integrations/qoder/qoder-checkin.ts +5 -5
  307. package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
  308. package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
  309. package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
  310. package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
  311. package/src/providers/integrations/qoder/qoder-types.ts +9 -0
  312. package/src/providers/integrations/qoder/qoder.ts +3 -26
  313. package/src/providers/integrations/sensenova/provider.ts +144 -0
  314. package/src/providers/integrations/trae/trae-protocol.ts +9 -5
  315. package/src/providers/integrations/trae/trae-quota.ts +21 -29
  316. package/src/providers/integrations/trae/trae-realm.ts +38 -0
  317. package/src/providers/integrations/trae/trae.ts +7 -24
  318. package/src/providers/integrations/vercel/provider.ts +117 -0
  319. package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
  320. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  321. package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
  322. package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
  323. package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
  324. package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
  325. package/src/providers/integrations/zro/zro.ts +205 -0
  326. package/src/providers/operations/account-error-classification.ts +199 -307
  327. package/src/providers/operations/account-health-service.ts +82 -1
  328. package/src/providers/operations/account-refresh-status.ts +36 -37
  329. package/src/providers/operations/account-tier.ts +15 -16
  330. package/src/providers/operations/client-version-resolver.ts +1 -1
  331. package/src/providers/operations/client-versions.ts +100 -81
  332. package/src/providers/operations/cooldown-rules.ts +11 -16
  333. package/src/providers/operations/oauth-refresh-lead.ts +46 -22
  334. package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
  335. package/src/providers/operations/provider-catalog-service.ts +1 -1
  336. package/src/providers/operations/provider-credential-service.ts +118 -51
  337. package/src/providers/operations/provider-rules/github.ts +48 -0
  338. package/src/providers/operations/provider-rules/grok.ts +33 -0
  339. package/src/providers/operations/provider-rules/inception.ts +49 -0
  340. package/src/providers/operations/provider-rules/index.ts +32 -0
  341. package/src/providers/operations/provider-rules/perch.ts +37 -0
  342. package/src/providers/operations/provider-rules/qoder.ts +70 -0
  343. package/src/providers/operations/provider-rules/trae.ts +81 -0
  344. package/src/providers/operations/session-resolution.ts +17 -1
  345. package/src/providers/provider-contracts.ts +6 -4
  346. package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
  347. package/src/providers/provider-metadata.ts +70 -10
  348. package/src/providers/provider-probe.ts +15 -1
  349. package/src/providers/provider-registry.ts +42 -33
  350. package/src/providers/quota/quota-contracts.ts +2 -0
  351. package/src/providers/quota/quota-limit-windows.ts +16 -5
  352. package/src/providers/quota/quota-support.ts +1 -1
  353. package/src/providers/quota/quota-window-parser.ts +90 -18
  354. package/src/providers/quota/refresh.ts +100 -21
  355. package/src/security/api-key-auth-cache.ts +1 -1
  356. package/src/security/api-key-auth.ts +6 -2
  357. package/src/security/ip-boundary.ts +1 -1
  358. package/src/transport/dispatch/attempt-finalize.ts +15 -14
  359. package/src/transport/dispatch/attempt-loop.ts +73 -19
  360. package/src/transport/dispatch/generation.ts +2 -8
  361. package/src/transport/dispatch/handler-deps.ts +1 -1
  362. package/src/transport/dispatch/leases.ts +1 -1
  363. package/src/transport/dispatch/native-shared.ts +25 -0
  364. package/src/transport/dispatch/proxy-request.ts +8 -46
  365. package/src/transport/dispatch/responses-compact.ts +2 -8
  366. package/src/transport/dispatch/retry-policy.ts +1 -1
  367. package/src/transport/dispatch/streaming/finalize.ts +10 -7
  368. package/src/transport/dispatch/streaming/stream.ts +9 -37
  369. package/src/transport/dispatch/systemone.ts +2 -8
  370. package/src/transport/dispatch/token-count.ts +2 -6
  371. package/src/transport/dispatch/upstream.ts +63 -0
  372. package/src/transport/drain-endpoint.ts +31 -4
  373. package/src/transport/failure-policy.ts +16 -30
  374. package/src/transport/guards/body-policy.ts +7 -1
  375. package/src/transport/guards/error-lifecycle.ts +13 -1
  376. package/src/transport/guards/request-context.ts +22 -14
  377. package/src/transport/request/preparer.ts +81 -71
  378. package/src/transport/routing/bounded-lru.ts +14 -0
  379. package/src/transport/routing/drain-reserve.ts +129 -0
  380. package/src/transport/routing/plan-gate.ts +7 -1
  381. package/src/transport/routing/round-robin.ts +60 -0
  382. package/src/transport/routing/route-catalog.ts +123 -111
  383. package/src/transport/routing/route-model.ts +51 -9
  384. package/src/transport/routing/router.ts +393 -32
  385. package/src/transport/shutdown-notice.ts +1 -1
  386. package/src/transport/surface/adapters.ts +11 -2
  387. package/src/transport/surface/chat/adapter.ts +2 -2
  388. package/src/transport/surface/chat/encode.ts +47 -4
  389. package/src/transport/surface/chat/parse.ts +2 -12
  390. package/src/transport/surface/completion.ts +44 -33
  391. package/src/transport/surface/content-parts.ts +21 -0
  392. package/src/transport/surface/messages/adapter.ts +7 -1
  393. package/src/transport/surface/messages/encode.ts +11 -3
  394. package/src/transport/surface/messages/parse.ts +2 -1
  395. package/src/transport/surface/messages/stream.ts +33 -17
  396. package/src/transport/surface/responses/encode.ts +22 -5
  397. package/src/transport/surface/responses/parse.ts +3 -11
  398. package/src/workers/buddy-growth-tasks.ts +8 -8
  399. package/src/workers/daily-checkin.ts +4 -4
  400. package/src/workers/quota-refresh-sweep.ts +1 -1
  401. package/src/workers/zcode-gift-claim.ts +1 -1
  402. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
  403. package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
  404. package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
  405. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
  406. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
  407. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
  408. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +0 -1
  409. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
  410. package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
  411. package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
  412. package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
  413. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
  414. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +0 -8
  415. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
  416. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
  417. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +0 -1
  418. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
  419. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js +0 -9
  420. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
  421. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js +0 -1
  422. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
  423. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
  424. package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
  425. package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
  426. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
  427. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
  428. package/dist/dashboard/assets/app-BX2nnKqs.css +0 -2
  429. package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
  430. package/dist/dashboard/assets/assert-CndthXFQ.js +0 -1
  431. package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
  432. package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
  433. package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
  434. package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
  435. package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
  436. package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
  437. package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
  438. package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
  439. package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
  440. package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
  441. package/dist/dashboard/assets/entry-D2xD0Tmd.js +0 -4
  442. package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
  443. package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
  444. package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
  445. package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
  446. package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
  447. package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
  448. package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
  449. package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
  450. package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
  451. package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
  452. package/dist/dashboard/assets/pagination-ecPvG_8g.js.gz +0 -0
  453. package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
  454. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  455. package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
  456. package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
  457. package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
  458. package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
  459. package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
  460. package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
  461. package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
  462. package/src/network/pool-health.ts +0 -210
  463. package/src/providers/bundled-discovery-providers.ts +0 -98
  464. package/src/providers/configured-openai-providers.ts +0 -120
  465. package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
  466. package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
  467. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
  468. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
  469. package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
  470. package/src/providers/integrations/buddy/buddy-identity.ts +0 -300
  471. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
  472. package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
  473. package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
  474. package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
  475. package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
  476. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
  477. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -185
  478. package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
  479. package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -175
  480. package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
  481. package/src/providers/integrations/qoder/chat-proto.ts +0 -328
  482. package/src/providers/integrations/qoder/qoder-grpc.ts +0 -871
  483. package/src/providers/integrations/qoder/qoder-ide.ts +0 -588
  484. package/src/providers/operations/model-discovery-cache.ts +0 -34
  485. package/src/providers/operations/probe-fetch.ts +0 -15
  486. package/src/providers/operations/provider-version-cache.ts +0 -30
  487. package/src/providers/provider-capabilities.ts +0 -33
  488. package/src/providers/provider-credential-envelope.ts +0 -43
  489. /package/dist/dashboard/assets/{app-B7kIWFm5.js → app-C0gTXEoN.js} +0 -0
  490. /package/dist/dashboard/assets/{app-B7kIWFm5.js.gz → app-C0gTXEoN.js.gz} +0 -0
  491. /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
  492. /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
  493. /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
  494. /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
  495. /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
  496. /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
@@ -1,417 +0,0 @@
1
- import { asNumber as sharedAsNumber } from "../../../shared/coerce";
2
-
3
- /**
4
- * Shared Tencent billing-meter parsing for the CodeBuddy family
5
- * (`cb`, `cbcn`).
6
- *
7
- * All three read the same endpoint shape — POST
8
- * `{base}/v2/billing/meter/get-user-resource` answering
9
- * `{ code, data: { Response: { Data: { Accounts: [...] } } } }` — and derive
10
- * the same windows from it: refill vs bonus is split by
11
- * `DeductionEndTime - CycleEndTime > 2d`, cadence labels are
12
- * Daily/Weekly/Monthly with dedup, and bonuses are `Bonus Pack N` sorted by
13
- * expiry. Only the endpoint, the identity headers, and the display name differ,
14
- * so those stay in each provider module and the envelope arithmetic lives here.
15
- */
16
- import type {
17
- FetchLike,
18
- ProviderQuotaResult,
19
- ProviderQuotaWindow,
20
- } from "../../quota/quota-contracts";
21
- import { cleanError, percentWindow, record as asRecord, text as asText } from "../../quota/quota-contracts";
22
-
23
- /**
24
- * One `Accounts[]` entry of the Tencent billing envelope.
25
- *
26
- * The `*Precise` string fields are exact and preferred; the plain numeric
27
- * fields are the lossy fallback. Extra fields are tolerated rather than
28
- * rejected, matching the upstream payload's open shape.
29
- */
30
- export interface TencentBillingAccount {
31
- readonly PackageName?: string | null;
32
- readonly SubProductName?: string | null;
33
- // Refill cycle live numbers.
34
- readonly CycleCapacityUsedPrecise?: string | number | null;
35
- readonly CycleCapacityUsed?: number | null;
36
- readonly CycleCapacitySizePrecise?: string | number | null;
37
- readonly CycleCapacitySize?: number | null;
38
- // Bonus lifetime numbers.
39
- readonly CapacityUsedPrecise?: string | number | null;
40
- readonly CapacityUsed?: number | null;
41
- readonly CapacitySizePrecise?: string | number | null;
42
- readonly CapacitySize?: number | null;
43
- // Cadence + expiry.
44
- readonly CycleStartTime?: string | number | null;
45
- readonly CycleEndTime?: string | number | null;
46
- readonly DeductionEndTime?: string | number | null;
47
- readonly [key: string]: unknown;
48
- }
49
-
50
- /** A bonus window whose expiry trails the cycle end by more than this is a refill. */
51
- const REFILL_GAP_MS = 2 * 24 * 60 * 60 * 1000;
52
- const TIMEOUT_MS = 15_000;
53
-
54
- /**
55
- * Handles Date | seconds/ms | numeric string | ISO → ISO string.
56
- *
57
- * Values below 1e12 are read as seconds; the upstream mixes both units across
58
- * accounts, so the magnitude decides rather than the field.
59
- */
60
- export function parseBillingResetTime(value: unknown): string | null {
61
- if (value === null || value === undefined || value === "") return null;
62
- try {
63
- if (value instanceof Date) return value.toISOString();
64
- if (typeof value === "number") {
65
- if (!Number.isFinite(value)) return null;
66
- return new Date(value < 1e12 ? value * 1000 : value).toISOString();
67
- }
68
- if (typeof value === "string") {
69
- const trimmed = value.trim();
70
- if (!trimmed) return null;
71
- if (/^\d+$/.test(trimmed)) {
72
- const timestamp = Number(trimmed);
73
- if (!Number.isFinite(timestamp)) return null;
74
- return new Date(timestamp < 1e12 ? timestamp * 1000 : timestamp).toISOString();
75
- }
76
- const d = new Date(trimmed);
77
- return Number.isFinite(d.getTime()) ? d.toISOString() : null;
78
- }
79
- return null;
80
- } catch {
81
- return null;
82
- }
83
- }
84
-
85
- /** Prefer the `*Precise` string field (exact), fall back to the numeric one. */
86
- export function billingNum(precise: unknown, plain: unknown): number {
87
- const n = Number(precise ?? plain);
88
- return Number.isFinite(n) ? n : 0;
89
- }
90
-
91
- /** Cadence label derived from the refill cycle's own span. */
92
- function refillCadence(acc: TencentBillingAccount): string {
93
- const start = parseBillingResetTime(acc.CycleStartTime);
94
- const end = parseBillingResetTime(acc.CycleEndTime);
95
- if (start && end) {
96
- const days = (new Date(end).getTime() - new Date(start).getTime()) / 86_400_000;
97
- if (days <= 1.5) return "Daily";
98
- if (days <= 10) return "Weekly";
99
- }
100
- return "Monthly";
101
- }
102
-
103
- /**
104
- * Resolves the bearer secret from a stored credential.
105
- *
106
- * The same account can be stored as a bare API key or as the OAuth envelope
107
- * JSON (`{accessToken, apiKey, token, ...}`), so both shapes resolve here and a
108
- * non-JSON credential passes through unchanged.
109
- */
110
- // Exported: the usage-detail read (`buddy-usage-shared.ts`) needs the same
111
- // credential resolution and must not re-implement it — an account stored as an
112
- // OAuth envelope has to resolve identically on both surfaces.
113
- export function resolveBearerToken(credential: string): string | null {
114
- const trimmed = credential.trim();
115
- if (!trimmed) return null;
116
- if (trimmed.startsWith("{")) {
117
- try {
118
- const parsed = JSON.parse(trimmed) as Record<string, unknown>;
119
- const rec = asRecord(parsed);
120
- if (rec) {
121
- const candidate =
122
- asText(rec.accessToken) ??
123
- asText(rec.access_token) ??
124
- asText(rec.apiKey) ??
125
- asText(rec.api_key) ??
126
- asText(rec.token);
127
- if (candidate) return candidate;
128
- }
129
- } catch {
130
- // fall through to raw credential
131
- }
132
- }
133
- return trimmed;
134
- }
135
-
136
- /**
137
- * Reads `data.Response.Data.Accounts` (the double-wrapped Tencent envelope).
138
- *
139
- * The exact path is tried first; the lowercase and single-wrapped variants are
140
- * accepted because the two regional gateways differ in casing.
141
- */
142
- function extractBillingAccounts(parsed: unknown): TencentBillingAccount[] {
143
- const root = asRecord(parsed) ?? {};
144
- const outerData = asRecord(root.data) ?? root;
145
- const responseRecord =
146
- asRecord(outerData["Response"]) ?? asRecord(outerData["response"]) ?? outerData;
147
- const innerData =
148
- asRecord(responseRecord["Data"]) ?? asRecord(responseRecord["data"]) ?? responseRecord;
149
- const rawAccounts = innerData["Accounts"] ?? innerData["accounts"] ?? [];
150
- if (!Array.isArray(rawAccounts)) return [];
151
- return rawAccounts
152
- .map((item) => asRecord(item) as TencentBillingAccount | null)
153
- .filter((v): v is TencentBillingAccount => v !== null);
154
- }
155
-
156
- function usedPercent(used: number, total: number): number | null {
157
- if (!Number.isFinite(used) || !Number.isFinite(total) || total <= 0) return null;
158
- const pct = (used / total) * 100;
159
- if (!Number.isFinite(pct)) return null;
160
- return Math.min(100, Math.max(0, pct));
161
- }
162
-
163
- function buildBillingWindows(
164
- accounts: readonly TencentBillingAccount[],
165
- defaultPlan: string,
166
- ): { plan: string; windows: ProviderQuotaWindow[] } {
167
- const cycleEndMs = (acc: TencentBillingAccount): number => {
168
- const r = parseBillingResetTime(acc.CycleEndTime);
169
- return r ? new Date(r).getTime() : Number.POSITIVE_INFINITY;
170
- };
171
- const isRefill = (acc: TencentBillingAccount): boolean => {
172
- const ce = cycleEndMs(acc);
173
- const parsedDeductionEnd = parseBillingResetTime(acc.DeductionEndTime);
174
- const de = parsedDeductionEnd ? new Date(parsedDeductionEnd).getTime() : Number.NaN;
175
- return Number.isFinite(ce) && Number.isFinite(de) && de - ce > REFILL_GAP_MS;
176
- };
177
- const byExpiry = (a: TencentBillingAccount, b: TencentBillingAccount): number =>
178
- cycleEndMs(a) - cycleEndMs(b);
179
-
180
- const refills = [...accounts].filter(isRefill).sort(byExpiry);
181
- const bonuses = [...accounts].filter((a) => !isRefill(a)).sort(byExpiry);
182
-
183
- const windows: ProviderQuotaWindow[] = [];
184
- const seenRefill: Record<string, number> = {};
185
-
186
- for (const acc of refills) {
187
- const base = refillCadence(acc);
188
- seenRefill[base] = (seenRefill[base] || 0) + 1;
189
- const label = seenRefill[base] > 1 ? `${base} ${seenRefill[base]}` : base;
190
- const used = billingNum(acc.CycleCapacityUsedPrecise, acc.CycleCapacityUsed);
191
- const total = billingNum(acc.CycleCapacitySizePrecise, acc.CycleCapacitySize);
192
- const resetAt = parseBillingResetTime(acc.CycleEndTime);
193
- const pct = usedPercent(used, total);
194
- // Kind encodes recurring semantics + cadence for Contract consumers that
195
- // do not read the optional `recurring` field. Keep label human-readable.
196
- const kind = `quota:${base.toLowerCase()}${seenRefill[base] > 1 ? `_${seenRefill[base]}` : ""}`;
197
- const w = percentWindow(kind, label, pct, resetAt, used, total, "pooled");
198
- windows.push({ ...w, kind, label, recurring: true });
199
- }
200
-
201
- bonuses.forEach((acc, idx) => {
202
- const label = `Bonus Pack ${idx + 1}`;
203
- const kind = `bonus:${idx + 1}`;
204
- const used = billingNum(acc.CapacityUsedPrecise, acc.CapacityUsed);
205
- const total = billingNum(acc.CapacitySizePrecise, acc.CapacitySize);
206
- const resetAt = parseBillingResetTime(acc.CycleEndTime);
207
- const pct = usedPercent(used, total);
208
- const w = percentWindow(kind, label, pct, resetAt, used, total, "pooled");
209
- windows.push({ ...w, kind, label, recurring: false });
210
- });
211
-
212
- const basePkg = (refills[0] ?? accounts[0] ?? {}) as TencentBillingAccount;
213
- const plan = asText(basePkg.PackageName) ?? asText(basePkg.SubProductName) ?? defaultPlan;
214
-
215
- return { plan, windows };
216
- }
217
-
218
- /** Everything provider-specific about one Tencent billing-meter read. */
219
- export interface TencentBillingFetchOptions {
220
- /** Canonical source id reported in the result. */
221
- readonly source: string;
222
- /** Operator-facing provider name used in every message. */
223
- readonly display: string;
224
- /** `{base}/v2/billing/meter/get-user-resource` for this provider. */
225
- readonly url: string;
226
- /** Provider transport/identity headers; `Authorization` is added here. */
227
- readonly headers: Record<string, string>;
228
- readonly credential: string;
229
- readonly fetcher: FetchLike;
230
- /** Plan name used when the payload carries neither package field. */
231
- readonly defaultPlan: string;
232
- /** Account UID for the `X-User-Id` billing identity header. */
233
- readonly uid?: string | undefined;
234
- }
235
-
236
- /**
237
- * Fetches one provider's quota from the Tencent billing endpoint.
238
- *
239
- * Auth is always `Authorization: Bearer <credential>` — both `api_key` and
240
- * `oauth` accounts store a secret the caller has already resolved. The filter
241
- * envelope is the reference shape (`ProductCode: p_tcaca`, statuses `[0, 3]`,
242
- * a 101-year package window): an empty `{}` can answer success with zero
243
- * credit packages for an account that has them.
244
- * Recurring semantics ride on the `kind` prefix (`quota:` vs `bonus:`) plus the optional
245
- * `recurring` boolean, so Contract consumers that ignore `recurring` still see
246
- * the split.
247
- */
248
- export async function fetchTencentBillingQuota(
249
- options: TencentBillingFetchOptions,
250
- ): Promise<ProviderQuotaResult> {
251
- const { source, display, url, headers, credential, fetcher, defaultPlan, uid } = options;
252
- const bearer = resolveBearerToken(credential);
253
- if (!bearer) {
254
- return { source, plan: null, windows: [], error: `${display} credential not available.` };
255
- }
256
-
257
- let response: Response;
258
- const now = new Date();
259
- const end = new Date(now.getTime() + 365 * 101 * 24 * 3600 * 1000);
260
- const stamp = (value: Date): string => value.toISOString().slice(0, 19).replace("T", " ");
261
- try {
262
- response = await fetcher(url, {
263
- method: "POST",
264
- headers: {
265
- ...headers,
266
- ...(uid === undefined || uid.length === 0 ? {} : { "X-User-Id": uid }),
267
- Authorization: `Bearer ${bearer}`,
268
- "Content-Type": "application/json",
269
- Accept: "application/json",
270
- },
271
- body: JSON.stringify({
272
- PageNumber: 1,
273
- PageSize: 100,
274
- ProductCode: "p_tcaca",
275
- Status: [0, 3],
276
- PackageEndTimeRangeBegin: stamp(now),
277
- PackageEndTimeRangeEnd: stamp(end),
278
- }),
279
- signal: AbortSignal.timeout(TIMEOUT_MS),
280
- });
281
- } catch (error) {
282
- return { source, plan: null, windows: [], error: cleanError(error) };
283
- }
284
-
285
- if (response.status === 401 || response.status === 403) {
286
- return { source, plan: null, windows: [], error: `${display} credential invalid or expired.` };
287
- }
288
- if (!response.ok) {
289
- return {
290
- source,
291
- plan: null,
292
- windows: [],
293
- error: `${display} quota API error (${response.status}).`,
294
- };
295
- }
296
-
297
- let textBody: string;
298
- try {
299
- textBody = await response.text();
300
- } catch (error) {
301
- return { source, plan: null, windows: [], error: cleanError(error) };
302
- }
303
-
304
- let json: unknown = null;
305
- try {
306
- json = textBody.length > 0 ? JSON.parse(textBody) : null;
307
- } catch {
308
- return {
309
- source,
310
- plan: null,
311
- windows: [],
312
- error: cleanError(new Error(`${display} quota response returned invalid JSON`)),
313
- };
314
- }
315
-
316
- const envelope = asRecord(json);
317
- // A non-zero `code` is a provider-side error even on HTTP 200.
318
- if (envelope !== null && "code" in envelope) {
319
- const code = Number(envelope["code"]);
320
- if (Number.isFinite(code) && code !== 0) {
321
- const msgText = asText(envelope["msg"]) ?? "unknown";
322
- return { source, plan: null, windows: [], error: `${display} quota error: ${msgText}` };
323
- }
324
- }
325
-
326
- const accounts = extractBillingAccounts(json);
327
- if (accounts.length === 0) {
328
- const enterprise = await fetchEnterpriseUsage(options, bearer);
329
- if (enterprise !== undefined) return enterprise;
330
- return { source, plan: null, windows: [], error: `${display} connected. No credit package found.` };
331
- }
332
-
333
- try {
334
- const { plan, windows } = buildBillingWindows(accounts, defaultPlan);
335
- return { source, plan, windows, error: null };
336
- } catch (error) {
337
- return {
338
- source,
339
- plan: null,
340
- windows: [],
341
- error: cleanError(error) || `${display} error: ${String(error)}`,
342
- };
343
- }
344
- }
345
-
346
- const ENTERPRISE_USAGE_PATH = "/v2/billing/meter/get-enterprise-user-usage";
347
-
348
- function enterpriseUsageUrl(resourceUrl: string): string {
349
- return resourceUrl.replace(/\/(?:v2\/)?billing\/meter\/get-user-resource$/, ENTERPRISE_USAGE_PATH);
350
- }
351
-
352
- function finiteNumber(value: unknown): number | undefined {
353
- return sharedAsNumber(value);
354
- }
355
-
356
- /**
357
- * Enterprise accounts answer the personal package endpoint with an empty list.
358
- * Their assigned credit lives on the enterprise usage endpoint, where `credit`
359
- * is spent and `limitNum` is the assignment.
360
- */
361
- async function fetchEnterpriseUsage(
362
- options: TencentBillingFetchOptions,
363
- bearer: string,
364
- ): Promise<ProviderQuotaResult | undefined> {
365
- const enterpriseId = options.headers["X-Enterprise-Id"] ?? options.headers["x-enterprise-id"];
366
- if (enterpriseId === undefined || enterpriseId.length === 0) return undefined;
367
- let response: Response;
368
- try {
369
- response = await options.fetcher(enterpriseUsageUrl(options.url), {
370
- method: "POST",
371
- headers: {
372
- ...options.headers,
373
- ...(options.uid === undefined || options.uid.length === 0 ? {} : { "X-User-Id": options.uid }),
374
- Authorization: `Bearer ${bearer}`,
375
- "Content-Type": "application/json",
376
- Accept: "application/json",
377
- },
378
- body: "{}",
379
- signal: AbortSignal.timeout(TIMEOUT_MS),
380
- });
381
- } catch {
382
- return undefined;
383
- }
384
- if (!response.ok) return undefined;
385
- let parsed: unknown;
386
- try {
387
- parsed = JSON.parse(await response.text());
388
- } catch {
389
- return undefined;
390
- }
391
- const envelope = asRecord(parsed);
392
- const data = asRecord(envelope?.["data"]) ?? envelope;
393
- if (data === null) return undefined;
394
- const limit = finiteNumber(data["limitNum"]) ?? finiteNumber(data["limit_num"]);
395
- const used = finiteNumber(data["credit"]) ?? finiteNumber(data["used_num"]) ?? 0;
396
- if (limit === undefined) return undefined;
397
- if (limit < 0) {
398
- return {
399
- source: options.source,
400
- plan: "Enterprise",
401
- windows: [percentWindow("quota:enterprise", "Enterprise", null, null, null, null, "independent")],
402
- error: null,
403
- };
404
- }
405
- const remaining = Math.max(0, limit - used);
406
- const usedPercent = limit === 0 ? 100 : Math.min(100, (used / limit) * 100);
407
- const resetsAt = typeof data["cycleEndTime"] === "string" ? data["cycleEndTime"] : null;
408
- return {
409
- source: options.source,
410
- plan: "Enterprise",
411
- windows: [{
412
- ...percentWindow("quota:enterprise", "Enterprise", usedPercent, resetsAt, used, limit, "pooled"),
413
- remaining,
414
- }],
415
- error: null,
416
- };
417
- }
@@ -1,240 +0,0 @@
1
- /**
2
- * Gateway-executed "server tools" for the Buddy family, ported from the
3
- * reference gateway (`lib/server/proxy/server-tools/` in `codebuddy2api`).
4
- *
5
- * Some clients ask the *provider* to run a web tool by declaring a
6
- * provider-executed type: Anthropic sends `web_search_20250305` /
7
- * `web_fetch_20250910`, the Responses API sends `web_search_preview`.
8
- * Upstream CodeBuddy has none of these, so the gateway executes the call
9
- * against a backend and hands the findings back as if upstream had produced
10
- * them.
11
- *
12
- * The distinction is declaration-driven, never name-driven: a client's own
13
- * ordinary `function` named `WebSearch` (Claude Code) is resolved by the
14
- * client, and the gateway must not pick it up. Only calls whose *declared
15
- * type* carries the provider-executed prefix are server tools.
16
- *
17
- * This module is the pure engine: classification, invocation building,
18
- * execution against injected backends, and the transcript messages a follow-up
19
- * upstream call needs. The streaming loop that re-drives the upstream within
20
- * one request is the adapter-side consumer.
21
- */
22
- import type { ToolDefinition } from "../../../protocol/canonical-model";
23
-
24
- export type ServerToolKind = "web_search" | "web_fetch";
25
-
26
- export interface ServerToolDeclaration {
27
- readonly kind: ServerToolKind;
28
- readonly name: string;
29
- }
30
-
31
- export interface ServerToolInvocation {
32
- readonly id: string;
33
- readonly kind: ServerToolKind;
34
- readonly rawArguments: string;
35
- }
36
-
37
- export interface ServerToolRunResult {
38
- /** Text handed back upstream as the tool's result message. */
39
- readonly content: string;
40
- readonly toolCallId: string;
41
- }
42
-
43
- export interface ServerToolExecutor {
44
- /** Runs one web fetch; `null` means the backend is unavailable. */
45
- readonly fetch?: ((input: { url: string }) => Promise<string>) | undefined;
46
- /** Runs one web search; `null` means the backend is unavailable. */
47
- readonly search?: ((input: { query: string }) => Promise<string>) | undefined;
48
- }
49
-
50
- /** The declared-type prefixes the provider-executed tools carry. */
51
- const SEARCH_TYPE_PREFIX = "web_search_";
52
- const FETCH_TYPE_PREFIX = "web_fetch_";
53
-
54
- function normalizeTypeName(value: string): string {
55
- return value.trim().toLowerCase().replace(/[_-]+/g, " ");
56
- }
57
-
58
- function classifyByType(type: string): ServerToolKind | null {
59
- const normalized = normalizeTypeName(type);
60
- if (normalized.startsWith(normalizeTypeName(SEARCH_TYPE_PREFIX))) return "web_search";
61
- if (normalized.startsWith(normalizeTypeName(FETCH_TYPE_PREFIX))) return "web_fetch";
62
- return null;
63
- }
64
-
65
- /**
66
- * Classifies one canonical tool declaration, or returns `null` for a
67
- * client-owned tool. Only the declared type counts; the name is never
68
- * consulted, so a client function named `WebSearch` is never mistaken for the
69
- * server tool. `native_type` retains the raw Anthropic/OpenAI type spelling
70
- * (`web_search_20250305`, `web_fetch_20250910`) when the surface carries one;
71
- * otherwise the normalized `tool_type` decides.
72
- */
73
- export function classifyServerToolDeclaration(tool: ToolDefinition): ServerToolKind | null {
74
- const raw = tool.native_type?.trim();
75
- if (raw !== undefined && raw.length > 0) {
76
- const byRaw = classifyByType(raw);
77
- if (byRaw !== null) return byRaw;
78
- }
79
- if (tool.tool_type === "web_search") return "web_search";
80
- if (tool.tool_type !== undefined && tool.tool_type !== "function") {
81
- const byType = classifyByType(tool.tool_type);
82
- if (byType !== null) return byType;
83
- }
84
- return null;
85
- }
86
-
87
- /** Indexes declared server tools by the name a call would carry. */
88
- export function findServerToolDeclarations(
89
- tools: readonly ToolDefinition[] | undefined,
90
- ): Map<string, ServerToolKind> {
91
- const out = new Map<string, ServerToolKind>();
92
- for (const tool of tools ?? []) {
93
- const kind = classifyServerToolDeclaration(tool);
94
- if (kind !== null) out.set(tool.name, kind);
95
- }
96
- return out;
97
- }
98
-
99
- /** True when a call's name matches a declared server tool. */
100
- export function isServerToolCall(
101
- name: string,
102
- declared: ReadonlyMap<string, ServerToolKind>,
103
- ): boolean {
104
- return declared.has(name);
105
- }
106
-
107
- function parseJsonArguments(raw: string): Record<string, unknown> {
108
- try {
109
- const parsed: unknown = JSON.parse(raw);
110
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
111
- return parsed as Record<string, unknown>;
112
- }
113
- } catch {
114
- // fall through
115
- }
116
- return {};
117
- }
118
-
119
- /** Builds one invocation from a tool call, kind already classified. */
120
- export function buildServerToolInvocation(
121
- callId: string,
122
- name: string,
123
- kind: ServerToolKind,
124
- rawArguments: string,
125
- ): ServerToolInvocation {
126
- return { id: callId || `server_tool_${name}`, kind, rawArguments };
127
- }
128
-
129
- function textOf(value: unknown): string {
130
- return typeof value === "string" ? value.trim() : "";
131
- }
132
-
133
- function safeFetchUrl(url: string): string | null {
134
- let parsed: URL;
135
- try {
136
- parsed = new URL(url);
137
- } catch {
138
- return null;
139
- }
140
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
141
- return parsed.toString();
142
- }
143
-
144
- /**
145
- * Runs one invocation against the injected backend. Failures become text
146
- * rather than exceptions: the arguments came from the model, so the useful
147
- * outcome is for it to see what went wrong and retry or answer without the
148
- * findings — not for the whole turn to fail.
149
- */
150
- async function runOne(
151
- executor: ServerToolExecutor,
152
- invocation: ServerToolInvocation,
153
- ): Promise<ServerToolRunResult> {
154
- const args = parseJsonArguments(invocation.rawArguments);
155
- if (invocation.kind === "web_fetch") {
156
- const url = safeFetchUrl(textOf(args["url"] ?? args["target"]));
157
- if (url === null) {
158
- return {
159
- toolCallId: invocation.id,
160
- content: "web_fetch failed: the model supplied an invalid or unsupported URL and nothing was fetched.",
161
- };
162
- }
163
- if (executor.fetch === undefined) {
164
- return {
165
- toolCallId: invocation.id,
166
- content: "web_fetch unavailable: the gateway has no fetch backend configured.",
167
- };
168
- }
169
- try {
170
- const content = await executor.fetch({ url });
171
- return { toolCallId: invocation.id, content };
172
- } catch (error) {
173
- return {
174
- toolCallId: invocation.id,
175
- content: `web_fetch failed: ${error instanceof Error ? error.message : String(error)}`,
176
- };
177
- }
178
- }
179
- const query = textOf(args["query"]);
180
- if (query.length === 0) {
181
- return {
182
- toolCallId: invocation.id,
183
- content: "web_search failed: the model supplied an empty query and nothing was searched.",
184
- };
185
- }
186
- if (executor.search === undefined) {
187
- return {
188
- toolCallId: invocation.id,
189
- content: "web_search unavailable: the gateway has no search backend configured.",
190
- };
191
- }
192
- try {
193
- const content = await executor.search({ query });
194
- return { toolCallId: invocation.id, content };
195
- } catch (error) {
196
- return {
197
- toolCallId: invocation.id,
198
- content: `web_search failed: ${error instanceof Error ? error.message : String(error)}`,
199
- };
200
- }
201
- }
202
-
203
- /** Runs every invocation and returns one tool message per call. */
204
- export async function executeServerToolInvocations(
205
- executor: ServerToolExecutor,
206
- invocations: readonly ServerToolInvocation[],
207
- ): Promise<readonly ServerToolRunResult[]> {
208
- return Promise.all(invocations.map((invocation) => runOne(executor, invocation)));
209
- }
210
-
211
- /**
212
- * Builds the transcript messages a follow-up upstream call needs: the
213
- * assistant turn carrying the calls, then one `tool` result per call. Matches
214
- * the OpenAI-shaped transcript the buddy upstream expects.
215
- */
216
- export function buildServerToolFollowUpMessages(
217
- assistantToolCalls: Array<{ id: string; name: string; arguments: string }>,
218
- results: readonly ServerToolRunResult[],
219
- ): Array<Record<string, unknown>> {
220
- const messages: Array<Record<string, unknown>> = [];
221
- if (assistantToolCalls.length > 0) {
222
- messages.push({
223
- role: "assistant",
224
- content: "",
225
- tool_calls: assistantToolCalls.map((call) => ({
226
- id: call.id,
227
- type: "function",
228
- function: { name: call.name, arguments: call.arguments },
229
- })),
230
- });
231
- }
232
- for (const result of results) {
233
- messages.push({
234
- role: "tool",
235
- tool_call_id: result.toolCallId,
236
- content: result.content,
237
- });
238
- }
239
- return messages;
240
- }