cartethyia 2.8.0 → 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 +3 -3
- package/dist/dashboard/assets/{BannedPage-Cc0vKpMG.js → BannedPage-Dq530awD.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-Bqj2aa1T.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-CXsaOAO2.js → CliToolsPage-DaZZ3cqP.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BeLpJ4ma.js → CombosPage-BlC_xdkX.js} +1 -1
- package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-DnV0uZ3m.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-CBxAjo63.js → CustomizationPage-7xT5yg2E.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-B5yEE0k6.js → HealthEventsModal-Clq8E9cW.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-BWFLECmz.js → InjectSystemPromptPanel-6Teoq9Se.js} +1 -1
- package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-CgoPc5B9.js → LoginPage-CZuI0MAR.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-D9bFQern.js → ModelPicker-C1hNTlkr.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
- package/dist/dashboard/assets/{OverviewPage-B_p4tL8k.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-D_0PiZtg.js → ProviderIcon-Ddhm1InL.js} +1 -1
- package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-CLI2bJTK.js → ProvidersPage-C3fYuu6i.js} +1 -1
- 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-DA-a1iSN.js → QuotaPage-D1RV_2BX.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-CctTga2C.js → SettingsPage-BzZAvYSX.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-febLU4r1.js → SetupPage--s-EfEIL.js} +1 -1
- package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-M6fvR5Wz.js → StudioPage-BjXFGSK4.js} +1 -1
- package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-nyeuRqz9.js → UsagePage-CP8F8BN7.js} +1 -1
- package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BFOlvrJC.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-CqTLw5e7.js → assert-BMcj1xZE.js} +1 -1
- package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
- package/dist/dashboard/assets/{badge-C83EqLB0.js → badge-DkRUuVfz.js} +1 -1
- package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
- package/dist/dashboard/assets/{banner-D1xn7pIe.js → banner-CDjW841Q.js} +1 -1
- package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
- package/dist/dashboard/assets/{card-DU3Lq5Lm.js → card-Bto5UlL9.js} +1 -1
- package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-Dw1U_drK.js → cli-tools-DaH6g9hL.js} +1 -1
- package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-CEL5Sg_J.js → clipboard-button-BYKBCzjk.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BjFR5UHy.js → dialog-DL0Zftwv.js} +1 -1
- package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-HvBW0AjI.js → drawer-Dpx_UXaS.js} +1 -1
- package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CECVELyy.js → entry-BV7-1NoM.js} +1 -1
- package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-DYYofQ7W.js → entry-CGvdNU6r.js} +1 -1
- package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BSu3gFlt.js → entry-DScKYvZj.js} +3 -3
- package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DLW4i8zK.js → formatters-BS3DZlZj.js} +1 -1
- package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
- package/dist/dashboard/assets/{index-1-fCqe64.js → index-SQnvvTWX.js} +2 -2
- package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DGvU2oLK.js → inline-Dl-cuLK0.js} +1 -1
- package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
- package/dist/dashboard/assets/{input-iNnO1X2K.js → input-Bfx0EzRc.js} +1 -1
- package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
- package/dist/dashboard/assets/{layout-0q8UGypb.js → layout-BxhbQ_JS.js} +1 -1
- package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-C0w2bQjJ.js → list-row-D49Y4ptn.js} +1 -1
- package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
- package/dist/dashboard/assets/{live-M5aJ_xCa.js → live-CbVTdX9V.js} +1 -1
- package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
- package/dist/dashboard/assets/{network-BKeQGT3K.js → network-JGvDveof.js} +1 -1
- package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-CivAk3da.js → numbers-Dw6kiew-.js} +1 -1
- package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-CpzvI5ep.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/{routing-BFQX2VBc.js → routing-8jZr-3WE.js} +1 -1
- package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
- package/dist/dashboard/assets/{select-CXbck0jG.js → select-oO1FxMVb.js} +1 -1
- package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
- package/dist/dashboard/assets/{settings-C_XvMHh9.js → settings-CLr3kdIx.js} +1 -1
- package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DggRiCxE.js → stack-Y3pDteZr.js} +1 -1
- package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
- package/dist/dashboard/assets/{state-ClFzfbYp.js → state-CArX_Iak.js} +1 -1
- package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
- package/dist/dashboard/assets/{switch-CTIFxKqS.js → switch-DwlwmDO-.js} +1 -1
- package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BClG3yWj.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 +51 -51
- package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
- package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
- package/package.json +1 -1
- package/src/boot/app.ts +1 -1
- package/src/boot/dependencies.ts +4 -6
- package/src/config/providers.ts +6 -5
- package/src/config/spec.ts +8 -8
- package/src/console/backup/nine-router.ts +71 -10
- package/src/console/backup/service.ts +8 -1
- package/src/console/console-router.ts +1 -1
- package/src/console/domain-registration.ts +7 -2
- package/src/console/inferhub/client.ts +1 -1
- package/src/console/observability/live.ts +2 -2
- 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 +2 -2
- package/src/console/providers/catalog/store.ts +1914 -1907
- 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-tenant.ts +5 -5
- package/src/console/quota/account-test-egress.ts +2 -2
- package/src/console/quota/daily-growth-job.ts +1 -1
- package/src/console/routing/pools/contracts.ts +1 -1
- package/src/console/routing/pools/operations.ts +1 -1
- 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/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/{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} +7 -7
- 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 +2 -2
- package/src/network/ssrf.ts +1 -1
- package/src/network/{retry.ts → util/retry.ts} +1 -1
- package/src/observability/telemetry-buffer.ts +1 -1
- package/src/persistence/schema.ts +9 -0
- package/src/persistence/ttl-cache.ts +1 -1
- package/src/protocol/codecs.ts +58 -1
- package/src/protocol/request/messages.ts +31 -18
- package/src/protocol/response/messages.ts +34 -7
- 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 +5 -0
- package/src/providers/authentication/oauth-client.ts +40 -6
- package/src/providers/authentication/oauth-device-flow.ts +7 -3
- package/src/providers/authentication/oauth-flow-store.ts +82 -1
- package/src/providers/authentication/oauth-refresh-service.ts +766 -739
- package/src/providers/default-registry.ts +76 -0
- package/src/providers/discovery/discovery-types.ts +7 -0
- package/src/providers/discovery/probe-phases.ts +6 -2
- package/src/providers/discovery/probing-service.ts +7 -0
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +11 -0
- package/src/providers/integrations/buddy/account.ts +969 -0
- package/src/providers/integrations/buddy/capabilities.ts +10 -10
- package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
- package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +5 -5
- package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
- package/src/providers/integrations/buddy/codebuddy/shared.ts +4 -4
- package/src/providers/integrations/buddy/{buddy-identity.ts → headers.ts} +138 -140
- 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/cline/cline-oauth.ts +10 -5
- package/src/providers/integrations/cline/cline.ts +3 -4
- 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/cursor/cursor-auth.ts +1 -1
- 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-shared.ts +1 -1
- package/src/providers/integrations/cursor/cursor.ts +485 -70
- 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/factory/factory-oauth.ts +16 -7
- package/src/providers/integrations/freebuff/freebuff.ts +2 -2
- 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 +4 -4
- package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
- package/src/providers/integrations/hermes/hermes.ts +34 -5
- 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-oauth.ts +2 -2
- package/src/providers/integrations/muse/muse-oauth.ts +5 -5
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/openrouter/provider.ts +1 -1
- package/src/providers/integrations/perch/perch-protocol.ts +0 -3
- package/src/providers/integrations/perch/perch.ts +3 -3
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
- package/src/providers/integrations/qoder/qoder-adapter.ts +19 -113
- package/src/providers/integrations/qoder/qoder-auth.ts +8 -1
- package/src/providers/integrations/qoder/qoder-catalog.ts +1 -1
- package/src/providers/integrations/qoder/qoder-chat.ts +25 -8
- package/src/providers/integrations/qoder/qoder-checkin.ts +1 -1
- 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 -25
- package/src/providers/integrations/sensenova/provider.ts +144 -0
- package/src/providers/integrations/trae/trae-quota.ts +15 -1
- 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/zro/zro.ts +2 -1
- package/src/providers/operations/account-refresh-status.ts +14 -0
- package/src/providers/operations/client-versions.ts +95 -30
- package/src/providers/operations/oauth-refresh-lead.ts +46 -22
- package/src/providers/operations/provider-caches.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +22 -25
- package/src/providers/operations/session-resolution.ts +17 -1
- package/src/providers/provider-contracts.ts +2 -2
- package/src/providers/provider-metadata.ts +30 -0
- package/src/providers/quota/quota-contracts.ts +2 -0
- package/src/providers/quota/refresh.ts +25 -3
- package/src/security/ip-boundary.ts +1 -1
- package/src/transport/dispatch/attempt-finalize.ts +1 -1
- package/src/transport/dispatch/attempt-loop.ts +3 -3
- package/src/transport/dispatch/handler-deps.ts +1 -1
- package/src/transport/dispatch/leases.ts +1 -1
- package/src/transport/dispatch/retry-policy.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +1 -1
- package/src/transport/dispatch/upstream.ts +1 -1
- package/src/transport/shutdown-notice.ts +1 -1
- 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-Cc0vKpMG.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +0 -8
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js +0 -9
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css +0 -2
- package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
- package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
- package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
- package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
- package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
- package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
- package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
- package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
- package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
- package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
- package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
- package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
- package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
- package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
- package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
- package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
- package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
- package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
- package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
- package/src/console/providers/catalog/discover-constants.ts +0 -6
- package/src/network/pool-health.ts +0 -198
- 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-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 -188
- package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
- package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -179
- 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 -870
- package/src/providers/integrations/qoder/qoder-ide.ts +0 -481
- /package/dist/dashboard/assets/{app-DIXLjojg.js → app-C0gTXEoN.js} +0 -0
- /package/dist/dashboard/assets/{app-DIXLjojg.js.gz → app-C0gTXEoN.js.gz} +0 -0
- /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
- /package/src/network/pool/{byte-accounting.ts → accounting.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
- /package/src/network/{rate-limit-verdict.ts → verdict.ts} +0 -0
|
@@ -4,7 +4,7 @@ import type { ApiKeyProviderSpec } from "../../configured-provider";
|
|
|
4
4
|
import { isRecord } from "../../../protocol/codecs";
|
|
5
5
|
import { boundedUpstreamArray, boundedUpstreamNumber, sanitizeUpstreamLabel } from "../../provider-sanitize";
|
|
6
6
|
import { getHermesVersion } from "../../operations/client-versions";
|
|
7
|
-
import { clineReasoningReplayPrepares } from "../buddy/
|
|
7
|
+
import { clineReasoningReplayPrepares } from "../buddy/chat";
|
|
8
8
|
|
|
9
9
|
const HERMES_PROVIDER_ID = "hermes" as const;
|
|
10
10
|
|
|
@@ -85,10 +85,16 @@ export const HERMES_MODELS: readonly ModelDefinition[] = [
|
|
|
85
85
|
// entirely and dispatching it answers 404 "This model's free period has
|
|
86
86
|
// ended" — a retired row, not a capacity blip, so it cannot be left in.
|
|
87
87
|
//
|
|
88
|
-
// `poolside/laguna-s-2.1:free
|
|
89
|
-
//
|
|
88
|
+
// `poolside/laguna-s-2.1:free` and `poolside/laguna-xs-2.1:free` are
|
|
89
|
+
// removed: they fail at dispatch.
|
|
90
|
+
//
|
|
91
|
+
// `stepfun/step-3.7-flash:free` is kept with a corrected output cap: the
|
|
92
|
+
// upstream serves a 256k context total (input + output), so the catalog's
|
|
93
|
+
// 256k `max_completion_tokens` makes every request exceed the endpoint and
|
|
94
|
+
// fail with "you requested about 259137 tokens". Clamped to 128k output the
|
|
95
|
+
// row fits the endpoint.
|
|
96
|
+
defineModel({ id: "stepfun/step-3.7-flash:free", ctx: 256_000, out: 128_000, reasoning: true, free: true, freeTier: true }),
|
|
90
97
|
defineModel({ id: "upstage/solar-mini4:free", ctx: 524288, out: 131072, reasoning: true, free: true, freeTier: true }),
|
|
91
|
-
defineModel({ id: "reflection/beam-1.0-501b-a23b:free", ctx: 262144, out: 131072, reasoning: true, free: true, freeTier: true }),
|
|
92
98
|
// Vision-corrected against the live catalog: `architecture.input_modalities`
|
|
93
99
|
// lists `image` for this row, so it was published without vision it has.
|
|
94
100
|
defineModel({ id: "meituan/longcat-2.5:free", ctx: 1048576, out: 131072, vision: true, reasoning: true, free: true, freeTier: true }),
|
|
@@ -162,16 +168,39 @@ export async function discoverHermesFreeModels(options: {
|
|
|
162
168
|
if (rows === undefined) return null;
|
|
163
169
|
|
|
164
170
|
const byId = new Map<string, ModelDefinition>();
|
|
171
|
+
// Ids the live catalog still lists as free but that fail at dispatch (404,
|
|
172
|
+
// retired, or a context window the catalog mis-states) — never surfaced as
|
|
173
|
+
// cards again.
|
|
174
|
+
const blockedIds = new Set([
|
|
175
|
+
"tencent/hy3:free",
|
|
176
|
+
"upstage/solar-pro4:free",
|
|
177
|
+
"meituan/longcat-2.0:free",
|
|
178
|
+
"stepfun/step-5-preview:free",
|
|
179
|
+
"poolside/laguna-s-2.1:free",
|
|
180
|
+
"poolside/laguna-xs-2.1:free",
|
|
181
|
+
"reflection/beam-1.0-501b-a23b:free",
|
|
182
|
+
]);
|
|
165
183
|
for (const entry of rows) {
|
|
166
184
|
if (!isRecord(entry)) continue;
|
|
167
185
|
const id = sanitizeUpstreamLabel(entry["id"]);
|
|
168
186
|
if (id === undefined) continue;
|
|
187
|
+
if (blockedIds.has(id)) continue;
|
|
169
188
|
if (!isFreePriced(entry)) continue;
|
|
170
189
|
|
|
171
190
|
const context = boundedUpstreamNumber(entry["context_length"], { min: 1, max: 1_000_000_000 }) ?? undefined;
|
|
172
191
|
const topProvider = isRecord(entry["top_provider"]) ? entry["top_provider"] : undefined;
|
|
173
|
-
const
|
|
192
|
+
const rawOutput =
|
|
174
193
|
boundedUpstreamNumber(topProvider?.["max_completion_tokens"], { min: 1, max: 1_000_000_000 }) ?? undefined;
|
|
194
|
+
// The upstream's `max_completion_tokens` is a total budget on some rows
|
|
195
|
+
// (e.g. `stepfun/step-3.7-flash:free` rejects any request above its 256k
|
|
196
|
+
// context). The gateway's output cap is a per-request token allowance, so
|
|
197
|
+
// clamp it under the context window to keep requests admissible.
|
|
198
|
+
const output =
|
|
199
|
+
rawOutput === undefined
|
|
200
|
+
? undefined
|
|
201
|
+
: context === undefined || rawOutput < context
|
|
202
|
+
? rawOutput
|
|
203
|
+
: Math.max(1, Math.floor(context * 0.75));
|
|
175
204
|
const architecture = isRecord(entry["architecture"]) ? entry["architecture"] : undefined;
|
|
176
205
|
const inputs = boundedUpstreamArray(architecture?.["input_modalities"]) ?? [];
|
|
177
206
|
const hasImage = inputs.includes("image");
|
|
@@ -109,8 +109,8 @@ export class KiloOAuthClient extends OAuthDeviceFlow {
|
|
|
109
109
|
super(fetchFn);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
override async startDeviceAuth(
|
|
113
|
-
const response = await this.
|
|
112
|
+
override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
|
|
113
|
+
const response = await this.fetchFor(context)(KILO_DEVICE_CODES_URL, {
|
|
114
114
|
method: "POST",
|
|
115
115
|
headers: { "content-type": "application/json", accept: "application/json" },
|
|
116
116
|
});
|
|
@@ -141,7 +141,7 @@ export class KiloOAuthClient extends OAuthDeviceFlow {
|
|
|
141
141
|
context?: OAuthDeviceFlowContext,
|
|
142
142
|
): Promise<OAuthDevicePollResult> {
|
|
143
143
|
const pollKey = context?.providerState ?? deviceAuthId;
|
|
144
|
-
const response = await this.
|
|
144
|
+
const response = await this.fetchFor(context)(`${KILO_DEVICE_CODES_URL}/${encodeURIComponent(pollKey)}`, {
|
|
145
145
|
headers: { accept: "application/json" },
|
|
146
146
|
});
|
|
147
147
|
if (response.status === 202) return { status: "pending" };
|
|
@@ -165,7 +165,7 @@ export class KiloOAuthClient extends OAuthDeviceFlow {
|
|
|
165
165
|
if (nonEmptyTrimmedString(payload.status) !== "approved" || accessToken === undefined) {
|
|
166
166
|
return { status: "pending" };
|
|
167
167
|
}
|
|
168
|
-
const orgId = await fetchOrganizationId(this.
|
|
168
|
+
const orgId = await fetchOrganizationId(this.fetchFor(context), accessToken, AbortSignal.timeout(10_000));
|
|
169
169
|
const label = accountLabelFrom(payload);
|
|
170
170
|
const result: OAuthExchangeResult = {
|
|
171
171
|
access: encodeKiloCredential(accessToken, orgId),
|
|
@@ -24,7 +24,7 @@ import type { CanonicalRequest } from "../../../protocol/canonical-model";
|
|
|
24
24
|
import { OpenAICompatibleAdapter, withBearerAuthentication } from "../../adapters/compatible-adapter";
|
|
25
25
|
import { providerBaseUrl } from "../../provider-metadata";
|
|
26
26
|
import { decodeKiloCredential } from "../../../security/credential-codec";
|
|
27
|
-
import { clineReasoningReplayPrepares } from "../buddy/
|
|
27
|
+
import { clineReasoningReplayPrepares } from "../buddy/chat";
|
|
28
28
|
|
|
29
29
|
export const KILO_PROVIDER_ID = "kilo" as const;
|
|
30
30
|
export const KILO_BASE_URL = providerBaseUrl("kilo");
|
|
@@ -123,13 +123,13 @@ export class KimiCodeOAuthClient extends OAuthDeviceFlow {
|
|
|
123
123
|
super(fetchFn);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
override async startDeviceAuth(
|
|
126
|
+
override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult & { providerState: string }> {
|
|
127
127
|
const deviceId = createKimiDeviceId();
|
|
128
128
|
// The Kimi device-authorization endpoint only parses
|
|
129
129
|
// `application/x-www-form-urlencoded` bodies — JSON `client_id` is
|
|
130
130
|
// rejected with `client_id is required`.
|
|
131
131
|
const body = new URLSearchParams({ client_id: KIMI_CODE_CLIENT_ID });
|
|
132
|
-
const response = await this.
|
|
132
|
+
const response = await this.fetchFor(context)(KIMI_CODE_DEVICE_AUTHORIZE_URL, {
|
|
133
133
|
method: "POST",
|
|
134
134
|
headers: { ...jsonHeaders(deviceId), "content-type": "application/x-www-form-urlencoded" },
|
|
135
135
|
body,
|
|
@@ -158,7 +158,7 @@ export class KimiCodeOAuthClient extends OAuthDeviceFlow {
|
|
|
158
158
|
device_code: deviceAuthId,
|
|
159
159
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
160
160
|
});
|
|
161
|
-
const response = await this.
|
|
161
|
+
const response = await this.fetchFor(context)(KIMI_CODE_TOKEN_URL, {
|
|
162
162
|
method: "POST",
|
|
163
163
|
headers: { ...jsonHeaders(deviceId), "content-type": "application/x-www-form-urlencoded" },
|
|
164
164
|
body,
|
|
@@ -504,7 +504,7 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
504
504
|
const region = assertAwsRegion(registration.region);
|
|
505
505
|
let response: Response;
|
|
506
506
|
try {
|
|
507
|
-
response = await this.
|
|
507
|
+
response = await this.fetchFor(context)(`https://oidc.${region}.amazonaws.com/token`, {
|
|
508
508
|
method: "POST",
|
|
509
509
|
headers: ssoOidcHeaders(),
|
|
510
510
|
body: JSON.stringify({
|
|
@@ -548,7 +548,7 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
548
548
|
// token is opaque, so ask the usage surface for the account email.
|
|
549
549
|
if (label === kiroDefaultLabel(registration.authMethod)) {
|
|
550
550
|
try {
|
|
551
|
-
const quota = await fetchKiroQuota(access, this.
|
|
551
|
+
const quota = await fetchKiroQuota(access, this.fetchFor(context), { auth_state: authStateOf(authState) });
|
|
552
552
|
if (quota.accountLabel !== undefined && quota.accountLabel.trim().length > 0) {
|
|
553
553
|
label = quota.accountLabel;
|
|
554
554
|
}
|
|
@@ -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
|
}
|
|
@@ -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;
|
|
@@ -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
|
|
|
@@ -27,9 +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
|
-
/** The CLI user-agent the upstream recognizes as a CLI surface. */
|
|
31
|
-
export const PERCH_CLI_USER_AGENT = "perchai-cli/unknown";
|
|
32
|
-
|
|
33
30
|
export interface PerchTurnContext {
|
|
34
31
|
readonly userId: string | null;
|
|
35
32
|
readonly workspaceId: string | null;
|
|
@@ -27,8 +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_CLI_USER_AGENT,
|
|
32
32
|
PERCH_SAFFRON_SYSTEM_PROMPT,
|
|
33
33
|
buildPerchModelCallBody,
|
|
34
34
|
parsePerchSse,
|
|
@@ -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" }),
|
|
@@ -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,
|
|
@@ -15,7 +15,7 @@
|
|
|
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/
|
|
18
|
+
import type { ValidatedFetch } from "../../../network/egress/fetch";
|
|
19
19
|
import { qoderRealmForProvider, QODER_REALMS } from "./qoder-realm";
|
|
20
20
|
import { exchangeQoderPatForHost } from "./qoder-auth";
|
|
21
21
|
import {
|
|
@@ -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
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,38 +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;
|
|
62
|
+
export function resolveQoderTransport(): "cosy" {
|
|
63
|
+
return "cosy";
|
|
85
64
|
}
|
|
86
65
|
|
|
87
66
|
class QoderAdapter implements ProviderAdapter {
|
|
88
67
|
readonly provider_id: ProviderId;
|
|
89
68
|
private readonly realm: QoderRealm;
|
|
90
|
-
private readonly surface: "grpc" | "cosy" | "ide";
|
|
91
69
|
private readonly fetchFn: typeof fetch;
|
|
92
70
|
|
|
93
71
|
constructor(options: QoderAdapterOptions = {}) {
|
|
@@ -100,7 +78,6 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
100
78
|
// mismatch that made `deepseek-v4.1-flash` fail while `qwen3.8-flash`
|
|
101
79
|
// passed. This is the same default `resolveQoderTransport(undefined)`
|
|
102
80
|
// already returns.
|
|
103
|
-
this.surface = options.surface ?? "cosy";
|
|
104
81
|
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
105
82
|
}
|
|
106
83
|
|
|
@@ -180,7 +157,7 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
180
157
|
|
|
181
158
|
const lifecycle = createUpstreamDeadlineLifecycle(context);
|
|
182
159
|
|
|
183
|
-
// Outbound fetch respects SSRF binding when provided
|
|
160
|
+
// Outbound fetch respects SSRF binding when provided.
|
|
184
161
|
const outboundFetch: typeof fetch = (context.outbound_fetch as unknown as typeof fetch) ?? this.fetchFn;
|
|
185
162
|
|
|
186
163
|
const fetcher: (url: string, init: RequestInit) => Promise<Response> = (url, init) =>
|
|
@@ -196,77 +173,6 @@ class QoderAdapter implements ProviderAdapter {
|
|
|
196
173
|
: qoderOAuthAuth(secret, context.credential.auth_state, context.credential.account_id);
|
|
197
174
|
|
|
198
175
|
// Transport selection — see module doc for the surface contract.
|
|
199
|
-
const forceIde = this.surface === "ide" || candidate.probe_surface === "ide";
|
|
200
|
-
const useGrpc = !forceIde && resolveQoderTransport(this.surface, modelConfig) === "grpc";
|
|
201
|
-
|
|
202
|
-
if (forceIde) {
|
|
203
|
-
const ideUrl = qoderIdeChatUrl(this.realm);
|
|
204
|
-
const ideBody = buildQoderIdeRequest(modelId, request, modelConfig, auth, this.realm, version);
|
|
205
|
-
// Header fingerprint mirrors the desktop client's IDE surface (`PFn` /
|
|
206
|
-
// `Hc`): X-Request-ID normalized to uppercase, Cosy-Version, real
|
|
207
|
-
// machine OS, and the sanitized hostname on infer SSE requests. The
|
|
208
|
-
// User-Agent is the real IDE client's (`Bun/1.4.4`, live capture) and
|
|
209
|
-
// `X-Session-ID` matches the body's session id (sha256 userId:model).
|
|
210
|
-
const ideHostname = qoderMachineHostname();
|
|
211
|
-
const res = await fetcher(ideUrl, {
|
|
212
|
-
method: "POST",
|
|
213
|
-
headers: {
|
|
214
|
-
Authorization: `Bearer ${auth.securityOauthToken}`,
|
|
215
|
-
"Content-Type": "application/json",
|
|
216
|
-
Accept: "text/event-stream",
|
|
217
|
-
"User-Agent": "Bun/1.4.4",
|
|
218
|
-
"X-Request-ID": randomUUID().toUpperCase(),
|
|
219
|
-
"X-Session-ID": qoderSessionId(auth.userId, modelId),
|
|
220
|
-
"Cosy-Version": version,
|
|
221
|
-
"Cosy-ClientType": "5",
|
|
222
|
-
"Cosy-MachineOS": qoderMachineOs(),
|
|
223
|
-
...(ideHostname.length > 0 ? { "Cosy-MachineHostname": ideHostname } : {}),
|
|
224
|
-
},
|
|
225
|
-
body: JSON.stringify(ideBody),
|
|
226
|
-
});
|
|
227
|
-
if (!res.ok) throw await mapUpstreamHttpError(res, "qoder");
|
|
228
|
-
if (!res.body) throw new GatewayError("platform_unavailable", 502, "Qoder returned empty body (request {requestId}). The upstream came back empty-handed — retry 🌙", {}, "upstream");
|
|
229
|
-
lifecycle.release();
|
|
230
|
-
yield* this.#withSessionCacheEstimate(
|
|
231
|
-
qoderBodyToCanonicalEvents(res.body as ReadableStream<Uint8Array>, lifecycle.signal, modelId),
|
|
232
|
-
request,
|
|
233
|
-
qoderSessionId(auth.userId, modelId),
|
|
234
|
-
);
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (useGrpc) {
|
|
239
|
-
// The gRPC request mirrors the HTTP surfaces' OpenAI-shaped body, so
|
|
240
|
-
// the shared builder supplies the messages, tool declarations, and
|
|
241
|
-
// tool-choice knobs both surfaces expect. Without the `tools` array
|
|
242
|
-
// the upstream sees no toolset and answers with a DSML text block (or
|
|
243
|
-
// claims no tool exists) instead of a structured `tool_calls`.
|
|
244
|
-
const legacy = buildQoderRequest(modelId, request, modelConfig, auth, this.realm);
|
|
245
|
-
const grpcRequest = buildQoderGrpcRequest(
|
|
246
|
-
modelId,
|
|
247
|
-
request,
|
|
248
|
-
auth.userId,
|
|
249
|
-
{
|
|
250
|
-
messages: legacy.messages as Array<Record<string, unknown>>,
|
|
251
|
-
tools: legacy.tools as Array<Record<string, unknown>>,
|
|
252
|
-
...(legacy.parameters === undefined
|
|
253
|
-
? {}
|
|
254
|
-
: { parameters: legacy.parameters as Record<string, unknown> }),
|
|
255
|
-
},
|
|
256
|
-
);
|
|
257
|
-
const grpcMetadata = qoderGrpcMetadata(auth);
|
|
258
|
-
lifecycle.release();
|
|
259
|
-
yield* this.#withSessionCacheEstimate(
|
|
260
|
-
qoderGrpcToCanonicalEvents(grpcRequest, grpcMetadata, this.realm, lifecycle.signal, context.provider_capture),
|
|
261
|
-
request,
|
|
262
|
-
qoderSessionId(auth.userId, modelId),
|
|
263
|
-
);
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Legacy COSY hosts are tried in order (api1 → api2 → api3) with the
|
|
268
|
-
// endpoint path from discovery when present. A network-level failure on
|
|
269
|
-
// one host falls through to the next; an HTTP error response surfaces as-is.
|
|
270
176
|
const cosyCandidates = candidate.endpoint_path?.startsWith("http")
|
|
271
177
|
? [candidate.endpoint_path]
|
|
272
178
|
: qoderInferenceCandidates(this.realm, auth.securityOauthToken).map(
|
|
@@ -71,6 +71,7 @@ export function qoderOAuthAuth(
|
|
|
71
71
|
securityOauthToken: secret,
|
|
72
72
|
refreshToken: "",
|
|
73
73
|
machineId: state.machineId || (accountId ? qoderMachineIdForAccount(accountId) : randomUUID()),
|
|
74
|
+
...(state.httpDnsIp !== undefined ? { httpDnsIp: state.httpDnsIp } : {}),
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -263,7 +264,13 @@ export async function resolveQoderAuth(input: {
|
|
|
263
264
|
}): Promise<QoderAuth> {
|
|
264
265
|
const realm = input.realm ?? "intl";
|
|
265
266
|
if (isQoderPat(input.secret)) {
|
|
266
|
-
|
|
267
|
+
const auth = await exchangeQoderPat(input.secret, input.signal, input.fetcher, getQoderVersion(), realm);
|
|
268
|
+
// A PAT exchange never returns the account's identity; when the persisted
|
|
269
|
+
// auth_state also carries the HTTPDNS-route IP, carry it over so the COSY
|
|
270
|
+
// request mirrors the desktop client's `X-Qoder-HTTPDNS-IP` header.
|
|
271
|
+
const state = parseQoderOAuthState(input.authState);
|
|
272
|
+
if (state?.httpDnsIp !== undefined) auth.httpDnsIp = state.httpDnsIp;
|
|
273
|
+
return auth;
|
|
267
274
|
}
|
|
268
275
|
// Discovery is handed only the secret and the persisted state, so the
|
|
269
276
|
// account id is read from that state — the same place `qoderOAuthAuth` reads
|
|
@@ -390,7 +390,7 @@ export type QoderSigner = (url: string, body: string) => Promise<Record<string,
|
|
|
390
390
|
* `fetcher` is optional in the type but required at runtime, and there is
|
|
391
391
|
* deliberately no global-`fetch` default: a test cannot reach the network by
|
|
392
392
|
* accident, and production is forced through the SSRF-validated egress
|
|
393
|
-
* (`network/
|
|
393
|
+
* (`network/egress/fetch.ts`). That egress is a `node:http` path, which is
|
|
394
394
|
* also the only way a body reaches the wire on a GET — `fetch` itself rejects
|
|
395
395
|
* `GET` + `body` with "request with GET/HEAD method cannot have body".
|
|
396
396
|
*/
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* `buildQoderRequest` translates one canonical request into the legacy
|
|
5
5
|
* `/algo/.../agent_chat_generation` body (and, by reuse, the messages/tools
|
|
6
6
|
* shared by the IDE and gRPC surfaces). `callQoder` signs and sends it over
|
|
7
|
-
* the COSY surface with the
|
|
8
|
-
* `x-model-key/source`, SSE accept)
|
|
7
|
+
* the COSY surface with the app/worker's headers (undici UA, machine
|
|
8
|
+
* identity, business/scene app profile, `x-model-key/source`, SSE accept)
|
|
9
|
+
* and a zstd-compressed body (capture 2026-11-11).
|
|
9
10
|
*/
|
|
10
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
11
12
|
import type {
|
|
@@ -18,7 +19,7 @@ import { reasoningTextForReplay } from "../../../protocol/reasoning-text";
|
|
|
18
19
|
import { resolveImageSource } from "../../../protocol/codecs";
|
|
19
20
|
import { getQoderVersion } from "../../operations/client-versions";
|
|
20
21
|
import { qoderMaxContextFor, qoderRosterMaxOutput } from "./qoder-catalog";
|
|
21
|
-
import { transformQoderBody, qoderBuildCosyHeaders } from "./qoder-cosy";
|
|
22
|
+
import { transformQoderBody, qoderBuildCosyHeaders, type QoderCosyProfile } from "./qoder-cosy";
|
|
22
23
|
import { qoderSessionId } from "./qoder-auth";
|
|
23
24
|
import { flattenCanonicalContent, qoderToolChoice, qoderTools } from "./qoder-body";
|
|
24
25
|
import type { QoderRealm } from "./qoder-realm";
|
|
@@ -469,8 +470,11 @@ type QoderFetcher = (url: string, init: RequestInit) => Promise<Response>;
|
|
|
469
470
|
|
|
470
471
|
/**
|
|
471
472
|
* Signs and sends one legacy COSY chat request. The body is alphabet-scrambled
|
|
472
|
-
*
|
|
473
|
-
*
|
|
473
|
+
* then zstd-compressed (as the desktop worker does on the wire, capture
|
|
474
|
+
* 2026-11-11) before signing; headers come from `qoderBuildCosyHeaders` plus
|
|
475
|
+
* the modern `x-model-key`/`x-model-source` and SSE accepts. When the runtime
|
|
476
|
+
* lacks zstd the scrambled body is sent uncompressed, matching the older CLI
|
|
477
|
+
* wire.
|
|
474
478
|
*/
|
|
475
479
|
export async function callQoder(
|
|
476
480
|
url: string,
|
|
@@ -480,21 +484,34 @@ export async function callQoder(
|
|
|
480
484
|
signal: AbortSignal,
|
|
481
485
|
fetcher: QoderFetcher,
|
|
482
486
|
version = getQoderVersion(),
|
|
487
|
+
profile: QoderCosyProfile = "app",
|
|
483
488
|
): Promise<Response> {
|
|
484
489
|
const encoded = transformQoderBody(JSON.stringify(body));
|
|
485
490
|
const modelConfig = (body["model_config"] as Record<string, unknown> | null) ?? null;
|
|
491
|
+
// The desktop worker compresses the scrambled body with zstd and stamps
|
|
492
|
+
// `Content-Encoding: zstd` (capture 2026-11-11), but the COSY signature is
|
|
493
|
+
// verified over the *scrambled* body — the server decompresses before
|
|
494
|
+
// verifying (proven live 2026-11-11: signing over the zstd bytes returns
|
|
495
|
+
// `403 Signature invalid`). The CLI sends the plain scrambled body. Fall
|
|
496
|
+
// back to the plain scrambled body (no encoding header) when the runtime
|
|
497
|
+
// lacks zstd.
|
|
498
|
+
const compressed =
|
|
499
|
+
profile === "app" && typeof Bun?.zstdCompressSync === "function"
|
|
500
|
+
? (Bun.zstdCompressSync(Buffer.from(encoded)) as Uint8Array<ArrayBuffer>)
|
|
501
|
+
: undefined;
|
|
502
|
+
const wireBody = (compressed ?? encoded) as unknown as Uint8Array;
|
|
486
503
|
return fetcher(url, {
|
|
487
504
|
method: "POST",
|
|
488
505
|
headers: {
|
|
489
|
-
...qoderBuildCosyHeaders(encoded, url, auth, version),
|
|
506
|
+
...qoderBuildCosyHeaders(encoded, url, auth, version, profile),
|
|
490
507
|
...(MODERN_PROFILE.sendModelSourceHeaders
|
|
491
508
|
? { "x-model-key": modelId, "x-model-source": typeof modelConfig?.["source"] === "string" ? (modelConfig["source"] as string) : "system" }
|
|
492
509
|
: {}),
|
|
493
510
|
Accept: "text/event-stream",
|
|
494
511
|
"Cache-Control": "no-cache",
|
|
495
|
-
"
|
|
512
|
+
...(compressed === undefined ? {} : { "Content-Encoding": "zstd" }),
|
|
496
513
|
},
|
|
497
|
-
body:
|
|
514
|
+
body: wireBody as unknown as BodyInit,
|
|
498
515
|
signal,
|
|
499
516
|
});
|
|
500
517
|
}
|
|
@@ -32,7 +32,7 @@ import { log } from "../../../observability/logger";
|
|
|
32
32
|
|
|
33
33
|
/** Structural fetch bound; Bun's own fetch type carries extra host props. */
|
|
34
34
|
type Fetcher = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
35
|
-
import type { DailyCheckinResult, DailyCheckinState } from "../buddy/
|
|
35
|
+
import type { DailyCheckinResult, DailyCheckinState } from "../buddy/lifecycle";
|
|
36
36
|
|
|
37
37
|
export const QODER_CHECKIN_PROVIDER_IDS: readonly string[] = ["qoder", "qodercn"];
|
|
38
38
|
|