cartethyia 2.5.3 → 2.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +13 -4
- package/README.md +2 -2
- package/dist/dashboard/assets/{BannedPage-hfCzi-Ux.js → BannedPage-CADDq4IU.js} +1 -1
- package/dist/dashboard/assets/BannedPage-CADDq4IU.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-DEDyKBlC.js → CliToolsPage-DuO1Z7dq.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DuO1Z7dq.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DNEPi3xU.js +1 -0
- package/dist/dashboard/assets/CombosPage-DNEPi3xU.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-bsLuZmYH.js → ConsoleLogPage-BVdpfiz1.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-BVdpfiz1.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-_UWhwziJ.js → CustomizationPage-BR5HHvbi.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BR5HHvbi.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-mUfcknqm.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-mUfcknqm.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-DDVIfZGW.js → LoginPage-V5faV9V2.js} +1 -1
- package/dist/dashboard/assets/LoginPage-V5faV9V2.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js +1 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-uPiGVTTK.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-uPiGVTTK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js +7 -0
- package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js +1 -0
- package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-D6pmQ86L.js → SetupPage-UB690SE2.js} +1 -1
- package/dist/dashboard/assets/SetupPage-UB690SE2.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-DWvKiuSl.js → SortableList-BLujdX7N.js} +1 -1
- package/dist/dashboard/assets/SortableList-BLujdX7N.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-CmkUrgdj.js → StudioPage-D5C2raAc.js} +3 -3
- package/dist/dashboard/assets/StudioPage-D5C2raAc.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-8uIcxa1W.js +56 -0
- package/dist/dashboard/assets/UsagePage-8uIcxa1W.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js +1 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +2 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/{assert-NHwYq95t.js → assert-C9FD_JwJ.js} +1 -1
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/{badge-6uzDDvqs.js → badge-CR72dasT.js} +1 -1
- package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
- package/dist/dashboard/assets/{banner-4_WNg2WZ.js → banner-CI9_eSfO.js} +1 -1
- package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
- package/dist/dashboard/assets/{card--AtFedAq.js → card-DS7CQEsO.js} +1 -1
- package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-B3e7dx3N.js → cli-tools-CNqzrfdb.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-NreUx6c8.js → clipboard-button-611pSsaX.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
- package/dist/dashboard/assets/{fingerprint-pattern-BWuHtbW4.js → concurrency-BDF0ok7y.js} +1 -1
- package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
- package/dist/dashboard/assets/database-Dz9N3zGd.js +1 -0
- package/dist/dashboard/assets/database-Dz9N3zGd.js.gz +0 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js +1 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js +1 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
- package/dist/dashboard/assets/entry-B9Xvtnfj.js +1 -0
- package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js +1 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
- package/dist/dashboard/assets/entry-DxBHnauy.js +4 -0
- package/dist/dashboard/assets/entry-DxBHnauy.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-D_eM3IgG.js → github-badge-sltk1qUi.js} +1 -1
- package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
- package/dist/dashboard/assets/{index-BydyKaDz.js → index-CmxZr14F.js} +2 -2
- package/dist/dashboard/assets/index-CmxZr14F.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BcNGF8Gr.js → inline-BhY2sSnO.js} +1 -1
- package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
- package/dist/dashboard/assets/{input-BG41et9G.js → input-V1umFiBk.js} +1 -1
- package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js +1 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Gh_W4OR_.js → list-row-1zzZYM0B.js} +1 -1
- package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
- package/dist/dashboard/assets/{live-BJGYRBnR.js → live-BivGfuUs.js} +1 -1
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CmffjLuG.js +1 -0
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-BkoLM5Ba.js → mutation-46P0oWMe.js} +1 -1
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js +2 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-D7wX2vM5.js → query-keys-BFhGpwR1.js} +1 -1
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/{routing-CcusqXA7.js → routing-CAb4F37R.js} +1 -1
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/{select-BO6h8yNv.js → select-BYR_5K2c.js} +2 -2
- package/dist/dashboard/assets/select-BYR_5K2c.js.gz +0 -0
- package/dist/dashboard/assets/{settings-mpmXTgUa.js → settings-DNX9sydi.js} +1 -1
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DwLmjUFK.js → stack-BHTfXdz_.js} +1 -1
- package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
- package/dist/dashboard/assets/{state-DYdQmdAl.js → state-mPDRlQkl.js} +1 -1
- package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
- package/dist/dashboard/assets/switch-DOmJxg7H.js +1 -0
- package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
- package/dist/dashboard/assets/terminal-0_IjPWTk.js +1 -0
- package/dist/dashboard/assets/terminal-0_IjPWTk.js.gz +0 -0
- package/dist/dashboard/assets/tokens-COzario9.js +1 -0
- package/dist/dashboard/assets/tokens-COzario9.js.gz +0 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js +1 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-w4u4CVue.js → useMutation-Dem0lXQj.js} +1 -1
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/dist/dashboard/assets/zap-C4EeVW4q.js +1 -0
- package/dist/dashboard/assets/zap-C4EeVW4q.js.gz +0 -0
- package/dist/dashboard/donation/qris-risun-marke.png +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +57 -57
- package/migrations/README.md +52 -52
- package/migrations/lite-sqlite3/0000_baseline.sql +2 -2
- package/package.json +96 -95
- package/scripts/cli/admin.ts +125 -0
- package/scripts/cli/autostart.ts +237 -0
- package/scripts/cli/{ui/background.ts → background.ts} +1 -1
- package/scripts/cli/{command/console-menu.ts → console-menu.ts} +275 -248
- package/scripts/cli/doctor.ts +37 -0
- package/scripts/cli/{support/env.ts → env.ts} +99 -99
- package/scripts/cli/{gateway/spawn.ts → gateway.ts} +112 -24
- package/scripts/cli/instance.ts +224 -0
- package/scripts/cli/main.ts +88 -18
- package/scripts/cli/{support/platform.ts → platform.ts} +64 -64
- package/scripts/cli/port.ts +100 -0
- package/scripts/cli/{support/services.ts → services.ts} +88 -88
- package/scripts/cli/{command/setup.ts → setup.ts} +35 -34
- package/scripts/cli/{command/start.ts → start.ts} +10 -5
- package/scripts/cli/{support/startup-signal.ts → startup-signal.ts} +23 -23
- package/scripts/cli/tray/index.ts +62 -12
- package/scripts/cli/{command/tray.ts → tray.ts} +208 -208
- package/scripts/cli/update.ts +40 -0
- package/scripts/cli/version.ts +67 -0
- package/scripts/cli/{ui/wordmark.ts → wordmark.ts} +58 -58
- package/src/boot/app.ts +11 -7
- package/src/boot/dependencies.ts +50 -19
- package/src/boot/timeout.ts +2 -13
- package/src/config/cooldowns.ts +186 -138
- package/src/config/network.ts +8 -14
- package/src/config/providers.ts +7 -10
- package/src/config/runtime.ts +76 -76
- package/src/config/security.ts +15 -0
- package/src/config/spec.ts +52 -7
- package/src/config/telemetry.ts +26 -0
- package/src/config/timeouts.ts +0 -6
- package/src/console/admin/model-abuse/contracts.ts +28 -6
- package/src/console/admin/model-abuse/routes.ts +56 -3
- package/src/console/api-keys/bucket-spend.ts +15 -4
- package/src/console/api-keys/contracts.ts +410 -414
- package/src/console/api-keys/routes.ts +881 -880
- package/src/console/auth/credentials.ts +3 -3
- package/src/console/auth/setup-service.ts +2 -2
- package/src/console/backup/contracts.ts +15 -2
- package/src/console/backup/export-endpoint.ts +6 -2
- package/src/console/backup/nine-router.ts +2 -5
- package/src/console/backup/service.ts +4 -0
- package/src/console/backup/store.ts +192 -29
- package/src/console/backup/validate.ts +3 -45
- package/src/console/cli-tools/fs-ops.ts +487 -487
- package/src/console/console-router-guards.ts +1 -1
- package/src/console/console-router.ts +8 -0
- package/src/console/data-management/contracts.ts +100 -0
- package/src/console/data-management/operations.ts +234 -0
- package/src/console/data-management/routes.ts +103 -0
- package/src/console/domain-registration.ts +45 -1
- package/src/console/observability/contracts.ts +524 -485
- package/src/console/observability/live.ts +22 -0
- package/src/console/observability/store.ts +1138 -1018
- package/src/console/observability/usage-dimensions.ts +4 -4
- package/src/console/providers/catalog/catalog-projections.ts +6 -7
- package/src/console/providers/catalog/contracts.ts +6 -0
- package/src/console/providers/catalog/model-operations.ts +5 -2
- package/src/console/providers/catalog/provider-operations.ts +24 -4
- package/src/console/providers/catalog/public-model-store.ts +8 -11
- package/src/console/providers/catalog/routes.ts +7 -0
- package/src/console/providers/catalog/store.ts +1553 -1532
- package/src/console/providers/oauth/routes.ts +8 -12
- package/src/console/quota/account-quota-shared.ts +52 -39
- package/src/console/quota/account-quota-tenant.ts +6 -6
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/quota/refresh.ts +441 -429
- package/src/console/remote-support/contracts.ts +61 -0
- package/src/console/remote-support/routes.ts +65 -0
- package/src/console/remote-support/service.ts +207 -0
- package/src/console/response-compression.ts +0 -3
- package/src/console/routing/pools/import-parse.ts +206 -0
- package/src/console/routing/pools/operations.ts +543 -501
- package/src/console/routing/pools/probe-result.ts +3 -2
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/routes.ts +181 -164
- package/src/console/routing/pools/store.ts +8 -8
- package/src/console/settings/contracts.ts +95 -2
- package/src/console/settings/store.ts +81 -3
- package/src/console/shared/cursor.ts +8 -8
- package/src/console/shared/errors.ts +1 -3
- package/src/console/updates/routes.ts +16 -0
- package/src/main.ts +10 -0
- package/src/network/http2-bidi.ts +2 -4
- package/src/network/pool/agent.ts +478 -478
- package/src/network/pool/selector.ts +5 -4
- package/src/network/retry.ts +1 -2
- package/src/network/ssrf.ts +5 -4
- package/src/observability/event-compaction.ts +317 -0
- package/src/observability/event-day-index.ts +440 -0
- package/src/observability/event-log.ts +155 -6
- package/src/observability/logger.ts +6 -6
- package/src/observability/payload-capture.ts +158 -33
- package/src/observability/payload-store.ts +82 -61
- package/src/observability/redaction.ts +5 -6
- package/src/observability/request-notify.ts +43 -0
- package/src/observability/telemetry-status.ts +8 -9
- package/src/observability/token-speed.ts +5 -4
- package/src/persistence/db-mode.ts +7 -7
- package/src/persistence/health-event-flush.ts +36 -0
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/schema.ts +75 -29
- package/src/persistence/shared-cache.ts +1 -2
- package/src/persistence/stores/api-key-store.ts +18 -6
- package/src/persistence/stores/share-store.ts +4 -3
- package/src/persistence/stores/telemetry-store.ts +33 -11
- package/src/protocol/canonical-model.ts +11 -3
- package/src/protocol/codecs.ts +139 -6
- package/src/protocol/gateway-error.ts +6 -4
- package/src/protocol/image-clamp.ts +19 -1
- package/src/protocol/registry.ts +8 -0
- package/src/protocol/request/chat.ts +21 -1
- package/src/protocol/request/codex.ts +10 -1
- package/src/protocol/request/gemini.ts +9 -2
- package/src/protocol/request/messages.ts +18 -7
- package/src/protocol/request/responses.ts +9 -1
- package/src/protocol/response/chat.ts +34 -4
- package/src/protocol/response/codex.ts +7 -4
- package/src/protocol/response/dsml.ts +140 -28
- package/src/protocol/response/responses.ts +11 -5
- package/src/protocol/stream-error-frames.ts +4 -5
- package/src/providers/adapters/compatible-adapter.ts +3 -0
- package/src/providers/authentication/jwt-validator.ts +9 -10
- package/src/providers/authentication/oauth-refresh-service.ts +9 -11
- package/src/providers/bundled-discovery-providers.ts +122 -119
- package/src/providers/configured-openai-providers.ts +4 -4
- package/src/providers/default-registry.ts +5 -4
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/catalog/catalog-format.ts +3 -6
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/openai-model-discovery.ts +5 -0
- package/src/providers/discovery/openrouter-free-roster.ts +15 -17
- package/src/providers/discovery/probe-phases.ts +4 -4
- package/src/providers/discovery/probe-wire.ts +6 -13
- package/src/providers/discovery/probing-service.ts +1117 -1045
- package/src/providers/integrations/alibabacp/alibabacp.ts +1 -1
- package/src/providers/integrations/antigravity/antigravity-quota.ts +5 -7
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +23 -2
- package/src/providers/integrations/buddy/buddy-checkin.ts +1 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +23 -6
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -1
- package/src/providers/integrations/buddy/buddy-identity.ts +52 -0
- package/src/providers/integrations/buddy/buddy-media.ts +2 -2
- package/src/providers/integrations/buddy/buddy-usage-store.ts +3 -4
- package/src/providers/integrations/buddy/codebuddy/quota.ts +2 -13
- package/src/providers/integrations/buddy/codebuddy/shared.ts +118 -115
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -11
- package/src/providers/integrations/buddy/workbuddy/shared.ts +5 -2
- package/src/providers/integrations/claude/claude.ts +608 -608
- package/src/providers/integrations/cline/cline-quota.ts +27 -20
- package/src/providers/integrations/cline/cline.ts +537 -539
- package/src/providers/integrations/codex/codex-headers.ts +25 -39
- package/src/providers/integrations/codex/codex-oauth.ts +1 -1
- package/src/providers/integrations/codex/codex.ts +2 -29
- package/src/providers/integrations/commandcode/commandcode.ts +674 -675
- package/src/providers/integrations/commandcode/discovery.ts +117 -116
- package/src/providers/integrations/cursor/cursor-auth.ts +418 -418
- package/src/providers/integrations/cursor/cursor-quota.ts +309 -0
- package/src/providers/integrations/cursor/cursor-request.ts +616 -471
- package/src/providers/integrations/cursor/cursor-session-manager.ts +319 -281
- package/src/providers/integrations/cursor/cursor.ts +1333 -960
- package/src/providers/integrations/cursor/protocol/messages.ts +25 -0
- package/src/providers/integrations/factory/factory-models.ts +3 -5
- package/src/providers/integrations/factory/factory.ts +8 -1
- package/src/providers/integrations/gemini.ts +6 -1
- package/src/providers/integrations/grok/grok-install-id.ts +2 -3
- package/src/providers/integrations/grok/grok-quota.ts +279 -177
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -0
- package/src/providers/integrations/grok/grok-ratelimit.ts +218 -0
- package/src/providers/integrations/grok/grok.ts +6 -7
- package/src/providers/integrations/hermes.ts +14 -18
- package/src/providers/integrations/kiro/kiro-discovery.ts +1 -1
- package/src/providers/integrations/kiro/kiro.ts +1 -1
- package/src/providers/integrations/mistral.ts +6 -6
- package/src/providers/integrations/opencode/fingerprint.ts +3 -3
- package/src/providers/integrations/opencode/provider.ts +19 -20
- package/src/providers/integrations/qoder/qoder-catalog.ts +450 -450
- package/src/providers/integrations/qoder/qoder-oauth.ts +388 -310
- package/src/providers/integrations/qoder/qoder-quota.ts +192 -0
- package/src/providers/integrations/qoder/qoder.ts +884 -865
- package/src/providers/integrations/shared/connect.ts +6 -6
- package/src/providers/integrations/trae/trae-catalog.ts +1 -2
- package/src/providers/integrations/trae/trae-quota.ts +384 -0
- package/src/providers/integrations/trae/trae.ts +2 -2
- package/src/providers/integrations/zcode/oauth.ts +6 -1
- package/src/providers/operations/account-health-service.ts +75 -27
- package/src/providers/operations/account-reset-service.ts +1 -1
- package/src/providers/operations/client-versions.ts +6 -6
- package/src/providers/operations/custom-cli-headers.ts +3 -4
- package/src/providers/operations/provider-catalog-seeder.ts +95 -95
- package/src/providers/operations/provider-catalog-service.ts +3 -4
- package/src/providers/provider-contracts.ts +7 -0
- package/src/providers/provider-metadata.ts +10 -8
- package/src/providers/provider-registry.ts +6 -7
- package/src/providers/provider-usage.ts +1 -1
- package/src/providers/quota/quota-support.ts +8 -0
- package/src/security/abuse.ts +15 -10
- package/src/security/access-control.ts +144 -146
- package/src/security/admission/contracts.ts +11 -0
- package/src/security/admission/in-memory-store.ts +4 -3
- package/src/security/admission/lease-sweep.ts +6 -6
- package/src/security/admission/reasons.ts +5 -1
- package/src/security/admission/redis-store.ts +211 -210
- package/src/security/admission/service.ts +4 -0
- package/src/security/api-key-auth.ts +27 -17
- package/src/security/ip-boundary.ts +155 -15
- package/src/security/model-abuse.ts +366 -94
- package/src/security/outbound-headers.ts +3 -4
- package/src/shared/sleep.ts +4 -0
- package/src/transport/dispatch/attempt-finalize.ts +47 -5
- package/src/transport/dispatch/attempt-loop.ts +531 -530
- package/src/transport/dispatch/generation.ts +6 -0
- package/src/transport/dispatch/proxy-request.ts +24 -1
- package/src/transport/dispatch/ratelimit-capture.ts +37 -0
- package/src/transport/dispatch/stream-bridge.ts +1 -3
- package/src/transport/dispatch/streaming/finalize.ts +1 -1
- package/src/transport/dispatch/streaming/stream.ts +19 -6
- package/src/transport/dispatch/upstream.ts +9 -0
- package/src/transport/failure-policy.ts +162 -8
- package/src/transport/guards/auth-guard.ts +7 -4
- package/src/transport/guards/concurrency-guard.ts +0 -5
- package/src/transport/guards/error-lifecycle.ts +13 -6
- package/src/transport/guards/pipeline.ts +15 -3
- package/src/transport/guards/request-context.ts +8 -2
- package/src/transport/request/inflight.ts +5 -6
- package/src/transport/request/preparer.ts +9 -8
- package/src/transport/request/state.ts +21 -8
- package/src/transport/request/tenant-preferences.ts +60 -0
- package/src/transport/routing/admission.ts +4 -0
- package/src/transport/routing/route-catalog.ts +65 -10
- package/src/transport/routing/route-model.ts +46 -14
- package/src/transport/routing/router.ts +21 -14
- package/src/transport/surface/completion.ts +2 -3
- package/src/transport/surface/dialects.ts +5 -6
- package/src/transport/surface/responses/encode.ts +38 -0
- package/src/transport/surface/responses/parse.ts +4 -0
- package/src/transport/translation/capabilities.ts +2 -3
- package/src/transport/translation/quirks.ts +3 -3
- package/src/workers/buddy-growth-tasks.ts +5 -10
- package/src/workers/daily-checkin.ts +17 -23
- package/src/workers/quota-refresh-sweep.ts +26 -26
- package/dist/dashboard/assets/BannedPage-hfCzi-Ux.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DEDyKBlC.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CfuYiQ8Z.js +0 -1
- package/dist/dashboard/assets/CombosPage-CfuYiQ8Z.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-bsLuZmYH.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-_UWhwziJ.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DDVIfZGW.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js +0 -1
- package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B53YYMng.js +0 -1
- package/dist/dashboard/assets/OverviewPage-B53YYMng.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js +0 -7
- package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BpOQCnun.js +0 -1
- package/dist/dashboard/assets/QuotaPage-BpOQCnun.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js +0 -1
- package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-D6pmQ86L.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DWvKiuSl.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-CmkUrgdj.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B47uOydr.js +0 -56
- package/dist/dashboard/assets/UsagePage-B47uOydr.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-uNywYlfv.js +0 -1
- package/dist/dashboard/assets/api-keys-uNywYlfv.js.gz +0 -0
- package/dist/dashboard/assets/app-DxivpfYG.css +0 -2
- package/dist/dashboard/assets/app-DxivpfYG.css.gz +0 -0
- package/dist/dashboard/assets/assert-NHwYq95t.js.gz +0 -0
- package/dist/dashboard/assets/badge-6uzDDvqs.js.gz +0 -0
- package/dist/dashboard/assets/banner-4_WNg2WZ.js.gz +0 -0
- package/dist/dashboard/assets/card--AtFedAq.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-B3e7dx3N.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-NreUx6c8.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BtpsJU98.js +0 -1
- package/dist/dashboard/assets/dialog-BtpsJU98.js.gz +0 -0
- package/dist/dashboard/assets/download-A0SW9OCW.js +0 -1
- package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
- package/dist/dashboard/assets/drawer-WwwtzihF.js +0 -1
- package/dist/dashboard/assets/drawer-WwwtzihF.js.gz +0 -0
- package/dist/dashboard/assets/entry-CkryrIea.js +0 -4
- package/dist/dashboard/assets/entry-CkryrIea.js.gz +0 -0
- package/dist/dashboard/assets/entry-TJIye_jT.js +0 -1
- package/dist/dashboard/assets/entry-TJIye_jT.js.gz +0 -0
- package/dist/dashboard/assets/entry-jy9_4-Ke.js +0 -1
- package/dist/dashboard/assets/entry-jy9_4-Ke.js.gz +0 -0
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-D_eM3IgG.js.gz +0 -0
- package/dist/dashboard/assets/index-BydyKaDz.js.gz +0 -0
- package/dist/dashboard/assets/inline-BcNGF8Gr.js.gz +0 -0
- package/dist/dashboard/assets/input-BG41et9G.js.gz +0 -0
- package/dist/dashboard/assets/layout-C8mer_Ak.js +0 -1
- package/dist/dashboard/assets/layout-C8mer_Ak.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Gh_W4OR_.js.gz +0 -0
- package/dist/dashboard/assets/live-BJGYRBnR.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-DeVLDA7I.js +0 -1
- package/dist/dashboard/assets/model-bans-DeVLDA7I.js.gz +0 -0
- package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
- package/dist/dashboard/assets/numbers-Bs4LOOuf.js +0 -2
- package/dist/dashboard/assets/numbers-Bs4LOOuf.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-D7wX2vM5.js.gz +0 -0
- package/dist/dashboard/assets/routing-CcusqXA7.js.gz +0 -0
- package/dist/dashboard/assets/select-BO6h8yNv.js.gz +0 -0
- package/dist/dashboard/assets/settings-mpmXTgUa.js.gz +0 -0
- package/dist/dashboard/assets/sparkles-BRaRUxAU.js +0 -1
- package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
- package/dist/dashboard/assets/stack-DwLmjUFK.js.gz +0 -0
- package/dist/dashboard/assets/state-DYdQmdAl.js.gz +0 -0
- package/dist/dashboard/assets/switch-CidSfbG7.js +0 -1
- package/dist/dashboard/assets/switch-CidSfbG7.js.gz +0 -0
- package/dist/dashboard/assets/terminal-Bd4ee8TA.js +0 -1
- package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
- package/dist/dashboard/assets/tokens-DDxMbSRL.js +0 -1
- package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js +0 -1
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-w4u4CVue.js.gz +0 -0
- package/scripts/cli/command/admin.ts +0 -65
- package/scripts/cli/command/doctor.ts +0 -257
- package/scripts/cli/command/update.ts +0 -23
- package/scripts/cli/gateway/drain-token.ts +0 -78
- package/scripts/cli/gateway/instance-lock.ts +0 -105
- package/scripts/cli/gateway/stop.ts +0 -51
- package/scripts/cli/support/bun.ts +0 -27
- package/scripts/cli/support/version.ts +0 -37
- /package/dist/dashboard/assets/{app-CafWtVXA.js → app-BIt446yE.js} +0 -0
- /package/dist/dashboard/assets/{app-CafWtVXA.js.gz → app-BIt446yE.js.gz} +0 -0
- /package/scripts/cli/{ui/prompt.ts → prompt.ts} +0 -0
|
@@ -166,8 +166,8 @@ export class DrizzleNetworkPoolStore implements NetworkPoolStore {
|
|
|
166
166
|
...(row.lastSpeedtestAt ? { lastSpeedtestAt: row.lastSpeedtestAt.toISOString() } : {}),
|
|
167
167
|
...(() => {
|
|
168
168
|
// A verdict describes the address recorded beside it. Once the pool is
|
|
169
|
-
// re-pointed, that address is gone and the verdict is a claim about
|
|
170
|
-
//
|
|
169
|
+
// re-pointed, that address is gone and the verdict is a claim about an
|
|
170
|
+
// address the pool no longer uses — report "never probed" instead of
|
|
171
171
|
// showing a colour the operator cannot trust.
|
|
172
172
|
const probe = storedProviderProbe(row, endpoint ?? "");
|
|
173
173
|
return probe === undefined ? {} : { providerProbe: probe };
|
|
@@ -350,10 +350,10 @@ export class DrizzleNetworkPoolStore implements NetworkPoolStore {
|
|
|
350
350
|
/**
|
|
351
351
|
* Banks a probe verdict on the pool row.
|
|
352
352
|
*
|
|
353
|
-
* The verdict
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
353
|
+
* The verdict is stored here rather than per-viewer so every reader agrees:
|
|
354
|
+
* another machine, the gateway, and the operator alike. Otherwise admission
|
|
355
|
+
* keeps choosing the pool the badge calls blocked. Storing it here is what
|
|
356
|
+
* makes the badge a fact about the pool rather than about one tab.
|
|
357
357
|
*
|
|
358
358
|
* `poolId` is the pool row; `endpoint` is what the verdict was measured
|
|
359
359
|
* against, so a later edit invalidates it on read. A probe against an unsaved
|
|
@@ -458,8 +458,8 @@ export class DrizzleNetworkPoolStore implements NetworkPoolStore {
|
|
|
458
458
|
.set({
|
|
459
459
|
endpointConfig,
|
|
460
460
|
kind: effectiveKind === "https" ? "http" : effectiveKind,
|
|
461
|
-
// Fields default to `undefined` when omitted from the patch
|
|
462
|
-
//
|
|
461
|
+
// Fields default to `undefined` when omitted from the patch, which
|
|
462
|
+
// would overwrite the current row with `undefined` and clobber
|
|
463
463
|
// existing values. Fall back to the current record so a partial
|
|
464
464
|
// PATCH truly leaves un-mentioned fields alone.
|
|
465
465
|
maxInflight: patch.maxInflight ?? current.maxInflight,
|
|
@@ -7,6 +7,10 @@ import { t } from "elysia";
|
|
|
7
7
|
import type { AccessDecision } from "../../security/access-control";
|
|
8
8
|
import type { RedisBackend } from "../../persistence/redis";
|
|
9
9
|
import type { ConsoleSettingsPreferences } from "../../persistence/schema";
|
|
10
|
+
import { RTK_LEVELS } from "../../transport/request/rtk/rtk-constants";
|
|
11
|
+
import { PONYTAIL_LEVELS } from "../../transport/request/rtk/ponytail-prompt";
|
|
12
|
+
|
|
13
|
+
export { RTK_LEVELS, PONYTAIL_LEVELS };
|
|
10
14
|
|
|
11
15
|
export interface RuntimeSettingsResponse {
|
|
12
16
|
readonly redisBackendActual: RedisBackend;
|
|
@@ -17,6 +21,8 @@ export interface RuntimeSettingsResponse {
|
|
|
17
21
|
readonly telemetryPayloads: TelemetryPayloadMode;
|
|
18
22
|
/** Capture depth for `full` mode; `minimum` when unset. */
|
|
19
23
|
readonly telemetryPayloadDepth: TelemetryPayloadDepth;
|
|
24
|
+
/** How long a captured frame is kept, in minutes; `null` means deployment default. */
|
|
25
|
+
readonly telemetryPayloadRetentionMinutes: number | null;
|
|
20
26
|
readonly privacyMode: PrivacyMode;
|
|
21
27
|
/** Compacts bulky tool-result text before dispatch (RTK prune). Default false. */
|
|
22
28
|
readonly rtkPruneEnabled: boolean;
|
|
@@ -26,6 +32,19 @@ export interface RuntimeSettingsResponse {
|
|
|
26
32
|
readonly ponyTailEnabled: boolean;
|
|
27
33
|
/** PonyTail directive intensity; `full` by default. */
|
|
28
34
|
readonly ponyTailLevel: PonyTailLevel;
|
|
35
|
+
/**
|
|
36
|
+
* Ignores an account-wide cooldown when building route candidates. Off by
|
|
37
|
+
* default: dialing a cooled-down account reproduces the refusal that parked
|
|
38
|
+
* it, and can exhaust the candidate list. Overrides nothing else — the
|
|
39
|
+
* cooldown is still recorded and still recovered by the sweep.
|
|
40
|
+
*/
|
|
41
|
+
readonly forceDisableCooldown: boolean;
|
|
42
|
+
/** Per-tenant upstream request timeout in seconds; null means deployment default. */
|
|
43
|
+
readonly upstreamTimeoutSeconds: number | null;
|
|
44
|
+
/** Per-tenant telemetry compaction age in days; null means deployment default. */
|
|
45
|
+
readonly telemetryWindowMinutes: number | null;
|
|
46
|
+
/** Per-tenant inline attachment ceiling in bytes; null means deployment default. */
|
|
47
|
+
readonly imageClampMaxBytes: number | null;
|
|
29
48
|
readonly updatedAt: string;
|
|
30
49
|
}
|
|
31
50
|
|
|
@@ -50,6 +69,17 @@ export interface RuntimeSettingsConfig {
|
|
|
50
69
|
readonly store: RuntimeSettingsStore;
|
|
51
70
|
readonly accessResolver: ConsoleAccessResolver;
|
|
52
71
|
readonly auditSink?: AuditSink;
|
|
72
|
+
/**
|
|
73
|
+
* Structurally satisfied by `InMemoryRouteSnapshotService`.
|
|
74
|
+
*
|
|
75
|
+
* Two preferences here are read on the request path, not per request: the
|
|
76
|
+
* route snapshot reads `forceDisableCooldown` when it builds candidates, and
|
|
77
|
+
* the admission path reads `tenantConcurrencyLimit`. The snapshot is cached
|
|
78
|
+
* until something invalidates it, so without this a saved setting stays
|
|
79
|
+
* invisible to `/v1/*` until some unrelated console write happens to
|
|
80
|
+
* invalidate — the toggle looks broken while the row is already correct.
|
|
81
|
+
*/
|
|
82
|
+
readonly snapshotInvalidator?: { invalidate(): unknown };
|
|
53
83
|
}
|
|
54
84
|
|
|
55
85
|
|
|
@@ -62,13 +92,25 @@ export type TelemetryPayloadMode = (typeof TELEMETRY_PAYLOAD_MODES)[number];
|
|
|
62
92
|
export const TELEMETRY_PAYLOAD_DEPTHS = ["minimum", "moderate", "maximum"] as const;
|
|
63
93
|
/** Capture depth for `full` mode accepted by the runtime-settings PATCH. */
|
|
64
94
|
export type TelemetryPayloadDepth = (typeof TELEMETRY_PAYLOAD_DEPTHS)[number];
|
|
95
|
+
/**
|
|
96
|
+
* How long a captured payload frame is kept, in whole minutes, or `null` for
|
|
97
|
+
* the deployment default.
|
|
98
|
+
*
|
|
99
|
+
* Only the offered values are accepted, and a value off the list is *rejected*
|
|
100
|
+
* rather than clamped: clamping would answer a request for thirty days with
|
|
101
|
+
* seven and report success, and an operator who asked for the longer window
|
|
102
|
+
* would believe they got it.
|
|
103
|
+
*/
|
|
104
|
+
export const TELEMETRY_PAYLOAD_RETENTION_MINUTES = [15, 30, 60, 360, 720, 1440, 10080] as const;
|
|
105
|
+
/** Payload retention in minutes accepted by the runtime-settings PATCH. */
|
|
106
|
+
export type TelemetryPayloadRetentionMinutes =
|
|
107
|
+
| (typeof TELEMETRY_PAYLOAD_RETENTION_MINUTES)[number]
|
|
108
|
+
| null;
|
|
65
109
|
export const PRIVACY_MODES = ["masked", "full"] as const;
|
|
66
110
|
/** Provider/model label privacy accepted by the runtime-settings PATCH. */
|
|
67
111
|
export type PrivacyMode = (typeof PRIVACY_MODES)[number];
|
|
68
|
-
export const PONYTAIL_LEVELS = ["lite", "full", "ultra"] as const;
|
|
69
112
|
/** PonyTail directive intensity accepted by the runtime-settings PATCH. */
|
|
70
113
|
export type PonyTailLevel = (typeof PONYTAIL_LEVELS)[number];
|
|
71
|
-
export const RTK_LEVELS = ["lite", "full", "ultra"] as const;
|
|
72
114
|
/** RTK prune strength accepted by the runtime-settings PATCH. */
|
|
73
115
|
export type RtkLevel = (typeof RTK_LEVELS)[number];
|
|
74
116
|
|
|
@@ -107,6 +149,19 @@ export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
|
|
|
107
149
|
if (patch.telemetryPayloadDepth !== undefined && !TELEMETRY_PAYLOAD_DEPTHS.includes(patch.telemetryPayloadDepth)) {
|
|
108
150
|
throw new ConsoleDomainError("invalid_request", 400, "Invalid telemetryPayloadDepth");
|
|
109
151
|
}
|
|
152
|
+
if (patch.telemetryPayloadRetentionMinutes !== undefined) {
|
|
153
|
+
const minutes = patch.telemetryPayloadRetentionMinutes;
|
|
154
|
+
if (
|
|
155
|
+
minutes !== null &&
|
|
156
|
+
!(TELEMETRY_PAYLOAD_RETENTION_MINUTES as readonly number[]).includes(minutes)
|
|
157
|
+
) {
|
|
158
|
+
throw new ConsoleDomainError(
|
|
159
|
+
"invalid_request",
|
|
160
|
+
400,
|
|
161
|
+
"Invalid telemetryPayloadRetentionMinutes",
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
110
165
|
if (patch.privacyMode !== undefined && !PRIVACY_MODES.includes(patch.privacyMode)) {
|
|
111
166
|
throw new ConsoleDomainError("invalid_request", 400, "Invalid privacyMode");
|
|
112
167
|
}
|
|
@@ -127,6 +182,14 @@ export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
|
|
|
127
182
|
target: a.tenantId,
|
|
128
183
|
detail: patch as Record<string, unknown>,
|
|
129
184
|
});
|
|
185
|
+
// Saved preferences are routing changes: `forceDisableCooldown` decides
|
|
186
|
+
// candidate eligibility and `tenantConcurrencyLimit` decides admission,
|
|
187
|
+
// and both are read from the cached route snapshot. Invalidate after the
|
|
188
|
+
// write commits so the next `/v1/*` request uses them instead of the
|
|
189
|
+
// revision that was built before the save. A failure here must not fail
|
|
190
|
+
// the save — the row is already correct, and the snapshot rebuilds on
|
|
191
|
+
// the next read anyway.
|
|
192
|
+
await Promise.resolve(deps.snapshotInvalidator?.invalidate()).catch(() => undefined);
|
|
130
193
|
return updated;
|
|
131
194
|
},
|
|
132
195
|
};
|
|
@@ -143,6 +206,12 @@ export const runtimeUpdateBody = t.Object({
|
|
|
143
206
|
responsesReasoningSummary: t.Optional(literalUnion(RESPONSES_REASONING_SUMMARIES)),
|
|
144
207
|
telemetryPayloads: t.Optional(literalUnion(TELEMETRY_PAYLOAD_MODES)),
|
|
145
208
|
telemetryPayloadDepth: t.Optional(literalUnion(TELEMETRY_PAYLOAD_DEPTHS)),
|
|
209
|
+
telemetryPayloadRetentionMinutes: t.Optional(
|
|
210
|
+
t.Union([
|
|
211
|
+
t.Null(),
|
|
212
|
+
t.Union(TELEMETRY_PAYLOAD_RETENTION_MINUTES.map((minutes) => t.Literal(minutes))),
|
|
213
|
+
]),
|
|
214
|
+
),
|
|
146
215
|
privacyMode: t.Optional(literalUnion(PRIVACY_MODES)),
|
|
147
216
|
tenantConcurrencyLimit: t.Optional(t.Union([t.Null(), t.Number()])),
|
|
148
217
|
thinkingNormalizationEnabled: t.Optional(t.Boolean()),
|
|
@@ -150,4 +219,28 @@ export const runtimeUpdateBody = t.Object({
|
|
|
150
219
|
rtkPruneLevel: t.Optional(literalUnion(RTK_LEVELS)),
|
|
151
220
|
ponyTailEnabled: t.Optional(t.Boolean()),
|
|
152
221
|
ponyTailLevel: t.Optional(literalUnion(PONYTAIL_LEVELS)),
|
|
222
|
+
/*
|
|
223
|
+
* Operational overrides. Every field a caller may patch must appear here:
|
|
224
|
+
* this schema is what the route parses the body against, and a key it does
|
|
225
|
+
* not name is dropped before the store sees it. These four were added to the
|
|
226
|
+
* request type and the store but never to this object, so a save returned
|
|
227
|
+
* 200 and changed nothing — the mutation's toast fired while the stored
|
|
228
|
+
* value stayed put.
|
|
229
|
+
*
|
|
230
|
+
* `t.Null()` is what "Deployment default" sends, and it must survive:
|
|
231
|
+
* clearing an override is a real edit, not an absent one.
|
|
232
|
+
*
|
|
233
|
+
* The bounds mirror the store's own normalizers, so an out-of-range value is
|
|
234
|
+
* rejected here rather than silently clamped on the way in.
|
|
235
|
+
*/
|
|
236
|
+
forceDisableCooldown: t.Optional(t.Boolean()),
|
|
237
|
+
upstreamTimeoutSeconds: t.Optional(
|
|
238
|
+
t.Union([t.Null(), t.Number({ minimum: 5, maximum: 600 })]),
|
|
239
|
+
),
|
|
240
|
+
telemetryWindowMinutes: t.Optional(
|
|
241
|
+
t.Union([t.Null(), t.Number({ minimum: 1, maximum: 1440 })]),
|
|
242
|
+
),
|
|
243
|
+
imageClampMaxBytes: t.Optional(
|
|
244
|
+
t.Union([t.Null(), t.Number({ minimum: 64 * 1024, maximum: 100 * 1024 * 1024 })]),
|
|
245
|
+
),
|
|
153
246
|
});
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
RTK_LEVELS,
|
|
12
12
|
TELEMETRY_PAYLOAD_DEPTHS,
|
|
13
13
|
TELEMETRY_PAYLOAD_MODES,
|
|
14
|
+
TELEMETRY_PAYLOAD_RETENTION_MINUTES,
|
|
14
15
|
type PonyTailLevel,
|
|
15
16
|
type ResponsesReasoningSummary,
|
|
16
17
|
type RtkLevel,
|
|
@@ -68,6 +69,62 @@ function normalizeTelemetryPayloadDepth(value: unknown): TelemetryPayloadDepth {
|
|
|
68
69
|
return "minimum";
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Bounds a per-tenant numeric override, or `null` for "use the default".
|
|
74
|
+
*
|
|
75
|
+
* Clamped rather than rejected: these are operational knobs an operator tunes
|
|
76
|
+
* live, and a PATCH that throws on an out-of-range value leaves them stuck at
|
|
77
|
+
* whatever was stored. Clamping keeps the value inside the range the request
|
|
78
|
+
* path can actually honour — the same range the deployment env var accepts.
|
|
79
|
+
*
|
|
80
|
+
* `null` (and every non-finite value) means "no override", which is distinct
|
|
81
|
+
* from "override with zero".
|
|
82
|
+
*/
|
|
83
|
+
function normalizeBoundedNumber(value: unknown, min: number, max: number): number | null {
|
|
84
|
+
if (value === null || value === undefined) return null;
|
|
85
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
86
|
+
if (!Number.isFinite(parsed)) return null;
|
|
87
|
+
// A value at or below zero is not a setting, it is a typo: clamping it to
|
|
88
|
+
// the floor would silently give the request a 5-second budget when the
|
|
89
|
+
// operator meant "no override". Non-positive therefore reads as absent.
|
|
90
|
+
if (parsed <= 0) return null;
|
|
91
|
+
return Math.min(max, Math.max(min, Math.floor(parsed)));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Upstream timeout in seconds: 5 s floor, 600 s ceiling, matching the env var. */
|
|
95
|
+
function normalizeUpstreamTimeoutSeconds(value: unknown): number | null {
|
|
96
|
+
return normalizeBoundedNumber(value, 5, 600);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Source window in minutes: 1 floor, 1440 (one day) ceiling, matching the env var. */
|
|
100
|
+
function normalizeTelemetryWindowMinutes(value: unknown): number | null {
|
|
101
|
+
return normalizeBoundedNumber(value, 1, 1440);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Inline attachment ceiling in bytes: 64 KB floor, 100 MB ceiling. */
|
|
105
|
+
function normalizeImageClampMaxBytes(value: unknown): number | null {
|
|
106
|
+
return normalizeBoundedNumber(value, 64 * 1024, 100 * 1024 * 1024);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* How long a captured payload frame is kept, in whole minutes.
|
|
111
|
+
*
|
|
112
|
+
* Only one of the offered values survives; anything else reads as "deployment
|
|
113
|
+
* default" rather than being clamped to the nearest one. A stored value from
|
|
114
|
+
* before this list existed would otherwise be silently rewritten to a window
|
|
115
|
+
* the operator never chose, and dropping it to `null` is the same outcome the
|
|
116
|
+
* operator gets by never having set it.
|
|
117
|
+
*/
|
|
118
|
+
function normalizePayloadRetentionMinutes(value: unknown): number | null {
|
|
119
|
+
if (value === null || value === undefined) return null;
|
|
120
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
121
|
+
if (!Number.isFinite(parsed)) return null;
|
|
122
|
+
const minutes = Math.floor(parsed);
|
|
123
|
+
return (TELEMETRY_PAYLOAD_RETENTION_MINUTES as readonly number[]).includes(minutes)
|
|
124
|
+
? minutes
|
|
125
|
+
: null;
|
|
126
|
+
}
|
|
127
|
+
|
|
71
128
|
function mapRuntimeSettingsRow(
|
|
72
129
|
row: typeof consoleSettings.$inferSelect | undefined,
|
|
73
130
|
redisBackend: RedisBackend,
|
|
@@ -83,6 +140,9 @@ function mapRuntimeSettingsRow(
|
|
|
83
140
|
: "detailed",
|
|
84
141
|
telemetryPayloads: normalizeTelemetryPayloadMode(prefs.telemetryPayloads),
|
|
85
142
|
telemetryPayloadDepth: normalizeTelemetryPayloadDepth(prefs.telemetryPayloadDepth),
|
|
143
|
+
telemetryPayloadRetentionMinutes: normalizePayloadRetentionMinutes(
|
|
144
|
+
prefs.telemetryPayloadRetentionMinutes,
|
|
145
|
+
),
|
|
86
146
|
privacyMode: prefs.privacyMode === "full" ? "full" : "masked",
|
|
87
147
|
rtkPruneEnabled: prefs.rtkPruneEnabled === true,
|
|
88
148
|
rtkPruneLevel: normalizeRtkLevel(prefs.rtkPruneLevel),
|
|
@@ -90,11 +150,15 @@ function mapRuntimeSettingsRow(
|
|
|
90
150
|
// RTK above works. `ponyTailLevel` selects the intensity and is kept while
|
|
91
151
|
// the feature is off so a re-enable restores it, but a stored level is NOT
|
|
92
152
|
// consent: the dashboard lets an operator choose a strength with the toggle
|
|
93
|
-
// still off, which writes a level and no flag. Reading that as enabled
|
|
94
|
-
//
|
|
95
|
-
//
|
|
153
|
+
// still off, which writes a level and no flag. Reading that as enabled
|
|
154
|
+
// would report the directive as on while the dispatch gate — which requires
|
|
155
|
+
// the flag — never injects it.
|
|
96
156
|
ponyTailEnabled: prefs.ponyTailEnabled === true,
|
|
97
157
|
ponyTailLevel: normalizePonyTailLevel(prefs.ponyTailLevel),
|
|
158
|
+
forceDisableCooldown: prefs.forceDisableCooldown === true,
|
|
159
|
+
upstreamTimeoutSeconds: normalizeUpstreamTimeoutSeconds(prefs.upstreamTimeoutSeconds),
|
|
160
|
+
telemetryWindowMinutes: normalizeTelemetryWindowMinutes(prefs.telemetryWindowMinutes),
|
|
161
|
+
imageClampMaxBytes: normalizeImageClampMaxBytes(prefs.imageClampMaxBytes),
|
|
98
162
|
updatedAt,
|
|
99
163
|
};
|
|
100
164
|
}
|
|
@@ -128,11 +192,25 @@ export class DrizzleRuntimeSettingsStore implements RuntimeSettingsStore {
|
|
|
128
192
|
patchPrefs.responsesReasoningSummary = patch.responsesReasoningSummary;
|
|
129
193
|
if (patch.telemetryPayloads !== undefined) patchPrefs.telemetryPayloads = patch.telemetryPayloads;
|
|
130
194
|
if (patch.telemetryPayloadDepth !== undefined) patchPrefs.telemetryPayloadDepth = patch.telemetryPayloadDepth;
|
|
195
|
+
if (patch.telemetryPayloadRetentionMinutes !== undefined)
|
|
196
|
+
patchPrefs.telemetryPayloadRetentionMinutes = normalizePayloadRetentionMinutes(
|
|
197
|
+
patch.telemetryPayloadRetentionMinutes,
|
|
198
|
+
);
|
|
131
199
|
if (patch.privacyMode !== undefined) patchPrefs.privacyMode = patch.privacyMode;
|
|
132
200
|
if (patch.rtkPruneEnabled !== undefined) patchPrefs.rtkPruneEnabled = patch.rtkPruneEnabled;
|
|
133
201
|
if (patch.rtkPruneLevel !== undefined) patchPrefs.rtkPruneLevel = patch.rtkPruneLevel;
|
|
134
202
|
if (patch.ponyTailEnabled !== undefined) patchPrefs.ponyTailEnabled = patch.ponyTailEnabled;
|
|
135
203
|
if (patch.ponyTailLevel !== undefined) patchPrefs.ponyTailLevel = patch.ponyTailLevel;
|
|
204
|
+
if (patch.forceDisableCooldown !== undefined)
|
|
205
|
+
patchPrefs.forceDisableCooldown = patch.forceDisableCooldown === true;
|
|
206
|
+
if (patch.upstreamTimeoutSeconds !== undefined)
|
|
207
|
+
patchPrefs.upstreamTimeoutSeconds = normalizeUpstreamTimeoutSeconds(patch.upstreamTimeoutSeconds);
|
|
208
|
+
if (patch.telemetryWindowMinutes !== undefined)
|
|
209
|
+
patchPrefs.telemetryWindowMinutes = normalizeTelemetryWindowMinutes(
|
|
210
|
+
patch.telemetryWindowMinutes,
|
|
211
|
+
);
|
|
212
|
+
if (patch.imageClampMaxBytes !== undefined)
|
|
213
|
+
patchPrefs.imageClampMaxBytes = normalizeImageClampMaxBytes(patch.imageClampMaxBytes);
|
|
136
214
|
|
|
137
215
|
const rows = await this.db
|
|
138
216
|
.insert(consoleSettings)
|
|
@@ -13,8 +13,8 @@ const decodeCache = new Map<string, unknown>();
|
|
|
13
13
|
* Maximum nesting depth a decoded cursor may have.
|
|
14
14
|
*
|
|
15
15
|
* `JSON.parse` accepts arbitrary nesting (it is iterative in this runtime), but
|
|
16
|
-
* every traversal of the result is not: `
|
|
17
|
-
* level
|
|
16
|
+
* every traversal of the result is not: `structuredClone` recurses once per
|
|
17
|
+
* level. A payload past the stack limit
|
|
18
18
|
* therefore threw `RangeError: Maximum call stack size exceeded` out of a function
|
|
19
19
|
* that documents returning `undefined` for a malformed value — failing OPEN into a
|
|
20
20
|
* 500 where the contract is "fall back to the first page", and leaving a partially
|
|
@@ -116,8 +116,8 @@ function copyDecoded<T>(value: unknown): T {
|
|
|
116
116
|
// `structuredClone` recurses, so a value past its own budget throws. The
|
|
117
117
|
// depth limit makes this unreachable for anything `decodeCursor` accepts, but
|
|
118
118
|
// the documented contract is `undefined` for a value that cannot be decoded,
|
|
119
|
-
// and
|
|
120
|
-
//
|
|
119
|
+
// and the documented contract is `undefined` for a value that cannot be
|
|
120
|
+
// decoded.
|
|
121
121
|
return undefined as T;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -133,10 +133,10 @@ export interface DatedCursor {
|
|
|
133
133
|
/**
|
|
134
134
|
* Decodes a `(createdAt, id)` keyset cursor, rejecting anything malformed.
|
|
135
135
|
*
|
|
136
|
-
* Both the audit log and the telemetry event listing paginate on the same pair
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
136
|
+
* Both the audit log and the telemetry event listing paginate on the same pair,
|
|
137
|
+
* so the shape check plus parseable-timestamp validation lives here and the two
|
|
138
|
+
* listings cannot drift: a cursor the audit store accepts must be one the stats
|
|
139
|
+
* store accepts.
|
|
140
140
|
*
|
|
141
141
|
* Deliberately not generic over `T extends DatedCursor`: a caller could then
|
|
142
142
|
* instantiate it with a wider type and read fields that were never validated.
|
|
@@ -141,9 +141,7 @@ export function requireGlobalAdmin(access: AccessDecision | undefined): AccessDe
|
|
|
141
141
|
/**
|
|
142
142
|
* Builds the console's catch-all error handler for an Elysia route group.
|
|
143
143
|
*
|
|
144
|
-
*
|
|
145
|
-
* calling `errorResponse` with a group-specific fallback message — 95 copies,
|
|
146
|
-
* 22 of them in one file. Elysia's global `error(handler)` hook receives any
|
|
144
|
+
* Elysia's global `error(handler)` hook receives any
|
|
147
145
|
* throw from the group (including a `.use()`d child), so one hook per group
|
|
148
146
|
* produces the identical envelope with the identical fallback text.
|
|
149
147
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Elysia } from "elysia";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
2
3
|
import { consoleErrorHandler, requireTenantScope } from "../shared/errors";
|
|
3
4
|
import { consoleSseResponse, createConsoleSseStream } from "../observability/sse";
|
|
4
5
|
import type { ObservabilityStore } from "../observability/contracts";
|
|
@@ -86,6 +87,21 @@ export function createUpdateRoutes(config: {
|
|
|
86
87
|
return new Elysia()
|
|
87
88
|
.error(consoleErrorHandler("Update check failed"))
|
|
88
89
|
.get("/updates/release", async () => await fetchRelease())
|
|
90
|
+
.post("/updates/apply", ({ request }) => {
|
|
91
|
+
requireTenantScope(config.accessResolver(request), "dashboard:write");
|
|
92
|
+
const isWin = process.platform === "win32";
|
|
93
|
+
const shell = isWin ? "cmd.exe" : "/bin/sh";
|
|
94
|
+
const install = "npm install -g cartethyia@latest --prefer-online";
|
|
95
|
+
const restart = isWin
|
|
96
|
+
? "cartethyia start"
|
|
97
|
+
: "systemctl restart cartethyia 2>/dev/null || cartethyia start";
|
|
98
|
+
const script = `${install} && ${restart}`;
|
|
99
|
+
const args = isWin ? ["/c", script] : ["-c", script];
|
|
100
|
+
const child = spawn(shell, args, { detached: true, stdio: "ignore" });
|
|
101
|
+
child.unref();
|
|
102
|
+
setTimeout(() => process.exit(0), 1500);
|
|
103
|
+
return { started: true };
|
|
104
|
+
})
|
|
89
105
|
.get("/notifications/stream", ({ request }) => {
|
|
90
106
|
const tenantId = requireTenantScope(
|
|
91
107
|
config.accessResolver(request),
|
package/src/main.ts
CHANGED
|
@@ -68,6 +68,16 @@ const app = boot
|
|
|
68
68
|
admissionService: boot.deps.admissionService,
|
|
69
69
|
modelStrikes: boot.deps.modelStrikes,
|
|
70
70
|
readRoutingAccountInflight: boot.deps.readRoutingAccountInflight,
|
|
71
|
+
// The same three writers the shutdown coordinator drains, so a manual
|
|
72
|
+
// flush from the console does exactly what a graceful stop would do
|
|
73
|
+
// for the queues, minus the stop.
|
|
74
|
+
flushBuffers: {
|
|
75
|
+
flush: async () => {
|
|
76
|
+
await boot.deps.telemetryBuffer.flush();
|
|
77
|
+
await boot.deps.usageFlush.flush();
|
|
78
|
+
await boot.deps.healthEventFlush.flush();
|
|
79
|
+
},
|
|
80
|
+
},
|
|
71
81
|
},
|
|
72
82
|
})
|
|
73
83
|
: createGatewayShell();
|
|
@@ -73,10 +73,8 @@ export interface Http2BidiOpenOptions {
|
|
|
73
73
|
* The dialer contract a provider adapter is handed.
|
|
74
74
|
*
|
|
75
75
|
* Declared here rather than in the provider layer so the dependency runs one
|
|
76
|
-
* way: `provider-contracts` imports this module, never the reverse
|
|
77
|
-
*
|
|
78
|
-
* other — type-only, so it never deadlocked, but it meant this
|
|
79
|
-
* transport-neutral contract had to know about providers.
|
|
76
|
+
* way: `provider-contracts` imports this module, never the reverse, and this
|
|
77
|
+
* transport-neutral contract stays free of any knowledge of providers.
|
|
80
78
|
*/
|
|
81
79
|
export type ValidatedOutboundHttp2Bidi = (
|
|
82
80
|
url: URL,
|