cartethyia 2.5.4 → 2.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +13 -4
- package/README.md +2 -2
- package/dist/dashboard/assets/{BannedPage-C2orBsI2.js → BannedPage-CADDq4IU.js} +1 -1
- package/dist/dashboard/assets/BannedPage-CADDq4IU.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-_z1hID7E.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BV6pxVQo.js → CliToolsPage-DuO1Z7dq.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DuO1Z7dq.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DNEPi3xU.js +1 -0
- package/dist/dashboard/assets/CombosPage-DNEPi3xU.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CW-wPmJ6.js → ConsoleLogPage-BVdpfiz1.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-BVdpfiz1.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-0p8uX7vB.js → CustomizationPage-BR5HHvbi.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-BR5HHvbi.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-BjqSMfko.js → HealthEventsModal-mUfcknqm.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-mUfcknqm.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-BvzCx8Kr.js → LoginPage-V5faV9V2.js} +1 -1
- package/dist/dashboard/assets/LoginPage-V5faV9V2.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js +1 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BDu20Jdx.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-Dllr_aFO.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-Js9jUPSb.js → ProvidersPage-uPiGVTTK.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-uPiGVTTK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js +7 -0
- package/dist/dashboard/assets/ProxyPage-BaUkO-4O.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js +1 -0
- package/dist/dashboard/assets/QuotaPage-o0IFAsFb.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CYWqbXjp.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-DDbMQsoo.js → SetupPage-UB690SE2.js} +1 -1
- package/dist/dashboard/assets/SetupPage-UB690SE2.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-CB3Z7VTf.js → SortableList-BLujdX7N.js} +1 -1
- package/dist/dashboard/assets/SortableList-BLujdX7N.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-D95UCPHI.js → StudioPage-D5C2raAc.js} +3 -3
- package/dist/dashboard/assets/StudioPage-D5C2raAc.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-8uIcxa1W.js +56 -0
- package/dist/dashboard/assets/UsagePage-8uIcxa1W.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js +1 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +2 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/{assert-Chi7pCnt.js → assert-C9FD_JwJ.js} +1 -1
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D4pQapKj.js → badge-CR72dasT.js} +1 -1
- package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DH6cg-_B.js → banner-CI9_eSfO.js} +1 -1
- package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
- package/dist/dashboard/assets/{card-BKqlUOe4.js → card-DS7CQEsO.js} +1 -1
- package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-fTgljTGA.js → cli-tools-CNqzrfdb.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-D5Ys_sM0.js → clipboard-button-611pSsaX.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
- package/dist/dashboard/assets/{fingerprint-pattern-BWuHtbW4.js → concurrency-BDF0ok7y.js} +1 -1
- package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js +1 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js +1 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CQBr6U9c.js → entry-B9Xvtnfj.js} +1 -1
- package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js +1 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
- package/dist/dashboard/assets/entry-DxBHnauy.js +4 -0
- package/dist/dashboard/assets/entry-DxBHnauy.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-BlptL8Uf.js → github-badge-sltk1qUi.js} +1 -1
- package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
- package/dist/dashboard/assets/{index-js6TJvdr.js → index-CmxZr14F.js} +2 -2
- package/dist/dashboard/assets/index-CmxZr14F.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DUWDSmDV.js → inline-BhY2sSnO.js} +1 -1
- package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
- package/dist/dashboard/assets/{input-LVjgZq4f.js → input-V1umFiBk.js} +1 -1
- package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js +1 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-DupvLfDx.js → list-row-1zzZYM0B.js} +1 -1
- package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
- package/dist/dashboard/assets/{live-C4pEJUqI.js → live-BivGfuUs.js} +1 -1
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-BRiuufkB.js → model-bans-CmffjLuG.js} +1 -1
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-B4ROwk8L.js → mutation-46P0oWMe.js} +1 -1
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js +2 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-gmjzC3RS.js → query-keys-BFhGpwR1.js} +1 -1
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/{routing-C7tBT5o-.js → routing-CAb4F37R.js} +1 -1
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/{select-WUCXorXw.js → select-BYR_5K2c.js} +1 -1
- package/dist/dashboard/assets/select-BYR_5K2c.js.gz +0 -0
- package/dist/dashboard/assets/{settings-C_zKl1GS.js → settings-DNX9sydi.js} +1 -1
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/{stack-_3O0F8Xn.js → stack-BHTfXdz_.js} +1 -1
- package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
- package/dist/dashboard/assets/{state-BgTxHcJS.js → state-mPDRlQkl.js} +1 -1
- package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DEjTtOWf.js → switch-DOmJxg7H.js} +1 -1
- package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
- package/dist/dashboard/assets/tokens-COzario9.js +1 -0
- package/dist/dashboard/assets/tokens-COzario9.js.gz +0 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js +1 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BWSC0UAS.js → useMutation-Dem0lXQj.js} +1 -1
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -54
- package/migrations/README.md +52 -52
- package/migrations/lite-sqlite3/0000_baseline.sql +2 -2
- package/package.json +96 -95
- package/scripts/cli/admin.ts +125 -0
- package/scripts/cli/{ui/background.ts → background.ts} +1 -1
- package/scripts/cli/{command/console-menu.ts → console-menu.ts} +275 -244
- package/scripts/cli/doctor.ts +37 -0
- package/scripts/cli/{support/env.ts → env.ts} +99 -99
- package/scripts/cli/{gateway/spawn.ts → gateway.ts} +112 -24
- package/scripts/cli/instance.ts +224 -0
- package/scripts/cli/main.ts +61 -18
- package/scripts/cli/{support/platform.ts → platform.ts} +64 -64
- package/scripts/cli/port.ts +100 -0
- package/scripts/cli/{support/services.ts → services.ts} +88 -88
- package/scripts/cli/{command/setup.ts → setup.ts} +35 -34
- package/scripts/cli/{command/start.ts → start.ts} +10 -5
- package/scripts/cli/{support/startup-signal.ts → startup-signal.ts} +23 -23
- package/scripts/cli/tray/index.ts +62 -12
- package/scripts/cli/{command/tray.ts → tray.ts} +208 -208
- package/scripts/cli/{command/update.ts → update.ts} +39 -39
- package/scripts/cli/version.ts +67 -0
- package/scripts/cli/{ui/wordmark.ts → wordmark.ts} +58 -58
- package/src/boot/app.ts +697 -693
- package/src/boot/dependencies.ts +647 -646
- package/src/boot/timeout.ts +2 -13
- package/src/config/cooldowns.ts +186 -138
- package/src/config/network.ts +8 -14
- package/src/config/providers.ts +7 -10
- package/src/config/runtime.ts +76 -76
- package/src/config/security.ts +15 -0
- package/src/config/spec.ts +20 -30
- package/src/config/telemetry.ts +0 -20
- package/src/config/timeouts.ts +0 -6
- package/src/console/admin/model-abuse/contracts.ts +28 -6
- package/src/console/admin/model-abuse/routes.ts +56 -3
- package/src/console/api-keys/contracts.ts +410 -414
- package/src/console/api-keys/routes.ts +881 -880
- package/src/console/auth/credentials.ts +3 -3
- package/src/console/auth/setup-service.ts +2 -2
- package/src/console/backup/export-endpoint.ts +6 -2
- package/src/console/backup/nine-router.ts +2 -5
- package/src/console/backup/service.ts +4 -0
- package/src/console/backup/store.ts +192 -29
- package/src/console/backup/validate.ts +3 -45
- package/src/console/cli-tools/fs-ops.ts +487 -487
- package/src/console/console-router-guards.ts +1 -1
- package/src/console/data-management/contracts.ts +13 -6
- package/src/console/data-management/operations.ts +23 -18
- package/src/console/data-management/routes.ts +0 -1
- package/src/console/domain-registration.ts +10 -5
- package/src/console/observability/live.ts +22 -0
- package/src/console/observability/store.ts +39 -16
- package/src/console/observability/usage-dimensions.ts +4 -4
- package/src/console/providers/catalog/catalog-projections.ts +6 -7
- package/src/console/providers/catalog/model-operations.ts +5 -2
- package/src/console/providers/catalog/provider-operations.ts +4 -4
- package/src/console/providers/catalog/public-model-store.ts +8 -11
- package/src/console/providers/catalog/store.ts +1553 -1553
- package/src/console/providers/oauth/routes.ts +8 -12
- package/src/console/quota/account-quota-tenant.ts +6 -6
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/contracts.ts +61 -0
- package/src/console/remote-support/routes.ts +65 -0
- package/src/console/remote-support/service.ts +207 -0
- package/src/console/response-compression.ts +0 -3
- package/src/console/routing/pools/probe-result.ts +3 -2
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/store.ts +8 -8
- package/src/console/settings/contracts.ts +58 -2
- package/src/console/settings/store.ts +30 -3
- package/src/console/shared/cursor.ts +8 -8
- package/src/console/shared/errors.ts +1 -3
- package/src/network/http2-bidi.ts +2 -4
- package/src/network/pool/agent.ts +478 -478
- package/src/network/pool/selector.ts +5 -4
- package/src/network/retry.ts +1 -2
- package/src/network/ssrf.ts +5 -4
- package/src/observability/event-compaction.ts +80 -78
- package/src/observability/event-day-index.ts +13 -15
- package/src/observability/event-log.ts +4 -4
- package/src/observability/logger.ts +6 -6
- package/src/observability/payload-capture.ts +151 -33
- package/src/observability/payload-store.ts +397 -440
- package/src/observability/redaction.ts +5 -6
- package/src/observability/request-notify.ts +43 -0
- package/src/observability/telemetry-status.ts +8 -9
- package/src/observability/token-speed.ts +5 -4
- package/src/persistence/db-mode.ts +7 -7
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/schema.ts +1406 -1402
- package/src/persistence/shared-cache.ts +1 -2
- package/src/persistence/stores/api-key-store.ts +18 -6
- package/src/persistence/stores/share-store.ts +4 -3
- package/src/persistence/stores/telemetry-store.ts +236 -237
- package/src/protocol/canonical-model.ts +11 -3
- package/src/protocol/codecs.ts +138 -4
- package/src/protocol/gateway-error.ts +6 -4
- package/src/protocol/request/chat.ts +21 -1
- package/src/protocol/request/codex.ts +10 -1
- package/src/protocol/request/messages.ts +4 -5
- package/src/protocol/request/responses.ts +9 -1
- package/src/protocol/response/chat.ts +34 -4
- package/src/protocol/response/codex.ts +7 -4
- package/src/protocol/response/dsml.ts +140 -28
- package/src/protocol/response/responses.ts +11 -5
- package/src/protocol/stream-error-frames.ts +4 -5
- package/src/providers/authentication/jwt-validator.ts +9 -10
- package/src/providers/authentication/oauth-refresh-service.ts +9 -11
- package/src/providers/configured-openai-providers.ts +134 -136
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/catalog/catalog-format.ts +3 -6
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +4 -4
- package/src/providers/discovery/probe-wire.ts +6 -13
- package/src/providers/discovery/probing-service.ts +1117 -1045
- package/src/providers/integrations/alibabacp/alibabacp.ts +1 -1
- package/src/providers/integrations/antigravity/antigravity-quota.ts +5 -7
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +23 -2
- package/src/providers/integrations/buddy/buddy-checkin.ts +1 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +23 -6
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -1
- package/src/providers/integrations/buddy/buddy-identity.ts +52 -0
- package/src/providers/integrations/buddy/buddy-media.ts +2 -2
- package/src/providers/integrations/buddy/buddy-usage-store.ts +3 -4
- package/src/providers/integrations/buddy/codebuddy/quota.ts +2 -13
- package/src/providers/integrations/buddy/codebuddy/shared.ts +118 -115
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -11
- package/src/providers/integrations/buddy/workbuddy/shared.ts +5 -2
- package/src/providers/integrations/claude/claude.ts +608 -608
- package/src/providers/integrations/cline/cline-quota.ts +132 -132
- package/src/providers/integrations/cline/cline.ts +537 -539
- package/src/providers/integrations/codex/codex-oauth.ts +1 -1
- package/src/providers/integrations/commandcode/commandcode.ts +674 -675
- package/src/providers/integrations/commandcode/discovery.ts +117 -116
- package/src/providers/integrations/cursor/cursor-auth.ts +418 -418
- package/src/providers/integrations/cursor/cursor-quota.ts +309 -310
- package/src/providers/integrations/cursor/cursor.ts +11 -163
- package/src/providers/integrations/factory/factory-models.ts +3 -5
- package/src/providers/integrations/grok/grok-install-id.ts +2 -3
- package/src/providers/integrations/grok/grok.ts +6 -7
- package/src/providers/integrations/hermes.ts +14 -18
- package/src/providers/integrations/kiro/kiro-discovery.ts +1 -1
- package/src/providers/integrations/kiro/kiro.ts +1 -1
- package/src/providers/integrations/mistral.ts +6 -6
- package/src/providers/integrations/opencode/fingerprint.ts +3 -3
- package/src/providers/integrations/opencode/provider.ts +8 -8
- package/src/providers/integrations/qoder/qoder-catalog.ts +450 -450
- package/src/providers/integrations/qoder/qoder-oauth.ts +388 -310
- package/src/providers/integrations/qoder/qoder.ts +884 -865
- package/src/providers/integrations/shared/connect.ts +6 -6
- package/src/providers/integrations/trae/trae-catalog.ts +1 -2
- package/src/providers/integrations/trae/trae.ts +2 -2
- package/src/providers/integrations/zcode/oauth.ts +6 -1
- package/src/providers/operations/account-health-service.ts +1528 -1523
- package/src/providers/operations/account-reset-service.ts +1 -1
- package/src/providers/operations/client-versions.ts +6 -6
- package/src/providers/operations/custom-cli-headers.ts +3 -4
- package/src/providers/operations/provider-catalog-seeder.ts +95 -95
- package/src/providers/operations/provider-catalog-service.ts +3 -4
- package/src/providers/provider-metadata.ts +4 -8
- package/src/providers/provider-registry.ts +6 -7
- package/src/providers/provider-usage.ts +1 -1
- package/src/security/abuse.ts +15 -10
- package/src/security/access-control.ts +144 -146
- package/src/security/admission/contracts.ts +11 -0
- package/src/security/admission/in-memory-store.ts +4 -3
- package/src/security/admission/lease-sweep.ts +6 -6
- package/src/security/admission/reasons.ts +5 -1
- package/src/security/admission/redis-store.ts +211 -210
- package/src/security/admission/service.ts +4 -0
- package/src/security/api-key-auth.ts +504 -504
- package/src/security/ip-boundary.ts +155 -15
- package/src/security/model-abuse.ts +366 -94
- package/src/security/outbound-headers.ts +3 -4
- package/src/shared/sleep.ts +4 -0
- package/src/transport/dispatch/attempt-finalize.ts +47 -5
- package/src/transport/dispatch/attempt-loop.ts +531 -530
- package/src/transport/dispatch/stream-bridge.ts +1 -3
- package/src/transport/dispatch/streaming/finalize.ts +1 -1
- package/src/transport/dispatch/streaming/stream.ts +5 -6
- package/src/transport/failure-policy.ts +162 -8
- package/src/transport/guards/auth-guard.ts +7 -4
- package/src/transport/guards/concurrency-guard.ts +0 -5
- package/src/transport/guards/error-lifecycle.ts +13 -6
- package/src/transport/guards/pipeline.ts +15 -3
- package/src/transport/guards/request-context.ts +8 -2
- package/src/transport/request/inflight.ts +5 -6
- package/src/transport/request/preparer.ts +2 -2
- package/src/transport/request/state.ts +12 -8
- package/src/transport/routing/admission.ts +4 -0
- package/src/transport/routing/route-catalog.ts +8 -1
- package/src/transport/routing/route-model.ts +46 -14
- package/src/transport/routing/router.ts +21 -14
- package/src/transport/surface/completion.ts +2 -3
- package/src/transport/surface/dialects.ts +5 -6
- package/src/transport/surface/responses/encode.ts +38 -0
- package/src/transport/surface/responses/parse.ts +4 -0
- package/src/transport/translation/capabilities.ts +2 -3
- package/src/transport/translation/quirks.ts +3 -3
- package/src/workers/buddy-growth-tasks.ts +5 -10
- package/src/workers/daily-checkin.ts +17 -23
- package/src/workers/quota-refresh-sweep.ts +26 -26
- package/dist/dashboard/assets/BannedPage-C2orBsI2.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BV6pxVQo.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-Bsan4679.js +0 -1
- package/dist/dashboard/assets/CombosPage-Bsan4679.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CW-wPmJ6.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-0p8uX7vB.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BvzCx8Kr.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js +0 -1
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js +0 -1
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js +0 -7
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js +0 -1
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-DDbMQsoo.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-CB3Z7VTf.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-D95UCPHI.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js +0 -1
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js.gz +0 -0
- package/dist/dashboard/assets/app-COlK95oi.css +0 -2
- package/dist/dashboard/assets/app-COlK95oi.css.gz +0 -0
- package/dist/dashboard/assets/assert-Chi7pCnt.js.gz +0 -0
- package/dist/dashboard/assets/badge-D4pQapKj.js.gz +0 -0
- package/dist/dashboard/assets/banner-DH6cg-_B.js.gz +0 -0
- package/dist/dashboard/assets/card-BKqlUOe4.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-fTgljTGA.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-D5Ys_sM0.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BYj-8WuK.js +0 -1
- package/dist/dashboard/assets/dialog-BYj-8WuK.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js +0 -1
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js.gz +0 -0
- package/dist/dashboard/assets/entry-CBg5zeu8.js +0 -1
- package/dist/dashboard/assets/entry-CBg5zeu8.js.gz +0 -0
- package/dist/dashboard/assets/entry-CQBr6U9c.js.gz +0 -0
- package/dist/dashboard/assets/entry-CgdSAzEV.js +0 -4
- package/dist/dashboard/assets/entry-CgdSAzEV.js.gz +0 -0
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-BlptL8Uf.js.gz +0 -0
- package/dist/dashboard/assets/index-js6TJvdr.js.gz +0 -0
- package/dist/dashboard/assets/inline-DUWDSmDV.js.gz +0 -0
- package/dist/dashboard/assets/input-LVjgZq4f.js.gz +0 -0
- package/dist/dashboard/assets/layout-CPfxpsfy.js +0 -1
- package/dist/dashboard/assets/layout-CPfxpsfy.js.gz +0 -0
- package/dist/dashboard/assets/list-row-DupvLfDx.js.gz +0 -0
- package/dist/dashboard/assets/live-C4pEJUqI.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-BRiuufkB.js.gz +0 -0
- package/dist/dashboard/assets/mutation-B4ROwk8L.js.gz +0 -0
- package/dist/dashboard/assets/numbers-YNcHvRY7.js +0 -2
- package/dist/dashboard/assets/numbers-YNcHvRY7.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-gmjzC3RS.js.gz +0 -0
- package/dist/dashboard/assets/routing-C7tBT5o-.js.gz +0 -0
- package/dist/dashboard/assets/select-WUCXorXw.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_zKl1GS.js.gz +0 -0
- package/dist/dashboard/assets/stack-_3O0F8Xn.js.gz +0 -0
- package/dist/dashboard/assets/state-BgTxHcJS.js.gz +0 -0
- package/dist/dashboard/assets/switch-DEjTtOWf.js.gz +0 -0
- package/dist/dashboard/assets/tokens-DDxMbSRL.js +0 -1
- package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js +0 -1
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BWSC0UAS.js.gz +0 -0
- package/scripts/cli/command/admin.ts +0 -65
- package/scripts/cli/command/doctor.ts +0 -257
- package/scripts/cli/gateway/drain-token.ts +0 -78
- package/scripts/cli/gateway/instance-lock.ts +0 -105
- package/scripts/cli/gateway/stop.ts +0 -51
- package/scripts/cli/support/bun.ts +0 -27
- package/scripts/cli/support/version.ts +0 -37
- /package/dist/dashboard/assets/{app-DCQZRmHY.js → app-BIt446yE.js} +0 -0
- /package/dist/dashboard/assets/{app-DCQZRmHY.js.gz → app-BIt446yE.js.gz} +0 -0
- /package/scripts/cli/{ui/prompt.ts → prompt.ts} +0 -0
|
@@ -278,7 +278,7 @@ export class NetworkPoolSelector {
|
|
|
278
278
|
// One script, not `SET` then `SADD`: a crash between the two left the
|
|
279
279
|
// marker unindexed (invisible to the pool listing) or the index
|
|
280
280
|
// pointing at a marker that expired. The script also arms the index
|
|
281
|
-
// set's own TTL, which
|
|
281
|
+
// set's own TTL, which must be armed or the index grows without bound.
|
|
282
282
|
await redisEvalNumber(
|
|
283
283
|
this.redis,
|
|
284
284
|
COOLDOWN_FLAG_SCRIPT,
|
|
@@ -458,9 +458,10 @@ export class NetworkPoolSelector {
|
|
|
458
458
|
/**
|
|
459
459
|
* Removes one cooldown marker and its index entry in one atomic step.
|
|
460
460
|
*
|
|
461
|
-
* The two writes
|
|
462
|
-
*
|
|
463
|
-
* report a cooldown that no longer
|
|
461
|
+
* The two writes must not be separate `DEL` + `SREM`: a crash between them
|
|
462
|
+
* would leave the index listing a provider whose marker is gone — a pool
|
|
463
|
+
* would report a cooldown that no longer applies until the index entry
|
|
464
|
+
* expires.
|
|
464
465
|
*/
|
|
465
466
|
private async clearRedisCooldown(poolId: string, providerId: string): Promise<void> {
|
|
466
467
|
if (!this.redis) return;
|
package/src/network/retry.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { exponentialBackoff, type BackoffOptions } from "./backoff";
|
|
10
|
+
import { sleep as defaultSleep } from "../shared/sleep";
|
|
10
11
|
|
|
11
12
|
export interface RetryOptions {
|
|
12
13
|
readonly maxRetries?: number;
|
|
@@ -18,8 +19,6 @@ export interface RetryOptions {
|
|
|
18
19
|
readonly shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const defaultSleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
|
-
|
|
23
22
|
/**
|
|
24
23
|
* Runs `fn`, retrying on failure with exponential backoff (100 ms, 200 ms,
|
|
25
24
|
* 400 ms, …) capped at `maxDelayMs` and jittered by up to 25% to avoid a
|
package/src/network/ssrf.ts
CHANGED
|
@@ -73,10 +73,11 @@ function cidrContains(address: string, cidr: string): boolean {
|
|
|
73
73
|
const [network, prefixText] = cidr.split("/");
|
|
74
74
|
if (!network || prefixText === undefined) return false;
|
|
75
75
|
// A present-but-blank prefix (`"10.0.0.0/"`) must be refused, not coerced.
|
|
76
|
-
// `Number("")` is 0 and `""` is not `undefined`, so
|
|
77
|
-
//
|
|
78
|
-
// operator's typo therefore silently
|
|
79
|
-
// space, including the link-local metadata endpoint this guard exists to
|
|
76
|
+
// `Number("")` is 0 and `""` is not `undefined`, so a plain truthiness check
|
|
77
|
+
// here would leave `prefix = 0` — and a /0 admits every address of its family.
|
|
78
|
+
// An operator's typo would therefore silently widen the allowlist to the whole
|
|
79
|
+
// IPv4 space, including the link-local metadata endpoint this guard exists to
|
|
80
|
+
// refuse.
|
|
80
81
|
//
|
|
81
82
|
// Requiring a run of digits (after trimming) also refuses the exotic spellings
|
|
82
83
|
// `Number` accepts for zero — `-0`, `+0`, `0x0`, `0b0`, `0o0`, `0.0` — all of
|
|
@@ -1,70 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* summary row per (tenant, API key, day).
|
|
2
|
+
* Bucket primitives for the per-day telemetry index.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Compaction is **accumulation, not sampling**. A compacted row carries the
|
|
11
|
-
* running sums of everything it replaced, so a total that read 2B tokens
|
|
12
|
-
* before compaction still reads 2B after it. Nothing is estimated and nothing
|
|
13
|
-
* is averaged away — the per-request rows are gone, their totals are not.
|
|
14
|
-
*
|
|
15
|
-
* What compaction cannot preserve is anything that needs the individual row:
|
|
16
|
-
* the request list, per-request filters, and the detail drawer. Latency is the
|
|
17
|
-
* interesting middle case — a percentile needs every sample, so compaction
|
|
18
|
-
* keeps a fixed histogram instead. P50 stays available as an approximation and
|
|
19
|
-
* is marked as one rather than silently reported as exact.
|
|
20
|
-
*
|
|
21
|
-
* Retention becomes tiered:
|
|
22
|
-
*
|
|
23
|
-
* 0 .. N days hourly files, complete
|
|
24
|
-
* N .. M days compacted daily summary
|
|
25
|
-
* beyond M deleted
|
|
4
|
+
* Each day's events are folded into a `CompactedBucket` via `accumulate()` and
|
|
5
|
+
* stored as a `.index.json` file alongside the source `.events.jsonl`. Buckets
|
|
6
|
+
* are pure accumulators: every field is a running sum, min, or max — nothing
|
|
7
|
+
* is averaged or estimated away. `mergeBucketsInternal` adds two buckets so
|
|
8
|
+
* partial indexes can be merged without re-reading source files.
|
|
26
9
|
*/
|
|
27
10
|
import type { TelemetryEventRecord } from "./event-log";
|
|
28
|
-
import type { CartethyiaDatabase } from "../persistence/db-handle";
|
|
29
|
-
import { consoleSettings } from "../persistence/schema";
|
|
30
|
-
import { resolveTelemetryWindowMinutes } from "../config/telemetry";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The compaction age to apply on a pass with no explicit age given.
|
|
34
|
-
*
|
|
35
|
-
* Hourly files are **not** partitioned per tenant — one file per hour holds
|
|
36
|
-
* every tenant's rows — so a per-tenant age cannot be honoured per file. The
|
|
37
|
-
* safest reading of several tenants' preferences is the **largest**: compacting
|
|
38
|
-
* smaller than one tenant asked for moves its requests into the summary before
|
|
39
|
-
* that tenant expected, losing the per-request view of them, while keeping them
|
|
40
|
-
* longer than another wanted only costs disk and a slower scan. Losing detail
|
|
41
|
-
* is not reversible; using more disk is.
|
|
42
|
-
*
|
|
43
|
-
* Falls back to the deployment default when no tenant overrides, and never
|
|
44
|
-
* exceeds the retention window (compacting at or past the delete age would
|
|
45
|
-
* fold a file and discard it in the same pass).
|
|
46
|
-
*/
|
|
47
|
-
export async function resolveEffectiveWindowMinutes(db: CartethyiaDatabase): Promise<number> {
|
|
48
|
-
const fallback = resolveTelemetryWindowMinutes();
|
|
49
|
-
try {
|
|
50
|
-
const rows = await db
|
|
51
|
-
.select({ preferences: consoleSettings.preferences })
|
|
52
|
-
.from(consoleSettings);
|
|
53
|
-
let widest = fallback;
|
|
54
|
-
for (const row of rows) {
|
|
55
|
-
const minutes = row.preferences?.telemetryWindowMinutes;
|
|
56
|
-
if (typeof minutes === "number" && Number.isFinite(minutes) && minutes > widest) {
|
|
57
|
-
widest = minutes;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return Math.floor(widest);
|
|
61
|
-
} catch {
|
|
62
|
-
return fallback;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Discriminator so a reader can tell a summary from a request row at a glance. */
|
|
67
|
-
const COMPACTED_KIND = "compacted" as const;
|
|
68
11
|
|
|
69
12
|
/**
|
|
70
13
|
* Histogram bucket edges in milliseconds.
|
|
@@ -112,23 +55,53 @@ export interface CompactedBucket {
|
|
|
112
55
|
readonly byProvider: Readonly<Record<string, number>>;
|
|
113
56
|
readonly byClient: Readonly<Record<string, number>>;
|
|
114
57
|
readonly byClientIp: Readonly<Record<string, number>>;
|
|
58
|
+
/**
|
|
59
|
+
* Requests per HTTP status, keyed by the status as a decimal string.
|
|
60
|
+
*
|
|
61
|
+
* Kept as counts for the same reason the other dimensions are: the Usage
|
|
62
|
+
* summary's status chips and the request-list filters read these, and without
|
|
63
|
+
* them every chip reads zero once the day's events have been folded — the
|
|
64
|
+
* numbers were on the source events, which no longer exist.
|
|
65
|
+
*/
|
|
66
|
+
readonly byStatus: Readonly<Record<string, number>>;
|
|
67
|
+
/**
|
|
68
|
+
* Sum and count of gateway routing delay, so `avgResolveMs` survives
|
|
69
|
+
* compaction. Kept as a sum rather than an average of averages for the same
|
|
70
|
+
* reason the latency sum is: an average of per-file averages drifts as soon
|
|
71
|
+
* as the files hold different request counts.
|
|
72
|
+
*/
|
|
73
|
+
readonly resolveSumMs: number;
|
|
74
|
+
readonly resolveCount: number;
|
|
115
75
|
}
|
|
116
76
|
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
/** A number from a value that may be a numeric string; 0 when unusable. */
|
|
127
|
-
function numeric(value: unknown): number {
|
|
77
|
+
/**
|
|
78
|
+
* A number from a value that may be a numeric string; 0 when unusable.
|
|
79
|
+
*
|
|
80
|
+
* Exported so readers of a compacted bucket coerce its fields the same way the
|
|
81
|
+
* fold does: a second local copy of "is this a number" is how one caller ends
|
|
82
|
+
* up summing `undefined` into NaN while another reads it as zero.
|
|
83
|
+
*/
|
|
84
|
+
export function numeric(value: unknown): number {
|
|
128
85
|
const asNumber = typeof value === "number" ? value : Number(value);
|
|
129
86
|
return Number.isFinite(asNumber) ? asNumber : 0;
|
|
130
87
|
}
|
|
131
88
|
|
|
89
|
+
/**
|
|
90
|
+
* The two fields added after the first index format, read defensively.
|
|
91
|
+
*
|
|
92
|
+
* An index file written before they existed carries neither, and `undefined +
|
|
93
|
+
0` is `NaN` — which then propagates straight through every later fold and
|
|
94
|
+
* merge, so one old day turns the whole period's `avgResolveMs` into `NaN`.
|
|
95
|
+
* Reading them as 0/{} is the same answer an empty day gives, and a folded
|
|
96
|
+
* day that predates the field genuinely has no resolve measurement to report.
|
|
97
|
+
*/
|
|
98
|
+
function readResolveSum(bucket: CompactedBucket): number {
|
|
99
|
+
return numeric(bucket.resolveSumMs);
|
|
100
|
+
}
|
|
101
|
+
function readResolveCount(bucket: CompactedBucket): number {
|
|
102
|
+
return numeric(bucket.resolveCount);
|
|
103
|
+
}
|
|
104
|
+
|
|
132
105
|
/** Adds one occurrence to a count map, ignoring an absent key. */
|
|
133
106
|
function bump(
|
|
134
107
|
counts: Readonly<Record<string, number>> | undefined,
|
|
@@ -156,6 +129,9 @@ export function emptyBucket(): CompactedBucket {
|
|
|
156
129
|
byProvider: {},
|
|
157
130
|
byClient: {},
|
|
158
131
|
byClientIp: {},
|
|
132
|
+
byStatus: {},
|
|
133
|
+
resolveSumMs: 0,
|
|
134
|
+
resolveCount: 0,
|
|
159
135
|
requests: 0,
|
|
160
136
|
inputTokens: 0,
|
|
161
137
|
cachedTokens: 0,
|
|
@@ -207,11 +183,18 @@ export function accumulate(
|
|
|
207
183
|
// summing, which turned a cost of 0.000966 into "00.000966". Coerced here so
|
|
208
184
|
// every downstream sum is arithmetic.
|
|
209
185
|
const priced = numeric(event.estimatedCostUsd);
|
|
186
|
+
const resolve = event.resolveMs;
|
|
187
|
+
const hasResolve = resolve !== null && resolve !== undefined && Number.isFinite(resolve);
|
|
210
188
|
return {
|
|
211
189
|
byModel: bump(into.byModel, event.requestedModel ?? event.reportedModel),
|
|
212
190
|
byProvider: bump(into.byProvider, event.providerId),
|
|
213
191
|
byClient: bump(into.byClient, event.sourceSurface),
|
|
214
192
|
byClientIp: bump(into.byClientIp, event.clientIp),
|
|
193
|
+
// Keyed on the HTTP status, not the internal one: the chips and the filters
|
|
194
|
+
// are the status the client was given.
|
|
195
|
+
byStatus: bump(into.byStatus, event.httpStatus?.toString()),
|
|
196
|
+
resolveSumMs: readResolveSum(into) + (hasResolve ? resolve : 0),
|
|
197
|
+
resolveCount: readResolveCount(into) + (hasResolve ? 1 : 0),
|
|
215
198
|
requests: into.requests + 1,
|
|
216
199
|
inputTokens: into.inputTokens + (event.inputTokens ?? 0),
|
|
217
200
|
cachedTokens: into.cachedTokens + (event.cachedInputTokens ?? 0),
|
|
@@ -251,13 +234,20 @@ export function accumulate(
|
|
|
251
234
|
*
|
|
252
235
|
* Returns the upper edge of the bucket that contains it — never a value
|
|
253
236
|
* between edges, because a histogram cannot be more precise than the bucket
|
|
254
|
-
* that holds the sample. `null` when there are no samples
|
|
255
|
-
*
|
|
237
|
+
* that holds the sample. `null` when there are no samples.
|
|
238
|
+
*
|
|
239
|
+
* The overflow bucket is the last one and has no upper edge, so it returns
|
|
240
|
+
* {@link HISTOGRAM_OVERFLOW_MS} — the top edge of the last real bucket — rather
|
|
241
|
+
* than `null`. Returning `null` there was read as "no data" by every caller,
|
|
242
|
+
* which collapsed a P95 of 40 s to `0 ms`: the slowest, most interesting
|
|
243
|
+
* percentiles were the ones reported as zero. Callers that can do better pass
|
|
244
|
+
* the period's `latencyMaxMs` as `overflowMs` and get that instead.
|
|
256
245
|
*/
|
|
257
246
|
export function percentileFromHistogram(
|
|
258
247
|
histogram: readonly number[],
|
|
259
248
|
percentile: number,
|
|
260
|
-
|
|
249
|
+
overflowMs?: number | null,
|
|
250
|
+
): number | null {
|
|
261
251
|
const total = histogram.reduce((sum, count) => sum + count, 0);
|
|
262
252
|
if (total === 0) return null;
|
|
263
253
|
const target = Math.ceil((percentile / 100) * total);
|
|
@@ -267,12 +257,21 @@ export function percentileFromHistogram(
|
|
|
267
257
|
if (seen >= target) {
|
|
268
258
|
// The bucket's upper edge: a percentile from a histogram can never be
|
|
269
259
|
// more precise than the bucket that contains it.
|
|
270
|
-
|
|
260
|
+
if (index < LATENCY_BUCKETS_MS.length) return LATENCY_BUCKETS_MS[index]!;
|
|
261
|
+
// Above the last edge. `latencyMaxMs` is the honest answer when the
|
|
262
|
+
// caller has it — it is the real slowest request in the period, so a
|
|
263
|
+
// percentile past the last edge is at most that. Falling back to the
|
|
264
|
+
// last edge rather than 0 keeps the figure in the right order of
|
|
265
|
+
// magnitude even when no max was recorded.
|
|
266
|
+
return overflowMs ?? HISTOGRAM_OVERFLOW_MS;
|
|
271
267
|
}
|
|
272
268
|
}
|
|
273
269
|
return null;
|
|
274
270
|
}
|
|
275
271
|
|
|
272
|
+
/** Top edge of the last real latency bucket; the floor for an overflow answer. */
|
|
273
|
+
export const HISTOGRAM_OVERFLOW_MS = LATENCY_BUCKETS_MS[LATENCY_BUCKETS_MS.length - 1]!;
|
|
274
|
+
|
|
276
275
|
/** Newline count of a file, so a torn or unparsable line is detectable. */
|
|
277
276
|
/** Adds two buckets. Exported so the day index can merge without duplicating it. */
|
|
278
277
|
export function mergeBucketsInternal(a: CompactedBucket, b: CompactedBucket): CompactedBucket {
|
|
@@ -282,6 +281,9 @@ export function mergeBucketsInternal(a: CompactedBucket, b: CompactedBucket): Co
|
|
|
282
281
|
byProvider: mergeCounts(a.byProvider, b.byProvider),
|
|
283
282
|
byClient: mergeCounts(a.byClient, b.byClient),
|
|
284
283
|
byClientIp: mergeCounts(a.byClientIp, b.byClientIp),
|
|
284
|
+
byStatus: mergeCounts(a.byStatus, b.byStatus),
|
|
285
|
+
resolveSumMs: readResolveSum(a) + readResolveSum(b),
|
|
286
|
+
resolveCount: readResolveCount(a) + readResolveCount(b),
|
|
285
287
|
requests: a.requests + b.requests,
|
|
286
288
|
inputTokens: a.inputTokens + b.inputTokens,
|
|
287
289
|
cachedTokens: a.cachedTokens + b.cachedTokens,
|
|
@@ -12,17 +12,15 @@ import { TELEMETRY_EVENT_DIR, type TelemetryEventRecord } from "./event-log";
|
|
|
12
12
|
* One sidecar index per day, beside that day's append-only event file.
|
|
13
13
|
*
|
|
14
14
|
* The event file grows all day and is never rewritten while it is open. The
|
|
15
|
-
* index is the thing the dashboard reads: it holds the
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* deleting source files.
|
|
15
|
+
* index is the thing the dashboard reads: it holds the totals and the breakdown
|
|
16
|
+
* counts, maintained incrementally from a byte watermark rather than rebuilt by
|
|
17
|
+
* folding and deleting source files.
|
|
19
18
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* idempotent.
|
|
19
|
+
* The watermark is what makes that possible. Deciding which file was still
|
|
20
|
+
* being written by comparing file *names* lets the open file be folded and
|
|
21
|
+
* unlinked mid-write, so its requests never reach the summary. A byte offset
|
|
22
|
+
* has no such ambiguity: everything below it is counted, everything above it is
|
|
23
|
+
* not, and re-reading the tail is idempotent.
|
|
26
24
|
*/
|
|
27
25
|
|
|
28
26
|
/**
|
|
@@ -58,16 +56,16 @@ export function utcDay(at: Date): string {
|
|
|
58
56
|
* One hour's share of a bucket.
|
|
59
57
|
*
|
|
60
58
|
* The Traffic chart asks how a day was spent, which the totals alone cannot
|
|
61
|
-
* answer. Twenty-four slots per bucket is small — four numbers each —
|
|
62
|
-
* held *inside* the index rather than as
|
|
59
|
+
* answer. Twenty-four slots per bucket is small — four numbers each — so it is
|
|
60
|
+
* held *inside* the index rather than as separate per-hour files.
|
|
63
61
|
*/
|
|
64
62
|
/**
|
|
65
63
|
* One named dimension's share of a bucket.
|
|
66
64
|
*
|
|
67
65
|
* The breakdown asks for tokens, errors and cost per model/provider/client/IP,
|
|
68
|
-
* not merely how many requests each took. Counts alone
|
|
69
|
-
*
|
|
70
|
-
*
|
|
66
|
+
* not merely how many requests each took. Counts alone would leave every
|
|
67
|
+
* breakdown column reading zero, so each named entry carries the same running
|
|
68
|
+
* sums the bucket does.
|
|
71
69
|
*/
|
|
72
70
|
export interface IndexedDimension {
|
|
73
71
|
readonly requests: number;
|
|
@@ -5,9 +5,9 @@ import { resolveDataDir } from "../persistence/db-mode";
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Per-request telemetry lives in hourly JSONL files, not in Postgres. The
|
|
8
|
-
* database keeps only the durable aggregate
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* database keeps only the durable aggregate; the files are the single source
|
|
9
|
+
* for recent requests and readers must go through this module, not a
|
|
10
|
+
* `telemetry_events` table.
|
|
11
11
|
*/
|
|
12
12
|
/**
|
|
13
13
|
* Where hourly event files and their compacted summaries live.
|
|
@@ -325,7 +325,7 @@ export async function queryTelemetryEvents(query: EventQuery): Promise<Telemetry
|
|
|
325
325
|
//
|
|
326
326
|
// The lower bound is the retention window, not just `since`: a caller that
|
|
327
327
|
// asks for all time still must not scan files that have already been aged
|
|
328
|
-
// out, and one that passes no `since` at all
|
|
328
|
+
// out, and one that passes no `since` at all must not read every file ever
|
|
329
329
|
// written. Files older than retention are gone or compacted; either way they
|
|
330
330
|
// are not here.
|
|
331
331
|
const retentionFloor = hourStamp(
|
|
@@ -115,12 +115,12 @@ function logArgs(
|
|
|
115
115
|
msg: string,
|
|
116
116
|
args: readonly unknown[],
|
|
117
117
|
): void {
|
|
118
|
-
// The level is consulted first. Building the safe copy
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
118
|
+
// The level is consulted first. Building the safe copy unconditionally would
|
|
119
|
+
// make a `debug` call at the production `info` default still pay a
|
|
120
|
+
// depth-capped deep copy plus a JSON.stringify of every argument, on every
|
|
121
|
+
// request that reaches it — the surface-detection debug line fires on almost
|
|
122
|
+
// every chat request. Pino's own level check happens inside `write`, which is
|
|
123
|
+
// already too late to skip the work.
|
|
124
124
|
if (!logger.isLevelEnabled(level)) return;
|
|
125
125
|
const safeArgs = safeLogArgs(args);
|
|
126
126
|
ring(level, msg, safeArgs);
|