cartethyia 2.5.4 → 2.5.6
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 +13 -4
- package/README.md +2 -2
- package/dist/dashboard/assets/{BannedPage-C2orBsI2.js → BannedPage-CADDq4IU.js} +1 -1
- package/dist/dashboard/assets/BannedPage-CADDq4IU.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BV6pxVQo.js → CliToolsPage-DuO1Z7dq.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DuO1Z7dq.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DNEPi3xU.js +1 -0
- package/dist/dashboard/assets/CombosPage-DNEPi3xU.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CW-wPmJ6.js → ConsoleLogPage-BVdpfiz1.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-BVdpfiz1.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-0p8uX7vB.js → CustomizationPage-BR5HHvbi.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BR5HHvbi.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-BjqSMfko.js → HealthEventsModal-mUfcknqm.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-mUfcknqm.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-BvzCx8Kr.js → LoginPage-V5faV9V2.js} +1 -1
- package/dist/dashboard/assets/LoginPage-V5faV9V2.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js +1 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-Js9jUPSb.js → ProvidersPage-uPiGVTTK.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-uPiGVTTK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js +7 -0
- package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js +1 -0
- package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-DDbMQsoo.js → SetupPage-UB690SE2.js} +1 -1
- package/dist/dashboard/assets/SetupPage-UB690SE2.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-CB3Z7VTf.js → SortableList-BLujdX7N.js} +1 -1
- package/dist/dashboard/assets/SortableList-BLujdX7N.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-D95UCPHI.js → StudioPage-D5C2raAc.js} +3 -3
- package/dist/dashboard/assets/StudioPage-D5C2raAc.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-8uIcxa1W.js +56 -0
- package/dist/dashboard/assets/UsagePage-8uIcxa1W.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js +1 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +2 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/{assert-Chi7pCnt.js → assert-C9FD_JwJ.js} +1 -1
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D4pQapKj.js → badge-CR72dasT.js} +1 -1
- package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DH6cg-_B.js → banner-CI9_eSfO.js} +1 -1
- package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
- package/dist/dashboard/assets/{card-BKqlUOe4.js → card-DS7CQEsO.js} +1 -1
- package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-fTgljTGA.js → cli-tools-CNqzrfdb.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-D5Ys_sM0.js → clipboard-button-611pSsaX.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
- package/dist/dashboard/assets/{fingerprint-pattern-BWuHtbW4.js → concurrency-BDF0ok7y.js} +1 -1
- package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js +1 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js +1 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CQBr6U9c.js → entry-B9Xvtnfj.js} +1 -1
- package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js +1 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
- package/dist/dashboard/assets/entry-DxBHnauy.js +4 -0
- package/dist/dashboard/assets/entry-DxBHnauy.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-BlptL8Uf.js → github-badge-sltk1qUi.js} +1 -1
- package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
- package/dist/dashboard/assets/{index-js6TJvdr.js → index-CmxZr14F.js} +2 -2
- package/dist/dashboard/assets/index-CmxZr14F.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DUWDSmDV.js → inline-BhY2sSnO.js} +1 -1
- package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
- package/dist/dashboard/assets/{input-LVjgZq4f.js → input-V1umFiBk.js} +1 -1
- package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js +1 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-DupvLfDx.js → list-row-1zzZYM0B.js} +1 -1
- package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
- package/dist/dashboard/assets/{live-C4pEJUqI.js → live-BivGfuUs.js} +1 -1
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-BRiuufkB.js → model-bans-CmffjLuG.js} +1 -1
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-B4ROwk8L.js → mutation-46P0oWMe.js} +1 -1
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js +2 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-gmjzC3RS.js → query-keys-BFhGpwR1.js} +1 -1
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/{routing-C7tBT5o-.js → routing-CAb4F37R.js} +1 -1
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/{select-WUCXorXw.js → select-BYR_5K2c.js} +1 -1
- package/dist/dashboard/assets/select-BYR_5K2c.js.gz +0 -0
- package/dist/dashboard/assets/{settings-C_zKl1GS.js → settings-DNX9sydi.js} +1 -1
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/{stack-_3O0F8Xn.js → stack-BHTfXdz_.js} +1 -1
- package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
- package/dist/dashboard/assets/{state-BgTxHcJS.js → state-mPDRlQkl.js} +1 -1
- package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DEjTtOWf.js → switch-DOmJxg7H.js} +1 -1
- package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
- package/dist/dashboard/assets/tokens-COzario9.js +1 -0
- package/dist/dashboard/assets/tokens-COzario9.js.gz +0 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js +1 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BWSC0UAS.js → useMutation-Dem0lXQj.js} +1 -1
- package/dist/dashboard/assets/useMutation-Dem0lXQj.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 +54 -54
- package/migrations/README.md +52 -52
- package/migrations/lite-sqlite3/0000_baseline.sql +2 -2
- package/package.json +96 -95
- package/scripts/cli/admin.ts +125 -0
- package/scripts/cli/{ui/background.ts → background.ts} +1 -1
- package/scripts/cli/{command/console-menu.ts → console-menu.ts} +275 -244
- package/scripts/cli/doctor.ts +37 -0
- package/scripts/cli/{support/env.ts → env.ts} +99 -99
- package/scripts/cli/{gateway/spawn.ts → gateway.ts} +112 -24
- package/scripts/cli/instance.ts +224 -0
- package/scripts/cli/main.ts +61 -18
- package/scripts/cli/{support/platform.ts → platform.ts} +64 -64
- package/scripts/cli/port.ts +100 -0
- package/scripts/cli/{support/services.ts → services.ts} +88 -88
- package/scripts/cli/{command/setup.ts → setup.ts} +35 -34
- package/scripts/cli/{command/start.ts → start.ts} +10 -5
- package/scripts/cli/{support/startup-signal.ts → startup-signal.ts} +23 -23
- package/scripts/cli/tray/index.ts +62 -12
- package/scripts/cli/{command/tray.ts → tray.ts} +208 -208
- package/scripts/cli/{command/update.ts → update.ts} +39 -39
- package/scripts/cli/version.ts +67 -0
- package/scripts/cli/{ui/wordmark.ts → wordmark.ts} +58 -58
- package/src/boot/app.ts +697 -693
- package/src/boot/dependencies.ts +647 -646
- package/src/boot/timeout.ts +2 -13
- package/src/config/cooldowns.ts +186 -138
- package/src/config/network.ts +8 -14
- package/src/config/providers.ts +7 -10
- package/src/config/runtime.ts +76 -76
- package/src/config/security.ts +15 -0
- package/src/config/spec.ts +20 -30
- package/src/config/telemetry.ts +0 -20
- package/src/config/timeouts.ts +0 -6
- package/src/console/admin/model-abuse/contracts.ts +28 -6
- package/src/console/admin/model-abuse/routes.ts +56 -3
- package/src/console/api-keys/contracts.ts +410 -414
- package/src/console/api-keys/routes.ts +881 -880
- package/src/console/auth/credentials.ts +3 -3
- package/src/console/auth/setup-service.ts +2 -2
- package/src/console/backup/export-endpoint.ts +6 -2
- package/src/console/backup/nine-router.ts +2 -5
- package/src/console/backup/service.ts +4 -0
- package/src/console/backup/store.ts +192 -29
- package/src/console/backup/validate.ts +3 -45
- package/src/console/cli-tools/fs-ops.ts +487 -487
- package/src/console/console-router-guards.ts +1 -1
- package/src/console/data-management/contracts.ts +13 -6
- package/src/console/data-management/operations.ts +23 -18
- package/src/console/data-management/routes.ts +0 -1
- package/src/console/domain-registration.ts +10 -5
- package/src/console/observability/live.ts +22 -0
- package/src/console/observability/store.ts +39 -16
- package/src/console/observability/usage-dimensions.ts +4 -4
- package/src/console/providers/catalog/catalog-projections.ts +6 -7
- package/src/console/providers/catalog/model-operations.ts +5 -2
- package/src/console/providers/catalog/provider-operations.ts +4 -4
- package/src/console/providers/catalog/public-model-store.ts +8 -11
- package/src/console/providers/catalog/store.ts +1553 -1553
- package/src/console/providers/oauth/routes.ts +8 -12
- package/src/console/quota/account-quota-tenant.ts +6 -6
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/contracts.ts +61 -0
- package/src/console/remote-support/routes.ts +65 -0
- package/src/console/remote-support/service.ts +207 -0
- package/src/console/response-compression.ts +0 -3
- package/src/console/routing/pools/probe-result.ts +3 -2
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/store.ts +8 -8
- package/src/console/settings/contracts.ts +58 -2
- package/src/console/settings/store.ts +30 -3
- package/src/console/shared/cursor.ts +8 -8
- package/src/console/shared/errors.ts +1 -3
- package/src/network/http2-bidi.ts +2 -4
- package/src/network/pool/agent.ts +478 -478
- package/src/network/pool/selector.ts +5 -4
- package/src/network/retry.ts +1 -2
- package/src/network/ssrf.ts +5 -4
- package/src/observability/event-compaction.ts +80 -78
- package/src/observability/event-day-index.ts +13 -15
- package/src/observability/event-log.ts +4 -4
- package/src/observability/logger.ts +6 -6
- package/src/observability/payload-capture.ts +151 -33
- package/src/observability/payload-store.ts +397 -440
- package/src/observability/redaction.ts +5 -6
- package/src/observability/request-notify.ts +43 -0
- package/src/observability/telemetry-status.ts +8 -9
- package/src/observability/token-speed.ts +5 -4
- package/src/persistence/db-mode.ts +7 -7
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/schema.ts +1406 -1402
- package/src/persistence/shared-cache.ts +1 -2
- package/src/persistence/stores/api-key-store.ts +18 -6
- package/src/persistence/stores/share-store.ts +4 -3
- package/src/persistence/stores/telemetry-store.ts +236 -237
- package/src/protocol/canonical-model.ts +11 -3
- package/src/protocol/codecs.ts +138 -4
- package/src/protocol/gateway-error.ts +6 -4
- package/src/protocol/request/chat.ts +21 -1
- package/src/protocol/request/codex.ts +10 -1
- package/src/protocol/request/messages.ts +4 -5
- package/src/protocol/request/responses.ts +9 -1
- package/src/protocol/response/chat.ts +34 -4
- package/src/protocol/response/codex.ts +7 -4
- package/src/protocol/response/dsml.ts +140 -28
- package/src/protocol/response/responses.ts +11 -5
- package/src/protocol/stream-error-frames.ts +4 -5
- package/src/providers/authentication/jwt-validator.ts +9 -10
- package/src/providers/authentication/oauth-refresh-service.ts +9 -11
- package/src/providers/configured-openai-providers.ts +134 -136
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/catalog/catalog-format.ts +3 -6
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +4 -4
- package/src/providers/discovery/probe-wire.ts +6 -13
- package/src/providers/discovery/probing-service.ts +1117 -1045
- package/src/providers/integrations/alibabacp/alibabacp.ts +1 -1
- package/src/providers/integrations/antigravity/antigravity-quota.ts +5 -7
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +23 -2
- package/src/providers/integrations/buddy/buddy-checkin.ts +1 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +23 -6
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -1
- package/src/providers/integrations/buddy/buddy-identity.ts +52 -0
- package/src/providers/integrations/buddy/buddy-media.ts +2 -2
- package/src/providers/integrations/buddy/buddy-usage-store.ts +3 -4
- package/src/providers/integrations/buddy/codebuddy/quota.ts +2 -13
- package/src/providers/integrations/buddy/codebuddy/shared.ts +118 -115
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -11
- package/src/providers/integrations/buddy/workbuddy/shared.ts +5 -2
- package/src/providers/integrations/claude/claude.ts +608 -608
- package/src/providers/integrations/cline/cline-quota.ts +132 -132
- package/src/providers/integrations/cline/cline.ts +537 -539
- package/src/providers/integrations/codex/codex-oauth.ts +1 -1
- package/src/providers/integrations/commandcode/commandcode.ts +674 -675
- package/src/providers/integrations/commandcode/discovery.ts +117 -116
- package/src/providers/integrations/cursor/cursor-auth.ts +418 -418
- package/src/providers/integrations/cursor/cursor-quota.ts +309 -310
- package/src/providers/integrations/cursor/cursor.ts +11 -163
- package/src/providers/integrations/factory/factory-models.ts +3 -5
- package/src/providers/integrations/grok/grok-install-id.ts +2 -3
- package/src/providers/integrations/grok/grok.ts +6 -7
- package/src/providers/integrations/hermes.ts +14 -18
- package/src/providers/integrations/kiro/kiro-discovery.ts +1 -1
- package/src/providers/integrations/kiro/kiro.ts +1 -1
- package/src/providers/integrations/mistral.ts +6 -6
- package/src/providers/integrations/opencode/fingerprint.ts +3 -3
- package/src/providers/integrations/opencode/provider.ts +8 -8
- package/src/providers/integrations/qoder/qoder-catalog.ts +450 -450
- package/src/providers/integrations/qoder/qoder-oauth.ts +388 -310
- package/src/providers/integrations/qoder/qoder.ts +884 -865
- package/src/providers/integrations/shared/connect.ts +6 -6
- package/src/providers/integrations/trae/trae-catalog.ts +1 -2
- package/src/providers/integrations/trae/trae.ts +2 -2
- package/src/providers/integrations/zcode/oauth.ts +6 -1
- package/src/providers/operations/account-health-service.ts +1528 -1523
- package/src/providers/operations/account-reset-service.ts +1 -1
- package/src/providers/operations/client-versions.ts +6 -6
- package/src/providers/operations/custom-cli-headers.ts +3 -4
- package/src/providers/operations/provider-catalog-seeder.ts +95 -95
- package/src/providers/operations/provider-catalog-service.ts +3 -4
- package/src/providers/provider-metadata.ts +4 -8
- package/src/providers/provider-registry.ts +6 -7
- package/src/providers/provider-usage.ts +1 -1
- package/src/security/abuse.ts +15 -10
- package/src/security/access-control.ts +144 -146
- package/src/security/admission/contracts.ts +11 -0
- package/src/security/admission/in-memory-store.ts +4 -3
- package/src/security/admission/lease-sweep.ts +6 -6
- package/src/security/admission/reasons.ts +5 -1
- package/src/security/admission/redis-store.ts +211 -210
- package/src/security/admission/service.ts +4 -0
- package/src/security/api-key-auth.ts +504 -504
- package/src/security/ip-boundary.ts +155 -15
- package/src/security/model-abuse.ts +366 -94
- package/src/security/outbound-headers.ts +3 -4
- package/src/shared/sleep.ts +4 -0
- package/src/transport/dispatch/attempt-finalize.ts +47 -5
- package/src/transport/dispatch/attempt-loop.ts +531 -530
- package/src/transport/dispatch/stream-bridge.ts +1 -3
- package/src/transport/dispatch/streaming/finalize.ts +1 -1
- package/src/transport/dispatch/streaming/stream.ts +5 -6
- package/src/transport/failure-policy.ts +162 -8
- package/src/transport/guards/auth-guard.ts +7 -4
- package/src/transport/guards/concurrency-guard.ts +0 -5
- package/src/transport/guards/error-lifecycle.ts +13 -6
- package/src/transport/guards/pipeline.ts +15 -3
- package/src/transport/guards/request-context.ts +8 -2
- package/src/transport/request/inflight.ts +5 -6
- package/src/transport/request/preparer.ts +2 -2
- package/src/transport/request/state.ts +12 -8
- package/src/transport/routing/admission.ts +4 -0
- package/src/transport/routing/route-catalog.ts +8 -1
- package/src/transport/routing/route-model.ts +46 -14
- package/src/transport/routing/router.ts +21 -14
- package/src/transport/surface/completion.ts +2 -3
- package/src/transport/surface/dialects.ts +5 -6
- package/src/transport/surface/responses/encode.ts +38 -0
- package/src/transport/surface/responses/parse.ts +4 -0
- package/src/transport/translation/capabilities.ts +2 -3
- package/src/transport/translation/quirks.ts +3 -3
- package/src/workers/buddy-growth-tasks.ts +5 -10
- package/src/workers/daily-checkin.ts +17 -23
- package/src/workers/quota-refresh-sweep.ts +26 -26
- package/dist/dashboard/assets/BannedPage-C2orBsI2.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BV6pxVQo.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-Bsan4679.js +0 -1
- package/dist/dashboard/assets/CombosPage-Bsan4679.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CW-wPmJ6.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-0p8uX7vB.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BvzCx8Kr.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js +0 -1
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js +0 -1
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js +0 -7
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js +0 -1
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-DDbMQsoo.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-CB3Z7VTf.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-D95UCPHI.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js +0 -1
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js.gz +0 -0
- package/dist/dashboard/assets/app-COlK95oi.css +0 -2
- package/dist/dashboard/assets/app-COlK95oi.css.gz +0 -0
- package/dist/dashboard/assets/assert-Chi7pCnt.js.gz +0 -0
- package/dist/dashboard/assets/badge-D4pQapKj.js.gz +0 -0
- package/dist/dashboard/assets/banner-DH6cg-_B.js.gz +0 -0
- package/dist/dashboard/assets/card-BKqlUOe4.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-fTgljTGA.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-D5Ys_sM0.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BYj-8WuK.js +0 -1
- package/dist/dashboard/assets/dialog-BYj-8WuK.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js +0 -1
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js.gz +0 -0
- package/dist/dashboard/assets/entry-CBg5zeu8.js +0 -1
- package/dist/dashboard/assets/entry-CBg5zeu8.js.gz +0 -0
- package/dist/dashboard/assets/entry-CQBr6U9c.js.gz +0 -0
- package/dist/dashboard/assets/entry-CgdSAzEV.js +0 -4
- package/dist/dashboard/assets/entry-CgdSAzEV.js.gz +0 -0
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-BlptL8Uf.js.gz +0 -0
- package/dist/dashboard/assets/index-js6TJvdr.js.gz +0 -0
- package/dist/dashboard/assets/inline-DUWDSmDV.js.gz +0 -0
- package/dist/dashboard/assets/input-LVjgZq4f.js.gz +0 -0
- package/dist/dashboard/assets/layout-CPfxpsfy.js +0 -1
- package/dist/dashboard/assets/layout-CPfxpsfy.js.gz +0 -0
- package/dist/dashboard/assets/list-row-DupvLfDx.js.gz +0 -0
- package/dist/dashboard/assets/live-C4pEJUqI.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-BRiuufkB.js.gz +0 -0
- package/dist/dashboard/assets/mutation-B4ROwk8L.js.gz +0 -0
- package/dist/dashboard/assets/numbers-YNcHvRY7.js +0 -2
- package/dist/dashboard/assets/numbers-YNcHvRY7.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-gmjzC3RS.js.gz +0 -0
- package/dist/dashboard/assets/routing-C7tBT5o-.js.gz +0 -0
- package/dist/dashboard/assets/select-WUCXorXw.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_zKl1GS.js.gz +0 -0
- package/dist/dashboard/assets/stack-_3O0F8Xn.js.gz +0 -0
- package/dist/dashboard/assets/state-BgTxHcJS.js.gz +0 -0
- package/dist/dashboard/assets/switch-DEjTtOWf.js.gz +0 -0
- package/dist/dashboard/assets/tokens-DDxMbSRL.js +0 -1
- package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js +0 -1
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BWSC0UAS.js.gz +0 -0
- package/scripts/cli/command/admin.ts +0 -65
- package/scripts/cli/command/doctor.ts +0 -257
- package/scripts/cli/gateway/drain-token.ts +0 -78
- package/scripts/cli/gateway/instance-lock.ts +0 -105
- package/scripts/cli/gateway/stop.ts +0 -51
- package/scripts/cli/support/bun.ts +0 -27
- package/scripts/cli/support/version.ts +0 -37
- /package/dist/dashboard/assets/{app-DCQZRmHY.js → app-BIt446yE.js} +0 -0
- /package/dist/dashboard/assets/{app-DCQZRmHY.js.gz → app-BIt446yE.js.gz} +0 -0
- /package/scripts/cli/{ui/prompt.ts → prompt.ts} +0 -0
|
@@ -1,208 +1,208 @@
|
|
|
1
|
-
import { isNewerRelease } from "
|
|
2
|
-
import { resolvePackagePath } from "
|
|
3
|
-
import { resolvePort } from "
|
|
4
|
-
import { ensureDrainToken } from "
|
|
5
|
-
import { startGateway } from "
|
|
6
|
-
import { stopGateway } from "
|
|
7
|
-
import { readInstalledVersion, readLatestPublishedVersion } from "
|
|
8
|
-
import { reportTrayStartup } from "
|
|
9
|
-
import { createTray } from "
|
|
10
|
-
import { openUrl } from "
|
|
11
|
-
import { exportBackup, resetConsoleAccount } from "./admin";
|
|
12
|
-
import { printUpdateInstructions } from "./update";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Runs the gateway with a tray icon. The tray is platform-specific (`tray/`):
|
|
16
|
-
* on Windows it is a PowerShell NotifyIcon, because a Node tray library leaves
|
|
17
|
-
* an empty console tab open for the tray's whole lifetime, and that window is
|
|
18
|
-
* owned by Windows Terminal — not by this process, so it cannot be found and
|
|
19
|
-
* hidden from inside.
|
|
20
|
-
*
|
|
21
|
-
* The gateway it starts is detached, which is what makes the health poll below
|
|
22
|
-
* necessary — see {@link TRAY_HEALTH_POLL_MS}.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* How often the tray asks whether the gateway is still answering.
|
|
27
|
-
*
|
|
28
|
-
* 10 s: often enough that an operator who stops the gateway by hand sees the
|
|
29
|
-
* icon go away, and far below the cost of a request. A detached gateway can no
|
|
30
|
-
* longer be waited on, so this poll is the only signal left.
|
|
31
|
-
*/
|
|
32
|
-
const TRAY_HEALTH_POLL_MS = 10_000;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Consecutive misses to forgive before a dead gateway closes the tray.
|
|
36
|
-
*
|
|
37
|
-
* One refusal is not a stopped gateway — a probe can lose a race with startup
|
|
38
|
-
* or a slow request — and closing the icon on one would make startup feel
|
|
39
|
-
* random.
|
|
40
|
-
*/
|
|
41
|
-
const TRAY_HEALTH_MISSES_BEFORE_CLOSE = 3;
|
|
42
|
-
|
|
43
|
-
export async function startTray(): Promise<void> {
|
|
44
|
-
const port = resolvePort();
|
|
45
|
-
const drainToken = ensureDrainToken();
|
|
46
|
-
const dashboardUrl = `http://127.0.0.1:${port}/console`;
|
|
47
|
-
const repositoryUrl = "https://github.com/risunCode/cartethyia";
|
|
48
|
-
const currentVersion = await readInstalledVersion();
|
|
49
|
-
|
|
50
|
-
let gateway: Bun.Subprocess | null = null;
|
|
51
|
-
let closing = false;
|
|
52
|
-
let busy = false;
|
|
53
|
-
let resetArmed = false;
|
|
54
|
-
let latestVersion: string | null = null;
|
|
55
|
-
let closePromise: Promise<void> | undefined;
|
|
56
|
-
|
|
57
|
-
const tray = createTray({
|
|
58
|
-
iconPath: resolvePackagePath(
|
|
59
|
-
"scripts",
|
|
60
|
-
"cli",
|
|
61
|
-
"tray",
|
|
62
|
-
"assets",
|
|
63
|
-
process.platform === "win32" ? "cartethyia.ico" : "cartethyia.png",
|
|
64
|
-
),
|
|
65
|
-
tooltip: `Cartethyia ${currentVersion}`,
|
|
66
|
-
items: [
|
|
67
|
-
{ kind: "action", id: "update", title: `Cartethyia ${currentVersion} \u00b7 checking\u2026`, enabled: true },
|
|
68
|
-
{ kind: "separator" },
|
|
69
|
-
{ kind: "action", id: "console", title: `Open Console \u00b7 ${dashboardUrl}` },
|
|
70
|
-
{ kind: "action", id: "reset", title: "Reset console password" },
|
|
71
|
-
{ kind: "action", id: "backup", title: "Backup" },
|
|
72
|
-
{ kind: "separator" },
|
|
73
|
-
{ kind: "action", id: "exit", title: "Exit" },
|
|
74
|
-
],
|
|
75
|
-
onSelect: (id: string) => void handleSelect(id),
|
|
76
|
-
onExit: () => void close(1),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const close = (exitCode = 0): Promise<void> => {
|
|
80
|
-
if (closePromise) return closePromise;
|
|
81
|
-
closing = true;
|
|
82
|
-
closePromise = (async () => {
|
|
83
|
-
const current: Bun.Subprocess | null = gateway;
|
|
84
|
-
gateway = null;
|
|
85
|
-
if (current) await stopGateway(current, port, drainToken);
|
|
86
|
-
await tray.close();
|
|
87
|
-
process.exitCode = exitCode;
|
|
88
|
-
})();
|
|
89
|
-
return closePromise;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const refreshVersion = async (): Promise<void> => {
|
|
93
|
-
const fetched = await readLatestPublishedVersion();
|
|
94
|
-
if (closing) return;
|
|
95
|
-
latestVersion = fetched;
|
|
96
|
-
if (fetched === null) {
|
|
97
|
-
tray.update("update", `Cartethyia ${currentVersion} \u00b7 check for updates`, true);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const newer = isNewerRelease(currentVersion, fetched);
|
|
101
|
-
tray.update(
|
|
102
|
-
"update",
|
|
103
|
-
newer
|
|
104
|
-
? `Cartethyia ${currentVersion} \u00b7 update to ${fetched}`
|
|
105
|
-
: `Cartethyia ${currentVersion} \u00b7 up to date`,
|
|
106
|
-
// Always clickable: the row opens the repository, and only offers the
|
|
107
|
-
// update command when there is something newer.
|
|
108
|
-
true,
|
|
109
|
-
);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const runUpdate = async (): Promise<void> => {
|
|
113
|
-
if (busy) return;
|
|
114
|
-
busy = true;
|
|
115
|
-
const latest = await readLatestPublishedVersion();
|
|
116
|
-
await close();
|
|
117
|
-
printUpdateInstructions(currentVersion, latest);
|
|
118
|
-
// The tray has no terminal of its own, so these instructions are only
|
|
119
|
-
// visible when it was started from one. Hold that terminal open long
|
|
120
|
-
// enough to copy the command, then exit.
|
|
121
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 180_000));
|
|
122
|
-
process.exit(0);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
async function handleSelect(id: string): Promise<void> {
|
|
126
|
-
if (closing) return;
|
|
127
|
-
if (id === "exit") return void (await close());
|
|
128
|
-
if (id === "console") return void (await openUrl(dashboardUrl));
|
|
129
|
-
if (id === "backup") {
|
|
130
|
-
const result = await exportBackup(port, drainToken);
|
|
131
|
-
if (result !== null) {
|
|
132
|
-
console.log(
|
|
133
|
-
`Backup written to ${result.path} (${(result.bytes / 1024).toFixed(1)} KiB). Telemetry is not included.`,
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (id === "reset") {
|
|
139
|
-
// A tray click cannot ask for the typed confirmation the menu uses, so
|
|
140
|
-
// the first click arms the action and the second one runs it.
|
|
141
|
-
if (!resetArmed) {
|
|
142
|
-
resetArmed = true;
|
|
143
|
-
tray.update("reset", "Reset console password \u00b7 click again to delete", true);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
resetArmed = false;
|
|
147
|
-
tray.update("reset", "Reset console password", true);
|
|
148
|
-
await resetConsoleAccount(port, drainToken);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
if (id === "update") {
|
|
152
|
-
// Nothing to install: the row is the way to the repository instead.
|
|
153
|
-
if (latestVersion === null || !isNewerRelease(currentVersion, latestVersion)) {
|
|
154
|
-
await openUrl(repositoryUrl);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
await runUpdate();
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
process.once("SIGINT", () => void close());
|
|
162
|
-
process.once("SIGTERM", () => void close());
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
await tray.ready();
|
|
166
|
-
// Detached, unlike every other caller: the gateway must outlive this
|
|
167
|
-
// process. Attached, it dies with the tray — and the tray dies with the
|
|
168
|
-
// terminal that launched it, so closing that terminal took the gateway
|
|
169
|
-
// down too and "Hide to tray" left nothing serving. `stopGateway` drains
|
|
170
|
-
// over HTTP before it ever reaches for `kill()`, so detaching costs the
|
|
171
|
-
// exit path nothing: the tray can still stop the gateway on Exit.
|
|
172
|
-
gateway = await startGateway(port, drainToken, { detached: true });
|
|
173
|
-
// The launcher that spawned this tray is blocked on a signal file: it
|
|
174
|
-
// cannot read our stdio, because the tray is detached. Without this write
|
|
175
|
-
// it waits out its 60 s deadline and reports a failure for a tray that is
|
|
176
|
-
// running fine.
|
|
177
|
-
reportTrayStartup({ status: "ready", url: dashboardUrl });
|
|
178
|
-
console.log(`Cartethyia is running at ${dashboardUrl}`);
|
|
179
|
-
void refreshVersion();
|
|
180
|
-
|
|
181
|
-
let misses = 0;
|
|
182
|
-
// A detached child's `exited` never settles on Windows, so the old
|
|
183
|
-
// `await current.exited` would have parked here forever and the tray would
|
|
184
|
-
// never notice the gateway going away. Poll its health endpoint instead:
|
|
185
|
-
// that answers for a process we no longer own, which is the whole point of
|
|
186
|
-
// detaching it. The probe is the same one startup used, so a gateway that
|
|
187
|
-
// is up reads as up regardless of who spawned it.
|
|
188
|
-
while (!closing) {
|
|
189
|
-
await Bun.sleep(TRAY_HEALTH_POLL_MS);
|
|
190
|
-
if (closing || gateway === null) continue;
|
|
191
|
-
const alive = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
192
|
-
signal: AbortSignal.timeout(2_000),
|
|
193
|
-
}).then((response) => response.ok).catch(() => false);
|
|
194
|
-
if (alive) {
|
|
195
|
-
misses = 0;
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
misses += 1;
|
|
199
|
-
if (misses < TRAY_HEALTH_MISSES_BEFORE_CLOSE) continue;
|
|
200
|
-
gateway = null;
|
|
201
|
-
console.error("Cartethyia stopped answering; the tray is closing.");
|
|
202
|
-
await close(1);
|
|
203
|
-
}
|
|
204
|
-
} catch (error) {
|
|
205
|
-
await close(1);
|
|
206
|
-
throw error;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
1
|
+
import { isNewerRelease } from "../../src/console/updates/version-compare";
|
|
2
|
+
import { resolvePackagePath } from "../../src/config/package-paths";
|
|
3
|
+
import { resolvePort } from "../../src/config/runtime";
|
|
4
|
+
import { ensureDrainToken } from "./instance";
|
|
5
|
+
import { startGateway } from "./gateway";
|
|
6
|
+
import { stopGateway } from "./gateway";
|
|
7
|
+
import { readInstalledVersion, readLatestPublishedVersion } from "./version";
|
|
8
|
+
import { reportTrayStartup } from "./startup-signal";
|
|
9
|
+
import { createTray } from "./tray/index";
|
|
10
|
+
import { openUrl } from "./background";
|
|
11
|
+
import { exportBackup, resetConsoleAccount } from "./admin";
|
|
12
|
+
import { printUpdateInstructions } from "./update";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Runs the gateway with a tray icon. The tray is platform-specific (`tray/`):
|
|
16
|
+
* on Windows it is a PowerShell NotifyIcon, because a Node tray library leaves
|
|
17
|
+
* an empty console tab open for the tray's whole lifetime, and that window is
|
|
18
|
+
* owned by Windows Terminal — not by this process, so it cannot be found and
|
|
19
|
+
* hidden from inside.
|
|
20
|
+
*
|
|
21
|
+
* The gateway it starts is detached, which is what makes the health poll below
|
|
22
|
+
* necessary — see {@link TRAY_HEALTH_POLL_MS}.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* How often the tray asks whether the gateway is still answering.
|
|
27
|
+
*
|
|
28
|
+
* 10 s: often enough that an operator who stops the gateway by hand sees the
|
|
29
|
+
* icon go away, and far below the cost of a request. A detached gateway can no
|
|
30
|
+
* longer be waited on, so this poll is the only signal left.
|
|
31
|
+
*/
|
|
32
|
+
const TRAY_HEALTH_POLL_MS = 10_000;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Consecutive misses to forgive before a dead gateway closes the tray.
|
|
36
|
+
*
|
|
37
|
+
* One refusal is not a stopped gateway — a probe can lose a race with startup
|
|
38
|
+
* or a slow request — and closing the icon on one would make startup feel
|
|
39
|
+
* random.
|
|
40
|
+
*/
|
|
41
|
+
const TRAY_HEALTH_MISSES_BEFORE_CLOSE = 3;
|
|
42
|
+
|
|
43
|
+
export async function startTray(): Promise<void> {
|
|
44
|
+
const port = resolvePort();
|
|
45
|
+
const drainToken = ensureDrainToken();
|
|
46
|
+
const dashboardUrl = `http://127.0.0.1:${port}/console`;
|
|
47
|
+
const repositoryUrl = "https://github.com/risunCode/cartethyia";
|
|
48
|
+
const currentVersion = await readInstalledVersion();
|
|
49
|
+
|
|
50
|
+
let gateway: Bun.Subprocess | null = null;
|
|
51
|
+
let closing = false;
|
|
52
|
+
let busy = false;
|
|
53
|
+
let resetArmed = false;
|
|
54
|
+
let latestVersion: string | null = null;
|
|
55
|
+
let closePromise: Promise<void> | undefined;
|
|
56
|
+
|
|
57
|
+
const tray = createTray({
|
|
58
|
+
iconPath: resolvePackagePath(
|
|
59
|
+
"scripts",
|
|
60
|
+
"cli",
|
|
61
|
+
"tray",
|
|
62
|
+
"assets",
|
|
63
|
+
process.platform === "win32" ? "cartethyia.ico" : "cartethyia.png",
|
|
64
|
+
),
|
|
65
|
+
tooltip: `Cartethyia ${currentVersion}`,
|
|
66
|
+
items: [
|
|
67
|
+
{ kind: "action", id: "update", title: `Cartethyia ${currentVersion} \u00b7 checking\u2026`, enabled: true },
|
|
68
|
+
{ kind: "separator" },
|
|
69
|
+
{ kind: "action", id: "console", title: `Open Console \u00b7 ${dashboardUrl}` },
|
|
70
|
+
{ kind: "action", id: "reset", title: "Reset console password" },
|
|
71
|
+
{ kind: "action", id: "backup", title: "Backup" },
|
|
72
|
+
{ kind: "separator" },
|
|
73
|
+
{ kind: "action", id: "exit", title: "Exit" },
|
|
74
|
+
],
|
|
75
|
+
onSelect: (id: string) => void handleSelect(id),
|
|
76
|
+
onExit: () => void close(1),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const close = (exitCode = 0): Promise<void> => {
|
|
80
|
+
if (closePromise) return closePromise;
|
|
81
|
+
closing = true;
|
|
82
|
+
closePromise = (async () => {
|
|
83
|
+
const current: Bun.Subprocess | null = gateway;
|
|
84
|
+
gateway = null;
|
|
85
|
+
if (current) await stopGateway(current, port, drainToken);
|
|
86
|
+
await tray.close();
|
|
87
|
+
process.exitCode = exitCode;
|
|
88
|
+
})();
|
|
89
|
+
return closePromise;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const refreshVersion = async (): Promise<void> => {
|
|
93
|
+
const fetched = await readLatestPublishedVersion();
|
|
94
|
+
if (closing) return;
|
|
95
|
+
latestVersion = fetched;
|
|
96
|
+
if (fetched === null) {
|
|
97
|
+
tray.update("update", `Cartethyia ${currentVersion} \u00b7 check for updates`, true);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const newer = isNewerRelease(currentVersion, fetched);
|
|
101
|
+
tray.update(
|
|
102
|
+
"update",
|
|
103
|
+
newer
|
|
104
|
+
? `Cartethyia ${currentVersion} \u00b7 update to ${fetched}`
|
|
105
|
+
: `Cartethyia ${currentVersion} \u00b7 up to date`,
|
|
106
|
+
// Always clickable: the row opens the repository, and only offers the
|
|
107
|
+
// update command when there is something newer.
|
|
108
|
+
true,
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const runUpdate = async (): Promise<void> => {
|
|
113
|
+
if (busy) return;
|
|
114
|
+
busy = true;
|
|
115
|
+
const latest = await readLatestPublishedVersion();
|
|
116
|
+
await close();
|
|
117
|
+
printUpdateInstructions(currentVersion, latest);
|
|
118
|
+
// The tray has no terminal of its own, so these instructions are only
|
|
119
|
+
// visible when it was started from one. Hold that terminal open long
|
|
120
|
+
// enough to copy the command, then exit.
|
|
121
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 180_000));
|
|
122
|
+
process.exit(0);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
async function handleSelect(id: string): Promise<void> {
|
|
126
|
+
if (closing) return;
|
|
127
|
+
if (id === "exit") return void (await close());
|
|
128
|
+
if (id === "console") return void (await openUrl(dashboardUrl));
|
|
129
|
+
if (id === "backup") {
|
|
130
|
+
const result = await exportBackup(port, drainToken);
|
|
131
|
+
if (result !== null) {
|
|
132
|
+
console.log(
|
|
133
|
+
`Backup written to ${result.path} (${(result.bytes / 1024).toFixed(1)} KiB). Telemetry is not included.`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (id === "reset") {
|
|
139
|
+
// A tray click cannot ask for the typed confirmation the menu uses, so
|
|
140
|
+
// the first click arms the action and the second one runs it.
|
|
141
|
+
if (!resetArmed) {
|
|
142
|
+
resetArmed = true;
|
|
143
|
+
tray.update("reset", "Reset console password \u00b7 click again to delete", true);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
resetArmed = false;
|
|
147
|
+
tray.update("reset", "Reset console password", true);
|
|
148
|
+
await resetConsoleAccount(port, drainToken);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (id === "update") {
|
|
152
|
+
// Nothing to install: the row is the way to the repository instead.
|
|
153
|
+
if (latestVersion === null || !isNewerRelease(currentVersion, latestVersion)) {
|
|
154
|
+
await openUrl(repositoryUrl);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
await runUpdate();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
process.once("SIGINT", () => void close());
|
|
162
|
+
process.once("SIGTERM", () => void close());
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
await tray.ready();
|
|
166
|
+
// Detached, unlike every other caller: the gateway must outlive this
|
|
167
|
+
// process. Attached, it dies with the tray — and the tray dies with the
|
|
168
|
+
// terminal that launched it, so closing that terminal took the gateway
|
|
169
|
+
// down too and "Hide to tray" left nothing serving. `stopGateway` drains
|
|
170
|
+
// over HTTP before it ever reaches for `kill()`, so detaching costs the
|
|
171
|
+
// exit path nothing: the tray can still stop the gateway on Exit.
|
|
172
|
+
gateway = await startGateway(port, drainToken, { detached: true });
|
|
173
|
+
// The launcher that spawned this tray is blocked on a signal file: it
|
|
174
|
+
// cannot read our stdio, because the tray is detached. Without this write
|
|
175
|
+
// it waits out its 60 s deadline and reports a failure for a tray that is
|
|
176
|
+
// running fine.
|
|
177
|
+
reportTrayStartup({ status: "ready", url: dashboardUrl });
|
|
178
|
+
console.log(`Cartethyia is running at ${dashboardUrl}`);
|
|
179
|
+
void refreshVersion();
|
|
180
|
+
|
|
181
|
+
let misses = 0;
|
|
182
|
+
// A detached child's `exited` never settles on Windows, so the old
|
|
183
|
+
// `await current.exited` would have parked here forever and the tray would
|
|
184
|
+
// never notice the gateway going away. Poll its health endpoint instead:
|
|
185
|
+
// that answers for a process we no longer own, which is the whole point of
|
|
186
|
+
// detaching it. The probe is the same one startup used, so a gateway that
|
|
187
|
+
// is up reads as up regardless of who spawned it.
|
|
188
|
+
while (!closing) {
|
|
189
|
+
await Bun.sleep(TRAY_HEALTH_POLL_MS);
|
|
190
|
+
if (closing || gateway === null) continue;
|
|
191
|
+
const alive = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
192
|
+
signal: AbortSignal.timeout(2_000),
|
|
193
|
+
}).then((response) => response.ok).catch(() => false);
|
|
194
|
+
if (alive) {
|
|
195
|
+
misses = 0;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
misses += 1;
|
|
199
|
+
if (misses < TRAY_HEALTH_MISSES_BEFORE_CLOSE) continue;
|
|
200
|
+
gateway = null;
|
|
201
|
+
console.error("Cartethyia stopped answering; the tray is closing.");
|
|
202
|
+
await close(1);
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
await close(1);
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -1,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
|
}
|
|
@@ -0,0 +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
|
+
}
|