cartethyia 2.7.9 → 2.8.1
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 +6 -5
- package/dist/dashboard/assets/{BannedPage-BxoG1MrL.js → BannedPage-Dq530awD.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-ixwgKk53.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BmttNV3U.js → CliToolsPage-DaZZ3cqP.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BE3qwwfC.js → CombosPage-BlC_xdkX.js} +1 -1
- package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CRZdmMIX.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-Dv_FJ6Er.js → CustomizationPage-7xT5yg2E.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-BOoO8rpz.js → InjectSystemPromptPanel-6Teoq9Se.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-ChJuNNVN.js → LoginPage-CZuI0MAR.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BZAJtPg1.js → ModelPicker-C1hNTlkr.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
- package/dist/dashboard/assets/{OverviewPage-BE40s3u5.js → OverviewPage-0yfhSI1C.js} +1 -1
- package/dist/dashboard/assets/OverviewPage-0yfhSI1C.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js.gz +0 -0
- package/dist/dashboard/assets/{ProviderIcon-CQ6bPUf_.js → ProviderIcon-Ddhm1InL.js} +1 -1
- package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js +9 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-C5HPWqEv.js → SettingsPage-BzZAvYSX.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-CFNcATJJ.js → SetupPage--s-EfEIL.js} +1 -1
- package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-YoD4tY5l.js → StudioPage-BjXFGSK4.js} +1 -1
- package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-DxjaAhb_.js → UsagePage-CP8F8BN7.js} +1 -1
- package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BTlkq_Uk.js → api-keys-2hc12Uz2.js} +1 -1
- package/dist/dashboard/assets/api-keys-2hc12Uz2.js.gz +0 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css +2 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css.gz +0 -0
- package/dist/dashboard/assets/assert-BMcj1xZE.js +1 -0
- package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DdbT_7Am.js → badge-DkRUuVfz.js} +1 -1
- package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
- package/dist/dashboard/assets/{banner-CsGN-nbm.js → banner-CDjW841Q.js} +1 -1
- package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
- package/dist/dashboard/assets/{card-mgZ_TLLZ.js → card-Bto5UlL9.js} +1 -1
- package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-A1-v8-03.js → cli-tools-DaH6g9hL.js} +1 -1
- package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-CJ4Zty89.js → clipboard-button-BYKBCzjk.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-B5L2jPQ_.js → dialog-DL0Zftwv.js} +1 -1
- package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-DkjUouAj.js → drawer-Dpx_UXaS.js} +1 -1
- package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
- package/dist/dashboard/assets/{entry-B-LVLV6o.js → entry-BV7-1NoM.js} +1 -1
- package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CAhdYl3B.js → entry-CGvdNU6r.js} +1 -1
- package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
- package/dist/dashboard/assets/entry-DScKYvZj.js +4 -0
- package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-CrsdLVsn.js → formatters-BS3DZlZj.js} +1 -1
- package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
- package/dist/dashboard/assets/{index-BU64Lqvp.js → index-SQnvvTWX.js} +2 -2
- package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2aDLyyg.js → inline-Dl-cuLK0.js} +1 -1
- package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
- package/dist/dashboard/assets/{input-BCVkdR9d.js → input-Bfx0EzRc.js} +1 -1
- package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
- package/dist/dashboard/assets/{layout-DNcAdMK-.js → layout-BxhbQ_JS.js} +1 -1
- package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-DgrIH0hw.js → list-row-D49Y4ptn.js} +1 -1
- package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
- package/dist/dashboard/assets/{live-Cae01iu8.js → live-CbVTdX9V.js} +1 -1
- package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
- package/dist/dashboard/assets/{network-CRib7H_2.js → network-JGvDveof.js} +1 -1
- package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BKQE8Xpz.js → numbers-Dw6kiew-.js} +1 -1
- package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-ecPvG_8g.js → pagination-iUoI8GeE.js} +1 -1
- package/dist/dashboard/assets/pagination-iUoI8GeE.js.gz +0 -0
- package/dist/dashboard/assets/providers/sensenova-intl.png +0 -0
- package/dist/dashboard/assets/providers/sensenova.png +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/quota-DiPD6UKH.js +1 -0
- package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
- package/dist/dashboard/assets/{routing-Ckm1UXuC.js → routing-8jZr-3WE.js} +1 -1
- package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
- package/dist/dashboard/assets/{select-ChoFWyKz.js → select-oO1FxMVb.js} +2 -2
- package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
- package/dist/dashboard/assets/{settings-CKjFPlKW.js → settings-CLr3kdIx.js} +1 -1
- package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DWCwU3fh.js → stack-Y3pDteZr.js} +1 -1
- package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
- package/dist/dashboard/assets/{state-Cg3VL5J_.js → state-CArX_Iak.js} +1 -1
- package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
- package/dist/dashboard/assets/{switch-Db70BMix.js → switch-DwlwmDO-.js} +1 -1
- package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BqdE0LuM.js → useMutation-DJ9GYEgf.js} +1 -1
- package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -53
- package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
- package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
- package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
- package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
- package/package.json +1 -1
- package/src/boot/app.ts +11 -14
- package/src/boot/dependencies.ts +5 -7
- package/src/boot/flush.ts +20 -0
- package/src/boot/lifecycle.ts +7 -5
- package/src/config/providers.ts +6 -5
- package/src/config/routing.ts +26 -0
- package/src/config/spec-metadata.ts +72 -0
- package/src/config/spec.ts +28 -8
- package/src/console/api-keys/contracts.ts +12 -1
- package/src/console/api-keys/routes.ts +40 -28
- 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/backup/nine-router.ts +71 -10
- package/src/console/backup/service.ts +8 -1
- package/src/console/cli-tools/service.ts +6 -12
- package/src/console/console-router-guards.ts +23 -16
- package/src/console/console-router.ts +1 -1
- package/src/console/domain-registration.ts +7 -4
- package/src/console/inferhub/client.ts +1 -1
- package/src/console/inferhub/normalize.ts +24 -28
- package/src/console/inferhub/types.ts +3 -0
- package/src/console/ip-bans/routes.ts +7 -13
- package/src/console/observability/live.ts +2 -2
- package/src/console/observability/store.ts +0 -53
- package/src/console/providers/catalog/contracts.ts +9 -3
- package/src/console/providers/catalog/model-operations.ts +2 -3
- package/src/console/providers/catalog/provider-operations.ts +1 -1
- package/src/console/providers/catalog/routes.ts +3 -2
- package/src/console/providers/catalog/store.ts +1914 -1902
- package/src/console/providers/detail/routes.ts +1 -0
- package/src/console/providers/detail/store.ts +4 -0
- package/src/console/providers/oauth/callback-listener.ts +30 -11
- package/src/console/providers/oauth/device-egress.ts +123 -0
- package/src/console/providers/oauth/routes.ts +123 -16
- package/src/console/quota/account-quota-global.ts +14 -24
- package/src/console/quota/account-quota-shared.ts +49 -0
- package/src/console/quota/account-quota-tenant.ts +11 -23
- package/src/console/quota/account-test-egress.ts +2 -2
- package/src/console/quota/daily-growth-job.ts +1 -1
- package/src/console/quota/quota-view.ts +19 -3
- package/src/console/routing/pools/contracts.ts +1 -1
- package/src/console/routing/pools/operations.ts +32 -47
- package/src/console/routing/pools/probe-verdict.ts +2 -2
- package/src/console/routing/pools/relay-deploy.ts +1 -1
- package/src/console/routing/pools/store.ts +4 -4
- package/src/console/shared/errors.ts +33 -6
- package/src/main.ts +2 -3
- package/src/network/client.ts +67 -0
- package/src/network/egress/bidi-session.ts +322 -0
- package/src/network/egress/dial.ts +201 -0
- package/src/network/egress/factory.ts +191 -0
- package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
- package/src/network/egress/tls-profile.ts +123 -0
- package/src/network/egress/ws.ts +175 -0
- package/src/network/pool/{byte-accounting.ts → accounting.ts} +3 -2
- package/src/network/{types.ts → pool/agent-config.ts} +1 -1
- package/src/network/pool/agent.ts +2 -2
- package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +95 -20
- package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
- package/src/network/pool/resolver.ts +10 -849
- package/src/network/pool/selector.ts +184 -41
- package/src/network/ssrf.ts +7 -7
- package/src/network/{retry.ts → util/retry.ts} +1 -1
- package/src/network/{rate-limit-verdict.ts → verdict.ts} +1 -1
- package/src/observability/event-log.ts +7 -361
- package/src/observability/performance-metrics.ts +9 -0
- package/src/observability/runtime-metrics.ts +14 -9
- package/src/observability/telemetry-buffer.ts +16 -26
- 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 +18 -0
- package/src/persistence/migrate.ts +6 -3
- package/src/persistence/schema.ts +17 -0
- package/src/persistence/ttl-cache.ts +21 -3
- package/src/protocol/codecs.ts +59 -2
- package/src/protocol/dsml.ts +34 -86
- package/src/protocol/markup-stream-parser.ts +195 -0
- package/src/protocol/reasoning-text.ts +19 -0
- package/src/protocol/request/chat.ts +10 -10
- package/src/protocol/request/gemini.ts +8 -2
- package/src/protocol/request/messages.ts +34 -22
- package/src/protocol/response/chat.ts +12 -20
- package/src/protocol/response/messages.ts +34 -7
- package/src/protocol/response/omp-tool-call-markup.ts +43 -86
- package/src/protocol/response/responses.ts +18 -2
- package/src/protocol/stop-reason.ts +23 -1
- package/src/protocol/think-split.ts +112 -0
- package/src/protocol/translation/dialect/qwen.ts +26 -58
- package/src/protocol/translation/quirks.ts +15 -0
- package/src/protocol/upstream/send-claude-messages.ts +2 -2
- package/src/providers/adapters/compatible-adapter.ts +76 -12
- package/src/providers/authentication/oauth-client.ts +40 -6
- package/src/providers/authentication/oauth-device-flow.ts +105 -11
- package/src/providers/authentication/oauth-flow-store.ts +82 -1
- package/src/providers/authentication/oauth-refresh-service.ts +766 -758
- package/src/providers/configured-provider.ts +148 -0
- package/src/providers/default-registry.ts +137 -36
- package/src/providers/discovery/discovery-types.ts +7 -0
- package/src/providers/discovery/probe-phases.ts +20 -6
- package/src/providers/discovery/probing-service.ts +22 -2
- 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 +16 -1
- package/src/providers/integrations/buddy/account.ts +969 -0
- package/src/providers/integrations/buddy/capabilities.ts +12 -12
- package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
- package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +176 -20
- package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
- package/src/providers/integrations/buddy/codebuddy/shared.ts +41 -26
- package/src/providers/integrations/buddy/headers.ts +311 -0
- package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
- package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
- package/src/providers/integrations/claude/claude-quota.ts +14 -17
- package/src/providers/integrations/cline/cline-oauth.ts +10 -5
- package/src/providers/integrations/cline/cline.ts +5 -6
- package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
- package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
- package/src/providers/integrations/clixad/provider.ts +145 -0
- package/src/providers/integrations/codex/codex-oauth.ts +8 -5
- package/src/providers/integrations/commandcode/quota.ts +22 -28
- package/src/providers/integrations/cursor/cursor-auth.ts +1 -9
- package/src/providers/integrations/cursor/cursor-exec.ts +9 -2
- package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
- package/src/providers/integrations/cursor/cursor-request.ts +26 -12
- package/src/providers/integrations/cursor/cursor-shared.ts +8 -5
- package/src/providers/integrations/cursor/cursor.ts +574 -110
- 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/deepai/deepai-catalog.ts +149 -0
- package/src/providers/integrations/deepai/deepai-common.ts +145 -0
- package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
- package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
- package/src/providers/integrations/deepai/provider.ts +42 -0
- package/src/providers/integrations/devin/devin-auth.ts +7 -0
- package/src/providers/integrations/devin/protocol/devin-proto.ts +1 -27
- package/src/providers/integrations/factory/factory-oauth.ts +33 -51
- 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 +5 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
- package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
- package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
- package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
- package/src/providers/integrations/freepi/freepi-session.ts +158 -0
- package/src/providers/integrations/freepi/freepi.ts +80 -54
- package/src/providers/integrations/github/github-discovery.ts +30 -13
- package/src/providers/integrations/github/github-oauth.ts +217 -44
- package/src/providers/integrations/github/github-quota.ts +22 -5
- package/src/providers/integrations/github/github.ts +40 -32
- package/src/providers/integrations/grok/grok-oauth.ts +52 -49
- 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 +3 -3
- package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +42 -13
- package/src/providers/integrations/hermes/index.ts +2 -0
- package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
- package/src/providers/integrations/kilo/kilo.ts +1 -1
- package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
- 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-oauth.ts +2 -2
- package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
- package/src/providers/integrations/kiro/kiro.ts +15 -19
- package/src/providers/integrations/muse/muse-oauth.ts +5 -5
- 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/openrouter/provider.ts +1 -1
- package/src/providers/integrations/perch/perch-credential.ts +25 -0
- package/src/providers/integrations/perch/perch-protocol.ts +0 -6
- package/src/providers/integrations/perch/perch-quota.ts +2 -16
- package/src/providers/integrations/perch/perch.ts +6 -16
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
- package/src/providers/integrations/qoder/qoder-adapter.ts +20 -125
- package/src/providers/integrations/qoder/qoder-auth.ts +109 -30
- package/src/providers/integrations/qoder/qoder-catalog.ts +21 -6
- package/src/providers/integrations/qoder/qoder-chat.ts +32 -29
- package/src/providers/integrations/qoder/qoder-checkin.ts +5 -5
- package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
- package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
- package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
- package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
- package/src/providers/integrations/qoder/qoder-types.ts +9 -0
- package/src/providers/integrations/qoder/qoder.ts +3 -26
- package/src/providers/integrations/sensenova/provider.ts +144 -0
- package/src/providers/integrations/trae/trae-protocol.ts +9 -5
- package/src/providers/integrations/trae/trae-quota.ts +21 -29
- package/src/providers/integrations/trae/trae-realm.ts +38 -0
- package/src/providers/integrations/trae/trae.ts +7 -24
- package/src/providers/integrations/vercel/provider.ts +117 -0
- package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
- 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 +205 -0
- package/src/providers/operations/account-error-classification.ts +199 -307
- package/src/providers/operations/account-health-service.ts +82 -1
- package/src/providers/operations/account-refresh-status.ts +36 -37
- package/src/providers/operations/account-tier.ts +15 -16
- package/src/providers/operations/client-version-resolver.ts +1 -1
- package/src/providers/operations/client-versions.ts +100 -81
- package/src/providers/operations/cooldown-rules.ts +11 -16
- package/src/providers/operations/oauth-refresh-lead.ts +46 -22
- package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
- package/src/providers/operations/provider-catalog-service.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +118 -51
- 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/operations/session-resolution.ts +17 -1
- package/src/providers/provider-contracts.ts +6 -4
- package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
- package/src/providers/provider-metadata.ts +70 -10
- package/src/providers/provider-probe.ts +15 -1
- package/src/providers/provider-registry.ts +42 -33
- package/src/providers/quota/quota-contracts.ts +2 -0
- 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 +100 -21
- package/src/security/api-key-auth-cache.ts +1 -1
- package/src/security/api-key-auth.ts +6 -2
- package/src/security/ip-boundary.ts +1 -1
- package/src/transport/dispatch/attempt-finalize.ts +15 -14
- package/src/transport/dispatch/attempt-loop.ts +73 -19
- package/src/transport/dispatch/generation.ts +2 -8
- package/src/transport/dispatch/handler-deps.ts +1 -1
- package/src/transport/dispatch/leases.ts +1 -1
- package/src/transport/dispatch/native-shared.ts +25 -0
- package/src/transport/dispatch/proxy-request.ts +8 -46
- package/src/transport/dispatch/responses-compact.ts +2 -8
- package/src/transport/dispatch/retry-policy.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +10 -7
- package/src/transport/dispatch/streaming/stream.ts +9 -37
- package/src/transport/dispatch/systemone.ts +2 -8
- package/src/transport/dispatch/token-count.ts +2 -6
- package/src/transport/dispatch/upstream.ts +63 -0
- package/src/transport/drain-endpoint.ts +31 -4
- package/src/transport/failure-policy.ts +16 -30
- 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 +393 -32
- package/src/transport/shutdown-notice.ts +1 -1
- 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 +7 -1
- 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 +22 -5
- package/src/transport/surface/responses/parse.ts +3 -11
- package/src/workers/buddy-growth-tasks.ts +8 -8
- package/src/workers/daily-checkin.ts +4 -4
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/zcode-gift-claim.ts +1 -1
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +0 -8
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js +0 -9
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js +0 -1
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css +0 -2
- package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js +0 -1
- package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
- package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
- package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
- package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
- package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
- package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
- package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js +0 -4
- package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
- package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
- package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
- package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
- package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
- package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
- package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
- package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
- package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
- package/dist/dashboard/assets/pagination-ecPvG_8g.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-Ckm1UXuC.js.gz +0 -0
- package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
- package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
- package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
- package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
- package/src/network/pool-health.ts +0 -210
- package/src/providers/bundled-discovery-providers.ts +0 -98
- package/src/providers/configured-openai-providers.ts +0 -120
- package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
- package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
- package/src/providers/integrations/buddy/buddy-identity.ts +0 -300
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
- package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
- package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -185
- package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
- package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -175
- package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
- package/src/providers/integrations/qoder/chat-proto.ts +0 -328
- package/src/providers/integrations/qoder/qoder-grpc.ts +0 -871
- package/src/providers/integrations/qoder/qoder-ide.ts +0 -588
- 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-B7kIWFm5.js → app-C0gTXEoN.js} +0 -0
- /package/dist/dashboard/assets/{app-B7kIWFm5.js.gz → app-C0gTXEoN.js.gz} +0 -0
- /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
- /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
- /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
- /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
- /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
- /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
|
@@ -131,25 +131,25 @@ export class MuseCodeOAuthClient extends OAuthDeviceFlow {
|
|
|
131
131
|
protected override readonly tokenUrl = MUSE_CODE_DEVICE_TOKEN_URL;
|
|
132
132
|
protected override readonly scopes = "";
|
|
133
133
|
|
|
134
|
-
override async startDeviceAuth(
|
|
134
|
+
override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
|
|
135
135
|
return this.startGenericDeviceAuth({
|
|
136
136
|
tokenUrl: MUSE_CODE_DEVICE_TOKEN_URL,
|
|
137
137
|
deviceAuthUrl: MUSE_CODE_DEVICE_AUTHORIZE_URL,
|
|
138
138
|
clientId: MUSE_CODE_CLIENT_ID,
|
|
139
139
|
formHeaders: metaHeaders,
|
|
140
|
-
});
|
|
140
|
+
}, context);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
override async pollDeviceAuth(
|
|
144
144
|
deviceAuthId: string,
|
|
145
|
-
|
|
145
|
+
context?: OAuthDeviceFlowContext,
|
|
146
146
|
): Promise<OAuthDevicePollResult> {
|
|
147
147
|
const result = await this.pollGenericDeviceAuth(deviceAuthId, {
|
|
148
148
|
tokenUrl: MUSE_CODE_DEVICE_TOKEN_URL,
|
|
149
149
|
deviceAuthUrl: MUSE_CODE_DEVICE_AUTHORIZE_URL,
|
|
150
150
|
clientId: MUSE_CODE_CLIENT_ID,
|
|
151
151
|
formHeaders: metaHeaders,
|
|
152
|
-
});
|
|
152
|
+
}, context);
|
|
153
153
|
if (result.status !== "complete") return result;
|
|
154
154
|
const accessToken = result.result.access;
|
|
155
155
|
// The key mint runs after approval: a 429, a payment gate, or a transient
|
|
@@ -157,7 +157,7 @@ export class MuseCodeOAuthClient extends OAuthDeviceFlow {
|
|
|
157
157
|
// exception that escapes the dashboard's poll request.
|
|
158
158
|
let keyPayload;
|
|
159
159
|
try {
|
|
160
|
-
keyPayload = await requestMuseCodeKey(accessToken, this.
|
|
160
|
+
keyPayload = await requestMuseCodeKey(accessToken, this.fetchFor(context));
|
|
161
161
|
} catch (error) {
|
|
162
162
|
return { status: "failed", reason: error instanceof Error ? error.message : "Muse Code key exchange failed" };
|
|
163
163
|
}
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
* All six serve the chat wire (none is a Responses-native id), and all report
|
|
17
17
|
* `supported_in_api: true` on the live roster.
|
|
18
18
|
*/
|
|
19
|
-
import { defineModel } from "
|
|
20
|
-
import type { ModelDefinition } from "
|
|
19
|
+
import { defineModel } from "../../provider-model-definition";
|
|
20
|
+
import type { ModelDefinition } from "../../provider-model-definition";
|
|
21
21
|
|
|
22
22
|
export const OLLAMACLOUD_PROVIDER_ID = "ollamacloud" as const;
|
|
23
23
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { FetchLike, ProviderQuotaResult } from "
|
|
2
|
-
import { getJson, record, text } from "
|
|
3
|
-
import { parseQuotaWindows } from "
|
|
1
|
+
import type { FetchLike, ProviderQuotaResult } from "../../quota/quota-contracts";
|
|
2
|
+
import { getJson, record, text } from "../../quota/quota-contracts";
|
|
3
|
+
import { parseQuotaWindows } from "../../quota/quota-window-parser";
|
|
4
4
|
|
|
5
5
|
async function fetchPlan(fetcher: FetchLike, headers: Record<string, string>): Promise<string> {
|
|
6
6
|
try {
|
|
@@ -9,7 +9,7 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
9
9
|
* can pin the exact identity sent before discovery resolves. Keep both in
|
|
10
10
|
* lockstep when the CLI releases.
|
|
11
11
|
*/
|
|
12
|
-
export const OPENCODE_CLIENT_VERSION = "2.2.
|
|
12
|
+
export const OPENCODE_CLIENT_VERSION = "2.2.7";
|
|
13
13
|
|
|
14
14
|
let lastTimestamp = 0;
|
|
15
15
|
let sequenceCounter = 0;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getJson,
|
|
3
|
+
isoDate,
|
|
3
4
|
type FetchLike,
|
|
4
5
|
type ProviderQuotaResult,
|
|
5
6
|
type ProviderQuotaWindow,
|
|
@@ -42,13 +43,6 @@ function labelFor(kind: string): string {
|
|
|
42
43
|
return kind.charAt(0).toUpperCase() + kind.slice(1);
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
function resetIso(value: string | null | undefined): string | null {
|
|
46
|
-
if (typeof value !== "string" || value.trim() === "") return null;
|
|
47
|
-
const parsed = Date.parse(value);
|
|
48
|
-
if (!Number.isFinite(parsed)) return null;
|
|
49
|
-
return new Date(parsed).toISOString();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
46
|
/**
|
|
53
47
|
* One window, or `undefined` when the upstream omitted what makes it usable.
|
|
54
48
|
*
|
|
@@ -75,7 +69,7 @@ function windowFor(
|
|
|
75
69
|
accounting: "independent",
|
|
76
70
|
usedPercent: percent,
|
|
77
71
|
remainingPercent: 100 - percent,
|
|
78
|
-
resetsAt:
|
|
72
|
+
resetsAt: isoDate(raw.resetsAt),
|
|
79
73
|
};
|
|
80
74
|
}
|
|
81
75
|
|
|
@@ -5,7 +5,7 @@ import { providerBaseUrl } from "../../provider-metadata";
|
|
|
5
5
|
import type { DiscoveryInput } from "../../discovery/discovery-types";
|
|
6
6
|
import { defineModel } from "../../provider-model-definition";
|
|
7
7
|
import { openrouterFreeRoster } from "../../discovery/openrouter-free-roster";
|
|
8
|
-
import { clineReasoningReplayPrepares } from "../buddy/
|
|
8
|
+
import { clineReasoningReplayPrepares } from "../buddy/chat";
|
|
9
9
|
|
|
10
10
|
const OPENROUTER_PROVIDER_ID = "openrouter" as const;
|
|
11
11
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Perch credential-envelope parsing.
|
|
2
|
+
//
|
|
3
|
+
// Perch accounts authenticate via Supabase OAuth (PKCE), and the stored secret
|
|
4
|
+
// is the OAuth envelope JSON with `accessToken` (or `access_token`). Both the
|
|
5
|
+
// dispatch adapter and the quota collector need the access token out of that
|
|
6
|
+
// envelope; the parse is shared here and each surface keeps its own fallback
|
|
7
|
+
// for a secret that is not an envelope.
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Reads the access token out of a stored Perch OAuth envelope.
|
|
11
|
+
*
|
|
12
|
+
* Returns the token when the secret parses as JSON and carries `accessToken`
|
|
13
|
+
* (or `access_token`) as a string; `null` when it is not such an envelope
|
|
14
|
+
* (not JSON, or JSON without a token field). The caller decides what a
|
|
15
|
+
* `null` means for its surface.
|
|
16
|
+
*/
|
|
17
|
+
export function parsePerchCredentialToken(secret: string): string | null {
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(secret) as Record<string, unknown>;
|
|
20
|
+
const found = parsed["accessToken"] ?? parsed["access_token"];
|
|
21
|
+
return typeof found === "string" ? found : null;
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -27,12 +27,6 @@ import { PERCH_STARTER_PINS } from "./perch-realm";
|
|
|
27
27
|
/** Default hosted model id when the account endpoint is unreachable. */
|
|
28
28
|
export const PERCH_DEFAULT_CHAT_MODEL = "wandb-kimi-k2-6";
|
|
29
29
|
|
|
30
|
-
/** App origin (model-call + ticket + account + plan endpoints). */
|
|
31
|
-
export const PERCH_APP_URL = "https://app.perchai.app";
|
|
32
|
-
|
|
33
|
-
/** The CLI user-agent the upstream recognizes as a CLI surface. */
|
|
34
|
-
export const PERCH_CLI_USER_AGENT = "perchai-cli/unknown";
|
|
35
|
-
|
|
36
30
|
export interface PerchTurnContext {
|
|
37
31
|
readonly userId: string | null;
|
|
38
32
|
readonly workspaceId: string | null;
|
|
@@ -11,21 +11,7 @@ import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../
|
|
|
11
11
|
import { cleanError, percentWindow, text } from "../../quota/quota-contracts";
|
|
12
12
|
import type { QuotaCollectionContext } from "../../quota/quota-support";
|
|
13
13
|
import { PERCH_APP_URL } from "./perch-realm";
|
|
14
|
-
|
|
15
|
-
interface PerchSessionCredential {
|
|
16
|
-
readonly accessToken?: string;
|
|
17
|
-
readonly access_token?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function credentialToken(credential: string): string {
|
|
21
|
-
try {
|
|
22
|
-
const parsed = JSON.parse(credential) as PerchSessionCredential;
|
|
23
|
-
const found = parsed.accessToken ?? parsed.access_token;
|
|
24
|
-
return typeof found === "string" ? found : "";
|
|
25
|
-
} catch {
|
|
26
|
-
return credential.trim();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
14
|
+
import { parsePerchCredentialToken } from "./perch-credential";
|
|
29
15
|
|
|
30
16
|
async function readAccount(
|
|
31
17
|
baseUrl: string,
|
|
@@ -61,7 +47,7 @@ export async function fetchPerchQuota(
|
|
|
61
47
|
_context?: QuotaCollectionContext,
|
|
62
48
|
): Promise<ProviderQuotaResult> {
|
|
63
49
|
try {
|
|
64
|
-
const token =
|
|
50
|
+
const token = parsePerchCredentialToken(credential) ?? "";
|
|
65
51
|
const account = await readAccount(PERCH_APP_URL, token, fetcher);
|
|
66
52
|
if (!account) {
|
|
67
53
|
return { source: "perch", plan: null, windows: [], error: "Perch account endpoint unavailable" };
|
|
@@ -27,9 +27,8 @@ import {
|
|
|
27
27
|
type ProviderId,
|
|
28
28
|
} from "../../provider-contracts";
|
|
29
29
|
import { usageFromProvider } from "../../provider-usage";
|
|
30
|
+
import { getPerchVersion } from "../../operations/client-versions";
|
|
30
31
|
import {
|
|
31
|
-
PERCH_APP_URL,
|
|
32
|
-
PERCH_CLI_USER_AGENT,
|
|
33
32
|
PERCH_SAFFRON_SYSTEM_PROMPT,
|
|
34
33
|
buildPerchModelCallBody,
|
|
35
34
|
parsePerchSse,
|
|
@@ -39,7 +38,8 @@ import {
|
|
|
39
38
|
PERCH_MODEL_PINS,
|
|
40
39
|
PERCH_MODEL_ROOST_TIERS,
|
|
41
40
|
} from "./perch-catalog";
|
|
42
|
-
import { PERCH_PROVIDER_ID } from "./perch-realm";
|
|
41
|
+
import { PERCH_APP_URL, PERCH_PROVIDER_ID } from "./perch-realm";
|
|
42
|
+
import { parsePerchCredentialToken } from "./perch-credential";
|
|
43
43
|
|
|
44
44
|
interface PerchAdapterOptions {
|
|
45
45
|
readonly providerId?: string;
|
|
@@ -96,7 +96,7 @@ export class PerchAdapter implements ProviderAdapter {
|
|
|
96
96
|
headers: {
|
|
97
97
|
authorization: `Bearer ${access}`,
|
|
98
98
|
"content-type": "application/json",
|
|
99
|
-
"user-agent":
|
|
99
|
+
"user-agent": `perchai-cli/${getPerchVersion()}`,
|
|
100
100
|
"x-perch-locale": "en",
|
|
101
101
|
},
|
|
102
102
|
body: JSON.stringify({ surface: "cli", profile: "standard" }),
|
|
@@ -126,7 +126,7 @@ export class PerchAdapter implements ProviderAdapter {
|
|
|
126
126
|
}
|
|
127
127
|
const secret = readCredentialSecret(context.credential, "Perch credential is required");
|
|
128
128
|
const modelId = candidate.model_id || request.model || "standard";
|
|
129
|
-
const access =
|
|
129
|
+
const access = parsePerchCredentialToken(secret) ?? secret.trim();
|
|
130
130
|
// Probes pass a validated outbound fetch (with capture wrapped around it);
|
|
131
131
|
// live dispatch passes one too. `globalThis.fetch` here silently split the
|
|
132
132
|
// probe from the route's egress policy and left Request Detail empty.
|
|
@@ -164,7 +164,7 @@ export class PerchAdapter implements ProviderAdapter {
|
|
|
164
164
|
authorization: `Bearer ${access}`,
|
|
165
165
|
"content-type": "application/json",
|
|
166
166
|
accept: "text/event-stream",
|
|
167
|
-
"user-agent":
|
|
167
|
+
"user-agent": `perchai-cli/${getPerchVersion()}`,
|
|
168
168
|
"x-perch-locale": "en",
|
|
169
169
|
"x-perch-call-delta": "1",
|
|
170
170
|
"x-perch-turn-ticket": ticket.ticket,
|
|
@@ -281,16 +281,6 @@ function normalizePerchUsage(usage: Record<string, unknown>): Record<string, unk
|
|
|
281
281
|
return out;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
function parseAccessToken(secret: string): string {
|
|
285
|
-
try {
|
|
286
|
-
const parsed = JSON.parse(secret) as Record<string, unknown>;
|
|
287
|
-
const found = parsed["accessToken"] ?? parsed["access_token"];
|
|
288
|
-
return typeof found === "string" ? found : secret.trim();
|
|
289
|
-
} catch {
|
|
290
|
-
return secret.trim();
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
284
|
/**
|
|
295
285
|
* Builds an upstream verdict for the Perch surface. `origin: "upstream"` is
|
|
296
286
|
* what lets the account health machine see the failure: `attempt-finalize`
|
|
@@ -15,11 +15,15 @@
|
|
|
15
15
|
* 401; verified live 2026-11-04).
|
|
16
16
|
*/
|
|
17
17
|
import type { CartethyiaDatabase } from "../../../persistence/db-handle";
|
|
18
|
-
import type { ValidatedFetch } from "../../../network/
|
|
19
|
-
import { getQoderVersion } from "../../operations/client-versions";
|
|
18
|
+
import type { ValidatedFetch } from "../../../network/egress/fetch";
|
|
20
19
|
import { qoderRealmForProvider, QODER_REALMS } from "./qoder-realm";
|
|
20
|
+
import { exchangeQoderPatForHost } from "./qoder-auth";
|
|
21
21
|
import {
|
|
22
|
+
asNumber,
|
|
23
|
+
asRecord,
|
|
22
24
|
buildCampaignHeaders,
|
|
25
|
+
normalizeCampaign,
|
|
26
|
+
safeParse,
|
|
23
27
|
} from "./qoder-checkin";
|
|
24
28
|
import { qoderMachineHeaders } from "./qoder-umid";
|
|
25
29
|
import {
|
|
@@ -38,31 +42,6 @@ export const QODER_CREDIT_PROVIDER_IDS = ["qoder", "qodercn"] as const;
|
|
|
38
42
|
|
|
39
43
|
const PAT_PREFIX = "pt-";
|
|
40
44
|
|
|
41
|
-
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
42
|
-
if (typeof value !== "object" || value === null) return null;
|
|
43
|
-
return value as Record<string, unknown>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function asNumber(value: unknown): number | null {
|
|
47
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
48
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
49
|
-
const parsed = Number(value);
|
|
50
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function normalizeCampaign(raw: Record<string, unknown>): Record<string, unknown> {
|
|
56
|
-
const benefit = asRecord(raw["benefit"]);
|
|
57
|
-
return {
|
|
58
|
-
campaign_id: String(raw["campaignId"] ?? raw["campaign_id"] ?? ""),
|
|
59
|
-
campaign_key: String(raw["campaignKey"] ?? raw["campaign_key"] ?? ""),
|
|
60
|
-
action_type: String(raw["actionType"] ?? raw["action_type"] ?? ""),
|
|
61
|
-
claim_status: String(raw["claimStatus"] ?? raw["claim_status"] ?? ""),
|
|
62
|
-
...(benefit !== null ? { benefit } : {}),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
45
|
/**
|
|
67
46
|
* Exchanges a PAT (`pt-…`) for a job token (`jt-…`) on the realm's openapi
|
|
68
47
|
* host. Returns the job token, or `null` when the exchange fails.
|
|
@@ -70,7 +49,8 @@ function normalizeCampaign(raw: Record<string, unknown>): Record<string, unknown
|
|
|
70
49
|
* The real client binds the machine identity at exchange time
|
|
71
50
|
* (`getMachineIdentityRequestFields`), so `machine_id` + `machine_token` are
|
|
72
51
|
* sent when available — the job token is then bound to that machine. The
|
|
73
|
-
*
|
|
52
|
+
* request is the shared `exchangeQoderPatForHost` (same v1 body and client
|
|
53
|
+
* identity headers as the auth path), parameterized by this site's own host.
|
|
74
54
|
*/
|
|
75
55
|
async function exchangePatToJobToken(
|
|
76
56
|
base: string,
|
|
@@ -78,30 +58,17 @@ async function exchangePatToJobToken(
|
|
|
78
58
|
fetcher: ValidatedFetch,
|
|
79
59
|
machine?: { readonly id?: string; readonly token?: string },
|
|
80
60
|
): Promise<string | null> {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"Cosy-Version": version,
|
|
93
|
-
},
|
|
94
|
-
body: JSON.stringify(body),
|
|
95
|
-
signal: AbortSignal.timeout(25_000),
|
|
96
|
-
});
|
|
97
|
-
if (!res.ok) return null;
|
|
98
|
-
const payload: unknown = await res.json().catch(() => null);
|
|
99
|
-
const rec = asRecord(payload);
|
|
100
|
-
const token = String(rec?.["token"] ?? rec?.["device_token"] ?? rec?.["access_token"] ?? "");
|
|
101
|
-
return token.length > 0 ? token : null;
|
|
102
|
-
} catch {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
61
|
+
const exchanged = await exchangeQoderPatForHost({
|
|
62
|
+
pat,
|
|
63
|
+
url: `${base}/api/v1/jobToken/exchange`,
|
|
64
|
+
...(machine === undefined ? {} : { machine }),
|
|
65
|
+
fetcher,
|
|
66
|
+
signal: AbortSignal.timeout(25_000),
|
|
67
|
+
tokenKeys: ["token", "device_token", "access_token"],
|
|
68
|
+
});
|
|
69
|
+
if (exchanged === null) return null;
|
|
70
|
+
if (!exchanged.response.ok) return null;
|
|
71
|
+
return exchanged.result?.token ?? null;
|
|
105
72
|
}
|
|
106
73
|
|
|
107
74
|
/**
|
|
@@ -248,11 +215,3 @@ export async function claimQoderCredits(args: {
|
|
|
248
215
|
return { state: "error", amount: 0, detail: "Claim request failed." };
|
|
249
216
|
}
|
|
250
217
|
}
|
|
251
|
-
|
|
252
|
-
function safeParse(raw: string): unknown {
|
|
253
|
-
try {
|
|
254
|
-
return JSON.parse(raw);
|
|
255
|
-
} catch {
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Qoder adapter — transport selection, credential resolution, dispatch.
|
|
3
3
|
*
|
|
4
|
-
* Surface contract (2026-11-07):
|
|
4
|
+
* Surface contract (2026-11-07, verified live 2026-11-11):
|
|
5
5
|
* - `cosy` (default, production): `/algo/.../agent_chat_generation` on
|
|
6
|
-
*
|
|
7
|
-
* verified alive.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* ~256 KiB body cap cannot carry the 1M window the roster declares. Dispatch
|
|
15
|
-
* reaches it only when `probe_surface: "ide"` is set (discovery probes) or
|
|
16
|
-
* the option `surface: "ide"` is forced explicitly for local testing.
|
|
6
|
+
* the realm's token-kind-specific gateway set (legacy COSY) — the only
|
|
7
|
+
* surface verified alive. A PAT is exchanged to a job token first; job
|
|
8
|
+
* tokens use `api2` → `api1` only, since `api3` rejects them with
|
|
9
|
+
* `105 Login expired` (403). Accepts free-tier rows (`lite`).
|
|
10
|
+
* - The gRPC surface (`model.chat.ChatService/ChatCompletionStream`) and the
|
|
11
|
+
* IDE surface (`/model/v1/chat/completions`) are dead on every host that
|
|
12
|
+
* resolves (gRPC: UNIMPLEMENTED; IDE: 404) since 2026-11-07 and are not
|
|
13
|
+
* dispatched anymore.
|
|
17
14
|
*/
|
|
18
|
-
import { randomUUID } from "node:crypto";
|
|
19
15
|
import { GatewayError } from "../../../protocol/gateway-error";
|
|
20
16
|
import type { CanonicalEvent, CanonicalRequest } from "../../../protocol/canonical-model";
|
|
21
17
|
import {
|
|
@@ -28,19 +24,16 @@ import {
|
|
|
28
24
|
} from "../../provider-contracts";
|
|
29
25
|
import { abortGatewayError, createUpstreamDeadlineLifecycle } from "../../operations/upstream-deadline";
|
|
30
26
|
import { getQoderVersion } from "../../operations/client-versions";
|
|
31
|
-
import { qoderMachineHostname, qoderMachineOs } from "../../operations/cli-platform";
|
|
32
27
|
import { mapUpstreamHttpError } from "../../../transport/failure-policy";
|
|
33
|
-
import { QODER_CN_MODEL_CONFIGS, QODER_INTL_MODEL_CONFIGS
|
|
28
|
+
import { QODER_CN_MODEL_CONFIGS, QODER_INTL_MODEL_CONFIGS } from "./qoder-catalog";
|
|
34
29
|
import { isQoderVirtualModel, qoderDispatchModelId } from "./qoder-virtual";
|
|
35
30
|
import { isQoderPat, qoderOAuthAuth, qoderSessionId, resolveQoderAuth } from "./qoder-auth";
|
|
36
31
|
import { buildQoderRequest, callQoder } from "./qoder-chat";
|
|
37
|
-
import {
|
|
38
|
-
import { buildQoderIdeRequest, qoderBodyToCanonicalEvents } from "./qoder-ide";
|
|
32
|
+
import { qoderBodyToCanonicalEvents } from "./qoder-sse";
|
|
39
33
|
import { estimateQoderSessionCache } from "./qoder-cache";
|
|
40
34
|
import {
|
|
41
35
|
QODER_CHAT_PATH,
|
|
42
36
|
QODER_PROVIDER_ID,
|
|
43
|
-
qoderIdeChatUrl,
|
|
44
37
|
qoderInferenceCandidates,
|
|
45
38
|
type QoderRealm,
|
|
46
39
|
} from "./qoder-realm";
|
|
@@ -56,48 +49,23 @@ interface QoderAdapterOptions {
|
|
|
56
49
|
* Which chat transport to dispatch on.
|
|
57
50
|
*
|
|
58
51
|
* `cosy` (default) = legacy COSY (`/algo/.../agent_chat_generation`) on
|
|
59
|
-
* `api1` → `api2` → `api3
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* — **no longer the default**; its host is not verified alive.
|
|
65
|
-
*
|
|
66
|
-
* `ide` forces `/model/v1` — **probe/testing only, never production**
|
|
67
|
-
* (see module doc).
|
|
52
|
+
* the realm's gateway candidates (device tokens: `api1` → `api2` → `api3`;
|
|
53
|
+
* job tokens minted from a PAT: `api2` → `api1` only — `api3` answers
|
|
54
|
+
* `105 Login expired`). The `api2-v2` model-server host is NXDOMAIN, and
|
|
55
|
+
* the gRPC service is UNIMPLEMENTED on every host that resolves. This path
|
|
56
|
+
* also accepts free-tier rows (`lite`).
|
|
68
57
|
*/
|
|
69
|
-
readonly surface?: "
|
|
58
|
+
readonly surface?: "cosy";
|
|
70
59
|
}
|
|
71
60
|
|
|
72
61
|
/** Resolves the transport for one dispatch. Exported for tests. */
|
|
73
|
-
export function resolveQoderTransport(
|
|
74
|
-
|
|
75
|
-
modelConfig: QoderModelConfig,
|
|
76
|
-
): "grpc" | "cosy" | "ide" {
|
|
77
|
-
const chosen = surface ?? "cosy";
|
|
78
|
-
// `ide` is an explicit testing override — never auto-selected, and it does
|
|
79
|
-
// not apply the free-tier rule (a forced IDE call is a test call).
|
|
80
|
-
if (chosen === "ide") return "ide";
|
|
81
|
-
// Free-tier rows (`lite`) are refused on gRPC with `quota exceeded` (116);
|
|
82
|
-
// they must use the legacy COSY surface.
|
|
83
|
-
if (chosen === "grpc" && modelConfig.is_free === true) return "cosy";
|
|
84
|
-
return chosen;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* The widest selectable context window for one model key (from the live list's
|
|
89
|
-
* `context_config`; falls back to the static roster's stated window).
|
|
90
|
-
*/
|
|
91
|
-
function qoderMaxContextFor(modelId: string, modelConfig: QoderModelConfig, realm: QoderRealm): number {
|
|
92
|
-
const discovered = qoderDiscoveredMaxContexts(realm).get(modelId);
|
|
93
|
-
if (discovered !== undefined) return discovered;
|
|
94
|
-
return modelConfig.max_input_tokens;
|
|
62
|
+
export function resolveQoderTransport(): "cosy" {
|
|
63
|
+
return "cosy";
|
|
95
64
|
}
|
|
96
65
|
|
|
97
66
|
class QoderAdapter implements ProviderAdapter {
|
|
98
67
|
readonly provider_id: ProviderId;
|
|
99
68
|
private readonly realm: QoderRealm;
|
|
100
|
-
private readonly surface: "grpc" | "cosy" | "ide";
|
|
101
69
|
private readonly fetchFn: typeof fetch;
|
|
102
70
|
|
|
103
71
|
constructor(options: QoderAdapterOptions = {}) {
|
|
@@ -110,7 +78,6 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
110
78
|
// mismatch that made `deepseek-v4.1-flash` fail while `qwen3.8-flash`
|
|
111
79
|
// passed. This is the same default `resolveQoderTransport(undefined)`
|
|
112
80
|
// already returns.
|
|
113
|
-
this.surface = options.surface ?? "cosy";
|
|
114
81
|
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
115
82
|
}
|
|
116
83
|
|
|
@@ -190,7 +157,7 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
190
157
|
|
|
191
158
|
const lifecycle = createUpstreamDeadlineLifecycle(context);
|
|
192
159
|
|
|
193
|
-
// Outbound fetch respects SSRF binding when provided
|
|
160
|
+
// Outbound fetch respects SSRF binding when provided.
|
|
194
161
|
const outboundFetch: typeof fetch = (context.outbound_fetch as unknown as typeof fetch) ?? this.fetchFn;
|
|
195
162
|
|
|
196
163
|
const fetcher: (url: string, init: RequestInit) => Promise<Response> = (url, init) =>
|
|
@@ -206,78 +173,6 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
206
173
|
: qoderOAuthAuth(secret, context.credential.auth_state, context.credential.account_id);
|
|
207
174
|
|
|
208
175
|
// Transport selection — see module doc for the surface contract.
|
|
209
|
-
const forceIde = this.surface === "ide" || candidate.probe_surface === "ide";
|
|
210
|
-
const useGrpc = !forceIde && resolveQoderTransport(this.surface, modelConfig) === "grpc";
|
|
211
|
-
|
|
212
|
-
if (forceIde) {
|
|
213
|
-
const ideUrl = qoderIdeChatUrl(this.realm);
|
|
214
|
-
const ideBody = buildQoderIdeRequest(modelId, request, modelConfig, auth, this.realm, version);
|
|
215
|
-
// Header fingerprint mirrors the desktop client's IDE surface (`PFn` /
|
|
216
|
-
// `Hc`): X-Request-ID normalized to uppercase, Cosy-Version, real
|
|
217
|
-
// machine OS, and the sanitized hostname on infer SSE requests. The
|
|
218
|
-
// User-Agent is the real IDE client's (`Bun/1.4.4`, live capture) and
|
|
219
|
-
// `X-Session-ID` matches the body's session id (sha256 userId:model).
|
|
220
|
-
const ideHostname = qoderMachineHostname();
|
|
221
|
-
const res = await fetcher(ideUrl, {
|
|
222
|
-
method: "POST",
|
|
223
|
-
headers: {
|
|
224
|
-
Authorization: `Bearer ${auth.securityOauthToken}`,
|
|
225
|
-
"Content-Type": "application/json",
|
|
226
|
-
Accept: "text/event-stream",
|
|
227
|
-
"User-Agent": "Bun/1.4.4",
|
|
228
|
-
"X-Request-ID": randomUUID().toUpperCase(),
|
|
229
|
-
"X-Session-ID": qoderSessionId(auth.userId, modelId),
|
|
230
|
-
"Cosy-Version": version,
|
|
231
|
-
"Cosy-ClientType": "5",
|
|
232
|
-
"Cosy-MachineOS": qoderMachineOs(),
|
|
233
|
-
...(ideHostname.length > 0 ? { "Cosy-MachineHostname": ideHostname } : {}),
|
|
234
|
-
},
|
|
235
|
-
body: JSON.stringify(ideBody),
|
|
236
|
-
});
|
|
237
|
-
if (!res.ok) throw await mapUpstreamHttpError(res, "qoder");
|
|
238
|
-
if (!res.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
239
|
-
lifecycle.release();
|
|
240
|
-
yield* this.#withSessionCacheEstimate(
|
|
241
|
-
qoderBodyToCanonicalEvents(res.body as ReadableStream<Uint8Array>, lifecycle.signal, modelId),
|
|
242
|
-
request,
|
|
243
|
-
qoderSessionId(auth.userId, modelId),
|
|
244
|
-
);
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (useGrpc) {
|
|
249
|
-
// The gRPC request mirrors the HTTP surfaces' OpenAI-shaped body, so
|
|
250
|
-
// the shared builder supplies the messages, tool declarations, and
|
|
251
|
-
// tool-choice knobs both surfaces expect. Without the `tools` array
|
|
252
|
-
// the upstream sees no toolset and answers with a DSML text block (or
|
|
253
|
-
// claims no tool exists) instead of a structured `tool_calls`.
|
|
254
|
-
const legacy = buildQoderRequest(modelId, request, modelConfig, auth, this.realm);
|
|
255
|
-
const grpcRequest = buildQoderGrpcRequest(
|
|
256
|
-
modelId,
|
|
257
|
-
request,
|
|
258
|
-
auth.userId,
|
|
259
|
-
qoderMaxContextFor(modelId, modelConfig, this.realm),
|
|
260
|
-
{
|
|
261
|
-
messages: legacy.messages as Array<Record<string, unknown>>,
|
|
262
|
-
tools: legacy.tools as Array<Record<string, unknown>>,
|
|
263
|
-
...(legacy.parameters === undefined
|
|
264
|
-
? {}
|
|
265
|
-
: { parameters: legacy.parameters as Record<string, unknown> }),
|
|
266
|
-
},
|
|
267
|
-
);
|
|
268
|
-
const grpcMetadata = qoderGrpcMetadata(auth);
|
|
269
|
-
lifecycle.release();
|
|
270
|
-
yield* this.#withSessionCacheEstimate(
|
|
271
|
-
qoderGrpcToCanonicalEvents(grpcRequest, grpcMetadata, this.realm, lifecycle.signal, context.provider_capture),
|
|
272
|
-
request,
|
|
273
|
-
qoderSessionId(auth.userId, modelId),
|
|
274
|
-
);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Legacy COSY hosts are tried in order (api1 → api2 → api3) with the
|
|
279
|
-
// endpoint path from discovery when present. A network-level failure on
|
|
280
|
-
// one host falls through to the next; an HTTP error response surfaces as-is.
|
|
281
176
|
const cosyCandidates = candidate.endpoint_path?.startsWith("http")
|
|
282
177
|
? [candidate.endpoint_path]
|
|
283
178
|
: qoderInferenceCandidates(this.realm, auth.securityOauthToken).map(
|