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
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "cartethyia",
3
- "version": "2.4.5",
3
+ "version": "2.5.0",
4
4
  "description": "A powerful, self-hosted reverse proxy for OpenAI- and Anthropic-compatible AI clients, powered by native Bun and Elysia. It serves both protocols from a single server, routes every model across any configured provider, and translates requests and responses in real time — streaming and prompt caching included. Underneath it runs a full delivery engine: per-account health tracking, quota and cooldown enforcement, automatic failover across pools and providers, usage accounting, and telemetry. Point OpenCode, Cline, Claude Code, Codex, or any compatible client at Cartethyia and it keeps its native protocol while the gateway absorbs every provider-specific difference behind it. Runs on embedded SQLite or external PostgreSQL with optional Redis coordination, ships as a global CLI with a system-tray console, and needs only Bun 1.4+ alongside Node.js/npm.",
5
5
  "license": "GPL-3.0-only",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.4.0",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/risunCode/Cartethyia.git"
10
+ "url": "git+https://github.com/risunCodex/Cartethyia.git"
11
11
  },
12
- "homepage": "https://github.com/risunCode/Cartethyia#readme",
12
+ "homepage": "https://github.com/risunCodex/Cartethyia#readme",
13
13
  "bugs": {
14
- "url": "https://github.com/risunCode/Cartethyia/issues"
14
+ "url": "https://github.com/risunCodex/Cartethyia/issues"
15
15
  },
16
16
  "keywords": [
17
17
  "ai",
@@ -21,18 +21,14 @@
21
21
  "self-hosted"
22
22
  ],
23
23
  "bin": {
24
- "cartethyia": "scripts/cli/cli-launcher.cjs",
25
- "carte": "scripts/cli/carte-launcher.cjs"
24
+ "cartethyia": "scripts/cli/bin/cartethyia.cjs",
25
+ "carte": "scripts/cli/bin/carte.cjs"
26
26
  },
27
27
  "files": [
28
28
  "scripts/cli/",
29
- "scripts/operations/probe-egress.ts",
30
- "scripts/operations/restart.ts",
31
- "scripts/shared/env.ts",
32
29
  "src/",
33
30
  "migrations/",
34
31
  "dist/dashboard/",
35
- "tray-assets/",
36
32
  ".env.example",
37
33
  "README.md"
38
34
  ],
@@ -45,48 +41,47 @@
45
41
  "scripts": {
46
42
  "dev": "bun run dev:stack",
47
43
  "dev:watch": "bun run scripts/dev/supervisor.ts",
48
- "test": "bun run scripts/test/run.ts",
49
- "build": "bun run scripts/build/build.ts",
44
+ "test": "bun run scripts/ci/run.ts",
45
+ "build": "bun run scripts/binary/build.ts",
50
46
  "prepack": "bun run dashboard:build",
51
- "setup": "bun run scripts/cli/setup.ts",
52
- "start": "bun run scripts/operations/start-production.ts",
53
- "doctor": "bun run scripts/cli/doctor.ts",
47
+ "setup": "bun run scripts/cli/command/setup.ts",
48
+ "start": "bun run scripts/cli/main.ts start",
49
+ "doctor": "bun run scripts/cli/command/doctor.ts",
54
50
  "probe:egress": "bun run scripts/operations/probe-egress.ts",
55
- "restart": "bun run scripts/operations/restart.ts",
56
- "build:aot": "bun run scripts/build/aot.ts",
51
+ "restart": "bun run scripts/binary/restart.ts",
52
+ "build:aot": "bun run scripts/binary/aot.ts",
57
53
  "dev:stack": "concurrently -n BACKEND,DASHBOARD -c cyan,magenta --kill-others-on-fail \"bun run dev:backend\" \"bun run dashboard:dev\"",
58
54
  "typecheck": "tsc --noEmit",
59
- "test:watch": "bun run scripts/test/run.ts --watch",
55
+ "test:watch": "bun run scripts/ci/run.ts --watch",
60
56
  "test-db:up": "docker compose -f docker-compose.test.yml up -d --wait postgres-test",
61
57
  "dev:backend": "bun run --hot src/main.ts",
62
- "dev:console-auth": "bun run scripts/dev/console-session.ts",
63
- "dev:visual-baseline": "bun run scripts/dev/visual-baseline.ts",
64
- "build:binary": "bun run scripts/build/binary.ts",
65
- "test:backend": "bun run scripts/test/run.ts --scope backend",
58
+ "dev:console-auth": "bun run scripts/qa/console-session.ts",
59
+ "dev:visual-baseline": "bun run scripts/qa/visual-baseline.ts",
60
+ "build:binary": "bun run scripts/binary/compile.ts",
61
+ "test:backend": "bun run scripts/ci/run.ts --scope backend",
66
62
  "test-db:down": "docker compose -f docker-compose.test.yml down",
67
63
  "dashboard:dev": "bun run --cwd dashboard dev",
68
- "test-db:check": "bun run scripts/test/db-check.ts",
69
- "dashboard:test": "bun run scripts/test/run.ts --scope dashboard",
64
+ "test-db:check": "bun run scripts/ci/db-check.ts",
65
+ "dashboard:test": "bun run scripts/ci/run.ts --scope dashboard",
70
66
  "test-db:reset": "docker compose -f docker-compose.test.yml down -v && docker compose -f docker-compose.test.yml up -d --wait postgres-test",
71
67
  "dashboard:build": "bun run --cwd dashboard build",
72
- "dashboard:typecheck": "bun run --cwd dashboard typecheck"
68
+ "dashboard:typecheck": "bun run --cwd dashboard typecheck",
69
+ "check:migrations": "bun run scripts/ci/check-migrations.ts"
73
70
  },
74
71
  "dependencies": {
75
- "@bufbuild/protobuf": "^2.14.1",
76
- "@electric-sql/pglite": "^0.5.8",
77
72
  "drizzle-orm": "0.45.2",
78
73
  "elysia": "^2.0.0-beta.16",
79
74
  "enquirer": "^2.4.1",
80
- "exact-mirror": "^1.2.6",
81
- "impit": "^0.14.5",
82
75
  "ioredis": "6.0.0",
76
+ "jose": "^6.2.12",
77
+ "jsonrepair": "^3.15.0",
83
78
  "pg": "8.23.0",
84
79
  "pino": "^10.3.1",
85
80
  "pino-pretty": "^13.1.3",
81
+ "protobufjs": "^7.6.5",
86
82
  "socks-proxy-agent": "^10.1.0",
87
83
  "systray2": "2.1.4",
88
84
  "typebox": "1.3.27",
89
- "undici": "^8.10.2",
90
85
  "ws": "8.22.0"
91
86
  },
92
87
  "devDependencies": {
@@ -94,6 +89,7 @@
94
89
  "@types/ws": "8.18.1",
95
90
  "bun-types": "1.4.0",
96
91
  "concurrently": "^10.0.5",
92
+ "drizzle-kit": "^0.31.11",
97
93
  "typescript": "5.9.2"
98
94
  }
99
95
  }
@@ -1,8 +1,8 @@
1
- #!/usr/bin/env node
2
-
3
- // Default to the arrow-key menu so `carte` works without a GUI; on Windows
4
- // especially, the tray icon can land in the hidden-icons overflow and never
5
- // be seen. `carte --tray` still opts into the system-tray icon.
6
- if (process.argv.length <= 2) process.argv.push("menu");
7
- else if (process.argv[2] === "--tray") process.argv[2] = "tray";
8
- require("./cli-launcher.cjs");
1
+ #!/usr/bin/env node
2
+
3
+ // Default to the arrow-key menu so `carte` works without a GUI; on Windows
4
+ // especially, the tray icon can land in the hidden-icons overflow and never
5
+ // be seen. `carte --tray` still opts into the system-tray icon.
6
+ if (process.argv.length <= 2) process.argv.push("menu");
7
+ else if (process.argv[2] === "--tray") process.argv[2] = "tray";
8
+ require("./cartethyia.cjs");
@@ -1,134 +1,167 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
-
4
- const fs = require("node:fs");
5
- const { randomUUID } = require("node:crypto");
6
- const os = require("node:os");
7
- const path = require("node:path");
8
- const { spawn } = require("node:child_process");
9
-
10
- const packageRoot = path.resolve(__dirname, "..", "..");
11
- const cliPath = path.join(packageRoot, "scripts", "cli", "cli.ts");
12
- const configDir = process.env.CARTETHYIA_CONFIG_DIR || (
13
- process.platform === "win32"
14
- ? path.join(process.env.APPDATA || os.homedir(), "Cartethyia")
15
- : process.platform === "darwin"
16
- ? path.join(os.homedir(), "Library", "Application Support", "Cartethyia")
17
- : path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "Cartethyia")
18
- );
19
- const trayMode = process.argv[2] === "tray";
20
- const startupSignalPath = trayMode
21
- ? path.join(os.tmpdir(), `cartethyia-tray-${process.pid}-${randomUUID()}.json`)
22
- : null;
23
- let spawnError;
24
- // On Windows, `cmd /c start "" /b` gives bun its own console group so the
25
- // launching terminal can close without signalling the gateway. Elsewhere
26
- // `detached: true` is enough. Either way the child is unref'd below so the
27
- // launcher can exit without taking the gateway down with it.
28
- const useWindowsDetach = process.platform === "win32" && trayMode;
29
- const child = useWindowsDetach
30
- ? spawn("cmd", ["/c", "start", "", "/b", "bun", "run", cliPath, ...process.argv.slice(2)], {
31
- cwd: packageRoot,
32
- detached: true,
33
- stdio: "ignore",
34
- windowsHide: true,
35
- env: {
36
- ...process.env,
37
- CARTETHYIA_CONFIG_DIR: configDir,
38
- CARTETHYIA_TRAY_STARTUP_SIGNAL: startupSignalPath,
39
- },
40
- })
41
- : spawn("bun", ["run", cliPath, ...process.argv.slice(2)], {
42
- cwd: packageRoot,
43
- detached: trayMode,
44
- stdio: trayMode ? "ignore" : "inherit",
45
- windowsHide: trayMode,
46
- env: {
47
- ...process.env,
48
- CARTETHYIA_CONFIG_DIR: configDir,
49
- ...(trayMode ? { CARTETHYIA_TRAY_STARTUP_SIGNAL: startupSignalPath } : {}),
50
- },
51
- });
52
-
53
- child.once("error", (error) => {
54
- spawnError = error;
55
- if (error.code === "ENOENT") {
56
- console.error("Cartethyia requires Bun 1.4.0 or newer. Install Bun from https://bun.sh, then run this command again.");
57
- } else {
58
- console.error(`Could not start Cartethyia's Bun runtime: ${error.message}`);
59
- }
60
- process.exitCode = 1;
61
- });
62
-
63
- if (trayMode) {
64
- child.unref();
65
- console.log("Loading Cartethyia…");
66
- let settled = false;
67
- const deadline = Date.now() + 60_000;
68
- const finish = (error, url) => {
69
- if (settled) return;
70
- settled = true;
71
- try {
72
- fs.unlinkSync(startupSignalPath);
73
- } catch {}
74
- if (error) {
75
- // `error.message` can be an empty string when the failure came from a
76
- // signal rather than a thrown message, which printed as a bare
77
- // "Cartethyia failed to start:" with no cause. Fall back to the code and
78
- // name what usually causes it.
79
- const detail =
80
- error.message && error.message.trim() !== ""
81
- ? error.message
82
- : `the process exited without a message (code ${child.exitCode ?? "unknown"})`;
83
- console.error(`Cartethyia failed to start: ${detail}`);
84
- console.error(
85
- "If another Cartethyia is already running on this port, stop it with `cartethyia stop` first.",
86
- );
87
- process.exitCode = 1;
88
- if (child.exitCode === null) child.kill();
89
- return;
90
- }
91
- console.log(`Cartethyia is ready at ${url}`);
92
- // The gateway child is already detached and unref'd; this Node process is
93
- // the only thing still holding the launching terminal open. Exit once the
94
- // URL has been shown so the shell prompt comes back.
95
- setTimeout(() => process.exit(0), 1_500);
96
- };
97
- const poll = () => {
98
- if (settled) return;
99
- if (fs.existsSync(startupSignalPath)) {
100
- try {
101
- const signal = JSON.parse(fs.readFileSync(startupSignalPath, "utf8"));
102
- if (signal.status === "ready" && typeof signal.url === "string") {
103
- finish(null, signal.url);
104
- return;
105
- }
106
- if (signal.status === "failed" && typeof signal.message === "string") {
107
- finish(new Error(signal.message));
108
- return;
109
- }
110
- } catch {}
111
- }
112
- if (spawnError) {
113
- finish(spawnError);
114
- return;
115
- }
116
- if (Date.now() >= deadline) {
117
- finish(new Error("startup did not complete within 60 seconds"));
118
- return;
119
- }
120
- setTimeout(poll, 150);
121
- };
122
- child.once("exit", (code, signal) => {
123
- if (!settled) setTimeout(() => finish(new Error(`the tray process exited before readiness (code ${code ?? signal ?? "unknown"})`)), 250);
124
- });
125
- poll();
126
- } else {
127
- child.once("exit", (code, signal) => {
128
- if (signal) {
129
- process.kill(process.pid, signal);
130
- return;
131
- }
132
- process.exitCode = code ?? 1;
133
- });
134
- }
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("node:fs");
5
+ const { randomUUID } = require("node:crypto");
6
+ const os = require("node:os");
7
+ const path = require("node:path");
8
+ const { spawn } = require("node:child_process");
9
+
10
+ const packageRoot = path.resolve(__dirname, "..", "..", "..");
11
+ const cliPath = path.join(packageRoot, "scripts", "cli", "main.ts");
12
+ const configDir = process.env.CARTETHYIA_CONFIG_DIR || (
13
+ process.platform === "win32"
14
+ ? path.join(process.env.APPDATA || os.homedir(), "Cartethyia")
15
+ : process.platform === "darwin"
16
+ ? path.join(os.homedir(), "Library", "Application Support", "Cartethyia")
17
+ : path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "Cartethyia")
18
+ );
19
+ const trayMode = process.argv[2] === "tray";
20
+ // The launcher itself runs under Node, so `process.execPath` is node.exe — not
21
+ // the Bun the child needs. Resolve bun from PATH instead; WScript.Shell does
22
+ // not inherit this process's environment, so the child needs an absolute path.
23
+ function resolveBunPath() {
24
+ if (process.env.CARTETHYIA_BUN) return process.env.CARTETHYIA_BUN;
25
+ const name = process.platform === "win32" ? "bun.exe" : "bun";
26
+ for (const dir of (process.env.PATH || "").split(path.delimiter)) {
27
+ if (!dir) continue;
28
+ const candidate = path.join(dir, name);
29
+ try {
30
+ if (fs.statSync(candidate).isFile()) return candidate;
31
+ } catch {}
32
+ }
33
+ return name;
34
+ }
35
+
36
+ const startupSignalPath = trayMode
37
+ ? path.join(os.tmpdir(), `cartethyia-tray-${process.pid}-${randomUUID()}.json`)
38
+ : null;
39
+ let spawnError;
40
+ // On Windows the tray must start with no console at all, and neither
41
+ // `detached: true` nor `windowsHide: true` achieves that: a console-subsystem
42
+ // child of a window-less parent still allocates a console, and inside Windows
43
+ // Terminal the tab is already on screen before a flag can take effect
44
+ // (measured). `wscript.exe` is GUI-subsystem, so it never allocates one, and a
45
+ // child launched from it with window style 0 inherits that. Elsewhere
46
+ // `detached: true` is enough.
47
+ const useWindowsDetach = process.platform === "win32" && trayMode;
48
+ const hiddenLauncher = path.join(packageRoot, "scripts", "cli", "tray", "hidden-window.vbs");
49
+ const child = useWindowsDetach
50
+ ? spawn(
51
+ "wscript.exe",
52
+ // Absolute path, not "bun": the VBS runs through WScript.Shell, which
53
+ // does not inherit this process's PATH additions and resolves a bare
54
+ // name against the system PATH only.
55
+ ["//nologo", hiddenLauncher, resolveBunPath(), "run", cliPath, ...process.argv.slice(2)],
56
+ {
57
+ cwd: packageRoot,
58
+ detached: true,
59
+ stdio: "ignore",
60
+ windowsHide: true,
61
+ env: {
62
+ ...process.env,
63
+ CARTETHYIA_CONFIG_DIR: configDir,
64
+ CARTETHYIA_TRAY_STARTUP_SIGNAL: startupSignalPath,
65
+ },
66
+ },
67
+ )
68
+ : spawn(resolveBunPath(), ["run", cliPath, ...process.argv.slice(2)], {
69
+ cwd: packageRoot,
70
+ detached: trayMode,
71
+ stdio: trayMode ? "ignore" : "inherit",
72
+ windowsHide: trayMode,
73
+ env: {
74
+ ...process.env,
75
+ CARTETHYIA_CONFIG_DIR: configDir,
76
+ ...(trayMode ? { CARTETHYIA_TRAY_STARTUP_SIGNAL: startupSignalPath } : {}),
77
+ },
78
+ });
79
+
80
+ child.once("error", (error) => {
81
+ spawnError = error;
82
+ if (error.code === "ENOENT") {
83
+ console.error("Cartethyia requires Bun 1.4.0 or newer. Install Bun from https://bun.sh, then run this command again.");
84
+ } else {
85
+ console.error(`Could not start Cartethyia's Bun runtime: ${error.message}`);
86
+ }
87
+ process.exitCode = 1;
88
+ });
89
+
90
+ if (trayMode) {
91
+ child.unref();
92
+ console.log("Loading Cartethyia…");
93
+ let settled = false;
94
+ const deadline = Date.now() + 60_000;
95
+ const finish = (error, url) => {
96
+ if (settled) return;
97
+ settled = true;
98
+ try {
99
+ fs.unlinkSync(startupSignalPath);
100
+ } catch {}
101
+ if (error) {
102
+ // `error.message` can be an empty string when the failure came from a
103
+ // signal rather than a thrown message, which printed as a bare
104
+ // "Cartethyia failed to start:" with no cause. Fall back to the code and
105
+ // name what usually causes it.
106
+ const detail =
107
+ error.message && error.message.trim() !== ""
108
+ ? error.message
109
+ : `the process exited without a message (code ${child.exitCode ?? "unknown"})`;
110
+ console.error(`Cartethyia failed to start: ${detail}`);
111
+ console.error(
112
+ "If another Cartethyia is already running on this port, stop the terminal that started it first.",
113
+ );
114
+ process.exitCode = 1;
115
+ if (child.exitCode === null) child.kill();
116
+ return;
117
+ }
118
+ console.log(`Cartethyia is ready at ${url}`);
119
+ // The gateway child is already detached and unref'd; this Node process is
120
+ // the only thing still holding the launching terminal open. Exit once the
121
+ // URL has been shown so the shell prompt comes back.
122
+ setTimeout(() => process.exit(0), 1_500);
123
+ };
124
+ const poll = () => {
125
+ if (settled) return;
126
+ if (fs.existsSync(startupSignalPath)) {
127
+ try {
128
+ const signal = JSON.parse(fs.readFileSync(startupSignalPath, "utf8"));
129
+ if (signal.status === "ready" && typeof signal.url === "string") {
130
+ finish(null, signal.url);
131
+ return;
132
+ }
133
+ if (signal.status === "failed" && typeof signal.message === "string") {
134
+ finish(new Error(signal.message));
135
+ return;
136
+ }
137
+ } catch {}
138
+ }
139
+ if (spawnError) {
140
+ finish(spawnError);
141
+ return;
142
+ }
143
+ if (Date.now() >= deadline) {
144
+ finish(new Error("startup did not complete within 60 seconds"));
145
+ return;
146
+ }
147
+ setTimeout(poll, 150);
148
+ };
149
+ // On Windows the spawned process is `wscript.exe`, which hands off to the
150
+ // tray and exits immediately — a successful handoff, not a failed start. Only
151
+ // a direct spawn's exit says anything about the tray, so the deadline covers
152
+ // the handoff case and this guard covers the direct one.
153
+ if (!useWindowsDetach) {
154
+ child.once("exit", (code, signal) => {
155
+ if (!settled) setTimeout(() => finish(new Error(`the tray process exited before readiness (code ${code ?? signal ?? "unknown"})`)), 250);
156
+ });
157
+ }
158
+ poll();
159
+ } else {
160
+ child.once("exit", (code, signal) => {
161
+ if (signal) {
162
+ process.kill(process.pid, signal);
163
+ return;
164
+ }
165
+ process.exitCode = code ?? 1;
166
+ });
167
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The two admin actions the menu and the tray both offer.
3
+ *
4
+ * Both go through the running gateway rather than the database file: the
5
+ * gateway holds the connection and the schema is dialect-dependent, so reading
6
+ * or writing the tables from this process would need a second implementation
7
+ * of both.
8
+ */
9
+
10
+ /**
11
+ * Asks the gateway to write a gzipped config backup into the download
12
+ * directory. Returns the written path and counts, or null when it failed.
13
+ */
14
+ export async function exportBackup(
15
+ port: number,
16
+ token: string,
17
+ ): Promise<{ path: string; bytes: number; counts: Record<string, number> } | null> {
18
+ try {
19
+ const response = await fetch(`http://127.0.0.1:${port}/admin/export-backup`, {
20
+ method: "POST",
21
+ headers: { "content-type": "application/json", "x-drain-token": token },
22
+ body: JSON.stringify({}),
23
+ signal: AbortSignal.timeout(60_000),
24
+ });
25
+ if (!response.ok) {
26
+ const detail = await response.text().catch(() => "");
27
+ console.error(`The gateway refused the backup (HTTP ${response.status}). ${detail}`.trim());
28
+ return null;
29
+ }
30
+ return (await response.json()) as {
31
+ path: string;
32
+ bytes: number;
33
+ counts: Record<string, number>;
34
+ };
35
+ } catch {
36
+ console.error("Could not reach the gateway. Is it still running?");
37
+ return null;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Posts the console-account reset to the running gateway.
43
+ *
44
+ * Returns 0 on success and 1 on any failure so the caller can print one line
45
+ * either way.
46
+ */
47
+ export async function resetConsoleAccount(port: number, token: string): Promise<number> {
48
+ try {
49
+ const response = await fetch(`http://127.0.0.1:${port}/admin/reset-console-account`, {
50
+ method: "POST",
51
+ headers: { "content-type": "application/json", "x-drain-token": token },
52
+ body: JSON.stringify({ confirm: "RESET" }),
53
+ signal: AbortSignal.timeout(10_000),
54
+ });
55
+ if (!response.ok) {
56
+ const detail = await response.text().catch(() => "");
57
+ console.error(`The gateway refused the reset (HTTP ${response.status}). ${detail}`.trim());
58
+ return 1;
59
+ }
60
+ return 0;
61
+ } catch {
62
+ console.error("Could not reach the gateway. Is it still running?");
63
+ return 1;
64
+ }
65
+ }