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
|
@@ -26,7 +26,8 @@ import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
|
26
26
|
import { sleep } from "../../shared/sleep";
|
|
27
27
|
import { providerAccounts, providerOauthStates } from "../../persistence/schema";
|
|
28
28
|
import { decryptCredentialToString, encryptCredential } from "../../security/crypto";
|
|
29
|
-
import { invalidateCredentialCache } from "../operations/provider-credential-service";
|
|
29
|
+
import { invalidateCredentialCache, loadAccountWithOauthState } from "../operations/provider-credential-service";
|
|
30
|
+
import { applyAccountVerdict } from "../operations/account-health-service";
|
|
30
31
|
import { resolveAccountDisabledRecoveryMs } from "../../config/cooldowns";
|
|
31
32
|
import { asJsonbRecord } from "../../shared/coerce";
|
|
32
33
|
import {
|
|
@@ -169,6 +170,22 @@ async function releaseLease(
|
|
|
169
170
|
);
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
/**
|
|
174
|
+
* D21 invariant guard: a refresh is not a health verdict, so mutating the
|
|
175
|
+
* account's `status` here would silently re-enable an account a refresh was
|
|
176
|
+
* never meant to resurrect. The `.set()` calls in `persistRefreshed` accept
|
|
177
|
+
* `status` at the type level (it is a column on `provider_accounts`), so the
|
|
178
|
+
* guard is a runtime assertion that fails loudly in development instead of
|
|
179
|
+
* letting a future edit change health state from the refresh path.
|
|
180
|
+
*/
|
|
181
|
+
function assertRefreshNeverMutatesStatus(set: Record<string, unknown>): void {
|
|
182
|
+
if ("status" in set) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
"persistRefreshed must never write `status`; a refresh is not a health verdict",
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
172
189
|
/** Persists a successful refresh and releases the lease, fenced on still holding it. */
|
|
173
190
|
async function persistRefreshed(
|
|
174
191
|
db: CartethyiaDatabase,
|
|
@@ -177,16 +194,21 @@ async function persistRefreshed(
|
|
|
177
194
|
result: OAuthTokenRefreshResult,
|
|
178
195
|
): Promise<boolean> {
|
|
179
196
|
return db.transaction(async (tx) => {
|
|
197
|
+
// This function must never write `status`; a refresh is not a health
|
|
198
|
+
// verdict. The guard below makes an accidental status write a loud dev
|
|
199
|
+
// failure rather than a silent re-enable.
|
|
200
|
+
const oauthSet = {
|
|
201
|
+
...(result.refresh === undefined
|
|
202
|
+
? {}
|
|
203
|
+
: { refreshCiphertext: encryptCredential(result.refresh) }),
|
|
204
|
+
expiresAt: result.expiresAt,
|
|
205
|
+
leaseOwner: null,
|
|
206
|
+
leaseExpiresAt: null,
|
|
207
|
+
};
|
|
208
|
+
assertRefreshNeverMutatesStatus(oauthSet);
|
|
180
209
|
const leaseRows = await tx
|
|
181
210
|
.update(providerOauthStates)
|
|
182
|
-
.set(
|
|
183
|
-
...(result.refresh === undefined
|
|
184
|
-
? {}
|
|
185
|
-
: { refreshCiphertext: encryptCredential(result.refresh) }),
|
|
186
|
-
expiresAt: result.expiresAt,
|
|
187
|
-
leaseOwner: null,
|
|
188
|
-
leaseExpiresAt: null,
|
|
189
|
-
})
|
|
211
|
+
.set(oauthSet)
|
|
190
212
|
.where(
|
|
191
213
|
and(
|
|
192
214
|
eq(providerOauthStates.providerAccountId, accountId),
|
|
@@ -195,23 +217,27 @@ async function persistRefreshed(
|
|
|
195
217
|
)
|
|
196
218
|
.returning({ id: providerOauthStates.providerAccountId });
|
|
197
219
|
if (leaseRows.length === 0) return false;
|
|
220
|
+
const accountSet = {
|
|
221
|
+
credentialCiphertext: encryptCredential(result.access),
|
|
222
|
+
// Only what the refresh reported: a refresher that learned nothing new
|
|
223
|
+
// leaves the stored configuration alone rather than blanking it.
|
|
224
|
+
// A learned identity only replaces a default label — an operator's
|
|
225
|
+
// custom rename is never overwritten. The CASE keeps the read and the
|
|
226
|
+
// write in one statement, so a concurrent rename cannot lose to the
|
|
227
|
+
// refresh's learned identity.
|
|
228
|
+
...(result.accountLabel === undefined
|
|
229
|
+
? {}
|
|
230
|
+
: {
|
|
231
|
+
label: sql`CASE WHEN lower(trim(${providerAccounts.label})) = lower(trim(${providerAccounts.providerId})) THEN ${result.accountLabel} ELSE ${providerAccounts.label} END`,
|
|
232
|
+
}),
|
|
233
|
+
...(result.auth_state === undefined ? {} : { authState: result.auth_state }),
|
|
234
|
+
};
|
|
235
|
+
// The second half of the D21 invariant: the credential write must never
|
|
236
|
+
// touch the account's health state either.
|
|
237
|
+
assertRefreshNeverMutatesStatus(accountSet);
|
|
198
238
|
await tx
|
|
199
239
|
.update(providerAccounts)
|
|
200
|
-
.set(
|
|
201
|
-
credentialCiphertext: encryptCredential(result.access),
|
|
202
|
-
// Only what the refresh reported: a refresher that learned nothing new
|
|
203
|
-
// leaves the stored configuration alone rather than blanking it.
|
|
204
|
-
// A learned identity only replaces a default label — an operator's
|
|
205
|
-
// custom rename is never overwritten. The CASE keeps the read and the
|
|
206
|
-
// write in one statement, so a concurrent rename cannot lose to the
|
|
207
|
-
// refresh's learned identity.
|
|
208
|
-
...(result.accountLabel === undefined
|
|
209
|
-
? {}
|
|
210
|
-
: {
|
|
211
|
-
label: sql`CASE WHEN lower(trim(${providerAccounts.label})) = lower(trim(${providerAccounts.providerId})) THEN ${result.accountLabel} ELSE ${providerAccounts.label} END`,
|
|
212
|
-
}),
|
|
213
|
-
...(result.auth_state === undefined ? {} : { authState: result.auth_state }),
|
|
214
|
-
})
|
|
240
|
+
.set(accountSet)
|
|
215
241
|
.where(eq(providerAccounts.id, accountId));
|
|
216
242
|
invalidateCredentialCache(accountId);
|
|
217
243
|
return true;
|
|
@@ -240,12 +266,21 @@ function buildRefreshContext(row: AccountRow, includeAccessToken: boolean): OAut
|
|
|
240
266
|
/**
|
|
241
267
|
* Marks an account permanently unusable after a definitive refresh failure,
|
|
242
268
|
* fenced on the lease.
|
|
269
|
+
*
|
|
270
|
+
* @param preserveOperatorDisable When true, an account that is already
|
|
271
|
+
* `disabled` with no `cooldownUntil` — the operator's own disable — keeps its
|
|
272
|
+
* lack of a recovery deadline (D23). A machine disable (this function being
|
|
273
|
+
* the machine) always writes a deadline for itself, but it must not convert
|
|
274
|
+
* an operator's terminal decision into a recoverable one: adding a deadline
|
|
275
|
+
* to an operator-disabled row is what made the Recover button appear on an
|
|
276
|
+
* account the operator disabled on purpose.
|
|
243
277
|
*/
|
|
244
278
|
async function disableAccount(
|
|
245
279
|
db: CartethyiaDatabase,
|
|
246
280
|
accountId: string,
|
|
247
281
|
owner: string,
|
|
248
282
|
message: string,
|
|
283
|
+
options: { readonly preserveOperatorDisable?: boolean } = {},
|
|
249
284
|
): Promise<void> {
|
|
250
285
|
await db.transaction(async (tx) => {
|
|
251
286
|
const leaseRows = await tx
|
|
@@ -260,30 +295,32 @@ async function disableAccount(
|
|
|
260
295
|
.returning({ id: providerOauthStates.providerAccountId });
|
|
261
296
|
if (leaseRows.length === 0) return;
|
|
262
297
|
const disabledRecoveryMs = resolveAccountDisabledRecoveryMs();
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
298
|
+
// Converge on the health application seam (proposal §3.5): a definitive
|
|
299
|
+
// OAuth refresh failure is a producer into `applyAccountVerdict`, not a
|
|
300
|
+
// separate state machine. The seam preserves D23 (operator-disable rows
|
|
301
|
+
// keep their `null` recovery deadline) via `preserveOperatorDisable`.
|
|
302
|
+
const applied = await applyAccountVerdict(tx, {
|
|
303
|
+
accountId,
|
|
304
|
+
status: "disabled",
|
|
305
|
+
// `auth_invalidated` is the category the console reads to render the
|
|
306
|
+
// "Re-login required" pill, and the one the quota sweep excludes so it
|
|
307
|
+
// stops re-probing a credential that cannot repair itself. The refresh
|
|
308
|
+
// grant was definitively rejected and the stored access token could not
|
|
309
|
+
// be shown to still work, so this is exactly that state.
|
|
310
|
+
reason: message,
|
|
311
|
+
category: "auth_invalidated",
|
|
312
|
+
// Carries a recovery deadline so `sweepExpiredCooldowns` can bring this
|
|
313
|
+
// account back once it elapses; without one, one definitive rejection
|
|
314
|
+
// parks the credential for good. The deadline is also what distinguishes
|
|
315
|
+
// this (machine-disabled) from an operator's own disable, which leaves
|
|
316
|
+
// `cooldownUntil` null and is therefore never swept (D23).
|
|
317
|
+
cooldownUntil:
|
|
318
|
+
disabledRecoveryMs > 0 ? new Date(Date.now() + disabledRecoveryMs) : null,
|
|
319
|
+
...(options.preserveOperatorDisable === true
|
|
320
|
+
? { preserveOperatorDisable: true }
|
|
321
|
+
: {}),
|
|
322
|
+
});
|
|
323
|
+
if (applied) invalidateCredentialCache(accountId);
|
|
287
324
|
});
|
|
288
325
|
}
|
|
289
326
|
|
|
@@ -299,20 +336,33 @@ async function disableAccount(
|
|
|
299
336
|
* operator or an explicit recovery may re-enable it), while an `active` one
|
|
300
337
|
* stays `active`. The static marker and the error clearing are orthogonal to
|
|
301
338
|
* the account's health state.
|
|
339
|
+
*
|
|
340
|
+
* D24: for a `disabled` row the error fields are deliberately kept. Once the
|
|
341
|
+
* account is parked, `lastErrorCategory` is the only record of *why* (e.g.
|
|
342
|
+
* `auth_invalidated` → "Re-login required"); wiping it on the static
|
|
343
|
+
* annotation would leave a disabled account with no diagnosable reason. An
|
|
344
|
+
* `active` row still clears them — there the error is stale by definition.
|
|
302
345
|
*/
|
|
303
346
|
async function markStaticToken(db: CartethyiaDatabase, accountId: string): Promise<void> {
|
|
347
|
+
const rows = await db
|
|
348
|
+
.select({ status: providerAccounts.status })
|
|
349
|
+
.from(providerAccounts)
|
|
350
|
+
.where(eq(providerAccounts.id, accountId))
|
|
351
|
+
.limit(1);
|
|
352
|
+
const isDisabled = rows[0]?.status === "disabled";
|
|
304
353
|
await db
|
|
305
354
|
.update(providerAccounts)
|
|
306
355
|
.set({
|
|
307
356
|
staticToken: true,
|
|
357
|
+
cooldownUntil: null,
|
|
308
358
|
// A static token is a normal, usable credential, not an error state: any
|
|
309
359
|
// error left by the failed refresh would otherwise keep the row reading
|
|
310
360
|
// "Re-login required" while it is in fact serving. Mirrors the console's
|
|
311
|
-
// static-token toggle.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
361
|
+
// static-token toggle. For a `disabled` row the category is preserved
|
|
362
|
+
// (D24), because there it is the diagnosis, not a stale artifact.
|
|
363
|
+
...(isDisabled
|
|
364
|
+
? {}
|
|
365
|
+
: { lastError: null, lastErrorCategory: null, lastErrorAt: null }),
|
|
316
366
|
})
|
|
317
367
|
.where(eq(providerAccounts.id, accountId));
|
|
318
368
|
}
|
|
@@ -362,7 +412,8 @@ function failureStatus(error: unknown): number | undefined {
|
|
|
362
412
|
* still perfectly usable. This pass re-reads those rows once at startup and
|
|
363
413
|
* moves each to where it now belongs:
|
|
364
414
|
*
|
|
365
|
-
* - access token still usable ⇒ static token
|
|
415
|
+
* - access token still usable ⇒ static token; the account stays `disabled`;
|
|
416
|
+
* only an operator can re-enable it;
|
|
366
417
|
* - otherwise ⇒ the `auth_invalidated` category the console renders as
|
|
367
418
|
* "Re-login required" and the quota sweep excludes.
|
|
368
419
|
*
|
|
@@ -495,15 +546,20 @@ export class OAuthRefreshService {
|
|
|
495
546
|
* that just refreshed cannot cause the caller to reuse a dead cached
|
|
496
547
|
* secret. Returns the current decrypted access token, or `null` when the
|
|
497
548
|
* account isn't OAuth, has no stored refresh token, or refresh failed.
|
|
549
|
+
*
|
|
550
|
+
* `opts.origin` labels who asked for the refresh (`ui`/`sweep`/`dispatch`)
|
|
551
|
+
* and is carried on the `token_refresh` log lines (D26). The origin is
|
|
552
|
+
* observational only — it never changes what a refresh does.
|
|
498
553
|
*/
|
|
499
554
|
async ensureFreshAccessToken(
|
|
500
555
|
accountId: string,
|
|
501
556
|
refresher: OAuthTokenRefresher,
|
|
502
|
-
opts: { force?: boolean; skewMs?: number } = {},
|
|
557
|
+
opts: { force?: boolean; skewMs?: number; origin?: "ui" | "sweep" | "dispatch" } = {},
|
|
503
558
|
): Promise<string | null> {
|
|
504
559
|
const force = opts.force === true;
|
|
505
560
|
// No explicit skew: the per-provider lead is applied at load time.
|
|
506
561
|
const skewMs = opts.skewMs;
|
|
562
|
+
const origin = opts.origin ?? "sweep";
|
|
507
563
|
// Forced refreshes get their own in-flight slot so they don't merge with
|
|
508
564
|
// a raced non-force call that would otherwise short-circuit.
|
|
509
565
|
const key = force ? `${accountId}:force` : accountId;
|
|
@@ -518,7 +574,7 @@ export class OAuthRefreshService {
|
|
|
518
574
|
const oldestKey = this.#inFlight.keys().next().value;
|
|
519
575
|
if (oldestKey !== undefined) this.#inFlight.delete(oldestKey);
|
|
520
576
|
}
|
|
521
|
-
const task = this.#refreshIfDue(accountId, refresher, skewMs, force).finally(() => {
|
|
577
|
+
const task = this.#refreshIfDue(accountId, refresher, skewMs, force, origin).finally(() => {
|
|
522
578
|
this.#inFlight.delete(key);
|
|
523
579
|
});
|
|
524
580
|
this.#inFlight.set(key, task);
|
|
@@ -530,6 +586,7 @@ export class OAuthRefreshService {
|
|
|
530
586
|
refresher: OAuthTokenRefresher,
|
|
531
587
|
skewMs: number | undefined,
|
|
532
588
|
force: boolean,
|
|
589
|
+
origin: "ui" | "sweep" | "dispatch",
|
|
533
590
|
): Promise<string | null> {
|
|
534
591
|
const account = await loadAccountWithFreshness(this.#db, accountId, skewMs);
|
|
535
592
|
if (!account || account.row.credentialKind !== "oauth") return null;
|
|
@@ -547,18 +604,24 @@ export class OAuthRefreshService {
|
|
|
547
604
|
? decryptCredentialToString(row.credentialCiphertext)
|
|
548
605
|
: null;
|
|
549
606
|
}
|
|
550
|
-
return this.#refreshNow(row, refresher, skewMs);
|
|
607
|
+
return this.#refreshNow(row, refresher, skewMs, origin);
|
|
551
608
|
}
|
|
552
609
|
|
|
553
610
|
async #refreshNow(
|
|
554
611
|
row: AccountRow,
|
|
555
612
|
refresher: OAuthTokenRefresher,
|
|
556
613
|
skewMs: number | undefined,
|
|
614
|
+
origin: "ui" | "sweep" | "dispatch",
|
|
557
615
|
): Promise<string | null> {
|
|
616
|
+
// D26: statusBefore is read once up front (the row projection used by the
|
|
617
|
+
// refresh path does not carry status) and statusAfter is re-read after any
|
|
618
|
+
// mutation, so the log proves a refresh never flipped the account.
|
|
619
|
+
const statusBefore = await readAccountStatus(this.#db, row.id);
|
|
558
620
|
pushStructuredConsoleLog("info", "OAuth token refresh started", {
|
|
559
621
|
event: "token_refresh",
|
|
560
622
|
accountId: row.id,
|
|
561
623
|
providerId: row.providerId,
|
|
624
|
+
details: { origin, statusBefore },
|
|
562
625
|
});
|
|
563
626
|
const owner = randomUUID();
|
|
564
627
|
const leased = await acquireLease(this.#db, row.id, owner);
|
|
@@ -583,6 +646,7 @@ export class OAuthRefreshService {
|
|
|
583
646
|
accountId: row.id,
|
|
584
647
|
providerId: row.providerId,
|
|
585
648
|
errorCode: "static_token",
|
|
649
|
+
details: { origin },
|
|
586
650
|
});
|
|
587
651
|
return null;
|
|
588
652
|
}
|
|
@@ -594,12 +658,20 @@ export class OAuthRefreshService {
|
|
|
594
658
|
buildRefreshContext(row, refresher.requiresAccessToken === true),
|
|
595
659
|
);
|
|
596
660
|
const persisted = await persistRefreshed(this.#db, row.id, owner, result);
|
|
661
|
+
// D26: `statusAfter` is re-read from the DB rather than assumed — a
|
|
662
|
+
// successful refresh never changes `status`, and the log proves it.
|
|
663
|
+
const statusAfter = await readAccountStatus(this.#db, row.id);
|
|
597
664
|
pushStructuredConsoleLog(persisted ? "info" : "warn", persisted ? "OAuth token refresh completed" : "OAuth token refresh lost lease", {
|
|
598
665
|
event: "token_refresh",
|
|
599
666
|
accountId: row.id,
|
|
600
667
|
providerId: row.providerId,
|
|
601
668
|
...(persisted ? {} : { errorCode: "refresh_lease_lost" }),
|
|
602
|
-
details: {
|
|
669
|
+
details: {
|
|
670
|
+
expiresAt: result.expiresAt.toISOString(),
|
|
671
|
+
origin,
|
|
672
|
+
statusBefore,
|
|
673
|
+
statusAfter,
|
|
674
|
+
},
|
|
603
675
|
});
|
|
604
676
|
return persisted ? result.access : null;
|
|
605
677
|
} catch (error) {
|
|
@@ -612,6 +684,7 @@ export class OAuthRefreshService {
|
|
|
612
684
|
accountId: row.id,
|
|
613
685
|
providerId: row.providerId,
|
|
614
686
|
errorCode: classification,
|
|
687
|
+
details: { origin, statusBefore },
|
|
615
688
|
});
|
|
616
689
|
if (classification === "definitive") {
|
|
617
690
|
// A definitive refresh failure means the refresh grant is dead — but
|
|
@@ -627,14 +700,29 @@ export class OAuthRefreshService {
|
|
|
627
700
|
if (accessUsability === "usable") {
|
|
628
701
|
await releaseLease(this.#db, row.id, owner);
|
|
629
702
|
await markStaticToken(this.#db, row.id);
|
|
703
|
+
const statusAfter = await readAccountStatus(this.#db, row.id);
|
|
630
704
|
pushStructuredConsoleLog("info", "OAuth refresh failed; access token still valid, marking static", {
|
|
631
705
|
event: "token_refresh",
|
|
632
706
|
accountId: row.id,
|
|
633
707
|
providerId: row.providerId,
|
|
634
708
|
errorCode: "static_token",
|
|
709
|
+
details: { origin, statusBefore, statusAfter },
|
|
635
710
|
});
|
|
636
711
|
} else {
|
|
637
|
-
await disableAccount(this.#db, row.id, owner, failureMessage(error)
|
|
712
|
+
await disableAccount(this.#db, row.id, owner, failureMessage(error), {
|
|
713
|
+
// D23: a definitive refresh failure must not turn an operator's
|
|
714
|
+
// disable into a machine-recoverable one. If the row is already
|
|
715
|
+
// `disabled` without a deadline, leave it that way.
|
|
716
|
+
preserveOperatorDisable: true,
|
|
717
|
+
});
|
|
718
|
+
const statusAfter = await readAccountStatus(this.#db, row.id);
|
|
719
|
+
pushStructuredConsoleLog("warn", "OAuth refresh failed definitively; account disabled for re-auth", {
|
|
720
|
+
event: "token_refresh",
|
|
721
|
+
accountId: row.id,
|
|
722
|
+
providerId: row.providerId,
|
|
723
|
+
errorCode: classification,
|
|
724
|
+
details: { origin, statusBefore, statusAfter },
|
|
725
|
+
});
|
|
638
726
|
}
|
|
639
727
|
} else {
|
|
640
728
|
await releaseLease(this.#db, row.id, owner);
|
|
@@ -643,3 +731,9 @@ export class OAuthRefreshService {
|
|
|
643
731
|
}
|
|
644
732
|
}
|
|
645
733
|
}
|
|
734
|
+
|
|
735
|
+
/** Reads one account's status (used only for logging/observability, D26). */
|
|
736
|
+
async function readAccountStatus(db: CartethyiaDatabase, accountId: string): Promise<string | undefined> {
|
|
737
|
+
const loaded = await loadAccountWithOauthState(db, accountId, { includeStatus: true });
|
|
738
|
+
return loaded?.status;
|
|
739
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { CanonicalRequest, WireFamily } from "../protocol/canonical-model";
|
|
|
3
3
|
import { GatewayError } from "../protocol/gateway-error";
|
|
4
4
|
import { OpenAICompatibleAdapter, withBearerAuthentication } from "./adapters/compatible-adapter";
|
|
5
5
|
import { providerBaseUrl } from "./provider-metadata";
|
|
6
|
+
import type { ProviderModule } from "./provider-registry";
|
|
6
7
|
|
|
7
8
|
export function ensurePayloadModel(
|
|
8
9
|
payload: Record<string, unknown>,
|
|
@@ -15,6 +16,7 @@ export function ensurePayloadModel(
|
|
|
15
16
|
payload["model"] = model;
|
|
16
17
|
return model;
|
|
17
18
|
}
|
|
19
|
+
|
|
18
20
|
/**
|
|
19
21
|
* Declarative description of one bearer-authenticated OpenAI-compatible provider.
|
|
20
22
|
*
|
|
@@ -98,3 +100,149 @@ export function createApiKeyAdapter(
|
|
|
98
100
|
}),
|
|
99
101
|
);
|
|
100
102
|
}
|
|
103
|
+
|
|
104
|
+
function apiKeySpec(
|
|
105
|
+
providerId: ProviderId,
|
|
106
|
+
paths: Readonly<Partial<Record<"chat" | "responses", string>>>,
|
|
107
|
+
): ApiKeyProviderSpec {
|
|
108
|
+
return {
|
|
109
|
+
provider_id: providerId,
|
|
110
|
+
endpoint_paths_by_wire_family: paths,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const BEARER_PATHS = { chat: "/chat/completions", responses: "/responses" } as const;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Builtin providers whose entire adapter contract is "bearer auth against the
|
|
118
|
+
* provider's `BUNDLED_PROVIDER_METADATA` base URL, OpenAI-compatible chat +
|
|
119
|
+
* responses, with endpoint paths declared below the base's version root".
|
|
120
|
+
*/
|
|
121
|
+
export const GENERIC_API_KEY_SPECS = {
|
|
122
|
+
larprouter: apiKeySpec("larprouter", BEARER_PATHS),
|
|
123
|
+
mistral: apiKeySpec("mistral", BEARER_PATHS),
|
|
124
|
+
fireworks: apiKeySpec("fireworks", BEARER_PATHS),
|
|
125
|
+
nvidia: apiKeySpec("nvidia", BEARER_PATHS),
|
|
126
|
+
gmi: apiKeySpec("gmi", BEARER_PATHS),
|
|
127
|
+
ollamacloud: apiKeySpec("ollamacloud", BEARER_PATHS),
|
|
128
|
+
together: apiKeySpec("together", BEARER_PATHS),
|
|
129
|
+
siliconflow: apiKeySpec("siliconflow", BEARER_PATHS),
|
|
130
|
+
siliconflowcn: apiKeySpec("siliconflowcn", BEARER_PATHS),
|
|
131
|
+
nanogpt: apiKeySpec("nanogpt", BEARER_PATHS),
|
|
132
|
+
baseten: apiKeySpec("baseten", BEARER_PATHS),
|
|
133
|
+
synthetic: apiKeySpec("synthetic", BEARER_PATHS),
|
|
134
|
+
stepfun: apiKeySpec("stepfun", BEARER_PATHS),
|
|
135
|
+
sakana: apiKeySpec("sakana", BEARER_PATHS),
|
|
136
|
+
helmcode: apiKeySpec("helmcode", BEARER_PATHS),
|
|
137
|
+
coralbricks: apiKeySpec("coralbricks", BEARER_PATHS),
|
|
138
|
+
aiand: apiKeySpec("aiand", BEARER_PATHS),
|
|
139
|
+
wafer: apiKeySpec("wafer", BEARER_PATHS),
|
|
140
|
+
coreweave: apiKeySpec("coreweave", BEARER_PATHS),
|
|
141
|
+
dashscope: apiKeySpec("dashscope", BEARER_PATHS),
|
|
142
|
+
"qwen-portal": apiKeySpec("qwen-portal", BEARER_PATHS),
|
|
143
|
+
} satisfies Readonly<Record<string, ApiKeyProviderSpec>>;
|
|
144
|
+
|
|
145
|
+
export type GenericApiKeyProviderId = keyof typeof GENERIC_API_KEY_SPECS;
|
|
146
|
+
|
|
147
|
+
export const GENERIC_API_KEY_PROVIDER_IDS = Object.keys(
|
|
148
|
+
GENERIC_API_KEY_SPECS,
|
|
149
|
+
) as readonly GenericApiKeyProviderId[];
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Ids whose base URL already ends at a version segment.
|
|
153
|
+
*/
|
|
154
|
+
export const VERSIONED_ROOT_API_KEY_SPECS = {
|
|
155
|
+
groq: apiKeySpec("groq", BEARER_PATHS),
|
|
156
|
+
deepinfra: apiKeySpec("deepinfra", BEARER_PATHS),
|
|
157
|
+
novita: apiKeySpec("novita", BEARER_PATHS),
|
|
158
|
+
venice: apiKeySpec("venice", BEARER_PATHS),
|
|
159
|
+
qianfan: apiKeySpec("qianfan", BEARER_PATHS),
|
|
160
|
+
zhipucoding: apiKeySpec("zhipucoding", BEARER_PATHS),
|
|
161
|
+
firepass: apiKeySpec("firepass", BEARER_PATHS),
|
|
162
|
+
} satisfies Readonly<Record<string, ApiKeyProviderSpec>>;
|
|
163
|
+
|
|
164
|
+
export type VersionedRootApiKeyProviderId =
|
|
165
|
+
keyof typeof VERSIONED_ROOT_API_KEY_SPECS;
|
|
166
|
+
|
|
167
|
+
export const VERSIONED_ROOT_API_KEY_PROVIDER_IDS = Object.keys(
|
|
168
|
+
VERSIONED_ROOT_API_KEY_SPECS,
|
|
169
|
+
) as readonly VersionedRootApiKeyProviderId[];
|
|
170
|
+
|
|
171
|
+
type DiscoveryBackedCapabilities = Pick<
|
|
172
|
+
ProviderModule,
|
|
173
|
+
"endpointPathsByWireFamily" | "loadAdapter" | "loadModelDiscovery"
|
|
174
|
+
>;
|
|
175
|
+
|
|
176
|
+
type ModelDiscoveryFactory = NonNullable<ProviderModule["loadModelDiscovery"]>;
|
|
177
|
+
export type OpenAIModelDiscoveryFactory = (
|
|
178
|
+
providerId: string,
|
|
179
|
+
options?: {
|
|
180
|
+
readonly headers?: (credential: string) => Readonly<Record<string, string>>;
|
|
181
|
+
},
|
|
182
|
+
) => ModelDiscoveryFactory;
|
|
183
|
+
|
|
184
|
+
const DISCOVERY_BACKED_PROVIDER_SPECS = {
|
|
185
|
+
groq: VERSIONED_ROOT_API_KEY_SPECS.groq,
|
|
186
|
+
larprouter: GENERIC_API_KEY_SPECS.larprouter,
|
|
187
|
+
together: GENERIC_API_KEY_SPECS.together,
|
|
188
|
+
deepinfra: VERSIONED_ROOT_API_KEY_SPECS.deepinfra,
|
|
189
|
+
siliconflow: GENERIC_API_KEY_SPECS.siliconflow,
|
|
190
|
+
siliconflowcn: GENERIC_API_KEY_SPECS.siliconflowcn,
|
|
191
|
+
novita: VERSIONED_ROOT_API_KEY_SPECS.novita,
|
|
192
|
+
venice: VERSIONED_ROOT_API_KEY_SPECS.venice,
|
|
193
|
+
nanogpt: GENERIC_API_KEY_SPECS.nanogpt,
|
|
194
|
+
baseten: GENERIC_API_KEY_SPECS.baseten,
|
|
195
|
+
synthetic: GENERIC_API_KEY_SPECS.synthetic,
|
|
196
|
+
stepfun: GENERIC_API_KEY_SPECS.stepfun,
|
|
197
|
+
sakana: GENERIC_API_KEY_SPECS.sakana,
|
|
198
|
+
helmcode: GENERIC_API_KEY_SPECS.helmcode,
|
|
199
|
+
coralbricks: GENERIC_API_KEY_SPECS.coralbricks,
|
|
200
|
+
aiand: GENERIC_API_KEY_SPECS.aiand,
|
|
201
|
+
wafer: GENERIC_API_KEY_SPECS.wafer,
|
|
202
|
+
firepass: VERSIONED_ROOT_API_KEY_SPECS.firepass,
|
|
203
|
+
zhipucoding: VERSIONED_ROOT_API_KEY_SPECS.zhipucoding,
|
|
204
|
+
dashscope: GENERIC_API_KEY_SPECS.dashscope,
|
|
205
|
+
qianfan: VERSIONED_ROOT_API_KEY_SPECS.qianfan,
|
|
206
|
+
} satisfies Readonly<Record<string, ApiKeyProviderSpec>>;
|
|
207
|
+
|
|
208
|
+
type DiscoveryBackedProviderId = keyof typeof DISCOVERY_BACKED_PROVIDER_SPECS;
|
|
209
|
+
|
|
210
|
+
/** Canonical discovery-backed provider ids, in declaration order. */
|
|
211
|
+
export const DISCOVERY_BACKED_PROVIDER_IDS = Object.keys(
|
|
212
|
+
DISCOVERY_BACKED_PROVIDER_SPECS,
|
|
213
|
+
) as readonly DiscoveryBackedProviderId[];
|
|
214
|
+
|
|
215
|
+
export function discoveryBackedCapabilities(
|
|
216
|
+
providerId: string,
|
|
217
|
+
spec: ApiKeyProviderSpec,
|
|
218
|
+
openAIModelDiscovery: OpenAIModelDiscoveryFactory,
|
|
219
|
+
): DiscoveryBackedCapabilities {
|
|
220
|
+
return {
|
|
221
|
+
...(spec.endpoint_paths_by_wire_family === undefined
|
|
222
|
+
? {}
|
|
223
|
+
: { endpointPathsByWireFamily: spec.endpoint_paths_by_wire_family }),
|
|
224
|
+
loadAdapter: async () => createApiKeyAdapter(spec),
|
|
225
|
+
loadModelDiscovery: openAIModelDiscovery(providerId, {
|
|
226
|
+
headers: (credential) => ({ authorization: `Bearer ${credential}` }),
|
|
227
|
+
}),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Creates the standard provider capability table using the registry's shared
|
|
233
|
+
* discovery loader, keeping its lazy import and authentication behavior intact.
|
|
234
|
+
*/
|
|
235
|
+
export function createDiscoveryBackedProviderCapabilities(
|
|
236
|
+
openAIModelDiscovery: OpenAIModelDiscoveryFactory,
|
|
237
|
+
): Readonly<Record<DiscoveryBackedProviderId, DiscoveryBackedCapabilities>> {
|
|
238
|
+
const capabilities = {} as Record<DiscoveryBackedProviderId, DiscoveryBackedCapabilities>;
|
|
239
|
+
for (const providerId of DISCOVERY_BACKED_PROVIDER_IDS) {
|
|
240
|
+
capabilities[providerId] = discoveryBackedCapabilities(
|
|
241
|
+
providerId,
|
|
242
|
+
DISCOVERY_BACKED_PROVIDER_SPECS[providerId],
|
|
243
|
+
openAIModelDiscovery,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return capabilities;
|
|
247
|
+
}
|
|
248
|
+
|