cartethyia 2.5.10 → 2.6.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 (367) hide show
  1. package/.env.example +8 -0
  2. package/dist/dashboard/assets/{BannedPage-Cd30Z0b9.js → BannedPage-l6XMnfr9.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-wf5u-ww6.js → CliToolDetailPage-DwOzAW45.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-DykPvuck.js → CliToolsPage-BT3Up3QA.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-DwvSD3vs.js → CombosPage-6kYIUpzR.js} +1 -1
  9. package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-CIr7KDex.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-82X0DDdH.js → CustomizationPage-C7m-H1Rz.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
  14. package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
  15. package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
  16. package/dist/dashboard/assets/{LoginPage-CS5mPuVF.js → LoginPage-CbphLvjg.js} +1 -1
  17. package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
  18. package/dist/dashboard/assets/{ModelPicker-C0UCUsVJ.js → ModelPicker-DVny4S5_.js} +1 -1
  19. package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
  20. package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
  21. package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
  24. package/dist/dashboard/assets/{ProvidersPage-vdja5ZPK.js → ProvidersPage-Dfb2896L.js} +1 -1
  25. package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
  26. package/dist/dashboard/assets/{ProxyPage-BB4CNm4-.js → ProxyPage-BaCIdAUt.js} +1 -1
  27. package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
  28. package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
  29. package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
  30. package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
  31. package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
  32. package/dist/dashboard/assets/{SetupPage-FadUOl9S.js → SetupPage-D4g6vOGc.js} +1 -1
  33. package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
  34. package/dist/dashboard/assets/{SortableList-Cfy6_Khv.js → SortableList-C93i14BD.js} +1 -1
  35. package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
  36. package/dist/dashboard/assets/{StudioPage-CM92fgJf.js → StudioPage-SgQpymjm.js} +1 -1
  37. package/dist/dashboard/assets/{StudioPage-CM92fgJf.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
  38. package/dist/dashboard/assets/{UsagePage-BT4MyQeT.js → UsagePage-BFME-eba.js} +2 -2
  39. package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
  40. package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
  41. package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
  42. package/dist/dashboard/assets/{app-B7pVRhJ1.js → app-2KhtHCv9.js} +1 -1
  43. package/dist/dashboard/assets/{app-B7pVRhJ1.js.gz → app-2KhtHCv9.js.gz} +0 -0
  44. package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
  45. package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
  46. package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
  47. package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-Bvyr7D0u.js → badge-CKsF123d.js} +1 -1
  49. package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-DIvKAZih.js → banner-8j7uChA9.js} +1 -1
  51. package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-D5_gYIS_.js → card-CdsSlWA3.js} +1 -1
  53. package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-B6vDBtbm.js → clipboard-button-uL5aYLfW.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-CpjrgdwJ.js → dialog-C07hr5_G.js} +1 -1
  59. package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-BWmt3b5y.js → drawer-BPO1TcKH.js} +1 -1
  61. package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-C_oakV3z.js → entry-Bz_CtDfC.js} +1 -1
  63. package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
  64. package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
  65. package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
  67. package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
  68. package/dist/dashboard/assets/{github-badge-RRHHWGxh.js → github-badge-DmQreSDd.js} +1 -1
  69. package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-Bhey_rg_.js → index-Bblu6erR.js} +2 -2
  71. package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-C2_EQ5Uf.js → inline-C9dCYaBZ.js} +1 -1
  73. package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-BMRlQ450.js → input-CjCnXweJ.js} +1 -1
  75. package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-XR5xP8ew.js → layout-V6y8DAkl.js} +1 -1
  77. package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-Cw07dioc.js → list-row-CM1AaG_y.js} +1 -1
  79. package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
  81. package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
  82. package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
  83. package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
  84. package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
  85. package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
  86. package/dist/dashboard/assets/{numbers-DrdA9oSM.js → numbers-BOevJzdz.js} +1 -1
  87. package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
  88. package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
  89. package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
  90. package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
  91. package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
  92. package/dist/dashboard/assets/{select-CxVVB__N.js → select-C980c4cz.js} +1 -1
  93. package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
  94. package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
  95. package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
  96. package/dist/dashboard/assets/{stack-uPOwM9Hs.js → stack-_gfTdLVB.js} +1 -1
  97. package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
  98. package/dist/dashboard/assets/{state-D1Yz79iL.js → state-2c5V4zo7.js} +1 -1
  99. package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
  100. package/dist/dashboard/assets/{switch-sevsRrRo.js → switch-Dkke7Naf.js} +1 -1
  101. package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
  102. package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
  103. package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
  104. package/dist/dashboard/index.html +1 -1
  105. package/dist/dashboard/index.html.gz +0 -0
  106. package/dist/dashboard/precompressed-assets.json +51 -51
  107. package/migrations/README.md +52 -52
  108. package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
  109. package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
  110. package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
  111. package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
  112. package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
  113. package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
  114. package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
  115. package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
  116. package/package.json +96 -95
  117. package/scripts/cli/admin.ts +126 -128
  118. package/scripts/cli/autostart.ts +237 -237
  119. package/scripts/cli/background.ts +120 -110
  120. package/scripts/cli/bin/carte.cjs +8 -8
  121. package/scripts/cli/bin/cartethyia.cjs +167 -167
  122. package/scripts/cli/console-menu.ts +39 -30
  123. package/scripts/cli/controller.ts +145 -0
  124. package/scripts/cli/env.ts +99 -99
  125. package/scripts/cli/gateway.ts +264 -260
  126. package/scripts/cli/instance.ts +234 -224
  127. package/scripts/cli/main.ts +188 -167
  128. package/scripts/cli/platform.ts +64 -64
  129. package/scripts/cli/prompt.ts +217 -79
  130. package/scripts/cli/services.ts +88 -88
  131. package/scripts/cli/setup.ts +445 -445
  132. package/scripts/cli/start.ts +85 -85
  133. package/scripts/cli/startup-signal.ts +23 -23
  134. package/scripts/cli/telemetry.ts +70 -0
  135. package/scripts/cli/tray/index.ts +263 -263
  136. package/scripts/cli/tray/windows.ps1 +107 -107
  137. package/scripts/cli/tray.ts +29 -4
  138. package/scripts/cli/update.ts +39 -39
  139. package/scripts/cli/version.ts +67 -67
  140. package/scripts/cli/wordmark.ts +58 -58
  141. package/src/boot/app.ts +16 -1
  142. package/src/boot/dependencies.ts +128 -25
  143. package/src/config/resources.ts +137 -0
  144. package/src/config/runtime.ts +133 -76
  145. package/src/config/spec.ts +30 -29
  146. package/src/config/telemetry.ts +0 -25
  147. package/src/console/api-keys/bucket-spend.ts +14 -17
  148. package/src/console/api-keys/contracts.ts +410 -410
  149. package/src/console/api-keys/routes.ts +12 -0
  150. package/src/console/audit/store.ts +2 -3
  151. package/src/console/auth/setup-service.ts +8 -1
  152. package/src/console/backup/routes.ts +7 -0
  153. package/src/console/backup/service.ts +463 -463
  154. package/src/console/backup/store.ts +16 -11
  155. package/src/console/backup/validate.ts +1 -4
  156. package/src/console/cli-tools/fs-ops.ts +18 -2
  157. package/src/console/data-management/contracts.ts +1 -8
  158. package/src/console/data-management/operations.ts +61 -48
  159. package/src/console/data-management/routes.ts +4 -2
  160. package/src/console/domain-registration.ts +25 -3
  161. package/src/console/inferhub/client.ts +139 -0
  162. package/src/console/inferhub/key-store.ts +129 -0
  163. package/src/console/inferhub/normalize.ts +347 -0
  164. package/src/console/inferhub/routes.ts +411 -0
  165. package/src/console/inferhub/store.ts +109 -0
  166. package/src/console/inferhub/types.ts +183 -0
  167. package/src/console/observability/contracts.ts +2 -41
  168. package/src/console/observability/store.ts +176 -371
  169. package/src/console/providers/catalog/public-model-store.ts +0 -0
  170. package/src/console/providers/catalog/store.ts +23 -36
  171. package/src/console/quota/account-quota-shared.ts +52 -52
  172. package/src/console/quota/account-quota-tenant.ts +16 -24
  173. package/src/console/quota/refresh.ts +441 -441
  174. package/src/console/routing/pools/import-parse.ts +206 -206
  175. package/src/console/routing/pools/operations.ts +543 -543
  176. package/src/console/routing/pools/probe-verdict.ts +132 -132
  177. package/src/console/routing/pools/routes.ts +181 -181
  178. package/src/console/settings/contracts.ts +42 -40
  179. package/src/console/settings/store.ts +25 -25
  180. package/src/console/share/share-stats.ts +22 -4
  181. package/src/console/share/share-usage.ts +30 -4
  182. package/src/console/shared/cursor.ts +15 -0
  183. package/src/console/updates/routes.ts +12 -0
  184. package/src/main.ts +13 -5
  185. package/src/network/pool/resolver.ts +1104 -1104
  186. package/src/observability/client-name.ts +22 -0
  187. package/src/observability/event-log.ts +5 -29
  188. package/src/observability/event-query.ts +690 -0
  189. package/src/observability/log-ring.ts +26 -2
  190. package/src/observability/log-sanitize.ts +118 -0
  191. package/src/observability/logger.ts +17 -13
  192. package/src/observability/metrics.ts +35 -0
  193. package/src/observability/payload-capture.ts +114 -41
  194. package/src/observability/payload-store.ts +249 -24
  195. package/src/observability/raw-event-writer.ts +118 -0
  196. package/src/observability/runtime-metrics.ts +71 -1
  197. package/src/observability/telemetry-buffer.ts +100 -17
  198. package/src/observability/telemetry-reconciliation.ts +417 -0
  199. package/src/persistence/boot.ts +20 -9
  200. package/src/persistence/db-pg.ts +13 -0
  201. package/src/persistence/db-sqlite.ts +569 -497
  202. package/src/persistence/dialect.ts +3 -2
  203. package/src/persistence/migrate.ts +181 -181
  204. package/src/persistence/schema.ts +1496 -1406
  205. package/src/persistence/shared-cache.ts +5 -1
  206. package/src/persistence/stores/api-key-store.ts +581 -581
  207. package/src/persistence/stores/telemetry-store.ts +124 -63
  208. package/src/protocol/reasoning-text.ts +30 -0
  209. package/src/protocol/response/chat.ts +33 -7
  210. package/src/protocol/response/codex.ts +14 -2
  211. package/src/protocol/response/messages.ts +5 -2
  212. package/src/providers/authentication/oauth-flow-store.ts +3 -10
  213. package/src/providers/bundled-discovery-providers.ts +122 -122
  214. package/src/providers/default-registry.ts +10 -0
  215. package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
  216. package/src/providers/discovery/models-dev-catalog.ts +354 -354
  217. package/src/providers/discovery/probe-phases.ts +43 -7
  218. package/src/providers/discovery/probing-service.ts +70 -32
  219. package/src/providers/integrations/antigravity/antigravity.ts +9 -2
  220. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
  221. package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
  222. package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
  223. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
  224. package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
  225. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
  226. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
  227. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
  228. package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
  229. package/src/providers/integrations/claude/claude-quota.ts +2 -2
  230. package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
  231. package/src/providers/integrations/claude/claude.ts +3 -4
  232. package/src/providers/integrations/cline/cline.ts +94 -8
  233. package/src/providers/integrations/codex/codex.ts +61 -37
  234. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
  235. package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
  236. package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
  237. package/src/providers/integrations/cursor/cursor-request.ts +11 -0
  238. package/src/providers/integrations/cursor/cursor.ts +29 -17
  239. package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
  240. package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
  241. package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
  242. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
  243. package/src/providers/integrations/grok/grok-quota.ts +279 -279
  244. package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
  245. package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
  246. package/src/providers/integrations/hermes.ts +7 -1
  247. package/src/providers/integrations/inferhub.ts +201 -73
  248. package/src/providers/integrations/kilo/kilo.ts +152 -152
  249. package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
  250. package/src/providers/integrations/openrouter/provider.ts +129 -129
  251. package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
  252. package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
  253. package/src/providers/integrations/trae/trae-oauth.ts +4 -26
  254. package/src/providers/integrations/trae/trae-quota.ts +389 -384
  255. package/src/providers/integrations/trae/trae-realm.ts +15 -2
  256. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
  257. package/src/providers/integrations/zcode/oauth.ts +1 -1
  258. package/src/providers/operations/account-health-service.ts +605 -279
  259. package/src/providers/provider-metadata.ts +12 -1
  260. package/src/providers/provider-model-definition.ts +373 -373
  261. package/src/providers/quota/quota-contracts.ts +3 -7
  262. package/src/providers/search/search-provider.ts +2 -6
  263. package/src/security/admission/contracts.ts +10 -0
  264. package/src/security/admission/in-memory-store.ts +89 -14
  265. package/src/security/admission/lua.ts +191 -182
  266. package/src/security/admission/redis-store.ts +30 -0
  267. package/src/security/admission/service.ts +18 -0
  268. package/src/security/api-key-auth-cache.ts +96 -0
  269. package/src/security/api-key-auth.ts +11 -100
  270. package/src/security/client-router-fingerprint.ts +3 -7
  271. package/src/security/ip-boundary.ts +7 -3
  272. package/src/shared/coerce.ts +67 -0
  273. package/src/shared/hash.ts +6 -0
  274. package/src/shared/time.ts +7 -0
  275. package/src/transport/dispatch/attempt-finalize.ts +26 -138
  276. package/src/transport/dispatch/attempt-loop.ts +103 -81
  277. package/src/transport/dispatch/capture-policy.ts +78 -0
  278. package/src/transport/dispatch/proxy-request.ts +4 -2
  279. package/src/transport/dispatch/streaming/encoder.ts +111 -5
  280. package/src/transport/dispatch/streaming/stream.ts +19 -2
  281. package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
  282. package/src/transport/failure-policy.ts +27 -0
  283. package/src/transport/guards/abuse-guard.ts +2 -2
  284. package/src/transport/guards/auth-guard.ts +17 -0
  285. package/src/transport/guards/body-policy.ts +3 -2
  286. package/src/transport/guards/concurrency-guard.ts +89 -13
  287. package/src/transport/guards/early-rejection-capture.ts +43 -3
  288. package/src/transport/guards/error-lifecycle.ts +10 -2
  289. package/src/transport/guards/guard-errors.ts +23 -0
  290. package/src/transport/guards/headroom-guard.ts +41 -7
  291. package/src/transport/guards/pipeline.ts +24 -0
  292. package/src/transport/guards/request-context.ts +2 -1
  293. package/src/transport/routing/admission.ts +51 -9
  294. package/src/transport/routing/bounded-lru.ts +49 -0
  295. package/src/transport/routing/candidate-index.ts +88 -0
  296. package/src/transport/routing/reservations.ts +121 -7
  297. package/src/transport/routing/round-robin.ts +10 -22
  298. package/src/transport/routing/route-catalog.ts +26 -13
  299. package/src/transport/routing/route-model.ts +22 -2
  300. package/src/transport/routing/router.ts +53 -31
  301. package/src/transport/surface/chat/encode.ts +2 -3
  302. package/src/transport/surface/chat/parse.ts +2 -1
  303. package/src/transport/surface/dialects.ts +11 -3
  304. package/src/transport/surface/messages/encode.ts +2 -1
  305. package/src/transport/surface/messages/parse.ts +3 -1
  306. package/src/transport/surface/messages/stream.ts +2 -6
  307. package/src/workers/maintenance-lease.ts +200 -0
  308. package/src/workers/scheduler.ts +101 -1
  309. package/dist/dashboard/assets/BannedPage-Cd30Z0b9.js.gz +0 -0
  310. package/dist/dashboard/assets/CliToolDetailPage-wf5u-ww6.js.gz +0 -0
  311. package/dist/dashboard/assets/CliToolsPage-DykPvuck.js.gz +0 -0
  312. package/dist/dashboard/assets/CombosPage-DwvSD3vs.js.gz +0 -0
  313. package/dist/dashboard/assets/ConsoleLogPage-CIr7KDex.js.gz +0 -0
  314. package/dist/dashboard/assets/CustomizationPage-82X0DDdH.js.gz +0 -0
  315. package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js +0 -1
  316. package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js.gz +0 -0
  317. package/dist/dashboard/assets/LoginPage-CS5mPuVF.js.gz +0 -0
  318. package/dist/dashboard/assets/ModelPicker-C0UCUsVJ.js.gz +0 -0
  319. package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js +0 -1
  320. package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js.gz +0 -0
  321. package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js +0 -3
  322. package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js.gz +0 -0
  323. package/dist/dashboard/assets/ProvidersPage-vdja5ZPK.js.gz +0 -0
  324. package/dist/dashboard/assets/ProxyPage-BB4CNm4-.js.gz +0 -0
  325. package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js +0 -1
  326. package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js.gz +0 -0
  327. package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js +0 -1
  328. package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js.gz +0 -0
  329. package/dist/dashboard/assets/SetupPage-FadUOl9S.js.gz +0 -0
  330. package/dist/dashboard/assets/SortableList-Cfy6_Khv.js.gz +0 -0
  331. package/dist/dashboard/assets/UsagePage-BT4MyQeT.js.gz +0 -0
  332. package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
  333. package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
  334. package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
  335. package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
  336. package/dist/dashboard/assets/badge-Bvyr7D0u.js.gz +0 -0
  337. package/dist/dashboard/assets/banner-DIvKAZih.js.gz +0 -0
  338. package/dist/dashboard/assets/card-D5_gYIS_.js.gz +0 -0
  339. package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
  340. package/dist/dashboard/assets/clipboard-button-B6vDBtbm.js.gz +0 -0
  341. package/dist/dashboard/assets/dialog-CpjrgdwJ.js.gz +0 -0
  342. package/dist/dashboard/assets/drawer-BWmt3b5y.js.gz +0 -0
  343. package/dist/dashboard/assets/entry-B-gvimrg.js +0 -1
  344. package/dist/dashboard/assets/entry-B-gvimrg.js.gz +0 -0
  345. package/dist/dashboard/assets/entry-C_oakV3z.js.gz +0 -0
  346. package/dist/dashboard/assets/entry-Dj0cHfEc.js +0 -4
  347. package/dist/dashboard/assets/entry-Dj0cHfEc.js.gz +0 -0
  348. package/dist/dashboard/assets/github-badge-RRHHWGxh.js.gz +0 -0
  349. package/dist/dashboard/assets/index-Bhey_rg_.js.gz +0 -0
  350. package/dist/dashboard/assets/inline-C2_EQ5Uf.js.gz +0 -0
  351. package/dist/dashboard/assets/input-BMRlQ450.js.gz +0 -0
  352. package/dist/dashboard/assets/layout-XR5xP8ew.js.gz +0 -0
  353. package/dist/dashboard/assets/list-row-Cw07dioc.js.gz +0 -0
  354. package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
  355. package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
  356. package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
  357. package/dist/dashboard/assets/numbers-DrdA9oSM.js.gz +0 -0
  358. package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
  359. package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
  360. package/dist/dashboard/assets/select-CxVVB__N.js.gz +0 -0
  361. package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
  362. package/dist/dashboard/assets/stack-uPOwM9Hs.js.gz +0 -0
  363. package/dist/dashboard/assets/state-D1Yz79iL.js.gz +0 -0
  364. package/dist/dashboard/assets/switch-sevsRrRo.js.gz +0 -0
  365. package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
  366. package/src/observability/event-compaction.ts +0 -317
  367. package/src/observability/event-day-index.ts +0 -439
@@ -1,263 +1,263 @@
1
- import { spawn, type ChildProcess } from "node:child_process";
2
- import { createInterface } from "node:readline";
3
- import SysTray, { type MenuItem } from "systray2";
4
- import { resolvePackagePath } from "../../../src/config/package-paths";
5
-
6
- /**
7
- * A row as systray2 holds it. The helper numbers the rows itself and reports
8
- * that number back on a click and on an update, but it is absent from the
9
- * package's typings.
10
- */
11
- type IdentifiedItem = MenuItem & { readonly __id?: number };
12
-
13
- /**
14
- * One tray row. `action` rows report their id on click; separators are drawn
15
- * but never fire.
16
- */
17
- export type TrayItem =
18
- | { readonly kind: "action"; readonly id: string; readonly title: string; readonly enabled?: boolean }
19
- | { readonly kind: "separator" };
20
-
21
- export interface TrayOptions {
22
- readonly iconPath: string;
23
- readonly tooltip: string;
24
- readonly items: readonly TrayItem[];
25
- readonly onSelect: (id: string) => void;
26
- readonly onExit: () => void;
27
- }
28
-
29
- export interface Tray {
30
- /** Resolves once the icon is on screen and clicks can arrive. */
31
- ready(): Promise<void>;
32
- /** Replaces the title and enabled state of an existing row. */
33
- update(id: string, title: string, enabled?: boolean): void;
34
- setTooltip(text: string): void;
35
- /** Removes the icon. Idempotent. */
36
- close(): Promise<void>;
37
- }
38
-
39
- /**
40
- * Windows tray: a PowerShell NotifyIcon driven over JSON lines.
41
- *
42
- * `systray2` on Windows leaves the spawning process attached to a console, so
43
- * Windows Terminal keeps an empty tab open for the tray's whole lifetime — the
44
- * window cannot be hidden from inside the process because it is owned by the
45
- * terminal, and `MainWindowHandle` reads 0 so it cannot be found and closed
46
- * either. A PowerShell NotifyIcon started with `-WindowStyle Hidden` owns no
47
- * console at all, which is the only arrangement that shows an icon and no
48
- * window.
49
- */
50
- class WindowsTray implements Tray {
51
- readonly #process: ChildProcess;
52
- readonly #onSelect: (id: string) => void;
53
- readonly #onExit: () => void;
54
- readonly #index = new Map<string, number>();
55
- #ready: Promise<void>;
56
- #closed = false;
57
-
58
- constructor(options: TrayOptions) {
59
- this.#onSelect = options.onSelect;
60
- this.#onExit = options.onExit;
61
-
62
- this.#process = spawn(
63
- "powershell.exe",
64
- [
65
- "-NoProfile",
66
- "-NonInteractive",
67
- "-ExecutionPolicy",
68
- "Bypass",
69
- "-WindowStyle",
70
- "Hidden",
71
- "-InputFormat",
72
- "Text",
73
- "-OutputFormat",
74
- "Text",
75
- "-File",
76
- resolvePackagePath("scripts", "cli", "tray", "windows.ps1"),
77
- "-IconPath",
78
- options.iconPath,
79
- "-Tooltip",
80
- options.tooltip,
81
- ],
82
- { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] },
83
- );
84
-
85
- const { promise, resolve, reject } = Promise.withResolvers<void>();
86
- this.#ready = promise;
87
-
88
- const lines = createInterface({ input: this.#process.stdout! });
89
- lines.on("line", (line) => {
90
- let event: unknown;
91
- try {
92
- event = JSON.parse(line);
93
- } catch {
94
- return;
95
- }
96
- if (event === null || typeof event !== "object" || !("type" in event)) return;
97
- if (event.type === "ready") {
98
- resolve();
99
- return;
100
- }
101
- if (event.type === "click" && "index" in event && typeof event.index === "number") {
102
- for (const [id, index] of this.#index) {
103
- if (index === event.index) {
104
- this.#onSelect(id);
105
- return;
106
- }
107
- }
108
- }
109
- });
110
-
111
- this.#process.once("error", reject);
112
- this.#process.once("exit", () => {
113
- if (!this.#closed) this.#onExit();
114
- reject(new Error("the tray process exited before it was ready"));
115
- });
116
-
117
- // Rows are added after "started": the script creates its NotifyIcon first
118
- // and would drop commands that arrive before it is listening.
119
- let index = 0;
120
- for (const item of options.items) {
121
- if (item.kind === "separator") {
122
- this.#send({ action: "add-separator", index });
123
- index += 1;
124
- continue;
125
- }
126
- this.#index.set(item.id, index);
127
- this.#send({ action: "add-item", index, title: item.title, enabled: item.enabled ?? true });
128
- index += 1;
129
- }
130
- this.#send({ action: "ready" });
131
- }
132
-
133
- #send(command: Record<string, unknown>): void {
134
- if (this.#closed || !this.#process.stdin?.writable) return;
135
- this.#process.stdin.write(`${JSON.stringify(command)}\n`, "utf8");
136
- }
137
-
138
- ready(): Promise<void> {
139
- return this.#ready;
140
- }
141
-
142
- update(id: string, title: string, enabled = true): void {
143
- const index = this.#index.get(id);
144
- if (index === undefined) return;
145
- this.#send({ action: "update-item", index, title, enabled });
146
- }
147
-
148
- setTooltip(text: string): void {
149
- this.#send({ action: "set-tooltip", text });
150
- }
151
-
152
- async close(): Promise<void> {
153
- if (this.#closed) return;
154
- this.#closed = true;
155
- this.#send({ action: "kill" });
156
- await new Promise<void>((resolve) => {
157
- const timer = setTimeout(() => {
158
- this.#process.kill();
159
- resolve();
160
- }, 1_500);
161
- this.#process.once("exit", () => {
162
- clearTimeout(timer);
163
- resolve();
164
- });
165
- });
166
- }
167
- }
168
-
169
- /**
170
- * macOS and Linux tray: `systray2` runs its own helper, and neither platform
171
- * attaches a console window to it, so the bundled tray is used as-is.
172
- *
173
- * Clicks arrive only through `onClick`; a menu built without a listener shows
174
- * every row and reports none of them, which is why the callbacks are wired
175
- * here rather than left to the default.
176
- */
177
- class UnixTray implements Tray {
178
- readonly #tray: SysTray;
179
- readonly #onSelect: (id: string) => void;
180
- readonly #onExit: () => void;
181
- /** Row objects kept by reference: a click hands back the object, not the id. */
182
- readonly #items = new Map<string, IdentifiedItem>();
183
- #closed = false;
184
-
185
- constructor(options: TrayOptions) {
186
- this.#onSelect = options.onSelect;
187
- this.#onExit = options.onExit;
188
- const items = options.items.map((item) => {
189
- if (item.kind === "separator") return SysTray.separator;
190
- const menuItem: IdentifiedItem = {
191
- title: item.title,
192
- tooltip: item.title,
193
- enabled: item.enabled ?? true,
194
- };
195
- this.#items.set(item.id, menuItem);
196
- return menuItem;
197
- });
198
- this.#tray = new SysTray({
199
- menu: {
200
- icon: options.iconPath,
201
- title: "Cartethyia",
202
- tooltip: options.tooltip,
203
- items,
204
- },
205
- copyDir: true,
206
- });
207
- }
208
-
209
- async ready(): Promise<void> {
210
- await this.#tray.ready();
211
- // Registered after `ready()`: the helper process does not exist before
212
- // that, and `onExit` attaches to it directly.
213
- this.#tray.onExit(() => {
214
- if (!this.#closed) this.#onExit();
215
- });
216
- await this.#tray.onClick((action) => {
217
- for (const [id, item] of this.#items) {
218
- // Identity first: `onClick` merges the clicked row into the object this
219
- // class built, so a match needs no assumptions about the numbering.
220
- if (item === action.item || item.__id === action.__id) {
221
- this.#onSelect(id);
222
- return;
223
- }
224
- }
225
- });
226
- }
227
-
228
- update(id: string, title: string, enabled = true): void {
229
- const item = this.#items.get(id);
230
- // An update sent before the helper reports ready carries no row number, and
231
- // systray2 then rewrites the first row instead of this one.
232
- if (item?.__id === undefined) return;
233
- const next: IdentifiedItem = { title, tooltip: title, enabled, __id: item.__id };
234
- void this.#tray.sendAction({ type: "update-item", item: next });
235
- }
236
-
237
- setTooltip(text: string): void {
238
- // systray2 has no tooltip-only action; the tooltip travels with the title,
239
- // which `update()` already sends.
240
- void text;
241
- }
242
-
243
- async close(): Promise<void> {
244
- if (this.#closed) return;
245
- this.#closed = true;
246
- const helper = this.#tray.process;
247
- // `kill()` settles on the helper's `exit` event, so a helper that already
248
- // exited would leave it pending forever and hang the shutdown after it.
249
- if (helper.exitCode !== null || helper.signalCode !== null) return;
250
- try {
251
- // `false`: systray2 otherwise calls `process.exit(0)` when the helper
252
- // exits, which discards the exit code this close was given.
253
- await this.#tray.kill(false);
254
- } catch {
255
- // The helper may already be gone.
256
- }
257
- }
258
- }
259
-
260
- /** Creates the tray for this platform. */
261
- export function createTray(options: TrayOptions): Tray {
262
- return process.platform === "win32" ? new WindowsTray(options) : new UnixTray(options);
263
- }
1
+ import { spawn, type ChildProcess } from "node:child_process";
2
+ import { createInterface } from "node:readline";
3
+ import SysTray, { type MenuItem } from "systray2";
4
+ import { resolvePackagePath } from "../../../src/config/package-paths";
5
+
6
+ /**
7
+ * A row as systray2 holds it. The helper numbers the rows itself and reports
8
+ * that number back on a click and on an update, but it is absent from the
9
+ * package's typings.
10
+ */
11
+ type IdentifiedItem = MenuItem & { readonly __id?: number };
12
+
13
+ /**
14
+ * One tray row. `action` rows report their id on click; separators are drawn
15
+ * but never fire.
16
+ */
17
+ export type TrayItem =
18
+ | { readonly kind: "action"; readonly id: string; readonly title: string; readonly enabled?: boolean }
19
+ | { readonly kind: "separator" };
20
+
21
+ export interface TrayOptions {
22
+ readonly iconPath: string;
23
+ readonly tooltip: string;
24
+ readonly items: readonly TrayItem[];
25
+ readonly onSelect: (id: string) => void;
26
+ readonly onExit: () => void;
27
+ }
28
+
29
+ export interface Tray {
30
+ /** Resolves once the icon is on screen and clicks can arrive. */
31
+ ready(): Promise<void>;
32
+ /** Replaces the title and enabled state of an existing row. */
33
+ update(id: string, title: string, enabled?: boolean): void;
34
+ setTooltip(text: string): void;
35
+ /** Removes the icon. Idempotent. */
36
+ close(): Promise<void>;
37
+ }
38
+
39
+ /**
40
+ * Windows tray: a PowerShell NotifyIcon driven over JSON lines.
41
+ *
42
+ * `systray2` on Windows leaves the spawning process attached to a console, so
43
+ * Windows Terminal keeps an empty tab open for the tray's whole lifetime — the
44
+ * window cannot be hidden from inside the process because it is owned by the
45
+ * terminal, and `MainWindowHandle` reads 0 so it cannot be found and closed
46
+ * either. A PowerShell NotifyIcon started with `-WindowStyle Hidden` owns no
47
+ * console at all, which is the only arrangement that shows an icon and no
48
+ * window.
49
+ */
50
+ class WindowsTray implements Tray {
51
+ readonly #process: ChildProcess;
52
+ readonly #onSelect: (id: string) => void;
53
+ readonly #onExit: () => void;
54
+ readonly #index = new Map<string, number>();
55
+ #ready: Promise<void>;
56
+ #closed = false;
57
+
58
+ constructor(options: TrayOptions) {
59
+ this.#onSelect = options.onSelect;
60
+ this.#onExit = options.onExit;
61
+
62
+ this.#process = spawn(
63
+ "powershell.exe",
64
+ [
65
+ "-NoProfile",
66
+ "-NonInteractive",
67
+ "-ExecutionPolicy",
68
+ "Bypass",
69
+ "-WindowStyle",
70
+ "Hidden",
71
+ "-InputFormat",
72
+ "Text",
73
+ "-OutputFormat",
74
+ "Text",
75
+ "-File",
76
+ resolvePackagePath("scripts", "cli", "tray", "windows.ps1"),
77
+ "-IconPath",
78
+ options.iconPath,
79
+ "-Tooltip",
80
+ options.tooltip,
81
+ ],
82
+ { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] },
83
+ );
84
+
85
+ const { promise, resolve, reject } = Promise.withResolvers<void>();
86
+ this.#ready = promise;
87
+
88
+ const lines = createInterface({ input: this.#process.stdout! });
89
+ lines.on("line", (line) => {
90
+ let event: unknown;
91
+ try {
92
+ event = JSON.parse(line);
93
+ } catch {
94
+ return;
95
+ }
96
+ if (event === null || typeof event !== "object" || !("type" in event)) return;
97
+ if (event.type === "ready") {
98
+ resolve();
99
+ return;
100
+ }
101
+ if (event.type === "click" && "index" in event && typeof event.index === "number") {
102
+ for (const [id, index] of this.#index) {
103
+ if (index === event.index) {
104
+ this.#onSelect(id);
105
+ return;
106
+ }
107
+ }
108
+ }
109
+ });
110
+
111
+ this.#process.once("error", reject);
112
+ this.#process.once("exit", () => {
113
+ if (!this.#closed) this.#onExit();
114
+ reject(new Error("the tray process exited before it was ready"));
115
+ });
116
+
117
+ // Rows are added after "started": the script creates its NotifyIcon first
118
+ // and would drop commands that arrive before it is listening.
119
+ let index = 0;
120
+ for (const item of options.items) {
121
+ if (item.kind === "separator") {
122
+ this.#send({ action: "add-separator", index });
123
+ index += 1;
124
+ continue;
125
+ }
126
+ this.#index.set(item.id, index);
127
+ this.#send({ action: "add-item", index, title: item.title, enabled: item.enabled ?? true });
128
+ index += 1;
129
+ }
130
+ this.#send({ action: "ready" });
131
+ }
132
+
133
+ #send(command: Record<string, unknown>): void {
134
+ if (this.#closed || !this.#process.stdin?.writable) return;
135
+ this.#process.stdin.write(`${JSON.stringify(command)}\n`, "utf8");
136
+ }
137
+
138
+ ready(): Promise<void> {
139
+ return this.#ready;
140
+ }
141
+
142
+ update(id: string, title: string, enabled = true): void {
143
+ const index = this.#index.get(id);
144
+ if (index === undefined) return;
145
+ this.#send({ action: "update-item", index, title, enabled });
146
+ }
147
+
148
+ setTooltip(text: string): void {
149
+ this.#send({ action: "set-tooltip", text });
150
+ }
151
+
152
+ async close(): Promise<void> {
153
+ if (this.#closed) return;
154
+ this.#closed = true;
155
+ this.#send({ action: "kill" });
156
+ await new Promise<void>((resolve) => {
157
+ const timer = setTimeout(() => {
158
+ this.#process.kill();
159
+ resolve();
160
+ }, 1_500);
161
+ this.#process.once("exit", () => {
162
+ clearTimeout(timer);
163
+ resolve();
164
+ });
165
+ });
166
+ }
167
+ }
168
+
169
+ /**
170
+ * macOS and Linux tray: `systray2` runs its own helper, and neither platform
171
+ * attaches a console window to it, so the bundled tray is used as-is.
172
+ *
173
+ * Clicks arrive only through `onClick`; a menu built without a listener shows
174
+ * every row and reports none of them, which is why the callbacks are wired
175
+ * here rather than left to the default.
176
+ */
177
+ class UnixTray implements Tray {
178
+ readonly #tray: SysTray;
179
+ readonly #onSelect: (id: string) => void;
180
+ readonly #onExit: () => void;
181
+ /** Row objects kept by reference: a click hands back the object, not the id. */
182
+ readonly #items = new Map<string, IdentifiedItem>();
183
+ #closed = false;
184
+
185
+ constructor(options: TrayOptions) {
186
+ this.#onSelect = options.onSelect;
187
+ this.#onExit = options.onExit;
188
+ const items = options.items.map((item) => {
189
+ if (item.kind === "separator") return SysTray.separator;
190
+ const menuItem: IdentifiedItem = {
191
+ title: item.title,
192
+ tooltip: item.title,
193
+ enabled: item.enabled ?? true,
194
+ };
195
+ this.#items.set(item.id, menuItem);
196
+ return menuItem;
197
+ });
198
+ this.#tray = new SysTray({
199
+ menu: {
200
+ icon: options.iconPath,
201
+ title: "Cartethyia",
202
+ tooltip: options.tooltip,
203
+ items,
204
+ },
205
+ copyDir: true,
206
+ });
207
+ }
208
+
209
+ async ready(): Promise<void> {
210
+ await this.#tray.ready();
211
+ // Registered after `ready()`: the helper process does not exist before
212
+ // that, and `onExit` attaches to it directly.
213
+ this.#tray.onExit(() => {
214
+ if (!this.#closed) this.#onExit();
215
+ });
216
+ await this.#tray.onClick((action) => {
217
+ for (const [id, item] of this.#items) {
218
+ // Identity first: `onClick` merges the clicked row into the object this
219
+ // class built, so a match needs no assumptions about the numbering.
220
+ if (item === action.item || item.__id === action.__id) {
221
+ this.#onSelect(id);
222
+ return;
223
+ }
224
+ }
225
+ });
226
+ }
227
+
228
+ update(id: string, title: string, enabled = true): void {
229
+ const item = this.#items.get(id);
230
+ // An update sent before the helper reports ready carries no row number, and
231
+ // systray2 then rewrites the first row instead of this one.
232
+ if (item?.__id === undefined) return;
233
+ const next: IdentifiedItem = { title, tooltip: title, enabled, __id: item.__id };
234
+ void this.#tray.sendAction({ type: "update-item", item: next });
235
+ }
236
+
237
+ setTooltip(text: string): void {
238
+ // systray2 has no tooltip-only action; the tooltip travels with the title,
239
+ // which `update()` already sends.
240
+ void text;
241
+ }
242
+
243
+ async close(): Promise<void> {
244
+ if (this.#closed) return;
245
+ this.#closed = true;
246
+ const helper = this.#tray.process;
247
+ // `kill()` settles on the helper's `exit` event, so a helper that already
248
+ // exited would leave it pending forever and hang the shutdown after it.
249
+ if (helper.exitCode !== null || helper.signalCode !== null) return;
250
+ try {
251
+ // `false`: systray2 otherwise calls `process.exit(0)` when the helper
252
+ // exits, which discards the exit code this close was given.
253
+ await this.#tray.kill(false);
254
+ } catch {
255
+ // The helper may already be gone.
256
+ }
257
+ }
258
+ }
259
+
260
+ /** Creates the tray for this platform. */
261
+ export function createTray(options: TrayOptions): Tray {
262
+ return process.platform === "win32" ? new WindowsTray(options) : new UnixTray(options);
263
+ }