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.
- package/.env.example +8 -0
- package/dist/dashboard/assets/{BannedPage-Cd30Z0b9.js → BannedPage-l6XMnfr9.js} +1 -1
- package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-wf5u-ww6.js → CliToolDetailPage-DwOzAW45.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-DykPvuck.js → CliToolsPage-BT3Up3QA.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-DwvSD3vs.js → CombosPage-6kYIUpzR.js} +1 -1
- package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CIr7KDex.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-82X0DDdH.js → CustomizationPage-C7m-H1Rz.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-CS5mPuVF.js → LoginPage-CbphLvjg.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C0UCUsVJ.js → ModelPicker-DVny4S5_.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-vdja5ZPK.js → ProvidersPage-Dfb2896L.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-BB4CNm4-.js → ProxyPage-BaCIdAUt.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-FadUOl9S.js → SetupPage-D4g6vOGc.js} +1 -1
- package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-Cfy6_Khv.js → SortableList-C93i14BD.js} +1 -1
- package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-CM92fgJf.js → StudioPage-SgQpymjm.js} +1 -1
- package/dist/dashboard/assets/{StudioPage-CM92fgJf.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
- package/dist/dashboard/assets/{UsagePage-BT4MyQeT.js → UsagePage-BFME-eba.js} +2 -2
- package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
- package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
- package/dist/dashboard/assets/{app-B7pVRhJ1.js → app-2KhtHCv9.js} +1 -1
- package/dist/dashboard/assets/{app-B7pVRhJ1.js.gz → app-2KhtHCv9.js.gz} +0 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
- package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
- package/dist/dashboard/assets/{badge-Bvyr7D0u.js → badge-CKsF123d.js} +1 -1
- package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DIvKAZih.js → banner-8j7uChA9.js} +1 -1
- package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
- package/dist/dashboard/assets/{card-D5_gYIS_.js → card-CdsSlWA3.js} +1 -1
- package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
- package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-B6vDBtbm.js → clipboard-button-uL5aYLfW.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-CpjrgdwJ.js → dialog-C07hr5_G.js} +1 -1
- package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BWmt3b5y.js → drawer-BPO1TcKH.js} +1 -1
- package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
- package/dist/dashboard/assets/{entry-C_oakV3z.js → entry-Bz_CtDfC.js} +1 -1
- package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-RRHHWGxh.js → github-badge-DmQreSDd.js} +1 -1
- package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
- package/dist/dashboard/assets/{index-Bhey_rg_.js → index-Bblu6erR.js} +2 -2
- package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
- package/dist/dashboard/assets/{inline-C2_EQ5Uf.js → inline-C9dCYaBZ.js} +1 -1
- package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
- package/dist/dashboard/assets/{input-BMRlQ450.js → input-CjCnXweJ.js} +1 -1
- package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
- package/dist/dashboard/assets/{layout-XR5xP8ew.js → layout-V6y8DAkl.js} +1 -1
- package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Cw07dioc.js → list-row-CM1AaG_y.js} +1 -1
- package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
- package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
- package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
- package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
- package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-DrdA9oSM.js → numbers-BOevJzdz.js} +1 -1
- package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
- package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
- package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
- package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
- package/dist/dashboard/assets/{select-CxVVB__N.js → select-C980c4cz.js} +1 -1
- package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
- package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
- package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
- package/dist/dashboard/assets/{stack-uPOwM9Hs.js → stack-_gfTdLVB.js} +1 -1
- package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
- package/dist/dashboard/assets/{state-D1Yz79iL.js → state-2c5V4zo7.js} +1 -1
- package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
- package/dist/dashboard/assets/{switch-sevsRrRo.js → switch-Dkke7Naf.js} +1 -1
- package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
- package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -51
- package/migrations/README.md +52 -52
- package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
- package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
- package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/package.json +96 -95
- package/scripts/cli/admin.ts +126 -128
- package/scripts/cli/autostart.ts +237 -237
- package/scripts/cli/background.ts +120 -110
- package/scripts/cli/bin/carte.cjs +8 -8
- package/scripts/cli/bin/cartethyia.cjs +167 -167
- package/scripts/cli/console-menu.ts +39 -30
- package/scripts/cli/controller.ts +145 -0
- package/scripts/cli/env.ts +99 -99
- package/scripts/cli/gateway.ts +264 -260
- package/scripts/cli/instance.ts +234 -224
- package/scripts/cli/main.ts +188 -167
- package/scripts/cli/platform.ts +64 -64
- package/scripts/cli/prompt.ts +217 -79
- package/scripts/cli/services.ts +88 -88
- package/scripts/cli/setup.ts +445 -445
- package/scripts/cli/start.ts +85 -85
- package/scripts/cli/startup-signal.ts +23 -23
- package/scripts/cli/telemetry.ts +70 -0
- package/scripts/cli/tray/index.ts +263 -263
- package/scripts/cli/tray/windows.ps1 +107 -107
- package/scripts/cli/tray.ts +29 -4
- package/scripts/cli/update.ts +39 -39
- package/scripts/cli/version.ts +67 -67
- package/scripts/cli/wordmark.ts +58 -58
- package/src/boot/app.ts +16 -1
- package/src/boot/dependencies.ts +128 -25
- package/src/config/resources.ts +137 -0
- package/src/config/runtime.ts +133 -76
- package/src/config/spec.ts +30 -29
- package/src/config/telemetry.ts +0 -25
- package/src/console/api-keys/bucket-spend.ts +14 -17
- package/src/console/api-keys/contracts.ts +410 -410
- package/src/console/api-keys/routes.ts +12 -0
- package/src/console/audit/store.ts +2 -3
- package/src/console/auth/setup-service.ts +8 -1
- package/src/console/backup/routes.ts +7 -0
- package/src/console/backup/service.ts +463 -463
- package/src/console/backup/store.ts +16 -11
- package/src/console/backup/validate.ts +1 -4
- package/src/console/cli-tools/fs-ops.ts +18 -2
- package/src/console/data-management/contracts.ts +1 -8
- package/src/console/data-management/operations.ts +61 -48
- package/src/console/data-management/routes.ts +4 -2
- package/src/console/domain-registration.ts +25 -3
- package/src/console/inferhub/client.ts +139 -0
- package/src/console/inferhub/key-store.ts +129 -0
- package/src/console/inferhub/normalize.ts +347 -0
- package/src/console/inferhub/routes.ts +411 -0
- package/src/console/inferhub/store.ts +109 -0
- package/src/console/inferhub/types.ts +183 -0
- package/src/console/observability/contracts.ts +2 -41
- package/src/console/observability/store.ts +176 -371
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/store.ts +23 -36
- package/src/console/quota/account-quota-shared.ts +52 -52
- package/src/console/quota/account-quota-tenant.ts +16 -24
- package/src/console/quota/refresh.ts +441 -441
- package/src/console/routing/pools/import-parse.ts +206 -206
- package/src/console/routing/pools/operations.ts +543 -543
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/routes.ts +181 -181
- package/src/console/settings/contracts.ts +42 -40
- package/src/console/settings/store.ts +25 -25
- package/src/console/share/share-stats.ts +22 -4
- package/src/console/share/share-usage.ts +30 -4
- package/src/console/shared/cursor.ts +15 -0
- package/src/console/updates/routes.ts +12 -0
- package/src/main.ts +13 -5
- package/src/network/pool/resolver.ts +1104 -1104
- package/src/observability/client-name.ts +22 -0
- package/src/observability/event-log.ts +5 -29
- package/src/observability/event-query.ts +690 -0
- package/src/observability/log-ring.ts +26 -2
- package/src/observability/log-sanitize.ts +118 -0
- package/src/observability/logger.ts +17 -13
- package/src/observability/metrics.ts +35 -0
- package/src/observability/payload-capture.ts +114 -41
- package/src/observability/payload-store.ts +249 -24
- package/src/observability/raw-event-writer.ts +118 -0
- package/src/observability/runtime-metrics.ts +71 -1
- package/src/observability/telemetry-buffer.ts +100 -17
- package/src/observability/telemetry-reconciliation.ts +417 -0
- package/src/persistence/boot.ts +20 -9
- package/src/persistence/db-pg.ts +13 -0
- package/src/persistence/db-sqlite.ts +569 -497
- package/src/persistence/dialect.ts +3 -2
- package/src/persistence/migrate.ts +181 -181
- package/src/persistence/schema.ts +1496 -1406
- package/src/persistence/shared-cache.ts +5 -1
- package/src/persistence/stores/api-key-store.ts +581 -581
- package/src/persistence/stores/telemetry-store.ts +124 -63
- package/src/protocol/reasoning-text.ts +30 -0
- package/src/protocol/response/chat.ts +33 -7
- package/src/protocol/response/codex.ts +14 -2
- package/src/protocol/response/messages.ts +5 -2
- package/src/providers/authentication/oauth-flow-store.ts +3 -10
- package/src/providers/bundled-discovery-providers.ts +122 -122
- package/src/providers/default-registry.ts +10 -0
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +43 -7
- package/src/providers/discovery/probing-service.ts +70 -32
- package/src/providers/integrations/antigravity/antigravity.ts +9 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
- package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
- package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
- package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
- package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
- package/src/providers/integrations/claude/claude-quota.ts +2 -2
- package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
- package/src/providers/integrations/claude/claude.ts +3 -4
- package/src/providers/integrations/cline/cline.ts +94 -8
- package/src/providers/integrations/codex/codex.ts +61 -37
- package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
- package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
- package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
- package/src/providers/integrations/cursor/cursor-request.ts +11 -0
- package/src/providers/integrations/cursor/cursor.ts +29 -17
- package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
- package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
- package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
- package/src/providers/integrations/grok/grok-quota.ts +279 -279
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
- package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
- package/src/providers/integrations/hermes.ts +7 -1
- package/src/providers/integrations/inferhub.ts +201 -73
- package/src/providers/integrations/kilo/kilo.ts +152 -152
- package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
- package/src/providers/integrations/openrouter/provider.ts +129 -129
- package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
- package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
- package/src/providers/integrations/trae/trae-oauth.ts +4 -26
- package/src/providers/integrations/trae/trae-quota.ts +389 -384
- package/src/providers/integrations/trae/trae-realm.ts +15 -2
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/operations/account-health-service.ts +605 -279
- package/src/providers/provider-metadata.ts +12 -1
- package/src/providers/provider-model-definition.ts +373 -373
- package/src/providers/quota/quota-contracts.ts +3 -7
- package/src/providers/search/search-provider.ts +2 -6
- package/src/security/admission/contracts.ts +10 -0
- package/src/security/admission/in-memory-store.ts +89 -14
- package/src/security/admission/lua.ts +191 -182
- package/src/security/admission/redis-store.ts +30 -0
- package/src/security/admission/service.ts +18 -0
- package/src/security/api-key-auth-cache.ts +96 -0
- package/src/security/api-key-auth.ts +11 -100
- package/src/security/client-router-fingerprint.ts +3 -7
- package/src/security/ip-boundary.ts +7 -3
- package/src/shared/coerce.ts +67 -0
- package/src/shared/hash.ts +6 -0
- package/src/shared/time.ts +7 -0
- package/src/transport/dispatch/attempt-finalize.ts +26 -138
- package/src/transport/dispatch/attempt-loop.ts +103 -81
- package/src/transport/dispatch/capture-policy.ts +78 -0
- package/src/transport/dispatch/proxy-request.ts +4 -2
- package/src/transport/dispatch/streaming/encoder.ts +111 -5
- package/src/transport/dispatch/streaming/stream.ts +19 -2
- package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
- package/src/transport/failure-policy.ts +27 -0
- package/src/transport/guards/abuse-guard.ts +2 -2
- package/src/transport/guards/auth-guard.ts +17 -0
- package/src/transport/guards/body-policy.ts +3 -2
- package/src/transport/guards/concurrency-guard.ts +89 -13
- package/src/transport/guards/early-rejection-capture.ts +43 -3
- package/src/transport/guards/error-lifecycle.ts +10 -2
- package/src/transport/guards/guard-errors.ts +23 -0
- package/src/transport/guards/headroom-guard.ts +41 -7
- package/src/transport/guards/pipeline.ts +24 -0
- package/src/transport/guards/request-context.ts +2 -1
- package/src/transport/routing/admission.ts +51 -9
- package/src/transport/routing/bounded-lru.ts +49 -0
- package/src/transport/routing/candidate-index.ts +88 -0
- package/src/transport/routing/reservations.ts +121 -7
- package/src/transport/routing/round-robin.ts +10 -22
- package/src/transport/routing/route-catalog.ts +26 -13
- package/src/transport/routing/route-model.ts +22 -2
- package/src/transport/routing/router.ts +53 -31
- package/src/transport/surface/chat/encode.ts +2 -3
- package/src/transport/surface/chat/parse.ts +2 -1
- package/src/transport/surface/dialects.ts +11 -3
- package/src/transport/surface/messages/encode.ts +2 -1
- package/src/transport/surface/messages/parse.ts +3 -1
- package/src/transport/surface/messages/stream.ts +2 -6
- package/src/workers/maintenance-lease.ts +200 -0
- package/src/workers/scheduler.ts +101 -1
- package/dist/dashboard/assets/BannedPage-Cd30Z0b9.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-wf5u-ww6.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DykPvuck.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DwvSD3vs.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CIr7KDex.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-82X0DDdH.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-CS5mPuVF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C0UCUsVJ.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-vdja5ZPK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BB4CNm4-.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js +0 -1
- package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js +0 -1
- package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-FadUOl9S.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-Cfy6_Khv.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BT4MyQeT.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/badge-Bvyr7D0u.js.gz +0 -0
- package/dist/dashboard/assets/banner-DIvKAZih.js.gz +0 -0
- package/dist/dashboard/assets/card-D5_gYIS_.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-B6vDBtbm.js.gz +0 -0
- package/dist/dashboard/assets/dialog-CpjrgdwJ.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BWmt3b5y.js.gz +0 -0
- package/dist/dashboard/assets/entry-B-gvimrg.js +0 -1
- package/dist/dashboard/assets/entry-B-gvimrg.js.gz +0 -0
- package/dist/dashboard/assets/entry-C_oakV3z.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dj0cHfEc.js +0 -4
- package/dist/dashboard/assets/entry-Dj0cHfEc.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-RRHHWGxh.js.gz +0 -0
- package/dist/dashboard/assets/index-Bhey_rg_.js.gz +0 -0
- package/dist/dashboard/assets/inline-C2_EQ5Uf.js.gz +0 -0
- package/dist/dashboard/assets/input-BMRlQ450.js.gz +0 -0
- package/dist/dashboard/assets/layout-XR5xP8ew.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Cw07dioc.js.gz +0 -0
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DrdA9oSM.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/select-CxVVB__N.js.gz +0 -0
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/stack-uPOwM9Hs.js.gz +0 -0
- package/dist/dashboard/assets/state-D1Yz79iL.js.gz +0 -0
- package/dist/dashboard/assets/switch-sevsRrRo.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/src/observability/event-compaction.ts +0 -317
- package/src/observability/event-day-index.ts +0 -439
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
# Cartethyia tray icon for Windows using NotifyIcon.
|
|
2
|
-
#
|
|
3
|
-
# Why PowerShell instead of a Node tray library: `systray2` and friends bundle a
|
|
4
|
-
# helper binary and still leave the spawning process attached to a console, so
|
|
5
|
-
# Windows Terminal shows an empty tab for the tray's whole lifetime. A
|
|
6
|
-
# PowerShell NotifyIcon owns no console of its own when started with
|
|
7
|
-
# -WindowStyle Hidden, and the protocol here is plain JSON lines on
|
|
8
|
-
# stdin/stdout, so the parent stays in charge.
|
|
9
|
-
#
|
|
10
|
-
# Protocol: the parent writes one JSON object per line.
|
|
11
|
-
# {"action":"add-item","index":0,"title":"...","enabled":true}
|
|
12
|
-
# {"action":"update-item","index":0,"title":"...","enabled":true}
|
|
13
|
-
# {"action":"set-tooltip","text":"..."}
|
|
14
|
-
# {"action":"kill"}
|
|
15
|
-
# This script writes one JSON object per line:
|
|
16
|
-
# {"type":"started"} {"type":"ready"} {"type":"click","index":0}
|
|
17
|
-
param([string]$IconPath, [string]$Tooltip)
|
|
18
|
-
|
|
19
|
-
$ErrorActionPreference = "Stop"
|
|
20
|
-
|
|
21
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
22
|
-
Add-Type -AssemblyName System.Drawing
|
|
23
|
-
|
|
24
|
-
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
25
|
-
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
|
26
|
-
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
27
|
-
|
|
28
|
-
[System.Windows.Forms.Application]::EnableVisualStyles()
|
|
29
|
-
[System.Windows.Forms.Application]::SetCompatibleTextRenderingDefault($false)
|
|
30
|
-
|
|
31
|
-
$script:notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
32
|
-
$script:notifyIcon.Icon = New-Object System.Drawing.Icon($IconPath)
|
|
33
|
-
$script:notifyIcon.Text = $Tooltip
|
|
34
|
-
$script:notifyIcon.Visible = $true
|
|
35
|
-
|
|
36
|
-
$script:menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
37
|
-
$script:notifyIcon.ContextMenuStrip = $script:menu
|
|
38
|
-
$script:items = @()
|
|
39
|
-
|
|
40
|
-
function Write-Event($obj) {
|
|
41
|
-
$json = $obj | ConvertTo-Json -Compress
|
|
42
|
-
[Console]::Out.WriteLine($json)
|
|
43
|
-
[Console]::Out.Flush()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function Add-MenuItem($index, $title, $enabled) {
|
|
47
|
-
$item = New-Object System.Windows.Forms.ToolStripMenuItem
|
|
48
|
-
$item.Text = $title
|
|
49
|
-
$item.Enabled = $enabled
|
|
50
|
-
$idx = $index
|
|
51
|
-
$item.Add_Click({ Write-Event @{ type = "click"; index = $idx } }.GetNewClosure())
|
|
52
|
-
$script:menu.Items.Add($item) | Out-Null
|
|
53
|
-
$script:items += $item
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
# A real ToolStripSeparator draws the divider. Sending a "-" title instead
|
|
57
|
-
# renders a literal dash row, which is what the menu showed before.
|
|
58
|
-
function Add-MenuSeparator($index) {
|
|
59
|
-
$item = New-Object System.Windows.Forms.ToolStripSeparator
|
|
60
|
-
$script:menu.Items.Add($item) | Out-Null
|
|
61
|
-
$script:items += $item
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function Update-MenuItem($index, $title, $enabled) {
|
|
65
|
-
if ($index -lt $script:items.Count) {
|
|
66
|
-
$script:items[$index].Text = $title
|
|
67
|
-
$script:items[$index].Enabled = $enabled
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function Set-Tooltip($text) {
|
|
72
|
-
# NotifyIcon.Text is capped at 63 characters.
|
|
73
|
-
if ($text.Length -gt 63) { $text = $text.Substring(0, 63) }
|
|
74
|
-
$script:notifyIcon.Text = $text
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
# Stdin is polled from the UI thread: a background thread reading Console.In
|
|
78
|
-
# races the message loop and deadlocks when the menu is open.
|
|
79
|
-
$script:timer = New-Object System.Windows.Forms.Timer
|
|
80
|
-
$script:timer.Interval = 100
|
|
81
|
-
$script:timer.Add_Tick({
|
|
82
|
-
try {
|
|
83
|
-
while ([Console]::In.Peek() -ne -1) {
|
|
84
|
-
$line = [Console]::In.ReadLine()
|
|
85
|
-
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
|
86
|
-
$cmd = $line | ConvertFrom-Json
|
|
87
|
-
switch ($cmd.action) {
|
|
88
|
-
"add-item" { Add-MenuItem $cmd.index $cmd.title $cmd.enabled }
|
|
89
|
-
"add-separator" { Add-MenuSeparator $cmd.index }
|
|
90
|
-
"update-item" { Update-MenuItem $cmd.index $cmd.title $cmd.enabled }
|
|
91
|
-
"set-tooltip" { Set-Tooltip $cmd.text }
|
|
92
|
-
"ready" { Write-Event @{ type = "ready" } }
|
|
93
|
-
"kill" {
|
|
94
|
-
$script:notifyIcon.Visible = $false
|
|
95
|
-
$script:notifyIcon.Dispose()
|
|
96
|
-
[System.Windows.Forms.Application]::Exit()
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
} catch {
|
|
101
|
-
Write-Event @{ type = "error"; message = $_.Exception.Message }
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
$script:timer.Start()
|
|
105
|
-
|
|
106
|
-
Write-Event @{ type = "started" }
|
|
107
|
-
[System.Windows.Forms.Application]::Run()
|
|
1
|
+
# Cartethyia tray icon for Windows using NotifyIcon.
|
|
2
|
+
#
|
|
3
|
+
# Why PowerShell instead of a Node tray library: `systray2` and friends bundle a
|
|
4
|
+
# helper binary and still leave the spawning process attached to a console, so
|
|
5
|
+
# Windows Terminal shows an empty tab for the tray's whole lifetime. A
|
|
6
|
+
# PowerShell NotifyIcon owns no console of its own when started with
|
|
7
|
+
# -WindowStyle Hidden, and the protocol here is plain JSON lines on
|
|
8
|
+
# stdin/stdout, so the parent stays in charge.
|
|
9
|
+
#
|
|
10
|
+
# Protocol: the parent writes one JSON object per line.
|
|
11
|
+
# {"action":"add-item","index":0,"title":"...","enabled":true}
|
|
12
|
+
# {"action":"update-item","index":0,"title":"...","enabled":true}
|
|
13
|
+
# {"action":"set-tooltip","text":"..."}
|
|
14
|
+
# {"action":"kill"}
|
|
15
|
+
# This script writes one JSON object per line:
|
|
16
|
+
# {"type":"started"} {"type":"ready"} {"type":"click","index":0}
|
|
17
|
+
param([string]$IconPath, [string]$Tooltip)
|
|
18
|
+
|
|
19
|
+
$ErrorActionPreference = "Stop"
|
|
20
|
+
|
|
21
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
22
|
+
Add-Type -AssemblyName System.Drawing
|
|
23
|
+
|
|
24
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
25
|
+
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
|
26
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
27
|
+
|
|
28
|
+
[System.Windows.Forms.Application]::EnableVisualStyles()
|
|
29
|
+
[System.Windows.Forms.Application]::SetCompatibleTextRenderingDefault($false)
|
|
30
|
+
|
|
31
|
+
$script:notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
32
|
+
$script:notifyIcon.Icon = New-Object System.Drawing.Icon($IconPath)
|
|
33
|
+
$script:notifyIcon.Text = $Tooltip
|
|
34
|
+
$script:notifyIcon.Visible = $true
|
|
35
|
+
|
|
36
|
+
$script:menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
37
|
+
$script:notifyIcon.ContextMenuStrip = $script:menu
|
|
38
|
+
$script:items = @()
|
|
39
|
+
|
|
40
|
+
function Write-Event($obj) {
|
|
41
|
+
$json = $obj | ConvertTo-Json -Compress
|
|
42
|
+
[Console]::Out.WriteLine($json)
|
|
43
|
+
[Console]::Out.Flush()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function Add-MenuItem($index, $title, $enabled) {
|
|
47
|
+
$item = New-Object System.Windows.Forms.ToolStripMenuItem
|
|
48
|
+
$item.Text = $title
|
|
49
|
+
$item.Enabled = $enabled
|
|
50
|
+
$idx = $index
|
|
51
|
+
$item.Add_Click({ Write-Event @{ type = "click"; index = $idx } }.GetNewClosure())
|
|
52
|
+
$script:menu.Items.Add($item) | Out-Null
|
|
53
|
+
$script:items += $item
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# A real ToolStripSeparator draws the divider. Sending a "-" title instead
|
|
57
|
+
# renders a literal dash row, which is what the menu showed before.
|
|
58
|
+
function Add-MenuSeparator($index) {
|
|
59
|
+
$item = New-Object System.Windows.Forms.ToolStripSeparator
|
|
60
|
+
$script:menu.Items.Add($item) | Out-Null
|
|
61
|
+
$script:items += $item
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function Update-MenuItem($index, $title, $enabled) {
|
|
65
|
+
if ($index -lt $script:items.Count) {
|
|
66
|
+
$script:items[$index].Text = $title
|
|
67
|
+
$script:items[$index].Enabled = $enabled
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function Set-Tooltip($text) {
|
|
72
|
+
# NotifyIcon.Text is capped at 63 characters.
|
|
73
|
+
if ($text.Length -gt 63) { $text = $text.Substring(0, 63) }
|
|
74
|
+
$script:notifyIcon.Text = $text
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Stdin is polled from the UI thread: a background thread reading Console.In
|
|
78
|
+
# races the message loop and deadlocks when the menu is open.
|
|
79
|
+
$script:timer = New-Object System.Windows.Forms.Timer
|
|
80
|
+
$script:timer.Interval = 100
|
|
81
|
+
$script:timer.Add_Tick({
|
|
82
|
+
try {
|
|
83
|
+
while ([Console]::In.Peek() -ne -1) {
|
|
84
|
+
$line = [Console]::In.ReadLine()
|
|
85
|
+
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
|
86
|
+
$cmd = $line | ConvertFrom-Json
|
|
87
|
+
switch ($cmd.action) {
|
|
88
|
+
"add-item" { Add-MenuItem $cmd.index $cmd.title $cmd.enabled }
|
|
89
|
+
"add-separator" { Add-MenuSeparator $cmd.index }
|
|
90
|
+
"update-item" { Update-MenuItem $cmd.index $cmd.title $cmd.enabled }
|
|
91
|
+
"set-tooltip" { Set-Tooltip $cmd.text }
|
|
92
|
+
"ready" { Write-Event @{ type = "ready" } }
|
|
93
|
+
"kill" {
|
|
94
|
+
$script:notifyIcon.Visible = $false
|
|
95
|
+
$script:notifyIcon.Dispose()
|
|
96
|
+
[System.Windows.Forms.Application]::Exit()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
Write-Event @{ type = "error"; message = $_.Exception.Message }
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
$script:timer.Start()
|
|
105
|
+
|
|
106
|
+
Write-Event @{ type = "started" }
|
|
107
|
+
[System.Windows.Forms.Application]::Run()
|
package/scripts/cli/tray.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolvePackagePath } from "../../src/config/package-paths";
|
|
2
2
|
import { resolvePort } from "../../src/config/runtime";
|
|
3
|
-
import { ensureDrainToken } from "./instance";
|
|
3
|
+
import { ensureDrainToken, attachGateway, stopAttachedGateway, type RunningGateway } from "./instance";
|
|
4
4
|
import { startGateway } from "./gateway";
|
|
5
5
|
import { stopGateway } from "./gateway";
|
|
6
6
|
import { readInstalledVersion } from "./version";
|
|
@@ -8,6 +8,8 @@ import { reportTrayStartup } from "./startup-signal";
|
|
|
8
8
|
import { createTray } from "./tray/index";
|
|
9
9
|
import { openUrl } from "./background";
|
|
10
10
|
import { exportBackup, resetConsoleAccount } from "./admin";
|
|
11
|
+
import { acquireController, inheritedControllerHandoff } from "./controller";
|
|
12
|
+
import { restoreControllerAfterFailedHandoff } from "./controller";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Runs the gateway with a tray icon. The tray is platform-specific (`tray/`):
|
|
@@ -39,6 +41,24 @@ const TRAY_HEALTH_POLL_MS = 10_000;
|
|
|
39
41
|
const TRAY_HEALTH_MISSES_BEFORE_CLOSE = 3;
|
|
40
42
|
|
|
41
43
|
export async function startTray(): Promise<void> {
|
|
44
|
+
const handoff = inheritedControllerHandoff();
|
|
45
|
+
const controller = acquireController("tray", handoff);
|
|
46
|
+
const releaseController = (): void => controller.release();
|
|
47
|
+
const failedHandoff = (): void => {
|
|
48
|
+
if (handoff) restoreControllerAfterFailedHandoff(handoff);
|
|
49
|
+
};
|
|
50
|
+
process.once("exit", failedHandoff);
|
|
51
|
+
process.once("exit", releaseController);
|
|
52
|
+
try {
|
|
53
|
+
await runTray();
|
|
54
|
+
} finally {
|
|
55
|
+
process.removeListener("exit", failedHandoff);
|
|
56
|
+
controller.release();
|
|
57
|
+
process.removeListener("exit", releaseController);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function runTray(): Promise<void> {
|
|
42
62
|
const port = resolvePort();
|
|
43
63
|
const drainToken = ensureDrainToken();
|
|
44
64
|
const dashboardUrl = `http://127.0.0.1:${port}/console`;
|
|
@@ -46,6 +66,7 @@ export async function startTray(): Promise<void> {
|
|
|
46
66
|
const currentVersion = await readInstalledVersion();
|
|
47
67
|
|
|
48
68
|
let gateway: Bun.Subprocess | null = null;
|
|
69
|
+
let adoptedGateway: RunningGateway | null = null;
|
|
49
70
|
let closing = false;
|
|
50
71
|
let resetArmed = false;
|
|
51
72
|
let closePromise: Promise<void> | undefined;
|
|
@@ -83,6 +104,7 @@ export async function startTray(): Promise<void> {
|
|
|
83
104
|
const current: Bun.Subprocess | null = gateway;
|
|
84
105
|
gateway = null;
|
|
85
106
|
if (current) await stopGateway(current, port, drainToken);
|
|
107
|
+
else if (adoptedGateway !== null) await stopAttachedGateway(adoptedGateway, drainToken);
|
|
86
108
|
await tray.close();
|
|
87
109
|
process.exitCode = exitCode;
|
|
88
110
|
})();
|
|
@@ -96,7 +118,9 @@ export async function startTray(): Promise<void> {
|
|
|
96
118
|
if (id === "version") return void (await openUrl(releasesUrl));
|
|
97
119
|
if (id === "backup") {
|
|
98
120
|
const result = await exportBackup(port, drainToken);
|
|
99
|
-
if (
|
|
121
|
+
if ("error" in result) {
|
|
122
|
+
console.error(result.error);
|
|
123
|
+
} else {
|
|
100
124
|
console.log(
|
|
101
125
|
`Backup written to ${result.path} (${(result.bytes / 1024).toFixed(1)} KiB). Telemetry is not included.`,
|
|
102
126
|
);
|
|
@@ -129,7 +153,8 @@ export async function startTray(): Promise<void> {
|
|
|
129
153
|
// down too and "Hide to tray" left nothing serving. `stopGateway` drains
|
|
130
154
|
// over HTTP before it ever reaches for `kill()`, so detaching costs the
|
|
131
155
|
// exit path nothing: the tray can still stop the gateway on Exit.
|
|
132
|
-
|
|
156
|
+
adoptedGateway = await attachGateway(port, drainToken);
|
|
157
|
+
if (adoptedGateway === null) gateway = await startGateway(port, drainToken, { detached: true });
|
|
133
158
|
// The launcher that spawned this tray is blocked on a signal file: it
|
|
134
159
|
// cannot read our stdio, because the tray is detached. Without this write
|
|
135
160
|
// it waits out its 60 s deadline and reports a failure for a tray that is
|
|
@@ -146,7 +171,7 @@ export async function startTray(): Promise<void> {
|
|
|
146
171
|
// is up reads as up regardless of who spawned it.
|
|
147
172
|
while (!closing) {
|
|
148
173
|
await Bun.sleep(TRAY_HEALTH_POLL_MS);
|
|
149
|
-
if (closing || gateway === null) continue;
|
|
174
|
+
if (closing || gateway === null && adoptedGateway === null) continue;
|
|
150
175
|
const alive = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
151
176
|
signal: AbortSignal.timeout(2_000),
|
|
152
177
|
}).then((response) => response.ok).catch(() => false);
|
package/scripts/cli/update.ts
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The update path.
|
|
3
|
-
*
|
|
4
|
-
* npm cannot replace this package's directory while a process is executing out
|
|
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.
|
|
10
|
-
*/
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
12
|
-
|
|
13
|
-
export function updateCommand(): string {
|
|
14
|
-
return "npm install -g cartethyia@latest --prefer-online";
|
|
15
|
-
}
|
|
16
|
-
|
|
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
|
-
|
|
33
|
-
export function printUpdateInstructions(from: string, to: string | null): void {
|
|
34
|
-
console.log("");
|
|
35
|
-
console.log(`Update available: ${from} \u2192 ${to ?? "latest"}`);
|
|
36
|
-
console.log("");
|
|
37
|
-
console.log("Starting detached update; this process will exit now.");
|
|
38
|
-
console.log("The gateway will restart automatically after the install.");
|
|
39
|
-
console.log("");
|
|
1
|
+
/**
|
|
2
|
+
* The update path.
|
|
3
|
+
*
|
|
4
|
+
* npm cannot replace this package's directory while a process is executing out
|
|
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.
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
|
|
13
|
+
export function updateCommand(): string {
|
|
14
|
+
return "npm install -g cartethyia@latest --prefer-online";
|
|
15
|
+
}
|
|
16
|
+
|
|
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
|
+
|
|
33
|
+
export function printUpdateInstructions(from: string, to: string | null): void {
|
|
34
|
+
console.log("");
|
|
35
|
+
console.log(`Update available: ${from} \u2192 ${to ?? "latest"}`);
|
|
36
|
+
console.log("");
|
|
37
|
+
console.log("Starting detached update; this process will exit now.");
|
|
38
|
+
console.log("The gateway will restart automatically after the install.");
|
|
39
|
+
console.log("");
|
|
40
40
|
}
|
package/scripts/cli/version.ts
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import { resolvePackagePath } from "../../src/config/package-paths";
|
|
2
|
-
|
|
3
|
-
export async function readRequiredBunVersion(): Promise<string> {
|
|
4
|
-
const metadata: unknown = await Bun.file(resolvePackagePath("package.json")).json();
|
|
5
|
-
if (metadata === null || typeof metadata !== "object" || !("engines" in metadata) ||
|
|
6
|
-
metadata.engines === null || typeof metadata.engines !== "object" ||
|
|
7
|
-
!("bun" in metadata.engines) || typeof metadata.engines.bun !== "string") {
|
|
8
|
-
throw new Error("Cartethyia's required Bun version is unavailable in package.json");
|
|
9
|
-
}
|
|
10
|
-
const minimum = metadata.engines.bun.trim().replace(/^>=\s*/, "");
|
|
11
|
-
if (!/^\d+(?:\.\d+){0,2}$/.test(minimum)) throw new Error(`Unsupported Bun engine requirement: ${metadata.engines.bun}`);
|
|
12
|
-
return minimum;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function bunVersionAtLeast(actual: string, minimum: string): boolean {
|
|
16
|
-
const left = actual.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
17
|
-
const right = minimum.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
18
|
-
const length = Math.max(left.length, right.length);
|
|
19
|
-
for (let i = 0; i < length; i += 1) {
|
|
20
|
-
const a = left[i] ?? 0;
|
|
21
|
-
const b = right[i] ?? 0;
|
|
22
|
-
if (a !== b) return a > b;
|
|
23
|
-
}
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function assertBunVersion(): Promise<void> {
|
|
28
|
-
const minimum = await readRequiredBunVersion();
|
|
29
|
-
if (bunVersionAtLeast(Bun.version, minimum)) return;
|
|
30
|
-
throw new Error(`Bun ${minimum} or newer is required (found ${Bun.version}). Install it from https://bun.sh and run this command again.`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Reads the version from the installed package manifest. */
|
|
34
|
-
export async function readInstalledVersion(): Promise<string> {
|
|
35
|
-
const metadata: unknown = await Bun.file(resolvePackagePath("package.json")).json();
|
|
36
|
-
if (
|
|
37
|
-
metadata === null ||
|
|
38
|
-
typeof metadata !== "object" ||
|
|
39
|
-
!("version" in metadata) ||
|
|
40
|
-
typeof metadata.version !== "string"
|
|
41
|
-
) {
|
|
42
|
-
throw new Error("Cartethyia package version is unavailable");
|
|
43
|
-
}
|
|
44
|
-
return metadata.version;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Asks the npm registry for the newest published version, or null on failure. */
|
|
48
|
-
export async function readLatestPublishedVersion(): Promise<string | null> {
|
|
49
|
-
try {
|
|
50
|
-
const response = await fetch("https://registry.npmjs.org/cartethyia/latest", {
|
|
51
|
-
signal: AbortSignal.timeout(4_000),
|
|
52
|
-
});
|
|
53
|
-
if (!response.ok) return null;
|
|
54
|
-
const metadata: unknown = await response.json();
|
|
55
|
-
if (
|
|
56
|
-
metadata !== null &&
|
|
57
|
-
typeof metadata === "object" &&
|
|
58
|
-
"version" in metadata &&
|
|
59
|
-
typeof metadata.version === "string"
|
|
60
|
-
) {
|
|
61
|
-
return metadata.version;
|
|
62
|
-
}
|
|
63
|
-
return null;
|
|
64
|
-
} catch {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
1
|
+
import { resolvePackagePath } from "../../src/config/package-paths";
|
|
2
|
+
|
|
3
|
+
export async function readRequiredBunVersion(): Promise<string> {
|
|
4
|
+
const metadata: unknown = await Bun.file(resolvePackagePath("package.json")).json();
|
|
5
|
+
if (metadata === null || typeof metadata !== "object" || !("engines" in metadata) ||
|
|
6
|
+
metadata.engines === null || typeof metadata.engines !== "object" ||
|
|
7
|
+
!("bun" in metadata.engines) || typeof metadata.engines.bun !== "string") {
|
|
8
|
+
throw new Error("Cartethyia's required Bun version is unavailable in package.json");
|
|
9
|
+
}
|
|
10
|
+
const minimum = metadata.engines.bun.trim().replace(/^>=\s*/, "");
|
|
11
|
+
if (!/^\d+(?:\.\d+){0,2}$/.test(minimum)) throw new Error(`Unsupported Bun engine requirement: ${metadata.engines.bun}`);
|
|
12
|
+
return minimum;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function bunVersionAtLeast(actual: string, minimum: string): boolean {
|
|
16
|
+
const left = actual.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
17
|
+
const right = minimum.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
18
|
+
const length = Math.max(left.length, right.length);
|
|
19
|
+
for (let i = 0; i < length; i += 1) {
|
|
20
|
+
const a = left[i] ?? 0;
|
|
21
|
+
const b = right[i] ?? 0;
|
|
22
|
+
if (a !== b) return a > b;
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function assertBunVersion(): Promise<void> {
|
|
28
|
+
const minimum = await readRequiredBunVersion();
|
|
29
|
+
if (bunVersionAtLeast(Bun.version, minimum)) return;
|
|
30
|
+
throw new Error(`Bun ${minimum} or newer is required (found ${Bun.version}). Install it from https://bun.sh and run this command again.`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Reads the version from the installed package manifest. */
|
|
34
|
+
export async function readInstalledVersion(): Promise<string> {
|
|
35
|
+
const metadata: unknown = await Bun.file(resolvePackagePath("package.json")).json();
|
|
36
|
+
if (
|
|
37
|
+
metadata === null ||
|
|
38
|
+
typeof metadata !== "object" ||
|
|
39
|
+
!("version" in metadata) ||
|
|
40
|
+
typeof metadata.version !== "string"
|
|
41
|
+
) {
|
|
42
|
+
throw new Error("Cartethyia package version is unavailable");
|
|
43
|
+
}
|
|
44
|
+
return metadata.version;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Asks the npm registry for the newest published version, or null on failure. */
|
|
48
|
+
export async function readLatestPublishedVersion(): Promise<string | null> {
|
|
49
|
+
try {
|
|
50
|
+
const response = await fetch("https://registry.npmjs.org/cartethyia/latest", {
|
|
51
|
+
signal: AbortSignal.timeout(4_000),
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) return null;
|
|
54
|
+
const metadata: unknown = await response.json();
|
|
55
|
+
if (
|
|
56
|
+
metadata !== null &&
|
|
57
|
+
typeof metadata === "object" &&
|
|
58
|
+
"version" in metadata &&
|
|
59
|
+
typeof metadata.version === "string"
|
|
60
|
+
) {
|
|
61
|
+
return metadata.version;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
package/scripts/cli/wordmark.ts
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The console wordmark.
|
|
3
|
-
*
|
|
4
|
-
* Rendered with half-block characters so each letter is two pixel rows tall in
|
|
5
|
-
* one character cell, and coloured with a horizontal gradient across the
|
|
6
|
-
* Cartethyia blue-grey range. The caller decides whether to print it: a
|
|
7
|
-
* non-colour terminal, or one narrower than the wordmark, should skip it.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const WORDMARK: readonly string[] = [
|
|
11
|
-
" ██████╗ █████╗ ██████╗ ████████╗███████╗████████╗██╗ ██╗██╗ ██╗██╗ █████╗ ",
|
|
12
|
-
"██╔════╝██╔══██╗██╔══██╗╚══██╔══╝██╔════╝╚══██╔══╝██║ ██║╚██╗ ██╔╝██║██╔══██╗",
|
|
13
|
-
"██║ ███████║██████╔╝ ██║ █████╗ ██║ ███████║ ╚████╔╝ ██║███████║",
|
|
14
|
-
"██║ ██╔══██║██╔══██╗ ██║ ██╔══╝ ██║ ██╔══██║ ╚██╔╝ ██║██╔══██║",
|
|
15
|
-
"╚██████╗██║ ██║██║ ██║ ██║ ███████╗ ██║ ██║ ██║ ██║ ██║██║ ██║",
|
|
16
|
-
" ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝",
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
/** Blue-grey gradient: dim at the edges, brighter through the middle. */
|
|
20
|
-
const GRADIENT: readonly [number, number, number][] = [
|
|
21
|
-
[90, 120, 150],
|
|
22
|
-
[120, 160, 200],
|
|
23
|
-
[150, 200, 240],
|
|
24
|
-
[120, 160, 200],
|
|
25
|
-
[90, 120, 150],
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
const RESET = "\u001b[0m";
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Returns the wordmark lines with per-column colour, or an empty array when the
|
|
32
|
-
* terminal is too narrow or colour is disabled.
|
|
33
|
-
*/
|
|
34
|
-
export function renderWordmark(columns = process.stdout.columns ?? 80): string[] {
|
|
35
|
-
if (process.env.NO_COLOR !== undefined) return [];
|
|
36
|
-
const width = WORDMARK[0]!.length;
|
|
37
|
-
if (columns < width) return [];
|
|
38
|
-
|
|
39
|
-
const coloured: string[] = [];
|
|
40
|
-
for (const row of WORDMARK) {
|
|
41
|
-
let line = "";
|
|
42
|
-
let current = -1;
|
|
43
|
-
for (let column = 0; column < row.length; column += 1) {
|
|
44
|
-
const band = Math.min(
|
|
45
|
-
GRADIENT.length - 1,
|
|
46
|
-
Math.floor((column / width) * GRADIENT.length),
|
|
47
|
-
);
|
|
48
|
-
const [r, g, b] = GRADIENT[band]!;
|
|
49
|
-
if (band !== current) {
|
|
50
|
-
line += `\u001b[38;2;${r};${g};${b}m`;
|
|
51
|
-
current = band;
|
|
52
|
-
}
|
|
53
|
-
line += row[column];
|
|
54
|
-
}
|
|
55
|
-
coloured.push(`${line}${RESET}`);
|
|
56
|
-
}
|
|
57
|
-
return coloured;
|
|
58
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* The console wordmark.
|
|
3
|
+
*
|
|
4
|
+
* Rendered with half-block characters so each letter is two pixel rows tall in
|
|
5
|
+
* one character cell, and coloured with a horizontal gradient across the
|
|
6
|
+
* Cartethyia blue-grey range. The caller decides whether to print it: a
|
|
7
|
+
* non-colour terminal, or one narrower than the wordmark, should skip it.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const WORDMARK: readonly string[] = [
|
|
11
|
+
" ██████╗ █████╗ ██████╗ ████████╗███████╗████████╗██╗ ██╗██╗ ██╗██╗ █████╗ ",
|
|
12
|
+
"██╔════╝██╔══██╗██╔══██╗╚══██╔══╝██╔════╝╚══██╔══╝██║ ██║╚██╗ ██╔╝██║██╔══██╗",
|
|
13
|
+
"██║ ███████║██████╔╝ ██║ █████╗ ██║ ███████║ ╚████╔╝ ██║███████║",
|
|
14
|
+
"██║ ██╔══██║██╔══██╗ ██║ ██╔══╝ ██║ ██╔══██║ ╚██╔╝ ██║██╔══██║",
|
|
15
|
+
"╚██████╗██║ ██║██║ ██║ ██║ ███████╗ ██║ ██║ ██║ ██║ ██║██║ ██║",
|
|
16
|
+
" ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
/** Blue-grey gradient: dim at the edges, brighter through the middle. */
|
|
20
|
+
const GRADIENT: readonly [number, number, number][] = [
|
|
21
|
+
[90, 120, 150],
|
|
22
|
+
[120, 160, 200],
|
|
23
|
+
[150, 200, 240],
|
|
24
|
+
[120, 160, 200],
|
|
25
|
+
[90, 120, 150],
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const RESET = "\u001b[0m";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns the wordmark lines with per-column colour, or an empty array when the
|
|
32
|
+
* terminal is too narrow or colour is disabled.
|
|
33
|
+
*/
|
|
34
|
+
export function renderWordmark(columns = process.stdout.columns ?? 80): string[] {
|
|
35
|
+
if (process.env.NO_COLOR !== undefined) return [];
|
|
36
|
+
const width = WORDMARK[0]!.length;
|
|
37
|
+
if (columns < width) return [];
|
|
38
|
+
|
|
39
|
+
const coloured: string[] = [];
|
|
40
|
+
for (const row of WORDMARK) {
|
|
41
|
+
let line = "";
|
|
42
|
+
let current = -1;
|
|
43
|
+
for (let column = 0; column < row.length; column += 1) {
|
|
44
|
+
const band = Math.min(
|
|
45
|
+
GRADIENT.length - 1,
|
|
46
|
+
Math.floor((column / width) * GRADIENT.length),
|
|
47
|
+
);
|
|
48
|
+
const [r, g, b] = GRADIENT[band]!;
|
|
49
|
+
if (band !== current) {
|
|
50
|
+
line += `\u001b[38;2;${r};${g};${b}m`;
|
|
51
|
+
current = band;
|
|
52
|
+
}
|
|
53
|
+
line += row[column];
|
|
54
|
+
}
|
|
55
|
+
coloured.push(`${line}${RESET}`);
|
|
56
|
+
}
|
|
57
|
+
return coloured;
|
|
58
|
+
}
|