cartethyia 2.7.8 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -3
- package/README.md +29 -59
- package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js +1 -0
- package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-Bqj2aa1T.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-CXsaOAO2.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js +1 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BWFLECmz.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-CgoPc5B9.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-D9bFQern.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js +1 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BwNC_-Cx.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js +1 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-febLU4r1.js} +1 -1
- package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-M6fvR5Wz.js} +1 -1
- package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js +56 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js +1 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css +2 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
- package/dist/dashboard/assets/{assert-CidQVoNo.js → assert-CqTLw5e7.js} +1 -1
- package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-C83EqLB0.js} +1 -1
- package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
- package/dist/dashboard/assets/ban-durations-B58FyRxC.js +1 -0
- package/dist/dashboard/assets/ban-durations-B58FyRxC.js.gz +0 -0
- package/dist/dashboard/assets/{banner-B-yXtCqb.js → banner-D1xn7pIe.js} +1 -1
- package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
- package/dist/dashboard/assets/brand/bansos_page.webp +0 -0
- package/dist/dashboard/assets/brand/denia_bansos.webp +0 -0
- package/dist/dashboard/assets/brand/favicon.webp +0 -0
- package/dist/dashboard/assets/brand/login_register.webp +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.webp +0 -0
- package/dist/dashboard/assets/brand/og_bansos.webp +0 -0
- package/dist/dashboard/assets/brand/og_image.webp +0 -0
- package/dist/dashboard/assets/brand/sidebar_ico.webp +0 -0
- package/dist/dashboard/assets/{card-_HwAEqFF.js → card-DU3Lq5Lm.js} +1 -1
- package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js +1 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CEL5Sg_J.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-BjFR5UHy.js} +1 -1
- package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-HvBW0AjI.js} +1 -1
- package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js +4 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
- package/dist/dashboard/assets/{entry-COaC-irN.js → entry-CECVELyy.js} +1 -1
- package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js +1 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-DLW4i8zK.js} +1 -1
- package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
- package/dist/dashboard/assets/{index-CgKmFngv.js → index-1-fCqe64.js} +2 -2
- package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2BirKry.js → inline-DGvU2oLK.js} +1 -1
- package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
- package/dist/dashboard/assets/{input-D-qV9dKY.js → input-iNnO1X2K.js} +1 -1
- package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
- package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-0q8UGypb.js} +1 -1
- package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-C0w2bQjJ.js} +1 -1
- package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
- package/dist/dashboard/assets/{live-DGizF06-.js → live-M5aJ_xCa.js} +1 -1
- package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-GU7XdEjz.js → mutation-BQ0ccH_A.js} +1 -1
- package/dist/dashboard/assets/mutation-BQ0ccH_A.js.gz +0 -0
- package/dist/dashboard/assets/{network-C9UE-bOr.js → network-BKeQGT3K.js} +1 -1
- package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-CivAk3da.js} +1 -1
- package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-CpzvI5ep.js} +1 -1
- package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
- package/dist/dashboard/assets/providers/devin.svg +9 -0
- package/dist/dashboard/assets/providers/devin.svg.gz +0 -0
- package/dist/dashboard/assets/providers/windsurf.svg +10 -0
- package/dist/dashboard/assets/providers/windsurf.svg.gz +0 -0
- package/dist/dashboard/assets/providers/zro.svg +3 -0
- package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
- package/dist/dashboard/assets/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
- package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js +1 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
- package/dist/dashboard/assets/{select-BCOcv8WK.js → select-CXbck0jG.js} +2 -2
- package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js +1 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
- package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DggRiCxE.js} +1 -1
- package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
- package/dist/dashboard/assets/{state-DXbGTx7N.js → state-ClFzfbYp.js} +1 -1
- package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
- package/dist/dashboard/assets/story/25817331.webp +0 -0
- package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
- package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
- package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
- package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
- package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
- package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
- package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
- package/dist/dashboard/assets/{switch-ZeTsW623.js → switch-CTIFxKqS.js} +1 -1
- package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BClG3yWj.js} +1 -1
- package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.webp +0 -0
- package/dist/dashboard/console-backgrounds_initial.webp +0 -0
- package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
- package/dist/dashboard/index.html +3 -3
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +59 -56
- package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
- package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
- package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
- package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
- package/package.json +1 -1
- package/src/boot/app.ts +10 -13
- package/src/boot/dependencies.ts +12 -4
- package/src/boot/flush.ts +20 -0
- package/src/boot/lifecycle.ts +7 -5
- package/src/config/cooldowns.ts +1 -1
- package/src/config/routing.ts +26 -0
- package/src/config/spec-metadata.ts +72 -0
- package/src/config/spec.ts +20 -6
- package/src/console/api-keys/contracts.ts +12 -1
- package/src/console/api-keys/routes.ts +50 -31
- package/src/console/auth/account-reset.ts +7 -9
- package/src/console/backup/contracts.ts +4 -0
- package/src/console/backup/export-endpoint.ts +7 -5
- package/src/console/cli-tools/service.ts +6 -12
- package/src/console/console-router-guards.ts +23 -16
- package/src/console/console-router.ts +7 -0
- package/src/console/dashboard-assets.ts +5 -5
- package/src/console/domain-registration.ts +27 -2
- package/src/console/inferhub/normalize.ts +24 -28
- package/src/console/inferhub/types.ts +3 -0
- package/src/console/ip-bans/routes.ts +93 -0
- package/src/console/observability/store.ts +0 -53
- package/src/console/providers/catalog/catalog-projections.ts +5 -2
- package/src/console/providers/catalog/contracts.ts +123 -0
- package/src/console/providers/catalog/discover-constants.ts +6 -0
- package/src/console/providers/catalog/model-operations.ts +122 -10
- package/src/console/providers/catalog/provider-operations.ts +116 -8
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +65 -4
- package/src/console/providers/catalog/store-helpers.ts +30 -1
- package/src/console/providers/catalog/store.ts +249 -4
- package/src/console/quota/account-quota-global.ts +97 -19
- package/src/console/quota/account-quota-shared.ts +49 -0
- package/src/console/quota/account-quota-tenant.ts +6 -18
- package/src/console/quota/native-identity.ts +2 -0
- package/src/console/quota/quota-view.ts +19 -3
- package/src/console/routing/pools/operations.ts +31 -46
- package/src/console/settings/contracts.ts +18 -0
- package/src/console/settings/store.ts +32 -0
- package/src/console/shared/errors.ts +33 -6
- package/src/console/system-prompt/contracts.ts +0 -1
- package/src/main.ts +3 -3
- package/src/network/pool/byte-accounting.ts +3 -2
- package/src/network/pool/selector.ts +182 -39
- package/src/network/pool-cooldown-store.ts +89 -14
- package/src/network/pool-health.ts +49 -61
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/ssrf.ts +6 -6
- package/src/observability/event-log.ts +7 -361
- package/src/observability/event-query.ts +18 -4
- package/src/observability/performance-metrics.ts +9 -0
- package/src/observability/runtime-metrics.ts +14 -9
- package/src/observability/telemetry-buffer.ts +15 -25
- package/src/observability/telemetry-reconciliation.ts +9 -37
- package/src/observability/telemetry-row.ts +125 -0
- package/src/persistence/db-mode.ts +4 -1
- package/src/persistence/dialect.ts +30 -0
- package/src/persistence/migrate.ts +6 -3
- package/src/persistence/schema.ts +28 -2
- package/src/persistence/ttl-cache.ts +20 -2
- package/src/protocol/codecs.ts +1 -1
- package/src/protocol/dsml.ts +35 -87
- package/src/protocol/gateway-error.ts +1 -0
- package/src/protocol/markup-stream-parser.ts +195 -0
- package/src/protocol/reasoning-text.ts +19 -0
- package/src/protocol/reasoning.ts +1 -1
- package/src/protocol/request/chat.ts +14 -14
- package/src/protocol/request/gemini.ts +8 -2
- package/src/protocol/request/messages.ts +4 -5
- package/src/protocol/response/chat.ts +12 -20
- package/src/protocol/response/gemini.ts +6 -1
- package/src/protocol/response/omp-tool-call-markup.ts +43 -86
- package/src/protocol/response/responses.ts +18 -2
- package/src/protocol/sse-decoder.ts +25 -8
- package/src/protocol/stop-reason.ts +23 -1
- package/src/protocol/stream-error-frames.ts +13 -3
- package/src/protocol/think-split.ts +112 -0
- package/src/protocol/translation/dialect/qwen.ts +26 -58
- package/src/protocol/translation/quirks.ts +1 -1
- package/src/protocol/translation/tool-repair.ts +2 -2
- package/src/protocol/upstream/send-claude-messages.ts +15 -4
- package/src/protocol/upstream-parsing.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +78 -17
- package/src/providers/authentication/oauth-client.ts +1 -1
- package/src/providers/authentication/oauth-device-flow.ts +98 -8
- package/src/providers/authentication/oauth-refresh-service.ts +153 -59
- package/src/providers/configured-provider.ts +148 -0
- package/src/providers/default-registry.ts +117 -27
- package/src/providers/discovery/discovery-types.ts +12 -0
- package/src/providers/discovery/probe-phases.ts +74 -33
- package/src/providers/discovery/probing-service.ts +293 -165
- package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
- package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +121 -2
- package/src/providers/integrations/antigravity/antigravity.ts +13 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +191 -35
- package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
- package/src/providers/integrations/buddy/buddy-identity.ts +89 -6
- package/src/providers/integrations/buddy/buddy-media.ts +4 -5
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
- package/src/providers/integrations/buddy/capabilities.ts +6 -43
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +23 -1
- package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
- package/src/providers/integrations/buddy/codebuddy/shared.ts +38 -27
- package/src/providers/integrations/claude/claude-quota.ts +14 -17
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/commandcode/quota.ts +22 -28
- package/src/providers/integrations/cursor/cursor-auth.ts +0 -8
- package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
- package/src/providers/integrations/cursor/cursor-exec.ts +745 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
- package/src/providers/integrations/cursor/cursor-request.ts +35 -20
- package/src/providers/integrations/cursor/cursor-shared.ts +26 -5
- package/src/providers/integrations/cursor/cursor.ts +139 -70
- package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
- package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
- package/src/providers/integrations/devin/devin-auth.ts +353 -0
- package/src/providers/integrations/devin/devin-catalog.ts +207 -0
- package/src/providers/integrations/devin/devin-http.ts +91 -0
- package/src/providers/integrations/devin/devin-quota.ts +214 -0
- package/src/providers/integrations/devin/devin-shared.ts +111 -0
- package/src/providers/integrations/devin/devin.ts +539 -0
- package/src/providers/integrations/devin/protocol/devin-proto.ts +558 -0
- package/src/providers/integrations/devin/protocol/framing.ts +124 -0
- package/src/providers/integrations/factory/factory-oauth.ts +17 -44
- package/src/providers/integrations/factory/factory.ts +2 -2
- package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
- package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
- package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
- package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
- package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
- package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
- package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
- package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
- package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
- package/src/providers/integrations/freebuff/freebuff.ts +682 -0
- package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
- package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
- package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
- package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
- package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
- package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
- package/src/providers/integrations/freepi/freepi.ts +292 -0
- package/src/providers/integrations/gemini.ts +2 -2
- package/src/providers/integrations/grok/grok-oauth.ts +48 -45
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
- package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
- package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
- package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +13 -21
- package/src/providers/integrations/hermes/index.ts +2 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
- package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
- package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
- package/src/providers/integrations/kiro/kiro.ts +15 -19
- package/src/providers/integrations/ollamacloud/index.ts +2 -0
- package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
- package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
- package/src/providers/integrations/perch/perch-catalog.ts +105 -0
- package/src/providers/integrations/perch/perch-credential.ts +25 -0
- package/src/providers/integrations/perch/perch-oauth.ts +170 -0
- package/src/providers/integrations/perch/perch-protocol.ts +208 -0
- package/src/providers/integrations/perch/perch-quota.ts +123 -0
- package/src/providers/integrations/perch/perch-realm.ts +82 -0
- package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
- package/src/providers/integrations/perch/perch.ts +305 -0
- package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
- package/src/providers/integrations/qoder/chat-proto.ts +328 -328
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
- package/src/providers/integrations/qoder/qoder-adapter.ts +90 -40
- package/src/providers/integrations/qoder/qoder-auth.ts +101 -29
- package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +26 -6
- package/src/providers/integrations/qoder/qoder-chat.ts +7 -21
- package/src/providers/integrations/qoder/qoder-checkin.ts +6 -6
- package/src/providers/integrations/qoder/qoder-grpc.ts +210 -7
- package/src/providers/integrations/qoder/qoder-ide.ts +5 -108
- package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
- package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
- package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
- package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
- package/src/providers/integrations/qoder/qoder.ts +3 -7
- package/src/providers/integrations/trae/trae-catalog.ts +9 -14
- package/src/providers/integrations/trae/trae-headers.ts +36 -11
- package/src/providers/integrations/trae/trae-oauth.ts +14 -17
- package/src/providers/integrations/trae/trae-protocol.ts +12 -11
- package/src/providers/integrations/trae/trae-quota.ts +96 -36
- package/src/providers/integrations/trae/trae-realm.ts +74 -15
- package/src/providers/integrations/trae/trae.ts +99 -26
- package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
- package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
- package/src/providers/integrations/zro/zro.ts +204 -0
- package/src/providers/operations/account-error-classification.ts +235 -278
- package/src/providers/operations/account-health-service.ts +273 -26
- package/src/providers/operations/account-refresh-status.ts +269 -0
- package/src/providers/operations/account-tier.ts +66 -0
- package/src/providers/operations/client-version-resolver.ts +1 -1
- package/src/providers/operations/client-versions.ts +42 -150
- package/src/providers/operations/cooldown-rules.ts +56 -0
- package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
- package/src/providers/operations/provider-catalog-seeder.ts +4 -0
- package/src/providers/operations/provider-catalog-service.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +102 -32
- package/src/providers/operations/provider-rules/github.ts +48 -0
- package/src/providers/operations/provider-rules/grok.ts +33 -0
- package/src/providers/operations/provider-rules/inception.ts +49 -0
- package/src/providers/operations/provider-rules/index.ts +32 -0
- package/src/providers/operations/provider-rules/perch.ts +37 -0
- package/src/providers/operations/provider-rules/qoder.ts +70 -0
- package/src/providers/operations/provider-rules/trae.ts +81 -0
- package/src/providers/provider-contracts.ts +33 -2
- package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
- package/src/providers/provider-metadata.ts +99 -14
- package/src/providers/provider-model-definition.ts +36 -0
- package/src/providers/provider-probe.ts +15 -1
- package/src/providers/provider-registry.ts +42 -33
- package/src/providers/quota/quota-limit-windows.ts +16 -5
- package/src/providers/quota/quota-support.ts +1 -1
- package/src/providers/quota/quota-window-parser.ts +90 -18
- package/src/providers/quota/refresh.ts +96 -18
- package/src/security/abuse.ts +71 -0
- package/src/security/api-key-auth-cache.ts +1 -1
- package/src/security/api-key-auth.ts +6 -2
- package/src/transport/dispatch/attempt-finalize.ts +14 -13
- package/src/transport/dispatch/attempt-loop.ts +71 -17
- package/src/transport/dispatch/generation.ts +2 -8
- package/src/transport/dispatch/native-shared.ts +25 -0
- package/src/transport/dispatch/proxy-request.ts +9 -46
- package/src/transport/dispatch/responses-compact.ts +2 -8
- package/src/transport/dispatch/streaming/finalize.ts +9 -6
- package/src/transport/dispatch/streaming/stream.ts +9 -36
- package/src/transport/dispatch/systemone.ts +2 -8
- package/src/transport/dispatch/token-count.ts +2 -6
- package/src/transport/dispatch/upstream.ts +67 -1
- package/src/transport/drain-endpoint.ts +31 -4
- package/src/transport/failure-policy.ts +129 -37
- package/src/transport/guards/body-policy.ts +7 -1
- package/src/transport/guards/error-lifecycle.ts +13 -1
- package/src/transport/guards/request-context.ts +22 -14
- package/src/transport/request/preparer.ts +81 -71
- package/src/transport/routing/bounded-lru.ts +14 -0
- package/src/transport/routing/drain-reserve.ts +129 -0
- package/src/transport/routing/plan-gate.ts +7 -1
- package/src/transport/routing/round-robin.ts +60 -0
- package/src/transport/routing/route-catalog.ts +123 -111
- package/src/transport/routing/route-model.ts +51 -9
- package/src/transport/routing/router.ts +418 -33
- package/src/transport/surface/adapters.ts +11 -2
- package/src/transport/surface/chat/adapter.ts +2 -2
- package/src/transport/surface/chat/encode.ts +47 -4
- package/src/transport/surface/chat/parse.ts +2 -12
- package/src/transport/surface/completion.ts +44 -33
- package/src/transport/surface/content-parts.ts +21 -0
- package/src/transport/surface/messages/adapter.ts +9 -2
- package/src/transport/surface/messages/encode.ts +11 -3
- package/src/transport/surface/messages/parse.ts +2 -1
- package/src/transport/surface/messages/stream.ts +33 -17
- package/src/transport/surface/responses/encode.ts +34 -6
- package/src/transport/surface/responses/parse.ts +3 -11
- package/src/workers/daily-checkin.ts +1 -1
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/scheduler.ts +22 -4
- package/dist/dashboard/assets/BannedPage-DeIbIiBU.js +0 -1
- package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js +0 -1
- package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-BEsU8hKs.css +0 -2
- package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
- package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
- package/dist/dashboard/assets/brand/bansos_page.png +0 -0
- package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/favicon.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
- package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
- package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
- package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
- package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
- package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
- package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
- package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
- package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
- package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
- package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
- package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
- package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
- package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
- package/dist/dashboard/assets/switch-ZeTsW623.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/donation/qris-risun-marke.png +0 -0
- package/src/providers/bundled-discovery-providers.ts +0 -98
- package/src/providers/configured-openai-providers.ts +0 -120
- package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
- package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
- package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
- package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
- package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
- package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
- package/src/providers/operations/model-discovery-cache.ts +0 -34
- package/src/providers/operations/probe-fetch.ts +0 -15
- package/src/providers/operations/provider-version-cache.ts +0 -30
- package/src/providers/provider-capabilities.ts +0 -33
- package/src/providers/provider-credential-envelope.ts +0 -43
- /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-DIXLjojg.js} +0 -0
- /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-DIXLjojg.js.gz} +0 -0
|
@@ -11,6 +11,27 @@ import { GatewayError } from "../protocol/gateway-error";
|
|
|
11
11
|
import { fastPathname } from "../transport/pathname";
|
|
12
12
|
import { parseCookieValue, SESSION_COOKIE_NAME, isCsrfValid } from "../security/csrf";
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Console auth routes are excluded from CSRF and mutation-rate limiting: they
|
|
16
|
+
* run before a session exists or are the very requests that mint the session,
|
|
17
|
+
* so they must not require a CSRF token or count against the session's budget.
|
|
18
|
+
*/
|
|
19
|
+
const AUTH_ROUTE_SUFFIXES = ["/auth/login", "/auth/setup", "/auth/setup/restore", "/auth/session"] as const;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether `request` is a protected console mutation: a `/console/api/*` route
|
|
23
|
+
* in an unsafe method, excluding the auth-route suffixes. Shared by the CSRF
|
|
24
|
+
* and mutation-limiter guards so a change to the protected set lands in both.
|
|
25
|
+
*/
|
|
26
|
+
function isProtectedConsoleMutation(request: Request): boolean {
|
|
27
|
+
const path = fastPathname(request.url);
|
|
28
|
+
return (
|
|
29
|
+
path.startsWith("/console/api/") &&
|
|
30
|
+
["POST", "PUT", "PATCH", "DELETE"].includes(request.method) &&
|
|
31
|
+
!AUTH_ROUTE_SUFFIXES.some((suffix) => path.endsWith(suffix))
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
14
35
|
/**
|
|
15
36
|
* Stateless double-submit CSRF guard for unsafe `/console/api/*` mutations.
|
|
16
37
|
* Compares the `x-csrf-token` header against the readable `csrf_token`
|
|
@@ -19,16 +40,9 @@ import { parseCookieValue, SESSION_COOKIE_NAME, isCsrfValid } from "../security/
|
|
|
19
40
|
* store whose outage could masquerade as an auth failure.
|
|
20
41
|
*/
|
|
21
42
|
export function createConsoleCsrfMiddleware(): Elysia {
|
|
22
|
-
const excluded = ["/auth/login", "/auth/setup", "/auth/setup/restore", "/auth/session"];
|
|
23
43
|
return new Elysia()
|
|
24
44
|
.beforeHandle(async ({ request }) => {
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
!path.startsWith("/console/api/") ||
|
|
28
|
-
!["POST", "PUT", "PATCH", "DELETE"].includes(request.method) ||
|
|
29
|
-
excluded.some((suffix) => path.endsWith(suffix))
|
|
30
|
-
)
|
|
31
|
-
return;
|
|
45
|
+
if (!isProtectedConsoleMutation(request)) return;
|
|
32
46
|
const token = parseCookieValue(request, SESSION_COOKIE_NAME);
|
|
33
47
|
// No session cookie means no ambient authority to forge: logout and
|
|
34
48
|
// other mutations without a session fail authentication downstream.
|
|
@@ -75,16 +89,9 @@ function pruneConsoleMutationHits(now: number): void {
|
|
|
75
89
|
}
|
|
76
90
|
|
|
77
91
|
export function createConsoleMutationLimiterMiddleware(): Elysia {
|
|
78
|
-
const excluded = ["/auth/login", "/auth/setup", "/auth/setup/restore", "/auth/session"];
|
|
79
92
|
return new Elysia()
|
|
80
93
|
.beforeHandle(async ({ request, set }) => {
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
!path.startsWith("/console/api/") ||
|
|
84
|
-
!["POST", "PUT", "PATCH", "DELETE"].includes(request.method) ||
|
|
85
|
-
excluded.some((suffix) => path.endsWith(suffix))
|
|
86
|
-
)
|
|
87
|
-
return;
|
|
94
|
+
if (!isProtectedConsoleMutation(request)) return;
|
|
88
95
|
const key = consoleMutationKey(request);
|
|
89
96
|
// No session cookie means no ambient authority to abuse: the request
|
|
90
97
|
// fails authentication downstream. Only tracked sessions are throttled.
|
|
@@ -35,12 +35,18 @@ import type { ProviderRegistry } from "../providers/provider-registry";
|
|
|
35
35
|
import type { BundledProviderCatalog } from "../providers/operations/provider-catalog-service";
|
|
36
36
|
import type { ValidatedNetworkBindingFactory } from "../network/pool/resolver";
|
|
37
37
|
import type { TrustedProxyBoundary } from "../config/network";
|
|
38
|
+
import type { IpAbuseStore } from "../security/abuse";
|
|
38
39
|
import { gzipResponse } from "./response-compression";
|
|
39
40
|
import { beginConsoleActivity } from "../shared/invoke-guard";
|
|
40
41
|
|
|
41
42
|
export interface ConsoleApiCompositionDeps {
|
|
42
43
|
readonly db: CartethyiaDatabase;
|
|
43
44
|
readonly accessResolver: ConsoleAccessResolver;
|
|
45
|
+
/**
|
|
46
|
+
* The IP-abuse store backing manual IP bans and the live ban list. Absent
|
|
47
|
+
* (reduced compositions) means the IP-ban routes are not offered.
|
|
48
|
+
*/
|
|
49
|
+
readonly ipAbuseStore?: IpAbuseStore;
|
|
44
50
|
/**
|
|
45
51
|
* Drops the cached route snapshot after a console write changes the catalog.
|
|
46
52
|
* Structurally satisfied by `RouteSnapshotService`; declared here so the
|
|
@@ -222,6 +228,7 @@ export function createConsoleRouter(deps: ConsoleApiCompositionDeps): AnyElysia
|
|
|
222
228
|
telemetryBuffer,
|
|
223
229
|
...(deps.stateStore ? { stateStore: deps.stateStore } : {}),
|
|
224
230
|
...(deps.shareStore ? { shareStore: deps.shareStore } : {}),
|
|
231
|
+
...(deps.ipAbuseStore ? { ipAbuseStore: deps.ipAbuseStore } : {}),
|
|
225
232
|
providerRegistry: deps.providerRegistry,
|
|
226
233
|
bundledModelCatalog: deps.bundledModelCatalog,
|
|
227
234
|
networkBindingFactory: deps.networkBindingFactory,
|
|
@@ -62,13 +62,13 @@ export const SHARE_SOCIAL_META: SocialMeta = {
|
|
|
62
62
|
title: "Cartethyia — Bansos Token",
|
|
63
63
|
description:
|
|
64
64
|
"Come and save your tokens 💖 Shared Cartethyia API key — Bansos Token with live quota, usage and model allowlist.",
|
|
65
|
-
image: "/assets/brand/og_bansos.
|
|
66
|
-
imageType: "image/
|
|
65
|
+
image: "/assets/brand/og_bansos.webp",
|
|
66
|
+
imageType: "image/webp",
|
|
67
67
|
// Read from the file's own header, not carried over from a previous image:
|
|
68
68
|
// og:image:width/height are what a crawler uses to allocate the card before
|
|
69
69
|
// the bytes arrive, so a copied size makes every preview crop.
|
|
70
|
-
imageWidth:
|
|
71
|
-
imageHeight:
|
|
70
|
+
imageWidth: 1200,
|
|
71
|
+
imageHeight: 393,
|
|
72
72
|
imageAlt: "Cartethyia Bansos Token — Come and save your tokens",
|
|
73
73
|
};
|
|
74
74
|
|
|
@@ -405,7 +405,7 @@ function getMimeType(filePath: string): string {
|
|
|
405
405
|
* and the modern Vite spelling `app-BCnqn3N3.js` — plus `_`, and then required
|
|
406
406
|
* to *look generated*: the segment must contain a digit or mix upper and lower
|
|
407
407
|
* case. The second half is what keeps a plain word out of the set, since
|
|
408
|
-
* `console-backgrounds.
|
|
408
|
+
* `console-backgrounds.webp` and `openai-light.svg` both match the separator
|
|
409
409
|
* alone and are emphatically not immutable.
|
|
410
410
|
*
|
|
411
411
|
* Getting this wrong in either direction costs real money: a hashed asset
|
|
@@ -36,7 +36,8 @@ import { createLiveRoutes } from "./observability/live";
|
|
|
36
36
|
import { createLogRoutes } from "./observability/logs";
|
|
37
37
|
import { createAuditRoutes } from "./audit/routes";
|
|
38
38
|
import { DrizzleAuditReadStore } from "./audit/store";
|
|
39
|
-
import {
|
|
39
|
+
import { createIpBanRoutes } from "./ip-bans/routes";
|
|
40
|
+
import type { IpAbuseStore } from "../security/abuse";
|
|
40
41
|
import { createUpdateRoutes } from "./updates/routes";
|
|
41
42
|
import { createRemoteSupportRoutes } from "./remote-support/routes";
|
|
42
43
|
import { createRemoteSupportService } from "./remote-support/service";
|
|
@@ -55,6 +56,7 @@ import {
|
|
|
55
56
|
createAccountExportCredentialsResolver,
|
|
56
57
|
createAccountSecretResolver,
|
|
57
58
|
} from "../providers/operations/provider-credential-service";
|
|
59
|
+
import { refreshAccountStatus } from "../providers/operations/account-refresh-status";
|
|
58
60
|
import { syncByokProvider } from "../providers/operations/provider-catalog-service";
|
|
59
61
|
import { DrizzleApiKeyStore } from "../persistence/stores/api-key-store";
|
|
60
62
|
import {
|
|
@@ -82,6 +84,11 @@ import type { ConsoleAccessResolver } from "./auth/access";
|
|
|
82
84
|
export interface ConsoleDomainContext {
|
|
83
85
|
readonly db: CartethyiaDatabase;
|
|
84
86
|
readonly accessResolver: ConsoleAccessResolver;
|
|
87
|
+
/**
|
|
88
|
+
* The IP-abuse store backing manual IP bans and the live ban list. Absent
|
|
89
|
+
* (reduced compositions) means the IP-ban routes are not offered.
|
|
90
|
+
*/
|
|
91
|
+
readonly ipAbuseStore?: IpAbuseStore;
|
|
85
92
|
readonly providerRegistry: ProviderRegistry;
|
|
86
93
|
readonly bundledModelCatalog: BundledProviderCatalog;
|
|
87
94
|
readonly networkBindingFactory: ValidatedNetworkBindingFactory;
|
|
@@ -159,6 +166,17 @@ export function registerConsoleDomains(
|
|
|
159
166
|
resolveRefresher: (id) => ctx.providerRegistry.resolveRefresher(id),
|
|
160
167
|
refreshService: ctx.oauthRefreshService,
|
|
161
168
|
});
|
|
169
|
+
/** Manual OAuth token refresh for the catalog route (area 04). */
|
|
170
|
+
const refreshAccountStatusForConsole = (accountId: string, opts?: { readonly origin?: "ui" | "sweep" | "dispatch" }) =>
|
|
171
|
+
refreshAccountStatus(
|
|
172
|
+
{
|
|
173
|
+
db: ctx.db,
|
|
174
|
+
refreshService: ctx.oauthRefreshService,
|
|
175
|
+
resolveRefresher: (id) => ctx.providerRegistry.resolveRefresher(id),
|
|
176
|
+
},
|
|
177
|
+
accountId,
|
|
178
|
+
opts,
|
|
179
|
+
);
|
|
162
180
|
const providerCatalogStore = new DrizzleProviderCatalogStore(ctx.db, {
|
|
163
181
|
telemetryBuffer: ctx.telemetryBuffer,
|
|
164
182
|
bundledModelCatalog: ctx.bundledModelCatalog,
|
|
@@ -238,6 +256,7 @@ export function registerConsoleDomains(
|
|
|
238
256
|
};
|
|
239
257
|
},
|
|
240
258
|
snapshotInvalidator: ctx.routeSnapshotService,
|
|
259
|
+
redis: ctx.redis,
|
|
241
260
|
readAccountInflight: async (providerId, tenantId) =>
|
|
242
261
|
ctx.readRoutingAccountInflight ? ctx.readRoutingAccountInflight(providerId, tenantId) : [],
|
|
243
262
|
});
|
|
@@ -258,7 +277,6 @@ export function registerConsoleDomains(
|
|
|
258
277
|
...(ctx.stateStore ? { stateStore: ctx.stateStore } : {}),
|
|
259
278
|
}));
|
|
260
279
|
console.use(createLogRoutes({ accessResolver: ctx.accessResolver, auditSink: ctx.auditRecorder }));
|
|
261
|
-
console.use(createPerformanceRoutes({ accessResolver: ctx.accessResolver }));
|
|
262
280
|
console.use(createUpdateRoutes({
|
|
263
281
|
accessResolver: ctx.accessResolver,
|
|
264
282
|
observabilityStore,
|
|
@@ -271,6 +289,12 @@ export function registerConsoleDomains(
|
|
|
271
289
|
service: createRemoteSupportService({ port: resolvePort() }),
|
|
272
290
|
}));
|
|
273
291
|
console.use(createAuditRoutes({ store: auditReadStore, accessResolver: ctx.accessResolver }));
|
|
292
|
+
if (ctx.ipAbuseStore) {
|
|
293
|
+
console.use(createIpBanRoutes({
|
|
294
|
+
accessResolver: ctx.accessResolver,
|
|
295
|
+
store: ctx.ipAbuseStore,
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
274
298
|
// InferHub market data. On demand only: a route fetches when its snapshot is
|
|
275
299
|
// past its freshness window, so a page nobody opens causes no upstream calls.
|
|
276
300
|
const inferhubKeys = createInferhubKeyStore(ctx.db);
|
|
@@ -287,6 +311,7 @@ export function registerConsoleDomains(
|
|
|
287
311
|
redis: ctx.redis,
|
|
288
312
|
snapshotInvalidator: ctx.routeSnapshotService,
|
|
289
313
|
resolveExportCredentials,
|
|
314
|
+
refreshAccountStatus: refreshAccountStatusForConsole,
|
|
290
315
|
// Without this, a custom provider only becomes dispatchable after a
|
|
291
316
|
// restart: boot registers BYOK rows once, and the catalog routes are the
|
|
292
317
|
syncByokProvider: (providerId) =>
|
|
@@ -35,19 +35,12 @@ const FAMILY_STATES: readonly FamilyState[] = [
|
|
|
35
35
|
"low_traffic",
|
|
36
36
|
];
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const parsed = Number(value);
|
|
45
|
-
if (Number.isFinite(parsed)) return parsed;
|
|
46
|
-
}
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Reads a finite number, or null. Never coerces a missing metric to zero. */
|
|
38
|
+
/**
|
|
39
|
+
* Reads a finite number, or null. Never coerces a missing metric to zero.
|
|
40
|
+
*
|
|
41
|
+
* Catalog and budgets carry decimal strings; market carries numbers. Both
|
|
42
|
+
* are read here so one helper covers the wire's own inconsistency.
|
|
43
|
+
*/
|
|
51
44
|
export function numOrNull(value: unknown): number | null {
|
|
52
45
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
53
46
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
@@ -122,8 +115,8 @@ function pricePointsToTiers(
|
|
|
122
115
|
const seen = new Map<number, Tier>();
|
|
123
116
|
for (const point of points) {
|
|
124
117
|
if (!Array.isArray(point) || point.length < 2) continue;
|
|
125
|
-
const price =
|
|
126
|
-
const avail =
|
|
118
|
+
const price = numOrNull(point[0]);
|
|
119
|
+
const avail = numOrNull(point[1]);
|
|
127
120
|
if (price === null || avail === null) continue;
|
|
128
121
|
const count = Math.max(0, Math.trunc(avail));
|
|
129
122
|
const key = Math.round(price * 10 ** PRICE_PRECISION) / 10 ** PRICE_PRECISION;
|
|
@@ -208,8 +201,8 @@ export function normalizeCatalogModel(
|
|
|
208
201
|
const prefix = str(upstream.prefix) ?? "";
|
|
209
202
|
if (modelSlug === null || prefix.length === 0) return null;
|
|
210
203
|
const id = `${prefix}/${modelSlug}`;
|
|
211
|
-
const officialIn =
|
|
212
|
-
const officialOut =
|
|
204
|
+
const officialIn = numOrNull(raw.officialIn);
|
|
205
|
+
const officialOut = numOrNull(raw.officialOut);
|
|
213
206
|
const tiersIn = pricePointsToTiers(raw.pricePointsIn, officialIn);
|
|
214
207
|
const tiersOut = pricePointsToTiers(raw.pricePointsOut, officialOut);
|
|
215
208
|
// Live providers is the capacity behind the cheapest tier, not a row count:
|
|
@@ -234,10 +227,13 @@ export function normalizeCatalogModel(
|
|
|
234
227
|
minAskOut: tiersOut[0]?.price ?? null,
|
|
235
228
|
maxAskIn: tiersIn.at(-1)?.price ?? null,
|
|
236
229
|
maxAskOut: tiersOut.at(-1)?.price ?? null,
|
|
230
|
+
// Computed by the route layer before sending; starts null in internal models.
|
|
231
|
+
capacityPriceIn: null,
|
|
232
|
+
capacityPriceOut: null,
|
|
237
233
|
providersLive,
|
|
238
234
|
// Not carried by the catalog; merged from `/api/market` by the store.
|
|
239
235
|
lastRate: null,
|
|
240
|
-
cacheHitRate:
|
|
236
|
+
cacheHitRate: numOrNull(raw.cacheHitRate),
|
|
241
237
|
health: {
|
|
242
238
|
successRatePct: null,
|
|
243
239
|
avgTtftMs: null,
|
|
@@ -245,8 +241,8 @@ export function normalizeCatalogModel(
|
|
|
245
241
|
familyState: null,
|
|
246
242
|
},
|
|
247
243
|
capabilities: {
|
|
248
|
-
contextWindow:
|
|
249
|
-
maxOutputTokens:
|
|
244
|
+
contextWindow: numOrNull(raw.contextWindow),
|
|
245
|
+
maxOutputTokens: numOrNull(raw.maxOutputTokens),
|
|
250
246
|
modalities: stringArray(raw.modalities),
|
|
251
247
|
outputModality:
|
|
252
248
|
outputModality === "image" || outputModality === "video" ? outputModality : "text",
|
|
@@ -274,13 +270,13 @@ export function mergeMarketPrices(
|
|
|
274
270
|
if (entry === undefined) return model;
|
|
275
271
|
return {
|
|
276
272
|
...model,
|
|
277
|
-
lastRate:
|
|
273
|
+
lastRate: numOrNull(entry.lastRate),
|
|
278
274
|
// Market is the cheaper source for the extremes, but the catalog's tier
|
|
279
275
|
// grouping is what gives them capacity, so only the nulls are filled.
|
|
280
|
-
minAskIn: model.minAskIn ??
|
|
281
|
-
minAskOut: model.minAskOut ??
|
|
282
|
-
maxAskIn: model.maxAskIn ??
|
|
283
|
-
maxAskOut: model.maxAskOut ??
|
|
276
|
+
minAskIn: model.minAskIn ?? numOrNull(entry.minAskIn),
|
|
277
|
+
minAskOut: model.minAskOut ?? numOrNull(entry.minAskOut),
|
|
278
|
+
maxAskIn: model.maxAskIn ?? numOrNull(entry.maxAskIn),
|
|
279
|
+
maxAskOut: model.maxAskOut ?? numOrNull(entry.maxAskOut),
|
|
284
280
|
};
|
|
285
281
|
});
|
|
286
282
|
}
|
|
@@ -306,9 +302,9 @@ export function mergeModelHealth(
|
|
|
306
302
|
...model,
|
|
307
303
|
health: {
|
|
308
304
|
...model.health,
|
|
309
|
-
successRatePct:
|
|
310
|
-
avgTtftMs:
|
|
311
|
-
avgTps:
|
|
305
|
+
successRatePct: numOrNull(entry.successRatePct),
|
|
306
|
+
avgTtftMs: numOrNull(entry.avgTtftMs),
|
|
307
|
+
avgTps: numOrNull(entry.avgTps),
|
|
312
308
|
},
|
|
313
309
|
};
|
|
314
310
|
});
|
|
@@ -61,6 +61,9 @@ export interface ModelMarket {
|
|
|
61
61
|
readonly minAskOut: number | null;
|
|
62
62
|
readonly maxAskIn: number | null;
|
|
63
63
|
readonly maxAskOut: number | null;
|
|
64
|
+
/** Capacity-weighted price computed by the route layer; null when no tiers. */
|
|
65
|
+
readonly capacityPriceIn: number | null;
|
|
66
|
+
readonly capacityPriceOut: number | null;
|
|
64
67
|
/** Zero means no live provider — listed, but never routed to. */
|
|
65
68
|
readonly providersLive: number;
|
|
66
69
|
readonly lastRate: number | null;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// IP-ban domain: manual IP bans visible in the Banned Users modal.
|
|
2
|
+
//
|
|
3
|
+
// The gateway's abuse store keeps automatic bans (escalation after a burst).
|
|
4
|
+
// This domain exposes the same store to the operator so an address can be
|
|
5
|
+
// banned BY HAND and listed/unbanned from the console. IP bans are instance
|
|
6
|
+
// state, not tenant data, so every route is guarded by `platform:admin` like
|
|
7
|
+
// the other instance-level operations.
|
|
8
|
+
|
|
9
|
+
import { Elysia, t, type AnyElysia } from "elysia";
|
|
10
|
+
import { errorResponse, requireGlobalAdmin } from "../shared/errors";
|
|
11
|
+
import type { IpAbuseStore } from "../../security/abuse";
|
|
12
|
+
import type { ConsoleAccessResolver } from "../auth/access";
|
|
13
|
+
import { BAN_DURATION_CHOICES } from "../api-keys/ban-constants";
|
|
14
|
+
|
|
15
|
+
/** One active IP ban, as the console returns it. */
|
|
16
|
+
export interface IpBanResponse {
|
|
17
|
+
/** The banned address. */
|
|
18
|
+
readonly ip: string;
|
|
19
|
+
/** When the ban lifts, as ISO timestamp; null = permanent. */
|
|
20
|
+
readonly bannedUntil: string | null;
|
|
21
|
+
/** True when the ban was set by hand rather than by abuse escalation. */
|
|
22
|
+
readonly manual: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface IpBanRoutesConfig {
|
|
26
|
+
readonly accessResolver: ConsoleAccessResolver;
|
|
27
|
+
readonly store: IpAbuseStore;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Parses a duration choice into minutes; null = permanent; undefined = invalid. */
|
|
31
|
+
function parseBanDuration(value: unknown): number | null | undefined {
|
|
32
|
+
if (value === null || value === undefined) return value === null ? null : undefined;
|
|
33
|
+
const minutes = Number(value);
|
|
34
|
+
if (!Number.isFinite(minutes) || minutes <= 0) return undefined;
|
|
35
|
+
return Math.round(minutes);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createIpBanRoutes(config: IpBanRoutesConfig): AnyElysia {
|
|
39
|
+
const { accessResolver, store } = config;
|
|
40
|
+
return new Elysia()
|
|
41
|
+
.get("/ip-bans", async ({ request, set }) => {
|
|
42
|
+
try {
|
|
43
|
+
requireGlobalAdmin(accessResolver(request));
|
|
44
|
+
const bans = await store.listBans();
|
|
45
|
+
return bans.map((ban) => ({
|
|
46
|
+
ip: ban.identity,
|
|
47
|
+
bannedUntil: ban.bannedUntil === null ? null : new Date(ban.bannedUntil).toISOString(),
|
|
48
|
+
manual: true,
|
|
49
|
+
}));
|
|
50
|
+
} catch (e) {
|
|
51
|
+
return errorResponse(e, set, "Could not list IP bans");
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.post(
|
|
55
|
+
"/ip-bans",
|
|
56
|
+
{ body: t.Object({ ip: t.String(), minutes: t.Optional(t.Nullable(t.Number())) }) },
|
|
57
|
+
async ({ request, body, set }) => {
|
|
58
|
+
try {
|
|
59
|
+
requireGlobalAdmin(accessResolver(request));
|
|
60
|
+
const ip = body.ip.trim();
|
|
61
|
+
if (ip.length === 0)
|
|
62
|
+
return errorResponse(new Error("ip is required"), set, "Invalid IP ban request");
|
|
63
|
+
// Absent is permanent (the default), exactly like an explicit null;
|
|
64
|
+
// parseBanDuration maps those to null and rejects anything else.
|
|
65
|
+
const minutes = body.minutes === undefined ? null : parseBanDuration(body.minutes);
|
|
66
|
+
if (minutes === undefined)
|
|
67
|
+
return errorResponse(new Error("minutes must be a positive integer or null"), set, "Invalid IP ban request");
|
|
68
|
+
await store.ban(ip, minutes === null ? 0 : minutes * 60_000);
|
|
69
|
+
return {
|
|
70
|
+
ip,
|
|
71
|
+
bannedUntil: minutes === null ? null : new Date(Date.now() + minutes * 60_000).toISOString(),
|
|
72
|
+
manual: true,
|
|
73
|
+
};
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return errorResponse(e, set, "Could not ban IP");
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
)
|
|
79
|
+
.delete("/ip-bans/:ip", async ({ request, params, set }) => {
|
|
80
|
+
try {
|
|
81
|
+
requireGlobalAdmin(accessResolver(request));
|
|
82
|
+
const ip = typeof params?.["ip"] === "string" ? decodeURIComponent(params["ip"]) : "";
|
|
83
|
+
if (ip.length === 0)
|
|
84
|
+
return errorResponse(new Error("ip is required"), set, "Invalid unban request");
|
|
85
|
+
await store.unban(ip);
|
|
86
|
+
return { success: true, ip };
|
|
87
|
+
} catch (e) {
|
|
88
|
+
return errorResponse(e, set, "Could not unban IP");
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { BAN_DURATION_CHOICES };
|
|
@@ -196,59 +196,6 @@ const TOP_N = 50;
|
|
|
196
196
|
*/
|
|
197
197
|
const USAGE_READ_THROUGH_TTL_SECONDS = 30;
|
|
198
198
|
|
|
199
|
-
/**
|
|
200
|
-
* Nearest-rank percentile over a sorted array — kept for the JS-side
|
|
201
|
-
* `summarizeUsagePerformance` (still used by callers that already hold a
|
|
202
|
-
* bounded event list). The usage endpoints read percentiles from SQL via
|
|
203
|
-
* `summarizeUsagePerformanceSql`, which is what the dashboard polls.
|
|
204
|
-
*/
|
|
205
|
-
function percentile(sorted: readonly number[], fraction: number): number {
|
|
206
|
-
if (sorted.length === 0) return 0;
|
|
207
|
-
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(fraction * sorted.length) - 1));
|
|
208
|
-
return Math.round(sorted[index] ?? 0);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
interface UsagePerformanceSummary {
|
|
212
|
-
avgResolveMs: number;
|
|
213
|
-
avgDurationMs: number;
|
|
214
|
-
latencyP50Ms: number;
|
|
215
|
-
latencyP95Ms: number;
|
|
216
|
-
latencyP99Ms: number;
|
|
217
|
-
/** Events each series was computed from; see `UsageSummaryTotals`. */
|
|
218
|
-
latencySampleCount: number;
|
|
219
|
-
resolveSampleCount: number;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** Summarizes routing delay and request latency for an already-filtered period. */
|
|
223
|
-
export function summarizeUsagePerformance(
|
|
224
|
-
events: readonly Pick<TelemetryEventRecord, "latencyMs" | "resolveMs">[],
|
|
225
|
-
): UsagePerformanceSummary {
|
|
226
|
-
const latencies: number[] = [];
|
|
227
|
-
let latencySum = 0;
|
|
228
|
-
let resolveSum = 0;
|
|
229
|
-
let resolveCount = 0;
|
|
230
|
-
for (const event of events) {
|
|
231
|
-
if (event.latencyMs !== null) {
|
|
232
|
-
latencies.push(event.latencyMs);
|
|
233
|
-
latencySum += event.latencyMs;
|
|
234
|
-
}
|
|
235
|
-
if (event.resolveMs !== null) {
|
|
236
|
-
resolveSum += event.resolveMs;
|
|
237
|
-
resolveCount += 1;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
latencies.sort((a, b) => a - b);
|
|
241
|
-
return {
|
|
242
|
-
avgResolveMs: resolveCount > 0 ? resolveSum / resolveCount : 0,
|
|
243
|
-
avgDurationMs: latencies.length > 0 ? latencySum / latencies.length : 0,
|
|
244
|
-
latencyP50Ms: percentile(latencies, 0.5),
|
|
245
|
-
latencyP95Ms: percentile(latencies, 0.95),
|
|
246
|
-
latencyP99Ms: percentile(latencies, 0.99),
|
|
247
|
-
latencySampleCount: latencies.length,
|
|
248
|
-
resolveSampleCount: resolveCount,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
199
|
/**
|
|
253
200
|
* The console dimension as the SQL reader's grouping key.
|
|
254
201
|
*
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { isBundledProviderId } from "../../../providers/provider-contracts";
|
|
12
12
|
import { BUILTIN_DEFAULT_ENDPOINTS } from "../../../protocol/codecs";
|
|
13
13
|
|
|
14
|
-
import { providerDisplayName, providerServiceKinds } from "../../../providers/provider-metadata";
|
|
14
|
+
import { providerDisplayName, providerServiceKinds, customProviderServiceKinds } from "../../../providers/provider-metadata";
|
|
15
15
|
import type { CompatibilityProfile } from "../../../providers/provider-metadata";
|
|
16
16
|
import { supportedWireFamiliesForProvider } from "../../../providers/discovery/probe-wire";
|
|
17
17
|
import { providers } from "../../../persistence/schema";
|
|
@@ -74,6 +74,7 @@ export function mapProviderRow(
|
|
|
74
74
|
row.wireFamilyDefault,
|
|
75
75
|
row.compatibilityProfile,
|
|
76
76
|
);
|
|
77
|
+
const compatibilityProfile = row.compatibilityProfile as CompatibilityProfile | null;
|
|
77
78
|
return {
|
|
78
79
|
providerId: row.id,
|
|
79
80
|
displayName: providerDisplayName(row.id),
|
|
@@ -82,7 +83,9 @@ export function mapProviderRow(
|
|
|
82
83
|
isBuiltIn: isBundledProviderId(row.id),
|
|
83
84
|
configured: false,
|
|
84
85
|
requiresAccount: row.requiresAccount,
|
|
85
|
-
serviceKinds:
|
|
86
|
+
serviceKinds: isBundledProviderId(row.id)
|
|
87
|
+
? providerServiceKinds(row.id)
|
|
88
|
+
: customProviderServiceKinds(compatibilityProfile),
|
|
86
89
|
// No model advertises search; see the note in provider-metadata.ts.
|
|
87
90
|
supportsWebSearch: false,
|
|
88
91
|
supportsModelDiscovery:
|