cartethyia 2.5.2 → 2.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/dist/dashboard/assets/{BannedPage-MsQaeQTs.js → BannedPage-C2orBsI2.js} +1 -1
  2. package/dist/dashboard/assets/BannedPage-C2orBsI2.js.gz +0 -0
  3. package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js +1 -0
  4. package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolsPage-DfWHTJ7v.js → CliToolsPage-BV6pxVQo.js} +1 -1
  6. package/dist/dashboard/assets/CliToolsPage-BV6pxVQo.js.gz +0 -0
  7. package/dist/dashboard/assets/{CombosPage-BrCBQDAc.js → CombosPage-Bsan4679.js} +1 -1
  8. package/dist/dashboard/assets/CombosPage-Bsan4679.js.gz +0 -0
  9. package/dist/dashboard/assets/{ConsoleLogPage-C6Uh33er.js → ConsoleLogPage-CW-wPmJ6.js} +1 -1
  10. package/dist/dashboard/assets/ConsoleLogPage-CW-wPmJ6.js.gz +0 -0
  11. package/dist/dashboard/assets/{CustomizationPage-8aS9hyJu.js → CustomizationPage-0p8uX7vB.js} +1 -1
  12. package/dist/dashboard/assets/CustomizationPage-0p8uX7vB.js.gz +0 -0
  13. package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js +1 -0
  14. package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js.gz +0 -0
  15. package/dist/dashboard/assets/{LoginPage-C8hKyXBe.js → LoginPage-BvzCx8Kr.js} +1 -1
  16. package/dist/dashboard/assets/LoginPage-BvzCx8Kr.js.gz +0 -0
  17. package/dist/dashboard/assets/{ModelPicker-CWkTUtpX.js → ModelPicker-BY1aKEkP.js} +1 -1
  18. package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js.gz +0 -0
  19. package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js +1 -0
  20. package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js.gz +0 -0
  21. package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js +3 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js.gz +0 -0
  23. package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js +1 -0
  24. package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js.gz +0 -0
  25. package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js +7 -0
  26. package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js.gz +0 -0
  27. package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js +1 -0
  28. package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js.gz +0 -0
  29. package/dist/dashboard/assets/SettingsPage-CS86ho5X.js +1 -0
  30. package/dist/dashboard/assets/SettingsPage-CS86ho5X.js.gz +0 -0
  31. package/dist/dashboard/assets/{SetupPage-CXjUlfqr.js → SetupPage-DDbMQsoo.js} +1 -1
  32. package/dist/dashboard/assets/SetupPage-DDbMQsoo.js.gz +0 -0
  33. package/dist/dashboard/assets/{SortableList-C-_oD3F2.js → SortableList-CB3Z7VTf.js} +1 -1
  34. package/dist/dashboard/assets/SortableList-CB3Z7VTf.js.gz +0 -0
  35. package/dist/dashboard/assets/{StudioPage-u9IxmYOI.js → StudioPage-D95UCPHI.js} +3 -3
  36. package/dist/dashboard/assets/StudioPage-D95UCPHI.js.gz +0 -0
  37. package/dist/dashboard/assets/{UsagePage-BEVLVrwP.js → UsagePage-Cnae9SWA.js} +13 -13
  38. package/dist/dashboard/assets/UsagePage-Cnae9SWA.js.gz +0 -0
  39. package/dist/dashboard/assets/{api-keys-uNywYlfv.js → api-keys-VY68Mhd1.js} +1 -1
  40. package/dist/dashboard/assets/api-keys-VY68Mhd1.js.gz +0 -0
  41. package/dist/dashboard/assets/app-COlK95oi.css +2 -0
  42. package/dist/dashboard/assets/app-COlK95oi.css.gz +0 -0
  43. package/dist/dashboard/assets/{assert-NHwYq95t.js → assert-Chi7pCnt.js} +1 -1
  44. package/dist/dashboard/assets/assert-Chi7pCnt.js.gz +0 -0
  45. package/dist/dashboard/assets/{badge-6uzDDvqs.js → badge-D4pQapKj.js} +1 -1
  46. package/dist/dashboard/assets/badge-D4pQapKj.js.gz +0 -0
  47. package/dist/dashboard/assets/{banner-4_WNg2WZ.js → banner-DH6cg-_B.js} +1 -1
  48. package/dist/dashboard/assets/banner-DH6cg-_B.js.gz +0 -0
  49. package/dist/dashboard/assets/{card--AtFedAq.js → card-BKqlUOe4.js} +1 -1
  50. package/dist/dashboard/assets/card-BKqlUOe4.js.gz +0 -0
  51. package/dist/dashboard/assets/{cli-tools-B3e7dx3N.js → cli-tools-fTgljTGA.js} +1 -1
  52. package/dist/dashboard/assets/cli-tools-fTgljTGA.js.gz +0 -0
  53. package/dist/dashboard/assets/{clipboard-button-NreUx6c8.js → clipboard-button-D5Ys_sM0.js} +1 -1
  54. package/dist/dashboard/assets/clipboard-button-D5Ys_sM0.js.gz +0 -0
  55. package/dist/dashboard/assets/database-Dz9N3zGd.js +1 -0
  56. package/dist/dashboard/assets/database-Dz9N3zGd.js.gz +0 -0
  57. package/dist/dashboard/assets/{dialog-BtpsJU98.js → dialog-BYj-8WuK.js} +1 -1
  58. package/dist/dashboard/assets/dialog-BYj-8WuK.js.gz +0 -0
  59. package/dist/dashboard/assets/{drawer-WwwtzihF.js → drawer-CmXIZ6H1.js} +1 -1
  60. package/dist/dashboard/assets/drawer-CmXIZ6H1.js.gz +0 -0
  61. package/dist/dashboard/assets/entry-CBg5zeu8.js +1 -0
  62. package/dist/dashboard/assets/entry-CBg5zeu8.js.gz +0 -0
  63. package/dist/dashboard/assets/entry-CQBr6U9c.js +1 -0
  64. package/dist/dashboard/assets/entry-CQBr6U9c.js.gz +0 -0
  65. package/dist/dashboard/assets/entry-CgdSAzEV.js +4 -0
  66. package/dist/dashboard/assets/entry-CgdSAzEV.js.gz +0 -0
  67. package/dist/dashboard/assets/{github-badge-D_eM3IgG.js → github-badge-BlptL8Uf.js} +1 -1
  68. package/dist/dashboard/assets/github-badge-BlptL8Uf.js.gz +0 -0
  69. package/dist/dashboard/assets/{index-DCBtROoK.js → index-js6TJvdr.js} +2 -2
  70. package/dist/dashboard/assets/index-js6TJvdr.js.gz +0 -0
  71. package/dist/dashboard/assets/{inline-BcNGF8Gr.js → inline-DUWDSmDV.js} +1 -1
  72. package/dist/dashboard/assets/inline-DUWDSmDV.js.gz +0 -0
  73. package/dist/dashboard/assets/{input-BG41et9G.js → input-LVjgZq4f.js} +1 -1
  74. package/dist/dashboard/assets/input-LVjgZq4f.js.gz +0 -0
  75. package/dist/dashboard/assets/{layout-C8mer_Ak.js → layout-CPfxpsfy.js} +1 -1
  76. package/dist/dashboard/assets/layout-CPfxpsfy.js.gz +0 -0
  77. package/dist/dashboard/assets/{list-row-Gh_W4OR_.js → list-row-DupvLfDx.js} +1 -1
  78. package/dist/dashboard/assets/list-row-DupvLfDx.js.gz +0 -0
  79. package/dist/dashboard/assets/{live-BJGYRBnR.js → live-C4pEJUqI.js} +1 -1
  80. package/dist/dashboard/assets/live-C4pEJUqI.js.gz +0 -0
  81. package/dist/dashboard/assets/model-bans-BRiuufkB.js +1 -0
  82. package/dist/dashboard/assets/model-bans-BRiuufkB.js.gz +0 -0
  83. package/dist/dashboard/assets/{mutation-BkoLM5Ba.js → mutation-B4ROwk8L.js} +1 -1
  84. package/dist/dashboard/assets/mutation-B4ROwk8L.js.gz +0 -0
  85. package/dist/dashboard/assets/numbers-YNcHvRY7.js +2 -0
  86. package/dist/dashboard/assets/numbers-YNcHvRY7.js.gz +0 -0
  87. package/dist/dashboard/assets/{query-keys-D7wX2vM5.js → query-keys-gmjzC3RS.js} +1 -1
  88. package/dist/dashboard/assets/{query-keys-D7wX2vM5.js.gz → query-keys-gmjzC3RS.js.gz} +0 -0
  89. package/dist/dashboard/assets/{routing-CcusqXA7.js → routing-C7tBT5o-.js} +1 -1
  90. package/dist/dashboard/assets/routing-C7tBT5o-.js.gz +0 -0
  91. package/dist/dashboard/assets/{select-DsX5QT8K.js → select-WUCXorXw.js} +2 -2
  92. package/dist/dashboard/assets/select-WUCXorXw.js.gz +0 -0
  93. package/dist/dashboard/assets/{settings-mpmXTgUa.js → settings-C_zKl1GS.js} +1 -1
  94. package/dist/dashboard/assets/settings-C_zKl1GS.js.gz +0 -0
  95. package/dist/dashboard/assets/{stack-DwLmjUFK.js → stack-_3O0F8Xn.js} +1 -1
  96. package/dist/dashboard/assets/stack-_3O0F8Xn.js.gz +0 -0
  97. package/dist/dashboard/assets/{state-DYdQmdAl.js → state-BgTxHcJS.js} +1 -1
  98. package/dist/dashboard/assets/state-BgTxHcJS.js.gz +0 -0
  99. package/dist/dashboard/assets/switch-DEjTtOWf.js +1 -0
  100. package/dist/dashboard/assets/switch-DEjTtOWf.js.gz +0 -0
  101. package/dist/dashboard/assets/terminal-0_IjPWTk.js +1 -0
  102. package/dist/dashboard/assets/terminal-0_IjPWTk.js.gz +0 -0
  103. package/dist/dashboard/assets/{useMutation-w4u4CVue.js → useMutation-BWSC0UAS.js} +1 -1
  104. package/dist/dashboard/assets/useMutation-BWSC0UAS.js.gz +0 -0
  105. package/dist/dashboard/assets/zap-C4EeVW4q.js +1 -0
  106. package/dist/dashboard/assets/zap-C4EeVW4q.js.gz +0 -0
  107. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  108. package/dist/dashboard/index.html +1 -1
  109. package/dist/dashboard/index.html.gz +0 -0
  110. package/dist/dashboard/precompressed-assets.json +54 -54
  111. package/package.json +1 -1
  112. package/scripts/cli/autostart.ts +237 -0
  113. package/scripts/cli/command/console-menu.ts +244 -246
  114. package/scripts/cli/command/tray.ts +208 -204
  115. package/scripts/cli/command/update.ts +27 -10
  116. package/scripts/cli/main.ts +28 -1
  117. package/src/boot/app.ts +693 -693
  118. package/src/boot/dependencies.ts +646 -616
  119. package/src/config/spec.ts +81 -2
  120. package/src/config/telemetry.ts +46 -0
  121. package/src/console/api-keys/bucket-spend.ts +15 -4
  122. package/src/console/backup/contracts.ts +15 -2
  123. package/src/console/backup/routes.ts +52 -1
  124. package/src/console/backup/service.ts +60 -4
  125. package/src/console/console-router.ts +8 -0
  126. package/src/console/data-management/contracts.ts +93 -0
  127. package/src/console/data-management/operations.ts +229 -0
  128. package/src/console/data-management/routes.ts +104 -0
  129. package/src/console/domain-registration.ts +39 -0
  130. package/src/console/observability/contracts.ts +524 -485
  131. package/src/console/observability/store.ts +1115 -1018
  132. package/src/console/providers/catalog/contracts.ts +6 -0
  133. package/src/console/providers/catalog/provider-operations.ts +20 -0
  134. package/src/console/providers/catalog/routes.ts +7 -0
  135. package/src/console/providers/catalog/store.ts +22 -1
  136. package/src/console/quota/account-quota-shared.ts +52 -39
  137. package/src/console/quota/refresh.ts +441 -429
  138. package/src/console/routing/pools/import-parse.ts +206 -0
  139. package/src/console/routing/pools/operations.ts +543 -501
  140. package/src/console/routing/pools/routes.ts +181 -164
  141. package/src/console/settings/contracts.ts +37 -0
  142. package/src/console/settings/store.ts +51 -0
  143. package/src/console/share/share-router.ts +6 -9
  144. package/src/console/updates/routes.ts +16 -0
  145. package/src/main.ts +10 -0
  146. package/src/observability/event-compaction.ts +315 -0
  147. package/src/observability/event-day-index.ts +442 -0
  148. package/src/observability/event-log.ts +152 -3
  149. package/src/observability/payload-capture.ts +373 -344
  150. package/src/observability/payload-store.ts +440 -366
  151. package/src/observability/telemetry-status.ts +28 -16
  152. package/src/persistence/health-event-flush.ts +36 -0
  153. package/src/persistence/schema.ts +1402 -1360
  154. package/src/persistence/stores/api-key-store.ts +6 -0
  155. package/src/persistence/stores/share-store.ts +583 -556
  156. package/src/persistence/stores/telemetry-store.ts +237 -214
  157. package/src/protocol/codecs.ts +1 -2
  158. package/src/protocol/image-clamp.ts +19 -1
  159. package/src/protocol/registry.ts +8 -0
  160. package/src/protocol/request/gemini.ts +9 -2
  161. package/src/protocol/request/messages.ts +14 -2
  162. package/src/protocol/response/gemini.ts +18 -4
  163. package/src/providers/adapters/compatible-adapter.ts +3 -0
  164. package/src/providers/bundled-discovery-providers.ts +122 -119
  165. package/src/providers/configured-openai-providers.ts +136 -134
  166. package/src/providers/default-registry.ts +5 -4
  167. package/src/providers/discovery/openai-model-discovery.ts +5 -0
  168. package/src/providers/discovery/openrouter-free-roster.ts +15 -17
  169. package/src/providers/integrations/antigravity/antigravity-quota.ts +11 -3
  170. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +4 -4
  171. package/src/providers/integrations/buddy/workbuddy/shared.ts +201 -200
  172. package/src/providers/integrations/cline/cline-quota.ts +132 -128
  173. package/src/providers/integrations/codex/codex-headers.ts +25 -39
  174. package/src/providers/integrations/codex/codex.ts +2 -29
  175. package/src/providers/integrations/cursor/cursor-quota.ts +310 -0
  176. package/src/providers/integrations/cursor/cursor-request.ts +616 -471
  177. package/src/providers/integrations/cursor/cursor-session-manager.ts +319 -281
  178. package/src/providers/integrations/cursor/cursor.ts +1485 -960
  179. package/src/providers/integrations/cursor/protocol/messages.ts +25 -0
  180. package/src/providers/integrations/factory/factory.ts +8 -1
  181. package/src/providers/integrations/gemini.ts +6 -1
  182. package/src/providers/integrations/grok/grok-quota.ts +279 -177
  183. package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -0
  184. package/src/providers/integrations/grok/grok-ratelimit.ts +218 -0
  185. package/src/providers/integrations/opencode/provider.ts +531 -532
  186. package/src/providers/integrations/qoder/qoder-quota.ts +192 -0
  187. package/src/providers/integrations/tokenharbor.ts +2 -2
  188. package/src/providers/integrations/trae/trae-catalog.ts +5 -3
  189. package/src/providers/integrations/trae/trae-quota.ts +384 -0
  190. package/src/providers/operations/account-health-service.ts +1523 -1457
  191. package/src/providers/operations/client-versions.ts +9 -2
  192. package/src/providers/provider-contracts.ts +7 -0
  193. package/src/providers/provider-metadata.ts +13 -1
  194. package/src/providers/quota/quota-support.ts +8 -0
  195. package/src/security/api-key-auth.ts +504 -494
  196. package/src/transport/dispatch/attempt-finalize.ts +46 -5
  197. package/src/transport/dispatch/generation.ts +6 -0
  198. package/src/transport/dispatch/proxy-request.ts +33 -1
  199. package/src/transport/dispatch/ratelimit-capture.ts +37 -0
  200. package/src/transport/dispatch/streaming/stream.ts +30 -2
  201. package/src/transport/dispatch/upstream.ts +9 -0
  202. package/src/transport/request/preparer.ts +7 -6
  203. package/src/transport/request/state.ts +9 -0
  204. package/src/transport/request/tenant-preferences.ts +60 -0
  205. package/src/transport/routing/route-catalog.ts +854 -799
  206. package/dist/dashboard/assets/BannedPage-MsQaeQTs.js.gz +0 -0
  207. package/dist/dashboard/assets/CliToolDetailPage-BO9nKg9w.js +0 -1
  208. package/dist/dashboard/assets/CliToolDetailPage-BO9nKg9w.js.gz +0 -0
  209. package/dist/dashboard/assets/CliToolsPage-DfWHTJ7v.js.gz +0 -0
  210. package/dist/dashboard/assets/CombosPage-BrCBQDAc.js.gz +0 -0
  211. package/dist/dashboard/assets/ConsoleLogPage-C6Uh33er.js.gz +0 -0
  212. package/dist/dashboard/assets/CustomizationPage-8aS9hyJu.js.gz +0 -0
  213. package/dist/dashboard/assets/HealthEventsModal-qomXThxX.js +0 -1
  214. package/dist/dashboard/assets/HealthEventsModal-qomXThxX.js.gz +0 -0
  215. package/dist/dashboard/assets/LoginPage-C8hKyXBe.js.gz +0 -0
  216. package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js.gz +0 -0
  217. package/dist/dashboard/assets/OverviewPage-C-ExCy1V.js +0 -1
  218. package/dist/dashboard/assets/OverviewPage-C-ExCy1V.js.gz +0 -0
  219. package/dist/dashboard/assets/ProviderDetailPage-tr811uaF.js +0 -3
  220. package/dist/dashboard/assets/ProviderDetailPage-tr811uaF.js.gz +0 -0
  221. package/dist/dashboard/assets/ProvidersPage-BS9IEpvv.js +0 -1
  222. package/dist/dashboard/assets/ProvidersPage-BS9IEpvv.js.gz +0 -0
  223. package/dist/dashboard/assets/ProxyPage-Rn_qXScb.js +0 -7
  224. package/dist/dashboard/assets/ProxyPage-Rn_qXScb.js.gz +0 -0
  225. package/dist/dashboard/assets/QuotaPage-BihkgQSq.js +0 -1
  226. package/dist/dashboard/assets/QuotaPage-BihkgQSq.js.gz +0 -0
  227. package/dist/dashboard/assets/SettingsPage-DhiEjm6T.js +0 -1
  228. package/dist/dashboard/assets/SettingsPage-DhiEjm6T.js.gz +0 -0
  229. package/dist/dashboard/assets/SetupPage-CXjUlfqr.js.gz +0 -0
  230. package/dist/dashboard/assets/SortableList-C-_oD3F2.js.gz +0 -0
  231. package/dist/dashboard/assets/StudioPage-u9IxmYOI.js.gz +0 -0
  232. package/dist/dashboard/assets/UsagePage-BEVLVrwP.js.gz +0 -0
  233. package/dist/dashboard/assets/api-keys-uNywYlfv.js.gz +0 -0
  234. package/dist/dashboard/assets/app-DxivpfYG.css +0 -2
  235. package/dist/dashboard/assets/app-DxivpfYG.css.gz +0 -0
  236. package/dist/dashboard/assets/assert-NHwYq95t.js.gz +0 -0
  237. package/dist/dashboard/assets/badge-6uzDDvqs.js.gz +0 -0
  238. package/dist/dashboard/assets/banner-4_WNg2WZ.js.gz +0 -0
  239. package/dist/dashboard/assets/card--AtFedAq.js.gz +0 -0
  240. package/dist/dashboard/assets/cli-tools-B3e7dx3N.js.gz +0 -0
  241. package/dist/dashboard/assets/clipboard-button-NreUx6c8.js.gz +0 -0
  242. package/dist/dashboard/assets/dialog-BtpsJU98.js.gz +0 -0
  243. package/dist/dashboard/assets/download-A0SW9OCW.js +0 -1
  244. package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
  245. package/dist/dashboard/assets/drawer-WwwtzihF.js.gz +0 -0
  246. package/dist/dashboard/assets/entry-Dj9AR0gY.js +0 -4
  247. package/dist/dashboard/assets/entry-Dj9AR0gY.js.gz +0 -0
  248. package/dist/dashboard/assets/entry-TJIye_jT.js +0 -1
  249. package/dist/dashboard/assets/entry-TJIye_jT.js.gz +0 -0
  250. package/dist/dashboard/assets/entry-jy9_4-Ke.js +0 -1
  251. package/dist/dashboard/assets/entry-jy9_4-Ke.js.gz +0 -0
  252. package/dist/dashboard/assets/github-badge-D_eM3IgG.js.gz +0 -0
  253. package/dist/dashboard/assets/index-DCBtROoK.js.gz +0 -0
  254. package/dist/dashboard/assets/inline-BcNGF8Gr.js.gz +0 -0
  255. package/dist/dashboard/assets/input-BG41et9G.js.gz +0 -0
  256. package/dist/dashboard/assets/layout-C8mer_Ak.js.gz +0 -0
  257. package/dist/dashboard/assets/list-row-Gh_W4OR_.js.gz +0 -0
  258. package/dist/dashboard/assets/live-BJGYRBnR.js.gz +0 -0
  259. package/dist/dashboard/assets/model-bans-DeVLDA7I.js +0 -1
  260. package/dist/dashboard/assets/model-bans-DeVLDA7I.js.gz +0 -0
  261. package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
  262. package/dist/dashboard/assets/numbers-Bs4LOOuf.js +0 -2
  263. package/dist/dashboard/assets/numbers-Bs4LOOuf.js.gz +0 -0
  264. package/dist/dashboard/assets/routing-CcusqXA7.js.gz +0 -0
  265. package/dist/dashboard/assets/select-DsX5QT8K.js.gz +0 -0
  266. package/dist/dashboard/assets/settings-mpmXTgUa.js.gz +0 -0
  267. package/dist/dashboard/assets/sparkles-BRaRUxAU.js +0 -1
  268. package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
  269. package/dist/dashboard/assets/stack-DwLmjUFK.js.gz +0 -0
  270. package/dist/dashboard/assets/state-DYdQmdAl.js.gz +0 -0
  271. package/dist/dashboard/assets/switch-CidSfbG7.js +0 -1
  272. package/dist/dashboard/assets/switch-CidSfbG7.js.gz +0 -0
  273. package/dist/dashboard/assets/terminal-Bd4ee8TA.js +0 -1
  274. package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
  275. package/dist/dashboard/assets/useMutation-w4u4CVue.js.gz +0 -0
  276. /package/dist/dashboard/assets/{app-CafWtVXA.js → app-DCQZRmHY.js} +0 -0
  277. /package/dist/dashboard/assets/{app-CafWtVXA.js.gz → app-DCQZRmHY.js.gz} +0 -0
@@ -1,204 +1,208 @@
1
- import { isNewerRelease } from "../../../src/console/updates/version-compare";
2
- import { resolvePackagePath } from "../../../src/config/package-paths";
3
- import { resolvePort } from "../../../src/config/runtime";
4
- import { ensureDrainToken } from "../gateway/drain-token";
5
- import { startGateway } from "../gateway/spawn";
6
- import { stopGateway } from "../gateway/stop";
7
- import { readInstalledVersion, readLatestPublishedVersion } from "../support/version";
8
- import { reportTrayStartup } from "../support/startup-signal";
9
- import { createTray } from "../tray";
10
- import { openUrl } from "../ui/background";
11
- import { exportBackup, resetConsoleAccount } from "./admin";
12
- import { printUpdateInstructions } from "./update";
13
-
14
- /**
15
- * Runs the gateway with a tray icon. The tray is platform-specific (`tray/`):
16
- * on Windows it is a PowerShell NotifyIcon, because a Node tray library leaves
17
- * an empty console tab open for the tray's whole lifetime, and that window is
18
- * owned by Windows Terminal — not by this process, so it cannot be found and
19
- * hidden from inside.
20
- *
21
- * The gateway it starts is detached, which is what makes the health poll below
22
- * necessary — see {@link TRAY_HEALTH_POLL_MS}.
23
- */
24
-
25
- /**
26
- * How often the tray asks whether the gateway is still answering.
27
- *
28
- * 10 s: often enough that an operator who stops the gateway by hand sees the
29
- * icon go away, and far below the cost of a request. A detached gateway can no
30
- * longer be waited on, so this poll is the only signal left.
31
- */
32
- const TRAY_HEALTH_POLL_MS = 10_000;
33
-
34
- /**
35
- * Consecutive misses to forgive before a dead gateway closes the tray.
36
- *
37
- * One refusal is not a stopped gateway — a probe can lose a race with startup
38
- * or a slow request — and closing the icon on one would make startup feel
39
- * random.
40
- */
41
- const TRAY_HEALTH_MISSES_BEFORE_CLOSE = 3;
42
-
43
- export async function startTray(): Promise<void> {
44
- const port = resolvePort();
45
- const drainToken = ensureDrainToken();
46
- const dashboardUrl = `http://127.0.0.1:${port}/console`;
47
- const repositoryUrl = "https://github.com/risunCode/cartethyia";
48
- const currentVersion = await readInstalledVersion();
49
-
50
- let gateway: Bun.Subprocess | null = null;
51
- let closing = false;
52
- let busy = false;
53
- let resetArmed = false;
54
- let latestVersion: string | null = null;
55
- let closePromise: Promise<void> | undefined;
56
-
57
- const tray = createTray({
58
- iconPath: resolvePackagePath(
59
- "scripts",
60
- "cli",
61
- "tray",
62
- "assets",
63
- process.platform === "win32" ? "cartethyia.ico" : "cartethyia.png",
64
- ),
65
- tooltip: `Cartethyia ${currentVersion}`,
66
- items: [
67
- { kind: "action", id: "update", title: `Cartethyia ${currentVersion} \u00b7 checking\u2026`, enabled: true },
68
- { kind: "separator" },
69
- { kind: "action", id: "console", title: `Open Console \u00b7 ${dashboardUrl}` },
70
- { kind: "action", id: "reset", title: "Reset console password" },
71
- { kind: "action", id: "backup", title: "Backup" },
72
- { kind: "separator" },
73
- { kind: "action", id: "exit", title: "Exit" },
74
- ],
75
- onSelect: (id: string) => void handleSelect(id),
76
- onExit: () => void close(1),
77
- });
78
-
79
- const close = (exitCode = 0): Promise<void> => {
80
- if (closePromise) return closePromise;
81
- closing = true;
82
- closePromise = (async () => {
83
- const current: Bun.Subprocess | null = gateway;
84
- gateway = null;
85
- if (current) await stopGateway(current, port, drainToken);
86
- await tray.close();
87
- process.exitCode = exitCode;
88
- })();
89
- return closePromise;
90
- };
91
-
92
- const refreshVersion = async (): Promise<void> => {
93
- const fetched = await readLatestPublishedVersion();
94
- if (closing) return;
95
- latestVersion = fetched;
96
- if (fetched === null) {
97
- tray.update("update", `Cartethyia ${currentVersion} \u00b7 check for updates`, true);
98
- return;
99
- }
100
- const newer = isNewerRelease(currentVersion, fetched);
101
- tray.update(
102
- "update",
103
- newer
104
- ? `Cartethyia ${currentVersion} \u00b7 update to ${fetched}`
105
- : `Cartethyia ${currentVersion} \u00b7 up to date`,
106
- // Always clickable: the row opens the repository, and only offers the
107
- // update command when there is something newer.
108
- true,
109
- );
110
- };
111
-
112
- const runUpdate = async (): Promise<void> => {
113
- if (busy) return;
114
- busy = true;
115
- const latest = await readLatestPublishedVersion();
116
- await close();
117
- printUpdateInstructions(currentVersion, latest);
118
- process.exit(0);
119
- };
120
-
121
- async function handleSelect(id: string): Promise<void> {
122
- if (closing) return;
123
- if (id === "exit") return void (await close());
124
- if (id === "console") return void (await openUrl(dashboardUrl));
125
- if (id === "backup") {
126
- const result = await exportBackup(port, drainToken);
127
- if (result !== null) {
128
- console.log(
129
- `Backup written to ${result.path} (${(result.bytes / 1024).toFixed(1)} KiB). Telemetry is not included.`,
130
- );
131
- }
132
- return;
133
- }
134
- if (id === "reset") {
135
- // A tray click cannot ask for the typed confirmation the menu uses, so
136
- // the first click arms the action and the second one runs it.
137
- if (!resetArmed) {
138
- resetArmed = true;
139
- tray.update("reset", "Reset console password \u00b7 click again to delete", true);
140
- return;
141
- }
142
- resetArmed = false;
143
- tray.update("reset", "Reset console password", true);
144
- await resetConsoleAccount(port, drainToken);
145
- return;
146
- }
147
- if (id === "update") {
148
- // Nothing to install: the row is the way to the repository instead.
149
- if (latestVersion === null || !isNewerRelease(currentVersion, latestVersion)) {
150
- await openUrl(repositoryUrl);
151
- return;
152
- }
153
- await runUpdate();
154
- }
155
- }
156
-
157
- process.once("SIGINT", () => void close());
158
- process.once("SIGTERM", () => void close());
159
-
160
- try {
161
- await tray.ready();
162
- // Detached, unlike every other caller: the gateway must outlive this
163
- // process. Attached, it dies with the tray — and the tray dies with the
164
- // terminal that launched it, so closing that terminal took the gateway
165
- // down too and "Hide to tray" left nothing serving. `stopGateway` drains
166
- // over HTTP before it ever reaches for `kill()`, so detaching costs the
167
- // exit path nothing: the tray can still stop the gateway on Exit.
168
- gateway = await startGateway(port, drainToken, { detached: true });
169
- // The launcher that spawned this tray is blocked on a signal file: it
170
- // cannot read our stdio, because the tray is detached. Without this write
171
- // it waits out its 60 s deadline and reports a failure for a tray that is
172
- // running fine.
173
- reportTrayStartup({ status: "ready", url: dashboardUrl });
174
- console.log(`Cartethyia is running at ${dashboardUrl}`);
175
- void refreshVersion();
176
-
177
- let misses = 0;
178
- // A detached child's `exited` never settles on Windows, so the old
179
- // `await current.exited` would have parked here forever and the tray would
180
- // never notice the gateway going away. Poll its health endpoint instead:
181
- // that answers for a process we no longer own, which is the whole point of
182
- // detaching it. The probe is the same one startup used, so a gateway that
183
- // is up reads as up regardless of who spawned it.
184
- while (!closing) {
185
- await Bun.sleep(TRAY_HEALTH_POLL_MS);
186
- if (closing || gateway === null) continue;
187
- const alive = await fetch(`http://127.0.0.1:${port}/health`, {
188
- signal: AbortSignal.timeout(2_000),
189
- }).then((response) => response.ok).catch(() => false);
190
- if (alive) {
191
- misses = 0;
192
- continue;
193
- }
194
- misses += 1;
195
- if (misses < TRAY_HEALTH_MISSES_BEFORE_CLOSE) continue;
196
- gateway = null;
197
- console.error("Cartethyia stopped answering; the tray is closing.");
198
- await close(1);
199
- }
200
- } catch (error) {
201
- await close(1);
202
- throw error;
203
- }
204
- }
1
+ import { isNewerRelease } from "../../../src/console/updates/version-compare";
2
+ import { resolvePackagePath } from "../../../src/config/package-paths";
3
+ import { resolvePort } from "../../../src/config/runtime";
4
+ import { ensureDrainToken } from "../gateway/drain-token";
5
+ import { startGateway } from "../gateway/spawn";
6
+ import { stopGateway } from "../gateway/stop";
7
+ import { readInstalledVersion, readLatestPublishedVersion } from "../support/version";
8
+ import { reportTrayStartup } from "../support/startup-signal";
9
+ import { createTray } from "../tray";
10
+ import { openUrl } from "../ui/background";
11
+ import { exportBackup, resetConsoleAccount } from "./admin";
12
+ import { printUpdateInstructions } from "./update";
13
+
14
+ /**
15
+ * Runs the gateway with a tray icon. The tray is platform-specific (`tray/`):
16
+ * on Windows it is a PowerShell NotifyIcon, because a Node tray library leaves
17
+ * an empty console tab open for the tray's whole lifetime, and that window is
18
+ * owned by Windows Terminal — not by this process, so it cannot be found and
19
+ * hidden from inside.
20
+ *
21
+ * The gateway it starts is detached, which is what makes the health poll below
22
+ * necessary — see {@link TRAY_HEALTH_POLL_MS}.
23
+ */
24
+
25
+ /**
26
+ * How often the tray asks whether the gateway is still answering.
27
+ *
28
+ * 10 s: often enough that an operator who stops the gateway by hand sees the
29
+ * icon go away, and far below the cost of a request. A detached gateway can no
30
+ * longer be waited on, so this poll is the only signal left.
31
+ */
32
+ const TRAY_HEALTH_POLL_MS = 10_000;
33
+
34
+ /**
35
+ * Consecutive misses to forgive before a dead gateway closes the tray.
36
+ *
37
+ * One refusal is not a stopped gateway — a probe can lose a race with startup
38
+ * or a slow request — and closing the icon on one would make startup feel
39
+ * random.
40
+ */
41
+ const TRAY_HEALTH_MISSES_BEFORE_CLOSE = 3;
42
+
43
+ export async function startTray(): Promise<void> {
44
+ const port = resolvePort();
45
+ const drainToken = ensureDrainToken();
46
+ const dashboardUrl = `http://127.0.0.1:${port}/console`;
47
+ const repositoryUrl = "https://github.com/risunCode/cartethyia";
48
+ const currentVersion = await readInstalledVersion();
49
+
50
+ let gateway: Bun.Subprocess | null = null;
51
+ let closing = false;
52
+ let busy = false;
53
+ let resetArmed = false;
54
+ let latestVersion: string | null = null;
55
+ let closePromise: Promise<void> | undefined;
56
+
57
+ const tray = createTray({
58
+ iconPath: resolvePackagePath(
59
+ "scripts",
60
+ "cli",
61
+ "tray",
62
+ "assets",
63
+ process.platform === "win32" ? "cartethyia.ico" : "cartethyia.png",
64
+ ),
65
+ tooltip: `Cartethyia ${currentVersion}`,
66
+ items: [
67
+ { kind: "action", id: "update", title: `Cartethyia ${currentVersion} \u00b7 checking\u2026`, enabled: true },
68
+ { kind: "separator" },
69
+ { kind: "action", id: "console", title: `Open Console \u00b7 ${dashboardUrl}` },
70
+ { kind: "action", id: "reset", title: "Reset console password" },
71
+ { kind: "action", id: "backup", title: "Backup" },
72
+ { kind: "separator" },
73
+ { kind: "action", id: "exit", title: "Exit" },
74
+ ],
75
+ onSelect: (id: string) => void handleSelect(id),
76
+ onExit: () => void close(1),
77
+ });
78
+
79
+ const close = (exitCode = 0): Promise<void> => {
80
+ if (closePromise) return closePromise;
81
+ closing = true;
82
+ closePromise = (async () => {
83
+ const current: Bun.Subprocess | null = gateway;
84
+ gateway = null;
85
+ if (current) await stopGateway(current, port, drainToken);
86
+ await tray.close();
87
+ process.exitCode = exitCode;
88
+ })();
89
+ return closePromise;
90
+ };
91
+
92
+ const refreshVersion = async (): Promise<void> => {
93
+ const fetched = await readLatestPublishedVersion();
94
+ if (closing) return;
95
+ latestVersion = fetched;
96
+ if (fetched === null) {
97
+ tray.update("update", `Cartethyia ${currentVersion} \u00b7 check for updates`, true);
98
+ return;
99
+ }
100
+ const newer = isNewerRelease(currentVersion, fetched);
101
+ tray.update(
102
+ "update",
103
+ newer
104
+ ? `Cartethyia ${currentVersion} \u00b7 update to ${fetched}`
105
+ : `Cartethyia ${currentVersion} \u00b7 up to date`,
106
+ // Always clickable: the row opens the repository, and only offers the
107
+ // update command when there is something newer.
108
+ true,
109
+ );
110
+ };
111
+
112
+ const runUpdate = async (): Promise<void> => {
113
+ if (busy) return;
114
+ busy = true;
115
+ const latest = await readLatestPublishedVersion();
116
+ await close();
117
+ printUpdateInstructions(currentVersion, latest);
118
+ // The tray has no terminal of its own, so these instructions are only
119
+ // visible when it was started from one. Hold that terminal open long
120
+ // enough to copy the command, then exit.
121
+ await new Promise<void>((resolve) => setTimeout(resolve, 180_000));
122
+ process.exit(0);
123
+ };
124
+
125
+ async function handleSelect(id: string): Promise<void> {
126
+ if (closing) return;
127
+ if (id === "exit") return void (await close());
128
+ if (id === "console") return void (await openUrl(dashboardUrl));
129
+ if (id === "backup") {
130
+ const result = await exportBackup(port, drainToken);
131
+ if (result !== null) {
132
+ console.log(
133
+ `Backup written to ${result.path} (${(result.bytes / 1024).toFixed(1)} KiB). Telemetry is not included.`,
134
+ );
135
+ }
136
+ return;
137
+ }
138
+ if (id === "reset") {
139
+ // A tray click cannot ask for the typed confirmation the menu uses, so
140
+ // the first click arms the action and the second one runs it.
141
+ if (!resetArmed) {
142
+ resetArmed = true;
143
+ tray.update("reset", "Reset console password \u00b7 click again to delete", true);
144
+ return;
145
+ }
146
+ resetArmed = false;
147
+ tray.update("reset", "Reset console password", true);
148
+ await resetConsoleAccount(port, drainToken);
149
+ return;
150
+ }
151
+ if (id === "update") {
152
+ // Nothing to install: the row is the way to the repository instead.
153
+ if (latestVersion === null || !isNewerRelease(currentVersion, latestVersion)) {
154
+ await openUrl(repositoryUrl);
155
+ return;
156
+ }
157
+ await runUpdate();
158
+ }
159
+ }
160
+
161
+ process.once("SIGINT", () => void close());
162
+ process.once("SIGTERM", () => void close());
163
+
164
+ try {
165
+ await tray.ready();
166
+ // Detached, unlike every other caller: the gateway must outlive this
167
+ // process. Attached, it dies with the tray — and the tray dies with the
168
+ // terminal that launched it, so closing that terminal took the gateway
169
+ // down too and "Hide to tray" left nothing serving. `stopGateway` drains
170
+ // over HTTP before it ever reaches for `kill()`, so detaching costs the
171
+ // exit path nothing: the tray can still stop the gateway on Exit.
172
+ gateway = await startGateway(port, drainToken, { detached: true });
173
+ // The launcher that spawned this tray is blocked on a signal file: it
174
+ // cannot read our stdio, because the tray is detached. Without this write
175
+ // it waits out its 60 s deadline and reports a failure for a tray that is
176
+ // running fine.
177
+ reportTrayStartup({ status: "ready", url: dashboardUrl });
178
+ console.log(`Cartethyia is running at ${dashboardUrl}`);
179
+ void refreshVersion();
180
+
181
+ let misses = 0;
182
+ // A detached child's `exited` never settles on Windows, so the old
183
+ // `await current.exited` would have parked here forever and the tray would
184
+ // never notice the gateway going away. Poll its health endpoint instead:
185
+ // that answers for a process we no longer own, which is the whole point of
186
+ // detaching it. The probe is the same one startup used, so a gateway that
187
+ // is up reads as up regardless of who spawned it.
188
+ while (!closing) {
189
+ await Bun.sleep(TRAY_HEALTH_POLL_MS);
190
+ if (closing || gateway === null) continue;
191
+ const alive = await fetch(`http://127.0.0.1:${port}/health`, {
192
+ signal: AbortSignal.timeout(2_000),
193
+ }).then((response) => response.ok).catch(() => false);
194
+ if (alive) {
195
+ misses = 0;
196
+ continue;
197
+ }
198
+ misses += 1;
199
+ if (misses < TRAY_HEALTH_MISSES_BEFORE_CLOSE) continue;
200
+ gateway = null;
201
+ console.error("Cartethyia stopped answering; the tray is closing.");
202
+ await close(1);
203
+ }
204
+ } catch (error) {
205
+ await close(1);
206
+ throw error;
207
+ }
208
+ }
@@ -1,23 +1,40 @@
1
1
  /**
2
- * The update path, which prints a command rather than running one.
2
+ * The update path.
3
3
  *
4
4
  * npm cannot replace this package's directory while a process is executing out
5
- * of it: on Windows the rename fails with EBUSY and npm exits 14. Stopping the
6
- * gateway is not enough either, because the CLI or tray itself is the process
7
- * holding the directory. So every caller stops, prints the command, and exits —
8
- * the same shape 9router uses.
5
+ * of it (EBUSY on Windows). So we spawn the installer as a detached process,
6
+ * print a short notice, and exit — the installer runs after this process is gone.
7
+ * After install, it attempts to restart the gateway (systemd if available, else
8
+ * `cartethyia start`), so the new version comes up automatically.
9
+ * Cross-OS: uses npm.cmd on Windows.
9
10
  */
11
+ import { spawn } from "node:child_process";
12
+
10
13
  export function updateCommand(): string {
11
14
  return "npm install -g cartethyia@latest --prefer-online";
12
15
  }
13
16
 
14
- /** Prints the "run this now" block after Cartethyia has stopped. */
17
+ export function runDetachedUpdate(): void {
18
+ const isWin = process.platform === "win32";
19
+ const shell = isWin ? "cmd.exe" : "/bin/sh";
20
+ const install = "npm install -g cartethyia@latest --prefer-online";
21
+ const restart = isWin
22
+ ? "cartethyia start"
23
+ : "systemctl restart cartethyia 2>/dev/null || cartethyia start";
24
+ const script = `${install} && ${restart}`;
25
+ const args = isWin ? ["/c", script] : ["-c", script];
26
+ const child = spawn(shell, args, {
27
+ detached: true,
28
+ stdio: "ignore",
29
+ });
30
+ child.unref();
31
+ }
32
+
15
33
  export function printUpdateInstructions(from: string, to: string | null): void {
16
34
  console.log("");
17
35
  console.log(`Update available: ${from} \u2192 ${to ?? "latest"}`);
18
36
  console.log("");
19
- console.log("Run this now that Cartethyia has stopped:");
37
+ console.log("Starting detached update; this process will exit now.");
38
+ console.log("The gateway will restart automatically after the install.");
20
39
  console.log("");
21
- console.log(` ${updateCommand()}`);
22
- console.log("");
23
- }
40
+ }
@@ -2,7 +2,7 @@ import { mkdirSync } from "node:fs";
2
2
  import { resolveUserConfigDir } from "../../src/config/package-paths";
3
3
  import { ENV_PATH, readEnvFile } from "./support/env";
4
4
  import { MINIMUM_BUN_VERSION, assertBunVersion } from "./support/bun";
5
- import { printUpdateInstructions } from "./command/update";
5
+ import { printUpdateInstructions, runDetachedUpdate } from "./command/update";
6
6
  import { reportTrayStartup } from "./support/startup-signal";
7
7
 
8
8
  process.env.CARTETHYIA_CONFIG_DIR ??= resolveUserConfigDir();
@@ -67,12 +67,39 @@ async function main(): Promise<void> {
67
67
  await startTray();
68
68
  return;
69
69
  }
70
+ case "autostart": {
71
+ const { enableAutostart, disableAutostart, autostartStatus } = await import("./autostart");
72
+ const arg = Bun.argv[3]?.trim();
73
+ if (arg === "off" || arg === "disable") {
74
+ const state = await disableAutostart();
75
+ console.log(`Autostart disabled (${state.location || state.platform}).`);
76
+ return;
77
+ }
78
+ if (arg === "status") {
79
+ const state = autostartStatus();
80
+ console.log(
81
+ `Autostart: ${state.enabled ? "enabled" : "not enabled"} — ${state.location || state.platform}` +
82
+ (state.reason ? ` (${state.reason})` : ""),
83
+ );
84
+ return;
85
+ }
86
+ // The tray is what autostart launches: a gateway alone has no session to
87
+ // attach to and no icon to show.
88
+ const state = await enableAutostart(process.execPath, [Bun.argv[1] ?? "carte", "tray"]);
89
+ console.log(
90
+ state.enabled
91
+ ? `Autostart enabled — ${state.location}`
92
+ : `Autostart not enabled: ${state.reason ?? "unknown reason"}`,
93
+ );
94
+ return;
95
+ }
70
96
  case "doctor": {
71
97
  const { doctor } = await import("./command/doctor");
72
98
  await doctor();
73
99
  return;
74
100
  }
75
101
  case "update":
102
+ runDetachedUpdate();
76
103
  printUpdateInstructions("this install", null);
77
104
  return;
78
105
  case "--help":