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
package/scripts/cli/gateway.ts
CHANGED
|
@@ -1,261 +1,264 @@
|
|
|
1
|
-
import { openSync, statSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
clearInstanceLock,
|
|
4
|
-
fingerprint,
|
|
5
|
-
pidAlive,
|
|
6
|
-
readInstanceLock,
|
|
7
|
-
writeInstanceLock,
|
|
8
|
-
} from "./instance";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import { PACKAGE_ROOT, resolvePackagePath } from "../../src/config/package-paths";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
function
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
//
|
|
170
|
-
stdin
|
|
171
|
-
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
stderr
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (result.status === "
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
child.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
1
|
+
import { openSync, statSync } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
clearInstanceLock,
|
|
4
|
+
fingerprint,
|
|
5
|
+
pidAlive,
|
|
6
|
+
readInstanceLock,
|
|
7
|
+
writeInstanceLock,
|
|
8
|
+
} from "./instance";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { PACKAGE_ROOT, compiledBinaryPath, resolvePackagePath } from "../../src/config/package-paths";
|
|
11
|
+
import { spawn as spawnDetached, type ChildProcess } from "node:child_process";
|
|
12
|
+
import {
|
|
13
|
+
blockedPortMessage,
|
|
14
|
+
gatewayLogPath,
|
|
15
|
+
listenPortErrorMessage,
|
|
16
|
+
probeListenPort,
|
|
17
|
+
startupFailureMessage,
|
|
18
|
+
} from "./port";
|
|
19
|
+
|
|
20
|
+
/** Resolve a configured gateway without mistaking a Bun launcher for a compiled app. */
|
|
21
|
+
export async function gatewayExecutable(configured: string | undefined): Promise<readonly string[]> {
|
|
22
|
+
const trimmed = configured?.trim();
|
|
23
|
+
const binary = trimmed ? resolvePackagePath(trimmed) : resolvePackagePath(compiledBinaryPath());
|
|
24
|
+
if (await Bun.file(binary).exists()) {
|
|
25
|
+
return binary.endsWith(".ts") ? [process.execPath, "run", binary] : [binary];
|
|
26
|
+
}
|
|
27
|
+
if (trimmed) throw new Error(`Configured gateway binary does not exist: ${trimmed}`);
|
|
28
|
+
return [process.execPath, "run", resolvePackagePath("src", "main.ts")];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function detachedSubprocess(executable: readonly string[], cwd: string, env: NodeJS.ProcessEnv, logFd: number | null): Bun.Subprocess {
|
|
32
|
+
const child: ChildProcess = spawnDetached(executable[0]!, [...executable.slice(1)], {
|
|
33
|
+
cwd,
|
|
34
|
+
env,
|
|
35
|
+
detached: true,
|
|
36
|
+
windowsHide: true,
|
|
37
|
+
stdio: ["ignore", logFd ?? "ignore", logFd ?? "ignore"],
|
|
38
|
+
});
|
|
39
|
+
child.unref();
|
|
40
|
+
const exited = new Promise<number>((resolve) => {
|
|
41
|
+
child.once("exit", (code) => resolve(code ?? 1));
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
pid: child.pid!,
|
|
45
|
+
exited,
|
|
46
|
+
exitCode: null,
|
|
47
|
+
signalCode: null,
|
|
48
|
+
kill: () => child.kill(),
|
|
49
|
+
unref: () => child.unref(),
|
|
50
|
+
} as unknown as Bun.Subprocess;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Frees the port when what holds it is this install's own gateway.
|
|
55
|
+
*
|
|
56
|
+
* Returns `null` when the port is free (or was successfully taken), and a
|
|
57
|
+
* message to fail with otherwise. It never kills a process it cannot identify:
|
|
58
|
+
* a stranger's service on this port is reported, not drained.
|
|
59
|
+
*
|
|
60
|
+
* The probe is two-step on purpose. `/health` alone is answered by anything, so
|
|
61
|
+
* it only says "something is there"; the drain request is what distinguishes
|
|
62
|
+
* ours from someone else's, because it is token-gated — and a 401 means another
|
|
63
|
+
* Cartethyia install, which this one must not stop.
|
|
64
|
+
*/
|
|
65
|
+
async function claimPort(port: number, drainToken: string): Promise<string | null> {
|
|
66
|
+
// A lock naming this install's own gateway is the strongest evidence
|
|
67
|
+
// available, so prefer it over the network probe when it is live.
|
|
68
|
+
const lock = readInstanceLock();
|
|
69
|
+
const ours = lock !== null && lock.port === port && lock.tokenFingerprint === fingerprint(drainToken);
|
|
70
|
+
|
|
71
|
+
const response = await fetch(`http://127.0.0.1:${port}/admin/drain`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { "x-drain-token": drainToken },
|
|
74
|
+
signal: AbortSignal.timeout(3_000),
|
|
75
|
+
}).catch(() => null);
|
|
76
|
+
|
|
77
|
+
if (response === null) {
|
|
78
|
+
// Nothing answered a drain request. If our own lock names a dead pid, the
|
|
79
|
+
// marker is stale and nothing is holding the port — clear it and continue.
|
|
80
|
+
if (ours && !pidAlive(lock.pid)) {
|
|
81
|
+
clearInstanceLock();
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return `Port ${port} is busy and nothing there answers a drain request. Stop whatever holds it, or set a different PORT.`;
|
|
85
|
+
}
|
|
86
|
+
if (response.status === 401) {
|
|
87
|
+
return `Another Cartethyia gateway is already running on port ${port} under a different install. Stop it from its own terminal or tray — this install will not stop a gateway it does not own.`;
|
|
88
|
+
}
|
|
89
|
+
if (response.status === 403) {
|
|
90
|
+
return `Port ${port} refused a drain from loopback. Stop what holds it, then start again.`;
|
|
91
|
+
}
|
|
92
|
+
if (response.status !== 202) {
|
|
93
|
+
return `Port ${port} is busy and refused to drain (HTTP ${response.status}). Stop it manually, then start again.`;
|
|
94
|
+
}
|
|
95
|
+
// Drain accepted. Wait for the port to actually free: the gateway flushes its
|
|
96
|
+
// 202 before closing sockets, so acceptance is not release.
|
|
97
|
+
const deadline = Date.now() + 30_000;
|
|
98
|
+
while (Date.now() < deadline) {
|
|
99
|
+
const stillUp = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
100
|
+
signal: AbortSignal.timeout(500),
|
|
101
|
+
}).catch(() => null);
|
|
102
|
+
if (stillUp === null) {
|
|
103
|
+
clearInstanceLock();
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
await Bun.sleep(200);
|
|
107
|
+
}
|
|
108
|
+
return `The gateway on port ${port} accepted a drain but did not release the port within 30 seconds. Stop it manually, then start again.`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Starts the gateway and resolves once it answers its own health probe.
|
|
113
|
+
*
|
|
114
|
+
* The child is attached by default. A detached child cannot be killed from an
|
|
115
|
+
* `exit` handler on Windows (measured: `kill()` runs, the gateway keeps
|
|
116
|
+
* serving), and a terminal's Ctrl-C only reaches children in the same process
|
|
117
|
+
* group. Staying attached means Ctrl-C and the `exit` backstop both stop the
|
|
118
|
+
* gateway. "Hide to tray" passes `detached: true` because that gateway must
|
|
119
|
+
* outlive this process.
|
|
120
|
+
*/
|
|
121
|
+
export async function startGateway(
|
|
122
|
+
port: number,
|
|
123
|
+
drainToken: string,
|
|
124
|
+
options: { readonly detached?: boolean } = {},
|
|
125
|
+
): Promise<Bun.Subprocess> {
|
|
126
|
+
// A dead marker from the previous process must not turn its now-reused port
|
|
127
|
+
// into an ownership claim. Only identity + drain authorization may stop one.
|
|
128
|
+
const lock = readInstanceLock();
|
|
129
|
+
if (lock && !pidAlive(lock.pid)) clearInstanceLock();
|
|
130
|
+
const probe = await probeListenPort(port);
|
|
131
|
+
if (probe.status === "blocked") {
|
|
132
|
+
if (probe.code === "EADDRINUSE") {
|
|
133
|
+
const claimed = await claimPort(port, drainToken);
|
|
134
|
+
if (claimed !== null) {
|
|
135
|
+
if (claimed.startsWith("Port") && claimed.includes("nothing there answers")) {
|
|
136
|
+
throw new Error(blockedPortMessage(port));
|
|
137
|
+
}
|
|
138
|
+
throw new Error(claimed);
|
|
139
|
+
}
|
|
140
|
+
const released = await probeListenPort(port);
|
|
141
|
+
if (released.status === "blocked") throw new Error(blockedPortMessage(port));
|
|
142
|
+
} else {
|
|
143
|
+
throw new Error(listenPortErrorMessage(port, probe));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// The gateway logs a startup phase line per stage. In menu mode those lines
|
|
147
|
+
// would scroll through the same terminal the menu is drawing on, so they go
|
|
148
|
+
// to a file instead and the operator sees only the menu.
|
|
149
|
+
const configDir = process.env.CARTETHYIA_CONFIG_DIR;
|
|
150
|
+
let logFd: number | null = null;
|
|
151
|
+
if (configDir) {
|
|
152
|
+
try {
|
|
153
|
+
logFd = openSync(join(configDir, "gateway.log"), "a");
|
|
154
|
+
} catch {
|
|
155
|
+
// A read-only config directory costs only the log, not startup.
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const logPath = gatewayLogPath(configDir);
|
|
159
|
+
let logOffset = 0;
|
|
160
|
+
if (logPath) {
|
|
161
|
+
try { logOffset = statSync(logPath).size; } catch { logOffset = 0; }
|
|
162
|
+
}
|
|
163
|
+
const executable = [...await gatewayExecutable(process.env.CARTETHYIA_GATEWAY_BINARY)];
|
|
164
|
+
const child = options.detached
|
|
165
|
+
? detachedSubprocess(executable, PACKAGE_ROOT, { ...process.env, CARTETHYIA_DRAIN_TOKEN: drainToken }, logFd)
|
|
166
|
+
: Bun.spawn(executable, {
|
|
167
|
+
cwd: PACKAGE_ROOT,
|
|
168
|
+
env: { ...process.env, CARTETHYIA_DRAIN_TOKEN: drainToken },
|
|
169
|
+
// "ignore", not "inherit": the gateway does not read stdin, and in menu
|
|
170
|
+
// mode the menu is the only stdin consumer. With "inherit" the gateway
|
|
171
|
+
// held the fd and swallowed every keypress after the first, so the menu
|
|
172
|
+
// rendered but never received Enter.
|
|
173
|
+
stdin: "ignore",
|
|
174
|
+
stdout: logFd ?? "ignore",
|
|
175
|
+
// stderr goes to the same file: "inherit" ties the gateway to the
|
|
176
|
+
// terminal, so closing that terminal took the gateway down with it and
|
|
177
|
+
// "Hide to tray" left nothing running.
|
|
178
|
+
stderr: logFd ?? "ignore",
|
|
179
|
+
detached: false,
|
|
180
|
+
});
|
|
181
|
+
const startupController = new AbortController();
|
|
182
|
+
const ready = (async (): Promise<void> => {
|
|
183
|
+
const deadline = Date.now() + 30_000;
|
|
184
|
+
while (!startupController.signal.aborted && Date.now() < deadline) {
|
|
185
|
+
try {
|
|
186
|
+
const response = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
187
|
+
signal: AbortSignal.timeout(500),
|
|
188
|
+
});
|
|
189
|
+
if (response.ok) {
|
|
190
|
+
const body: unknown = await response.json();
|
|
191
|
+
if (body !== null && typeof body === "object" && "status" in body && body.status === "ok") return;
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
// The gateway is not accepting health requests yet.
|
|
195
|
+
}
|
|
196
|
+
await Bun.sleep(250);
|
|
197
|
+
}
|
|
198
|
+
if (startupController.signal.aborted) throw new Error("Cartethyia exited before becoming ready");
|
|
199
|
+
throw new Error("Cartethyia did not open its configured port within 30 seconds");
|
|
200
|
+
})();
|
|
201
|
+
const exitedEarly = child.exited.then((code) => {
|
|
202
|
+
startupController.abort();
|
|
203
|
+
return { status: "exit" as const, code };
|
|
204
|
+
});
|
|
205
|
+
const readyResult = ready.then(
|
|
206
|
+
() => ({ status: "ready" as const }),
|
|
207
|
+
(error: unknown) => ({ status: "failure" as const, error }),
|
|
208
|
+
);
|
|
209
|
+
try {
|
|
210
|
+
const result = await Promise.race([readyResult, exitedEarly]);
|
|
211
|
+
if (result.status === "exit") {
|
|
212
|
+
throw new Error(startupFailureMessage(port, result.code, logPath, logOffset));
|
|
213
|
+
}
|
|
214
|
+
if (result.status === "failure") throw result.error;
|
|
215
|
+
// Recorded only once the child is actually serving: a marker written before
|
|
216
|
+
// readiness would name a gateway that never came up, and the next start
|
|
217
|
+
// would treat a live port as ours when it is not.
|
|
218
|
+
writeInstanceLock(port, child.pid, drainToken, executable[0]!);
|
|
219
|
+
if (options.detached) child.unref();
|
|
220
|
+
return child;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
child.kill();
|
|
223
|
+
await child.exited;
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Stops a child gateway, draining in-flight responses before termination. */
|
|
229
|
+
export async function stopGateway(
|
|
230
|
+
current: Bun.Subprocess,
|
|
231
|
+
port: number,
|
|
232
|
+
drainToken: string,
|
|
233
|
+
): Promise<void> {
|
|
234
|
+
let drainAccepted = false;
|
|
235
|
+
try {
|
|
236
|
+
const response = await fetch(`http://127.0.0.1:${port}/admin/drain`, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers: { "x-drain-token": drainToken },
|
|
239
|
+
signal: AbortSignal.timeout(2_000),
|
|
240
|
+
});
|
|
241
|
+
drainAccepted = response.status === 202;
|
|
242
|
+
} catch {
|
|
243
|
+
// The process may already be exiting.
|
|
244
|
+
}
|
|
245
|
+
if (!drainAccepted) {
|
|
246
|
+
console.error("Could not request a graceful gateway drain; terminating the gateway process.");
|
|
247
|
+
current.kill();
|
|
248
|
+
await current.exited;
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
let timer: Timer | undefined;
|
|
252
|
+
const drained = await Promise.race([
|
|
253
|
+
current.exited,
|
|
254
|
+
new Promise<null>((resolveExit) => {
|
|
255
|
+
timer = setTimeout(() => resolveExit(null), 30_000);
|
|
256
|
+
}),
|
|
257
|
+
]);
|
|
258
|
+
clearTimeout(timer);
|
|
259
|
+
if (drained === null) {
|
|
260
|
+
console.error("Graceful drain timed out; terminating the gateway process.");
|
|
261
|
+
current.kill();
|
|
262
|
+
await current.exited;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -3,10 +3,10 @@ import { constants } from "node:fs";
|
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { Client } from "pg";
|
|
5
5
|
import Redis from "ioredis";
|
|
6
|
-
import { hasPlaceholderSecrets, readEnvFile } from "./
|
|
7
|
-
import { parseServiceUrl, probeTcpService } from "./
|
|
8
|
-
import { resolveDataDir, resolveDbMode } from "
|
|
9
|
-
import { bunVersionAtLeast, readRequiredBunVersion } from "./
|
|
6
|
+
import { hasPlaceholderSecrets, readEnvFile } from "./env";
|
|
7
|
+
import { parseServiceUrl, probeTcpService } from "./services";
|
|
8
|
+
import { resolveDataDir, resolveDbMode } from "../../src/persistence/db-mode";
|
|
9
|
+
import { bunVersionAtLeast, readRequiredBunVersion } from "./version";
|
|
10
10
|
|
|
11
11
|
export type CheckPhase = "preflight" | "configuration" | "runtime";
|
|
12
12
|
export type CheckStatus = "pass" | "warn" | "fail" | "skip";
|