cartethyia 2.4.5 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (415) hide show
  1. package/.env.example +4 -1
  2. package/README.md +63 -13
  3. package/dist/dashboard/assets/AccountCooldown-ChpdzxRH.js.gz +0 -0
  4. package/dist/dashboard/assets/BannedPage-9igfctfZ.js +1 -0
  5. package/dist/dashboard/assets/BannedPage-9igfctfZ.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolDetailPage-BzGevuql.js → CliToolDetailPage-ZRr2K2sF.js} +1 -1
  7. package/dist/dashboard/assets/CliToolDetailPage-ZRr2K2sF.js.gz +0 -0
  8. package/dist/dashboard/assets/{CliToolsPage-BaRM_7We.js → CliToolsPage-D3xqZbN-.js} +1 -1
  9. package/dist/dashboard/assets/CliToolsPage-D3xqZbN-.js.gz +0 -0
  10. package/dist/dashboard/assets/CombosPage-CVnQPBBz.js +1 -0
  11. package/dist/dashboard/assets/CombosPage-CVnQPBBz.js.gz +0 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js +1 -0
  13. package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js.gz +0 -0
  14. package/dist/dashboard/assets/{CustomizationPage-DqUGcoH0.js → CustomizationPage-CTTpCb25.js} +1 -1
  15. package/dist/dashboard/assets/CustomizationPage-CTTpCb25.js.gz +0 -0
  16. package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js +1 -0
  17. package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js.gz +0 -0
  18. package/dist/dashboard/assets/{LoginPage-DbhTGfhb.js → LoginPage-DHoB3Agn.js} +1 -1
  19. package/dist/dashboard/assets/LoginPage-DHoB3Agn.js.gz +0 -0
  20. package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js +1 -0
  21. package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
  22. package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js +1 -0
  23. package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js +3 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js.gz +0 -0
  26. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +1 -0
  27. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  28. package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js +1 -0
  29. package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js.gz +0 -0
  30. package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js +7 -0
  31. package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js.gz +0 -0
  32. package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js +1 -0
  33. package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js.gz +0 -0
  34. package/dist/dashboard/assets/{SettingsPage-YxRml8AT.js → SettingsPage-CvjZxHE4.js} +1 -1
  35. package/dist/dashboard/assets/SettingsPage-CvjZxHE4.js.gz +0 -0
  36. package/dist/dashboard/assets/SetupPage-D3nza7LH.js +1 -0
  37. package/dist/dashboard/assets/SetupPage-D3nza7LH.js.gz +0 -0
  38. package/dist/dashboard/assets/{SortableList-Bol1ASmI.js → SortableList-Dtt12WT1.js} +1 -1
  39. package/dist/dashboard/assets/SortableList-Dtt12WT1.js.gz +0 -0
  40. package/dist/dashboard/assets/StudioPage-CbK3hXvU.js +31 -0
  41. package/dist/dashboard/assets/StudioPage-CbK3hXvU.js.gz +0 -0
  42. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  43. package/dist/dashboard/assets/UsagePage-BQlWbWg2.js +56 -0
  44. package/dist/dashboard/assets/UsagePage-BQlWbWg2.js.gz +0 -0
  45. package/dist/dashboard/assets/api-keys-BmDbsior.js +1 -0
  46. package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
  47. package/dist/dashboard/assets/app-BbpSarVV.css +2 -0
  48. package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
  49. package/dist/dashboard/assets/{app-BDJWMThu.js → app-C-Y4oZT0.js} +3 -3
  50. package/dist/dashboard/assets/app-C-Y4oZT0.js.gz +0 -0
  51. package/dist/dashboard/assets/arrow-left-CpBxkoxI.js.gz +0 -0
  52. package/dist/dashboard/assets/arrow-right-Dl5yKnp8.js.gz +0 -0
  53. package/dist/dashboard/assets/assert-BHfaei-X.js +1 -0
  54. package/dist/dashboard/assets/assert-BHfaei-X.js.gz +0 -0
  55. package/dist/dashboard/assets/backup-file-gdKIlg8U.js.gz +0 -0
  56. package/dist/dashboard/assets/{badge-BwmwEOgA.js → badge-DpXT4Ixt.js} +1 -1
  57. package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
  58. package/dist/dashboard/assets/{banner-BKBfKTqn.js → banner-CSAAWLJv.js} +1 -1
  59. package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
  60. package/dist/dashboard/assets/brain-Cwed_72z.js.gz +0 -0
  61. package/dist/dashboard/assets/{card-BTdbRFFg.js → card-DEhRXXw4.js} +1 -1
  62. package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
  63. package/dist/dashboard/assets/check-D69FzdvC.js.gz +0 -0
  64. package/dist/dashboard/assets/chevron-right-BXIy5l3h.js.gz +0 -0
  65. package/dist/dashboard/assets/circle-alert-CntYHV5v.js.gz +0 -0
  66. package/dist/dashboard/assets/circle-check-BU3v_mFW.js.gz +0 -0
  67. package/dist/dashboard/assets/cli-tools-B0wBq-RL.js +1 -0
  68. package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
  69. package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js +1 -0
  70. package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
  71. package/dist/dashboard/assets/cog-CdWhsajs.js.gz +0 -0
  72. package/dist/dashboard/assets/copy-ngmQ9zcZ.js.gz +0 -0
  73. package/dist/dashboard/assets/{dialog-iocKA3jr.js → dialog-BvQeBMfP.js} +1 -1
  74. package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
  75. package/dist/dashboard/assets/dialog-actions-B1axpEg7.js.gz +0 -0
  76. package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
  77. package/dist/dashboard/assets/download-dRFo5_Wu.js.gz +0 -0
  78. package/dist/dashboard/assets/{drawer-CDKOTZdY.js → drawer-CMwPXwQB.js} +1 -1
  79. package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
  80. package/dist/dashboard/assets/entry-D4O2229X.js +1 -0
  81. package/dist/dashboard/assets/entry-D4O2229X.js.gz +0 -0
  82. package/dist/dashboard/assets/entry-DKvr5Q9K.js +1 -0
  83. package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
  84. package/dist/dashboard/assets/entry-FQCejoDY.js +4 -0
  85. package/dist/dashboard/assets/entry-FQCejoDY.js.gz +0 -0
  86. package/dist/dashboard/assets/eye-C_hawfH4.js.gz +0 -0
  87. package/dist/dashboard/assets/eye-off-Bk5g2luF.js.gz +0 -0
  88. package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
  89. package/dist/dashboard/assets/gauge-BbIMy7MB.js.gz +0 -0
  90. package/dist/dashboard/assets/github-badge-zliLvBwr.js +1 -0
  91. package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
  92. package/dist/dashboard/assets/image-BPaouViG.js.gz +0 -0
  93. package/dist/dashboard/assets/{index-C4hXfAMs.js → index-CXm3FIyt.js} +2 -2
  94. package/dist/dashboard/assets/index-CXm3FIyt.js.gz +0 -0
  95. package/dist/dashboard/assets/{inline-DCejSLlA.js → inline-D1IlZZ8H.js} +1 -1
  96. package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
  97. package/dist/dashboard/assets/{input-Bn4_kNav.js → input-BpVZdrbc.js} +1 -1
  98. package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
  99. package/dist/dashboard/assets/jsx-runtime-CXUMngI_.js.gz +0 -0
  100. package/dist/dashboard/assets/key-round-UkavsGxp.js.gz +0 -0
  101. package/dist/dashboard/assets/layers-2-glBbjl.js.gz +0 -0
  102. package/dist/dashboard/assets/{layout-Bv-NxqZ5.js → layout-BioLW7gN.js} +1 -1
  103. package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
  104. package/dist/dashboard/assets/{list-row-BLA0sldD.js → list-row-CwVcGAIc.js} +1 -1
  105. package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
  106. package/dist/dashboard/assets/{live-YlifPYox.js → live-yfEJFF9T.js} +1 -1
  107. package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
  108. package/dist/dashboard/assets/log-in-BmRjy60c.js.gz +0 -0
  109. package/dist/dashboard/assets/message-circle-CCxGwRvv.js.gz +0 -0
  110. package/dist/dashboard/assets/model-bans-CT-TxSNX.js +1 -0
  111. package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
  112. package/dist/dashboard/assets/mutation-BkoLM5Ba.js +1 -0
  113. package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
  114. package/dist/dashboard/assets/numbers-CFL4_Dqc.js.gz +0 -0
  115. package/dist/dashboard/assets/pencil-768ysRAr.js.gz +0 -0
  116. package/dist/dashboard/assets/plus-DIdX_g-x.js.gz +0 -0
  117. package/dist/dashboard/assets/power-off-Yjw9GMJh.js +1 -0
  118. package/dist/dashboard/assets/power-off-Yjw9GMJh.js.gz +0 -0
  119. package/dist/dashboard/assets/privacy-mode-GM8bej85.js +1 -0
  120. package/dist/dashboard/assets/privacy-mode-GM8bej85.js.gz +0 -0
  121. package/dist/dashboard/assets/providers/agentrouter.svg.gz +0 -0
  122. package/dist/dashboard/assets/providers/aihubmix.svg.gz +0 -0
  123. package/dist/dashboard/assets/providers/alibaba-coding-plan.svg.gz +0 -0
  124. package/dist/dashboard/assets/providers/alibaba.svg.gz +0 -0
  125. package/dist/dashboard/assets/providers/antigravity.svg.gz +0 -0
  126. package/dist/dashboard/assets/providers/bai.svg.gz +0 -0
  127. package/dist/dashboard/assets/providers/blackbox.svg.gz +0 -0
  128. package/dist/dashboard/assets/providers/claude-code.svg.gz +0 -0
  129. package/dist/dashboard/assets/providers/cursor.webp +0 -0
  130. package/dist/dashboard/assets/providers/exa.svg.gz +0 -0
  131. package/dist/dashboard/assets/providers/gmi-cloud.svg.gz +0 -0
  132. package/dist/dashboard/assets/providers/inferhub.svg.gz +0 -0
  133. package/dist/dashboard/assets/providers/muse.svg.gz +0 -0
  134. package/dist/dashboard/assets/providers/nous-research.svg.gz +0 -0
  135. package/dist/dashboard/assets/providers/openai-light.svg.gz +0 -0
  136. package/dist/dashboard/assets/providers/parallel.svg.gz +0 -0
  137. package/dist/dashboard/assets/providers/tokenharbor.svg.gz +0 -0
  138. package/dist/dashboard/assets/providers/trae.webp +0 -0
  139. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  140. package/dist/dashboard/assets/{query-keys-ROf-g1Wi.js → query-keys-D7wX2vM5.js} +1 -1
  141. package/dist/dashboard/assets/query-keys-D7wX2vM5.js.gz +0 -0
  142. package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
  143. package/dist/dashboard/assets/refresh-cw-BqJFk4sU.js.gz +0 -0
  144. package/dist/dashboard/assets/rotate-ccw-CufcqITi.js.gz +0 -0
  145. package/dist/dashboard/assets/route-CNSNj9Zm.js.gz +0 -0
  146. package/dist/dashboard/assets/routing-Cs6jl49L.js +1 -0
  147. package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
  148. package/dist/dashboard/assets/scaling-nWE4Cx8f.js.gz +0 -0
  149. package/dist/dashboard/assets/search-JOKus9rb.js.gz +0 -0
  150. package/dist/dashboard/assets/select-C0Rer3gF.js +41 -0
  151. package/dist/dashboard/assets/select-C0Rer3gF.js.gz +0 -0
  152. package/dist/dashboard/assets/settings-CfcTcOyU.js +1 -0
  153. package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
  154. package/dist/dashboard/assets/shield-check-DVS0Foqh.js.gz +0 -0
  155. package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
  156. package/dist/dashboard/assets/{stack-C1kTcOmP.js → stack-C9_uUjaA.js} +1 -1
  157. package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
  158. package/dist/dashboard/assets/{state-DHKdg-pm.js → state-DA0B95UQ.js} +1 -1
  159. package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
  160. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  161. package/dist/dashboard/assets/{switch-DIDeAiz9.js → switch-Cittxy-x.js} +1 -1
  162. package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
  163. package/dist/dashboard/assets/terminal-Bd4ee8TA.js +1 -0
  164. package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
  165. package/dist/dashboard/assets/toast-CNt_jzO4.js +2 -0
  166. package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
  167. package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
  168. package/dist/dashboard/assets/trash-Cppn5c3R.js.gz +0 -0
  169. package/dist/dashboard/assets/use-clipboard-B0Sy03fE.js.gz +0 -0
  170. package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
  171. package/dist/dashboard/assets/use-reduced-motion-C4B0R6Sz.js.gz +0 -0
  172. package/dist/dashboard/assets/use-timeout-BwcBBRrT.js.gz +0 -0
  173. package/dist/dashboard/assets/{useMutation-DVmwOKU-.js → useMutation-BQKMcJ1H.js} +1 -1
  174. package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
  175. package/dist/dashboard/assets/wrench-B6E1oEuY.js.gz +0 -0
  176. package/dist/dashboard/index.html +1 -1
  177. package/dist/dashboard/index.html.gz +0 -0
  178. package/dist/dashboard/precompressed-assets.json +122 -0
  179. package/migrations/full-postgres/0013_add_providers_tenant_id_index.sql +22 -0
  180. package/migrations/full-postgres/0014_add_provider_accounts_tenant_id_index.sql +21 -0
  181. package/migrations/full-postgres/0015_add_payload_stages.sql +18 -0
  182. package/migrations/full-postgres/0016_add_share_link_slug.sql +20 -0
  183. package/migrations/full-postgres/0017_add_models_pro_only.sql +18 -0
  184. package/migrations/full-postgres/0018_add_accounts_quota_plan.sql +18 -0
  185. package/migrations/full-postgres/0019_add_models_free_tier.sql +15 -0
  186. package/migrations/lite-sqlite3/0000_baseline.sql +4 -2
  187. package/migrations/lite-sqlite3/0013_add_providers_tenant_id_index.sql +4 -0
  188. package/migrations/lite-sqlite3/0014_add_provider_accounts_tenant_id_index.sql +4 -0
  189. package/migrations/lite-sqlite3/0015_add_payload_stages.sql +18 -0
  190. package/migrations/lite-sqlite3/0016_add_share_link_slug.sql +20 -0
  191. package/migrations/lite-sqlite3/0017_add_models_pro_only.sql +18 -0
  192. package/migrations/lite-sqlite3/0018_add_accounts_quota_plan.sql +18 -0
  193. package/migrations/lite-sqlite3/0019_add_models_free_tier.sql +15 -0
  194. package/package.json +26 -30
  195. package/scripts/cli/{carte-launcher.cjs → bin/carte.cjs} +8 -8
  196. package/scripts/cli/{cli-launcher.cjs → bin/cartethyia.cjs} +167 -134
  197. package/scripts/cli/command/admin.ts +65 -0
  198. package/scripts/cli/command/console-menu.ts +246 -0
  199. package/scripts/cli/{doctor.ts → command/doctor.ts} +257 -258
  200. package/scripts/cli/{setup.ts → command/setup.ts} +8 -12
  201. package/scripts/cli/command/start.ts +77 -0
  202. package/scripts/cli/command/tray.ts +165 -0
  203. package/scripts/cli/command/update.ts +23 -0
  204. package/scripts/cli/{runtime-state.ts → gateway/drain-token.ts} +78 -78
  205. package/scripts/cli/gateway/instance-lock.ts +105 -0
  206. package/scripts/cli/gateway/spawn.ts +173 -0
  207. package/scripts/cli/gateway/stop.ts +51 -0
  208. package/scripts/cli/main.ts +94 -0
  209. package/scripts/cli/support/bun.ts +27 -0
  210. package/scripts/cli/support/env.ts +99 -0
  211. package/scripts/cli/support/platform.ts +64 -0
  212. package/scripts/cli/support/services.ts +88 -0
  213. package/scripts/cli/support/startup-signal.ts +23 -0
  214. package/scripts/cli/{version.ts → support/version.ts} +1 -1
  215. package/scripts/cli/tray/hidden-window.vbs +28 -0
  216. package/scripts/cli/{tray.ts → tray/index.ts} +3 -3
  217. package/scripts/cli/{tray.ps1 → tray/windows.ps1} +107 -98
  218. package/scripts/cli/ui/background.ts +110 -0
  219. package/src/boot/app.ts +23 -7
  220. package/src/boot/dependencies.ts +55 -13
  221. package/src/boot/elysia-typebox-bridge.ts +37 -0
  222. package/src/boot/lifecycle.ts +2 -0
  223. package/src/config/cooldowns.ts +138 -138
  224. package/src/config/providers.ts +31 -5
  225. package/src/config/runtime.ts +19 -3
  226. package/src/config/spec.ts +20 -0
  227. package/src/console/api-keys/contracts.ts +414 -391
  228. package/src/console/api-keys/routes.ts +880 -870
  229. package/src/console/auth/account-reset-store.ts +14 -0
  230. package/src/console/auth/account-reset.ts +3 -2
  231. package/src/console/auth/setup-service.ts +6 -3
  232. package/src/console/backup/routes.ts +8 -0
  233. package/src/console/cli-tools/contracts.ts +6 -6
  234. package/src/console/console-router.ts +5 -3
  235. package/src/console/dashboard-assets.ts +81 -11
  236. package/src/console/domain-registration.ts +9 -2
  237. package/src/console/observability/contracts.ts +475 -423
  238. package/src/console/observability/routes.ts +31 -6
  239. package/src/console/observability/store.ts +1015 -912
  240. package/src/console/providers/catalog/contracts.ts +31 -0
  241. package/src/console/providers/catalog/provider-operations.ts +8 -0
  242. package/src/console/providers/catalog/public-model-store.ts +91 -60
  243. package/src/console/providers/catalog/store.ts +1532 -1521
  244. package/src/console/providers/oauth/callback-listener.ts +18 -3
  245. package/src/console/providers/oauth/routes.ts +38 -15
  246. package/src/console/quota/account-quota-tenant.ts +9 -6
  247. package/src/console/quota/refresh.ts +19 -0
  248. package/src/console/response-compression.ts +69 -2
  249. package/src/console/routing/pools/probe-verdict.ts +72 -0
  250. package/src/console/share/share-router.ts +7 -4
  251. package/src/console/share/share-stats.ts +12 -5
  252. package/src/console/shared/read-through-cache.ts +133 -7
  253. package/src/console/updates/repository-updates.ts +39 -34
  254. package/src/console/updates/routes.ts +162 -4
  255. package/src/main.ts +3 -0
  256. package/src/network/http2-bidi.ts +85 -0
  257. package/src/network/outbound-fetch.ts +87 -24
  258. package/src/network/pool/agent.ts +478 -460
  259. package/src/network/pool/resolver.ts +1104 -544
  260. package/src/network/pool-health-machine.ts +12 -5
  261. package/src/observability/event-log.ts +29 -8
  262. package/src/observability/metrics.ts +40 -13
  263. package/src/observability/payload-capture.ts +344 -324
  264. package/src/observability/runtime-metrics.ts +7 -0
  265. package/src/persistence/db-pg.ts +9 -3
  266. package/src/persistence/db-sqlite.ts +497 -495
  267. package/src/persistence/health-event-flush.ts +137 -0
  268. package/src/persistence/migrate.ts +181 -129
  269. package/src/persistence/schema.ts +1360 -1257
  270. package/src/persistence/settings-revision.ts +25 -0
  271. package/src/persistence/shared-cache.ts +217 -0
  272. package/src/persistence/stores/share-store.ts +556 -528
  273. package/src/persistence/usage-flush.ts +61 -1
  274. package/src/protocol/canonical-model.ts +23 -1
  275. package/src/protocol/response/chat.ts +143 -10
  276. package/src/protocol/response/codex.ts +9 -75
  277. package/src/protocol/response/omp-tool-call-markup.ts +221 -0
  278. package/src/protocol/tool-identity.ts +43 -9
  279. package/src/providers/authentication/jwt-validator.ts +130 -187
  280. package/src/providers/authentication/oauth-flow-store.ts +16 -9
  281. package/src/providers/default-registry.ts +177 -1
  282. package/src/providers/discovery/catalog/catalog-format.ts +20 -0
  283. package/src/providers/discovery/models-dev-catalog.ts +39 -22
  284. package/src/providers/discovery/openai-model-discovery.ts +39 -3
  285. package/src/providers/discovery/openrouter-free-roster.ts +98 -0
  286. package/src/providers/discovery/probe-wire.ts +29 -5
  287. package/src/providers/discovery/probing-service.ts +1022 -1001
  288. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +42 -2
  289. package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
  290. package/src/providers/integrations/buddy/buddy-cli-headers.ts +88 -0
  291. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +22 -5
  292. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +33 -1
  293. package/src/providers/integrations/buddy/buddy-identity.ts +46 -0
  294. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +37 -1
  295. package/src/providers/integrations/buddy/buddy-usage-store.ts +40 -5
  296. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +4 -0
  297. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -175
  298. package/src/providers/integrations/buddy/codebuddy/shared.ts +115 -73
  299. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +5 -2
  300. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
  301. package/src/providers/integrations/buddy/workbuddy/quota.ts +172 -159
  302. package/src/providers/integrations/buddy/workbuddy/shared.ts +200 -171
  303. package/src/providers/integrations/claude/claude.ts +606 -582
  304. package/src/providers/integrations/cline/cline-quota.ts +18 -16
  305. package/src/providers/integrations/cline/cline.ts +539 -468
  306. package/src/providers/integrations/codex/codex.ts +5 -8
  307. package/src/providers/integrations/commandcode/commandcode.ts +675 -541
  308. package/src/providers/integrations/commandcode/discovery.ts +116 -0
  309. package/src/providers/integrations/commandcode/quota.ts +160 -0
  310. package/src/providers/integrations/cursor/cursor-auth.ts +418 -0
  311. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -0
  312. package/src/providers/integrations/cursor/cursor-http2.ts +300 -0
  313. package/src/providers/integrations/cursor/cursor-identity.ts +92 -0
  314. package/src/providers/integrations/cursor/cursor-request.ts +471 -0
  315. package/src/providers/integrations/cursor/cursor-session-manager.ts +281 -0
  316. package/src/providers/integrations/cursor/cursor-shared.ts +108 -0
  317. package/src/providers/integrations/cursor/cursor.ts +960 -0
  318. package/src/providers/integrations/cursor/protocol/framing.ts +129 -0
  319. package/src/providers/integrations/cursor/protocol/messages.ts +2355 -0
  320. package/src/providers/integrations/cursor/protocol/struct.ts +305 -0
  321. package/src/providers/integrations/factory/factory-oauth.ts +7 -11
  322. package/src/providers/integrations/github/github.ts +4 -4
  323. package/src/providers/integrations/hermes.ts +79 -8
  324. package/src/providers/integrations/kilo/kilo-discovery.ts +10 -1
  325. package/src/providers/integrations/kilo/kilo.ts +153 -146
  326. package/src/providers/integrations/opencode/opencode-go-quota.ts +130 -0
  327. package/src/providers/integrations/opencode/provider.ts +532 -499
  328. package/src/providers/integrations/openrouter/provider.ts +139 -281
  329. package/src/providers/integrations/qoder/qoder-catalog.ts +450 -0
  330. package/src/providers/integrations/qoder/qoder-oauth.ts +310 -263
  331. package/src/providers/integrations/qoder/qoder-realm.ts +106 -0
  332. package/src/providers/integrations/qoder/qoder-virtual.ts +77 -0
  333. package/src/providers/integrations/qoder/qoder.ts +865 -730
  334. package/src/providers/integrations/shared/install-id.ts +29 -2
  335. package/src/providers/integrations/trae/trae-catalog.ts +190 -0
  336. package/src/providers/integrations/trae/trae-headers.ts +149 -0
  337. package/src/providers/integrations/trae/trae-oauth.ts +773 -0
  338. package/src/providers/integrations/trae/trae-protocol.ts +400 -0
  339. package/src/providers/integrations/trae/trae-realm.ts +220 -0
  340. package/src/providers/integrations/trae/trae.ts +458 -0
  341. package/src/providers/operations/account-health-service.ts +107 -10
  342. package/src/providers/operations/client-versions.ts +143 -6
  343. package/src/providers/operations/model-catalog-cache.ts +18 -3
  344. package/src/providers/operations/provider-catalog-seeder.ts +95 -86
  345. package/src/providers/operations/provider-catalog-service.ts +76 -0
  346. package/src/providers/operations/provider-credential-service.ts +46 -7
  347. package/src/providers/provider-contracts.ts +76 -0
  348. package/src/providers/provider-metadata.ts +78 -6
  349. package/src/providers/provider-model-definition.ts +373 -349
  350. package/src/security/api-key-auth.ts +126 -8
  351. package/src/transport/contracts.ts +3 -0
  352. package/src/transport/dispatch/attempt-finalize.ts +39 -2
  353. package/src/transport/dispatch/attempt-loop.ts +530 -513
  354. package/src/transport/dispatch/empty-stream.ts +17 -26
  355. package/src/transport/dispatch/generation.ts +88 -21
  356. package/src/transport/dispatch/leases.ts +99 -2
  357. package/src/transport/dispatch/proxy-request.ts +60 -3
  358. package/src/transport/dispatch/streaming/finalize.ts +16 -1
  359. package/src/transport/dispatch/streaming/stream.ts +22 -2
  360. package/src/transport/dispatch/upstream.ts +19 -1
  361. package/src/transport/failure-policy.ts +20 -0
  362. package/src/transport/guards/auth-guard.ts +26 -1
  363. package/src/transport/guards/body-policy.ts +5 -4
  364. package/src/transport/request/preparer.ts +103 -0
  365. package/src/transport/request/state.ts +14 -0
  366. package/src/transport/routing/plan-gate.ts +45 -0
  367. package/src/transport/routing/route-catalog.ts +799 -738
  368. package/src/transport/routing/route-model.ts +52 -1
  369. package/src/transport/routing/router.ts +51 -0
  370. package/src/transport/surface/chat/encode.ts +18 -31
  371. package/src/transport/surface/messages/stream.ts +3 -2
  372. package/src/transport/translation/tool-repair.ts +37 -16
  373. package/src/workers/account-identity-sweep.ts +14 -12
  374. package/src/workers/daily-checkin.ts +62 -0
  375. package/src/workers/quota-refresh-sweep.ts +27 -2
  376. package/dist/dashboard/assets/BannedPage-DUrXpp-X.js +0 -1
  377. package/dist/dashboard/assets/CombosPage-B7ETkfVH.js +0 -1
  378. package/dist/dashboard/assets/ConsoleLogPage-AisQzCI1.js +0 -1
  379. package/dist/dashboard/assets/HealthEventsModal-BNflDm_f.js +0 -1
  380. package/dist/dashboard/assets/ModelPicker-CAiPR5Vd.js +0 -1
  381. package/dist/dashboard/assets/OverviewPage-C3NIzsDx.js +0 -1
  382. package/dist/dashboard/assets/ProviderDetailPage-BK8i5B5X.js +0 -3
  383. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
  384. package/dist/dashboard/assets/ProvidersPage-CvCVgm1e.js +0 -1
  385. package/dist/dashboard/assets/ProxyPage-DjabkuzP.js +0 -7
  386. package/dist/dashboard/assets/QuotaPage-Dxe0B0iu.js +0 -1
  387. package/dist/dashboard/assets/SetupPage-BaD4P_jW.js +0 -1
  388. package/dist/dashboard/assets/StudioPage-CLP4le3U.js +0 -31
  389. package/dist/dashboard/assets/UsagePage-Djes-Epy.js +0 -56
  390. package/dist/dashboard/assets/api-keys-CjIGQhe7.js +0 -1
  391. package/dist/dashboard/assets/app-Cjgo4-Lf.css +0 -2
  392. package/dist/dashboard/assets/assert-Bwh6DJTK.js +0 -1
  393. package/dist/dashboard/assets/cli-tools-DccaOfhX.js +0 -1
  394. package/dist/dashboard/assets/clipboard-button-DQmMikNQ.js +0 -1
  395. package/dist/dashboard/assets/entry-Cpi0rph1.js +0 -4
  396. package/dist/dashboard/assets/entry-Dm3PpDCO.js +0 -1
  397. package/dist/dashboard/assets/entry-keNDZ_Wz.js +0 -1
  398. package/dist/dashboard/assets/github-badge-BuAJr8oU.js +0 -1
  399. package/dist/dashboard/assets/model-bans-CVvHZ-qz.js +0 -1
  400. package/dist/dashboard/assets/mutation-BIizFZav.js +0 -1
  401. package/dist/dashboard/assets/privacy-mode-CQjbZ3xk.js +0 -1
  402. package/dist/dashboard/assets/routing-jntw9-Ui.js +0 -1
  403. package/dist/dashboard/assets/select-CGUuTQmj.js +0 -41
  404. package/dist/dashboard/assets/settings-BAS6QbmJ.js +0 -1
  405. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
  406. package/dist/dashboard/assets/terminal-C9mZ4Ugm.js +0 -1
  407. package/dist/dashboard/assets/toast-DKAdP2mo.js +0 -2
  408. package/scripts/cli/cli.ts +0 -771
  409. package/scripts/operations/probe-egress.ts +0 -335
  410. package/scripts/operations/restart.ts +0 -120
  411. package/scripts/shared/env.ts +0 -269
  412. /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.ico +0 -0
  413. /package/{tray-assets → scripts/cli/tray/assets}/cartethyia.png +0 -0
  414. /package/scripts/cli/{menu.ts → ui/prompt.ts} +0 -0
  415. /package/scripts/cli/{wordmark.ts → ui/wordmark.ts} +0 -0
@@ -0,0 +1,110 @@
1
+ import { spawn } from "node:child_process";
2
+ import { readFileSync, unlinkSync } from "node:fs";
3
+ import { PACKAGE_ROOT, resolvePackagePath } from "../../../src/config/package-paths";
4
+
5
+ /** Opens a URL in the operator's default browser, ignoring a missing handler. */
6
+ export function openUrl(url: string): Promise<void> {
7
+ const command = process.platform === "win32"
8
+ ? "explorer.exe"
9
+ : process.platform === "darwin"
10
+ ? "open"
11
+ : "xdg-open";
12
+ const child = Bun.spawn([command, url], {
13
+ stdin: "ignore",
14
+ stdout: "ignore",
15
+ stderr: "ignore",
16
+ });
17
+ return child.exited.then(() => undefined);
18
+ }
19
+
20
+ /**
21
+ * Starts a detached process and resolves once it reports readiness.
22
+ *
23
+ * The child owns no stdio, so a signal file is the only channel that survives
24
+ * the parent. Without it the caller prints "running in the background" for a
25
+ * child that may already have died — which is exactly what a port clash looks
26
+ * like, because the child's own startup probe fails while the parent's gateway
27
+ * still holds the port.
28
+ *
29
+ * Resolves with the URL the child reported. Rejects with the child's message
30
+ * when it reports failure, or with a timeout if it never reports at all.
31
+ */
32
+ export function launchBackgroundWithStatus(
33
+ args: readonly string[],
34
+ options: { readonly signalPath: string; readonly timeoutMs?: number } = { signalPath: "" },
35
+ ): Promise<string> {
36
+ return new Promise<string>((resolve, reject) => {
37
+ const signalPath = options.signalPath;
38
+ const deadline = Date.now() + (options.timeoutMs ?? 60_000);
39
+ let settled = false;
40
+ const finish = (error: Error | null, url?: string): void => {
41
+ if (settled) return;
42
+ settled = true;
43
+ clearInterval(timer);
44
+ try {
45
+ unlinkSync(signalPath);
46
+ } catch {}
47
+ if (error) reject(error);
48
+ else resolve(url ?? "");
49
+ };
50
+
51
+ const env = { ...process.env, CARTETHYIA_TRAY_STARTUP_SIGNAL: signalPath };
52
+ spawnBackground(args, env);
53
+
54
+ const timer = setInterval(() => {
55
+ if (settled) return;
56
+ let raw: string;
57
+ try {
58
+ raw = readFileSync(signalPath, "utf8");
59
+ } catch {
60
+ if (Date.now() >= deadline) finish(new Error("the background process did not report startup within the deadline"));
61
+ return;
62
+ }
63
+ try {
64
+ const signal = JSON.parse(raw) as { status?: string; url?: string; message?: string };
65
+ if (signal.status === "ready" && typeof signal.url === "string") finish(null, signal.url);
66
+ else if (signal.status === "failed") finish(new Error(signal.message ?? "the background process failed to start"));
67
+ } catch {
68
+ // A partially written file is read again on the next tick.
69
+ }
70
+ }, 150);
71
+ timer.unref?.();
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Starts a Bun process that owns no console window.
77
+ *
78
+ * On Windows, spawning the child directly is not enough: a console-subsystem
79
+ * child of a window-less parent allocates a new console, and when the parent is
80
+ * inside Windows Terminal the tab is already on screen before any flag can take
81
+ * effect — measured: `detached: true` plus `windowsHide: true` still produced a
82
+ * Windows Terminal window titled with the bun path. `wscript.exe` is a
83
+ * GUI-subsystem host, so it never allocates a console, and a child launched
84
+ * from it with window style 0 inherits that. The VBS is that launcher.
85
+ *
86
+ * Elsewhere `detached` is enough and the helper is not used.
87
+ */
88
+ export function launchBackground(args: readonly string[]): void {
89
+ spawnBackground(args, process.env);
90
+ }
91
+
92
+ function spawnBackground(args: readonly string[], env: NodeJS.ProcessEnv): void {
93
+ if (process.platform !== "win32") {
94
+ const child = spawn(process.execPath, [...args], {
95
+ cwd: PACKAGE_ROOT,
96
+ detached: true,
97
+ stdio: "ignore",
98
+ env,
99
+ });
100
+ child.unref();
101
+ return;
102
+ }
103
+ const launcher = resolvePackagePath("scripts", "cli", "tray", "hidden-window.vbs");
104
+ const child = spawn(
105
+ "wscript.exe",
106
+ ["//nologo", launcher, process.execPath, ...args],
107
+ { cwd: PACKAGE_ROOT, detached: true, stdio: "ignore", windowsHide: true, env },
108
+ );
109
+ child.unref();
110
+ }
package/src/boot/app.ts CHANGED
@@ -34,14 +34,21 @@ import {
34
34
  import { createResponsesCompactHandler } from "../transport/dispatch/responses-compact";
35
35
  import { createSystemoneHandler } from "../transport/dispatch/systemone";
36
36
  import { createWebsearchHandler } from "../transport/dispatch/websearch";
37
- import { createImageGenerationHandler, createVideoGenerationHandler } from "../transport/dispatch/generation";
37
+ import {
38
+ createAudioSpeechHandler,
39
+ createAudioTranscriptionHandler,
40
+ createEmbeddingHandler,
41
+ createImageGenerationHandler,
42
+ createVideoGenerationHandler,
43
+ } from "../transport/dispatch/generation";
38
44
  import { createTokenCountHandler } from "../transport/dispatch/token-count";
39
45
 
40
46
  import { createTransportPipeline } from "../transport/guards/pipeline";
41
47
  import { createDrainHandler, isLoopbackPeer, tokenMatches } from "../transport/drain-endpoint";
42
48
  import { createConsoleAccountResetHandler } from "../console/auth/account-reset";
49
+ import { resetConsoleAuthState } from "../console/auth/account-reset-store";
43
50
  import { createBackupExportHandler } from "../console/backup/export-endpoint";
44
- import { consoleUsers, tenants } from "../persistence/schema";
51
+ import { tenants } from "../persistence/schema";
45
52
  import { PublicModelCatalogStore, type AllowedModelEntry } from "../console/providers/catalog/public-model-store";
46
53
  import type { IpAbuseProtectionService } from "../security/abuse";
47
54
  import type { ReadinessCheckResult } from "../persistence/readiness";
@@ -196,7 +203,9 @@ export function createGatewayApp(deps: GatewayAppDeps) {
196
203
  const peerAddresses = new WeakMap<Request, string>();
197
204
  const serveDashboard = async ({ request }: { request: Request }): Promise<Response> => {
198
205
  const pathname = fastPathname(request.url);
199
- const result = await staticHandler(pathname);
206
+ // The client's `accept-encoding` is what decides whether a precompressed
207
+ // `.gz` sibling can be served instead of the plain asset.
208
+ const result = await staticHandler(pathname, request.headers.get("accept-encoding"));
200
209
  return new Response(result.body ? (result.body as unknown as BodyInit) : null, {
201
210
  status: result.status,
202
211
  headers: result.headers,
@@ -512,6 +521,13 @@ export function createGatewayApp(deps: GatewayAppDeps) {
512
521
  // Mounted with no model behind it on purpose: the surface exists so it can
513
522
  // be finished when an upstream is known, rather than 404ing like a typo.
514
523
  const handleVideoGeneration = createVideoGenerationHandler(generationDeps);
524
+ // Passthrough surfaces: routed and accounted for, but never interpreted.
525
+ // No bundled adapter implements embeddings or audio, and inventing a wire
526
+ // for them would be a guess; what these need to be useful is the route, the
527
+ // pipeline, and a compatible upstream behind it.
528
+ const handleEmbedding = createEmbeddingHandler(generationDeps);
529
+ const handleAudioTranscription = createAudioTranscriptionHandler(generationDeps);
530
+ const handleAudioSpeech = createAudioSpeechHandler(generationDeps);
515
531
  // Shares the generation dependency bundle: same pipeline, and a count is
516
532
  // neither chat-shaped nor generation-shaped, so it needs exactly the same
517
533
  // routing, leasing, and accounting surface.
@@ -533,6 +549,9 @@ export function createGatewayApp(deps: GatewayAppDeps) {
533
549
  routes.post("/search", handleWebsearch);
534
550
  routes.post("/images/generations", handleImageGeneration);
535
551
  routes.post("/videos/generations", handleVideoGeneration);
552
+ routes.post("/embeddings", handleEmbedding);
553
+ routes.post("/audio/transcriptions", handleAudioTranscription);
554
+ routes.post("/audio/speech", handleAudioSpeech);
536
555
  routes.get("/models", handleModelsList);
537
556
  routes.get("/models/info", handleModelsDetail);
538
557
  routes.get("/models/*", handleModelsDetail);
@@ -580,10 +599,7 @@ export function createGatewayApp(deps: GatewayAppDeps) {
580
599
  createConsoleAccountResetHandler({
581
600
  token: deps.drainToken,
582
601
  resolvePeerAddress: (request) => peerAddresses.get(request) ?? null,
583
- resetConsoleAccount: async () => {
584
- const users = await deps.db.delete(consoleUsers);
585
- return users.rowCount ?? 0;
586
- },
602
+ resetConsoleAccount: () => resetConsoleAuthState(deps.db),
587
603
  }),
588
604
  );
589
605
  }
@@ -7,6 +7,7 @@ import type { RedisClient } from "../persistence/redis";
7
7
  import {
8
8
  bundledModelCatalog,
9
9
  liveProviderUpstreamHosts,
10
+ markCatalogSeeded,
10
11
  refreshActiveProviderCatalogs,
11
12
  registerByokProviders,
12
13
  retireUnbundledProviders,
@@ -31,6 +32,9 @@ import { RedisAdmissionCounterStore } from "../security/admission/redis-store";
31
32
  import { sweepLeases } from "../security/admission/lease-sweep";
32
33
  import { DrizzleApiKeyStore } from "../persistence/stores/api-key-store";
33
34
  import { UsageFlushBuffer } from "../persistence/usage-flush";
35
+ import { setPendingUsageSource } from "../security/api-key-auth";
36
+ import { HealthEventFlushBuffer, setHealthEventBuffer } from "../persistence/health-event-flush";
37
+ import { subscribePeerSettingsRevisions } from "../persistence/settings-revision";
34
38
  import { deleteExpiredSessions } from "../console/auth/session-service";
35
39
  import { InMemoryIpAbuseStore, IpAbuseProtectionService, RedisIpAbuseStore } from "../security/abuse";
36
40
  import {
@@ -61,6 +65,7 @@ import {
61
65
  } from "../workers/buddy-growth-tasks";
62
66
  import { createAccountSecretResolver } from "../providers/operations/provider-credential-service";
63
67
  import { quotaCacheSize } from "../console/quota/cache";
68
+ import { modelCatalogCacheSize } from "../providers/operations/model-catalog-cache";
64
69
  import { preferencesReaderFor } from "../persistence/tenant-preferences";
65
70
  import { sweepExpiredCooldowns } from "../providers/operations/account-health-service";
66
71
  import { DrizzleTelemetryStore } from "../persistence/stores/telemetry-store";
@@ -109,6 +114,7 @@ export interface ProductionDeps {
109
114
  poolSelector: NetworkPoolSelector;
110
115
  telemetryBuffer: TelemetryBatchBuffer;
111
116
  usageFlush: UsageFlushBuffer;
117
+ healthEventFlush: HealthEventFlushBuffer;
112
118
  trustedProxyBoundary: TrustedProxyBoundary;
113
119
  providerRegistry: ProviderRegistry;
114
120
  resolveOAuthRefresher: (providerId: string) => Promise<OAuthTokenRefresher | undefined>;
@@ -130,7 +136,9 @@ const OAUTH_REFRESH_INTER_ITEM_DELAY_MS = 1_500;
130
136
  * Client identities are refreshed out of band so provider dispatch never waits
131
137
  * on npm/provider version endpoints. Resolver TTLs deduplicate the sources.
132
138
  */
133
- const CLIENT_VERSION_MONITOR_INTERVAL_MS = 15 * 60_000;
139
+ // Matched to the resolver's own 30 min cache TTL: at 15 min every second pass
140
+ // was a pure cache read that could not discover anything new.
141
+ const CLIENT_VERSION_MONITOR_INTERVAL_MS = 30 * 60_000;
134
142
  const CLIENT_VERSION_INITIAL_DELAY_MS = 10_000;
135
143
 
136
144
  export async function buildProductionDeps(): Promise<ProductionDeps> {
@@ -166,6 +174,9 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
166
174
  await measureStartupPhase("bundled-model-seed", () =>
167
175
  seedBundledModels(db, bundledCatalog.modelsByProvider),
168
176
  );
177
+ // The boot seed above is unconditional, so record it: the snapshot builder's
178
+ // first rebuild would otherwise re-seed an identical catalog.
179
+ markCatalogSeeded();
169
180
  const snapshotService = new InMemoryRouteSnapshotService(
170
181
  createDatabaseSnapshotBuilder(db, () => refreshActiveProviderCatalogs(db, registry)),
171
182
  );
@@ -197,7 +208,21 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
197
208
  }
198
209
  return [...totals].map(([accountId, inflight]) => ({ accountId, inflight }));
199
210
  };
211
+ // Peer processes' settings mutations arrive here. Subscribed once for the
212
+ // process: the revision counter is process-global, and the handler only
213
+ // moves it forward.
214
+ subscribePeerSettingsRevisions();
200
215
  const usageFlush = new UsageFlushBuffer(db);
216
+ // Auth reads lifetime usage to seed the admission counter. The persisted
217
+ // value lags by up to one flush interval, so hand it the in-flight deltas
218
+ // too — otherwise the seed is systematically under what has actually been
219
+ // spent, worst exactly when the key is busiest.
220
+ setPendingUsageSource((apiKeyId) => usageFlush.pendingFor(apiKeyId));
221
+ // Health audit rows are append-only and read by nobody in dispatch, so they
222
+ // no longer land inside the request. The status write they accompany stays
223
+ // synchronous — routing reads it.
224
+ const healthEventFlush = new HealthEventFlushBuffer(db);
225
+ setHealthEventBuffer(healthEventFlush);
201
226
  const admissionStore = redis
202
227
  ? new RedisAdmissionCounterStore(redis)
203
228
  : new InMemoryAdmissionCounterStore();
@@ -280,7 +305,11 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
280
305
  });
281
306
  scheduledTasks.register({
282
307
  name: "console-session-sweep",
283
- intervalMs: 60 * 60_000,
308
+ // Sessions live 48 h, so an hourly sweep deleted nothing 47 times out of
309
+ // 48 — each pass a full scan of the session table for rows that cannot yet
310
+ // be expired. Half the session lifetime is the point where a pass can
311
+ // actually find work, and still leaves a full grace period after it.
312
+ intervalMs: 12 * 60 * 60_000,
284
313
  run: () => deleteExpiredSessions(db),
285
314
  });
286
315
  scheduledTasks.register({
@@ -309,6 +338,7 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
309
338
  routingRoundRobinEntries: () => routingEngine.roundRobinEntries(),
310
339
  ipAbuseKeys: () => (ipStore instanceof InMemoryIpAbuseStore ? ipStore.keyCount() : 0),
311
340
  quotaCacheEntries: () => quotaCacheSize(),
341
+ modelCatalogEntries: () => modelCatalogCacheSize(),
312
342
  // Late-bound via bindInFlight once the request state store exists.
313
343
  inFlight: () => 0,
314
344
  });
@@ -322,7 +352,11 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
322
352
  });
323
353
  scheduledTasks.register({
324
354
  name: "pool-agent-sweep",
325
- intervalMs: 60_000,
355
+ // `resolveAgent` already self-sweeps every 30 s, so a 1 min timer added a
356
+ // second pass that almost always found nothing to release. 5 min is well
357
+ // inside the idle timeout this exists to enforce, and outside the path
358
+ // that already runs.
359
+ intervalMs: 5 * 60_000,
326
360
  run: () => poolAgentResolver.sweepIdleAgents(),
327
361
  });
328
362
  const oauthRefreshService = new OAuthRefreshService(db);
@@ -338,7 +372,12 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
338
372
  // credential — no upstream call — so it runs often and cheaply.
339
373
  scheduledTasks.register({
340
374
  name: "account-enrichment-sweep",
341
- intervalMs: 30_000,
375
+ // Converges via `identity_checked_at`: once every stored credential has
376
+ // been parsed, the listing query returns nothing — and a 30 s timer kept
377
+ // running that empty query forever, two million times a year. 10 min is
378
+ // still instant for the case that matters (a batch import, where the label
379
+ // and email show up on the next tick) and costs nothing when idle.
380
+ intervalMs: 10 * 60_000,
342
381
  run: () => accountIdentitySweep({ db }),
343
382
  });
344
383
  scheduledTasks.register({
@@ -434,9 +473,11 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
434
473
  // an account unhealthy — a growth endpoint that 404s is a display miss, not a
435
474
  // dispatch failure.
436
475
  //
437
- // Hourly, not minute-cadenced: each claim is once per account per day, and a
438
- // task that ticks every minute would spend its whole budget re-reading the
439
- // day marker.
476
+ // Daily, not hourly: each claim is once per account per day and is
477
+ // ledger-guarded, so at an hourly cadence 23 of every 24 passes were a full
478
+ // scan of active accounts plus a ledger read per account that resolved to
479
+ // nothing. Once a day is the shortest interval where a pass can still find
480
+ // work, which is the only reason to run at all.
440
481
  const buddyTaskDeps = {
441
482
  db,
442
483
  resolveCredential: quotaResolveCredential,
@@ -447,37 +488,37 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
447
488
  };
448
489
  scheduledTasks.register({
449
490
  name: "buddy-streak",
450
- intervalMs: 60 * 60_000,
491
+ intervalMs: 24 * 60 * 60_000,
451
492
  initialDelayMs: 5 * 60_000,
452
493
  run: () => buddyStreakTask(buddyTaskDeps),
453
494
  });
454
495
  scheduledTasks.register({
455
496
  name: "buddy-redeem",
456
- intervalMs: 60 * 60_000,
497
+ intervalMs: 24 * 60 * 60_000,
457
498
  initialDelayMs: 8 * 60_000,
458
499
  run: () => buddyRedeemTask(buddyTaskDeps),
459
500
  });
460
501
  scheduledTasks.register({
461
502
  name: "buddy-lottery",
462
- intervalMs: 60 * 60_000,
503
+ intervalMs: 24 * 60 * 60_000,
463
504
  initialDelayMs: 11 * 60_000,
464
505
  run: () => buddyLotteryTask(buddyTaskDeps),
465
506
  });
466
507
  scheduledTasks.register({
467
508
  name: "buddy-travel",
468
- intervalMs: 60 * 60_000,
509
+ intervalMs: 24 * 60 * 60_000,
469
510
  initialDelayMs: 14 * 60_000,
470
511
  run: () => buddyTravelTask(buddyTaskDeps),
471
512
  });
472
513
  scheduledTasks.register({
473
514
  name: "buddy-adopt",
474
- intervalMs: 60 * 60_000,
515
+ intervalMs: 24 * 60 * 60_000,
475
516
  initialDelayMs: 17 * 60_000,
476
517
  run: () => buddyAdoptTask(buddyTaskDeps),
477
518
  });
478
519
  scheduledTasks.register({
479
520
  name: "buddy-trial",
480
- intervalMs: 6 * 60 * 60_000,
521
+ intervalMs: 24 * 60 * 60_000,
481
522
  initialDelayMs: 20 * 60_000,
482
523
  run: () => buddyTrialTask(buddyTaskDeps),
483
524
  });
@@ -525,6 +566,7 @@ export async function buildProductionDeps(): Promise<ProductionDeps> {
525
566
  poolAgentResolver,
526
567
  telemetryBuffer,
527
568
  usageFlush,
569
+ healthEventFlush,
528
570
  poolSelector,
529
571
  admissionService,
530
572
  trustedProxyBoundary: resolveTrustedProxyBoundary(),
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Wires Elysia's TypeBox bridge statically, for a source-run process.
3
+ *
4
+ * Elysia reaches TypeBox through a lazy `require("typebox/schema")` that
5
+ * resolves to a partial namespace outside a bundler. The bridge supplies all
6
+ * five namespaces explicitly so the lookup never has to happen.
7
+ *
8
+ * This is necessary but **not sufficient** for `NODE_ENV=production`: it does
9
+ * not stop the eager validator branch from reading
10
+ * `this.tb.buildResult.external`, which is still absent on a source run. That
11
+ * half is owned by `resolveElysiaPrecompile()`, which turns precompilation off
12
+ * for a source run. Keep both — wiring the bridge without the precompile guard
13
+ * still crashes, and the guard without the bridge leaves the lazy require in
14
+ * place for anything else that needs it.
15
+ *
16
+ * Keep this import first in `main.ts` so it runs before any module builds a
17
+ * route.
18
+ */
19
+ import { setupTypebox } from "elysia";
20
+ import * as typeboxType from "typebox";
21
+ import * as typeboxCompile from "typebox/compile";
22
+ import * as typeboxSchema from "typebox/schema";
23
+ import * as typeboxSystem from "typebox/system";
24
+ import * as typeboxValue from "typebox/value";
25
+
26
+ setupTypebox({
27
+ typebox: {
28
+ // The type side (`type` + `system`) and the value side (`value` + `schema`
29
+ // + `compile`) must both be complete; Elysia rejects a partial set because
30
+ // a half-wired bridge fails later with a less obvious error.
31
+ type: typeboxType,
32
+ system: typeboxSystem,
33
+ value: typeboxValue,
34
+ schema: typeboxSchema,
35
+ compile: typeboxCompile,
36
+ },
37
+ });
@@ -220,12 +220,14 @@ export async function bootstrap(): Promise<CartethyiaBoot> {
220
220
  flushTelemetry: async () => {
221
221
  await deps.telemetryBuffer.flush();
222
222
  await deps.usageFlush.flush();
223
+ await deps.healthEventFlush.flush();
223
224
  },
224
225
  closePools: async () => {
225
226
  await boot.server?.stop();
226
227
  await deps.scheduledTasks.stop();
227
228
  await deps.telemetryBuffer.stop({ flush: true });
228
229
  await deps.usageFlush.stop({ flush: true });
230
+ await deps.healthEventFlush.stop();
229
231
  await Promise.allSettled([
230
232
  closeDb(),
231
233
  closeRedis(),