cartethyia 2.7.8 → 2.7.9
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 +0 -1
- package/README.md +29 -59
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js +1 -0
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-ixwgKk53.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-BmttNV3U.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js +1 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BOoO8rpz.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-ChJuNNVN.js} +1 -1
- package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-BZAJtPg1.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BJ87Pp0U.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-CMJKNYWb.js → QuotaPage-gkSyRAKa.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js +1 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-CFNcATJJ.js} +1 -1
- package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-YoD4tY5l.js} +1 -1
- package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js +56 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js +1 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css +2 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js +1 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-DdbT_7Am.js} +1 -1
- package/dist/dashboard/assets/badge-DdbT_7Am.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-CsGN-nbm.js} +1 -1
- package/dist/dashboard/assets/banner-CsGN-nbm.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-mgZ_TLLZ.js} +1 -1
- package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js +1 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CJ4Zty89.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-B5L2jPQ_.js} +1 -1
- package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-DkjUouAj.js} +1 -1
- package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
- package/dist/dashboard/assets/{entry-COaC-irN.js → entry-B-LVLV6o.js} +1 -1
- package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js +1 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js +4 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-CrsdLVsn.js} +1 -1
- package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
- package/dist/dashboard/assets/{index-CgKmFngv.js → index-BU64Lqvp.js} +2 -2
- package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2BirKry.js → inline-B2aDLyyg.js} +1 -1
- package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
- package/dist/dashboard/assets/{input-D-qV9dKY.js → input-BCVkdR9d.js} +1 -1
- package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
- package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-DNcAdMK-.js} +1 -1
- package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-DgrIH0hw.js} +1 -1
- package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
- package/dist/dashboard/assets/{live-DGizF06-.js → live-Cae01iu8.js} +1 -1
- package/dist/dashboard/assets/live-Cae01iu8.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-CRib7H_2.js} +1 -1
- package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-BKQE8Xpz.js} +1 -1
- package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-ecPvG_8g.js} +1 -1
- package/dist/dashboard/assets/pagination-ecPvG_8g.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/{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/routing-Ckm1UXuC.js +1 -0
- package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
- package/dist/dashboard/assets/{select-BCOcv8WK.js → select-ChoFWyKz.js} +1 -1
- package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js +1 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
- package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DWCwU3fh.js} +1 -1
- package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
- package/dist/dashboard/assets/{state-DXbGTx7N.js → state-Cg3VL5J_.js} +1 -1
- package/dist/dashboard/assets/state-Cg3VL5J_.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-Db70BMix.js} +1 -1
- package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BqdE0LuM.js} +1 -1
- package/dist/dashboard/assets/useMutation-BqdE0LuM.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 +57 -55
- 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/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
- package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/package.json +1 -1
- package/src/boot/dependencies.ts +11 -3
- package/src/config/cooldowns.ts +1 -1
- package/src/config/spec.ts +0 -6
- package/src/console/api-keys/routes.ts +11 -4
- package/src/console/console-router.ts +7 -0
- package/src/console/dashboard-assets.ts +5 -5
- package/src/console/domain-registration.ts +27 -0
- package/src/console/ip-bans/routes.ts +99 -0
- package/src/console/providers/catalog/catalog-projections.ts +5 -2
- package/src/console/providers/catalog/contracts.ts +123 -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 +64 -4
- package/src/console/providers/catalog/store-helpers.ts +30 -1
- package/src/console/providers/catalog/store.ts +243 -3
- package/src/console/quota/account-quota-global.ts +89 -1
- package/src/console/quota/native-identity.ts +2 -0
- package/src/console/settings/contracts.ts +18 -0
- package/src/console/settings/store.ts +32 -0
- package/src/console/system-prompt/contracts.ts +0 -1
- package/src/main.ts +1 -0
- package/src/observability/event-query.ts +18 -4
- package/src/persistence/dialect.ts +12 -0
- package/src/persistence/schema.ts +20 -2
- package/src/protocol/dsml.ts +1 -1
- package/src/protocol/gateway-error.ts +1 -0
- package/src/protocol/reasoning.ts +1 -1
- package/src/protocol/request/chat.ts +4 -4
- package/src/protocol/request/messages.ts +1 -1
- package/src/protocol/response/gemini.ts +6 -1
- package/src/protocol/sse-decoder.ts +25 -8
- package/src/protocol/stream-error-frames.ts +13 -3
- 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 +7 -5
- package/src/providers/authentication/oauth-refresh-service.ts +149 -36
- package/src/providers/default-registry.ts +68 -3
- package/src/providers/discovery/discovery-types.ts +12 -0
- package/src/providers/discovery/probe-phases.ts +61 -30
- package/src/providers/discovery/probing-service.ts +279 -164
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +117 -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 +18 -18
- 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 +70 -0
- 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 +4 -41
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +20 -1
- package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -2
- package/src/providers/integrations/buddy/codebuddy/shared.ts +6 -10
- package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
- package/src/providers/integrations/cursor/cursor-exec.ts +738 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
- package/src/providers/integrations/cursor/cursor-request.ts +9 -8
- package/src/providers/integrations/cursor/cursor-shared.ts +19 -1
- package/src/providers/integrations/cursor/cursor.ts +50 -30
- 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 +584 -0
- package/src/providers/integrations/devin/protocol/framing.ts +124 -0
- package/src/providers/integrations/factory/factory.ts +2 -2
- 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/hermes/hermes-oauth.ts +94 -0
- package/src/providers/integrations/hermes.ts +4 -12
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/perch/perch-catalog.ts +105 -0
- package/src/providers/integrations/perch/perch-oauth.ts +170 -0
- package/src/providers/integrations/perch/perch-protocol.ts +211 -0
- package/src/providers/integrations/perch/perch-quota.ts +137 -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 +315 -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 +1 -1
- package/src/providers/integrations/qoder/qoder-adapter.ts +89 -28
- package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +6 -1
- package/src/providers/integrations/qoder/qoder-checkin.ts +2 -2
- package/src/providers/integrations/qoder/qoder-grpc.ts +210 -6
- package/src/providers/integrations/qoder/qoder-ide.ts +5 -1
- 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 -6
- 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 +3 -6
- package/src/providers/integrations/trae/trae-quota.ts +103 -21
- package/src/providers/integrations/trae/trae-realm.ts +36 -15
- package/src/providers/integrations/trae/trae.ts +97 -7
- package/src/providers/operations/account-error-classification.ts +66 -1
- package/src/providers/operations/account-health-service.ts +192 -26
- package/src/providers/operations/account-refresh-status.ts +284 -0
- package/src/providers/operations/account-tier.ts +67 -0
- package/src/providers/operations/client-versions.ts +37 -99
- package/src/providers/operations/cooldown-rules.ts +61 -0
- package/src/providers/operations/provider-catalog-seeder.ts +4 -0
- package/src/providers/provider-contracts.ts +29 -0
- package/src/providers/provider-metadata.ts +59 -4
- package/src/providers/provider-model-definition.ts +36 -0
- package/src/providers/quota/refresh.ts +21 -0
- package/src/security/abuse.ts +71 -0
- package/src/transport/dispatch/proxy-request.ts +1 -0
- package/src/transport/dispatch/streaming/stream.ts +1 -0
- package/src/transport/dispatch/upstream.ts +4 -1
- package/src/transport/failure-policy.ts +113 -7
- package/src/transport/routing/router.ts +25 -1
- package/src/transport/surface/messages/adapter.ts +2 -1
- package/src/transport/surface/responses/encode.ts +12 -1
- 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.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 +0 -1
- 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/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/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/dist/dashboard/assets/{app-CTxoqyYB.js → app-B7kIWFm5.js} +0 -0
- /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-B7kIWFm5.js.gz} +0 -0
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
grok407ProbeFailure,
|
|
48
48
|
hasMeaningfulOutput,
|
|
49
49
|
loadProbePreferences,
|
|
50
|
-
|
|
50
|
+
PROBE_FAST_SAMPLE_MAX_CHARS,
|
|
51
51
|
recordProbeHealth,
|
|
52
52
|
resolveProbeAdapter,
|
|
53
53
|
resolveProbeTarget,
|
|
@@ -188,14 +188,16 @@ export class ProviderProbingService {
|
|
|
188
188
|
request: ProbeModelRequest,
|
|
189
189
|
): Promise<ProbeModelResult> {
|
|
190
190
|
const startedAt = Date.now();
|
|
191
|
-
// Qoder's
|
|
192
|
-
// (
|
|
193
|
-
//
|
|
194
|
-
//
|
|
191
|
+
// Qoder's legacy COSY surface is the only live chat transport since
|
|
192
|
+
// 2026-11-07 (the gRPC host is NXDOMAIN and the IDE `/model/v1` surface
|
|
193
|
+
// answers 404 on every alive host). Probes for Qoder default to it unless
|
|
194
|
+
// the operator picked a surface explicitly. The IDE surface also echoes
|
|
195
|
+
// provider metadata, but that is worthless when the surface itself is
|
|
196
|
+
// dead — a probe must report what live traffic would do.
|
|
195
197
|
const normalizedRequest: ProbeModelRequest =
|
|
196
198
|
(providerId === "qoder" || providerId === "qodercn") &&
|
|
197
199
|
(request.surface === undefined || request.surface === "default")
|
|
198
|
-
? { ...request, surface: "
|
|
200
|
+
? { ...request, surface: "cosy" }
|
|
199
201
|
: request;
|
|
200
202
|
const modelId = normalizedRequest.modelId;
|
|
201
203
|
// One provider snapshot per probe: the contract gate, the account gate, the
|
|
@@ -241,24 +243,29 @@ export class ProviderProbingService {
|
|
|
241
243
|
};
|
|
242
244
|
}
|
|
243
245
|
|
|
244
|
-
const
|
|
246
|
+
const preferences = await loadProbePreferences({
|
|
245
247
|
db: this.db,
|
|
246
248
|
tenantId,
|
|
247
249
|
wireFamily,
|
|
248
250
|
request,
|
|
249
251
|
});
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
const probeRequest = qoderCmodelPrompt === undefined ? normalizedRequest : { ...normalizedRequest, prompt: qoderCmodelPrompt };
|
|
252
|
+
const { probeReasoning, payloadCapture } = preferences;
|
|
253
|
+
// `detail` is the exact pre-existing behaviour; only `fast` changes any
|
|
254
|
+
// budget. The identity prompt itself is shared by both modes (D42).
|
|
255
|
+
const fast = preferences.mode === "fast";
|
|
256
|
+
const probeTimeoutMs = preferences.timeoutMs ?? 30_000;
|
|
257
|
+
const probeSignal = AbortSignal.timeout(probeTimeoutMs);
|
|
257
258
|
const canonicalRequest = buildProbeCanonicalRequest({
|
|
258
259
|
modelId,
|
|
259
|
-
request:
|
|
260
|
+
request: normalizedRequest,
|
|
260
261
|
probeReasoning,
|
|
261
262
|
sourceSurface,
|
|
263
|
+
...(preferences.maxOutputTokens === undefined
|
|
264
|
+
? {}
|
|
265
|
+
: { maxOutputTokens: preferences.maxOutputTokens }),
|
|
266
|
+
...(typeof adapter?.systemPrompt === "function"
|
|
267
|
+
? { providerSystemPrompt: adapter.systemPrompt() }
|
|
268
|
+
: {}),
|
|
262
269
|
});
|
|
263
270
|
let capturedRequest = canonicalRequest;
|
|
264
271
|
const parsedProviderId = parseProviderId(providerId);
|
|
@@ -282,7 +289,6 @@ export class ProviderProbingService {
|
|
|
282
289
|
: { probe_surface: normalizedRequest.surface }),
|
|
283
290
|
};
|
|
284
291
|
|
|
285
|
-
const probeSignal = AbortSignal.timeout(30_000);
|
|
286
292
|
const events: CanonicalEvent[] = [];
|
|
287
293
|
/**
|
|
288
294
|
* Populated when the probe dials. Declared here, not inside the try, so the
|
|
@@ -337,7 +343,7 @@ export class ProviderProbingService {
|
|
|
337
343
|
candidate,
|
|
338
344
|
{
|
|
339
345
|
credential,
|
|
340
|
-
deadline: startedAt +
|
|
346
|
+
deadline: startedAt + probeTimeoutMs,
|
|
341
347
|
abort_signal: probeSignal,
|
|
342
348
|
outbound_fetch: probeFetch,
|
|
343
349
|
},
|
|
@@ -373,7 +379,7 @@ export class ProviderProbingService {
|
|
|
373
379
|
candidate,
|
|
374
380
|
{
|
|
375
381
|
credential,
|
|
376
|
-
deadline: startedAt +
|
|
382
|
+
deadline: startedAt + probeTimeoutMs,
|
|
377
383
|
abort_signal: probeSignal,
|
|
378
384
|
outbound_fetch: probeFetch,
|
|
379
385
|
},
|
|
@@ -415,7 +421,7 @@ export class ProviderProbingService {
|
|
|
415
421
|
capturedRequest = request;
|
|
416
422
|
for await (const event of adapter.dispatch(request, candidate, {
|
|
417
423
|
credential,
|
|
418
|
-
deadline: startedAt +
|
|
424
|
+
deadline: startedAt + probeTimeoutMs,
|
|
419
425
|
abort_signal: probeSignal,
|
|
420
426
|
outbound_fetch: probeFetch,
|
|
421
427
|
// A bidi-only provider (Cursor's Connect-RPC Run) has no `fetch`
|
|
@@ -433,7 +439,8 @@ export class ProviderProbingService {
|
|
|
433
439
|
if (!hasMeaningfulOutput(events)) throw new Error("probe response content empty");
|
|
434
440
|
} catch (error) {
|
|
435
441
|
const message = error instanceof Error ? error.message : String(error);
|
|
436
|
-
|
|
442
|
+
// `fast` is deliberately single-shot: no 1024-token streamed retry.
|
|
443
|
+
if (fast || !/empty\s+(?:response\s+)?content|response\s+content\s+empty/i.test(message)) {
|
|
437
444
|
throw error;
|
|
438
445
|
}
|
|
439
446
|
// Some reasoning models spend the short probe budget before producing
|
|
@@ -526,26 +533,40 @@ export class ProviderProbingService {
|
|
|
526
533
|
latencyMs,
|
|
527
534
|
});
|
|
528
535
|
|
|
529
|
-
// Probe telemetry rides the same batch buffer as live traffic.
|
|
530
|
-
//
|
|
531
|
-
//
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
536
|
+
// Probe telemetry rides the same batch buffer as live traffic. `fast`
|
|
537
|
+
// keeps only the liveness row — no token-speed/usage/TTFB detail — so a
|
|
538
|
+
// "test all models" run stays cheap; `detail` is unchanged.
|
|
539
|
+
if (fast) {
|
|
540
|
+
this.telemetryBuffer?.enqueue({
|
|
541
|
+
tenantId,
|
|
542
|
+
requestId,
|
|
543
|
+
sourceSurface,
|
|
544
|
+
requestedModel: modelId,
|
|
545
|
+
endpoint: endpointPath,
|
|
546
|
+
providerId,
|
|
547
|
+
latencyMs,
|
|
548
|
+
stream: false,
|
|
549
|
+
status: ok ? "completed" : "failed",
|
|
550
|
+
});
|
|
551
|
+
} else {
|
|
552
|
+
this.telemetryBuffer?.enqueue({
|
|
553
|
+
tenantId,
|
|
554
|
+
requestId,
|
|
555
|
+
sourceSurface,
|
|
556
|
+
requestedModel: modelId,
|
|
557
|
+
endpoint: endpointPath,
|
|
558
|
+
providerId,
|
|
559
|
+
...(accountId ? { accountId } : {}),
|
|
560
|
+
...(networkPoolId ? { networkPoolId } : {}),
|
|
561
|
+
latencyMs,
|
|
562
|
+
...(ttfbMs !== undefined ? { ttfbMs } : {}),
|
|
563
|
+
stream: capturedRequest.stream,
|
|
564
|
+
status: ok ? "completed" : "failed",
|
|
565
|
+
...(ok ? {} : { errorCategory: errorMessage ?? "probe_failed" }),
|
|
566
|
+
...(usage ? { usage } : {}),
|
|
567
|
+
...(tokensPerSec !== undefined ? { tokensPerSec } : {}),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
549
570
|
if (this.telemetryBuffer && payloadCapture.enabled) {
|
|
550
571
|
if (payloadCapture.mode === "metadata") {
|
|
551
572
|
// Stage presence only: the provider request line (method, URL,
|
|
@@ -596,7 +617,13 @@ export class ProviderProbingService {
|
|
|
596
617
|
}
|
|
597
618
|
}
|
|
598
619
|
|
|
599
|
-
const
|
|
620
|
+
const rawSample = extractSample(events);
|
|
621
|
+
// `fast` answers one question — did the model talk back? — so an 80-char
|
|
622
|
+
// excerpt is enough; `detail` keeps the full sample verbatim (D40).
|
|
623
|
+
const sample =
|
|
624
|
+
rawSample === undefined || !fast
|
|
625
|
+
? rawSample
|
|
626
|
+
: rawSample.slice(0, PROBE_FAST_SAMPLE_MAX_CHARS);
|
|
600
627
|
// The HTTP-only `readReportedModel` covers response-body transports; a
|
|
601
628
|
// non-HTTP transport (Qoder gRPC) reports the upstream echo as a
|
|
602
629
|
// `response_start` event instead — fall back to that when the body read
|
|
@@ -611,11 +638,11 @@ export class ProviderProbingService {
|
|
|
611
638
|
...(accountId ? { accountId } : {}),
|
|
612
639
|
ok,
|
|
613
640
|
latencyMs,
|
|
614
|
-
...(ttfbMs
|
|
641
|
+
...(fast || ttfbMs === undefined ? {} : { ttfbMs }),
|
|
615
642
|
...(upstreamStatusCode === undefined ? {} : { statusCode: upstreamStatusCode }),
|
|
616
643
|
...(sample ? { sample } : {}),
|
|
617
|
-
...(reportedModel === undefined ? {} : { reportedModel }),
|
|
618
|
-
...(providerMetadata === undefined || Object.keys(providerMetadata).length === 0
|
|
644
|
+
...(fast || reportedModel === undefined ? {} : { reportedModel }),
|
|
645
|
+
...(fast || providerMetadata === undefined || Object.keys(providerMetadata).length === 0
|
|
619
646
|
? {}
|
|
620
647
|
: { providerMetadata }),
|
|
621
648
|
...(searchResults !== undefined && searchResults.length > 0 ? { searchResults } : {}),
|
|
@@ -717,38 +744,54 @@ export class ProviderProbingService {
|
|
|
717
744
|
}
|
|
718
745
|
}
|
|
719
746
|
|
|
720
|
-
|
|
747
|
+
/**
|
|
748
|
+
* Fetches one provider's model listing through the exact same two branches
|
|
749
|
+
* `syncModels` uses — a registry resolver, or the generic OpenAI-compatible
|
|
750
|
+
* `/models` fetch for BYOK — without writing anything. `supported:false`
|
|
751
|
+
* names a provider with no listing path so the Add-Model UI can offer its
|
|
752
|
+
* manual fallback instead of reporting a transport failure.
|
|
753
|
+
*
|
|
754
|
+
* The account is the first usable one (`selectUsableAccount`), the same
|
|
755
|
+
* selection sync uses (D27); its label is returned when available so the
|
|
756
|
+
* surface can say whose roster it is showing.
|
|
757
|
+
*/
|
|
758
|
+
async discoverModels(
|
|
759
|
+
tenantId: string,
|
|
760
|
+
providerId: string,
|
|
761
|
+
): Promise<{
|
|
762
|
+
readonly supported: boolean;
|
|
763
|
+
readonly models: readonly ModelDefinition[];
|
|
764
|
+
readonly accountLabel?: string;
|
|
765
|
+
/** Wire resolution context + BYOK family constraints for a later commit. */
|
|
766
|
+
readonly wire: {
|
|
767
|
+
readonly providerRow: ProbeProviderRow | undefined;
|
|
768
|
+
readonly supportedWireFamilies: readonly WireFamily[] | undefined;
|
|
769
|
+
readonly baseUrl: string | undefined;
|
|
770
|
+
};
|
|
771
|
+
}> {
|
|
721
772
|
const outbound = asOutboundBinding(await this.outboundFetchFor(tenantId, providerId));
|
|
722
773
|
try {
|
|
723
774
|
const discoveryFetch = asFetchLike(outbound.fetch);
|
|
724
775
|
const resolver = await this.providerRegistry.resolveModelDiscovery(providerId);
|
|
776
|
+
const account = await this.usableAccount(tenantId, providerId);
|
|
777
|
+
const providerRow = await this.loadProviderWireRow(providerId);
|
|
725
778
|
if (resolver) {
|
|
726
|
-
//
|
|
727
|
-
//
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const account = await this.usableAccount(tenantId, providerId);
|
|
733
|
-
const secret = account?.secret;
|
|
734
|
-
const providerRow = await this.loadProviderWireRow(providerId);
|
|
735
|
-
const effectiveBaseUrl = providerRow?.baseUrl ?? providerBaseUrl(providerId) ?? "";
|
|
779
|
+
// `providerBaseUrl` throws for a non-bundled id; a custom provider can
|
|
780
|
+
// still register a discovery resolver, and its own row's base URL is
|
|
781
|
+
// the authority. Only a bundled provider falls back to metadata.
|
|
782
|
+
const effectiveBaseUrl =
|
|
783
|
+
providerRow?.baseUrl ??
|
|
784
|
+
(isBundledProviderId(providerId) ? providerBaseUrl(providerId) : "");
|
|
736
785
|
let discovered: readonly ModelDefinition[] | null;
|
|
737
786
|
try {
|
|
738
787
|
discovered = await resolver({
|
|
739
788
|
baseUrl: effectiveBaseUrl,
|
|
740
|
-
credential: secret ?? "",
|
|
789
|
+
credential: account?.secret ?? "",
|
|
741
790
|
...(discoveryFetch ? { fetcher: discoveryFetch } : {}),
|
|
742
791
|
...(account?.authState === undefined ? {} : { authState: account.authState }),
|
|
743
792
|
});
|
|
744
793
|
} catch (error) {
|
|
745
|
-
throw
|
|
746
|
-
"transport_unavailable",
|
|
747
|
-
502,
|
|
748
|
-
`Model discovery failed for ${providerId}: ${error instanceof Error ? error.message : "unknown error"} (request {requestId}). The connection ghosted us — come back in a moment 👻`,
|
|
749
|
-
{},
|
|
750
|
-
"upstream",
|
|
751
|
-
);
|
|
794
|
+
throw this.discoveryFailure(providerId, error);
|
|
752
795
|
}
|
|
753
796
|
if (!discovered) {
|
|
754
797
|
throw new GatewayError(
|
|
@@ -759,101 +802,92 @@ export class ProviderProbingService {
|
|
|
759
802
|
"upstream",
|
|
760
803
|
);
|
|
761
804
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
effectiveBaseUrl,
|
|
769
|
-
);
|
|
805
|
+
return {
|
|
806
|
+
supported: true,
|
|
807
|
+
models: discovered,
|
|
808
|
+
...(account?.label === undefined ? {} : { accountLabel: account.label }),
|
|
809
|
+
wire: { providerRow, supportedWireFamilies: undefined, baseUrl: effectiveBaseUrl },
|
|
810
|
+
};
|
|
770
811
|
}
|
|
771
|
-
|
|
772
|
-
const providerRow = await this.loadProviderWireRow(providerId);
|
|
773
812
|
if (providerRow?.baseUrl) {
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
}
|
|
808
|
-
if (discovered && discovered.length > 0) {
|
|
809
|
-
const ctx = wireContextFrom(providerRow);
|
|
810
|
-
return this.persistDiscoveredModels(
|
|
811
|
-
providerId,
|
|
812
|
-
discovered,
|
|
813
|
-
(modelId) => resolveDiscoveredWire(modelId, ctx, undefined, this.defaultEndpoints),
|
|
814
|
-
ctx.endpointPathsByWireFamily,
|
|
815
|
-
providerRow.baseUrl ?? undefined,
|
|
816
|
-
// The generic `/models` fetch guesses a wire from the model id;
|
|
817
|
-
// the row's own profile is what the adapter will enforce, so it
|
|
818
|
-
// decides which guess is admissible.
|
|
819
|
-
wireProfile.supportedWireFamilies,
|
|
820
|
-
);
|
|
821
|
-
}
|
|
813
|
+
const wireProfile = resolveByokWireProfile(
|
|
814
|
+
providerRow.wireFamilyDefault,
|
|
815
|
+
providerRow.compatibilityProfile as CompatibilityProfile | null,
|
|
816
|
+
);
|
|
817
|
+
const modelsBaseUrl = modelDiscoveryBaseUrl(providerRow.baseUrl);
|
|
818
|
+
let discovered: readonly ModelDefinition[] | null;
|
|
819
|
+
try {
|
|
820
|
+
discovered = await fetchOpenAICompatibleModels({
|
|
821
|
+
baseUrl: modelsBaseUrl,
|
|
822
|
+
providerId,
|
|
823
|
+
headers: {
|
|
824
|
+
...authHeaders(
|
|
825
|
+
wireProfile.authHeaderShape,
|
|
826
|
+
new TextEncoder().encode(account?.secret ?? ""),
|
|
827
|
+
"api_key",
|
|
828
|
+
),
|
|
829
|
+
},
|
|
830
|
+
...(discoveryFetch ? { fetcher: discoveryFetch as typeof fetch } : {}),
|
|
831
|
+
});
|
|
832
|
+
} catch (error) {
|
|
833
|
+
throw this.discoveryFailure(providerId, error);
|
|
834
|
+
}
|
|
835
|
+
if (discovered && discovered.length > 0) {
|
|
836
|
+
return {
|
|
837
|
+
supported: true,
|
|
838
|
+
models: discovered,
|
|
839
|
+
...(account?.label === undefined ? {} : { accountLabel: account.label }),
|
|
840
|
+
wire: {
|
|
841
|
+
providerRow,
|
|
842
|
+
supportedWireFamilies: wireProfile.supportedWireFamilies,
|
|
843
|
+
baseUrl: providerRow.baseUrl ?? undefined,
|
|
844
|
+
},
|
|
845
|
+
};
|
|
822
846
|
}
|
|
823
847
|
}
|
|
848
|
+
return {
|
|
849
|
+
supported: false,
|
|
850
|
+
models: [],
|
|
851
|
+
...(account?.label === undefined ? {} : { accountLabel: account.label }),
|
|
852
|
+
wire: { providerRow, supportedWireFamilies: undefined, baseUrl: undefined },
|
|
853
|
+
};
|
|
854
|
+
} finally {
|
|
855
|
+
outbound.release?.();
|
|
856
|
+
}
|
|
857
|
+
}
|
|
824
858
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
859
|
+
/** One upstream-shaped error for both discovery branches. */
|
|
860
|
+
private discoveryFailure(providerId: string, error: unknown): GatewayError {
|
|
861
|
+
return new GatewayError(
|
|
862
|
+
"transport_unavailable",
|
|
863
|
+
502,
|
|
864
|
+
`Model discovery failed for ${providerId}: ${error instanceof Error ? error.message : "unknown error"} (request {requestId}). The connection ghosted us — come back in a moment 👻`,
|
|
865
|
+
{},
|
|
866
|
+
"upstream",
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* The single write path for every discovery result: one fresh fetch, one
|
|
872
|
+
* subset, one source. Sync passes the whole roster as `discovered`; the
|
|
873
|
+
* Add-Model commit passes the operator's selected definitions as `manual`.
|
|
874
|
+
* `definitionsFrom` is applied to the fresh fetch so a stale client-side
|
|
875
|
+
* `ModelDefinition` can never decide what gets stored.
|
|
876
|
+
*/
|
|
877
|
+
private async persistRoster(
|
|
878
|
+
tenantId: string,
|
|
879
|
+
providerId: string,
|
|
880
|
+
definitionsFrom: (
|
|
881
|
+
fresh: readonly ModelDefinition[],
|
|
882
|
+
) => readonly ModelDefinition[],
|
|
883
|
+
source?: "manual",
|
|
884
|
+
unsupportedError?: (accountExists: boolean) => GatewayError,
|
|
885
|
+
): Promise<{ synced: number }> {
|
|
886
|
+
const fresh = await this.discoverModels(tenantId, providerId);
|
|
887
|
+
if (!fresh.supported) {
|
|
888
|
+
if (unsupportedError) {
|
|
889
|
+
throw unsupportedError((await this.usableAccount(tenantId, providerId)) !== undefined);
|
|
854
890
|
}
|
|
855
|
-
// A credential exists and the provider still has no listing path: the
|
|
856
|
-
// catalog is the compiled one, and new models arrive with an update.
|
|
857
891
|
throw new GatewayError(
|
|
858
892
|
"capability_unsupported",
|
|
859
893
|
400,
|
|
@@ -861,9 +895,75 @@ export class ProviderProbingService {
|
|
|
861
895
|
{},
|
|
862
896
|
"cartethyia",
|
|
863
897
|
);
|
|
864
|
-
} finally {
|
|
865
|
-
outbound.release?.();
|
|
866
898
|
}
|
|
899
|
+
const ctx = wireContextFrom(fresh.wire.providerRow);
|
|
900
|
+
return this.persistDiscoveredModels(
|
|
901
|
+
providerId,
|
|
902
|
+
definitionsFrom(fresh.models),
|
|
903
|
+
(modelId) => resolveDiscoveredWire(modelId, ctx, undefined, this.defaultEndpoints),
|
|
904
|
+
ctx.endpointPathsByWireFamily,
|
|
905
|
+
fresh.wire.baseUrl,
|
|
906
|
+
fresh.wire.supportedWireFamilies,
|
|
907
|
+
source,
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Full sync: one fetch, upsert every id as `discovered` (or `auto_free`).
|
|
913
|
+
* Its original prerequisite diagnostics are preserved: a custom provider
|
|
914
|
+
* with no account is told to add one, a bundled provider with no listing
|
|
915
|
+
* endpoint is told its catalog ships with the release.
|
|
916
|
+
*/
|
|
917
|
+
async syncModels(tenantId: string, providerId: string): Promise<{ synced: number }> {
|
|
918
|
+
return this.persistRoster(
|
|
919
|
+
tenantId,
|
|
920
|
+
providerId,
|
|
921
|
+
(fresh) => fresh,
|
|
922
|
+
undefined,
|
|
923
|
+
(accountExists) => {
|
|
924
|
+
if (!isBundledProviderId(providerId) || !accountExists) {
|
|
925
|
+
const prefix = isBundledProviderId(providerId)
|
|
926
|
+
? `Add an account for ${providerId} before fetching its models`
|
|
927
|
+
: `Add an account for ${providerId} first — model listing needs a credential`;
|
|
928
|
+
return new GatewayError(
|
|
929
|
+
"invalid_request",
|
|
930
|
+
400,
|
|
931
|
+
`${prefix} (request {requestId}). Wrong key, right door — check the credential 🔑`,
|
|
932
|
+
{},
|
|
933
|
+
"cartethyia",
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
return new GatewayError(
|
|
937
|
+
"capability_unsupported",
|
|
938
|
+
400,
|
|
939
|
+
`${providerId} publishes no model-listing endpoint (request {requestId}). Its catalog ships with Cartethyia; new models appear with an update. Know your tools — this one just isn't in the box 🧰`,
|
|
940
|
+
{},
|
|
941
|
+
"cartethyia",
|
|
942
|
+
);
|
|
943
|
+
},
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Persists an operator-selected subset of a freshly fetched roster as
|
|
949
|
+
* `source="manual"` (D28). The store has already intersected the requested
|
|
950
|
+
* ids against its own fetch; this second fetch is the anti-forgery boundary
|
|
951
|
+
* and the source of every stored `ModelDefinition`.
|
|
952
|
+
*/
|
|
953
|
+
async commitDiscoveredModels(
|
|
954
|
+
tenantId: string,
|
|
955
|
+
providerId: string,
|
|
956
|
+
selectedIds: readonly string[],
|
|
957
|
+
): Promise<{ synced: number }> {
|
|
958
|
+
return this.persistRoster(
|
|
959
|
+
tenantId,
|
|
960
|
+
providerId,
|
|
961
|
+
(fresh) => {
|
|
962
|
+
const wanted = new Set(selectedIds);
|
|
963
|
+
return fresh.filter((definition) => wanted.has(definition.modelId));
|
|
964
|
+
},
|
|
965
|
+
"manual",
|
|
966
|
+
);
|
|
867
967
|
}
|
|
868
968
|
|
|
869
969
|
private async persistDiscoveredModels(
|
|
@@ -873,6 +973,7 @@ export class ProviderProbingService {
|
|
|
873
973
|
endpointOverrides?: Partial<Record<WireFamily, string>>,
|
|
874
974
|
baseUrl?: string,
|
|
875
975
|
supportedWireFamilies?: readonly WireFamily[],
|
|
976
|
+
source?: "manual",
|
|
876
977
|
): Promise<{ synced: number }> {
|
|
877
978
|
// Static definitions are the injected bundled catalog; the probing service
|
|
878
979
|
// and the console store are both handed the same startup-built map.
|
|
@@ -892,6 +993,10 @@ export class ProviderProbingService {
|
|
|
892
993
|
// rows for the same ids afterwards.
|
|
893
994
|
const resolvedPairs: Array<{ modelId: string; endpointPath: string }> = [];
|
|
894
995
|
const rows: Array<typeof models.$inferInsert> = [];
|
|
996
|
+
// A `manual` commit is an operator's subset, not a full roster. The
|
|
997
|
+
// builtin-id skip that protects sync from creating shadow rows would hide
|
|
998
|
+
// a legitimately selected operator row, so only sync applies it.
|
|
999
|
+
const skipBuiltinIds = source === undefined;
|
|
895
1000
|
// `(modelId, endpointPath)` is the row identity and the upsert's conflict
|
|
896
1001
|
// target, so a listing that repeats a pair (the bespoke discoveries return
|
|
897
1002
|
// raw upstream ids) would make a multi-row `ON CONFLICT DO UPDATE` fail with
|
|
@@ -900,7 +1005,7 @@ export class ProviderProbingService {
|
|
|
900
1005
|
const seenPairs = new Set<string>();
|
|
901
1006
|
for (const item of discoveredModels) {
|
|
902
1007
|
const modelId = typeof item === "string" ? item : item.modelId;
|
|
903
|
-
if (builtinIds.has(modelId)) continue;
|
|
1008
|
+
if (skipBuiltinIds && builtinIds.has(modelId)) continue;
|
|
904
1009
|
const resolved = resolve(modelId);
|
|
905
1010
|
const discDef = typeof item === "string" ? undefined : item;
|
|
906
1011
|
const { wireFamily, endpointPath } = applyDiscoveredWire({
|
|
@@ -927,6 +1032,10 @@ export class ProviderProbingService {
|
|
|
927
1032
|
reasoning: knownDefinition?.reasoning ?? discDef?.reasoning ?? false,
|
|
928
1033
|
toolCall: knownDefinition?.toolCall ?? discDef?.toolCall ?? false,
|
|
929
1034
|
cost: modelsDevCatalog.costFor(providerId, modelId),
|
|
1035
|
+
// R-E: fetched warnings win, a matching static definition is the
|
|
1036
|
+
// fallback, and an unknown id stores `NULL` (operator-editable).
|
|
1037
|
+
warningMetadata:
|
|
1038
|
+
discDef?.warnings ?? knownDefinition?.warnings ?? null,
|
|
930
1039
|
// Discovery builds its rows from the same static catalog, so a fetched
|
|
931
1040
|
// row carries the same answer a seeded one does. Null when neither
|
|
932
1041
|
// source marked it, which reads as "unknown" rather than "free".
|
|
@@ -948,7 +1057,8 @@ export class ProviderProbingService {
|
|
|
948
1057
|
// source, because the model list groups on it: a free-tier row is not
|
|
949
1058
|
// an ordinary fetched one, and collapsing both into `discovered` lost
|
|
950
1059
|
// that distinction at the only point where it is still known.
|
|
951
|
-
source:
|
|
1060
|
+
source:
|
|
1061
|
+
source ?? (discDef?.freeTier === true ? "auto_free" : "discovered"),
|
|
952
1062
|
sourceUpdatedAt,
|
|
953
1063
|
enabled: true,
|
|
954
1064
|
...(discDef?.dialect === undefined && knownDefinition?.dialect === undefined
|
|
@@ -975,6 +1085,7 @@ export class ProviderProbingService {
|
|
|
975
1085
|
cost: sql`excluded.cost`,
|
|
976
1086
|
proOnly: sql`excluded.pro_only`,
|
|
977
1087
|
freeTier: sql`excluded.free_tier`,
|
|
1088
|
+
warningMetadata: sql`excluded.warning_metadata`,
|
|
978
1089
|
dialect: sql`excluded.dialect`,
|
|
979
1090
|
// The proposed row's own source, not a constant: a free-tier
|
|
980
1091
|
// discovery and an ordinary one write the same upsert, and pinning
|
|
@@ -986,6 +1097,9 @@ export class ProviderProbingService {
|
|
|
986
1097
|
});
|
|
987
1098
|
}
|
|
988
1099
|
const synced = rows.length;
|
|
1100
|
+
// A manual commit is an operator's selection, not a roster replacement:
|
|
1101
|
+
// pruning would delete unrelated discovered rows with the same ids.
|
|
1102
|
+
if (source === "manual") return { synced };
|
|
989
1103
|
// Drop this provider's discovered rows for the synced ids that are no
|
|
990
1104
|
// longer part of the resolved set — the stale `(model, endpoint)` pair left
|
|
991
1105
|
// behind when a corrected wire family moved a model to a new path. Rows the
|
|
@@ -1015,13 +1129,6 @@ export class ProviderProbingService {
|
|
|
1015
1129
|
return { synced };
|
|
1016
1130
|
}
|
|
1017
1131
|
|
|
1018
|
-
private async usableAccountSecret(
|
|
1019
|
-
tenantId: string,
|
|
1020
|
-
providerId: string,
|
|
1021
|
-
): Promise<string | undefined> {
|
|
1022
|
-
return (await this.usableAccount(tenantId, providerId))?.secret;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
1132
|
/**
|
|
1026
1133
|
* The account a credential-scoped discovery will use, with its non-secret
|
|
1027
1134
|
* auth configuration. Selection is unchanged — first usable row, preferring a
|
|
@@ -1030,7 +1137,11 @@ export class ProviderProbingService {
|
|
|
1030
1137
|
private async usableAccount(
|
|
1031
1138
|
tenantId: string,
|
|
1032
1139
|
providerId: string,
|
|
1033
|
-
): Promise<{
|
|
1140
|
+
): Promise<{
|
|
1141
|
+
readonly secret: string;
|
|
1142
|
+
readonly label?: string;
|
|
1143
|
+
readonly authState?: Readonly<Record<string, unknown>>;
|
|
1144
|
+
} | undefined> {
|
|
1034
1145
|
const rows = await this.db
|
|
1035
1146
|
.select()
|
|
1036
1147
|
.from(providerAccounts)
|
|
@@ -1045,6 +1156,7 @@ export class ProviderProbingService {
|
|
|
1045
1156
|
const authState = isRecord(usable.authState) ? usable.authState as Readonly<Record<string, unknown>> : undefined;
|
|
1046
1157
|
return {
|
|
1047
1158
|
secret: decryptCredentialToString(usable.credentialCiphertext),
|
|
1159
|
+
...(typeof usable.label === "string" ? { label: usable.label } : {}),
|
|
1048
1160
|
...(authState === undefined ? {} : { authState }),
|
|
1049
1161
|
};
|
|
1050
1162
|
}
|
|
@@ -1056,6 +1168,9 @@ export class ProviderProbingService {
|
|
|
1056
1168
|
* carries its model route and service kind so native services never fall
|
|
1057
1169
|
* through to an LLM probe.
|
|
1058
1170
|
*
|
|
1171
|
+
* `mode` flows through every request, so "Test all models" (D43) can use
|
|
1172
|
+
* the cheap `fast` path end to end.
|
|
1173
|
+
*
|
|
1059
1174
|
* Sliding window beats barrel-batching here: the historical pattern waited
|
|
1060
1175
|
* for the slowest probe in each group of `N` before starting the next group,
|
|
1061
1176
|
* so one slow route stalled nine others. With the worker loop, a worker
|