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
|
@@ -12,18 +12,20 @@ import {
|
|
|
12
12
|
record,
|
|
13
13
|
} from "./oauth-flow-store";
|
|
14
14
|
import type {
|
|
15
|
+
OAuthAccountIdentity,
|
|
15
16
|
OAuthAuthorizeRequest,
|
|
16
17
|
OAuthCodeExchangeContext,
|
|
17
18
|
OAuthExchangeResult,
|
|
18
19
|
OAuthLoginClient,
|
|
19
20
|
} from "./oauth-flow-store";
|
|
21
|
+
import { oauthAccountIdentity } from "./oauth-flow-store";
|
|
20
22
|
import type { OAuthTokenRefreshResult, OAuthTokenRefresher } from "./oauth-refresh-service";
|
|
21
23
|
|
|
22
24
|
/** Minimal fetch surface accepted by the generic OAuth client. */
|
|
23
25
|
export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
24
26
|
|
|
25
27
|
/** Normalized token shape returned by provider token endpoints. */
|
|
26
|
-
interface NormalizedTokenResponse {
|
|
28
|
+
export interface NormalizedTokenResponse extends OAuthAccountIdentity {
|
|
27
29
|
readonly access: string;
|
|
28
30
|
readonly refresh: string;
|
|
29
31
|
readonly expiresAt: Date;
|
|
@@ -83,12 +85,32 @@ export abstract class OAuthClient implements OAuthLoginClient, OAuthTokenRefresh
|
|
|
83
85
|
*/
|
|
84
86
|
readonly browserRedirectUri?: string;
|
|
85
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Custom state generator; `undefined` keeps the shared base64url state.
|
|
90
|
+
* A provider whose authorization server validates the format (e.g. DeepAI
|
|
91
|
+
* requires 32 hex chars) overrides this.
|
|
92
|
+
*/
|
|
93
|
+
generateState(): string | undefined {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
protected readonly fetchFn: FetchLike;
|
|
87
98
|
|
|
88
99
|
constructor(fetchFn: FetchLike = globalThis.fetch) {
|
|
89
100
|
this.fetchFn = fetchFn;
|
|
90
101
|
}
|
|
91
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Resolves the transport a flow should use. When the console injects a
|
|
105
|
+
* `fetchFn` in the flow context (device-code OAuth routed through a network
|
|
106
|
+
* pool, see `device_flow_proxy`), that override wins; otherwise the
|
|
107
|
+
* client's own fetch is used. Every start/poll/exchange request that talks
|
|
108
|
+
* to the provider MUST route through this so the override is honored.
|
|
109
|
+
*/
|
|
110
|
+
protected fetchFor(context?: { readonly fetchFn?: FetchLike | undefined }): FetchLike {
|
|
111
|
+
return context?.fetchFn ?? this.fetchFn;
|
|
112
|
+
}
|
|
113
|
+
|
|
92
114
|
// ---------------------------------------------------------------------------
|
|
93
115
|
// Public OAuthLoginClient surface
|
|
94
116
|
// ---------------------------------------------------------------------------
|
|
@@ -135,8 +157,9 @@ export abstract class OAuthClient implements OAuthLoginClient, OAuthTokenRefresh
|
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
/** Extracts an account label from a token exchange/refresh response. */
|
|
138
|
-
protected extractAccountLabel(
|
|
139
|
-
|
|
160
|
+
protected extractAccountLabel(data: unknown): string | undefined {
|
|
161
|
+
const identity = oauthAccountIdentity(data);
|
|
162
|
+
return identity.accountEmail ?? identity.accountName;
|
|
140
163
|
}
|
|
141
164
|
|
|
142
165
|
/** Calculates an expiry Date from an `expires_in` value. */
|
|
@@ -172,7 +195,14 @@ export abstract class OAuthClient implements OAuthLoginClient, OAuthTokenRefresh
|
|
|
172
195
|
const refresh = nonEmptyString(body.refresh_token) ?? fallbackRefresh ?? "";
|
|
173
196
|
const expiresAt = this.calculateExpiry(body.expires_in);
|
|
174
197
|
const accountLabel = this.extractAccountLabel(data);
|
|
175
|
-
|
|
198
|
+
const identity = oauthAccountIdentity(data);
|
|
199
|
+
return {
|
|
200
|
+
access,
|
|
201
|
+
refresh,
|
|
202
|
+
expiresAt,
|
|
203
|
+
...identity,
|
|
204
|
+
...(accountLabel ? { accountLabel } : {}),
|
|
205
|
+
};
|
|
176
206
|
}
|
|
177
207
|
|
|
178
208
|
/** Builds a PKCE authorization URL using the shared helper. */
|
|
@@ -231,22 +261,26 @@ export abstract class OAuthClient implements OAuthLoginClient, OAuthTokenRefresh
|
|
|
231
261
|
// ---------------------------------------------------------------------------
|
|
232
262
|
|
|
233
263
|
protected toExchangeResult(normalized: NormalizedTokenResponse): OAuthExchangeResult {
|
|
234
|
-
const { access, refresh, expiresAt, accountLabel, auth_state } = normalized;
|
|
264
|
+
const { access, refresh, expiresAt, accountLabel, accountEmail, accountName, auth_state } = normalized;
|
|
235
265
|
return {
|
|
236
266
|
access,
|
|
237
267
|
refresh,
|
|
238
268
|
expiresAt,
|
|
269
|
+
...(accountEmail === undefined ? {} : { accountEmail }),
|
|
270
|
+
...(accountName === undefined ? {} : { accountName }),
|
|
239
271
|
...(accountLabel ? { accountLabel } : {}),
|
|
240
272
|
...(auth_state === undefined ? {} : { auth_state }),
|
|
241
273
|
};
|
|
242
274
|
}
|
|
243
275
|
|
|
244
276
|
protected toRefreshResult(normalized: NormalizedTokenResponse): OAuthTokenRefreshResult {
|
|
245
|
-
const { access, refresh, expiresAt, accountLabel, auth_state } = normalized;
|
|
277
|
+
const { access, refresh, expiresAt, accountLabel, accountEmail, accountName, auth_state } = normalized;
|
|
246
278
|
return {
|
|
247
279
|
access,
|
|
248
280
|
...(refresh ? { refresh } : {}),
|
|
249
281
|
expiresAt,
|
|
282
|
+
...(accountEmail === undefined ? {} : { accountEmail }),
|
|
283
|
+
...(accountName === undefined ? {} : { accountName }),
|
|
250
284
|
...(accountLabel ? { accountLabel } : {}),
|
|
251
285
|
...(auth_state === undefined ? {} : { auth_state }),
|
|
252
286
|
};
|
|
@@ -8,8 +8,9 @@ import type {
|
|
|
8
8
|
OAuthDeviceFlowContext,
|
|
9
9
|
OAuthDevicePollResult,
|
|
10
10
|
OAuthDeviceStartResult,
|
|
11
|
+
OAuthExchangeResult,
|
|
11
12
|
} from "./oauth-flow-store";
|
|
12
|
-
import { OAuthClient } from "./oauth-client";
|
|
13
|
+
import { OAuthClient, type NormalizedTokenResponse } from "./oauth-client";
|
|
13
14
|
|
|
14
15
|
/** Configuration for standard device-code start and poll requests. */
|
|
15
16
|
export interface DeviceFlowConfig {
|
|
@@ -36,9 +37,12 @@ export abstract class OAuthDeviceFlow extends OAuthClient {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/** Standard device-authorization start. Override the body builder for custom providers. */
|
|
39
|
-
protected async startGenericDeviceAuth(
|
|
40
|
+
protected async startGenericDeviceAuth(
|
|
41
|
+
config: DeviceFlowConfig,
|
|
42
|
+
context?: OAuthDeviceFlowContext,
|
|
43
|
+
): Promise<OAuthDeviceStartResult> {
|
|
40
44
|
const body = this.buildDeviceStartBody(config);
|
|
41
|
-
const response = await this.
|
|
45
|
+
const response = await this.fetchFor(context)(config.deviceAuthUrl, {
|
|
42
46
|
method: "POST",
|
|
43
47
|
...(config.formHeaders === undefined ? {} : { headers: config.formHeaders }),
|
|
44
48
|
body,
|
|
@@ -67,12 +71,65 @@ export abstract class OAuthDeviceFlow extends OAuthClient {
|
|
|
67
71
|
return new URLSearchParams(params);
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Optional verdict computed from the parsed token body before the status
|
|
76
|
+
* code is considered. Defaults to undefined (continue with standard
|
|
77
|
+
* handling). A provider whose authoritative endpoint states the poll state
|
|
78
|
+
* in the body on any status can return a backoff verdict here — e.g. Grok,
|
|
79
|
+
* which votes on `error` before the HTTP status.
|
|
80
|
+
*/
|
|
81
|
+
protected preVerdict?(_payload: unknown): OAuthDevicePollResult | undefined;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Post-success hook for the standard device-token poll.
|
|
85
|
+
*
|
|
86
|
+
* Receives the raw token response payload and the normalized parsed token.
|
|
87
|
+
* Returns the exchange result to complete with, or a verdict (typically
|
|
88
|
+
* `failed`) instead. The default reproduces the generic behavior exactly.
|
|
89
|
+
* Errors thrown by a hook are converted to a `failed` verdict by
|
|
90
|
+
* `pollGenericDeviceAuth`, keeping the poll contract (verdicts, never
|
|
91
|
+
* throws).
|
|
92
|
+
*/
|
|
93
|
+
protected afterDeviceTokenExchange?(
|
|
94
|
+
_payload: unknown,
|
|
95
|
+
_normalized: NormalizedTokenResponse,
|
|
96
|
+
): OAuthExchangeResult | OAuthDevicePollResult | Promise<OAuthExchangeResult | OAuthDevicePollResult>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Failure reason for a non-JSON token body. Defaults to the generic
|
|
100
|
+
* phrasing; providers whose wire carries a distinct message override it.
|
|
101
|
+
*/
|
|
102
|
+
protected unparsablePollReason(status: number): string {
|
|
103
|
+
return `device polling failed (${status})`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Failure reason for a non-OK token response. The default preserves the
|
|
108
|
+
* generic behavior: the response's `error` field names the reason on the
|
|
109
|
+
* RFC 8628 400/428 statuses, and every other non-OK status gets the generic
|
|
110
|
+
* phrasing (the body is not trusted there).
|
|
111
|
+
*/
|
|
112
|
+
protected pollFailureReason(
|
|
113
|
+
status: number,
|
|
114
|
+
errorKey: string | undefined,
|
|
115
|
+
_errorDescription: string | undefined,
|
|
116
|
+
): string {
|
|
117
|
+
if (status === 400 || status === 428) return errorKey || `device polling failed (${status})`;
|
|
118
|
+
return `device polling failed (${status})`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Failure reason when a token response omits `access_token`. */
|
|
122
|
+
protected missingTokenReason(_errorKey: string | undefined, _errorDescription: string | undefined): string {
|
|
123
|
+
return `${this.providerLabel} token response omitted access_token`;
|
|
124
|
+
}
|
|
125
|
+
|
|
70
126
|
/** Standard device-token poll. Returns pending while authorization is incomplete. */
|
|
71
127
|
protected async pollGenericDeviceAuth(
|
|
72
128
|
deviceAuthId: string,
|
|
73
129
|
config: DeviceFlowConfig,
|
|
130
|
+
context?: OAuthDeviceFlowContext,
|
|
74
131
|
): Promise<OAuthDevicePollResult> {
|
|
75
|
-
const response = await this.
|
|
132
|
+
const response = await this.fetchFor(context)(config.tokenUrl, {
|
|
76
133
|
method: "POST",
|
|
77
134
|
...(config.formHeaders === undefined ? {} : { headers: config.formHeaders }),
|
|
78
135
|
body: new URLSearchParams({
|
|
@@ -88,20 +145,57 @@ export abstract class OAuthDeviceFlow extends OAuthClient {
|
|
|
88
145
|
} catch {
|
|
89
146
|
payload = undefined;
|
|
90
147
|
}
|
|
148
|
+
// A body that is not JSON is a failed poll regardless of status: verdicts
|
|
149
|
+
// must never throw, and the reason below lets providers keep their own
|
|
150
|
+
// phrasing (e.g. Grok's "returned invalid JSON").
|
|
151
|
+
if (payload === undefined) {
|
|
152
|
+
return { status: "failed", reason: this.unparsablePollReason(response.status) };
|
|
153
|
+
}
|
|
91
154
|
const root = record(payload) ?? {};
|
|
155
|
+
if (this.preVerdict) {
|
|
156
|
+
const verdict = this.preVerdict(payload);
|
|
157
|
+
if (verdict !== undefined) return verdict;
|
|
158
|
+
}
|
|
159
|
+
const errorKey = typeof root.error === "string" ? root.error : undefined;
|
|
160
|
+
const errorDescription = typeof root.error_description === "string" ? root.error_description : undefined;
|
|
92
161
|
if (response.status === 400 || response.status === 428) {
|
|
93
|
-
const
|
|
94
|
-
const verdict = devicePollBackoff(err, root.interval);
|
|
162
|
+
const verdict = devicePollBackoff(errorKey, root.interval);
|
|
95
163
|
if (verdict !== undefined) return verdict;
|
|
96
|
-
return {
|
|
164
|
+
return {
|
|
165
|
+
status: "failed",
|
|
166
|
+
reason: this.pollFailureReason(response.status, errorKey, errorDescription),
|
|
167
|
+
};
|
|
97
168
|
}
|
|
98
|
-
if (!response.ok
|
|
99
|
-
return { status: "failed", reason:
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
return { status: "failed", reason: this.pollFailureReason(response.status, errorKey, errorDescription) };
|
|
100
171
|
}
|
|
101
172
|
if (typeof root.access_token !== "string" || !root.access_token) {
|
|
102
|
-
return {
|
|
173
|
+
return {
|
|
174
|
+
status: "failed",
|
|
175
|
+
reason: this.missingTokenReason(errorKey, errorDescription),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
let normalized: NormalizedTokenResponse;
|
|
179
|
+
try {
|
|
180
|
+
normalized = this.parseTokenResponse(payload);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return {
|
|
183
|
+
status: "failed",
|
|
184
|
+
reason: error instanceof Error ? error.message : `${this.providerLabel} token response omitted access_token`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (this.afterDeviceTokenExchange) {
|
|
188
|
+
try {
|
|
189
|
+
const hooked = await this.afterDeviceTokenExchange(payload, normalized);
|
|
190
|
+
if ("status" in hooked) return hooked;
|
|
191
|
+
return { status: "complete", result: hooked };
|
|
192
|
+
} catch (error) {
|
|
193
|
+
return {
|
|
194
|
+
status: "failed",
|
|
195
|
+
reason: error instanceof Error ? error.message : `${this.providerLabel} authentication failed`,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
103
198
|
}
|
|
104
|
-
const normalized = this.parseTokenResponse(payload);
|
|
105
199
|
return { status: "complete", result: this.toExchangeResult(normalized) };
|
|
106
200
|
}
|
|
107
201
|
}
|
|
@@ -29,7 +29,15 @@ export interface OAuthCodeExchangeContext {
|
|
|
29
29
|
readonly providerState?: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
/** Structured identity an OAuth token response exposed at login or polling time. */
|
|
33
|
+
export interface OAuthAccountIdentity {
|
|
34
|
+
/** Human-readable name or username, when the provider exposes one. */
|
|
35
|
+
readonly accountName?: string;
|
|
36
|
+
/** Primary email, when the provider exposes one. */
|
|
37
|
+
readonly accountEmail?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface OAuthExchangeResult extends OAuthAccountIdentity {
|
|
33
41
|
readonly access: string;
|
|
34
42
|
readonly refresh: string;
|
|
35
43
|
readonly expiresAt: Date;
|
|
@@ -75,8 +83,20 @@ export interface OAuthDeviceFlowContext {
|
|
|
75
83
|
* needs none ignores it.
|
|
76
84
|
*/
|
|
77
85
|
readonly parameters?: Readonly<Record<string, string>> | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Transport override for the device-authorization and token-poll requests.
|
|
88
|
+
* When supplied, the client MUST use it instead of its default fetch — the
|
|
89
|
+
* console injects a pool-routed or direct validated fetch here (see the
|
|
90
|
+
* `device_flow_proxy` routing setting). Providers that ignore it keep
|
|
91
|
+
* dialing from the gateway's own egress, which is what made multiple device
|
|
92
|
+
* codes appear to arrive from one IP.
|
|
93
|
+
*/
|
|
94
|
+
readonly fetchFn?: FetchLike | undefined;
|
|
78
95
|
}
|
|
79
96
|
|
|
97
|
+
/** Minimal fetch surface accepted by the OAuth flow contracts. */
|
|
98
|
+
export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
99
|
+
|
|
80
100
|
export type OAuthDevicePollResult =
|
|
81
101
|
/**
|
|
82
102
|
* Still waiting on the user. `retryAfterSeconds` carries the server's
|
|
@@ -112,6 +132,12 @@ export interface OAuthLoginClient {
|
|
|
112
132
|
* compares it against the one the authorize step sent.
|
|
113
133
|
*/
|
|
114
134
|
readonly browserRedirectUri?: string;
|
|
135
|
+
/**
|
|
136
|
+
* Custom OAuth state generator for providers whose authorization server
|
|
137
|
+
* validates the `state` format (e.g. DeepAI accepts only 32 hex chars).
|
|
138
|
+
* Defaults to the shared base64url state when omitted.
|
|
139
|
+
*/
|
|
140
|
+
generateState?(): string | undefined;
|
|
115
141
|
prepareAuthorize?(request: OAuthAuthorizeRequest): Promise<OAuthAuthorizeRequest>;
|
|
116
142
|
/** Browser-code clients only; device-only clients omit both. */
|
|
117
143
|
buildAuthorizeUrl?(request: OAuthAuthorizeRequest): string;
|
|
@@ -425,6 +451,61 @@ export function decodeJwtPayload(token: string): Record<string, unknown> | undef
|
|
|
425
451
|
}
|
|
426
452
|
}
|
|
427
453
|
|
|
454
|
+
/** Trim a text value from an untrusted token response without accepting other types. */
|
|
455
|
+
function identityText(value: unknown): string | undefined {
|
|
456
|
+
const trimmed = nonEmptyTrimmedString(value);
|
|
457
|
+
return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function firstIdentityText(
|
|
461
|
+
source: Record<string, unknown> | undefined,
|
|
462
|
+
fields: readonly string[],
|
|
463
|
+
): string | undefined {
|
|
464
|
+
if (source === undefined) return undefined;
|
|
465
|
+
for (const field of fields) {
|
|
466
|
+
const value = identityText(source[field]);
|
|
467
|
+
if (value !== undefined) return value;
|
|
468
|
+
}
|
|
469
|
+
return undefined;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Reads identity from the token response shapes shared by WorkOS and the donor
|
|
474
|
+
* OAuth integrations. A provider that exposes only an id gets a display name,
|
|
475
|
+
* never an invented email; a response with neither leaves every field absent.
|
|
476
|
+
*/
|
|
477
|
+
export function oauthAccountIdentity(payload: unknown): OAuthAccountIdentity {
|
|
478
|
+
const root = record(payload);
|
|
479
|
+
if (root === undefined) return {};
|
|
480
|
+
const sources = [
|
|
481
|
+
root,
|
|
482
|
+
record(root.data),
|
|
483
|
+
record(root.user),
|
|
484
|
+
record(root.userInfo),
|
|
485
|
+
record(record(root.data)?.user),
|
|
486
|
+
record(record(root.data)?.userInfo),
|
|
487
|
+
decodeJwtPayload(identityText(root.access_token) ?? identityText(root.accessToken) ?? ""),
|
|
488
|
+
].filter((value): value is Record<string, unknown> => value !== undefined);
|
|
489
|
+
const email = sources
|
|
490
|
+
.map((source) => firstIdentityText(source, ["email", "userEmail", "preferred_username", "upn"]))
|
|
491
|
+
.find((value): value is string => value !== undefined);
|
|
492
|
+
const name = sources
|
|
493
|
+
.map((source) => firstIdentityText(source, ["name", "displayName", "nickname", "username", "login"]))
|
|
494
|
+
.find((value): value is string => value !== undefined);
|
|
495
|
+
const firstName = sources
|
|
496
|
+
.map((source) => firstIdentityText(source, ["firstName", "first_name", "given_name"]))
|
|
497
|
+
.find((value): value is string => value !== undefined);
|
|
498
|
+
const lastName = sources
|
|
499
|
+
.map((source) => firstIdentityText(source, ["lastName", "last_name", "family_name"]))
|
|
500
|
+
.find((value): value is string => value !== undefined);
|
|
501
|
+
const fullName = [firstName, lastName].filter((part): part is string => part !== undefined).join(" ").trim();
|
|
502
|
+
const accountName = name ?? (fullName.length > 0 ? fullName : undefined);
|
|
503
|
+
return {
|
|
504
|
+
...(email === undefined ? {} : { accountEmail: email }),
|
|
505
|
+
...(accountName === undefined ? {} : { accountName }),
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
428
509
|
/**
|
|
429
510
|
* Builds a PKCE authorization URL with the canonical parameter order
|
|
430
511
|
* (`client_id`, `response_type`, `redirect_uri`, `scope`, `state`,
|