cartethyia 2.7.3 → 2.7.4

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 (352) hide show
  1. package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-ChG5svFb.js} +1 -1
  2. package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
  3. package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-BIcWQun7.js} +1 -1
  4. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-Bf2VelDe.js} +1 -1
  6. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
  7. package/dist/dashboard/assets/{CombosPage-JEbYJFxw.js → CombosPage-DCgm5g6g.js} +1 -1
  8. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
  9. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +1 -0
  10. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
  11. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +1 -0
  12. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
  13. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +1 -0
  14. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
  15. package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-U6AKaJcF.js} +1 -1
  16. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
  17. package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-BMS2t8BA.js} +1 -1
  18. package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
  19. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +1 -0
  20. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
  21. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +5 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
  23. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +1 -0
  24. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
  25. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js +9 -0
  26. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
  27. package/dist/dashboard/assets/QuotaPage-L0skzML8.js +1 -0
  28. package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
  29. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +1 -0
  30. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
  31. package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-f0DE1h0-.js} +1 -1
  32. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
  33. package/dist/dashboard/assets/{SortableList-BAL5npBC.js → SortableList-ClzpwyET.js} +1 -1
  34. package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
  35. package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-Cq5tW7hW.js} +3 -3
  36. package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
  37. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +56 -0
  38. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
  39. package/dist/dashboard/assets/api-keys-C38iXoO4.js +1 -0
  40. package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
  41. package/dist/dashboard/assets/app-a159k0ub.css +2 -0
  42. package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
  43. package/dist/dashboard/assets/assert-DbRA8EG2.js +15 -0
  44. package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
  45. package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-D9CS6dZk.js} +1 -1
  46. package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
  47. package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-DympBNq4.js} +1 -1
  48. package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
  49. package/dist/dashboard/assets/{card-Be5Uzamx.js → card-ibRWU_uT.js} +1 -1
  50. package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
  51. package/dist/dashboard/assets/cli-tools-63qBGaRw.js +1 -0
  52. package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
  53. package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-DJm-9pz7.js} +1 -1
  54. package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
  55. package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-8fiE6tec.js} +1 -1
  56. package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
  57. package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-CqGYeOrd.js} +1 -1
  58. package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
  59. package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-BSAsYsmV.js} +1 -1
  60. package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
  61. package/dist/dashboard/assets/entry-CiIQULZD.js +4 -0
  62. package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
  63. package/dist/dashboard/assets/entry-Daz0lJ-W.js +1 -0
  64. package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
  65. package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-D-MTJxMr.js} +1 -1
  66. package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
  67. package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-CPK47xQx.js} +1 -1
  68. package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
  69. package/dist/dashboard/assets/{index-cPrX5vn6.js → index-DBNPOfhV.js} +2 -2
  70. package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
  71. package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-BT0zntEr.js} +1 -1
  72. package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
  73. package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-Da2ZViWg.js} +1 -1
  74. package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
  75. package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-BXNeLw6M.js} +1 -1
  76. package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
  77. package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-BycOrrIV.js} +1 -1
  78. package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
  79. package/dist/dashboard/assets/live-DCQNZpF7.js +1 -0
  80. package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
  81. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
  82. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  83. package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
  84. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  85. package/dist/dashboard/assets/network-9Yj2ZyLo.js +1 -0
  86. package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
  87. package/dist/dashboard/assets/{numbers-BNDZnsUE.js → numbers-CEagj6ZZ.js} +2 -2
  88. package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
  89. package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
  90. package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
  91. package/dist/dashboard/assets/quota-Caw7wqci.js +1 -0
  92. package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
  93. package/dist/dashboard/assets/routing-CO9zCUus.js +1 -0
  94. package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
  95. package/dist/dashboard/assets/{select-B3iTetHN.js → select-BIYIBkMU.js} +2 -2
  96. package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
  97. package/dist/dashboard/assets/settings-jS3-PNSA.js +1 -0
  98. package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
  99. package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-DfgnFDWY.js} +1 -1
  100. package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
  101. package/dist/dashboard/assets/{state-DLS7IDMy.js → state-CkfdYWel.js} +1 -1
  102. package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
  103. package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-SYm237HG.js} +1 -1
  104. package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
  105. package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-BQqCRNIi.js} +1 -1
  106. package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
  107. package/dist/dashboard/index.html +2 -2
  108. package/dist/dashboard/index.html.gz +0 -0
  109. package/dist/dashboard/precompressed-assets.json +55 -54
  110. package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
  111. package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
  112. package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
  113. package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
  114. package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
  115. package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
  116. package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
  117. package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
  118. package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
  119. package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
  120. package/package.json +2 -1
  121. package/src/boot/app.ts +9 -8
  122. package/src/config/network.ts +7 -3
  123. package/src/console/api-keys/contracts.ts +52 -0
  124. package/src/console/api-keys/routes.ts +93 -4
  125. package/src/console/audit/routes.ts +3 -3
  126. package/src/console/auth/account-reset.ts +4 -4
  127. package/src/console/auth/session.ts +3 -3
  128. package/src/console/backup/contracts.ts +2 -0
  129. package/src/console/backup/export-endpoint.ts +6 -6
  130. package/src/console/backup/routes.ts +3 -3
  131. package/src/console/cli-tools/routes.ts +3 -3
  132. package/src/console/console-router-guards.ts +2 -2
  133. package/src/console/console-router.ts +4 -4
  134. package/src/console/data-management/routes.ts +3 -3
  135. package/src/console/domain-registration.ts +2 -2
  136. package/src/console/observability/contracts.ts +4 -0
  137. package/src/console/observability/live.ts +3 -3
  138. package/src/console/observability/logs.ts +3 -3
  139. package/src/console/observability/performance.ts +3 -3
  140. package/src/console/observability/routes.ts +3 -3
  141. package/src/console/observability/store.ts +16 -16
  142. package/src/console/providers/catalog/contracts.ts +43 -22
  143. package/src/console/providers/catalog/provider-operations.ts +17 -2
  144. package/src/console/providers/catalog/routes.ts +3 -3
  145. package/src/console/providers/catalog/store-helpers.ts +349 -326
  146. package/src/console/providers/catalog/store.ts +1523 -1292
  147. package/src/console/providers/detail/routes.ts +3 -3
  148. package/src/console/providers/oauth/routes.ts +3 -3
  149. package/src/console/quota/account-quota-tenant.ts +26 -1
  150. package/src/console/quota/account-test-egress.ts +4 -4
  151. package/src/console/routing/model/routes.ts +3 -3
  152. package/src/console/routing/pools/contracts.ts +11 -8
  153. package/src/console/routing/pools/operations.ts +14 -7
  154. package/src/console/routing/pools/probe-verdict.ts +8 -3
  155. package/src/console/routing/pools/relay-deploy.ts +9 -9
  156. package/src/console/routing/pools/routes.ts +3 -3
  157. package/src/console/routing/pools/store.ts +21 -1
  158. package/src/console/settings/routes.ts +3 -3
  159. package/src/console/share/share-router.ts +12 -12
  160. package/src/console/share/share-stats.ts +105 -35
  161. package/src/console/shared/read-through-cache.ts +38 -2
  162. package/src/console/updates/routes.ts +7 -4
  163. package/src/network/outbound-fetch.ts +75 -4
  164. package/src/network/pool/resolver.ts +39 -39
  165. package/src/network/pool-cooldown-store.ts +71 -16
  166. package/src/network/pool-health-machine.ts +21 -1
  167. package/src/network/rate-limit-verdict.ts +68 -0
  168. package/src/network/ssrf.ts +4 -4
  169. package/src/observability/event-query.ts +320 -16
  170. package/src/observability/telemetry-status.ts +1 -1
  171. package/src/persistence/db-sqlite.ts +7 -2
  172. package/src/persistence/readiness.ts +18 -6
  173. package/src/persistence/schema.ts +66 -7
  174. package/src/persistence/stores/api-key-store.ts +99 -3
  175. package/src/persistence/stores/telemetry-store.ts +1 -1
  176. package/src/protocol/codecs.ts +6 -6
  177. package/src/protocol/gateway-error.ts +6 -3
  178. package/src/protocol/image-generation.ts +2 -2
  179. package/src/protocol/response/chat.ts +2 -2
  180. package/src/protocol/response/codex.ts +1 -1
  181. package/src/protocol/response/gemini.ts +1 -1
  182. package/src/protocol/response/messages.ts +5 -5
  183. package/src/protocol/response/responses.ts +2 -2
  184. package/src/protocol/sse-decoder.ts +14 -3
  185. package/src/protocol/translation/capabilities.ts +2 -2
  186. package/src/protocol/upstream/send-claude-messages.ts +3 -3
  187. package/src/providers/adapters/base-adapter.ts +1 -1
  188. package/src/providers/adapters/compatible-adapter.ts +2 -2
  189. package/src/providers/authentication/oauth-refresh-service.ts +2 -2
  190. package/src/providers/configured-provider.ts +1 -1
  191. package/src/providers/discovery/probe-phases.ts +2 -2
  192. package/src/providers/discovery/probing-service.ts +12 -12
  193. package/src/providers/integrations/agentrouter/agentrouter.ts +2 -3
  194. package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
  195. package/src/providers/integrations/anthropic.ts +4 -11
  196. package/src/providers/integrations/antigravity/antigravity.ts +13 -24
  197. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
  198. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
  199. package/src/providers/integrations/buddy/buddy-identity.ts +22 -1
  200. package/src/providers/integrations/buddy/buddy-media.ts +16 -16
  201. package/src/providers/integrations/buddy/codebuddy/provider.ts +1 -1
  202. package/src/providers/integrations/buddy/workbuddy/provider.ts +1 -1
  203. package/src/providers/integrations/claude/claude-betas.ts +1 -1
  204. package/src/providers/integrations/claude/claude-credentials.ts +1 -1
  205. package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
  206. package/src/providers/integrations/claude/claude.ts +3 -10
  207. package/src/providers/integrations/cline/cline.ts +1 -1
  208. package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
  209. package/src/providers/integrations/codex/codex-errors.ts +1 -1
  210. package/src/providers/integrations/codex/codex.ts +14 -15
  211. package/src/providers/integrations/commandcode/commandcode.ts +4 -4
  212. package/src/providers/integrations/commandcode/quota.ts +5 -1
  213. package/src/providers/integrations/cursor/cursor-http2.ts +11 -11
  214. package/src/providers/integrations/cursor/cursor.ts +15 -15
  215. package/src/providers/integrations/factory/factory.ts +4 -4
  216. package/src/providers/integrations/gemini.ts +11 -11
  217. package/src/providers/integrations/github/github.ts +6 -6
  218. package/src/providers/integrations/grok/grok.ts +2 -6
  219. package/src/providers/integrations/inferhub.ts +4 -11
  220. package/src/providers/integrations/kimi/kimi.ts +4 -11
  221. package/src/providers/integrations/kiro/kiro.ts +10 -10
  222. package/src/providers/integrations/muse/muse.ts +6 -9
  223. package/src/providers/integrations/perplexity.ts +2 -2
  224. package/src/providers/integrations/qoder/qoder-catalog.ts +47 -9
  225. package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
  226. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
  227. package/src/providers/integrations/qoder/qoder-realm.ts +15 -0
  228. package/src/providers/integrations/qoder/qoder-umid.ts +12 -13
  229. package/src/providers/integrations/qoder/qoder-virtual.ts +2 -2
  230. package/src/providers/integrations/qoder/qoder.ts +188 -22
  231. package/src/providers/integrations/tokenharbor.ts +2 -2
  232. package/src/providers/integrations/trae/trae.ts +6 -9
  233. package/src/providers/integrations/zcode/oauth.ts +1 -1
  234. package/src/providers/operations/provider-catalog-service.ts +4 -4
  235. package/src/providers/operations/provider-credential-service.ts +16 -4
  236. package/src/providers/operations/upstream-deadline.ts +3 -3
  237. package/src/providers/provider-contracts.ts +47 -3
  238. package/src/providers/provider-credential-resolver.ts +1 -1
  239. package/src/providers/provider-registry.ts +4 -4
  240. package/src/providers/quota/quota-contracts.ts +24 -10
  241. package/src/providers/quota/refresh.ts +2 -2
  242. package/src/providers/search/search-provider.ts +5 -5
  243. package/src/security/abuse.ts +4 -4
  244. package/src/security/admission/reasons.ts +17 -17
  245. package/src/security/api-key-auth.ts +4 -4
  246. package/src/shared/coerce.ts +19 -0
  247. package/src/transport/dispatch/attempt-finalize.ts +4 -3
  248. package/src/transport/dispatch/attempt-loop.ts +10 -7
  249. package/src/transport/dispatch/empty-stream.ts +6 -6
  250. package/src/transport/dispatch/generation.ts +9 -9
  251. package/src/transport/dispatch/leases.ts +8 -8
  252. package/src/transport/dispatch/proxy-request.ts +7 -7
  253. package/src/transport/dispatch/responses-compact.ts +11 -11
  254. package/src/transport/dispatch/retry-policy.ts +4 -67
  255. package/src/transport/dispatch/stream-bridge.ts +1 -1
  256. package/src/transport/dispatch/streaming/finalize.ts +9 -8
  257. package/src/transport/dispatch/systemone.ts +8 -8
  258. package/src/transport/dispatch/token-count.ts +9 -9
  259. package/src/transport/dispatch/websearch.ts +6 -6
  260. package/src/transport/drain-endpoint.ts +2 -2
  261. package/src/transport/failure-policy.ts +13 -6
  262. package/src/transport/guards/auth-guard.ts +6 -5
  263. package/src/transport/guards/body-policy.ts +4 -4
  264. package/src/transport/guards/error-lifecycle.ts +18 -3
  265. package/src/transport/guards/guard-errors.ts +7 -3
  266. package/src/transport/guards/headroom-guard.ts +1 -1
  267. package/src/transport/guards/pipeline.ts +6 -6
  268. package/src/transport/guards/readiness-guard.ts +16 -2
  269. package/src/transport/guards/request-context.ts +3 -3
  270. package/src/transport/request/preparer.ts +20 -20
  271. package/src/transport/request/state.ts +4 -7
  272. package/src/transport/routing/route-catalog.ts +97 -2
  273. package/src/transport/routing/route-model.ts +7 -7
  274. package/src/transport/routing/router.ts +2 -2
  275. package/src/transport/shutdown-notice.ts +19 -7
  276. package/src/transport/surface/completion.ts +1 -1
  277. package/src/transport/surface/messages/encode.ts +1 -1
  278. package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
  279. package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
  280. package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
  281. package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
  282. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
  283. package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
  284. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
  285. package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
  286. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
  287. package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
  288. package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
  289. package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
  290. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
  291. package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
  292. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
  293. package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
  294. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
  295. package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
  296. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
  297. package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
  298. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
  299. package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
  300. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
  301. package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
  302. package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
  303. package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
  304. package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
  305. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
  306. package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
  307. package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
  308. package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
  309. package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
  310. package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
  311. package/dist/dashboard/assets/assert-C0YYuZJ4.js +0 -1
  312. package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
  313. package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
  314. package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
  315. package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
  316. package/dist/dashboard/assets/cli-tools-muFRphDu.js +0 -1
  317. package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
  318. package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
  319. package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
  320. package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
  321. package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
  322. package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
  323. package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
  324. package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
  325. package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
  326. package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
  327. package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
  328. package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
  329. package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
  330. package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
  331. package/dist/dashboard/assets/layout-C_7A6H55.js.gz +0 -0
  332. package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
  333. package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
  334. package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
  335. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
  336. package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
  337. package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
  338. package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
  339. package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
  340. package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
  341. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  342. package/dist/dashboard/assets/routing-BEH32QOC.js +0 -1
  343. package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
  344. package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
  345. package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
  346. package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
  347. package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
  348. package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
  349. package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
  350. package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
  351. /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-BayTTy01.js} +0 -0
  352. /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-BayTTy01.js.gz} +0 -0
@@ -1,4 +1,4 @@
1
- import { Elysia, t } from "elysia";
1
+ import { Elysia, t, type AnyElysia } from "elysia";
2
2
  import { ConsoleDomainError, errorResponse, requireTenantScope } from "../../shared/errors";
3
3
  import { literalUnion } from "../../shared/elysia-schema";
4
4
  import type { AccessDecision } from "../../../security/access-control";
@@ -160,7 +160,7 @@ const updateRoutingBody = t.Object({
160
160
  });
161
161
 
162
162
 
163
- export function createProviderDetailRoutes(config: ProviderDetailConfig): Elysia {
163
+ export function createProviderDetailRoutes(config: ProviderDetailConfig): AnyElysia {
164
164
  const factory = createProviderDetailOperations(config);
165
165
  return new Elysia({ prefix: "/providers" })
166
166
  .get("/:providerId/routing", async ({ request, params, set }) => {
@@ -191,5 +191,5 @@ export function createProviderDetailRoutes(config: ProviderDetailConfig): Elysia
191
191
  return providerDetailErrorResponse(e, set);
192
192
  }
193
193
  },
194
- ) as unknown as Elysia;
194
+ )
195
195
  }
@@ -9,7 +9,7 @@ import type { ProviderRegistry } from "../../../providers/provider-registry";
9
9
  import { browserAuthorizeRedirectUri } from "../../../config/providers";
10
10
  import { log } from "../../../observability/logger";
11
11
  import { GatewayError } from "../../../protocol/gateway-error";
12
- import { Elysia, t } from "elysia";
12
+ import { Elysia, t, type AnyElysia } from "elysia";
13
13
  import { ConsoleDomainError, errorResponse, requireScope } from "../../shared/errors";
14
14
  import { generateOAuthState, generatePkcePair } from "../../../providers/authentication/oauth-flow-store";
15
15
  import type { OAuthFlowStorage } from "../../../providers/authentication/oauth-flow-store";
@@ -639,7 +639,7 @@ const importBody = t.Object({
639
639
  accountLabel: t.Optional(t.String()),
640
640
  });
641
641
 
642
- export function createOAuthLoginRoutes(config: OAuthLoginConfig): Elysia {
642
+ export function createOAuthLoginRoutes(config: OAuthLoginConfig): AnyElysia {
643
643
  const factory = createOAuthLoginOperations(config);
644
644
  return new Elysia({ prefix: "/providers" })
645
645
  .post(
@@ -721,6 +721,6 @@ export function createOAuthLoginRoutes(config: OAuthLoginConfig): Elysia {
721
721
  return oauthErrorResponse(e, set);
722
722
  }
723
723
  },
724
- ) as unknown as Elysia;
724
+ )
725
725
  }
726
726
 
@@ -54,6 +54,7 @@ import {
54
54
  listAccountResetCredits,
55
55
  } from "../../providers/operations/account-reset-service";
56
56
  import {
57
+ enqueueBackgroundQuotaRefresh,
57
58
  pendingQuotaRefreshes,
58
59
  toQuotaAccountHealth,
59
60
  toQuotaView,
@@ -369,6 +370,15 @@ export function registerAccountQuotaTenantRoutes(
369
370
  icon: providerId,
370
371
  }));
371
372
  let refreshing = 0;
373
+ // Cold-cache backfill: the overview renders whatever is cached, and the
374
+ // periodic sweep owns freshness — but with many accounts (e.g. a large
375
+ // Qoder roster) the sweep's per-pass cap cannot keep every cache warm,
376
+ // so a page open would show accounts with no badge for minutes. Enqueue
377
+ // a bounded background refresh for accounts whose cache entry is missing
378
+ // (not merely stale), so the badge fills in without waiting for the next
379
+ // sweep pass and without fanning out to the whole roster at once.
380
+ const coldCacheCap = 20;
381
+ let coldCacheEnqueued = 0;
372
382
  // The sweep's day markers answer "attempted today" without an upstream
373
383
  // request; only check-in-capable accounts can carry one, so only they
374
384
  // are read.
@@ -404,6 +414,21 @@ export function registerAccountQuotaTenantRoutes(
404
414
  const canRefresh = providerRegistry.hasQuotaCollector(row.providerId);
405
415
  const isRefreshing = canRefresh && (pending.has(row.id) || entry === undefined);
406
416
  if (isRefreshing) refreshing += 1;
417
+ // Backfill a missing cache entry on page open (bounded, see above).
418
+ if (
419
+ canRefresh &&
420
+ entry === undefined &&
421
+ !pending.has(row.id) &&
422
+ coldCacheEnqueued < coldCacheCap
423
+ ) {
424
+ coldCacheEnqueued += 1;
425
+ enqueueBackgroundQuotaRefresh(refreshDeps, {
426
+ accountId: row.id,
427
+ providerId: row.providerId,
428
+ tenantId: row.tenantId,
429
+ credentialKind: row.credentialKind,
430
+ });
431
+ }
407
432
  return {
408
433
  id: row.id,
409
434
  provider: row.providerId,
@@ -525,7 +550,7 @@ export function registerAccountQuotaTenantRoutes(
525
550
  throw new GatewayError(
526
551
  "proxy_pool_unavailable",
527
552
  503,
528
- "Account test cannot select a proxy because proxy routing is not configured.",
553
+ "Account test cannot select a proxy because proxy routing is not configured (request {requestId}). No bridge to cross — configure a pool first 🏗️",
529
554
  { reason: "proxy_routing_unconfigured" },
530
555
  "network",
531
556
  );
@@ -138,7 +138,7 @@ export async function selectAccountTestEgress(
138
138
  throw new GatewayError(
139
139
  "proxy_pool_unavailable",
140
140
  503,
141
- "No active proxy pool is available because coordination is unavailable.",
141
+ "No active proxy pool is available because coordination is unavailable (request {requestId}). The bouncer stepped out — come back shortly 🚪",
142
142
  { reason: "coordination_unavailable" },
143
143
  "network",
144
144
  );
@@ -159,10 +159,10 @@ export async function selectAccountTestEgress(
159
159
  const status = failure.reason === "at_capacity" ? 429 : 503;
160
160
  const message =
161
161
  failure.reason === "at_capacity"
162
- ? "Every proxy pool for this provider is at its concurrency limit. Retry shortly, or add a pool or raise its capacity."
162
+ ? "Every proxy pool for this provider is at its concurrency limit (request {requestId}). Retry shortly, or add a pool or raise its capacity. The pools are full right now — chill 🌊"
163
163
  : failure.reason === "cooldown"
164
- ? "Every proxy pool for this provider is cooling down after upstream failures. Retry once the cooldown resets."
165
- : "No active proxy pool is configured for this tenant and provider. Add one, or route this provider through direct egress.";
164
+ ? "Every proxy pool for this provider is cooling down after upstream failures (request {requestId}). Retry once the cooldown resets. The proxies are catching their breath — zzz 💤"
165
+ : "No active proxy pool is configured for this tenant and provider (request {requestId}). Add one, or route this provider through direct egress. No bridge to cross — configure a pool first 🏗️";
166
166
  throw new GatewayError(
167
167
  code,
168
168
  status,
@@ -1,5 +1,5 @@
1
1
  // Model routing control-plane HTTP routes. The only module that touches Elysia.
2
- import { Elysia, t } from "elysia";
2
+ import { Elysia, t, type AnyElysia } from "elysia";
3
3
  import { errorResponse } from "../../shared/errors";
4
4
  import { literalUnion } from "../../shared/elysia-schema";
5
5
  import { modelComboStrategy, type ComboStrategy } from "../../../persistence/schema";
@@ -41,7 +41,7 @@ const updateComboBody = t.Object({
41
41
  members: t.Optional(t.Array(t.String(), { minItems: 1 })),
42
42
  strategy: t.Optional(comboStrategySchema),
43
43
  });
44
- export function createModelRoutingRoutes(config: ModelRoutingConfig): Elysia {
44
+ export function createModelRoutingRoutes(config: ModelRoutingConfig): AnyElysia {
45
45
  const factory = createModelRoutingOperations(config);
46
46
  return new Elysia({ prefix: "/routing" })
47
47
  .get("/aliases", async ({ request, set }) => {
@@ -132,5 +132,5 @@ export function createModelRoutingRoutes(config: ModelRoutingConfig): Elysia {
132
132
  } catch (e) {
133
133
  return modelRoutingErrorResponse(e, set);
134
134
  }
135
- }) as unknown as Elysia;
135
+ });
136
136
  }
@@ -188,7 +188,7 @@ export function validateTransportConfig(
188
188
  case "http":
189
189
  case "https":
190
190
  if (cfg.timeout !== undefined && typeof cfg.timeout !== "number")
191
- throw new ConsoleDomainError("invalid_config", 400, "HTTP timeout must be a number");
191
+ throw new ConsoleDomainError("invalid_pool", 400, "HTTP timeout must be a number", { field: "timeout" });
192
192
  return;
193
193
  case "socks5":
194
194
  return;
@@ -196,9 +196,10 @@ export function validateTransportConfig(
196
196
  // Compile-time exhaustiveness: new TransportKind variants must add a case.
197
197
  kind satisfies never;
198
198
  throw new ConsoleDomainError(
199
- "invalid_config",
199
+ "invalid_pool",
200
200
  400,
201
201
  `Unsupported transport kind: ${String(kind)}`,
202
+ { field: "kind" },
202
203
  );
203
204
  }
204
205
  }
@@ -494,10 +495,10 @@ export function validateEndpoint(
494
495
  try {
495
496
  u = new URL(candidate);
496
497
  } catch {
497
- throw new ConsoleDomainError("invalid_endpoint", 400, "Endpoint must be host:port");
498
+ throw new ConsoleDomainError("invalid_pool", 400, "Endpoint must be host:port", { field: "endpoint" });
498
499
  }
499
500
  if (!u.hostname || !u.port)
500
- throw new ConsoleDomainError("invalid_endpoint", 400, "Endpoint must be host:port");
501
+ throw new ConsoleDomainError("invalid_pool", 400, "Endpoint must be host:port", { field: "endpoint" });
501
502
  if (u.hostname === "localhost" || u.hostname.endsWith(".internal"))
502
503
  throw new ConsoleDomainError("ssrf_rejected", 400, "Private endpoint rejected");
503
504
  if (isIP(u.hostname) !== 0 && !isAddressAllowed(u.hostname, policy))
@@ -508,10 +509,10 @@ export function validateEndpoint(
508
509
  try {
509
510
  u = new URL(endpoint);
510
511
  } catch {
511
- throw new ConsoleDomainError("invalid_endpoint", 400, "Endpoint must be a valid http(s) URL");
512
+ throw new ConsoleDomainError("invalid_pool", 400, "Endpoint must be a valid http(s) URL", { field: "endpoint" });
512
513
  }
513
514
  if (!["http:", "https:"].includes(u.protocol)) {
514
- throw new ConsoleDomainError("invalid_endpoint", 400, "Endpoint must be a valid http(s) URL");
515
+ throw new ConsoleDomainError("invalid_pool", 400, "Endpoint must be a valid http(s) URL", { field: "endpoint" });
515
516
  }
516
517
  if (u.hostname === "localhost" || u.hostname.endsWith(".internal")) {
517
518
  throw new ConsoleDomainError("ssrf_rejected", 400, "Private endpoint rejected");
@@ -529,9 +530,10 @@ export function validatePoolLimits(maxInflight?: number, weight?: number): void
529
530
  (!Number.isInteger(maxInflight) || maxInflight < 1 || maxInflight > MAX_POOL_INFLIGHT)
530
531
  ) {
531
532
  throw new ConsoleDomainError(
532
- "invalid_pool_limits",
533
+ "invalid_pool",
533
534
  400,
534
535
  "maxInflight must be an integer between 1 and 1000000",
536
+ { field: "maxInflight" },
535
537
  );
536
538
  }
537
539
  if (
@@ -539,9 +541,10 @@ export function validatePoolLimits(maxInflight?: number, weight?: number): void
539
541
  (!Number.isInteger(weight) || weight < 1 || weight > MAX_POOL_WEIGHT)
540
542
  ) {
541
543
  throw new ConsoleDomainError(
542
- "invalid_pool_limits",
544
+ "invalid_pool",
543
545
  400,
544
546
  "weight must be an integer between 1 and 1000",
547
+ { field: "weight" },
545
548
  );
546
549
  }
547
550
  }
@@ -141,9 +141,10 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
141
141
  const rotateCount = request.rotateCount ?? current.rotateCount;
142
142
  if (!Number.isInteger(rotateCount) || rotateCount < 1 || rotateCount > 1000) {
143
143
  throw new ConsoleDomainError(
144
- "invalid_pool_limits",
144
+ "invalid_pool",
145
145
  400,
146
146
  "rotateCount must be an integer between 1 and 1000",
147
+ { field: "rotateCount" },
147
148
  );
148
149
  }
149
150
  const saved = await config.store.setStrategy(a.tenantId, { strategy, rotateCount });
@@ -447,6 +448,12 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
447
448
  const a = requireTenantScope(access, "dashboard:write");
448
449
  const recovered = await config.store.recover(a.tenantId, poolId);
449
450
  if (!recovered) throw new ConsoleDomainError("pool_not_found", 404, `Pool ${poolId} not found`);
451
+ // Recover is the operator's "make this pool healthy again": besides
452
+ // resetting the pool-level state, drop every provider-rate-limit marker
453
+ // (volatile and durable) so a just-cleared pool is not still excluded
454
+ // by a stale 429 flag.
455
+ await config.poolSelector?.clearPoolCooldowns(poolId).catch(() => undefined);
456
+ await config.store.dropAllProviderCooldownMarkers?.(a.tenantId, poolId);
450
457
  await config.poolAgentReleaser?.releasePool(poolId, a.tenantId);
451
458
  await config.auditSink?.record({
452
459
  access: a,
@@ -482,7 +489,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
482
489
  ): Promise<PoolSpeedTestResult> {
483
490
  const a = requireTenantScope(access, "dashboard:read");
484
491
  if (!config.store.speedTest) {
485
- throw new ConsoleDomainError("not_supported", 400, "Speed tests are unavailable");
492
+ throw new ConsoleDomainError("capability_unsupported", 400, "Speed tests are unavailable");
486
493
  }
487
494
  if (!Number.isInteger(bytes) || bytes < SPEED_TEST_MIN_BYTES || bytes > SPEED_TEST_MAX_BYTES) {
488
495
  throw new ConsoleDomainError(
@@ -507,7 +514,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
507
514
  ): Promise<PoolProviderProbeResult> {
508
515
  const a = requireTenantScope(access, "dashboard:read");
509
516
  if (!config.store.providerProbe) {
510
- throw new ConsoleDomainError("not_supported", 400, "Provider probing is unavailable");
517
+ throw new ConsoleDomainError("capability_unsupported", 400, "Provider probing is unavailable");
511
518
  }
512
519
  const result = await config.store.providerProbe(a.tenantId, poolId, providerId);
513
520
  if (!config.poolSelector) return result;
@@ -596,7 +603,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
596
603
  );
597
604
  }
598
605
  if (!config.store.probeAdHocProvider) {
599
- throw new ConsoleDomainError("not_supported", 400, "Provider probing is unavailable");
606
+ throw new ConsoleDomainError("capability_unsupported", 400, "Provider probing is unavailable");
600
607
  }
601
608
  const probe = config.store.probeAdHocProvider.bind(config.store);
602
609
  const results: PoolBatchProviderProbeResult[] = new Array(requests.length);
@@ -632,7 +639,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
632
639
  if (config.store.probeAdHoc) {
633
640
  return config.store.probeAdHoc(a.tenantId, request);
634
641
  }
635
- throw new ConsoleDomainError("not_supported", 400, "Ad-hoc pool probing is unavailable");
642
+ throw new ConsoleDomainError("capability_unsupported", 400, "Ad-hoc pool probing is unavailable");
636
643
  },
637
644
  /**
638
645
  * Probes many unsaved pool definitions in one call.
@@ -656,7 +663,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
656
663
  );
657
664
  }
658
665
  if (!config.store.probeAdHoc) {
659
- throw new ConsoleDomainError("not_supported", 400, "Ad-hoc pool probing is unavailable");
666
+ throw new ConsoleDomainError("capability_unsupported", 400, "Ad-hoc pool probing is unavailable");
660
667
  }
661
668
  const probe = config.store.probeAdHoc.bind(config.store);
662
669
  const results: PoolBatchProbeResult[] = new Array(requests.length);
@@ -700,7 +707,7 @@ export function createNetworkPoolOperations(config: NetworkPoolConfig) {
700
707
  ): Promise<NetworkPoolResponse & { relayUrl: string }> {
701
708
  const a = requireTenantScope(access, "dashboard:write");
702
709
  if (!config.relayFetch)
703
- throw new ConsoleDomainError("not_supported", 503, "Relay deployment is unavailable");
710
+ throw new ConsoleDomainError("capability_unsupported", 503, "Relay deployment is unavailable");
704
711
  if (!isRelayTarget(request.target))
705
712
  throw new ConsoleDomainError("invalid_request", 422, `Unsupported relay target: ${String(request.target)}`);
706
713
  const result = await deployRelayWorker(config.relayFetch, request);
@@ -15,8 +15,16 @@
15
15
  * the address works (`allowed`, `throttled`) or says nothing about it
16
16
  * (`unreachable`), and treating "says nothing" as "refused" would drop a usable
17
17
  * pool for good.
18
+ *
19
+ * The 403-free-tier and 429 verdicts are the probe's reading of the same facts
20
+ * `classifyEgressFailure` (in `network/rate-limit-verdict`) reads on the
21
+ * dispatch path — for the probe they arrive as an HTTP status + body instead of
22
+ * a `GatewayError`, so the shared free-tier regex is borrowed from there to
23
+ * keep the two classifiers' boundaries honest.
18
24
  */
19
25
 
26
+ import { FREE_TIER_REFUSAL } from "../../../network/rate-limit-verdict";
27
+
20
28
  export type ProbeVerdict = "allowed" | "blocked" | "throttled" | "unreachable";
21
29
 
22
30
  export interface ProbeAnswer {
@@ -25,9 +33,6 @@ export interface ProbeAnswer {
25
33
  readonly reason?: string;
26
34
  }
27
35
 
28
- /** The upstream's own wording for "this address is retired". */
29
- const FREE_TIER_REFUSAL = /freetier|free tier can only be used/i;
30
-
31
36
  /**
32
37
  * Classifies one upstream response.
33
38
  *
@@ -151,7 +151,7 @@ async function deployCloudflare(
151
151
  request: RelayDeployRequest,
152
152
  ): Promise<RelayDeployResult> {
153
153
  const accountId = request.accountId?.trim();
154
- if (!accountId) throw new GatewayError("invalid_request", 400, "Cloudflare account id is required");
154
+ if (!accountId) throw new GatewayError("invalid_request", 400, "Cloudflare account id is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
155
155
  const projectName = request.projectName?.trim() || generateName("relay");
156
156
  const scriptUrl = `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(accountId)}/workers/scripts/${encodeURIComponent(projectName)}`;
157
157
  const form = new FormData();
@@ -174,7 +174,7 @@ async function deployCloudflare(
174
174
  body: form,
175
175
  });
176
176
  if (!upload.ok)
177
- throw new GatewayError("platform_unavailable", upload.status, `Cloudflare deploy failed: ${await readError(upload)}`);
177
+ throw new GatewayError("platform_unavailable", upload.status, `Cloudflare deploy failed (request {requestId}): ${await readError(upload)}. Flip the switch back on when ready 🔌`);
178
178
  // Enable the workers.dev route; a failure here is non-fatal because the
179
179
  // subdomain lookup below decides the final URL and reports its own error.
180
180
  await fetchImpl(`${scriptUrl}/subdomain`, {
@@ -190,7 +190,7 @@ async function deployCloudflare(
190
190
  throw new GatewayError(
191
191
  "platform_unavailable",
192
192
  subdomainRes.status,
193
- `Worker uploaded but its workers.dev subdomain could not be read: ${await readError(subdomainRes)}`,
193
+ `Worker uploaded but its workers.dev subdomain could not be read (request {requestId}): ${await readError(subdomainRes)}. Flip the switch back on when ready 🔌`,
194
194
  );
195
195
  const subdomainData = (await subdomainRes.json().catch(() => ({}))) as {
196
196
  result?: { subdomain?: string };
@@ -200,7 +200,7 @@ async function deployCloudflare(
200
200
  throw new GatewayError(
201
201
  "platform_unavailable",
202
202
  502,
203
- "Worker uploaded but no workers.dev subdomain is configured for this Cloudflare account.",
203
+ "Worker uploaded but no workers.dev subdomain is configured for this Cloudflare account (request {requestId}). Flip the switch back on when ready 🔌",
204
204
  );
205
205
  return { target: "cloudflare", projectName, relayUrl: `https://${projectName}.${subdomain}.workers.dev` };
206
206
  }
@@ -251,7 +251,7 @@ async function deployVercel(
251
251
  }),
252
252
  });
253
253
  if (!deployRes.ok)
254
- throw new GatewayError("platform_unavailable", deployRes.status, `Vercel deploy failed: ${await readError(deployRes)}`);
254
+ throw new GatewayError("platform_unavailable", deployRes.status, `Vercel deploy failed (request {requestId}): ${await readError(deployRes)}. Flip the switch back on when ready 🔌`);
255
255
  // Pin the region again after the deploy created the project: the PATCH above
256
256
  // is a no-op on first deploy (the project does not exist yet), and a redeploy
257
257
  // is what Vercel requires for a region change to take effect anyway. The SSO
@@ -280,7 +280,7 @@ async function deployVercel(
280
280
  }).catch(() => undefined);
281
281
  const deployment = (await deployRes.json().catch(() => ({}))) as { url?: string };
282
282
  if (!deployment.url)
283
- throw new GatewayError("platform_unavailable", 502, "Vercel deploy returned no URL");
283
+ throw new GatewayError("platform_unavailable", 502, "Vercel deploy returned no URL (request {requestId}). Flip the switch back on when ready 🔌");
284
284
  return {
285
285
  target: "vercel",
286
286
  projectName,
@@ -300,11 +300,11 @@ async function deployDeno(
300
300
  body: JSON.stringify({ project: projectName, entryPoint: "main.ts", files: { "main.ts": RELAY_DENO_SOURCE } }),
301
301
  });
302
302
  if (!deployRes.ok)
303
- throw new GatewayError("platform_unavailable", deployRes.status, `Deno deploy failed: ${await readError(deployRes)}`);
303
+ throw new GatewayError("platform_unavailable", deployRes.status, `Deno deploy failed (request {requestId}): ${await readError(deployRes)}. Flip the switch back on when ready 🔌`);
304
304
  const deployment = (await deployRes.json().catch(() => ({}))) as { url?: string; domains?: string[] };
305
305
  const url = deployment.url ?? deployment.domains?.[0];
306
306
  if (!url)
307
- throw new GatewayError("platform_unavailable", 502, "Deno deploy returned no URL");
307
+ throw new GatewayError("platform_unavailable", 502, "Deno deploy returned no URL (request {requestId}). Flip the switch back on when ready 🔌");
308
308
  return {
309
309
  target: "deno",
310
310
  projectName,
@@ -333,6 +333,6 @@ export async function deployRelay(
333
333
  request: RelayDeployRequest,
334
334
  ): Promise<RelayDeployResult> {
335
335
  if (!request.token.trim())
336
- throw new GatewayError("invalid_request", 400, `${request.target} API token is required`);
336
+ throw new GatewayError("invalid_request", 400, `${request.target} API token is required (request {requestId}). Wrong key, right door — check the credential 🔑`);
337
337
  return DEPLOYERS[request.target](fetchImpl, request);
338
338
  }
@@ -1,5 +1,5 @@
1
1
  // Network-pool control-plane HTTP routes. The only module that touches Elysia.
2
- import { Elysia, t } from "elysia";
2
+ import { Elysia, t, type AnyElysia } from "elysia";
3
3
  import { consoleErrorHandler } from "../../shared/errors";
4
4
  import { literalUnion } from "../../shared/elysia-schema";
5
5
  import { RELAY_TARGETS, type RelayDeployRequest } from "./relay-deploy";
@@ -56,7 +56,7 @@ const importPoolsBody = t.Object({
56
56
  family: t.Optional(t.Union([t.String(), t.Null()])),
57
57
  labelPrefix: t.Optional(t.String()),
58
58
  });
59
- export function createNetworkPoolRoutes(config: NetworkPoolConfig): Elysia {
59
+ export function createNetworkPoolRoutes(config: NetworkPoolConfig): AnyElysia {
60
60
  const factory = createNetworkPoolOperations(config);
61
61
  return new Elysia({ prefix: "/network/pools" })
62
62
  .error(consoleErrorHandler("Network pool operation failed"))
@@ -207,5 +207,5 @@ export function createNetworkPoolRoutes(config: NetworkPoolConfig): Elysia {
207
207
  body as Partial<PoolStrategySetting>,
208
208
  );
209
209
  },
210
- ) as unknown as Elysia;
210
+ );
211
211
  }
@@ -1,7 +1,9 @@
1
1
  // Drizzle-backed console persistence for network pools.
2
+ import { randomUUID } from "node:crypto";
2
3
  import { and, asc, eq, inArray, sql } from "drizzle-orm";
3
4
  import type { CartethyiaDatabase } from "../../../persistence/db-handle";
4
- import { networkPools, poolRoutingSettings } from "../../../persistence/schema";
5
+ import { networkPools, poolRoutingSettings, providerPoolAccounts } from "../../../persistence/schema";
6
+ import { providerRateLimitIsIpScoped } from "../../../providers/provider-metadata";
5
7
  import {
6
8
  DEFAULT_POOL_STRATEGY,
7
9
  type CreateNetworkPoolRequest,
@@ -444,6 +446,24 @@ export class DrizzleNetworkPoolStore implements NetworkPoolStore {
444
446
  ...(record.family !== undefined ? { family: record.family } : {}),
445
447
  }));
446
448
  await this.db.insert(networkPools).values(values);
449
+
450
+ // Materialize pool-as-account rows for ip-scoped, credential-less
451
+ // providers (OpenCode Free today): the operator sees the egress in the
452
+ // provider's Accounts card, and routing excludes a spent egress like a
453
+ // cooling account. Every other provider gets no row.
454
+ if (providerRateLimitIsIpScoped("opencode")) {
455
+ const now = new Date();
456
+ const poolAccounts = values.map((value) => ({
457
+ id: randomUUID(),
458
+ tenantId: value.tenantId,
459
+ networkPoolId: value.id,
460
+ providerId: "opencode",
461
+ label: `Pool ${value.id.slice(0, 8)}`,
462
+ createdAt: now,
463
+ updatedAt: now,
464
+ }));
465
+ await this.db.insert(providerPoolAccounts).values(poolAccounts);
466
+ }
447
467
  return values.length;
448
468
  }
449
469
 
@@ -1,4 +1,4 @@
1
- import { Elysia } from "elysia";
1
+ import { Elysia, type AnyElysia } from "elysia";
2
2
  import {
3
3
  createRuntimeSettingsOperations,
4
4
  runtimeUpdateBody,
@@ -7,7 +7,7 @@ import {
7
7
  type UpdateRuntimeSettingsRequest,
8
8
  } from "./contracts";
9
9
 
10
- export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig): Elysia {
10
+ export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig): AnyElysia {
11
11
  const factory = createRuntimeSettingsOperations(config);
12
12
  return new Elysia({ prefix: "/settings/runtime" })
13
13
  .get("/", async ({ request, set }) => {
@@ -26,5 +26,5 @@ export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig): Elys
26
26
  } catch (e) {
27
27
  return runtimeSettingsErrorResponse(e, set);
28
28
  }
29
- }) as unknown as Elysia;
29
+ });
30
30
  }
@@ -4,7 +4,7 @@ import { globalOrOwnedBy } from "../../persistence/tenant-scope";
4
4
  // Share URLs reveal policy and allow one child key to be created per canonical
5
5
  // client IP. The child bearer is returned only by the successful issue call.
6
6
 
7
- import { Elysia } from "elysia";
7
+ import { Elysia, type AnyElysia } from "elysia";
8
8
  import { and, eq, sql } from "drizzle-orm";
9
9
  import type { CartethyiaDatabase } from "../../persistence/db-handle";
10
10
  import { apiKeys, models, providers } from "../../persistence/schema";
@@ -34,12 +34,12 @@ const MIN_TOKEN_LENGTH = 20;
34
34
  /**
35
35
  * Cadence of the share stats SSE snapshot.
36
36
  *
37
- * Raised with `SHARE_STATS_CACHE_TTL_SECONDS` (10 s). At 2 s the tick was
38
- * shorter than the rollup's own cache TTL, so every push re-parsed the whole
39
- * retained telemetry window — the interval was the thing making this read
40
- * expensive, not the number of viewers. Short enough to still read as live.
37
+ * Roughly one tenth of the cache TTL (`SHARE_STATS_CACHE_TTL_SECONDS`, 300
38
+ * s), so nearly every tick is served from the cache and the cold-compute
39
+ * path runs at most once per five minutes per family. Short enough to still
40
+ * read as live.
41
41
  */
42
- const SHARE_STATS_STREAM_INTERVAL_MS = 10_000;
42
+ const SHARE_STATS_STREAM_INTERVAL_MS = 30_000;
43
43
 
44
44
  /**
45
45
  * Context and capabilities for one allowed model, sourced from the public
@@ -89,12 +89,12 @@ function json(body: unknown, status = 200): Response {
89
89
  function notFound(refusal: ShareLinkRefusal = "not_found"): Response {
90
90
  const message =
91
91
  refusal === "expired"
92
- ? "This share link has expired"
92
+ ? "This share link has expired (request {requestId}). Time for a fresh one 🌷"
93
93
  : refusal === "revoked"
94
- ? "This share link's key has been revoked"
94
+ ? "This share link's key has been revoked (request {requestId}). Ask for a new one 📩"
95
95
  : refusal === "disabled"
96
- ? "This share link's key is paused"
97
- : "Share link is unavailable";
96
+ ? "This share link's key is paused (request {requestId}). Re-enable it when ready 🌱"
97
+ : "Share link is unavailable (request {requestId}). This link leads nowhere — check it again 🔗";
98
98
  const error = new GatewayError(`link_${refusal}`, 404, message);
99
99
  return json(publicGatewayErrorBody(error), error.status);
100
100
  }
@@ -261,7 +261,7 @@ async function resolveFamilyStats(
261
261
  }
262
262
 
263
263
  /** Creates the public enrollment page and one-time shared-key issuance route. */
264
- export function createShareRouter(options: ShareRouterOptions): Elysia {
264
+ export function createShareRouter(options: ShareRouterOptions): AnyElysia {
265
265
  const { db, shareStore } = options;
266
266
 
267
267
  return new Elysia()
@@ -486,5 +486,5 @@ export function createShareRouter(options: ShareRouterOptions): Elysia {
486
486
  };
487
487
  }),
488
488
  );
489
- }) as unknown as Elysia;
489
+ })
490
490
  }