cartethyia 2.7.4 → 2.7.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 +5 -0
- package/README.md +18 -6
- package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai.webp +0 -0
- package/dist/dashboard/assets/providers/zcode.webp +0 -0
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +60 -59
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +794 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +19 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +33 -6
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +56 -24
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +37 -31
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +43 -299
- package/src/console/cli-tools/fs-ops.ts +29 -0
- package/src/console/cli-tools/injectors/claude.ts +3 -3
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/driver.ts +150 -247
- package/src/console/cli-tools/injectors/kilo.ts +146 -21
- package/src/console/cli-tools/injectors/opencode.ts +176 -153
- package/src/console/cli-tools/routes.ts +7 -1
- package/src/console/cli-tools/service.ts +8 -7
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +7 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +23 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +259 -66
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +11 -13
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +4 -4
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +252 -97
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +9 -2
- package/src/observability/event-query.ts +61 -291
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +19 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +121 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +29 -2
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +6 -10
- package/src/protocol/dsml.ts +8 -125
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +6 -6
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/providers/adapters/compatible-adapter.ts +71 -4
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +35 -37
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +22 -13
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +35 -3
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +9 -9
- package/src/providers/integrations/github/github-discovery.ts +70 -12
- package/src/providers/integrations/github/github-oauth.ts +184 -14
- package/src/providers/integrations/github/github.ts +134 -26
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +80 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +422 -71
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +51 -8
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +198 -133
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/byok-wire-profile.ts +19 -15
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-usage.ts +50 -19
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +13 -6
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +17 -3
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +70 -22
- package/src/transport/dispatch/attempt-loop.ts +22 -0
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +21 -1
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/error-lifecycle.ts +4 -0
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +7 -13
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +159 -37
- package/src/transport/shutdown-notice.ts +2 -2
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/reasoning-summary.ts +9 -14
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +431 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js +0 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-BayTTy01.js +0 -9
- package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/console/cli-tools/injectors/cline.ts +0 -110
- package/src/console/cli-tools/injectors/copilot.ts +0 -108
- package/src/console/cli-tools/injectors/cowork.ts +0 -64
- package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
- package/src/console/cli-tools/injectors/droid.ts +0 -96
- package/src/console/cli-tools/injectors/grok-build.ts +0 -70
- package/src/console/cli-tools/injectors/hermes.ts +0 -101
- package/src/console/cli-tools/injectors/jcode.ts +0 -115
- package/src/console/cli-tools/injectors/openclaw.ts +0 -161
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { Elysia, type AnyElysia } from "elysia";
|
|
2
|
-
import {
|
|
3
|
-
import { consoleErrorHandler, requireTenantScope } from "../shared/errors";
|
|
2
|
+
import { consoleErrorHandler, requireTenantScope, ConsoleDomainError } from "../shared/errors";
|
|
4
3
|
import { consoleSseResponse, createConsoleSseStream } from "../observability/sse";
|
|
5
4
|
import type { ObservabilityStore } from "../observability/contracts";
|
|
6
5
|
import type { ConsoleAccessResolver } from "../auth/access";
|
|
7
6
|
import { fetchLatestRelease, type LatestRelease } from "./repository-updates";
|
|
8
7
|
import { isNewerRelease } from "./version-compare";
|
|
8
|
+
import {
|
|
9
|
+
isFreshlyInstalling,
|
|
10
|
+
readUpdateStatus,
|
|
11
|
+
spawnDetachedUpdate,
|
|
12
|
+
updateStatusPath,
|
|
13
|
+
writeUpdateStatus,
|
|
14
|
+
} from "./apply-update";
|
|
15
|
+
import { CARTETHYIA_VERSION } from "../../version";
|
|
9
16
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
/** Minimal drain surface the apply route needs from the process coordinator. */
|
|
18
|
+
export interface UpdateShutdownCoordinator {
|
|
19
|
+
readonly begin: (reason: "update") => Promise<void>;
|
|
20
|
+
readonly totalShutdownBudgetMs: () => number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Memory is sampled once per process (a single shared sampler feeding every
|
|
24
|
+
// notifications subscriber), not per visible tab: ten seconds per stream made a
|
|
25
|
+
// console with many open tabs wake up constantly for numbers nobody is reading.
|
|
26
|
+
// 30 s is still responsive for a memory warning while cutting that work by three.
|
|
13
27
|
const MEMORY_SAMPLE_INTERVAL_MS = 30_000;
|
|
14
28
|
const UPDATE_SAMPLE_INTERVAL_MS = 60_000;
|
|
15
29
|
const HIGH_MEMORY_PERCENT = 85;
|
|
@@ -30,7 +44,7 @@ const MEMORY_GROWTH_RATIO = 1.2;
|
|
|
30
44
|
const MAX_MEMORY_SAMPLES = 8;
|
|
31
45
|
const RELEASES_URL = "https://github.com/risunCodex/Cartethyia/releases";
|
|
32
46
|
|
|
33
|
-
|
|
47
|
+
interface NotificationEvent {
|
|
34
48
|
readonly id: string;
|
|
35
49
|
readonly kind: "memory-high" | "memory-growth" | "update-available";
|
|
36
50
|
readonly title: string;
|
|
@@ -38,6 +52,8 @@ export interface NotificationEvent {
|
|
|
38
52
|
readonly createdAt: string;
|
|
39
53
|
readonly level: "warning" | "info";
|
|
40
54
|
readonly href?: string;
|
|
55
|
+
/** The release tag that triggered an `update-available` alert (with `v` prefix). */
|
|
56
|
+
readonly version?: string;
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
export function memoryNotifications(
|
|
@@ -85,6 +101,143 @@ export function memoryNotifications(
|
|
|
85
101
|
};
|
|
86
102
|
}
|
|
87
103
|
|
|
104
|
+
/** A subscriber of the process-wide update/notification sampler. */
|
|
105
|
+
type NotificationListener = (notification: NotificationEvent) => void;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* One sampler per gateway composition (the console mounts `createUpdateRoutes`
|
|
109
|
+
* exactly once per process, so this is process-scoped in production). It owns
|
|
110
|
+
* the memory and release cadences and fans results out to every subscriber;
|
|
111
|
+
* an SSE connection only subscribes. The timer runs while at least one
|
|
112
|
+
* subscriber is connected and stops with the last one, so an idle gateway
|
|
113
|
+
* samples nothing.
|
|
114
|
+
*/
|
|
115
|
+
function createUpdateSampler(config: {
|
|
116
|
+
readonly observabilityStore: ObservabilityStore;
|
|
117
|
+
readonly fetchRelease: () => Promise<LatestRelease | null>;
|
|
118
|
+
}): { subscribe(listener: NotificationListener): () => void } {
|
|
119
|
+
const subscribers = new Set<NotificationListener>();
|
|
120
|
+
const samples: Array<{ at: number; rss: number; percent: number }> = [];
|
|
121
|
+
let memoryState = { high: false, growth: false };
|
|
122
|
+
let latestTag: string | undefined;
|
|
123
|
+
let memoryTimer: ReturnType<typeof setTimeout> | undefined;
|
|
124
|
+
let updateTimer: ReturnType<typeof setTimeout> | undefined;
|
|
125
|
+
|
|
126
|
+
const broadcast = (notifications: readonly NotificationEvent[]): void => {
|
|
127
|
+
if (notifications.length === 0) return;
|
|
128
|
+
for (const listener of subscribers) {
|
|
129
|
+
for (const notification of notifications) listener(notification);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const sampleMemory = async (): Promise<void> => {
|
|
134
|
+
try {
|
|
135
|
+
const at = Date.now();
|
|
136
|
+
// Fresh, not `health`: that read is cached for 10 s and this samples on
|
|
137
|
+
// that exact cadence, so the cached bytes would make consecutive samples
|
|
138
|
+
// identical and a growth trend unreadable.
|
|
139
|
+
const live = await config.observabilityStore.liveMemory();
|
|
140
|
+
samples.push({ at, rss: live.memory_bytes, percent: live.memory_percent });
|
|
141
|
+
while (samples.length > 0 && at - samples[0]!.at > MEMORY_GROWTH_WINDOW_MS * 2) samples.shift();
|
|
142
|
+
if (samples.length > MAX_MEMORY_SAMPLES) samples.splice(0, samples.length - MAX_MEMORY_SAMPLES);
|
|
143
|
+
const computed = memoryNotifications(samples, memoryState);
|
|
144
|
+
memoryState = { high: computed.high, growth: computed.growth };
|
|
145
|
+
broadcast(computed.notifications);
|
|
146
|
+
} catch {
|
|
147
|
+
// A transient check must not terminate the feed or hide later events.
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const sampleUpdate = async (): Promise<void> => {
|
|
152
|
+
try {
|
|
153
|
+
const release = await config.fetchRelease();
|
|
154
|
+
if (release === null || release.tag_name === latestTag) return;
|
|
155
|
+
if (!isNewerRelease(CARTETHYIA_VERSION, release.tag_name)) return;
|
|
156
|
+
latestTag = release.tag_name;
|
|
157
|
+
broadcast([{
|
|
158
|
+
id: `update-${release.tag_name}`,
|
|
159
|
+
kind: "update-available",
|
|
160
|
+
title: "Cartethyia update available",
|
|
161
|
+
message: `Version ${release.tag_name} is available; this gateway runs ${CARTETHYIA_VERSION}.`,
|
|
162
|
+
createdAt: new Date().toISOString(),
|
|
163
|
+
level: "info",
|
|
164
|
+
href: RELEASES_URL,
|
|
165
|
+
version: release.tag_name,
|
|
166
|
+
} satisfies NotificationEvent]);
|
|
167
|
+
} catch {
|
|
168
|
+
// Release discovery is best-effort and cannot interrupt memory sampling.
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const start = (): void => {
|
|
173
|
+
if (memoryTimer !== undefined) return;
|
|
174
|
+
memoryTimer = setTimeout(async function tick() {
|
|
175
|
+
memoryTimer = setTimeout(tick, MEMORY_SAMPLE_INTERVAL_MS);
|
|
176
|
+
await sampleMemory();
|
|
177
|
+
}, MEMORY_SAMPLE_INTERVAL_MS);
|
|
178
|
+
updateTimer = setTimeout(async function tick() {
|
|
179
|
+
updateTimer = setTimeout(tick, UPDATE_SAMPLE_INTERVAL_MS);
|
|
180
|
+
await sampleUpdate();
|
|
181
|
+
}, UPDATE_SAMPLE_INTERVAL_MS);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const stop = (): void => {
|
|
185
|
+
if (memoryTimer !== undefined) clearTimeout(memoryTimer);
|
|
186
|
+
if (updateTimer !== undefined) clearTimeout(updateTimer);
|
|
187
|
+
memoryTimer = undefined;
|
|
188
|
+
updateTimer = undefined;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
subscribe(listener) {
|
|
193
|
+
subscribers.add(listener);
|
|
194
|
+
// Bootstrap: the first frames a fresh connection sees are the current
|
|
195
|
+
// alerts — a memory state that is high right now, an update that exists
|
|
196
|
+
// now — computed with a fresh transition state exactly like the old
|
|
197
|
+
// per-connection initial tick did. After that, the shared cadence takes
|
|
198
|
+
// over.
|
|
199
|
+
void (async () => {
|
|
200
|
+
try {
|
|
201
|
+
const at = Date.now();
|
|
202
|
+
const live = await config.observabilityStore.liveMemory();
|
|
203
|
+
const ring = [
|
|
204
|
+
...samples,
|
|
205
|
+
{ at, rss: live.memory_bytes, percent: live.memory_percent },
|
|
206
|
+
].filter((sample) => at - sample.at <= MEMORY_GROWTH_WINDOW_MS * 2)
|
|
207
|
+
.slice(-MAX_MEMORY_SAMPLES);
|
|
208
|
+
for (const notification of memoryNotifications(ring, { high: false, growth: false }).notifications) {
|
|
209
|
+
listener(notification);
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
// A transient read must not fail the subscription.
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const release = await config.fetchRelease();
|
|
216
|
+
if (release !== null && isNewerRelease(CARTETHYIA_VERSION, release.tag_name)) {
|
|
217
|
+
listener({
|
|
218
|
+
id: `update-${release.tag_name}`,
|
|
219
|
+
kind: "update-available",
|
|
220
|
+
title: "Cartethyia update available",
|
|
221
|
+
message: `Version ${release.tag_name} is available; this gateway runs ${CARTETHYIA_VERSION}.`,
|
|
222
|
+
createdAt: new Date().toISOString(),
|
|
223
|
+
level: "info",
|
|
224
|
+
href: RELEASES_URL,
|
|
225
|
+
version: release.tag_name,
|
|
226
|
+
} satisfies NotificationEvent);
|
|
227
|
+
}
|
|
228
|
+
} catch {
|
|
229
|
+
// Best-effort release discovery.
|
|
230
|
+
}
|
|
231
|
+
})();
|
|
232
|
+
if (subscribers.size > 0) start();
|
|
233
|
+
return () => {
|
|
234
|
+
subscribers.delete(listener);
|
|
235
|
+
if (subscribers.size === 0) stop();
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
88
241
|
/**
|
|
89
242
|
* Read-only update discovery for the console sidebar.
|
|
90
243
|
*
|
|
@@ -97,109 +250,111 @@ export function createUpdateRoutes(config: {
|
|
|
97
250
|
readonly accessResolver: ConsoleAccessResolver;
|
|
98
251
|
readonly observabilityStore: ObservabilityStore;
|
|
99
252
|
readonly fetchRelease?: () => Promise<LatestRelease | null>;
|
|
253
|
+
/**
|
|
254
|
+
* The process `ShutdownCoordinator`, narrowed to the drain surface the apply
|
|
255
|
+
* route needs. Optional only so reduced compositions (tests of the read-only
|
|
256
|
+
* routes) build without one; the apply route itself throws when absent,
|
|
257
|
+
* because exiting without a drain is the bug this redesign removes.
|
|
258
|
+
*/
|
|
259
|
+
readonly shutdownCoordinator?: UpdateShutdownCoordinator;
|
|
260
|
+
/** Overrides status-file resolution (tests); defaults to the config dir. */
|
|
261
|
+
readonly configDir?: string;
|
|
262
|
+
/** Overrides the detached spawn (tests); defaults to the real installer. */
|
|
263
|
+
readonly spawnUpdate?: typeof spawnDetachedUpdate;
|
|
100
264
|
}): AnyElysia {
|
|
101
265
|
const fetchRelease = config.fetchRelease ?? fetchLatestRelease;
|
|
266
|
+
const configDir = config.configDir;
|
|
267
|
+
const spawnUpdate = config.spawnUpdate ?? spawnDetachedUpdate;
|
|
268
|
+
const sampler = createUpdateSampler({
|
|
269
|
+
observabilityStore: config.observabilityStore,
|
|
270
|
+
fetchRelease,
|
|
271
|
+
});
|
|
102
272
|
return new Elysia()
|
|
103
273
|
.error(consoleErrorHandler("Update check failed"))
|
|
104
274
|
.get("/updates/release", async () => await fetchRelease())
|
|
105
|
-
|
|
275
|
+
// Same no-scope posture as `/updates/release`: the status file holds no
|
|
276
|
+
// credentials and no tenant data, only whether the last install landed.
|
|
277
|
+
.get("/updates/status", () => readUpdateStatus(configDir) ?? { status: "idle", startedAt: new Date(0).toISOString() })
|
|
278
|
+
.post("/updates/apply", async ({ request }) => {
|
|
106
279
|
requireTenantScope(config.accessResolver(request), "dashboard:write");
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
280
|
+
if (config.shutdownCoordinator === undefined) {
|
|
281
|
+
throw new ConsoleDomainError(
|
|
282
|
+
"update_unavailable",
|
|
283
|
+
503,
|
|
284
|
+
"The update flow needs a shutdown coordinator; this composition cannot apply updates.",
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
const status = readUpdateStatus(configDir);
|
|
288
|
+
// Double-apply guard: a status file younger than ten minutes that still
|
|
289
|
+
// says "installing" means the detached installer is running and another
|
|
290
|
+
// spawn would race it for the same npm cache and package directory.
|
|
291
|
+
if (isFreshlyInstalling(status)) {
|
|
292
|
+
throw new ConsoleDomainError(
|
|
293
|
+
"update_in_progress",
|
|
294
|
+
409,
|
|
295
|
+
"An update is already installing. Check /updates/status for its result.",
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
// The target tag is best-effort: it labels the intent file, but a failed
|
|
299
|
+
// release lookup must not block the install itself.
|
|
300
|
+
const release = await fetchRelease().catch(() => null);
|
|
301
|
+
const targetTag = release?.tag_name.replace(/^v/, "");
|
|
302
|
+
writeUpdateStatus(
|
|
303
|
+
{
|
|
304
|
+
status: "installing",
|
|
305
|
+
...(targetTag === undefined ? {} : { targetTag }),
|
|
306
|
+
startedAt: new Date().toISOString(),
|
|
307
|
+
},
|
|
308
|
+
configDir,
|
|
309
|
+
);
|
|
310
|
+
const spawnResult = spawnUpdate(configDir === undefined ? {} : { configDir });
|
|
311
|
+
if (!spawnResult.started) {
|
|
312
|
+
// Flip the intent file to `failed` here, regardless of whether the spawn
|
|
313
|
+
// implementation already did: the dashboard's poll loop must see a
|
|
314
|
+
// terminal status, never a silent `installing` with no installer behind it.
|
|
315
|
+
writeUpdateStatus(
|
|
316
|
+
{
|
|
317
|
+
status: "failed",
|
|
318
|
+
startedAt: new Date().toISOString(),
|
|
319
|
+
exitCode: -1,
|
|
320
|
+
stderrTail: spawnResult.error ?? "unknown error",
|
|
321
|
+
},
|
|
322
|
+
configDir,
|
|
323
|
+
);
|
|
324
|
+
throw new ConsoleDomainError(
|
|
325
|
+
"update_spawn_failed",
|
|
326
|
+
500,
|
|
327
|
+
"The detached installer could not be started.",
|
|
328
|
+
{ error: spawnResult.error ?? "unknown" },
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
const coordinator = config.shutdownCoordinator;
|
|
332
|
+
// Drain like a planned stop, then exit. The safety timer is the
|
|
333
|
+
// coordinator's own total budget: begin() is bounded by design, but a
|
|
334
|
+
// hung hook must not keep a half-updated process alive past it.
|
|
335
|
+
const budgetMs = coordinator.totalShutdownBudgetMs();
|
|
336
|
+
const safety = setTimeout(() => process.exit(0), budgetMs);
|
|
337
|
+
if (typeof safety.unref === "function") safety.unref();
|
|
338
|
+
void coordinator
|
|
339
|
+
.begin("update")
|
|
340
|
+
.catch(() => undefined)
|
|
341
|
+
.finally(() => {
|
|
342
|
+
clearTimeout(safety);
|
|
343
|
+
process.exit(0);
|
|
344
|
+
})
|
|
345
|
+
// The finally callback itself must never surface as an unhandled
|
|
346
|
+
// rejection: exit either succeeded or the process is already leaving.
|
|
347
|
+
.catch(() => undefined);
|
|
348
|
+
return { started: true, statusPath: updateStatusPath(configDir) };
|
|
119
349
|
})
|
|
120
350
|
.get("/notifications/stream", ({ request }) => {
|
|
121
|
-
|
|
351
|
+
requireTenantScope(
|
|
122
352
|
config.accessResolver(request),
|
|
123
353
|
"dashboard:read",
|
|
124
|
-
)
|
|
354
|
+
);
|
|
125
355
|
return consoleSseResponse(
|
|
126
356
|
createConsoleSseStream(request.signal, (sender) => {
|
|
127
|
-
|
|
128
|
-
let memoryTimer: ReturnType<typeof setTimeout> | undefined;
|
|
129
|
-
let updateTimer: ReturnType<typeof setTimeout> | undefined;
|
|
130
|
-
let latestTag: string | undefined;
|
|
131
|
-
let memoryState = { high: false, growth: false };
|
|
132
|
-
let samples: Array<{ at: number; rss: number; percent: number }> = [];
|
|
133
|
-
|
|
134
|
-
const publishMemory = async (): Promise<void> => {
|
|
135
|
-
if (closed) return;
|
|
136
|
-
try {
|
|
137
|
-
const at = Date.now();
|
|
138
|
-
// Fresh, not `health`: that read is cached for 10 s and this
|
|
139
|
-
// samples on that exact cadence, so the cached bytes would make
|
|
140
|
-
// consecutive samples identical and a growth trend unreadable.
|
|
141
|
-
const live = await config.observabilityStore.liveMemory();
|
|
142
|
-
samples = [
|
|
143
|
-
...samples,
|
|
144
|
-
{ at, rss: live.memory_bytes, percent: live.memory_percent },
|
|
145
|
-
].filter((sample) => at - sample.at <= MEMORY_GROWTH_WINDOW_MS * 2)
|
|
146
|
-
.slice(-MAX_MEMORY_SAMPLES);
|
|
147
|
-
const computed = memoryNotifications(samples, memoryState);
|
|
148
|
-
memoryState = { high: computed.high, growth: computed.growth };
|
|
149
|
-
for (const notification of computed.notifications) sender.send("notification", notification);
|
|
150
|
-
} catch {
|
|
151
|
-
// A transient check must not terminate the feed or hide later events.
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const publishUpdate = async (): Promise<void> => {
|
|
156
|
-
if (closed) return;
|
|
157
|
-
try {
|
|
158
|
-
const release = await fetchRelease();
|
|
159
|
-
if (release === null || release.tag_name === latestTag) return;
|
|
160
|
-
const health = (await config.observabilityStore.health(tenantId)).value;
|
|
161
|
-
if (!isNewerRelease(health.version, release.tag_name)) return;
|
|
162
|
-
latestTag = release.tag_name;
|
|
163
|
-
sender.send("notification", {
|
|
164
|
-
id: `update-${release.tag_name}`,
|
|
165
|
-
kind: "update-available",
|
|
166
|
-
title: "Cartethyia update available",
|
|
167
|
-
message: `Version ${release.tag_name} is available; this gateway runs ${health.version}.`,
|
|
168
|
-
createdAt: new Date().toISOString(),
|
|
169
|
-
level: "info",
|
|
170
|
-
href: RELEASES_URL,
|
|
171
|
-
} satisfies NotificationEvent);
|
|
172
|
-
} catch {
|
|
173
|
-
// Release discovery is best-effort and cannot interrupt memory sampling.
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const scheduleMemory = (): void => {
|
|
178
|
-
memoryTimer = setTimeout(() => {
|
|
179
|
-
void publishMemory().finally(() => {
|
|
180
|
-
if (!closed) scheduleMemory();
|
|
181
|
-
});
|
|
182
|
-
}, MEMORY_SAMPLE_INTERVAL_MS);
|
|
183
|
-
};
|
|
184
|
-
const scheduleUpdate = (): void => {
|
|
185
|
-
updateTimer = setTimeout(() => {
|
|
186
|
-
void publishUpdate().finally(() => {
|
|
187
|
-
if (!closed) scheduleUpdate();
|
|
188
|
-
});
|
|
189
|
-
}, UPDATE_SAMPLE_INTERVAL_MS);
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
void publishMemory().finally(() => {
|
|
193
|
-
if (!closed) scheduleMemory();
|
|
194
|
-
});
|
|
195
|
-
void publishUpdate().finally(() => {
|
|
196
|
-
if (!closed) scheduleUpdate();
|
|
197
|
-
});
|
|
198
|
-
return () => {
|
|
199
|
-
closed = true;
|
|
200
|
-
clearTimeout(memoryTimer);
|
|
201
|
-
clearTimeout(updateTimer);
|
|
202
|
-
};
|
|
357
|
+
return sampler.subscribe((notification) => sender.send("notification", notification));
|
|
203
358
|
}),
|
|
204
359
|
);
|
|
205
360
|
});
|
package/src/main.ts
CHANGED
|
@@ -33,6 +33,12 @@ const boot = Manifest.isCapturing()
|
|
|
33
33
|
? undefined
|
|
34
34
|
: (globalThis.__cartethyiaBoot ??= await bootstrap());
|
|
35
35
|
|
|
36
|
+
// Hoisted once: each resolver is an env parse + range check on a cold path, and
|
|
37
|
+
// the values are boot-stable, so computing them repeatedly buys nothing.
|
|
38
|
+
const maxBodyBytes = resolveMaxBodyBytes();
|
|
39
|
+
const topology = resolveProcessTopology();
|
|
40
|
+
const drainToken = resolveDrainToken();
|
|
41
|
+
|
|
36
42
|
const app = boot
|
|
37
43
|
? createGatewayApp({
|
|
38
44
|
mode: "production",
|
|
@@ -49,7 +55,7 @@ const app = boot
|
|
|
49
55
|
telemetryBuffer: boot.deps.telemetryBuffer,
|
|
50
56
|
resolveOAuthRefresher: boot.deps.resolveOAuthRefresher,
|
|
51
57
|
oauthRefreshService: boot.deps.oauthRefreshService,
|
|
52
|
-
maxBodyBytes
|
|
58
|
+
maxBodyBytes,
|
|
53
59
|
scheduledTasks: boot.deps.scheduledTasks,
|
|
54
60
|
// The sampler is constructed in the dependency graph and already
|
|
55
61
|
// sampling on its own interval; without passing it here the app cannot
|
|
@@ -60,8 +66,8 @@ const app = boot
|
|
|
60
66
|
shutdownCoordinator: boot.shutdownCoordinator,
|
|
61
67
|
// Signal-free graceful stop for platforms where a catchable signal cannot
|
|
62
68
|
// be delivered (Windows). Off unless `CARTETHYIA_DRAIN_TOKEN` is set.
|
|
63
|
-
...(
|
|
64
|
-
? { drainToken
|
|
69
|
+
...(drainToken !== undefined
|
|
70
|
+
? { drainToken, triggerDrain: () => shutdown("SIGTERM") }
|
|
65
71
|
: {}),
|
|
66
72
|
// The console mounts in every boot: without REDIS_URL it runs on the
|
|
67
73
|
// in-process memory backend like the rest of the gateway.
|
|
@@ -141,10 +147,10 @@ if (boot) {
|
|
|
141
147
|
// left always-on: with one process declared, enabling it lets an
|
|
142
148
|
// unrelated process on the same port silently take half the traffic —
|
|
143
149
|
// which is worse than a bind error, because nothing reports it.
|
|
144
|
-
reusePort:
|
|
150
|
+
reusePort: topology.multiProcess,
|
|
145
151
|
// 8 MiB cap on request bodies keeps a rogue client from allocating
|
|
146
152
|
// gigabytes of memory in the ingress path before validation rejects.
|
|
147
|
-
maxRequestBodySize:
|
|
153
|
+
maxRequestBodySize: maxBodyBytes,
|
|
148
154
|
},
|
|
149
155
|
() => {
|
|
150
156
|
log.info(`Cartethyia listening on :${port} (Bun ${Bun.version})`);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// and never opens a socket of its own.
|
|
14
14
|
|
|
15
15
|
/** Response metadata of one opened bidi stream. */
|
|
16
|
-
|
|
16
|
+
interface Http2BidiResponse {
|
|
17
17
|
/** HTTP/2 `:status`; `0` before the response headers arrive. */
|
|
18
18
|
readonly status: number;
|
|
19
19
|
readonly headers: Readonly<Record<string, string>>;
|