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
|
@@ -32,6 +32,14 @@ export interface CompatibilityProfile {
|
|
|
32
32
|
* bundled specs declare it on `ApiKeyProviderSpec.gatewayUserAgent`.
|
|
33
33
|
*/
|
|
34
34
|
readonly gateway_user_agent?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Which service surfaces this custom provider may serve. Absent means every
|
|
37
|
+
* kind is enabled (the historical default). The operator narrows this by
|
|
38
|
+
* checking only the capabilities their upstream actually answers — agentic
|
|
39
|
+
* (LLM) is always on when the list is present, and a kind the upstream
|
|
40
|
+
* cannot serve stays out of the provider's tab set.
|
|
41
|
+
*/
|
|
42
|
+
readonly service_kinds?: readonly ServiceKind[];
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
/** Identity and routing defaults for every builtin provider. */
|
|
@@ -144,6 +152,20 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
|
|
|
144
152
|
credentialUrl: "https://cursor.com",
|
|
145
153
|
credentialHint: "Sign in with Cursor or import a Cursor API key.",
|
|
146
154
|
},
|
|
155
|
+
{
|
|
156
|
+
id: "devin",
|
|
157
|
+
displayName: "Devin",
|
|
158
|
+
baseUrl: "https://server.codeium.com",
|
|
159
|
+
wireFamilyDefault: "chat",
|
|
160
|
+
requiresAccount: true,
|
|
161
|
+
bespokeWire: true,
|
|
162
|
+
hasAdapterUserAgent: true,
|
|
163
|
+
// Devin's upstream is a Connect-RPC protobuf service with its own framing;
|
|
164
|
+
// an egress proxy in front of it breaks the stream shape. Direct only.
|
|
165
|
+
defaultSkipProxy: true,
|
|
166
|
+
credentialUrl: "https://devin.ai",
|
|
167
|
+
credentialHint: "Sign in with Devin, Windsurf Enterprise, or import a Devin session token / Windsurf seat key.",
|
|
168
|
+
},
|
|
147
169
|
{ id: "kimi", displayName: "Kimi Code", baseUrl: "https://api.kimi.com/coding", credentialUrl: "https://platform.moonshot.ai/console/api-keys" },
|
|
148
170
|
{ id: "opencode", displayName: "OpenCode Free", baseUrl: "https://opencode.ai", requiresAccount: false, ipScopedRateLimit: true, hasAdapterUserAgent: true },
|
|
149
171
|
{ id: "opencode-zen", displayName: "OpenCode Zen", baseUrl: "https://opencode.ai", hasAdapterUserAgent: true, credentialUrl: "https://opencode.ai/auth" },
|
|
@@ -344,6 +366,7 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
|
|
|
344
366
|
// User-Agent. Letting the operator overwrite it would let them break the
|
|
345
367
|
// provider, so the console locks the control.
|
|
346
368
|
hasAdapterUserAgent: true,
|
|
369
|
+
credentialHint: "Paste a Nous Portal API key (portal.nousresearch.com → API keys), or sign in with the Nous Portal device code (OAuth) for the subscription route.",
|
|
347
370
|
},
|
|
348
371
|
{ id: "bai", displayName: "B.AI", baseUrl: "https://api.b.ai/v1", credentialUrl: "https://b.ai" },
|
|
349
372
|
{
|
|
@@ -367,8 +390,6 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
|
|
|
367
390
|
{ id: "cline", displayName: "Cline", baseUrl: "https://api.cline.bot/api/v1", hasAdapterUserAgent: true, defaultSkipProxy: true, credentialUrl: "https://app.cline.bot" },
|
|
368
391
|
{ id: "cb", displayName: "CodeBuddy", baseUrl: "https://www.codebuddy.ai/v2", hasAdapterUserAgent: true, serviceKinds: ["llm", "imageGeneration", "videoGeneration"], credentialUrl: "https://www.codebuddy.ai", credentialHint: "Sign in with the same CodeBuddy account you use in the desktop app." },
|
|
369
392
|
{ id: "cbcn", displayName: "CodeBuddy CN", baseUrl: "https://copilot.tencent.com/v2", hasAdapterUserAgent: true, credentialUrl: "https://copilot.tencent.com", credentialHint: "Sign in with the same CodeBuddy China account you use in the desktop app." },
|
|
370
|
-
{ id: "workbuddy", displayName: "WorkBuddy", baseUrl: "https://www.workbuddy.ai", hasAdapterUserAgent: true, serviceKinds: ["llm", "imageGeneration", "videoGeneration"], credentialUrl: "https://www.workbuddy.ai", credentialHint: "Sign in with the same WorkBuddy account you use in the desktop app." },
|
|
371
|
-
{ id: "workbuddycn", displayName: "WorkBuddy CN", baseUrl: "https://www.workbuddy.cn", hasAdapterUserAgent: true, serviceKinds: ["llm", "imageGeneration", "videoGeneration"], credentialUrl: "https://www.workbuddy.cn", credentialHint: "Sign in with the same WorkBuddy China account you use in the desktop app." },
|
|
372
393
|
{
|
|
373
394
|
// Kilo Code resells the OpenRouter catalog. The base URL carries the API's
|
|
374
395
|
// path prefix (`/api/openrouter`) and every model row carries only the wire
|
|
@@ -432,6 +453,22 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
|
|
|
432
453
|
credentialUrl: "https://www.trae.com.cn/",
|
|
433
454
|
credentialHint: "Paste a Trae CN account export (accessToken + refreshToken) or the raw access token from a Trae China account.",
|
|
434
455
|
},
|
|
456
|
+
{
|
|
457
|
+
id: "freepi",
|
|
458
|
+
displayName: "Free-pi",
|
|
459
|
+
baseUrl: "https://api.freepi.ai/v1/chat/completions",
|
|
460
|
+
defaultSkipProxy: true,
|
|
461
|
+
credentialUrl: "https://freepi.ai",
|
|
462
|
+
credentialHint: "Sign in with a GitHub device code (free-pi login), or paste a free-pi JWT issued by the backend.",
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
id: "perch",
|
|
466
|
+
displayName: "Perch",
|
|
467
|
+
baseUrl: "https://app.perchai.app",
|
|
468
|
+
defaultSkipProxy: true,
|
|
469
|
+
credentialUrl: "https://chat.perchai.app",
|
|
470
|
+
credentialHint: "Sign in with Google or GitHub (Perch account). Starter includes Roost Standard and Standard 1M.",
|
|
471
|
+
},
|
|
435
472
|
{ id: "ollamacloud", displayName: "Ollama Cloud", baseUrl: "https://ollama.com/v1", credentialUrl: "https://ollama.com/settings/keys" },
|
|
436
473
|
{ id: "gemini", displayName: "Google Gemini", baseUrl: "https://generativelanguage.googleapis.com/v1beta", serviceKinds: ["llm", "websearch", "imageGeneration"], credentialUrl: "https://aistudio.google.com/app/apikey" },
|
|
437
474
|
{ id: "xiaomipg", displayName: "Xiaomi MiMo (PAYG)", baseUrl: "https://api.xiaomimimo.com/v1", credentialUrl: "https://platform.xiaomimimo.com" },
|
|
@@ -453,6 +490,31 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
|
|
|
453
490
|
// rows are served by `/v1/search`, never by the canonical chat pipeline.
|
|
454
491
|
{ id: "exa", displayName: "Exa", baseUrl: "https://mcp.exa.ai", serviceKinds: ["websearch"], requiresAccount: false, credentialHint: "No API key. Search goes through the public MCP." },
|
|
455
492
|
{ id: "parallel", displayName: "Parallel", baseUrl: "https://search.parallel.ai", serviceKinds: ["websearch"], requiresAccount: false, credentialUrl: "https://platform.parallel.ai/settings?tab=api-keys", credentialHint: "Optional. Leave empty for the public MCP, or paste an API key for the higher-limit search API." },
|
|
493
|
+
{
|
|
494
|
+
// Freebuff (Codebuff Desktop). OAuth code flow → bearer token; every
|
|
495
|
+
// dispatch carries the desktop identity group + Ed25519 device signature
|
|
496
|
+
// (see documentation/research/freebuff/). The chat surface is
|
|
497
|
+
// OpenAI-compatible on `/api/v1/chat/completions` but requires a stateful
|
|
498
|
+
// session admission + agent-runs preflight — handled by the custom adapter.
|
|
499
|
+
id: "freebuff",
|
|
500
|
+
displayName: "Freebuff",
|
|
501
|
+
baseUrl: "https://www.codebuff.com",
|
|
502
|
+
hasAdapterUserAgent: true,
|
|
503
|
+
credentialUrl: "https://www.codebuff.com/login",
|
|
504
|
+
credentialHint: "Sign in with your Freebuff account (Desktop/CLI code flow).",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
// Zro (moonmath). Fast, private, multi-region inference for open models.
|
|
508
|
+
// Claude Code connects via the Messages wire with a plain bearer key; the
|
|
509
|
+
// provider's own Claude Code docs configure `ANTHROPIC_BASE_URL` +
|
|
510
|
+
// `ANTHROPIC_AUTH_TOKEN`. No quota endpoint exists — entitlement is the
|
|
511
|
+
// `/v1/models` list itself (a key only sees the models it may call).
|
|
512
|
+
id: "zro",
|
|
513
|
+
displayName: "Zro",
|
|
514
|
+
baseUrl: "https://zro.moonmath.ai",
|
|
515
|
+
credentialUrl: "https://zro.moonmath.ai/docs/key-controls",
|
|
516
|
+
credentialHint: "Create or copy an API key (sk-...) from the Zro key controls page (docs/key-controls).",
|
|
517
|
+
},
|
|
456
518
|
] as const;
|
|
457
519
|
|
|
458
520
|
/**
|
|
@@ -472,7 +534,7 @@ const RAW_BUNDLED_PROVIDER_METADATA = [
|
|
|
472
534
|
export type BundledProviderId = (typeof RAW_BUNDLED_PROVIDER_METADATA)[number]["id"];
|
|
473
535
|
|
|
474
536
|
/**
|
|
475
|
-
* The Buddy family: CodeBuddy
|
|
537
|
+
* The Buddy family: CodeBuddy and its China tenant.
|
|
476
538
|
*
|
|
477
539
|
* They share one billing and credit system, so several layers need to ask "is
|
|
478
540
|
* this a Buddy provider" — the health recorder, the request preparer, and the
|
|
@@ -480,7 +542,7 @@ export type BundledProviderId = (typeof RAW_BUNDLED_PROVIDER_METADATA)[number]["
|
|
|
480
542
|
* get added in one place and silently missed in the others. Declared here
|
|
481
543
|
* beside the identities it names; the layers keep their own *policies*.
|
|
482
544
|
*/
|
|
483
|
-
export const BUDDY_PROVIDER_IDS: ReadonlySet<string> = new Set(["cb", "cbcn"
|
|
545
|
+
export const BUDDY_PROVIDER_IDS: ReadonlySet<string> = new Set(["cb", "cbcn"]);
|
|
484
546
|
|
|
485
547
|
/**
|
|
486
548
|
* Per-provider JWT verification defaults for issued OAuth access tokens. An
|
|
@@ -580,6 +642,11 @@ const ADAPTER_USER_AGENT_PROVIDER_IDS: ReadonlySet<string> = new Set(
|
|
|
580
642
|
BUNDLED_PROVIDER_METADATA.filter((provider) => provider.hasAdapterUserAgent).map((provider) => provider.id),
|
|
581
643
|
);
|
|
582
644
|
|
|
645
|
+
/** Bundled metadata keyed by provider id for O(1) lookups. */
|
|
646
|
+
const BUNDLED_PROVIDER_BY_ID: ReadonlyMap<BundledProviderId, BundledProviderMetadata> = new Map(
|
|
647
|
+
BUNDLED_PROVIDER_METADATA.map((definition) => [definition.id, definition]),
|
|
648
|
+
);
|
|
649
|
+
|
|
583
650
|
/** Whether the bundled adapter provides its own upstream User-Agent. */
|
|
584
651
|
export function providerHasAdapterUserAgent(providerId: string): boolean {
|
|
585
652
|
return ADAPTER_USER_AGENT_PROVIDER_IDS.has(providerId);
|
|
@@ -591,7 +658,7 @@ export function providerHasAdapterUserAgent(providerId: string): boolean {
|
|
|
591
658
|
* minted inside a device/browser flow with no dedicated key page).
|
|
592
659
|
*/
|
|
593
660
|
export function providerCredentialUrl(providerId: string): string | undefined {
|
|
594
|
-
return
|
|
661
|
+
return BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)?.credentialUrl;
|
|
595
662
|
}
|
|
596
663
|
|
|
597
664
|
/**
|
|
@@ -599,18 +666,18 @@ export function providerCredentialUrl(providerId: string): string | undefined {
|
|
|
599
666
|
* `undefined` where the link alone is self-explanatory.
|
|
600
667
|
*/
|
|
601
668
|
export function providerCredentialHint(providerId: string): string | undefined {
|
|
602
|
-
return
|
|
669
|
+
return BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)?.credentialHint;
|
|
603
670
|
}
|
|
604
671
|
|
|
605
672
|
/** Presentation-only limitation for the provider detail page. */
|
|
606
673
|
export function providerOperatorNotice(providerId: string): string | undefined {
|
|
607
|
-
return
|
|
674
|
+
return BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)?.operatorNotice;
|
|
608
675
|
}
|
|
609
676
|
|
|
610
677
|
/** Canonical builtin display name; falls back to the provider id for unknowns. */
|
|
611
678
|
export function providerDisplayName(providerId: string): string {
|
|
612
679
|
return (
|
|
613
|
-
|
|
680
|
+
BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)?.displayName ??
|
|
614
681
|
providerId
|
|
615
682
|
);
|
|
616
683
|
}
|
|
@@ -642,7 +709,7 @@ export function providerDisplayName(providerId: string): string {
|
|
|
642
709
|
* gateway guessing it will not.
|
|
643
710
|
*/
|
|
644
711
|
export function providerServiceKinds(providerId: string): readonly ServiceKind[] {
|
|
645
|
-
const declared =
|
|
712
|
+
const declared = BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)
|
|
646
713
|
?.serviceKinds;
|
|
647
714
|
// Bundled providers are explicit: they say which surfaces they serve, and a
|
|
648
715
|
// bundled provider without the declaration serves LLM only.
|
|
@@ -650,10 +717,28 @@ export function providerServiceKinds(providerId: string): readonly ServiceKind[]
|
|
|
650
717
|
return SERVICE_KINDS;
|
|
651
718
|
}
|
|
652
719
|
|
|
720
|
+
/**
|
|
721
|
+
* Resolves the service kinds for a custom (BYOK) provider row.
|
|
722
|
+
*
|
|
723
|
+
* The operator may narrow the surface set via `compatibilityProfile.service_kinds`
|
|
724
|
+
* (the Advanced Capabilities checkboxes in the Add Custom Provider modal). Absent,
|
|
725
|
+
* every kind is allowed — the historical default. A present list never drops
|
|
726
|
+
* `llm` (agentic is the floor of a custom provider).
|
|
727
|
+
*/
|
|
728
|
+
export function customProviderServiceKinds(
|
|
729
|
+
compatibilityProfile: CompatibilityProfile | null | undefined,
|
|
730
|
+
): readonly ServiceKind[] {
|
|
731
|
+
const kinds = compatibilityProfile?.service_kinds;
|
|
732
|
+
if (kinds === undefined || kinds.length === 0) return SERVICE_KINDS;
|
|
733
|
+
const set = new Set<ServiceKind>(SERVICE_KINDS.filter((kind) => kinds.includes(kind)));
|
|
734
|
+
set.add("llm");
|
|
735
|
+
return SERVICE_KINDS.filter((kind) => set.has(kind));
|
|
736
|
+
}
|
|
737
|
+
|
|
653
738
|
|
|
654
739
|
/** Canonical builtin base URL used by adapters, discovery, and quota clients. */
|
|
655
740
|
export function providerBaseUrl(providerId: string): string {
|
|
656
|
-
const entry =
|
|
741
|
+
const entry = BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId);
|
|
657
742
|
if (!entry) throw new Error(`Unknown builtin provider "${providerId}"`);
|
|
658
743
|
return entry.baseUrl.replace(/\/+$/, "");
|
|
659
744
|
}
|
|
@@ -689,7 +774,7 @@ export const PROVIDER_COMPATIBILITY_PROFILES: Record<string, CompatibilityProfil
|
|
|
689
774
|
|
|
690
775
|
/** Default wire family for a builtin provider. */
|
|
691
776
|
export function providerDefaultWireFamily(providerId: string): WireFamily {
|
|
692
|
-
return
|
|
777
|
+
return BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)?.wireFamilyDefault ?? "chat";
|
|
693
778
|
}
|
|
694
779
|
|
|
695
780
|
/**
|
|
@@ -699,12 +784,12 @@ export function providerDefaultWireFamily(providerId: string): WireFamily {
|
|
|
699
784
|
* OpenAI-compatible adapter.
|
|
700
785
|
*/
|
|
701
786
|
export function providerUsesBespokeWire(providerId: string): boolean {
|
|
702
|
-
return
|
|
787
|
+
return BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)?.bespokeWire ?? false;
|
|
703
788
|
}
|
|
704
789
|
|
|
705
790
|
/** Whether a builtin provider requires a persisted account row. */
|
|
706
791
|
export function providerRequiresAccount(providerId: string): boolean {
|
|
707
|
-
return
|
|
792
|
+
return BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)?.requiresAccount ?? true;
|
|
708
793
|
}
|
|
709
794
|
|
|
710
795
|
/**
|
|
@@ -714,7 +799,7 @@ export function providerRequiresAccount(providerId: string): boolean {
|
|
|
714
799
|
*/
|
|
715
800
|
export function providerJwtVerification(providerId: string): ProviderJwtVerification {
|
|
716
801
|
return (
|
|
717
|
-
|
|
802
|
+
BUNDLED_PROVIDER_BY_ID.get(providerId as BundledProviderId)?.jwtVerification ?? {}
|
|
718
803
|
);
|
|
719
804
|
}
|
|
720
805
|
|
|
@@ -33,6 +33,37 @@ export const FREE_TIER_COST: ModelCostDefinition = {
|
|
|
33
33
|
pricing_model: "pay-per-use",
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Severity of one display-only model warning. `warn` is the default so a
|
|
38
|
+
* catalog that omits severity does not silently render a critical banner.
|
|
39
|
+
*/
|
|
40
|
+
export type ModelWarningSeverity = "info" | "warn" | "critical";
|
|
41
|
+
|
|
42
|
+
/** Severity values accepted on the wire, as a runtime tuple. */
|
|
43
|
+
export const MODEL_WARNING_SEVERITIES = ["info", "warn", "critical"] as const;
|
|
44
|
+
|
|
45
|
+
/** Longest accepted warning `kind` / `message`, in UTF-16 code units. */
|
|
46
|
+
export const MODEL_WARNING_KIND_MAX_LENGTH = 64;
|
|
47
|
+
export const MODEL_WARNING_MESSAGE_MAX_LENGTH = 500;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* One display-only warning attached to a model row. `kind` is an open string
|
|
51
|
+
* with three defined values (`fallback_model`, `cache_reliability`,
|
|
52
|
+
* `model_refusal`) so a new category needs no migration. Persisted as
|
|
53
|
+
* `{ warnings: ModelWarning[] }` in `models.warning_metadata`; never read by
|
|
54
|
+
* routing.
|
|
55
|
+
*/
|
|
56
|
+
export interface ModelWarning {
|
|
57
|
+
/** Stable machine-readable category; open vocabulary. */
|
|
58
|
+
readonly kind: string;
|
|
59
|
+
/** Operator-facing text. Provider-level claims use "may". */
|
|
60
|
+
readonly message: string;
|
|
61
|
+
/** Defaults to `warn` when absent. */
|
|
62
|
+
readonly severity?: ModelWarningSeverity;
|
|
63
|
+
/** ISO timestamp of the last edit, when known. */
|
|
64
|
+
readonly updatedAt?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
36
67
|
/** Canonical static metadata for one provider model. */
|
|
37
68
|
export interface ModelDefinition {
|
|
38
69
|
readonly modelId: string;
|
|
@@ -118,6 +149,8 @@ export interface ModelDefinition {
|
|
|
118
149
|
* turns a gap in our catalog into a claim about someone else's model.
|
|
119
150
|
*/
|
|
120
151
|
readonly limitsEstimated?: boolean;
|
|
152
|
+
/** Display-only warnings carried from a static catalog into the seeded row. */
|
|
153
|
+
readonly warnings?: readonly ModelWarning[];
|
|
121
154
|
}
|
|
122
155
|
|
|
123
156
|
/**
|
|
@@ -205,6 +238,8 @@ export interface ModelHelperRow {
|
|
|
205
238
|
readonly proOnly?: boolean;
|
|
206
239
|
/** Explicit wire dialect override for this row. See `ModelDefinition.dialect`. */
|
|
207
240
|
readonly dialect?: DialectId;
|
|
241
|
+
/** Display-only warnings to carry onto the built row. */
|
|
242
|
+
readonly warnings?: readonly ModelWarning[];
|
|
208
243
|
}
|
|
209
244
|
|
|
210
245
|
function defaultEndpoint(wireFamily: ModelDefinition["wireFamily"]): string {
|
|
@@ -309,6 +344,7 @@ export function defineModel(row: ModelHelperRow): ModelDefinition {
|
|
|
309
344
|
...(row.proOnly === undefined ? {} : { proOnly: row.proOnly }),
|
|
310
345
|
...(row.dialect === undefined ? {} : { dialect: row.dialect }),
|
|
311
346
|
...(limitsEstimated ? { limitsEstimated: true } : {}),
|
|
347
|
+
...(row.warnings === undefined ? {} : { warnings: row.warnings }),
|
|
312
348
|
};
|
|
313
349
|
}
|
|
314
350
|
|
|
@@ -16,7 +16,21 @@ import { resolveDialect } from "../protocol/translation/dialect/resolver";
|
|
|
16
16
|
import type { CanonicalRequest } from "../protocol/canonical-model";
|
|
17
17
|
import type { ProviderDispatchContext } from "./provider-contracts";
|
|
18
18
|
import { providerBaseUrl, providerRateLimitIsIpScoped } from "./provider-metadata";
|
|
19
|
-
import type { ProviderDispatchTarget, ProviderId } from "./provider-contracts";
|
|
19
|
+
import type { ProviderDispatchTarget, ProviderId, ValidatedOutboundFetch } from "./provider-contracts";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Probe fetch wrapper: merges `Request`-carried headers with per-call init
|
|
23
|
+
* headers before delegating to the validated fetch. Required wherever the
|
|
24
|
+
* fetch is handed to adapters as `outbound_fetch`, because adapters may call
|
|
25
|
+
* it with a `Request` whose headers would otherwise be dropped.
|
|
26
|
+
*/
|
|
27
|
+
export function createProbeFetch(fetcher: ValidatedOutboundFetch): ValidatedOutboundFetch {
|
|
28
|
+
return (input, init) => {
|
|
29
|
+
const headers = new Headers(input instanceof Request ? input.headers : undefined);
|
|
30
|
+
new Headers(init?.headers).forEach((value, name) => headers.set(name, value));
|
|
31
|
+
return fetcher(input, { ...init, headers });
|
|
32
|
+
};
|
|
33
|
+
}
|
|
20
34
|
|
|
21
35
|
|
|
22
36
|
/**
|
|
@@ -58,6 +58,36 @@ export interface ProviderAuthentication {
|
|
|
58
58
|
readonly refresher?: OAuthTokenRefresher;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/** Provider implementation fields composed with canonical metadata. */
|
|
62
|
+
export type ProviderModuleCapabilities = Partial<Omit<ProviderModule, "loadAdapter">> &
|
|
63
|
+
Pick<ProviderModule, "loadAdapter">;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Creates a lazy authentication loader from a dynamic import and client export.
|
|
67
|
+
* Most OAuth clients also refresh their own tokens.
|
|
68
|
+
*/
|
|
69
|
+
export function oauthCapability<M, K extends keyof M>(
|
|
70
|
+
loader: () => Promise<M>,
|
|
71
|
+
exportName: K,
|
|
72
|
+
options: { readonly withRefresher?: boolean } = {},
|
|
73
|
+
): () => Promise<ProviderAuthentication> {
|
|
74
|
+
return async () => {
|
|
75
|
+
const module = await loader();
|
|
76
|
+
const client = (module as Record<string, unknown>)[exportName as string] as OAuthLoginClient & { refresh: unknown };
|
|
77
|
+
return options.withRefresher
|
|
78
|
+
? ({ client, refresher: client } as ProviderAuthentication)
|
|
79
|
+
: ({ client } as ProviderAuthentication);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Creates a lazy quota collector loader from a dynamic import and export name. */
|
|
84
|
+
export function quotaCapability<M, K extends keyof M>(
|
|
85
|
+
loader: () => Promise<M>,
|
|
86
|
+
exportName: K,
|
|
87
|
+
): () => Promise<QuotaFetcher> {
|
|
88
|
+
return () => loader().then((module) => (module as Record<string, unknown>)[exportName as string] as QuotaFetcher);
|
|
89
|
+
}
|
|
90
|
+
|
|
61
91
|
/**
|
|
62
92
|
* Derives a registry registration from a fully populated provider module.
|
|
63
93
|
* Keeps the registration shape in one place so the default registry and any
|
|
@@ -95,12 +125,6 @@ function assertAdapterIdentity(expected: ProviderId, adapter: ProviderAdapter):
|
|
|
95
125
|
});
|
|
96
126
|
}
|
|
97
127
|
|
|
98
|
-
/** Result of loading the configured provider registrations. */
|
|
99
|
-
export interface ProviderRegistrySnapshot {
|
|
100
|
-
readonly revision: number;
|
|
101
|
-
readonly adapters: ReadonlyMap<ProviderId, ProviderAdapter>;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
128
|
/**
|
|
105
129
|
* Single provider-registration authority. Every registration loads on
|
|
106
130
|
* demand through its own loader; registrations that are not added are
|
|
@@ -116,7 +140,6 @@ export class ProviderRegistry {
|
|
|
116
140
|
#quotaLoading = new Map<ProviderId, Promise<QuotaFetcher>>();
|
|
117
141
|
#modelDiscovery = new Map<ProviderId, ProviderModelDiscovery>();
|
|
118
142
|
#modelDiscoveryLoading = new Map<ProviderId, Promise<ProviderModelDiscovery>>();
|
|
119
|
-
#revision = 0;
|
|
120
143
|
|
|
121
144
|
/** Adds one registration and rejects duplicate canonical IDs. */
|
|
122
145
|
register(registration: ProviderRegistration): void {
|
|
@@ -132,7 +155,6 @@ export class ProviderRegistry {
|
|
|
132
155
|
});
|
|
133
156
|
}
|
|
134
157
|
this.#registrations.set(providerId, registration);
|
|
135
|
-
this.#revision += 1;
|
|
136
158
|
}
|
|
137
159
|
/** Removes one registration and any cached adapter/auth/quota/discovery state. */
|
|
138
160
|
unregister(providerId: string): boolean {
|
|
@@ -142,7 +164,6 @@ export class ProviderRegistry {
|
|
|
142
164
|
this.#authentication.delete(normalized);
|
|
143
165
|
this.#quotaCollectors.delete(normalized);
|
|
144
166
|
this.#modelDiscovery.delete(normalized);
|
|
145
|
-
if (removed) this.#revision += 1;
|
|
146
167
|
return removed;
|
|
147
168
|
}
|
|
148
169
|
|
|
@@ -170,28 +191,25 @@ export class ProviderRegistry {
|
|
|
170
191
|
this.#authentication.delete(providerId);
|
|
171
192
|
this.#quotaCollectors.delete(providerId);
|
|
172
193
|
this.#modelDiscovery.delete(providerId);
|
|
173
|
-
this.#revision += 1;
|
|
174
194
|
return replaced;
|
|
175
195
|
}
|
|
176
196
|
|
|
177
|
-
/** Loads one registered provider, or `undefined` when it is not registered. */
|
|
178
|
-
async loadOne(providerId: string): Promise<ProviderAdapter | undefined> {
|
|
179
|
-
const normalized = parseProviderId(providerId);
|
|
180
|
-
const registration = this.#registrations.get(normalized);
|
|
181
|
-
if (registration === undefined) return undefined;
|
|
182
|
-
return this.#loadRegistration(normalized, registration);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
197
|
/**
|
|
186
198
|
* Resolves one adapter on demand, returning the cached instance when it has
|
|
187
|
-
* already loaded
|
|
188
|
-
*
|
|
189
|
-
*
|
|
199
|
+
* already loaded — unless `force` is set, which rebuilds the adapter from
|
|
200
|
+
* the registration (single-flight, and the fresh instance replaces the
|
|
201
|
+
* cache). This never evaluates a provider's module graph until that provider
|
|
202
|
+
* is actually dispatched to, so heavy bespoke adapters (protobuf-based
|
|
203
|
+
* providers) stay out of the startup path. `force` is used by the probe
|
|
204
|
+
* path, which wants the adapter rebuilt against the latest registration
|
|
205
|
+
* rather than a cached instance.
|
|
190
206
|
*/
|
|
191
|
-
async resolve(providerId: string): Promise<ProviderAdapter | undefined> {
|
|
207
|
+
async resolve(providerId: string, opts: { readonly force?: boolean } = {}): Promise<ProviderAdapter | undefined> {
|
|
192
208
|
const normalized = parseProviderId(providerId);
|
|
193
|
-
|
|
194
|
-
|
|
209
|
+
if (!opts.force) {
|
|
210
|
+
const cached = this.#loaded.get(normalized);
|
|
211
|
+
if (cached !== undefined) return cached;
|
|
212
|
+
}
|
|
195
213
|
const registration = this.#registrations.get(normalized);
|
|
196
214
|
if (registration === undefined) return undefined;
|
|
197
215
|
return this.#loadRegistration(normalized, registration);
|
|
@@ -306,7 +324,6 @@ export class ProviderRegistry {
|
|
|
306
324
|
metrics.proxy_provider_adapter_load_ms.observe(durationMs);
|
|
307
325
|
trackAdapterLoad(providerId, durationMs);
|
|
308
326
|
this.#loaded.set(providerId, adapter);
|
|
309
|
-
this.#revision += 1;
|
|
310
327
|
return adapter;
|
|
311
328
|
})().finally(() => {
|
|
312
329
|
this.#loading.delete(providerId);
|
|
@@ -315,14 +332,6 @@ export class ProviderRegistry {
|
|
|
315
332
|
return task;
|
|
316
333
|
}
|
|
317
334
|
|
|
318
|
-
/** Returns the immutable registry snapshot; disabled optional providers are absent. */
|
|
319
|
-
snapshot(): ProviderRegistrySnapshot {
|
|
320
|
-
return {
|
|
321
|
-
revision: this.#revision,
|
|
322
|
-
adapters: new Map(this.#loaded),
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
|
|
326
335
|
/** Returns registration metadata without loading optional code. */
|
|
327
336
|
registrations(): readonly ProviderRegistration[] {
|
|
328
337
|
return [...this.#registrations.values()];
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { ProviderQuotaWindow } from "./quota-contracts";
|
|
12
12
|
import { isoDate, number, percentWindow, record, text } from "./quota-contracts";
|
|
13
|
+
import { resolveWindowEmission } from "./quota-window-parser";
|
|
13
14
|
|
|
14
15
|
/** Where one provider's limit entries and their fields live. */
|
|
15
16
|
export interface LimitWindowShape {
|
|
@@ -77,14 +78,24 @@ export function parseLimitWindows(
|
|
|
77
78
|
const kind = text(readFirst(limit, shape.kindPaths)) ?? `window-${index + 1}`;
|
|
78
79
|
const percent = number(readFirst(limit, shape.percentPaths));
|
|
79
80
|
const reset = isoDate(readFirst(limit, shape.resetPaths));
|
|
80
|
-
|
|
81
|
+
const rawUsed = shape.usedPaths ? number(readFirst(limit, shape.usedPaths)) : null;
|
|
81
82
|
const limitValue = shape.limitPaths
|
|
82
83
|
? number(readFirst(limit, shape.limitPaths))
|
|
83
84
|
: null;
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
null
|
|
85
|
+
const { emit } = resolveWindowEmission({
|
|
86
|
+
explicitPercent: null,
|
|
87
|
+
usedPercent: percent,
|
|
88
|
+
remainingPercent: null,
|
|
89
|
+
used: rawUsed,
|
|
90
|
+
limit: limitValue,
|
|
91
|
+
reset,
|
|
92
|
+
deriveFromUsedLimit: false,
|
|
93
|
+
// The array walker has always emitted a reset-only window (percent null,
|
|
94
|
+
// reset present); only an entry with neither is dropped.
|
|
95
|
+
emitWithoutPercent: true,
|
|
96
|
+
});
|
|
97
|
+
if (!emit) continue;
|
|
98
|
+
const usedValue = rawUsed ?? shape.deriveUsed?.(limit, percent, limitValue) ?? null;
|
|
88
99
|
const label =
|
|
89
100
|
shape.labelFor?.(kind, limit) ?? shape.fallbackLabel?.(kind) ?? humanize(kind);
|
|
90
101
|
windows.push(percentWindow(kind, label, percent, reset, usedValue, limitValue, "independent"));
|
|
@@ -3,7 +3,7 @@ import { cleanError, unsupportedQuota } from "./quota-contracts";
|
|
|
3
3
|
import type { ProviderRegistry } from "../provider-registry";
|
|
4
4
|
import { parseProviderId } from "../provider-contracts";
|
|
5
5
|
import { providerBaseUrl } from "../provider-metadata";
|
|
6
|
-
import { createProbeFetch } from "../
|
|
6
|
+
import { createProbeFetch } from "../provider-probe";
|
|
7
7
|
/**
|
|
8
8
|
* Verifies an API key against the provider's OpenAI-compatible `/models`
|
|
9
9
|
* endpoint. For providers that expose no billing/quota surface, a reachable
|
|
@@ -98,6 +98,75 @@ function findReset(root: unknown, mapping: QuotaWindowMapping): string | null {
|
|
|
98
98
|
return mapping.resetFallback ?? null;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* The raw per-window candidates the shared emission core resolves.
|
|
103
|
+
*
|
|
104
|
+
* Both quota walkers (`parseQuotaWindows` and `parseLimitWindows`) feed their
|
|
105
|
+
* independently-read fields into one resolution so the percent fallback chain
|
|
106
|
+
* and the skip rule cannot drift between them.
|
|
107
|
+
*/
|
|
108
|
+
export interface WindowFieldCandidates {
|
|
109
|
+
/** Provider-preferred percent (e.g. a `derivePercent` result). */
|
|
110
|
+
readonly explicitPercent: number | null;
|
|
111
|
+
/** Raw used-percent read from the payload. */
|
|
112
|
+
readonly usedPercent: number | null;
|
|
113
|
+
/** Raw remaining-percent read from the payload. */
|
|
114
|
+
readonly remainingPercent: number | null;
|
|
115
|
+
/** Absolute used value. */
|
|
116
|
+
readonly used: number | null;
|
|
117
|
+
/** Absolute limit value. */
|
|
118
|
+
readonly limit: number | null;
|
|
119
|
+
/** Reset timestamp; required to emit a reset-only window. */
|
|
120
|
+
readonly reset: string | null;
|
|
121
|
+
/**
|
|
122
|
+
* Whether percent may be derived from absolute used/limit when no percentage
|
|
123
|
+
* was reported. Only `parseQuotaWindows` does this — `parseLimitWindows`
|
|
124
|
+
* reports percentages and resets the upstream gave it, never ones it
|
|
125
|
+
* invented, so Cline entries stay reset-only instead of gaining a derived
|
|
126
|
+
* percent.
|
|
127
|
+
*/
|
|
128
|
+
readonly deriveFromUsedLimit: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* When true, a reset-only window (percent null, reset present) is still
|
|
131
|
+
* emitted. When false/absent, an entry with neither percent nor reset is
|
|
132
|
+
* dropped — never rendered as 0% — and a reset-only window is dropped too.
|
|
133
|
+
*/
|
|
134
|
+
readonly emitWithoutPercent?: boolean;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Shared per-window field resolution: the percent fallback chain plus the
|
|
139
|
+
* "emit or skip" decision.
|
|
140
|
+
*
|
|
141
|
+
* Percent resolution order is exact: explicit percent, then used-percent, then
|
|
142
|
+
* remaining-percent inverted (100 − remaining), then derived from absolute
|
|
143
|
+
* used/limit (only when the limit is positive and `deriveFromUsedLimit` is
|
|
144
|
+
* set). The result is never coerced to 0: a missing percentage stays `null`,
|
|
145
|
+
* so a window can be omitted or rendered as a countdown depending on
|
|
146
|
+
* `emitWithoutPercent`.
|
|
147
|
+
*/
|
|
148
|
+
export function resolveWindowEmission(candidates: WindowFieldCandidates): {
|
|
149
|
+
readonly emit: boolean;
|
|
150
|
+
readonly percent: number | null;
|
|
151
|
+
} {
|
|
152
|
+
let percent = candidates.explicitPercent;
|
|
153
|
+
if (percent === null && candidates.usedPercent !== null) percent = candidates.usedPercent;
|
|
154
|
+
if (percent === null && candidates.remainingPercent !== null) {
|
|
155
|
+
percent = Math.max(0, 100 - candidates.remainingPercent);
|
|
156
|
+
}
|
|
157
|
+
if (
|
|
158
|
+
percent === null &&
|
|
159
|
+
candidates.deriveFromUsedLimit &&
|
|
160
|
+
candidates.used !== null &&
|
|
161
|
+
candidates.limit !== null &&
|
|
162
|
+
candidates.limit > 0
|
|
163
|
+
) {
|
|
164
|
+
percent = (candidates.used / candidates.limit) * 100;
|
|
165
|
+
}
|
|
166
|
+
const emit = percent !== null || (candidates.emitWithoutPercent === true && candidates.reset !== null);
|
|
167
|
+
return { emit, percent };
|
|
168
|
+
}
|
|
169
|
+
|
|
101
170
|
/** Parses a generic quota payload using the provided window mappings. */
|
|
102
171
|
export function parseQuotaWindows(
|
|
103
172
|
body: unknown,
|
|
@@ -110,27 +179,30 @@ export function parseQuotaWindows(
|
|
|
110
179
|
for (const mapping of windowMappings) {
|
|
111
180
|
const used = findNumber(payload, mapping.usedPaths);
|
|
112
181
|
const limit = findNumber(payload, mapping.limitPaths);
|
|
182
|
+
const reset = findReset(payload, mapping);
|
|
113
183
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
if (percent !== null && mapping.valueMultiplier !== undefined && mapping.valueMultiplier !== 0) {
|
|
127
|
-
percent = Math.min(100, Math.max(0, percent * mapping.valueMultiplier));
|
|
128
|
-
}
|
|
184
|
+
const { emit, percent } = resolveWindowEmission({
|
|
185
|
+
explicitPercent: mapping.derivePercent ? mapping.derivePercent(payload) : null,
|
|
186
|
+
usedPercent: findNumber(payload, mapping.usedPercentPaths),
|
|
187
|
+
remainingPercent: findNumber(payload, mapping.remainingPercentPaths),
|
|
188
|
+
used,
|
|
189
|
+
limit,
|
|
190
|
+
reset,
|
|
191
|
+
deriveFromUsedLimit: true,
|
|
192
|
+
...(mapping.emitWithoutPercent === undefined ? {} : { emitWithoutPercent: mapping.emitWithoutPercent }),
|
|
193
|
+
});
|
|
194
|
+
if (!emit) continue;
|
|
129
195
|
|
|
130
|
-
|
|
131
|
-
if (
|
|
196
|
+
let resolvedPercent = percent;
|
|
197
|
+
if (
|
|
198
|
+
resolvedPercent !== null &&
|
|
199
|
+
mapping.valueMultiplier !== undefined &&
|
|
200
|
+
mapping.valueMultiplier !== 0
|
|
201
|
+
) {
|
|
202
|
+
resolvedPercent = Math.min(100, Math.max(0, resolvedPercent * mapping.valueMultiplier));
|
|
203
|
+
}
|
|
132
204
|
|
|
133
|
-
windows.push(percentWindow(mapping.kind, mapping.label,
|
|
205
|
+
windows.push(percentWindow(mapping.kind, mapping.label, resolvedPercent, reset, used, limit, "independent"));
|
|
134
206
|
}
|
|
135
207
|
|
|
136
208
|
if (options.extraWindows) windows.push(...options.extraWindows(payload));
|