cartethyia 2.4.6 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/README.md +45 -13
  2. package/dist/dashboard/assets/BannedPage-ChqnTRpZ.js +1 -0
  3. package/dist/dashboard/assets/BannedPage-ChqnTRpZ.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-Bcy150Jo.js → CliToolDetailPage-Bb9CNuoh.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-Bb9CNuoh.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-DyNXefph.js → CliToolsPage-BXUaRCQQ.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-BXUaRCQQ.js.gz +0 -0
  8. package/dist/dashboard/assets/CombosPage-CisNZOUt.js +1 -0
  9. package/dist/dashboard/assets/CombosPage-CisNZOUt.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-CATpAEOz.js → ConsoleLogPage-BOpI_LOH.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-BOpI_LOH.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-D6jEU7nL.js → CustomizationPage-DAkdvJ4k.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-DAkdvJ4k.js.gz +0 -0
  14. package/dist/dashboard/assets/{HealthEventsModal-CZkpn0a7.js → HealthEventsModal-DeT-VXHd.js} +1 -1
  15. package/dist/dashboard/assets/HealthEventsModal-DeT-VXHd.js.gz +0 -0
  16. package/dist/dashboard/assets/{LoginPage-BJlMVN4U.js → LoginPage-CytxSeGP.js} +1 -1
  17. package/dist/dashboard/assets/LoginPage-CytxSeGP.js.gz +0 -0
  18. package/dist/dashboard/assets/{ModelPicker-Br3CXEau.js → ModelPicker-oQ-SDXp8.js} +1 -1
  19. package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
  20. package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js +1 -0
  21. package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js.gz +0 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js +3 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +1 -0
  25. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  26. package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js +1 -0
  27. package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js.gz +0 -0
  28. package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js +7 -0
  29. package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js.gz +0 -0
  30. package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js +1 -0
  31. package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js.gz +0 -0
  32. package/dist/dashboard/assets/{SettingsPage-BHW4Zt06.js → SettingsPage-8-eVadhc.js} +1 -1
  33. package/dist/dashboard/assets/SettingsPage-8-eVadhc.js.gz +0 -0
  34. package/dist/dashboard/assets/{SetupPage-VOYC_lIa.js → SetupPage-uEIXPyGO.js} +1 -1
  35. package/dist/dashboard/assets/SetupPage-uEIXPyGO.js.gz +0 -0
  36. package/dist/dashboard/assets/SortableList-DCY0TPTZ.js +1 -0
  37. package/dist/dashboard/assets/SortableList-DCY0TPTZ.js.gz +0 -0
  38. package/dist/dashboard/assets/{StudioPage-BNIDLiU6.js → StudioPage-DqK5E26W.js} +4 -4
  39. package/dist/dashboard/assets/StudioPage-DqK5E26W.js.gz +0 -0
  40. package/dist/dashboard/assets/UsagePage-38JfiZ0H.js +56 -0
  41. package/dist/dashboard/assets/UsagePage-38JfiZ0H.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BETfaMYZ.js → api-keys-BmDbsior.js} +1 -1
  43. package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
  44. package/dist/dashboard/assets/app-BbpSarVV.css +2 -0
  45. package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
  46. package/dist/dashboard/assets/assert-BHfaei-X.js +1 -0
  47. package/dist/dashboard/assets/assert-BHfaei-X.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-BTyGk_DL.js → badge-DpXT4Ixt.js} +1 -1
  49. package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-bujTWYM4.js → banner-CSAAWLJv.js} +1 -1
  51. package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-B0Bnx5CA.js → card-DEhRXXw4.js} +1 -1
  53. package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-DE2ug812.js → cli-tools-B0wBq-RL.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-R_rArH8S.js → clipboard-button-DOo6EU8O.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-BWIP_EvW.js → dialog-BvQeBMfP.js} +1 -1
  59. package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-BKcgVugF.js → drawer-CMwPXwQB.js} +1 -1
  61. package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
  62. package/dist/dashboard/assets/entry--yj84zk3.js +4 -0
  63. package/dist/dashboard/assets/entry--yj84zk3.js.gz +0 -0
  64. package/dist/dashboard/assets/{entry-CcWkGR4a.js → entry-D4O2229X.js} +1 -1
  65. package/dist/dashboard/assets/{entry-CcWkGR4a.js.gz → entry-D4O2229X.js.gz} +0 -0
  66. package/dist/dashboard/assets/{entry-kTiTFrvh.js → entry-DKvr5Q9K.js} +1 -1
  67. package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
  68. package/dist/dashboard/assets/{github-badge-Booc5mNe.js → github-badge-zliLvBwr.js} +1 -1
  69. package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-D70pChfn.js → index-CcD1eSJ6.js} +2 -2
  71. package/dist/dashboard/assets/index-CcD1eSJ6.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-D1TCqM2D.js → inline-D1IlZZ8H.js} +1 -1
  73. package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-B9oyU93y.js → input-BpVZdrbc.js} +1 -1
  75. package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-BArzXirQ.js → layout-BioLW7gN.js} +1 -1
  77. package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-BVZzaoHF.js → list-row-CwVcGAIc.js} +1 -1
  79. package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-EmuIayqp.js → live-yfEJFF9T.js} +1 -1
  81. package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
  82. package/dist/dashboard/assets/{model-bans-CSPQ9xB0.js → model-bans-CT-TxSNX.js} +1 -1
  83. package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
  84. package/dist/dashboard/assets/providers/cursor.webp +0 -0
  85. package/dist/dashboard/assets/providers/trae.webp +0 -0
  86. package/dist/dashboard/assets/{routing-BExgxxBU.js → routing-Cs6jl49L.js} +1 -1
  87. package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
  88. package/dist/dashboard/assets/select-XPJ-dIw3.js +41 -0
  89. package/dist/dashboard/assets/select-XPJ-dIw3.js.gz +0 -0
  90. package/dist/dashboard/assets/settings-CfcTcOyU.js +1 -0
  91. package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
  92. package/dist/dashboard/assets/{stack-Ca5HxI5i.js → stack-C9_uUjaA.js} +1 -1
  93. package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
  94. package/dist/dashboard/assets/{state-CzUPfLXo.js → state-DA0B95UQ.js} +1 -1
  95. package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
  96. package/dist/dashboard/assets/{switch-DOUIky_i.js → switch-Cittxy-x.js} +1 -1
  97. package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
  98. package/dist/dashboard/assets/{toast-C76WrMhc.js → toast-CNt_jzO4.js} +1 -1
  99. package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
  100. package/dist/dashboard/assets/{useMutation-D87FZwH0.js → useMutation-BQKMcJ1H.js} +1 -1
  101. package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
  102. package/dist/dashboard/index.html +1 -1
  103. package/dist/dashboard/index.html.gz +0 -0
  104. package/dist/dashboard/precompressed-assets.json +51 -52
  105. package/migrations/full-postgres/0015_add_payload_stages.sql +18 -0
  106. package/migrations/full-postgres/0016_add_share_link_slug.sql +20 -0
  107. package/migrations/full-postgres/0017_add_models_pro_only.sql +18 -0
  108. package/migrations/full-postgres/0018_add_accounts_quota_plan.sql +18 -0
  109. package/migrations/full-postgres/0019_add_models_free_tier.sql +15 -0
  110. package/migrations/lite-sqlite3/0015_add_payload_stages.sql +18 -0
  111. package/migrations/lite-sqlite3/0016_add_share_link_slug.sql +20 -0
  112. package/migrations/lite-sqlite3/0017_add_models_pro_only.sql +18 -0
  113. package/migrations/lite-sqlite3/0018_add_accounts_quota_plan.sql +18 -0
  114. package/migrations/lite-sqlite3/0019_add_models_free_tier.sql +15 -0
  115. package/package.json +3 -2
  116. package/scripts/cli/command/console-menu.ts +30 -11
  117. package/scripts/cli/command/start.ts +77 -13
  118. package/scripts/cli/command/tray.ts +47 -8
  119. package/scripts/cli/command/update.ts +1 -1
  120. package/scripts/cli/gateway/instance-lock.ts +105 -0
  121. package/scripts/cli/gateway/spawn.ts +173 -101
  122. package/scripts/cli/ui/background.ts +110 -49
  123. package/src/boot/app.ts +20 -6
  124. package/src/boot/dependencies.ts +19 -0
  125. package/src/boot/elysia-typebox-bridge.ts +37 -0
  126. package/src/boot/lifecycle.ts +2 -0
  127. package/src/config/cooldowns.ts +138 -138
  128. package/src/config/providers.ts +5 -5
  129. package/src/config/runtime.ts +19 -3
  130. package/src/config/spec.ts +9 -0
  131. package/src/console/api-keys/contracts.ts +414 -391
  132. package/src/console/api-keys/routes.ts +880 -870
  133. package/src/console/auth/account-reset-store.ts +14 -0
  134. package/src/console/auth/account-reset.ts +3 -2
  135. package/src/console/auth/setup-service.ts +6 -3
  136. package/src/console/domain-registration.ts +9 -2
  137. package/src/console/observability/contracts.ts +475 -455
  138. package/src/console/observability/store.ts +1015 -952
  139. package/src/console/providers/catalog/contracts.ts +31 -0
  140. package/src/console/providers/catalog/provider-operations.ts +8 -0
  141. package/src/console/providers/catalog/store.ts +1532 -1525
  142. package/src/console/providers/oauth/callback-listener.ts +18 -3
  143. package/src/console/providers/oauth/routes.ts +38 -15
  144. package/src/console/quota/refresh.ts +19 -0
  145. package/src/console/updates/routes.ts +162 -4
  146. package/src/main.ts +3 -0
  147. package/src/network/http2-bidi.ts +85 -0
  148. package/src/network/outbound-fetch.ts +1 -26
  149. package/src/network/pool/agent.ts +478 -460
  150. package/src/network/pool/resolver.ts +1104 -544
  151. package/src/network/pool-health-machine.ts +12 -5
  152. package/src/observability/payload-capture.ts +344 -324
  153. package/src/persistence/db-pg.ts +9 -3
  154. package/src/persistence/db-sqlite.ts +497 -495
  155. package/src/persistence/health-event-flush.ts +137 -0
  156. package/src/persistence/migrate.ts +181 -129
  157. package/src/persistence/schema.ts +1360 -1295
  158. package/src/persistence/settings-revision.ts +25 -0
  159. package/src/persistence/shared-cache.ts +217 -0
  160. package/src/persistence/stores/share-store.ts +556 -528
  161. package/src/persistence/usage-flush.ts +61 -1
  162. package/src/protocol/canonical-model.ts +23 -1
  163. package/src/protocol/response/chat.ts +143 -10
  164. package/src/protocol/response/omp-tool-call-markup.ts +221 -0
  165. package/src/providers/default-registry.ts +173 -1
  166. package/src/providers/discovery/openai-model-discovery.ts +39 -3
  167. package/src/providers/discovery/openrouter-free-roster.ts +98 -0
  168. package/src/providers/discovery/probing-service.ts +1022 -1001
  169. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +39 -0
  170. package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
  171. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +18 -5
  172. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +4 -0
  173. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -175
  174. package/src/providers/integrations/buddy/codebuddy/shared.ts +15 -6
  175. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
  176. package/src/providers/integrations/buddy/workbuddy/shared.ts +3 -3
  177. package/src/providers/integrations/claude/claude.ts +606 -582
  178. package/src/providers/integrations/cline/cline-quota.ts +18 -16
  179. package/src/providers/integrations/cline/cline.ts +539 -468
  180. package/src/providers/integrations/commandcode/commandcode.ts +675 -541
  181. package/src/providers/integrations/commandcode/discovery.ts +116 -0
  182. package/src/providers/integrations/commandcode/quota.ts +160 -0
  183. package/src/providers/integrations/cursor/cursor-auth.ts +418 -0
  184. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -0
  185. package/src/providers/integrations/cursor/cursor-http2.ts +300 -0
  186. package/src/providers/integrations/cursor/cursor-identity.ts +92 -0
  187. package/src/providers/integrations/cursor/cursor-request.ts +471 -0
  188. package/src/providers/integrations/cursor/cursor-session-manager.ts +281 -0
  189. package/src/providers/integrations/cursor/cursor-shared.ts +108 -0
  190. package/src/providers/integrations/cursor/cursor.ts +960 -0
  191. package/src/providers/integrations/cursor/protocol/framing.ts +129 -0
  192. package/src/providers/integrations/cursor/protocol/messages.ts +2355 -0
  193. package/src/providers/integrations/cursor/protocol/struct.ts +305 -0
  194. package/src/providers/integrations/github/github.ts +4 -4
  195. package/src/providers/integrations/hermes.ts +79 -8
  196. package/src/providers/integrations/kilo/kilo-discovery.ts +10 -1
  197. package/src/providers/integrations/kilo/kilo.ts +153 -146
  198. package/src/providers/integrations/opencode/provider.ts +532 -538
  199. package/src/providers/integrations/openrouter/provider.ts +139 -281
  200. package/src/providers/integrations/qoder/qoder-catalog.ts +450 -0
  201. package/src/providers/integrations/qoder/qoder-oauth.ts +310 -257
  202. package/src/providers/integrations/qoder/qoder-realm.ts +106 -0
  203. package/src/providers/integrations/qoder/qoder-virtual.ts +77 -0
  204. package/src/providers/integrations/qoder/qoder.ts +865 -730
  205. package/src/providers/integrations/trae/trae-catalog.ts +190 -0
  206. package/src/providers/integrations/trae/trae-headers.ts +149 -0
  207. package/src/providers/integrations/trae/trae-oauth.ts +773 -0
  208. package/src/providers/integrations/trae/trae-protocol.ts +400 -0
  209. package/src/providers/integrations/trae/trae-realm.ts +220 -0
  210. package/src/providers/integrations/trae/trae.ts +458 -0
  211. package/src/providers/operations/account-health-service.ts +107 -10
  212. package/src/providers/operations/client-versions.ts +133 -6
  213. package/src/providers/operations/provider-catalog-seeder.ts +95 -86
  214. package/src/providers/operations/provider-credential-service.ts +46 -7
  215. package/src/providers/provider-contracts.ts +76 -0
  216. package/src/providers/provider-metadata.ts +78 -6
  217. package/src/providers/provider-model-definition.ts +373 -349
  218. package/src/security/api-key-auth.ts +126 -8
  219. package/src/transport/contracts.ts +3 -0
  220. package/src/transport/dispatch/attempt-finalize.ts +32 -1
  221. package/src/transport/dispatch/attempt-loop.ts +530 -515
  222. package/src/transport/dispatch/generation.ts +88 -21
  223. package/src/transport/dispatch/leases.ts +99 -2
  224. package/src/transport/dispatch/proxy-request.ts +60 -3
  225. package/src/transport/dispatch/streaming/finalize.ts +16 -1
  226. package/src/transport/dispatch/streaming/stream.ts +22 -2
  227. package/src/transport/dispatch/upstream.ts +19 -1
  228. package/src/transport/guards/auth-guard.ts +26 -1
  229. package/src/transport/guards/body-policy.ts +5 -4
  230. package/src/transport/request/preparer.ts +103 -0
  231. package/src/transport/request/state.ts +14 -0
  232. package/src/transport/routing/plan-gate.ts +45 -0
  233. package/src/transport/routing/route-catalog.ts +799 -777
  234. package/src/transport/routing/route-model.ts +52 -1
  235. package/src/transport/routing/router.ts +51 -0
  236. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js +0 -1
  237. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js.gz +0 -0
  238. package/dist/dashboard/assets/CliToolDetailPage-Bcy150Jo.js.gz +0 -0
  239. package/dist/dashboard/assets/CliToolsPage-DyNXefph.js.gz +0 -0
  240. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js +0 -1
  241. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js.gz +0 -0
  242. package/dist/dashboard/assets/ConsoleLogPage-CATpAEOz.js.gz +0 -0
  243. package/dist/dashboard/assets/CustomizationPage-D6jEU7nL.js.gz +0 -0
  244. package/dist/dashboard/assets/HealthEventsModal-CZkpn0a7.js.gz +0 -0
  245. package/dist/dashboard/assets/LoginPage-BJlMVN4U.js.gz +0 -0
  246. package/dist/dashboard/assets/ModelPicker-Br3CXEau.js.gz +0 -0
  247. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js +0 -1
  248. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js.gz +0 -0
  249. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js +0 -3
  250. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js.gz +0 -0
  251. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
  252. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js.gz +0 -0
  253. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js +0 -1
  254. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js.gz +0 -0
  255. package/dist/dashboard/assets/ProxyPage-CXiumA53.js +0 -7
  256. package/dist/dashboard/assets/ProxyPage-CXiumA53.js.gz +0 -0
  257. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js +0 -1
  258. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js.gz +0 -0
  259. package/dist/dashboard/assets/SettingsPage-BHW4Zt06.js.gz +0 -0
  260. package/dist/dashboard/assets/SetupPage-VOYC_lIa.js.gz +0 -0
  261. package/dist/dashboard/assets/SortableList-DpOTnGuH.js +0 -1
  262. package/dist/dashboard/assets/SortableList-DpOTnGuH.js.gz +0 -0
  263. package/dist/dashboard/assets/StudioPage-BNIDLiU6.js.gz +0 -0
  264. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js +0 -56
  265. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js.gz +0 -0
  266. package/dist/dashboard/assets/api-keys-BETfaMYZ.js.gz +0 -0
  267. package/dist/dashboard/assets/app-BLCvGdzb.css +0 -2
  268. package/dist/dashboard/assets/app-BLCvGdzb.css.gz +0 -0
  269. package/dist/dashboard/assets/assert-7J6_LVD3.js +0 -1
  270. package/dist/dashboard/assets/assert-7J6_LVD3.js.gz +0 -0
  271. package/dist/dashboard/assets/badge-BTyGk_DL.js.gz +0 -0
  272. package/dist/dashboard/assets/banner-bujTWYM4.js.gz +0 -0
  273. package/dist/dashboard/assets/card-B0Bnx5CA.js.gz +0 -0
  274. package/dist/dashboard/assets/cli-tools-DE2ug812.js.gz +0 -0
  275. package/dist/dashboard/assets/clipboard-button-R_rArH8S.js.gz +0 -0
  276. package/dist/dashboard/assets/dialog-BWIP_EvW.js.gz +0 -0
  277. package/dist/dashboard/assets/drawer-BKcgVugF.js.gz +0 -0
  278. package/dist/dashboard/assets/entry-7dpgvpJ3.js +0 -4
  279. package/dist/dashboard/assets/entry-7dpgvpJ3.js.gz +0 -0
  280. package/dist/dashboard/assets/entry-kTiTFrvh.js.gz +0 -0
  281. package/dist/dashboard/assets/github-badge-Booc5mNe.js.gz +0 -0
  282. package/dist/dashboard/assets/index-D70pChfn.js.gz +0 -0
  283. package/dist/dashboard/assets/inline-D1TCqM2D.js.gz +0 -0
  284. package/dist/dashboard/assets/input-B9oyU93y.js.gz +0 -0
  285. package/dist/dashboard/assets/layout-BArzXirQ.js.gz +0 -0
  286. package/dist/dashboard/assets/list-row-BVZzaoHF.js.gz +0 -0
  287. package/dist/dashboard/assets/live-EmuIayqp.js.gz +0 -0
  288. package/dist/dashboard/assets/model-bans-CSPQ9xB0.js.gz +0 -0
  289. package/dist/dashboard/assets/routing-BExgxxBU.js.gz +0 -0
  290. package/dist/dashboard/assets/select-C9mMoEpJ.js +0 -41
  291. package/dist/dashboard/assets/select-C9mMoEpJ.js.gz +0 -0
  292. package/dist/dashboard/assets/settings-C4_Gn_wq.js +0 -1
  293. package/dist/dashboard/assets/settings-C4_Gn_wq.js.gz +0 -0
  294. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
  295. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js.gz +0 -0
  296. package/dist/dashboard/assets/stack-Ca5HxI5i.js.gz +0 -0
  297. package/dist/dashboard/assets/state-CzUPfLXo.js.gz +0 -0
  298. package/dist/dashboard/assets/switch-DOUIky_i.js.gz +0 -0
  299. package/dist/dashboard/assets/toast-C76WrMhc.js.gz +0 -0
  300. package/dist/dashboard/assets/useMutation-D87FZwH0.js.gz +0 -0
  301. package/src/network/tls/bun-client-hello.ts +0 -112
  302. /package/dist/dashboard/assets/{app-Cjq7nrwu.js → app-C-Y4oZT0.js} +0 -0
  303. /package/dist/dashboard/assets/{app-Cjq7nrwu.js.gz → app-C-Y4oZT0.js.gz} +0 -0
@@ -0,0 +1,137 @@
1
+ // Deferred writes for the account/pool health audit log.
2
+ //
3
+ // `health_events` is an append-only operator log: nothing in the request path
4
+ // reads it. It was written synchronously inside the same transaction as the
5
+ // `provider_accounts` UPDATE that routing *does* read, so every failed attempt
6
+ // paid an extra INSERT before the request could finish — on the embedded
7
+ // backend, where writes queue behind a single writer, that was the difference
8
+ // between one write and two.
9
+ //
10
+ // Only the audit row moves off the hot path. The status UPDATE stays
11
+ // synchronous and transactional, because routing reads it: deferring that would
12
+ // let a subsequent request pick an account that had just failed, which is worse
13
+ // under exactly the failure wave this work targets.
14
+ //
15
+ // Same shape as `persistence/usage-flush.ts`: enqueue and return, a timer
16
+ // flushes, shutdown flushes what remains. A crash loses the unflushed rows and
17
+ // nothing else — the durable state routing depends on was never in here.
18
+ import type { CartethyiaDatabase } from "./db-handle";
19
+ import { healthEvents } from "./schema";
20
+
21
+ /** One audit row waiting to be written. */
22
+ export interface HealthEventRow {
23
+ readonly entityKind: "account" | "pool";
24
+ readonly accountId?: string | null;
25
+ readonly networkPoolId?: string | null;
26
+ readonly fromStatus: string | null;
27
+ readonly toStatus: string;
28
+ readonly reason: string;
29
+ readonly errorCategory: string | null;
30
+ readonly modelId?: string | null;
31
+ readonly createdAt: Date;
32
+ }
33
+
34
+ /**
35
+ * Rows dropped rather than accumulated without bound.
36
+ *
37
+ * A health log is worth less than a stable process: a failure wave that cannot
38
+ * be flushed must not grow this queue until the process dies. Beyond the cap
39
+ * the oldest rows are discarded and counted.
40
+ */
41
+ const MAX_PENDING_ROWS = 2_000;
42
+
43
+ /**
44
+ * Process-wide sink, injected by `account-health-service` and the pool-health
45
+ * path.
46
+ *
47
+ * A module-level optional rather than a constructor parameter on every call:
48
+ * those functions are reached from dozens of call sites with a plain database
49
+ * handle, and threading a buffer through all of them would be a far larger
50
+ * change than the write it defers. `undefined` means "write synchronously",
51
+ * which is today's behaviour and what tests that assert the row landed expect.
52
+ */
53
+ let activeBuffer: HealthEventFlushBuffer | undefined;
54
+
55
+ /** Installs the process-wide buffer. */
56
+ export function setHealthEventBuffer(buffer: HealthEventFlushBuffer | undefined): void {
57
+ activeBuffer = buffer;
58
+ }
59
+
60
+ /** The installed buffer, or `undefined` when writes must stay synchronous. */
61
+ export function healthEventBuffer(): HealthEventFlushBuffer | undefined {
62
+ return activeBuffer;
63
+ }
64
+
65
+ export class HealthEventFlushBuffer {
66
+ private readonly pending: HealthEventRow[] = [];
67
+ private timer: ReturnType<typeof setInterval> | undefined;
68
+ private flushing = false;
69
+ private dropped = 0;
70
+
71
+ constructor(
72
+ private readonly db: CartethyiaDatabase,
73
+ private readonly intervalMs = 1_000,
74
+ ) {
75
+ this.arm();
76
+ }
77
+
78
+ private arm(): void {
79
+ if (this.timer !== undefined) return;
80
+ // `unref` so a pending interval never holds the process open at shutdown;
81
+ // `stop()` drains explicitly instead.
82
+ this.timer = setInterval(() => void this.flush(), this.intervalMs);
83
+ this.timer.unref?.();
84
+ }
85
+
86
+ /** Records one audit row. Never touches the database; safe per attempt. */
87
+ enqueue(row: HealthEventRow): void {
88
+ if (this.pending.length >= MAX_PENDING_ROWS) {
89
+ this.pending.shift();
90
+ this.dropped += 1;
91
+ }
92
+ this.pending.push(row);
93
+ }
94
+
95
+ /** Rows discarded because the queue was full. Observability for the buffer. */
96
+ get droppedCount(): number {
97
+ return this.dropped;
98
+ }
99
+
100
+ /** Writes everything accumulated so far. Safe to call concurrently. */
101
+ async flush(): Promise<void> {
102
+ if (this.flushing || this.pending.length === 0) return;
103
+ this.flushing = true;
104
+ // Drained before the await: rows enqueued while the insert runs stay for
105
+ // the next pass rather than being lost if this one throws.
106
+ const batch = this.pending.splice(0, this.pending.length);
107
+ try {
108
+ await this.db.insert(healthEvents).values(
109
+ batch.map((row) => ({
110
+ entityKind: row.entityKind,
111
+ accountId: row.accountId ?? null,
112
+ networkPoolId: row.networkPoolId ?? null,
113
+ fromStatus: row.fromStatus as "active" | "cooldown" | "disabled" | null,
114
+ toStatus: row.toStatus as "active" | "cooldown" | "disabled",
115
+ reason: row.reason,
116
+ errorCategory: row.errorCategory as never,
117
+ modelId: row.modelId ?? null,
118
+ createdAt: row.createdAt,
119
+ })),
120
+ );
121
+ } catch {
122
+ // An audit write must never fail a request or retry a dispatch. The rows
123
+ // are dropped; the durable status change was written synchronously.
124
+ } finally {
125
+ this.flushing = false;
126
+ }
127
+ }
128
+
129
+ /** Stops the timer and flushes what remains. Called at shutdown. */
130
+ async stop(): Promise<void> {
131
+ if (this.timer !== undefined) {
132
+ clearInterval(this.timer);
133
+ this.timer = undefined;
134
+ }
135
+ await this.flush();
136
+ }
137
+ }
@@ -1,129 +1,181 @@
1
- /** Migration discovery + ledger helpers shared by every database backend. */
2
- import { sql } from "drizzle-orm";
3
- import { createHash } from "node:crypto";
4
- import { existsSync, readdirSync } from "node:fs";
5
- import { basename, resolve } from "node:path";
6
- import { resolvePackagePath } from "../config/package-paths";
7
- import type { CartethyiaDatabase } from "./db-handle";
8
-
9
- /**
10
- * SQL-only migration ledger. Migration files are the source of truth; this
11
- * table stores only which numbered files have already run.
12
- */
13
- export const MIGRATION_LEDGER_TABLE = "cartethyia_schema_migrations";
14
-
15
- export const LEDGER_DDL = `CREATE TABLE IF NOT EXISTS ${MIGRATION_LEDGER_TABLE} (
16
- migration_id text PRIMARY KEY,
17
- applied_at timestamptz NOT NULL DEFAULT now()
18
- )`;
19
-
20
- export function resolveMigrationsFolder(): string {
21
- const folder = resolvePackagePath("migrations", "full-postgres");
22
- if (!existsSync(folder)) {
23
- throw new Error(`Migrations folder not found: ${folder}`);
24
- }
25
- return folder;
26
- }
27
-
28
- export function migrationFiles(folder: string): readonly string[] {
29
- return readdirSync(folder)
30
- .filter((file) => /^\d{4}_.+\.sql$/.test(file))
31
- .sort()
32
- .map((file) => resolve(folder, file));
33
- }
34
-
35
- export function migrationIdFor(file: string): string {
36
- return basename(file);
37
- }
38
-
39
- /** The only migration file a fresh database builds from. */
40
- export const BASELINE_MIGRATION_ID = "0000_baseline.sql";
41
- const BASELINE_SCHEMA_MARKER_PREFIX = "baseline-schema:";
42
-
43
- /** Identifies the exact baseline schema source recorded by a fresh database. */
44
- export function baselineSchemaMarker(source: string): string {
45
- const hash = createHash("sha256").update(source).digest("hex");
46
- return `${BASELINE_SCHEMA_MARKER_PREFIX}${hash}`;
47
- }
48
-
49
- /**
50
- * Rejects databases whose schema was created by retired migration files or
51
- * which have tables but no record of the current baseline.
52
- */
53
- export function assertMigrationHistorySupported(
54
- applied: ReadonlySet<string>,
55
- expected: readonly string[],
56
- hasTenants: boolean,
57
- currentBaselineMarker?: string,
58
- ): void {
59
- const supported = new Set(expected);
60
- const retired = [...applied].filter((migrationId) => !supported.has(migrationId));
61
- if (retired.length > 0) {
62
- throw new Error(
63
- `Database contains retired migration history (${retired.join(", ")}). ` +
64
- "Create a fresh database and restore from a backup; in-place upgrades are no longer supported.",
65
- );
66
- }
67
- if (hasTenants && !applied.has(BASELINE_MIGRATION_ID)) {
68
- throw new Error(
69
- "Database contains an unversioned schema. Create a fresh database and restore from a backup; " +
70
- "in-place upgrades are no longer supported.",
71
- );
72
- }
73
- if (
74
- hasTenants &&
75
- currentBaselineMarker !== undefined &&
76
- !applied.has(currentBaselineMarker)
77
- ) {
78
- throw new Error(
79
- "Database was created from a different baseline schema. Create a fresh database and restore from a backup; " +
80
- "in-place upgrades are no longer supported.",
81
- );
82
- }
83
- }
84
-
85
- /**
86
- * Splits a migration file on the drizzle `statement-breakpoint` markers the
87
- * files already carry. Only the embedded backend needs per-statement
88
- * application (single-statement failure tolerance); the pg path sends the
89
- * whole file at once exactly as before.
90
- */
91
- export function splitStatements(source: string): string[] {
92
- return source
93
- .split(/--> statement-breakpoint/g)
94
- .map((part) => part.trim())
95
- .filter((part) => part.length > 0);
96
- }
97
-
98
- /**
99
- * Readiness view of the migration ledger: the numbered SQL files discovered on
100
- * disk compared against the ids recorded in {@link MIGRATION_LEDGER_TABLE}.
101
- */
102
- export interface MigrationLedgerStatus {
103
- /** Migration file names discovered on disk, in apply order. */
104
- readonly expected: readonly string[];
105
- /** Expected migrations that have no ledger row. */
106
- readonly pending: readonly string[];
107
- /** True only when every expected migration is recorded in the ledger. */
108
- readonly applied: boolean;
109
- }
110
-
111
- /**
112
- * Compares the migration files discovered on disk against the applied-migration
113
- * ledger. A reachable ledger alone is not readiness: an interrupted upgrade
114
- * leaves the table present while later migrations are still unapplied.
115
- *
116
- * Runs on the drizzle handle, so it is identical on every backend.
117
- */
118
- export async function readMigrationLedgerStatus(
119
- db: CartethyiaDatabase,
120
- folder: string = resolveMigrationsFolder(),
121
- ): Promise<MigrationLedgerStatus> {
122
- const expected = migrationFiles(folder).map((file) => migrationIdFor(file));
123
- const result = await db.execute<{ migration_id: string }>(
124
- sql.raw(`SELECT migration_id FROM ${MIGRATION_LEDGER_TABLE}`),
125
- );
126
- const applied = new Set(result.rows.map((row) => row.migration_id));
127
- const pending = expected.filter((migrationId) => !applied.has(migrationId));
128
- return { expected, pending, applied: pending.length === 0 };
129
- }
1
+ /** Migration discovery + ledger helpers shared by every database backend. */
2
+ import { sql } from "drizzle-orm";
3
+ import { createHash } from "node:crypto";
4
+ import { existsSync, readdirSync } from "node:fs";
5
+ import { basename, resolve } from "node:path";
6
+ import { resolvePackagePath } from "../config/package-paths";
7
+ import type { CartethyiaDatabase } from "./db-handle";
8
+
9
+ /**
10
+ * SQL-only migration ledger. Migration files are the source of truth; this
11
+ * table stores only which numbered files have already run.
12
+ */
13
+ export const MIGRATION_LEDGER_TABLE = "cartethyia_schema_migrations";
14
+
15
+ export const LEDGER_DDL = `CREATE TABLE IF NOT EXISTS ${MIGRATION_LEDGER_TABLE} (
16
+ migration_id text PRIMARY KEY,
17
+ applied_at timestamptz NOT NULL DEFAULT now()
18
+ )`;
19
+
20
+ export function resolveMigrationsFolder(): string {
21
+ const folder = resolvePackagePath("migrations", "full-postgres");
22
+ if (!existsSync(folder)) {
23
+ throw new Error(`Migrations folder not found: ${folder}`);
24
+ }
25
+ return folder;
26
+ }
27
+
28
+ export function migrationFiles(folder: string): readonly string[] {
29
+ return readdirSync(folder)
30
+ .filter((file) => /^\d{4}_.+\.sql$/.test(file))
31
+ .sort()
32
+ .map((file) => resolve(folder, file));
33
+ }
34
+
35
+ export function migrationIdFor(file: string): string {
36
+ return basename(file);
37
+ }
38
+
39
+ /** The only migration file a fresh database builds from. */
40
+ export const BASELINE_MIGRATION_ID = "0000_baseline.sql";
41
+ const BASELINE_SCHEMA_MARKER_PREFIX = "baseline-schema:";
42
+
43
+ /**
44
+ * Identifies the exact baseline schema recorded by a fresh database.
45
+ *
46
+ * The hash is taken over the *statements*, with comments stripped, because the
47
+ * raw file text is not the thing that matters: a comment edit changes the hash
48
+ * without changing the schema, and a changed hash makes every existing database
49
+ * refuse to boot ("different baseline schema"). That is not hypothetical — a
50
+ * documentation-only edit to the SQLite baseline header shipped in 2.4.6 and
51
+ * locked out every database created by 2.4.5.
52
+ *
53
+ * Stripping comments makes the marker track the schema a database was built
54
+ * from, which is what the check is actually asserting.
55
+ */
56
+ export function baselineSchemaMarker(source: string): string {
57
+ return `${BASELINE_SCHEMA_MARKER_PREFIX}${hashStatements(source)}`;
58
+ }
59
+
60
+ /**
61
+ * Markers recorded by releases that hashed the raw baseline file text.
62
+ *
63
+ * Every 2.4.x release shipped the same SQLite baseline, so a database created by
64
+ * any of them carries this one marker. The *schema* is identical to the current
65
+ * baseline (verified: stripping comments from both yields the same hash), so
66
+ * these are accepted as proof of the same schema instead of telling every
67
+ * existing user to recreate their database.
68
+ *
69
+ * These are frozen history. Never add a marker for a schema that actually
70
+ * differs — that would let an incompatible database boot against a newer
71
+ * runtime, which is the failure this check exists to prevent.
72
+ */
73
+ const LEGACY_BASELINE_SCHEMA_MARKERS: readonly string[] = [
74
+ // full-postgres/0000_baseline.sql — unchanged across v2.4.0-v2.4.6, so this is
75
+ // the raw-text hash every existing Full-mode database carries.
76
+ "baseline-schema:bf7d2b4f1f99b4470c81850a77cc4ebc989997fbe8c8b68ec62833164c73a6ad",
77
+ // lite-sqlite3/0000_baseline.sql, v2.4.0 – v2.4.5 (raw-text sha256).
78
+ "baseline-schema:438c5e014c5f86f483a84e8a106b0e62143d4eb0b0daad728a300bcc683d8c02",
79
+ // lite-sqlite3/0000_baseline.sql, v2.4.6 (same schema, different header).
80
+ "baseline-schema:405083572bb108bfe42e726b6f3d0b47c8d76c8e1de1a3a5d5fbbbe88a7ac4dd",
81
+ ];
82
+
83
+ /** Accepted markers for the current baseline schema, current form first. */
84
+ export function acceptedBaselineSchemaMarkers(source: string): readonly string[] {
85
+ return [baselineSchemaMarker(source), ...LEGACY_BASELINE_SCHEMA_MARKERS];
86
+ }
87
+
88
+ /** Hash of the baseline's statements, with comments and formatting removed. */
89
+ function hashStatements(source: string): string {
90
+ const statements = source
91
+ // `--` to end of line, which is the only comment form these baselines use.
92
+ .replace(/--[^\n]*/g, "")
93
+ .split(/--> statement-breakpoint/)
94
+ .map((part) => part.replace(/\s+/g, " ").trim())
95
+ .filter((part) => part.length > 0)
96
+ .join("\n");
97
+ return createHash("sha256").update(statements).digest("hex");
98
+ }
99
+
100
+ /**
101
+ * Rejects databases whose schema was created by retired migration files or
102
+ * which have tables but no record of the current baseline.
103
+ */
104
+ export function assertMigrationHistorySupported(
105
+ applied: ReadonlySet<string>,
106
+ expected: readonly string[],
107
+ hasTenants: boolean,
108
+ currentBaselineMarker?: string,
109
+ ): void {
110
+ const supported = new Set(expected);
111
+ const retired = [...applied].filter((migrationId) => !supported.has(migrationId));
112
+ if (retired.length > 0) {
113
+ throw new Error(
114
+ `Database contains retired migration history (${retired.join(", ")}). ` +
115
+ "Create a fresh database and restore from a backup; in-place upgrades are no longer supported.",
116
+ );
117
+ }
118
+ if (hasTenants && !applied.has(BASELINE_MIGRATION_ID)) {
119
+ throw new Error(
120
+ "Database contains an unversioned schema. Create a fresh database and restore from a backup; " +
121
+ "in-place upgrades are no longer supported.",
122
+ );
123
+ }
124
+ // Any marker in `expected` counts, not just the current one: a database built
125
+ // by an older release carries a frozen historical marker for the same schema.
126
+ const acceptedMarker =
127
+ currentBaselineMarker !== undefined &&
128
+ expected.some((id) => id.startsWith(BASELINE_SCHEMA_MARKER_PREFIX) && applied.has(id));
129
+ if (hasTenants && currentBaselineMarker !== undefined && !acceptedMarker) {
130
+ throw new Error(
131
+ "Database was created from a different baseline schema. Create a fresh database and restore from a backup; " +
132
+ "in-place upgrades are no longer supported.",
133
+ );
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Splits a migration file on the drizzle `statement-breakpoint` markers the
139
+ * files already carry. Only the embedded backend needs per-statement
140
+ * application (single-statement failure tolerance); the pg path sends the
141
+ * whole file at once exactly as before.
142
+ */
143
+ export function splitStatements(source: string): string[] {
144
+ return source
145
+ .split(/--> statement-breakpoint/g)
146
+ .map((part) => part.trim())
147
+ .filter((part) => part.length > 0);
148
+ }
149
+
150
+ /**
151
+ * Readiness view of the migration ledger: the numbered SQL files discovered on
152
+ * disk compared against the ids recorded in {@link MIGRATION_LEDGER_TABLE}.
153
+ */
154
+ export interface MigrationLedgerStatus {
155
+ /** Migration file names discovered on disk, in apply order. */
156
+ readonly expected: readonly string[];
157
+ /** Expected migrations that have no ledger row. */
158
+ readonly pending: readonly string[];
159
+ /** True only when every expected migration is recorded in the ledger. */
160
+ readonly applied: boolean;
161
+ }
162
+
163
+ /**
164
+ * Compares the migration files discovered on disk against the applied-migration
165
+ * ledger. A reachable ledger alone is not readiness: an interrupted upgrade
166
+ * leaves the table present while later migrations are still unapplied.
167
+ *
168
+ * Runs on the drizzle handle, so it is identical on every backend.
169
+ */
170
+ export async function readMigrationLedgerStatus(
171
+ db: CartethyiaDatabase,
172
+ folder: string = resolveMigrationsFolder(),
173
+ ): Promise<MigrationLedgerStatus> {
174
+ const expected = migrationFiles(folder).map((file) => migrationIdFor(file));
175
+ const result = await db.execute<{ migration_id: string }>(
176
+ sql.raw(`SELECT migration_id FROM ${MIGRATION_LEDGER_TABLE}`),
177
+ );
178
+ const applied = new Set(result.rows.map((row) => row.migration_id));
179
+ const pending = expected.filter((migrationId) => !applied.has(migrationId));
180
+ return { expected, pending, applied: pending.length === 0 };
181
+ }