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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// SenseNova providers (CN + Intl) — bearer-authenticated OpenAI-compatible
|
|
2
|
+
// chat + image generation.
|
|
3
|
+
//
|
|
4
|
+
// SenseNova API keys are per-site: keys created on platform.sensenova.cn are
|
|
5
|
+
// rejected by token.sensenova.ai with 401 {"error":{"code":16,"message":"Forbidden"}}
|
|
6
|
+
// (and vice versa), so each deployment is its own provider with its own host
|
|
7
|
+
// and catalog — the same split as CodeBuddy cb/cbcn.
|
|
8
|
+
//
|
|
9
|
+
// Wire facts:
|
|
10
|
+
// - Chat is OpenAI-compatible `/v1/chat/completions` with Bearer auth.
|
|
11
|
+
// - Image generation is `/v1/images/generations` (OpenAI-shaped `{model,
|
|
12
|
+
// prompt, n, size, output_format, response_format, watermark,
|
|
13
|
+
// prompt_extend}`), answering `{data: [{url|b64_json}]}`.
|
|
14
|
+
// - Thinking streams under `reasoning` (delta + message) rather than
|
|
15
|
+
// `reasoning_content`; the shared compatible adapter's chat decoder maps
|
|
16
|
+
// it onto the canonical reasoning event.
|
|
17
|
+
|
|
18
|
+
import { defineModel, type ModelDefinition } from "../../provider-model-definition";
|
|
19
|
+
import type { ApiKeyProviderSpec } from "../../configured-provider";
|
|
20
|
+
import {
|
|
21
|
+
fetchOpenAICompatibleModels,
|
|
22
|
+
} from "../../discovery/openai-model-discovery";
|
|
23
|
+
|
|
24
|
+
/** Chat path below each realm's `/v1` base root. */
|
|
25
|
+
const SENSENOVA_CHAT_PATH = "/chat/completions";
|
|
26
|
+
/** Image-generation path below each realm's `/v1` base root. */
|
|
27
|
+
const SENSENOVA_IMAGE_PATH = "/images/generations";
|
|
28
|
+
|
|
29
|
+
export const SENSENOVA_PROVIDER_ID = "sensenova" as const;
|
|
30
|
+
export const SENSENOVA_INTL_PROVIDER_ID = "sensenova-intl" as const;
|
|
31
|
+
|
|
32
|
+
function sensenovaSpec(providerId: string): ApiKeyProviderSpec {
|
|
33
|
+
return {
|
|
34
|
+
provider_id: providerId as ApiKeyProviderSpec["provider_id"],
|
|
35
|
+
endpoint_paths_by_wire_family: { chat: SENSENOVA_CHAT_PATH },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** SenseNova CN adapter spec. */
|
|
40
|
+
export const SENSENOVA_SPEC: ApiKeyProviderSpec = sensenovaSpec(SENSENOVA_PROVIDER_ID);
|
|
41
|
+
|
|
42
|
+
/** SenseNova Intl adapter spec. */
|
|
43
|
+
export const SENSENOVA_INTL_SPEC: ApiKeyProviderSpec = sensenovaSpec(SENSENOVA_INTL_PROVIDER_ID);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Shared chat roster (both realms expose the same model families; the intl
|
|
47
|
+
* deployment advertises fewer static rows, and live discovery fills both).
|
|
48
|
+
*
|
|
49
|
+
* Image rows are media-generation rows (serviceKind `imageGeneration`), not
|
|
50
|
+
* chat rows — they answer `/v1/images/generations` and are routed by the
|
|
51
|
+
* native image route.
|
|
52
|
+
*/
|
|
53
|
+
const SENSENOVA_CHAT_MODELS: readonly string[] = [
|
|
54
|
+
"sensenova-6.8-flash",
|
|
55
|
+
"sensenova-6.8-flash-lite",
|
|
56
|
+
"sensenova-6.7-flash-lite",
|
|
57
|
+
"deepseek-flash",
|
|
58
|
+
"deepseek-v4-flash",
|
|
59
|
+
"glm-5.3",
|
|
60
|
+
"glm-5.2",
|
|
61
|
+
"kimi-k3",
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
function makeChatModels(providerId: string): ModelDefinition[] {
|
|
65
|
+
return SENSENOVA_CHAT_MODELS.map((id) =>
|
|
66
|
+
defineModel({
|
|
67
|
+
id,
|
|
68
|
+
providerId,
|
|
69
|
+
wireFamily: "chat",
|
|
70
|
+
endpoint: SENSENOVA_CHAT_PATH,
|
|
71
|
+
ctx: 200_000,
|
|
72
|
+
out: 32_000,
|
|
73
|
+
reasoning: true,
|
|
74
|
+
toolCall: true,
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function makeImageModels(providerId: string): ModelDefinition[] {
|
|
80
|
+
return [
|
|
81
|
+
defineModel({
|
|
82
|
+
id: "sensenova-u1.5-lite",
|
|
83
|
+
providerId,
|
|
84
|
+
wireFamily: "chat",
|
|
85
|
+
serviceKind: "imageGeneration",
|
|
86
|
+
endpoint: SENSENOVA_IMAGE_PATH,
|
|
87
|
+
ctx: null,
|
|
88
|
+
out: null,
|
|
89
|
+
toolCall: false,
|
|
90
|
+
output: ["image"],
|
|
91
|
+
}),
|
|
92
|
+
defineModel({
|
|
93
|
+
id: "sensenova-u1.5-fast",
|
|
94
|
+
providerId,
|
|
95
|
+
wireFamily: "chat",
|
|
96
|
+
serviceKind: "imageGeneration",
|
|
97
|
+
endpoint: SENSENOVA_IMAGE_PATH,
|
|
98
|
+
ctx: null,
|
|
99
|
+
out: null,
|
|
100
|
+
toolCall: false,
|
|
101
|
+
output: ["image"],
|
|
102
|
+
}),
|
|
103
|
+
defineModel({
|
|
104
|
+
id: "sensenova-u1-fast",
|
|
105
|
+
providerId,
|
|
106
|
+
wireFamily: "chat",
|
|
107
|
+
serviceKind: "imageGeneration",
|
|
108
|
+
endpoint: SENSENOVA_IMAGE_PATH,
|
|
109
|
+
ctx: null,
|
|
110
|
+
out: null,
|
|
111
|
+
toolCall: false,
|
|
112
|
+
output: ["image"],
|
|
113
|
+
}),
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Full CN catalog: chat + image rows. */
|
|
118
|
+
export const SENSENOVA_MODELS: readonly ModelDefinition[] = [
|
|
119
|
+
...makeChatModels(SENSENOVA_PROVIDER_ID),
|
|
120
|
+
...makeImageModels(SENSENOVA_PROVIDER_ID),
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
/** Full Intl catalog: chat + image rows. */
|
|
124
|
+
export const SENSENOVA_INTL_MODELS: readonly ModelDefinition[] = [
|
|
125
|
+
...makeChatModels(SENSENOVA_INTL_PROVIDER_ID),
|
|
126
|
+
...makeImageModels(SENSENOVA_INTL_PROVIDER_ID),
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
/** Live `/v1/models` listing for one realm. */
|
|
130
|
+
export function discoverSensenovaModels(options: {
|
|
131
|
+
readonly baseUrl: string;
|
|
132
|
+
readonly credential?: string;
|
|
133
|
+
readonly fetcher?: typeof fetch;
|
|
134
|
+
readonly providerId: string;
|
|
135
|
+
}): Promise<readonly ModelDefinition[] | null> {
|
|
136
|
+
return fetchOpenAICompatibleModels({
|
|
137
|
+
baseUrl: options.baseUrl,
|
|
138
|
+
providerId: options.providerId,
|
|
139
|
+
...(typeof options.credential === "string" && options.credential.trim().length > 0
|
|
140
|
+
? { headers: { authorization: `Bearer ${options.credential}` } }
|
|
141
|
+
: {}),
|
|
142
|
+
...(options.fetcher === undefined ? {} : { fetcher: options.fetcher }),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
@@ -187,7 +187,7 @@ export function buildTraeChatBody(
|
|
|
187
187
|
}
|
|
188
188
|
const parts: TraeContentPart[] = [];
|
|
189
189
|
const toolCalls: TraeToolCall[] = [];
|
|
190
|
-
|
|
190
|
+
const toolResults: Array<{ callId: string; text: string }> = [];
|
|
191
191
|
for (const part of message.content) {
|
|
192
192
|
if (part.kind === "toolCall") {
|
|
193
193
|
toolCalls.push({
|
|
@@ -198,7 +198,7 @@ export function buildTraeChatBody(
|
|
|
198
198
|
continue;
|
|
199
199
|
}
|
|
200
200
|
if (part.kind === "toolResult") {
|
|
201
|
-
|
|
201
|
+
toolResults.push({ callId: part.call_id, text: toolResultText(part.content) });
|
|
202
202
|
continue;
|
|
203
203
|
}
|
|
204
204
|
const mapped = toTraePart(part);
|
|
@@ -207,15 +207,19 @@ export function buildTraeChatBody(
|
|
|
207
207
|
|
|
208
208
|
// A tool answer becomes its own `tool` turn, which is the only shape
|
|
209
209
|
// upstream accepts for it — leaving it inside the assistant turn is
|
|
210
|
-
// rejected as a broken tool sequence.
|
|
211
|
-
|
|
210
|
+
// rejected as a broken tool sequence. One turn per result: a message
|
|
211
|
+
// whose content carries multiple `toolResult` parts (parallel tool calls)
|
|
212
|
+
// must emit one `tool` turn per call id, or upstream rejects the missing
|
|
213
|
+
// ones with `tool call failed: … No tool output found for function call
|
|
214
|
+
// <id>`.
|
|
215
|
+
for (const toolResult of toolResults) {
|
|
212
216
|
messages.push({
|
|
213
217
|
role: "tool",
|
|
214
218
|
content: [{ type: "text", text: toolResult.text }],
|
|
215
219
|
tool_call_id: toolResult.callId,
|
|
216
220
|
});
|
|
217
|
-
continue;
|
|
218
221
|
}
|
|
222
|
+
if (toolResults.length > 0) continue;
|
|
219
223
|
// Empty turns are skipped: upstream rejects them, and emitting
|
|
220
224
|
// `content: ""` turns leaves a ghost turn in the conversation.
|
|
221
225
|
if (parts.length === 0 && toolCalls.length === 0) continue;
|
|
@@ -40,10 +40,9 @@ import { log } from "../../../observability/logger";
|
|
|
40
40
|
import {
|
|
41
41
|
TRAE_ENT_USAGE_PATH,
|
|
42
42
|
TRAE_ENT_USAGE_PATH_V2,
|
|
43
|
-
TRAE_REGION_CORE_HOSTS,
|
|
44
|
-
TRAE_REGION_PAY_HOSTS,
|
|
45
|
-
TRAE_REALMS,
|
|
46
43
|
TRAE_SESSION_USAGE_PATH,
|
|
44
|
+
traeCoreForAccount,
|
|
45
|
+
traePayHostForAccount,
|
|
47
46
|
type TraeRealm,
|
|
48
47
|
} from "./trae-realm";
|
|
49
48
|
|
|
@@ -318,6 +317,13 @@ function logTraeQuotaMiss(errors: readonly string[]): void {
|
|
|
318
317
|
log.warn(`[quota] trae surfaces unavailable: ${errors.join("; ")}`);
|
|
319
318
|
}
|
|
320
319
|
|
|
320
|
+
/** Only this exact response from both quota surfaces on both realms is terminal auth evidence. */
|
|
321
|
+
function isTraeAuthInvalidation(errors: readonly string[]): boolean {
|
|
322
|
+
return errors.length === 4 && errors.every((error) =>
|
|
323
|
+
/HTTP 401\s+\{[^}]*"code"\s*:\s*1001\b/i.test(error),
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
321
327
|
/**
|
|
322
328
|
* Reads one Trae account's quota.
|
|
323
329
|
*
|
|
@@ -326,27 +332,6 @@ function logTraeQuotaMiss(errors: readonly string[]): void {
|
|
|
326
332
|
* whose pay host is unreachable still has a readable meter, and reporting
|
|
327
333
|
* "no quota" for it would park an account that can still serve.
|
|
328
334
|
*/
|
|
329
|
-
/**
|
|
330
|
-
* The core host for this account.
|
|
331
|
-
*
|
|
332
|
-
* A provisioned region overrides the realm default: an `sg` account sent to the
|
|
333
|
-
* US core is answered 401 code 1001, which looks exactly like a dead token.
|
|
334
|
-
*/
|
|
335
|
-
function coreHost(region: string, config: (typeof TRAE_REALMS)[TraeRealm]): string {
|
|
336
|
-
return (region !== "" ? TRAE_REGION_CORE_HOSTS[region] : undefined) ?? config.core;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* The pay host for this account.
|
|
341
|
-
*
|
|
342
|
-
* The pay surface correlates the credential against its region, too: an `sg`
|
|
343
|
-
* account sent to the US pay host is answered 401 code 1001, while the SG pay
|
|
344
|
-
* host accepts the same token (verified live 2026-11-06).
|
|
345
|
-
*/
|
|
346
|
-
function payHost(region: string, config: (typeof TRAE_REALMS)[TraeRealm]): string {
|
|
347
|
-
return (region !== "" ? TRAE_REGION_PAY_HOSTS[region] : undefined) ?? config.pay;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
335
|
export async function fetchTraeQuota(
|
|
351
336
|
credential: string,
|
|
352
337
|
fetcher: FetchLike,
|
|
@@ -412,13 +397,13 @@ export async function fetchTraeQuota(
|
|
|
412
397
|
* realm is tried: one of them accepts, and that is the answer.
|
|
413
398
|
*/
|
|
414
399
|
const tryRealms = async (
|
|
415
|
-
path: (
|
|
400
|
+
path: (realm: TraeRealm) => string,
|
|
416
401
|
surface: string,
|
|
417
402
|
parse: (payload: unknown) => { readonly windows: readonly ProviderQuotaWindow[]; readonly plan: string | null },
|
|
418
403
|
): Promise<{ readonly windows: readonly ProviderQuotaWindow[]; readonly plan: string | null } | null> => {
|
|
419
404
|
for (const candidate of realm === "cn" ? (["cn", "intl"] as const) : (["intl", "cn"] as const)) {
|
|
420
405
|
try {
|
|
421
|
-
const payload = await call(candidate, path(
|
|
406
|
+
const payload = await call(candidate, path(candidate), surface);
|
|
422
407
|
return parse(payload);
|
|
423
408
|
} catch (error) {
|
|
424
409
|
errors.push(`${candidate} ${surface}: ${cleanError(error)}`);
|
|
@@ -427,11 +412,11 @@ export async function fetchTraeQuota(
|
|
|
427
412
|
return null;
|
|
428
413
|
};
|
|
429
414
|
|
|
430
|
-
const session = await tryRealms((
|
|
415
|
+
const session = await tryRealms((r) => traeCoreForAccount(r, region) + TRAE_SESSION_USAGE_PATH, "session usage", parseSessionUsage);
|
|
431
416
|
// The entitlement path differs by realm: the CN realm answers v2 (v1 lacks
|
|
432
417
|
// `usage_summary` there), the intl realm answers v1 (v2 is a 404).
|
|
433
418
|
const entPath = realm === "cn" ? TRAE_ENT_USAGE_PATH_V2 : TRAE_ENT_USAGE_PATH;
|
|
434
|
-
const entitlement = await tryRealms((
|
|
419
|
+
const entitlement = await tryRealms((r) => traePayHostForAccount(r, region) + entPath, "entitlement", parseEntitlement);
|
|
435
420
|
|
|
436
421
|
const windows = [...(session?.windows ?? []), ...(entitlement?.windows ?? [])];
|
|
437
422
|
if (!session && !entitlement) {
|
|
@@ -443,7 +428,14 @@ export async function fetchTraeQuota(
|
|
|
443
428
|
// The detail is kept for the log instead, where it cannot be read as a
|
|
444
429
|
// balance.
|
|
445
430
|
logTraeQuotaMiss(errors);
|
|
446
|
-
|
|
431
|
+
const authInvalidated = isTraeAuthInvalidation(errors);
|
|
432
|
+
return {
|
|
433
|
+
source: "trae",
|
|
434
|
+
plan: null,
|
|
435
|
+
windows: [],
|
|
436
|
+
error: authInvalidated ? errors.join("; ") : null,
|
|
437
|
+
...(authInvalidated ? { authInvalidated: true } : {}),
|
|
438
|
+
};
|
|
447
439
|
}
|
|
448
440
|
return {
|
|
449
441
|
source: "trae",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
/** One Trae deployment. */
|
|
13
13
|
export type TraeRealm = "intl" | "cn";
|
|
14
14
|
|
|
15
|
+
/** Provider ids served by the Trae integration (intl + CN). */
|
|
16
|
+
export const TRAE_PROVIDER_IDS: ReadonlySet<string> = new Set(["trae", "traecn"]);
|
|
17
|
+
|
|
15
18
|
/** Per-realm upstream addresses, client identity, and auth scheme. */
|
|
16
19
|
export interface TraeRealmConfig {
|
|
17
20
|
/** Data-plane host: chat + model list (`/api/ide/v1/*`). */
|
|
@@ -245,3 +248,38 @@ export function traeAccountCandidates(realm: TraeRealm): readonly string[] {
|
|
|
245
248
|
const config = TRAE_REALMS[realm];
|
|
246
249
|
return [config.account, ...config.accountFallbacks];
|
|
247
250
|
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* The core host for this account.
|
|
254
|
+
*
|
|
255
|
+
* A provisioned region overrides the realm default: an `sg` account sent to the
|
|
256
|
+
* US core is answered 401 code 1001 (which looks exactly like a dead token), so
|
|
257
|
+
* the region lookup must win when it is known. Falling back to the realm keeps
|
|
258
|
+
* older accounts (stored before the region was captured) working exactly as
|
|
259
|
+
* before.
|
|
260
|
+
*/
|
|
261
|
+
export function traeCoreForAccount(
|
|
262
|
+
realm: TraeRealm,
|
|
263
|
+
region: string,
|
|
264
|
+
): string {
|
|
265
|
+
const regional = TRAE_REGION_CORE_HOSTS[region];
|
|
266
|
+
if (regional !== undefined) return regional;
|
|
267
|
+
return traeCoreCandidates(realm)[0] ?? "";
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The pay host for this account.
|
|
272
|
+
*
|
|
273
|
+
* The pay surface correlates the credential against its region, too: an `sg`
|
|
274
|
+
* account sent to the US pay host is answered 401 code 1001, while the SG pay
|
|
275
|
+
* host accepts the same token (verified live 2026-11-06). Falls back to the
|
|
276
|
+
* realm default host when no region is provisioned.
|
|
277
|
+
*/
|
|
278
|
+
export function traePayHostForAccount(
|
|
279
|
+
realm: TraeRealm,
|
|
280
|
+
region: string,
|
|
281
|
+
): string {
|
|
282
|
+
const regional = TRAE_REGION_PAY_HOSTS[region];
|
|
283
|
+
if (regional !== undefined) return regional;
|
|
284
|
+
return TRAE_REALMS[realm].pay;
|
|
285
|
+
}
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
TRAE_AGENT_ENDPOINT,
|
|
32
32
|
TRAE_CHAT_ENDPOINT,
|
|
33
33
|
} from "./trae-protocol";
|
|
34
|
-
import {
|
|
34
|
+
import { traeCoreForAccount, TRAE_MODEL_LIST_PATH, TRAE_AGENT_FUNCTION, type TraeRealm } from "./trae-realm";
|
|
35
35
|
|
|
36
36
|
export interface TraeAdapterOptions {
|
|
37
37
|
readonly providerId?: string;
|
|
@@ -269,7 +269,11 @@ class TraeAdapter implements ProviderAdapter {
|
|
|
269
269
|
trafficType: agentSurface ? "agent" : "chat",
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
-
const
|
|
272
|
+
const region =
|
|
273
|
+
typeof context.credential.auth_state?.["region"] === "string"
|
|
274
|
+
? context.credential.auth_state["region"].toLowerCase()
|
|
275
|
+
: "";
|
|
276
|
+
const primary = traeCoreForAccount(this.realm, region);
|
|
273
277
|
const path = agentSurface ? TRAE_AGENT_ENDPOINT : TRAE_CHAT_ENDPOINT;
|
|
274
278
|
const url =
|
|
275
279
|
candidate.endpoint_path && candidate.endpoint_path.startsWith("http")
|
|
@@ -572,10 +576,7 @@ export async function discoverTraeModels(input: {
|
|
|
572
576
|
uid: input.uid,
|
|
573
577
|
appVersion: getTraeVersion(),
|
|
574
578
|
});
|
|
575
|
-
const core = traeCoreForAccount(
|
|
576
|
-
input.realm,
|
|
577
|
-
input.region === undefined ? undefined : { region: input.region },
|
|
578
|
-
);
|
|
579
|
+
const core = traeCoreForAccount(input.realm, (input.region ?? "").toLowerCase());
|
|
579
580
|
|
|
580
581
|
if (input.realm === "intl") {
|
|
581
582
|
// The agent picker: the only surface that lists the intl roster's configs.
|
|
@@ -649,21 +650,3 @@ export async function discoverTraeModels(input: {
|
|
|
649
650
|
}
|
|
650
651
|
|
|
651
652
|
export { TRAE_CN_MODELS, TRAE_INTL_MODELS };
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* Core host for one account.
|
|
655
|
-
*
|
|
656
|
-
* An intl account is provisioned into a region and its token only authenticates
|
|
657
|
-
* against that region's host, so the account's own `region` wins over the
|
|
658
|
-
* realm default. Falling back to the realm keeps older accounts (stored before
|
|
659
|
-
* the region was captured) working exactly as before.
|
|
660
|
-
*/
|
|
661
|
-
function traeCoreForAccount(
|
|
662
|
-
realm: TraeRealm,
|
|
663
|
-
authState: Readonly<Record<string, unknown>> | undefined,
|
|
664
|
-
): string {
|
|
665
|
-
const region = typeof authState?.["region"] === "string" ? authState["region"].toLowerCase() : "";
|
|
666
|
-
const regional = TRAE_REGION_CORE_HOSTS[region];
|
|
667
|
-
if (regional !== undefined) return regional;
|
|
668
|
-
return traeCoreCandidates(realm)[0] ?? "";
|
|
669
|
-
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Vercel AI Gateway provider — TypeScript, Vercel AI Gateway, Bun gateway.
|
|
2
|
+
//
|
|
3
|
+
// Vercel AI Gateway is an OpenAI-compatible routing front for 500+ models
|
|
4
|
+
// across providers. The API key (`vck_...`) is bearer-authenticated against
|
|
5
|
+
// `https://ai-gateway.vercel.sh/v1`.
|
|
6
|
+
//
|
|
7
|
+
// Verified live 2026-11-11 with a free-tier key:
|
|
8
|
+
// - GET /v1/models → 200, 506 rows (OpenRouter-shaped list)
|
|
9
|
+
// - POST /v1/chat/completions → 200 for nvidia/nemotron-3.5-lightning-free
|
|
10
|
+
// - POST /v1/chat/completions → 403 `no_providers_available` for the other
|
|
11
|
+
// zero-priced rows (reflection/beam*, humansand/ampersand-v0.1,
|
|
12
|
+
// stealth/glyph-cluster) — listed at $0 but gated behind a paid/Pro plan.
|
|
13
|
+
// - GET /v1/credits → { balance, token_pass, total_used }
|
|
14
|
+
//
|
|
15
|
+
// The static roster below carries the five $0-priced rows, so the free section
|
|
16
|
+
// is populated before any fetch. Four of them are marked `proOnly` because the
|
|
17
|
+
// upstream refuses them on a free plan (verified live); discovery still finds
|
|
18
|
+
// them and the badge tracks the upstream's own listing.
|
|
19
|
+
|
|
20
|
+
import type { ApiKeyProviderSpec } from "../../configured-provider";
|
|
21
|
+
import { fetchOpenAICompatibleModels } from "../../discovery/openai-model-discovery";
|
|
22
|
+
import { defineModel, type ModelDefinition } from "../../provider-model-definition";
|
|
23
|
+
|
|
24
|
+
const VERCEL_PROVIDER_ID = "vercel" as const;
|
|
25
|
+
|
|
26
|
+
/** Chat path below the `/v1` base root. */
|
|
27
|
+
const VERCEL_CHAT_PATH = "/chat/completions";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Vercel AI Gateway adapter spec: plain bearer, OpenAI-compatible chat.
|
|
31
|
+
*
|
|
32
|
+
* The gateway accepts standard OpenAI fields (`max_tokens`, `temperature`,
|
|
33
|
+
* `stop`, `tools`, `tool_choice`, `reasoning`, `response_format`, …) and
|
|
34
|
+
* reports usage/cost in extended `provider_metadata`. No bespoke headers are
|
|
35
|
+
* required beyond the bearer, so the generic API-key adapter serves it.
|
|
36
|
+
*/
|
|
37
|
+
export const VERCEL_SPEC: ApiKeyProviderSpec = {
|
|
38
|
+
provider_id: VERCEL_PROVIDER_ID,
|
|
39
|
+
endpoint_paths_by_wire_family: {},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Default rows for the Vercel AI Gateway provider (2026-11-11).
|
|
44
|
+
*
|
|
45
|
+
* Verified live with a free-tier key: only
|
|
46
|
+
* `nvidia/nemotron-3.5-lightning-free` is served on a free plan; the other
|
|
47
|
+
* zero-priced listing rows are refused with 403 `no_providers_available`, so
|
|
48
|
+
* they are not seeded. The two billed rows (`meituan/longcat-2.5`,
|
|
49
|
+
* `inception/mercury-3`) are the operator's picks and carry the capabilities
|
|
50
|
+
* the upstream itself declares — LongCat is the vision-capable coding row,
|
|
51
|
+
* Mercury 3 is the diffusion row. Everything else comes from live discovery.
|
|
52
|
+
*/
|
|
53
|
+
export const VERCEL_DEFAULT_MODELS: readonly ModelDefinition[] = [
|
|
54
|
+
defineModel({
|
|
55
|
+
id: "nvidia/nemotron-3.5-lightning-free",
|
|
56
|
+
wireFamily: "chat",
|
|
57
|
+
endpoint: VERCEL_CHAT_PATH,
|
|
58
|
+
ctx: 1_000_000,
|
|
59
|
+
out: 65_000,
|
|
60
|
+
reasoning: true,
|
|
61
|
+
toolCall: true,
|
|
62
|
+
free: true,
|
|
63
|
+
freeTier: true,
|
|
64
|
+
}),
|
|
65
|
+
defineModel({
|
|
66
|
+
id: "meituan/longcat-2.5",
|
|
67
|
+
wireFamily: "chat",
|
|
68
|
+
endpoint: VERCEL_CHAT_PATH,
|
|
69
|
+
ctx: 1_048_576,
|
|
70
|
+
out: 131_072,
|
|
71
|
+
vision: true,
|
|
72
|
+
reasoning: true,
|
|
73
|
+
toolCall: true,
|
|
74
|
+
}),
|
|
75
|
+
defineModel({
|
|
76
|
+
id: "inception/mercury-3",
|
|
77
|
+
wireFamily: "chat",
|
|
78
|
+
endpoint: VERCEL_CHAT_PATH,
|
|
79
|
+
ctx: 1_000_000,
|
|
80
|
+
out: 131_072,
|
|
81
|
+
reasoning: true,
|
|
82
|
+
toolCall: true,
|
|
83
|
+
}),
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Live `/v1/models` listing.
|
|
88
|
+
*
|
|
89
|
+
* The listing requires the API key, so the credential is forwarded when
|
|
90
|
+
* present; an unauthenticated fetch fails cleanly and the static roster stays
|
|
91
|
+
* authoritative. Every fetched row is kept as the shared reader resolved it
|
|
92
|
+
* (limits/modalities/reasoning/tool flags come from the upstream's own JSON).
|
|
93
|
+
*
|
|
94
|
+
* A fetched id the static roster already knows keeps the roster row whole, not
|
|
95
|
+
* the listing's fields: the upstream does not publish `reasoning`/`tool_call`
|
|
96
|
+
* keys or tier flags, so the shared reader would overwrite the roster's
|
|
97
|
+
* reasoning/tool/freeTier/proOnly statements with its conservative defaults.
|
|
98
|
+
* The roster is the canonical metadata for the models we know; the listing only
|
|
99
|
+
* adds genuinely new ids.
|
|
100
|
+
*/
|
|
101
|
+
export async function discoverVercelModels(options: {
|
|
102
|
+
readonly baseUrl: string;
|
|
103
|
+
readonly credential?: string;
|
|
104
|
+
readonly fetcher?: typeof fetch;
|
|
105
|
+
}): Promise<readonly ModelDefinition[] | null> {
|
|
106
|
+
const listed = await fetchOpenAICompatibleModels({
|
|
107
|
+
baseUrl: options.baseUrl,
|
|
108
|
+
providerId: "vercel",
|
|
109
|
+
...(typeof options.credential === "string" && options.credential.trim().length > 0
|
|
110
|
+
? { headers: { authorization: `Bearer ${options.credential}` } }
|
|
111
|
+
: {}),
|
|
112
|
+
...(options.fetcher === undefined ? {} : { fetcher: options.fetcher }),
|
|
113
|
+
});
|
|
114
|
+
if (listed === null) return null;
|
|
115
|
+
const known = new Map(VERCEL_DEFAULT_MODELS.map((model) => [model.modelId, model]));
|
|
116
|
+
return listed.map((model) => known.get(model.modelId) ?? model);
|
|
117
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Vercel AI Gateway quota collector.
|
|
2
|
+
//
|
|
3
|
+
// Reads `GET /v1/credits` (Bearer) — the gateway's credit surface. It returns
|
|
4
|
+
// a remaining balance (`balance`, USD string), an optional `token_pass`, and
|
|
5
|
+
// the total spend (`total_used`, USD string). No ceiling is reported (balance
|
|
6
|
+
// is remaining, not a limit), so the window carries absolute remaining/used
|
|
7
|
+
// values and no percent — the pooled credit sum still works. Never throws: a
|
|
8
|
+
// failed read returns a null-result so the router treats it as unknown rather
|
|
9
|
+
// than dead.
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
FetchLike,
|
|
13
|
+
ProviderQuotaResult,
|
|
14
|
+
ProviderQuotaWindow,
|
|
15
|
+
} from "../../quota/quota-contracts";
|
|
16
|
+
import {
|
|
17
|
+
cleanError,
|
|
18
|
+
number,
|
|
19
|
+
unsupportedQuota,
|
|
20
|
+
} from "../../quota/quota-contracts";
|
|
21
|
+
import type { QuotaCollectionContext } from "../../quota/quota-support";
|
|
22
|
+
|
|
23
|
+
const VERCEL_CREDITS_URL = "https://ai-gateway.vercel.sh/v1/credits";
|
|
24
|
+
|
|
25
|
+
function readCredits(
|
|
26
|
+
credential: string,
|
|
27
|
+
fetcher: FetchLike,
|
|
28
|
+
): Promise<Record<string, unknown> | null> {
|
|
29
|
+
return fetcher(VERCEL_CREDITS_URL, {
|
|
30
|
+
method: "GET",
|
|
31
|
+
headers: {
|
|
32
|
+
authorization: `Bearer ${credential}`,
|
|
33
|
+
accept: "application/json",
|
|
34
|
+
},
|
|
35
|
+
signal: AbortSignal.timeout(15_000),
|
|
36
|
+
})
|
|
37
|
+
.then(async (res) => {
|
|
38
|
+
if (!res.ok) return null;
|
|
39
|
+
try {
|
|
40
|
+
return (await res.json()) as Record<string, unknown>;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
.catch(() => null);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Reads the Vercel AI Gateway credit balance and returns a normalized result. */
|
|
49
|
+
export async function fetchVercelQuota(
|
|
50
|
+
credential: string,
|
|
51
|
+
fetcher: FetchLike,
|
|
52
|
+
_context?: QuotaCollectionContext,
|
|
53
|
+
): Promise<ProviderQuotaResult> {
|
|
54
|
+
try {
|
|
55
|
+
const credits = await readCredits(credential, fetcher);
|
|
56
|
+
if (!credits) {
|
|
57
|
+
return unsupportedQuota("vercel");
|
|
58
|
+
}
|
|
59
|
+
const balance = number(credits["balance"]);
|
|
60
|
+
const totalUsed = number(credits["total_used"]);
|
|
61
|
+
if (balance === null && totalUsed === null) {
|
|
62
|
+
return {
|
|
63
|
+
source: "vercel",
|
|
64
|
+
plan: null,
|
|
65
|
+
windows: [],
|
|
66
|
+
error: "Vercel AI Gateway /v1/credits returned no usable numbers",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const windows: ProviderQuotaWindow[] = [
|
|
70
|
+
{
|
|
71
|
+
kind: "credits",
|
|
72
|
+
label: "AI Gateway credits",
|
|
73
|
+
accounting: "pooled",
|
|
74
|
+
usedPercent: null,
|
|
75
|
+
remainingPercent: null,
|
|
76
|
+
resetsAt: null,
|
|
77
|
+
used: totalUsed,
|
|
78
|
+
limit: null,
|
|
79
|
+
remaining: balance,
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
return {
|
|
83
|
+
source: "vercel",
|
|
84
|
+
plan: null,
|
|
85
|
+
windows,
|
|
86
|
+
error: null,
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
source: "vercel",
|
|
91
|
+
plan: null,
|
|
92
|
+
windows: [],
|
|
93
|
+
error: cleanError(error),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -135,8 +135,8 @@ export class XaiOAuthClient extends OAuthDeviceFlow {
|
|
|
135
135
|
protected override readonly tokenUrl = XAI_TOKEN_URL;
|
|
136
136
|
protected override readonly scopes = XAI_SCOPE;
|
|
137
137
|
|
|
138
|
-
override async startDeviceAuth(
|
|
139
|
-
const response = await this.
|
|
138
|
+
override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
|
|
139
|
+
const response = await this.fetchFor(context)(XAI_DEVICE_URL, {
|
|
140
140
|
method: "POST",
|
|
141
141
|
headers: formHeaders(),
|
|
142
142
|
body: new URLSearchParams({ client_id: XAI_CLIENT_ID, scope: XAI_SCOPE }),
|
|
@@ -157,9 +157,9 @@ export class XaiOAuthClient extends OAuthDeviceFlow {
|
|
|
157
157
|
|
|
158
158
|
override async pollDeviceAuth(
|
|
159
159
|
deviceAuthId: string,
|
|
160
|
-
|
|
160
|
+
context?: OAuthDeviceFlowContext,
|
|
161
161
|
): Promise<OAuthDevicePollResult> {
|
|
162
|
-
const response = await this.
|
|
162
|
+
const response = await this.fetchFor(context)(XAI_TOKEN_URL, {
|
|
163
163
|
method: "POST",
|
|
164
164
|
headers: formHeaders(),
|
|
165
165
|
body: new URLSearchParams({
|
|
@@ -203,7 +203,7 @@ export class XaiOAuthClient extends OAuthDeviceFlow {
|
|
|
203
203
|
};
|
|
204
204
|
}
|
|
205
205
|
const result = this.parseTokenResponse(payload);
|
|
206
|
-
const identity = await fetchXaiIdentity(result.access, this.
|
|
206
|
+
const identity = await fetchXaiIdentity(result.access, this.fetchFor(context));
|
|
207
207
|
const authState = xaiAuthState(identity);
|
|
208
208
|
return {
|
|
209
209
|
status: "complete",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { createHash } from "node:crypto";
|
|
23
23
|
import { sleep } from "../../../shared/sleep";
|
|
24
24
|
import { asRecord, asTrimmedString } from "../../../shared/coerce";
|
|
25
|
-
import type { ValidatedFetch } from "../../../network/
|
|
25
|
+
import type { ValidatedFetch } from "../../../network/egress/fetch";
|
|
26
26
|
|
|
27
27
|
const ZCODE = "https://zcode.z.ai";
|
|
28
28
|
const BILLING = `${ZCODE}/api/v1/zcode-plan/billing`;
|
|
@@ -62,10 +62,10 @@ export class ZcodeStartPlanOAuthClient extends OAuthDeviceFlow {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
override async startDeviceAuth(
|
|
65
|
-
|
|
65
|
+
context?: OAuthDeviceFlowContext,
|
|
66
66
|
): Promise<OAuthDeviceStartResult & { providerState: string }> {
|
|
67
67
|
const pollToken = crypto.randomBytes(32).toString("hex");
|
|
68
|
-
const response = await this.
|
|
68
|
+
const response = await this.fetchFor(context)(INIT_URL, {
|
|
69
69
|
method: "POST",
|
|
70
70
|
headers: { ...JSON_HEADERS, authorization: `Bearer ${pollToken}` },
|
|
71
71
|
body: JSON.stringify({ provider: "zai" }),
|
|
@@ -95,7 +95,7 @@ export class ZcodeStartPlanOAuthClient extends OAuthDeviceFlow {
|
|
|
95
95
|
if (!pollToken) {
|
|
96
96
|
return { status: "failed", reason: "sign-in session lost its poll token" };
|
|
97
97
|
}
|
|
98
|
-
const response = await this.
|
|
98
|
+
const response = await this.fetchFor(context)(`${POLL_BASE}/${encodeURIComponent(deviceAuthId)}`, {
|
|
99
99
|
headers: { ...JSON_HEADERS, authorization: `Bearer ${pollToken}` },
|
|
100
100
|
});
|
|
101
101
|
if (response.status === 404) {
|