cartethyia 2.5.9 → 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 +15 -42
- package/README.md +11 -10
- package/dist/dashboard/assets/{BannedPage-Brvuj_Eb.js → BannedPage-l6XMnfr9.js} +1 -1
- package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-CqfXtI3y.js → CliToolDetailPage-DwOzAW45.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-GQeS91bg.js → CliToolsPage-BT3Up3QA.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BDRsTXBB.js → CombosPage-6kYIUpzR.js} +1 -1
- package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-zP8AFHX0.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-BmfrNZ14.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-DoreIgm8.js → LoginPage-CbphLvjg.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C7pDb41R.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-BnoPgA7w.js → ProvidersPage-Dfb2896L.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-Bq_TGgdb.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-Day3mCOn.js → SetupPage-D4g6vOGc.js} +1 -1
- package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-DQxllnB5.js → SortableList-C93i14BD.js} +1 -1
- package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-uMExoWu1.js → StudioPage-SgQpymjm.js} +1 -1
- package/dist/dashboard/assets/{StudioPage-uMExoWu1.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
- package/dist/dashboard/assets/{UsagePage-DXa1oR-4.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-BIt446yE.js → app-2KhtHCv9.js} +1 -1
- package/dist/dashboard/assets/{app-BIt446yE.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-CR72dasT.js → badge-CKsF123d.js} +1 -1
- package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
- package/dist/dashboard/assets/{banner-CI9_eSfO.js → banner-8j7uChA9.js} +1 -1
- package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
- package/dist/dashboard/assets/{card-DS7CQEsO.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-611pSsaX.js → clipboard-button-uL5aYLfW.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-k37exjNt.js → dialog-C07hr5_G.js} +1 -1
- package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CX7N8ucR.js → drawer-BPO1TcKH.js} +1 -1
- package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CTP8AyiS.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-sltk1qUi.js → github-badge-DmQreSDd.js} +1 -1
- package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
- package/dist/dashboard/assets/{index-z3zDOSMM.js → index-Bblu6erR.js} +2 -2
- package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BhY2sSnO.js → inline-C9dCYaBZ.js} +1 -1
- package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
- package/dist/dashboard/assets/{input-V1umFiBk.js → input-CjCnXweJ.js} +1 -1
- package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BBOYycyS.js → layout-V6y8DAkl.js} +1 -1
- package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-1zzZYM0B.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-DkfMR5Qs.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-C6MMiaio.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-BHTfXdz_.js → stack-_gfTdLVB.js} +1 -1
- package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
- package/dist/dashboard/assets/{state-mPDRlQkl.js → state-2c5V4zo7.js} +1 -1
- package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DOmJxg7H.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 -97
- package/scripts/cli/admin.ts +126 -125
- 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 +308 -275
- package/scripts/cli/controller.ts +145 -0
- package/scripts/cli/doctor.ts +1 -1
- package/scripts/cli/env.ts +99 -99
- package/scripts/cli/gateway.ts +264 -261
- package/scripts/{health.ts → cli/health.ts} +4 -4
- package/scripts/cli/instance.ts +234 -224
- package/scripts/cli/main.ts +188 -164
- package/scripts/cli/platform.ts +64 -64
- package/scripts/cli/prompt.ts +275 -107
- package/scripts/cli/services.ts +88 -88
- package/scripts/cli/setup.ts +445 -445
- package/scripts/cli/start.ts +85 -82
- 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 +192 -208
- 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/package-paths.ts +24 -0
- 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/remote-support/service.ts +270 -114
- 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/routing/pools/store.ts +1 -1
- 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/repository-updates.ts +40 -39
- 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/registry.ts +2 -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 +14 -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/capabilities.ts +4 -0
- 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/github/github.ts +3 -1
- 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/opencode/provider.ts +2 -0
- 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-headers.ts +17 -48
- 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/trae/trae.ts +22 -24
- 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/provider-probe.ts +3 -0
- 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-Brvuj_Eb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-CqfXtI3y.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-GQeS91bg.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BDRsTXBB.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-zP8AFHX0.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BmfrNZ14.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Dn3M6WZc.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-Dn3M6WZc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DoreIgm8.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CvvD1Ugk.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CvvD1Ugk.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-Clbn1gsW.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-Clbn1gsW.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BnoPgA7w.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-Bq_TGgdb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-D9DHXvom.js +0 -1
- package/dist/dashboard/assets/QuotaPage-D9DHXvom.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-7xV8WkWL.js +0 -1
- package/dist/dashboard/assets/SettingsPage-7xV8WkWL.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-Day3mCOn.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DQxllnB5.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DXa1oR-4.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-CR72dasT.js.gz +0 -0
- package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
- package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
- package/dist/dashboard/assets/entry-B9Xvtnfj.js +0 -1
- package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
- package/dist/dashboard/assets/entry-ByMGkR3C.js +0 -4
- package/dist/dashboard/assets/entry-ByMGkR3C.js.gz +0 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
- package/dist/dashboard/assets/index-z3zDOSMM.js.gz +0 -0
- package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
- package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
- package/dist/dashboard/assets/list-row-1zzZYM0B.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-DkfMR5Qs.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-C6MMiaio.js.gz +0 -0
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
- package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
- package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/scripts/build/aot.ts +0 -143
- package/scripts/build/build.ts +0 -24
- package/scripts/build/compile.ts +0 -88
- package/scripts/build/restart.ts +0 -120
- package/scripts/build/start.ts +0 -25
- package/src/observability/event-compaction.ts +0 -317
- package/src/observability/event-day-index.ts +0 -440
|
@@ -1,110 +1,120 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { readFileSync, unlinkSync } from "node:fs";
|
|
3
|
-
import { PACKAGE_ROOT, resolvePackagePath } from "../../src/config/package-paths";
|
|
4
|
-
|
|
5
|
-
/** Opens a URL in the operator's default browser, ignoring a missing handler. */
|
|
6
|
-
export function openUrl(url: string): Promise<void> {
|
|
7
|
-
const command = process.platform === "win32"
|
|
8
|
-
? "explorer.exe"
|
|
9
|
-
: process.platform === "darwin"
|
|
10
|
-
? "open"
|
|
11
|
-
: "xdg-open";
|
|
12
|
-
const child = Bun.spawn([command, url], {
|
|
13
|
-
stdin: "ignore",
|
|
14
|
-
stdout: "ignore",
|
|
15
|
-
stderr: "ignore",
|
|
16
|
-
});
|
|
17
|
-
return child.exited.then(() => undefined);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Starts a detached process and resolves once it reports readiness.
|
|
22
|
-
*
|
|
23
|
-
* The child owns no stdio, so a signal file is the only channel that survives
|
|
24
|
-
* the parent. Without it the caller prints "running in the background" for a
|
|
25
|
-
* child that may already have died — which is exactly what a port clash looks
|
|
26
|
-
* like, because the child's own startup probe fails while the parent's gateway
|
|
27
|
-
* still holds the port.
|
|
28
|
-
*
|
|
29
|
-
* Resolves with the URL the child reported. Rejects with the child's message
|
|
30
|
-
* when it reports failure, or with a timeout if it never reports at all.
|
|
31
|
-
*/
|
|
32
|
-
export function launchBackgroundWithStatus(
|
|
33
|
-
args: readonly string[],
|
|
34
|
-
options: {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
*
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { PACKAGE_ROOT, resolvePackagePath } from "../../src/config/package-paths";
|
|
4
|
+
|
|
5
|
+
/** Opens a URL in the operator's default browser, ignoring a missing handler. */
|
|
6
|
+
export function openUrl(url: string): Promise<void> {
|
|
7
|
+
const command = process.platform === "win32"
|
|
8
|
+
? "explorer.exe"
|
|
9
|
+
: process.platform === "darwin"
|
|
10
|
+
? "open"
|
|
11
|
+
: "xdg-open";
|
|
12
|
+
const child = Bun.spawn([command, url], {
|
|
13
|
+
stdin: "ignore",
|
|
14
|
+
stdout: "ignore",
|
|
15
|
+
stderr: "ignore",
|
|
16
|
+
});
|
|
17
|
+
return child.exited.then(() => undefined);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Starts a detached process and resolves once it reports readiness.
|
|
22
|
+
*
|
|
23
|
+
* The child owns no stdio, so a signal file is the only channel that survives
|
|
24
|
+
* the parent. Without it the caller prints "running in the background" for a
|
|
25
|
+
* child that may already have died — which is exactly what a port clash looks
|
|
26
|
+
* like, because the child's own startup probe fails while the parent's gateway
|
|
27
|
+
* still holds the port.
|
|
28
|
+
*
|
|
29
|
+
* Resolves with the URL the child reported. Rejects with the child's message
|
|
30
|
+
* when it reports failure, or with a timeout if it never reports at all.
|
|
31
|
+
*/
|
|
32
|
+
export function launchBackgroundWithStatus(
|
|
33
|
+
args: readonly string[],
|
|
34
|
+
options: {
|
|
35
|
+
readonly signalPath: string;
|
|
36
|
+
readonly timeoutMs?: number;
|
|
37
|
+
readonly controllerHandoff?: { readonly token: string; readonly parentPid: number };
|
|
38
|
+
} = { signalPath: "" },
|
|
39
|
+
): Promise<string> {
|
|
40
|
+
return new Promise<string>((resolve, reject) => {
|
|
41
|
+
const signalPath = options.signalPath;
|
|
42
|
+
const deadline = Date.now() + (options.timeoutMs ?? 60_000);
|
|
43
|
+
let settled = false;
|
|
44
|
+
const finish = (error: Error | null, url?: string): void => {
|
|
45
|
+
if (settled) return;
|
|
46
|
+
settled = true;
|
|
47
|
+
clearInterval(timer);
|
|
48
|
+
try {
|
|
49
|
+
unlinkSync(signalPath);
|
|
50
|
+
} catch {}
|
|
51
|
+
if (error) reject(error);
|
|
52
|
+
else resolve(url ?? "");
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const env = {
|
|
56
|
+
...process.env,
|
|
57
|
+
CARTETHYIA_TRAY_STARTUP_SIGNAL: signalPath,
|
|
58
|
+
...(options.controllerHandoff ? {
|
|
59
|
+
CARTETHYIA_CONTROLLER_TOKEN: options.controllerHandoff.token,
|
|
60
|
+
CARTETHYIA_CONTROLLER_PARENT_PID: String(options.controllerHandoff.parentPid),
|
|
61
|
+
} : {}),
|
|
62
|
+
};
|
|
63
|
+
spawnBackground(args, env);
|
|
64
|
+
|
|
65
|
+
const timer = setInterval(() => {
|
|
66
|
+
if (settled) return;
|
|
67
|
+
let raw: string;
|
|
68
|
+
try {
|
|
69
|
+
raw = readFileSync(signalPath, "utf8");
|
|
70
|
+
} catch {
|
|
71
|
+
if (Date.now() >= deadline) finish(new Error("the background process did not report startup within the deadline"));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const signal = JSON.parse(raw) as { status?: string; url?: string; message?: string };
|
|
76
|
+
if (signal.status === "ready" && typeof signal.url === "string") finish(null, signal.url);
|
|
77
|
+
else if (signal.status === "failed") finish(new Error(signal.message ?? "the background process failed to start"));
|
|
78
|
+
} catch {
|
|
79
|
+
// A partially written file is read again on the next tick.
|
|
80
|
+
}
|
|
81
|
+
}, 150);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Starts a Bun process that owns no console window.
|
|
87
|
+
*
|
|
88
|
+
* On Windows, spawning the child directly is not enough: a console-subsystem
|
|
89
|
+
* child of a window-less parent allocates a new console, and when the parent is
|
|
90
|
+
* inside Windows Terminal the tab is already on screen before any flag can take
|
|
91
|
+
* effect — measured: `detached: true` plus `windowsHide: true` still produced a
|
|
92
|
+
* Windows Terminal window titled with the bun path. `wscript.exe` is a
|
|
93
|
+
* GUI-subsystem host, so it never allocates a console, and a child launched
|
|
94
|
+
* from it with window style 0 inherits that. The VBS is that launcher.
|
|
95
|
+
*
|
|
96
|
+
* Elsewhere `detached` is enough and the helper is not used.
|
|
97
|
+
*/
|
|
98
|
+
export function launchBackground(args: readonly string[]): void {
|
|
99
|
+
spawnBackground(args, process.env);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function spawnBackground(args: readonly string[], env: NodeJS.ProcessEnv): void {
|
|
103
|
+
if (process.platform !== "win32") {
|
|
104
|
+
const child = spawn(process.execPath, [...args], {
|
|
105
|
+
cwd: PACKAGE_ROOT,
|
|
106
|
+
detached: true,
|
|
107
|
+
stdio: "ignore",
|
|
108
|
+
env,
|
|
109
|
+
});
|
|
110
|
+
child.unref();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const launcher = resolvePackagePath("scripts", "cli", "tray", "hidden-window.vbs");
|
|
114
|
+
const child = spawn(
|
|
115
|
+
"wscript.exe",
|
|
116
|
+
["//nologo", launcher, process.execPath, ...args],
|
|
117
|
+
{ cwd: PACKAGE_ROOT, detached: true, stdio: "ignore", windowsHide: true, env },
|
|
118
|
+
);
|
|
119
|
+
child.unref();
|
|
120
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// Default to the arrow-key menu so `carte` works without a GUI; on Windows
|
|
4
|
-
// especially, the tray icon can land in the hidden-icons overflow and never
|
|
5
|
-
// be seen. `carte --tray` still opts into the system-tray icon.
|
|
6
|
-
if (process.argv.length <= 2) process.argv.push("menu");
|
|
7
|
-
else if (process.argv[2] === "--tray") process.argv[2] = "tray";
|
|
8
|
-
require("./cartethyia.cjs");
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Default to the arrow-key menu so `carte` works without a GUI; on Windows
|
|
4
|
+
// especially, the tray icon can land in the hidden-icons overflow and never
|
|
5
|
+
// be seen. `carte --tray` still opts into the system-tray icon.
|
|
6
|
+
if (process.argv.length <= 2) process.argv.push("menu");
|
|
7
|
+
else if (process.argv[2] === "--tray") process.argv[2] = "tray";
|
|
8
|
+
require("./cartethyia.cjs");
|
|
@@ -1,167 +1,167 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const { randomUUID } = require("node:crypto");
|
|
6
|
-
const os = require("node:os");
|
|
7
|
-
const path = require("node:path");
|
|
8
|
-
const { spawn } = require("node:child_process");
|
|
9
|
-
|
|
10
|
-
const packageRoot = path.resolve(__dirname, "..", "..", "..");
|
|
11
|
-
const cliPath = path.join(packageRoot, "scripts", "cli", "main.ts");
|
|
12
|
-
const configDir = process.env.CARTETHYIA_CONFIG_DIR || (
|
|
13
|
-
process.platform === "win32"
|
|
14
|
-
? path.join(process.env.APPDATA || os.homedir(), "Cartethyia")
|
|
15
|
-
: process.platform === "darwin"
|
|
16
|
-
? path.join(os.homedir(), "Library", "Application Support", "Cartethyia")
|
|
17
|
-
: path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "Cartethyia")
|
|
18
|
-
);
|
|
19
|
-
const trayMode = process.argv[2] === "tray";
|
|
20
|
-
// The launcher itself runs under Node, so `process.execPath` is node.exe — not
|
|
21
|
-
// the Bun the child needs. Resolve bun from PATH instead; WScript.Shell does
|
|
22
|
-
// not inherit this process's environment, so the child needs an absolute path.
|
|
23
|
-
function resolveBunPath() {
|
|
24
|
-
if (process.env.CARTETHYIA_BUN) return process.env.CARTETHYIA_BUN;
|
|
25
|
-
const name = process.platform === "win32" ? "bun.exe" : "bun";
|
|
26
|
-
for (const dir of (process.env.PATH || "").split(path.delimiter)) {
|
|
27
|
-
if (!dir) continue;
|
|
28
|
-
const candidate = path.join(dir, name);
|
|
29
|
-
try {
|
|
30
|
-
if (fs.statSync(candidate).isFile()) return candidate;
|
|
31
|
-
} catch {}
|
|
32
|
-
}
|
|
33
|
-
return name;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const startupSignalPath = trayMode
|
|
37
|
-
? path.join(os.tmpdir(), `cartethyia-tray-${process.pid}-${randomUUID()}.json`)
|
|
38
|
-
: null;
|
|
39
|
-
let spawnError;
|
|
40
|
-
// On Windows the tray must start with no console at all, and neither
|
|
41
|
-
// `detached: true` nor `windowsHide: true` achieves that: a console-subsystem
|
|
42
|
-
// child of a window-less parent still allocates a console, and inside Windows
|
|
43
|
-
// Terminal the tab is already on screen before a flag can take effect
|
|
44
|
-
// (measured). `wscript.exe` is GUI-subsystem, so it never allocates one, and a
|
|
45
|
-
// child launched from it with window style 0 inherits that. Elsewhere
|
|
46
|
-
// `detached: true` is enough.
|
|
47
|
-
const useWindowsDetach = process.platform === "win32" && trayMode;
|
|
48
|
-
const hiddenLauncher = path.join(packageRoot, "scripts", "cli", "tray", "hidden-window.vbs");
|
|
49
|
-
const child = useWindowsDetach
|
|
50
|
-
? spawn(
|
|
51
|
-
"wscript.exe",
|
|
52
|
-
// Absolute path, not "bun": the VBS runs through WScript.Shell, which
|
|
53
|
-
// does not inherit this process's PATH additions and resolves a bare
|
|
54
|
-
// name against the system PATH only.
|
|
55
|
-
["//nologo", hiddenLauncher, resolveBunPath(), "run", cliPath, ...process.argv.slice(2)],
|
|
56
|
-
{
|
|
57
|
-
cwd: packageRoot,
|
|
58
|
-
detached: true,
|
|
59
|
-
stdio: "ignore",
|
|
60
|
-
windowsHide: true,
|
|
61
|
-
env: {
|
|
62
|
-
...process.env,
|
|
63
|
-
CARTETHYIA_CONFIG_DIR: configDir,
|
|
64
|
-
CARTETHYIA_TRAY_STARTUP_SIGNAL: startupSignalPath,
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
)
|
|
68
|
-
: spawn(resolveBunPath(), ["run", cliPath, ...process.argv.slice(2)], {
|
|
69
|
-
cwd: packageRoot,
|
|
70
|
-
detached: trayMode,
|
|
71
|
-
stdio: trayMode ? "ignore" : "inherit",
|
|
72
|
-
windowsHide: trayMode,
|
|
73
|
-
env: {
|
|
74
|
-
...process.env,
|
|
75
|
-
CARTETHYIA_CONFIG_DIR: configDir,
|
|
76
|
-
...(trayMode ? { CARTETHYIA_TRAY_STARTUP_SIGNAL: startupSignalPath } : {}),
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
child.once("error", (error) => {
|
|
81
|
-
spawnError = error;
|
|
82
|
-
if (error.code === "ENOENT") {
|
|
83
|
-
console.error("Cartethyia requires Bun 1.4.0 or newer. Install Bun from https://bun.sh, then run this command again.");
|
|
84
|
-
} else {
|
|
85
|
-
console.error(`Could not start Cartethyia's Bun runtime: ${error.message}`);
|
|
86
|
-
}
|
|
87
|
-
process.exitCode = 1;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
if (trayMode) {
|
|
91
|
-
child.unref();
|
|
92
|
-
console.log("Loading Cartethyia…");
|
|
93
|
-
let settled = false;
|
|
94
|
-
const deadline = Date.now() + 60_000;
|
|
95
|
-
const finish = (error, url) => {
|
|
96
|
-
if (settled) return;
|
|
97
|
-
settled = true;
|
|
98
|
-
try {
|
|
99
|
-
fs.unlinkSync(startupSignalPath);
|
|
100
|
-
} catch {}
|
|
101
|
-
if (error) {
|
|
102
|
-
// `error.message` can be an empty string when the failure came from a
|
|
103
|
-
// signal rather than a thrown message, which printed as a bare
|
|
104
|
-
// "Cartethyia failed to start:" with no cause. Fall back to the code and
|
|
105
|
-
// name what usually causes it.
|
|
106
|
-
const detail =
|
|
107
|
-
error.message && error.message.trim() !== ""
|
|
108
|
-
? error.message
|
|
109
|
-
: `the process exited without a message (code ${child.exitCode ?? "unknown"})`;
|
|
110
|
-
console.error(`Cartethyia failed to start: ${detail}`);
|
|
111
|
-
console.error(
|
|
112
|
-
"If another Cartethyia is already running on this port, stop the terminal that started it first.",
|
|
113
|
-
);
|
|
114
|
-
process.exitCode = 1;
|
|
115
|
-
if (child.exitCode === null) child.kill();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
console.log(`Cartethyia is ready at ${url}`);
|
|
119
|
-
// The gateway child is already detached and unref'd; this Node process is
|
|
120
|
-
// the only thing still holding the launching terminal open. Exit once the
|
|
121
|
-
// URL has been shown so the shell prompt comes back.
|
|
122
|
-
setTimeout(() => process.exit(0), 1_500);
|
|
123
|
-
};
|
|
124
|
-
const poll = () => {
|
|
125
|
-
if (settled) return;
|
|
126
|
-
if (fs.existsSync(startupSignalPath)) {
|
|
127
|
-
try {
|
|
128
|
-
const signal = JSON.parse(fs.readFileSync(startupSignalPath, "utf8"));
|
|
129
|
-
if (signal.status === "ready" && typeof signal.url === "string") {
|
|
130
|
-
finish(null, signal.url);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
if (signal.status === "failed" && typeof signal.message === "string") {
|
|
134
|
-
finish(new Error(signal.message));
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
} catch {}
|
|
138
|
-
}
|
|
139
|
-
if (spawnError) {
|
|
140
|
-
finish(spawnError);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (Date.now() >= deadline) {
|
|
144
|
-
finish(new Error("startup did not complete within 60 seconds"));
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
setTimeout(poll, 150);
|
|
148
|
-
};
|
|
149
|
-
// On Windows the spawned process is `wscript.exe`, which hands off to the
|
|
150
|
-
// tray and exits immediately — a successful handoff, not a failed start. Only
|
|
151
|
-
// a direct spawn's exit says anything about the tray, so the deadline covers
|
|
152
|
-
// the handoff case and this guard covers the direct one.
|
|
153
|
-
if (!useWindowsDetach) {
|
|
154
|
-
child.once("exit", (code, signal) => {
|
|
155
|
-
if (!settled) setTimeout(() => finish(new Error(`the tray process exited before readiness (code ${code ?? signal ?? "unknown"})`)), 250);
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
poll();
|
|
159
|
-
} else {
|
|
160
|
-
child.once("exit", (code, signal) => {
|
|
161
|
-
if (signal) {
|
|
162
|
-
process.kill(process.pid, signal);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
process.exitCode = code ?? 1;
|
|
166
|
-
});
|
|
167
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const { randomUUID } = require("node:crypto");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const { spawn } = require("node:child_process");
|
|
9
|
+
|
|
10
|
+
const packageRoot = path.resolve(__dirname, "..", "..", "..");
|
|
11
|
+
const cliPath = path.join(packageRoot, "scripts", "cli", "main.ts");
|
|
12
|
+
const configDir = process.env.CARTETHYIA_CONFIG_DIR || (
|
|
13
|
+
process.platform === "win32"
|
|
14
|
+
? path.join(process.env.APPDATA || os.homedir(), "Cartethyia")
|
|
15
|
+
: process.platform === "darwin"
|
|
16
|
+
? path.join(os.homedir(), "Library", "Application Support", "Cartethyia")
|
|
17
|
+
: path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "Cartethyia")
|
|
18
|
+
);
|
|
19
|
+
const trayMode = process.argv[2] === "tray";
|
|
20
|
+
// The launcher itself runs under Node, so `process.execPath` is node.exe — not
|
|
21
|
+
// the Bun the child needs. Resolve bun from PATH instead; WScript.Shell does
|
|
22
|
+
// not inherit this process's environment, so the child needs an absolute path.
|
|
23
|
+
function resolveBunPath() {
|
|
24
|
+
if (process.env.CARTETHYIA_BUN) return process.env.CARTETHYIA_BUN;
|
|
25
|
+
const name = process.platform === "win32" ? "bun.exe" : "bun";
|
|
26
|
+
for (const dir of (process.env.PATH || "").split(path.delimiter)) {
|
|
27
|
+
if (!dir) continue;
|
|
28
|
+
const candidate = path.join(dir, name);
|
|
29
|
+
try {
|
|
30
|
+
if (fs.statSync(candidate).isFile()) return candidate;
|
|
31
|
+
} catch {}
|
|
32
|
+
}
|
|
33
|
+
return name;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const startupSignalPath = trayMode
|
|
37
|
+
? path.join(os.tmpdir(), `cartethyia-tray-${process.pid}-${randomUUID()}.json`)
|
|
38
|
+
: null;
|
|
39
|
+
let spawnError;
|
|
40
|
+
// On Windows the tray must start with no console at all, and neither
|
|
41
|
+
// `detached: true` nor `windowsHide: true` achieves that: a console-subsystem
|
|
42
|
+
// child of a window-less parent still allocates a console, and inside Windows
|
|
43
|
+
// Terminal the tab is already on screen before a flag can take effect
|
|
44
|
+
// (measured). `wscript.exe` is GUI-subsystem, so it never allocates one, and a
|
|
45
|
+
// child launched from it with window style 0 inherits that. Elsewhere
|
|
46
|
+
// `detached: true` is enough.
|
|
47
|
+
const useWindowsDetach = process.platform === "win32" && trayMode;
|
|
48
|
+
const hiddenLauncher = path.join(packageRoot, "scripts", "cli", "tray", "hidden-window.vbs");
|
|
49
|
+
const child = useWindowsDetach
|
|
50
|
+
? spawn(
|
|
51
|
+
"wscript.exe",
|
|
52
|
+
// Absolute path, not "bun": the VBS runs through WScript.Shell, which
|
|
53
|
+
// does not inherit this process's PATH additions and resolves a bare
|
|
54
|
+
// name against the system PATH only.
|
|
55
|
+
["//nologo", hiddenLauncher, resolveBunPath(), "run", cliPath, ...process.argv.slice(2)],
|
|
56
|
+
{
|
|
57
|
+
cwd: packageRoot,
|
|
58
|
+
detached: true,
|
|
59
|
+
stdio: "ignore",
|
|
60
|
+
windowsHide: true,
|
|
61
|
+
env: {
|
|
62
|
+
...process.env,
|
|
63
|
+
CARTETHYIA_CONFIG_DIR: configDir,
|
|
64
|
+
CARTETHYIA_TRAY_STARTUP_SIGNAL: startupSignalPath,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
: spawn(resolveBunPath(), ["run", cliPath, ...process.argv.slice(2)], {
|
|
69
|
+
cwd: packageRoot,
|
|
70
|
+
detached: trayMode,
|
|
71
|
+
stdio: trayMode ? "ignore" : "inherit",
|
|
72
|
+
windowsHide: trayMode,
|
|
73
|
+
env: {
|
|
74
|
+
...process.env,
|
|
75
|
+
CARTETHYIA_CONFIG_DIR: configDir,
|
|
76
|
+
...(trayMode ? { CARTETHYIA_TRAY_STARTUP_SIGNAL: startupSignalPath } : {}),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
child.once("error", (error) => {
|
|
81
|
+
spawnError = error;
|
|
82
|
+
if (error.code === "ENOENT") {
|
|
83
|
+
console.error("Cartethyia requires Bun 1.4.0 or newer. Install Bun from https://bun.sh, then run this command again.");
|
|
84
|
+
} else {
|
|
85
|
+
console.error(`Could not start Cartethyia's Bun runtime: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
process.exitCode = 1;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (trayMode) {
|
|
91
|
+
child.unref();
|
|
92
|
+
console.log("Loading Cartethyia…");
|
|
93
|
+
let settled = false;
|
|
94
|
+
const deadline = Date.now() + 60_000;
|
|
95
|
+
const finish = (error, url) => {
|
|
96
|
+
if (settled) return;
|
|
97
|
+
settled = true;
|
|
98
|
+
try {
|
|
99
|
+
fs.unlinkSync(startupSignalPath);
|
|
100
|
+
} catch {}
|
|
101
|
+
if (error) {
|
|
102
|
+
// `error.message` can be an empty string when the failure came from a
|
|
103
|
+
// signal rather than a thrown message, which printed as a bare
|
|
104
|
+
// "Cartethyia failed to start:" with no cause. Fall back to the code and
|
|
105
|
+
// name what usually causes it.
|
|
106
|
+
const detail =
|
|
107
|
+
error.message && error.message.trim() !== ""
|
|
108
|
+
? error.message
|
|
109
|
+
: `the process exited without a message (code ${child.exitCode ?? "unknown"})`;
|
|
110
|
+
console.error(`Cartethyia failed to start: ${detail}`);
|
|
111
|
+
console.error(
|
|
112
|
+
"If another Cartethyia is already running on this port, stop the terminal that started it first.",
|
|
113
|
+
);
|
|
114
|
+
process.exitCode = 1;
|
|
115
|
+
if (child.exitCode === null) child.kill();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
console.log(`Cartethyia is ready at ${url}`);
|
|
119
|
+
// The gateway child is already detached and unref'd; this Node process is
|
|
120
|
+
// the only thing still holding the launching terminal open. Exit once the
|
|
121
|
+
// URL has been shown so the shell prompt comes back.
|
|
122
|
+
setTimeout(() => process.exit(0), 1_500);
|
|
123
|
+
};
|
|
124
|
+
const poll = () => {
|
|
125
|
+
if (settled) return;
|
|
126
|
+
if (fs.existsSync(startupSignalPath)) {
|
|
127
|
+
try {
|
|
128
|
+
const signal = JSON.parse(fs.readFileSync(startupSignalPath, "utf8"));
|
|
129
|
+
if (signal.status === "ready" && typeof signal.url === "string") {
|
|
130
|
+
finish(null, signal.url);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (signal.status === "failed" && typeof signal.message === "string") {
|
|
134
|
+
finish(new Error(signal.message));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
} catch {}
|
|
138
|
+
}
|
|
139
|
+
if (spawnError) {
|
|
140
|
+
finish(spawnError);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (Date.now() >= deadline) {
|
|
144
|
+
finish(new Error("startup did not complete within 60 seconds"));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
setTimeout(poll, 150);
|
|
148
|
+
};
|
|
149
|
+
// On Windows the spawned process is `wscript.exe`, which hands off to the
|
|
150
|
+
// tray and exits immediately — a successful handoff, not a failed start. Only
|
|
151
|
+
// a direct spawn's exit says anything about the tray, so the deadline covers
|
|
152
|
+
// the handoff case and this guard covers the direct one.
|
|
153
|
+
if (!useWindowsDetach) {
|
|
154
|
+
child.once("exit", (code, signal) => {
|
|
155
|
+
if (!settled) setTimeout(() => finish(new Error(`the tray process exited before readiness (code ${code ?? signal ?? "unknown"})`)), 250);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
poll();
|
|
159
|
+
} else {
|
|
160
|
+
child.once("exit", (code, signal) => {
|
|
161
|
+
if (signal) {
|
|
162
|
+
process.kill(process.pid, signal);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
process.exitCode = code ?? 1;
|
|
166
|
+
});
|
|
167
|
+
}
|